#include "qemu/osdep.h"
#include "hw/i2c/i2c.h"
-
-typedef struct I2CNode I2CNode;
-
-struct I2CNode {
- I2CSlave *elt;
- QLIST_ENTRY(I2CNode) next;
-};
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+#include "trace.h"
#define I2C_BROADCAST 0x00
-struct I2CBus
-{
- BusState qbus;
- QLIST_HEAD(, I2CNode) current_devs;
- uint8_t saved_address;
- bool broadcast;
-};
-
static Property i2c_props[] = {
DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0),
DEFINE_PROP_END_OF_LIST(),
};
-#define TYPE_I2C_BUS "i2c-bus"
-#define I2C_BUS(obj) OBJECT_CHECK(I2CBus, (obj), TYPE_I2C_BUS)
-
static const TypeInfo i2c_bus_info = {
.name = TYPE_I2C_BUS,
.parent = TYPE_BUS,
bus = I2C_BUS(qbus_create(TYPE_I2C_BUS, parent, name));
QLIST_INIT(&bus->current_devs);
- vmstate_register(NULL, -1, &vmstate_i2c_bus, bus);
+ vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus);
return bus;
}
}
QLIST_FOREACH(node, &bus->current_devs, next) {
+ I2CSlave *s = node->elt;
int rv;
- sc = I2C_SLAVE_GET_CLASS(node->elt);
+ sc = I2C_SLAVE_GET_CLASS(s);
/* If the bus is already busy, assume this is a repeated
start condition. */
if (sc->event) {
- rv = sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND);
+ trace_i2c_event("start", s->address);
+ rv = sc->event(s, recv ? I2C_START_RECV : I2C_START_SEND);
if (rv && !bus->broadcast) {
if (bus_scanned) {
/* First call, terminate the transfer. */
I2CNode *node, *next;
QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {
- sc = I2C_SLAVE_GET_CLASS(node->elt);
+ I2CSlave *s = node->elt;
+ sc = I2C_SLAVE_GET_CLASS(s);
if (sc->event) {
- sc->event(node->elt, I2C_FINISH);
+ trace_i2c_event("finish", s->address);
+ sc->event(s, I2C_FINISH);
}
QLIST_REMOVE(node, next);
g_free(node);
int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send)
{
I2CSlaveClass *sc;
+ I2CSlave *s;
I2CNode *node;
int ret = 0;
if (send) {
QLIST_FOREACH(node, &bus->current_devs, next) {
- sc = I2C_SLAVE_GET_CLASS(node->elt);
+ s = node->elt;
+ sc = I2C_SLAVE_GET_CLASS(s);
if (sc->send) {
- ret = ret || sc->send(node->elt, *data);
+ trace_i2c_send(s->address, *data);
+ ret = ret || sc->send(s, *data);
} else {
ret = -1;
}
}
return ret ? -1 : 0;
} else {
- if ((QLIST_EMPTY(&bus->current_devs)) || (bus->broadcast)) {
- return -1;
- }
-
- sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
- if (sc->recv) {
- ret = sc->recv(QLIST_FIRST(&bus->current_devs)->elt);
- if (ret < 0) {
- return ret;
- } else {
- *data = ret;
- return 0;
+ ret = 0xff;
+ if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) {
+ sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
+ if (sc->recv) {
+ s = QLIST_FIRST(&bus->current_devs)->elt;
+ ret = sc->recv(s);
+ trace_i2c_recv(s->address, ret);
}
}
- return -1;
+ *data = ret;
+ return 0;
}
}
return i2c_send_recv(bus, &data, true);
}
-int i2c_recv(I2CBus *bus)
+uint8_t i2c_recv(I2CBus *bus)
{
- uint8_t data;
- int ret = i2c_send_recv(bus, &data, false);
+ uint8_t data = 0xff;
- return ret < 0 ? ret : data;
+ i2c_send_recv(bus, &data, false);
+ return data;
}
void i2c_nack(I2CBus *bus)
QLIST_FOREACH(node, &bus->current_devs, next) {
sc = I2C_SLAVE_GET_CLASS(node->elt);
if (sc->event) {
+ trace_i2c_event("nack", node->elt->address);
sc->event(node->elt, I2C_NACK);
}
}
}
};
-static int i2c_slave_qdev_init(DeviceState *dev)
-{
- I2CSlave *s = I2C_SLAVE(dev);
- I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s);
-
- if (sc->init) {
- return sc->init(s);
- }
-
- return 0;
-}
-
DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr)
{
DeviceState *dev;
static void i2c_slave_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
- k->init = i2c_slave_qdev_init;
set_bit(DEVICE_CATEGORY_MISC, k->categories);
k->bus_type = TYPE_I2C_BUS;
- k->props = i2c_props;
+ device_class_set_props(k, i2c_props);
}
static const TypeInfo i2c_slave_type_info = {