diff options
Diffstat (limited to 'hw/i2c/core.c')
-rw-r--r-- | hw/i2c/core.c | 55 |
1 files changed, 42 insertions, 13 deletions
diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 21ec52ac5a..3a7bae311d 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -77,6 +77,30 @@ int i2c_bus_busy(I2CBus *bus) return !QLIST_EMPTY(&bus->current_devs); } +bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, + I2CNodeList *current_devs) +{ + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { + DeviceState *qdev = kid->child; + I2CSlave *candidate = I2C_SLAVE(qdev); + I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(candidate); + + if (sc->match_and_add(candidate, address, broadcast, current_devs)) { + if (!broadcast) { + return true; + } + } + } + + /* + * If broadcast was true, and the list was full or empty, return true. If + * broadcast was false, return false. + */ + return broadcast; +} + /* TODO: Make this handle multiple masters. */ /* * Start or continue an i2c transaction. When this is called for the @@ -93,7 +117,6 @@ int i2c_bus_busy(I2CBus *bus) */ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) { - BusChild *kid; I2CSlaveClass *sc; I2CNode *node; bool bus_scanned = false; @@ -115,18 +138,8 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) * terminating the previous transaction. */ 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; - } - } - } + /* Disregard whether devices were found. */ + (void)i2c_scan_bus(bus, address, bus->broadcast, &bus->current_devs); bus_scanned = true; } @@ -290,12 +303,28 @@ I2CSlave *i2c_slave_create_simple(I2CBus *bus, const char *name, uint8_t addr) return dev; } +static bool i2c_slave_match(I2CSlave *candidate, uint8_t address, + bool broadcast, I2CNodeList *current_devs) +{ + if ((candidate->address == address) || (broadcast)) { + I2CNode *node = g_malloc(sizeof(struct I2CNode)); + node->elt = candidate; + QLIST_INSERT_HEAD(current_devs, node, next); + return true; + } + + /* Not found and not broadcast. */ + return false; +} + static void i2c_slave_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); set_bit(DEVICE_CATEGORY_MISC, k->categories); k->bus_type = TYPE_I2C_BUS; device_class_set_props(k, i2c_props); + sc->match_and_add = i2c_slave_match; } static const TypeInfo i2c_slave_type_info = { |