DF-1095 / fix.diff
diff --git a/sys/bus/iicbus/iicsmb.c b/sys/bus/iicbus/iicsmb.c index 0000000..1111111 100644 --- a/sys/bus/iicbus/iicsmb.c +++ b/sys/bus/iicbus/iicsmb.c @@ -465,8 +465,14 @@ device_t parent = device_get_parent(dev); int error, sent; + /* + * On start failure the bus was never placed in the STARTED state, + * so iicbus_stop() must NOT be issued -- it would be a no-op at + * best and a protocol violation at worst. Return directly, mirroring + * iicsmb_bread() below. DF-1095. + */ if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) - goto error; + return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; @@ -474,10 +480,18 @@ if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT))) goto error; - if ((error = iicbus_stop(parent))) - goto error; - error: + /* + * If start succeeded but a subsequent write failed (NACK/bus + * error/timeout), the bus is still STARTED and MUST be released. + * The previous code returned here without calling iicbus_stop(), + * leaving sc->started set (iiconf.c:178) so every subsequent + * iicbus_start() returned EINVAL and the SMBus bridge was + * permanently wedged. Always issue STOP -- on the success path + * the comment that originally guided the explicit iicbus_stop() + * above is preserved here. DF-1095. + */ + iicbus_stop(parent); return (error); } |