Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=939bc4943d0483961edc45b63a7d27b4ffe547e3
Commit:     939bc4943d0483961edc45b63a7d27b4ffe547e3
Parent:     51e5709ad46127da9476f31336113b5401f94dba
Author:     David Brownell <[EMAIL PROTECTED]>
AuthorDate: Sun Sep 9 22:29:14 2007 +0200
Committer:  Jean Delvare <[EMAIL PROTECTED]>
CommitDate: Sun Sep 9 22:29:14 2007 +0200

    i2c-algo-bit: Read block data bugfix
    
    This fixes a bug in the way i2c-algo-bit handles I2C_M_RECV_LEN,
    used to implement i2c_smbus_read_block_data().  Previously, in the
    absence of PEC (rarely used!) it would NAK the "length" byte:
    
        S addr Rd [A] [length] NA
    
    That prevents the subsequent data bytes from being read:
    
        S addr Rd [A] [length] { A [data] }* NA
    
    The primary fix just reorders two code blocks, so the length used
    in the "should I NAK now?" check incorporates the data which it
    just read from the slave device.
    
    However, that move also highlighted other fault handling glitches.
    This fixes those by abstracting the RX path ack/nak logic, so it
    can be used in more than one location.  Also, a few CodingStyle
    issues were also resolved.
    
    Signed-off-by: David Brownell <[EMAIL PROTECTED]>
    Signed-off-by: Jean Delvare <[EMAIL PROTECTED]>
---
 drivers/i2c/algos/i2c-algo-bit.c |   52 +++++++++++++++++++++++--------------
 1 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 8a5f582..7f0a0a6 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -357,13 +357,29 @@ static int sendbytes(struct i2c_adapter *i2c_adap, struct 
i2c_msg *msg)
        return wrcount;
 }
 
+static int acknak(struct i2c_adapter *i2c_adap, int is_ack)
+{
+       struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
+
+       /* assert: sda is high */
+       if (is_ack)             /* send ack */
+               setsda(adap, 0);
+       udelay((adap->udelay + 1) / 2);
+       if (sclhi(adap) < 0) {  /* timeout */
+               dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n");
+               return -ETIMEDOUT;
+       }
+       scllo(adap);
+       return 0;
+}
+
 static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
 {
        int inval;
        int rdcount=0;          /* counts bytes read */
-       struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
        unsigned char *temp = msg->buf;
        int count = msg->len;
+       const unsigned flags = msg->flags;
 
        while (count > 0) {
                inval = i2c_inb(i2c_adap);
@@ -377,28 +393,12 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct 
i2c_msg *msg)
                temp++;
                count--;
 
-               if (msg->flags & I2C_M_NO_RD_ACK) {
-                       bit_dbg(2, &i2c_adap->dev, "i2c_inb: 0x%02x\n",
-                               inval);
-                       continue;
-               }
-
-               /* assert: sda is high */
-               if (count)              /* send ack */
-                       setsda(adap, 0);
-               udelay((adap->udelay + 1) / 2);
-               bit_dbg(2, &i2c_adap->dev, "i2c_inb: 0x%02x %s\n", inval,
-                       count ? "A" : "NA");
-               if (sclhi(adap)<0) {    /* timeout */
-                       dev_err(&i2c_adap->dev, "readbytes: timeout at ack\n");
-                       return -ETIMEDOUT;
-               };
-               scllo(adap);
-
                /* Some SMBus transactions require that we receive the
                   transaction length as the first read byte. */
-               if (rdcount == 1 && (msg->flags & I2C_M_RECV_LEN)) {
+               if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) {
                        if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {
+                               if (!(flags & I2C_M_NO_RD_ACK))
+                                       acknak(i2c_adap, 0);
                                dev_err(&i2c_adap->dev, "readbytes: invalid "
                                        "block length (%d)\n", inval);
                                return -EREMOTEIO;
@@ -409,6 +409,18 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct 
i2c_msg *msg)
                        count += inval;
                        msg->len += inval;
                }
+
+               bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n",
+                       inval,
+                       (flags & I2C_M_NO_RD_ACK)
+                               ? "(no ack/nak)"
+                               : (count ? "A" : "NA"));
+
+               if (!(flags & I2C_M_NO_RD_ACK)) {
+                       inval = acknak(i2c_adap, count);
+                       if (inval < 0)
+                               return inval;
+               }
        }
        return rdcount;
 }
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to