From patchwork Sat Jun 13 08:26:30 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ellen Wang X-Patchwork-Id: 6602231 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 14D799F399 for ; Sat, 13 Jun 2015 08:26:44 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0E332206DC for ; Sat, 13 Jun 2015 08:26:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C1E9220523 for ; Sat, 13 Jun 2015 08:26:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751261AbbFMI0j (ORCPT ); Sat, 13 Jun 2015 04:26:39 -0400 Received: from mail-pd0-f174.google.com ([209.85.192.174]:36402 "EHLO mail-pd0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751237AbbFMI0h (ORCPT ); Sat, 13 Jun 2015 04:26:37 -0400 Received: by pdjm12 with SMTP id m12so37983765pdj.3 for ; Sat, 13 Jun 2015 01:26:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cumulusnetworks.com; s=google; h=from:to:cc:subject:date:message-id; bh=y382gwAL/nHulf8H8FwV2Qn48btK4JuYSErOxImOJnQ=; b=cBBrJ9wbBUMhLiHy9i5Kx/2Kx/vlB/rQ5ZoDVvf03WjHiByXuOVkXspPsMVCptPuvs mcGylKIJjHwELhg8iaM+/bGbBojvnlHvkh3jBlzjgX55QWze/aw9y7rsdEuUkgkUA683 k/+k9LVkMcHB/Ne8IrqZAsrJ48SeYy/pMsDdg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=y382gwAL/nHulf8H8FwV2Qn48btK4JuYSErOxImOJnQ=; b=QKCCrH9qileEoLsvzLaMBTCAphiORZQUdepO5N8aows5TD29HFrZedEVaFH3W2LcL7 np0AFxLmTou7U5fs/n03ecSVoGxE3kbus7nqNudc3WzuBjvkVYBnzCUDV6EJ+IH7mcVs XaAdqbI85gL+sXzkjCQT4pSN0WWMlBubeQ1TA9b098i7mPYBOZKEJsWi4NE4mUk1Ea6Q aF5YXbzVUdB8Fz83RxQd12olBMLWP81WyVIy2arDMBJe9azWGLt0W+LIExbS1tZ4xH9N CUHlJk++hQXKM2eY4EVxD5U7m1UiYED8+37plFeQ1aOuw6a2tw/xuRm6+JMcBhSQLv5U H7nA== X-Gm-Message-State: ALoCoQlYVbkH850A3BAMVIzXAIRNyT0wV1rN+Nk2u54uaEWniRI856QWz1OI1ZY3i2Q/HjowUtbf X-Received: by 10.68.107.97 with SMTP id hb1mr29867521pbb.143.1434183996263; Sat, 13 Jun 2015 01:26:36 -0700 (PDT) Received: from monster-01.cumulusnetworks.com ([216.129.126.126]) by mx.google.com with ESMTPSA id wh6sm5810527pbc.96.2015.06.13.01.26.34 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 13 Jun 2015 01:26:35 -0700 (PDT) From: Ellen Wang To: dbarksdale@uplogix.com, jkosina@suse.cz, linux-input@vger.kernel.org, linux-i2c@vger.kernel.org Cc: ellen@cumulusnetworks.com Subject: [PATCH v2] HID: support i2c write-read and large transfers in hid-cp2112 Date: Sat, 13 Jun 2015 01:26:30 -0700 Message-Id: <1434183990-2324-1-git-send-email-ellen@cumulusnetworks.com> X-Mailer: git-send-email 1.7.10.4 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID,T_RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP cp2112_i2c_xfer() only supports a single i2c_msg and only reads up to 61 bytes. More than one message at a time and longers reads just return errors. This breaks certain important cases. For example, the at24 eeprom driver generates paired write and read messages (for eeprom address and data). And the reads can be larger than 61 bytes. Since the device doesn't support i2c repeated starts in general, but does support a single write-repeated-start-read pair (as CP2112_DATA_WRITE_READ_REQUEST), we recognize the latter case and implement only that. To support large reads, we wrap a loop around cp2112_read() to pick up all the returned data. Signed-off-by: Ellen Wang --- As Antonio Borneo and I discussed previously, this is the updated patch that preserves the repeated start semantics (by not incorrectly implementing cases that don't work). Thank you for your time! --- drivers/hid/hid-cp2112.c | 79 +++++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 3318de6..2c10a45 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -444,6 +444,24 @@ static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data, return data_length + 3; } +static int cp2112_i2c_write_read_req(void *buf, u8 slave_address, + u8 *addr, int addr_length, + int read_length) +{ + struct cp2112_write_read_req_report *report = buf; + + if (read_length < 1 || read_length > 512 || + addr_length > sizeof(report->target_address)) + return -EINVAL; + + report->report = CP2112_DATA_WRITE_READ_REQUEST; + report->slave_address = slave_address << 1; + report->length = cpu_to_be16(read_length); + report->target_address_length = addr_length; + memcpy(report->target_address, addr, addr_length); + return addr_length + 5; +} + static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { @@ -451,26 +469,45 @@ static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, struct hid_device *hdev = dev->hdev; u8 buf[64]; ssize_t count; + ssize_t read_length = 0; + u8 *read_buf = NULL; unsigned int retries; int ret; hid_dbg(hdev, "I2C %d messages\n", num); - if (num != 1) { + if (num == 1) { + if (msgs->flags & I2C_M_RD) { + hid_dbg(hdev, "I2C read %#04x len %d\n", + msgs->addr, msgs->len); + read_length = msgs->len; + read_buf = msgs->buf; + count = cp2112_read_req(buf, msgs->addr, msgs->len); + } else { + hid_dbg(hdev, "I2C write %#04x len %d\n", + msgs->addr, msgs->len); + count = cp2112_i2c_write_req(buf, msgs->addr, + msgs->buf, msgs->len); + } + if (count < 0) + return count; + } else if (num == 2 && + msgs[0].addr == msgs[1].addr && + !(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) { + hid_dbg(hdev, "I2C write-read %#04x wlen %d rlen %d\n", + msgs[0].addr, msgs[0].len, msgs[1].len); + read_length = msgs[1].len; + read_buf = msgs[1].buf; + count = cp2112_i2c_write_read_req(buf, msgs[0].addr, + msgs[0].buf, msgs[0].len, msgs[1].len); + if (count < 0) + return count; + } else { hid_err(hdev, "Multi-message I2C transactions not supported\n"); return -EOPNOTSUPP; } - if (msgs->flags & I2C_M_RD) - count = cp2112_read_req(buf, msgs->addr, msgs->len); - else - count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf, - msgs->len); - - if (count < 0) - return count; - ret = hid_hw_power(hdev, PM_HINT_FULLON); if (ret < 0) { hid_err(hdev, "power management error: %d\n", ret); @@ -506,21 +543,19 @@ static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, goto power_normal; } - if (!(msgs->flags & I2C_M_RD)) - goto finish; - - ret = cp2112_read(dev, msgs->buf, msgs->len); - if (ret < 0) - goto power_normal; - if (ret != msgs->len) { - hid_warn(hdev, "short read: %d < %d\n", ret, msgs->len); - ret = -EIO; - goto power_normal; + for (count = 0; count < read_length;) { + ret = cp2112_read(dev, read_buf + count, read_length - count); + if (ret < 0) + goto power_normal; + count += ret; + if (count > read_length) { + hid_warn(hdev, "long read: %d > %zd\n", + ret, read_length - count + ret); + } } -finish: /* return the number of transferred messages */ - ret = 1; + ret = num; power_normal: hid_hw_power(hdev, PM_HINT_NORMAL);