diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index fa63bee0b345..57f55bbab8a1 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -208,11 +208,11 @@ s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, EXPORT_SYMBOL(i2c_smbus_write_word_data); /** - * i2c_smbus_read_block_data - SMBus "block read" protocol + * __i2c_smbus_read_block_data - SMBus "block read" protocol * @client: Handle to slave device * @command: Byte interpreted by slave - * @values: Byte array into which data will be read; big enough to hold - * the data returned by the slave. SMBus allows at most 32 bytes. + * @length: size of the @values array. SMBus allows at most 32 bytes + * @values: Byte array into which data will be read * * This executes the SMBus "block read" protocol, returning negative errno * else the number of data bytes in the slave's response. @@ -222,22 +222,30 @@ EXPORT_SYMBOL(i2c_smbus_write_word_data); * support this; its emulation through I2C messaging relies on a specific * mechanism (I2C_M_RECV_LEN) which may not be implemented. */ -s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, - u8 *values) +s32 __i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) { union i2c_smbus_data data; + int ret_len; int status; + if (length > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_READ, command, I2C_SMBUS_BLOCK_DATA, &data); if (status) return status; - memcpy(values, &data.block[1], data.block[0]); - return data.block[0]; + ret_len = data.block[0]; + if (ret_len > length) + return -EMSGSIZE; + + memcpy(values, &data.block[1], ret_len); + return ret_len; } -EXPORT_SYMBOL(i2c_smbus_read_block_data); +EXPORT_SYMBOL(__i2c_smbus_read_block_data); /** * i2c_smbus_write_block_data - SMBus "block write" protocol diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 4af2df969a05..f3192ab1982e 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -174,8 +174,23 @@ i2c_smbus_write_word_swapped(const struct i2c_client *client, } /* Returns the number of read bytes */ -s32 i2c_smbus_read_block_data(const struct i2c_client *client, - u8 command, u8 *values); +s32 __i2c_smbus_read_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values); +/* + * This monstrosity allows to call i2c_smbus_read_block_data() with either + * 3 or 4 arguments and will be removed once all users have been switched + * to the 4 argument version. + */ +#define __i2c_smbus_read_block_data_3arg(client, cmd, values) \ + __i2c_smbus_read_block_data(client, cmd, I2C_SMBUS_BLOCK_MAX, values) +#define __i2c_smbus_read_block_data_4arg(client, cmd, length, values) \ + __i2c_smbus_read_block_data(client, cmd, length, values) +#define __i2c_smbus_read_block_data_impl(_1, _2, _3, _4, impl, ...) impl +#define i2c_smbus_read_block_data(client, cmd, varargs...) \ + __i2c_smbus_read_block_data_impl(client, cmd, varargs, \ + __i2c_smbus_read_block_data_4arg, \ + __i2c_smbus_read_block_data_3arg) \ + (client, cmd, varargs) s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values); /* Returns the number of read bytes */