On Wed, Jun 01, 2022 at 11:08:28PM +0200, Klaus Jensen wrote: > From: Klaus Jensen > > Allow slaves to master the bus by registering a bottom halve. If the bus > is busy, the bottom halve is queued up. When a slave has succesfully ^ half > mastered the bus, the bottom halve is scheduled. ^ half "halve" is a verb that means to split something into two pieces. Yes, English is a strange language :). Also, technically from an I2C point of view, masters master the bus and slaves only respond. The way it's phrased here and elsewhere sounds a little strange from that point of view. I'm ok with this patch. It's straightforward. > > Signed-off-by: Klaus Jensen > --- > hw/i2c/core.c | 34 +++++++++++++++++++++++++++++++++- > include/hw/i2c/i2c.h | 14 ++++++++++++++ > 2 files changed, 47 insertions(+), 1 deletion(-) > > diff --git a/hw/i2c/core.c b/hw/i2c/core.c > index d0cb2d32fa44..145dce60782a 100644 > --- a/hw/i2c/core.c > +++ b/hw/i2c/core.c > @@ -13,6 +13,7 @@ > #include "migration/vmstate.h" > #include "qapi/error.h" > #include "qemu/module.h" > +#include "qemu/main-loop.h" > #include "trace.h" > > #define I2C_BROADCAST 0x00 > @@ -62,6 +63,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) > > bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name)); > QLIST_INIT(&bus->current_devs); > + QSIMPLEQ_INIT(&bus->pending_masters); > vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); > return bus; > } > @@ -74,7 +76,7 @@ void i2c_slave_set_address(I2CSlave *dev, uint8_t address) > /* Return nonzero if bus is busy. */ > int i2c_bus_busy(I2CBus *bus) > { > - return !QLIST_EMPTY(&bus->current_devs); > + return !QLIST_EMPTY(&bus->current_devs) || bus->bh; > } > > bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, > @@ -180,6 +182,26 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) > : I2C_START_SEND); > } > > +void i2c_bus_master(I2CBus *bus, QEMUBH *bh) > +{ > + if (i2c_bus_busy(bus)) { > + I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1); > + node->bh = bh; > + > + QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry); > + > + return; > + } > + > + bus->bh = bh; > + qemu_bh_schedule(bus->bh); > +} > + > +void i2c_bus_release(I2CBus *bus) > +{ > + bus->bh = NULL; > +} > + > int i2c_start_recv(I2CBus *bus, uint8_t address) > { > return i2c_do_start_transfer(bus, address, I2C_START_RECV); > @@ -206,6 +228,16 @@ void i2c_end_transfer(I2CBus *bus) > g_free(node); > } > bus->broadcast = false; > + > + if (!QSIMPLEQ_EMPTY(&bus->pending_masters)) { > + I2CPendingMaster *node = QSIMPLEQ_FIRST(&bus->pending_masters); > + bus->bh = node->bh; > + > + QSIMPLEQ_REMOVE_HEAD(&bus->pending_masters, entry); > + g_free(node); > + > + qemu_bh_schedule(bus->bh); > + } > } > > int i2c_send(I2CBus *bus, uint8_t data) > diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h > index 5ca3b708c0be..be8bb8b78a60 100644 > --- a/include/hw/i2c/i2c.h > +++ b/include/hw/i2c/i2c.h > @@ -69,13 +69,25 @@ struct I2CNode { > QLIST_ENTRY(I2CNode) next; > }; > > +typedef struct I2CPendingMaster I2CPendingMaster; > + > +struct I2CPendingMaster { > + QEMUBH *bh; > + QSIMPLEQ_ENTRY(I2CPendingMaster) entry; > +}; > + > typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; > +typedef QSIMPLEQ_HEAD(I2CPendingMasters, I2CPendingMaster) I2CPendingMasters; > > struct I2CBus { > BusState qbus; > I2CNodeList current_devs; > + I2CPendingMasters pending_masters; > uint8_t saved_address; > bool broadcast; > + > + /* Set from slave currently mastering the bus. */ > + QEMUBH *bh; > }; > > I2CBus *i2c_init_bus(DeviceState *parent, const char *name); > @@ -117,6 +129,8 @@ int i2c_start_send(I2CBus *bus, uint8_t address); > > void i2c_end_transfer(I2CBus *bus); > void i2c_nack(I2CBus *bus); > +void i2c_bus_master(I2CBus *bus, QEMUBH *bh); > +void i2c_bus_release(I2CBus *bus); > int i2c_send(I2CBus *bus, uint8_t data); > uint8_t i2c_recv(I2CBus *bus); > bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, > -- > 2.36.1 >