Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c-dev: Add support for I2C_M_RECV_LEN

As the bus driver side implementation of I2C_M_RECV_LEN is heavily
tied to SMBus, we can't support received length over 32 bytes, but
let's at least support that.

In practice, the caller will have to setup a buffer large enough to
cover the case where received length byte has value 32, so minimum
32 + 1 = 33 bytes, possibly more if there is a fixed number of bytes
added for the specific slave (for example a checksum.)

Signed-off-by: Jean Delvare <khali@linux-fr.org>
Tested-by: Douglas Gilbert <dgilbert@interlog.com>

authored by

Jean Delvare and committed by
Jean Delvare
838bfa60 731a7378

+26 -4
+26 -4
drivers/i2c/i2c-dev.c
··· 265 265 266 266 res = 0; 267 267 for (i = 0; i < rdwr_arg.nmsgs; i++) { 268 - /* Limit the size of the message to a sane amount; 269 - * and don't let length change either. */ 270 - if ((rdwr_pa[i].len > 8192) || 271 - (rdwr_pa[i].flags & I2C_M_RECV_LEN)) { 268 + /* Limit the size of the message to a sane amount */ 269 + if (rdwr_pa[i].len > 8192) { 272 270 res = -EINVAL; 273 271 break; 274 272 } 273 + 275 274 data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; 276 275 rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len); 277 276 if (IS_ERR(rdwr_pa[i].buf)) { 278 277 res = PTR_ERR(rdwr_pa[i].buf); 279 278 break; 279 + } 280 + 281 + /* 282 + * If the message length is received from the slave (similar 283 + * to SMBus block read), we must ensure that the buffer will 284 + * be large enough to cope with a message length of 285 + * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus 286 + * drivers allow. The first byte in the buffer must be 287 + * pre-filled with the number of extra bytes, which must be 288 + * at least one to hold the message length, but can be 289 + * greater (for example to account for a checksum byte at 290 + * the end of the message.) 291 + */ 292 + if (rdwr_pa[i].flags & I2C_M_RECV_LEN) { 293 + if (!(rdwr_pa[i].flags & I2C_M_RD) || 294 + rdwr_pa[i].buf[0] < 1 || 295 + rdwr_pa[i].len < rdwr_pa[i].buf[0] + 296 + I2C_SMBUS_BLOCK_MAX) { 297 + res = -EINVAL; 298 + break; 299 + } 300 + 301 + rdwr_pa[i].len = rdwr_pa[i].buf[0]; 280 302 } 281 303 } 282 304 if (res < 0) {