Subject: i2c: Emulate SMBus block read over I2C Let the I2C bus drivers emulate the SMBus Block Read and Block Process Call transactions if they wish. This requires to define a new message flag, which i2c-core will use to let the underlying I2C bus driver know that the first received byte will specify the length of the read message. Signed-off-by: Jean Delvare --- drivers/i2c/i2c-core.c | 34 ++++++++++++++++++++++++++-------- include/linux/i2c.h | 1 + 2 files changed, 27 insertions(+), 8 deletions(-) --- linux-2.6.21-pre.orig/drivers/i2c/i2c-core.c 2007-02-13 18:40:13.000000000 +0100 +++ linux-2.6.21-pre/drivers/i2c/i2c-core.c 2007-02-13 20:38:10.000000000 +0100 @@ -609,8 +609,9 @@ int i2c_transfer(struct i2c_adapter * ad #ifdef DEBUG for (ret = 0; ret < num; ret++) { dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " - "len=%d\n", ret, msgs[ret].flags & I2C_M_RD ? - 'R' : 'W', msgs[ret].addr, msgs[ret].len); + "len=%d%s\n", ret, msgs[ret].flags & I2C_M_RD ? + 'R' : 'W', msgs[ret].addr, msgs[ret].len, + msgs[ret].flags & I2C_M_RECV_LEN ? "+" : ""); } #endif @@ -1069,9 +1070,9 @@ static s32 i2c_smbus_xfer_emulated(struc break; case I2C_SMBUS_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { - dev_err(&adapter->dev, "Block read not supported " - "under I2C emulation!\n"); - return -1; + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ } else { msg[0].len = data->block[0] + 2; if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { @@ -1085,9 +1086,21 @@ static s32 i2c_smbus_xfer_emulated(struc } break; case I2C_SMBUS_BLOCK_PROC_CALL: - dev_dbg(&adapter->dev, "Block process call not supported " - "under I2C emulation!\n"); - return -1; + num = 2; /* Another special case */ + read_write = I2C_SMBUS_READ; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, "%s called with invalid " + "block proc call size (%d)\n", __FUNCTION__, + data->block[0]); + return -1; + } + msg[0].len = data->block[0] + 2; + for (i = 1; i < msg[0].len; i++) + msgbuf0[i] = data->block[i-1]; + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + break; case I2C_SMBUS_I2C_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { msg[1].len = I2C_SMBUS_BLOCK_MAX; @@ -1151,6 +1164,11 @@ static s32 i2c_smbus_xfer_emulated(struc for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++) data->block[i+1] = msgbuf1[i]; break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + for (i = 0; i < msgbuf1[0] + 1; i++) + data->block[i] = msgbuf1[i]; + break; } return 0; } --- linux-2.6.21-pre.orig/include/linux/i2c.h 2007-02-13 18:40:13.000000000 +0100 +++ linux-2.6.21-pre/include/linux/i2c.h 2007-02-13 20:38:10.000000000 +0100 @@ -360,6 +360,7 @@ struct i2c_msg { #define I2C_M_REV_DIR_ADDR 0x2000 #define I2C_M_IGNORE_NAK 0x1000 #define I2C_M_NO_RD_ACK 0x0800 +#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ };