On 06/28/2016 09:30 PM, miny...@acm.org wrote: > From: Corey Minyard <cminy...@mvista.com> > > Change 2293c27faddf (i2c: implement broadcast write) added broadcast > capability to the I2C bus, but it broke SMBus read transactions. > An SMBus read transaction does two i2c_start_transaction() calls > without an intervening i2c_end_transfer() call. This will > result in i2c_start_transfer() adding the same device to the > current_devs list twice, and then the ->event() for the same > device gets called twice in the second call to i2c_start_transfer(), > resulting in the smbus code getting confused.
Hi Corey, I didn't know that we can do two i2c_start_transfer without an end_transfert in the middle. Maybe worth a comment in the code? Otherwise: Reviewed-by: KONRAD Frederic <fred.kon...@greensocs.com> Tested-by: KONRAD Frederic <fred.kon...@greensocs.com> Thanks, Fred > > Note that this happens even with pure I2C devices when simulating > SMBus over I2C. > > This fix only scans the bus if the current set of devices is empty. > This means that the current set of devices stays fixed until > i2c_end_transfer() is called, which is really what you want. > > This also deletes the empty check from the top of i2c_end_transfer(). > It's unnecessary, and it prevents the broadcast variable from being > set to false at the end of the transaction if no devices were on > the bus. > > Cc: KONRAD Frederic <fred.kon...@greensocs.com> > Cc: Alistair Francis <alistair.fran...@xilinx.com> > Cc: Peter Crosthwaite <crosthwaite.pe...@gmail.com> > Cc: Kwon <hyun.k...@xilinx.com> > Cc: Peter Maydell <peter.mayd...@linaro.org> > Signed-off-by: Corey Minyard <cminy...@mvista.com> > --- > hw/i2c/core.c | 28 +++++++++++++++------------- > 1 file changed, 15 insertions(+), 13 deletions(-) > > This fix should work with I2C devices as well as SMBus devices. > > Sorry for not thinking it through all the way before. > > diff --git a/hw/i2c/core.c b/hw/i2c/core.c > index abb3efb..6313d31 100644 > --- a/hw/i2c/core.c > +++ b/hw/i2c/core.c > @@ -101,15 +101,21 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, > int recv) > bus->broadcast = true; > } > > - QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { > - DeviceState *qdev = kid->child; > - I2CSlave *candidate = I2C_SLAVE(qdev); > - if ((candidate->address == address) || (bus->broadcast)) { > - node = g_malloc(sizeof(struct I2CNode)); > - node->elt = candidate; > - QLIST_INSERT_HEAD(&bus->current_devs, node, next); > - if (!bus->broadcast) { > - break; > + /* > + * If there are already devices in the list, that means we are in > + * the middle of a transaction and we shouldn't rescan the bus. > + */ > + if (QLIST_EMPTY(&bus->current_devs)) { > + QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { > + DeviceState *qdev = kid->child; > + I2CSlave *candidate = I2C_SLAVE(qdev); > + if ((candidate->address == address) || (bus->broadcast)) { > + node = g_malloc(sizeof(struct I2CNode)); > + node->elt = candidate; > + QLIST_INSERT_HEAD(&bus->current_devs, node, next); > + if (!bus->broadcast) { > + break; > + } > } > } > } > @@ -134,10 +140,6 @@ void i2c_end_transfer(I2CBus *bus) > I2CSlaveClass *sc; > I2CNode *node, *next; > > - if (QLIST_EMPTY(&bus->current_devs)) { > - return; > - } > - > QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { > sc = I2C_SLAVE_GET_CLASS(node->elt); > if (sc->event) { >