DF-5522 / fix.diff
--- a/sys/bus/iicbus/iicsmb.c +++ b/sys/bus/iicbus/iicsmb.c @@ -464,6 +464,8 @@ { device_t parent = device_get_parent(dev); int error, sent; + u_char cnt = count; + if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) goto error; @@ -471,6 +473,9 @@ if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; + /* SMBus Block Write: the byte-count precedes the data on the wire */ + if ((error = iicbus_write(parent, (const char *)&cnt, 1, &sent, IICBUS_TIMEOUT))) + goto error; if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT))) goto error; @@ -486,6 +491,10 @@ { device_t parent = device_get_parent(dev); int error, sent, read; + u_char cnt, devcnt; + + if (*count < 1 || *count > 32) /* SMBus spec 2.0 block limit */ + return (SMB_EINVAL); if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); @@ -496,10 +505,22 @@ if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; - if ((error = iicbus_read(parent, buf, (int)*count, &read, - IIC_LAST_READ, IICBUS_TIMEOUT))) - goto error; - *count = read; + /* + * SMBus Block Read: the first byte the device sends after the + * repeated start is the block count; consume and honour it instead + * of blindly transferring the caller's count of raw bytes. + */ + if ((error = iicbus_read(parent, (char *)&devcnt, 1, &read, + IIC_LAST_READ, IICBUS_TIMEOUT))) + goto error; + if (devcnt < 1 || devcnt > 32) + devcnt = 32; + cnt = (devcnt > *count) ? *count : devcnt; + + if ((error = iicbus_read(parent, buf, (int)cnt, &read, + IIC_LAST_READ, IICBUS_TIMEOUT))) + goto error; + *count = cnt; error: iicbus_stop(parent); |