Message ID | 20220601210831.67259-7-its@irrelevant.dk |
---|---|
State | New |
Headers | show |
Series | hw/i2c: i2c slave mode support | expand |
On 6/1/22 23:08, Klaus Jensen wrote: > From: Klaus Jensen <k.jensen@samsung.com> > > Add an example I2C device to demonstrate how a slave may master the bus > and send data asynchronously to another slave. > > The device will echo whatever it is sent to the device identified by the > first byte received. I think this is useful and small enough to keep for the tests. Thanks, C. > > Signed-off-by: Klaus Jensen <k.jensen@samsung.com> > --- > hw/misc/i2c-echo.c | 162 ++++++++++++++++++++++++++++++++++++++++++++ > hw/misc/meson.build | 2 + > 2 files changed, 164 insertions(+) > create mode 100644 hw/misc/i2c-echo.c > > diff --git a/hw/misc/i2c-echo.c b/hw/misc/i2c-echo.c > new file mode 100644 > index 000000000000..27992eff8c5b > --- /dev/null > +++ b/hw/misc/i2c-echo.c > @@ -0,0 +1,162 @@ > +#include "qemu/osdep.h" > +#include "qemu/timer.h" > +#include "qemu/main-loop.h" > +#include "block/aio.h" > +#include "hw/i2c/i2c.h" > + > +#define TYPE_I2C_ECHO "i2c-echo" > +OBJECT_DECLARE_SIMPLE_TYPE(I2CEchoState, I2C_ECHO) > + > +enum i2c_echo_state { > + I2C_ECHO_STATE_IDLE, > + I2C_ECHO_STATE_REQUEST_MASTER, > + I2C_ECHO_STATE_START_SEND, > + I2C_ECHO_STATE_ACK, > +}; > + > +typedef struct I2CEchoState { > + I2CSlave parent_obj; > + > + I2CBus *bus; > + > + enum i2c_echo_state state; > + QEMUBH *bh; > + > + unsigned int pos; > + uint8_t data[3]; > +} I2CEchoState; > + > +static void i2c_echo_bh(void *opaque) > +{ > + I2CEchoState *state = opaque; > + > + switch (state->state) { > + case I2C_ECHO_STATE_IDLE: > + return; > + > + case I2C_ECHO_STATE_REQUEST_MASTER: > + i2c_bus_master(state->bus, state->bh); > + state->state = I2C_ECHO_STATE_START_SEND; > + return; > + > + case I2C_ECHO_STATE_START_SEND: > + if (i2c_start_send_async(state->bus, state->data[0])) { > + goto release_bus; > + } > + > + state->pos++; > + state->state = I2C_ECHO_STATE_ACK; > + return; > + > + case I2C_ECHO_STATE_ACK: > + if (state->pos > 2) { > + break; > + } > + > + if (i2c_send_async(state->bus, state->data[state->pos++])) { > + break; > + } > + > + return; > + } > + > + > + i2c_end_transfer(state->bus); > +release_bus: > + i2c_bus_release(state->bus); > + > + state->state = I2C_ECHO_STATE_IDLE; > +} > + > +static int i2c_echo_event(I2CSlave *s, enum i2c_event event) > +{ > + I2CEchoState *state = I2C_ECHO(s); > + > + switch (event) { > + case I2C_START_RECV: > + state->pos = 0; > + > + break; > + > + case I2C_START_SEND: > + state->pos = 0; > + > + break; > + > + case I2C_FINISH: > + state->pos = 0; > + state->state = I2C_ECHO_STATE_REQUEST_MASTER; > + qemu_bh_schedule(state->bh); > + > + break; > + > + case I2C_NACK: > + break; > + > + default: > + return -1; > + } > + > + return 0; > +} > + > +static uint8_t i2c_echo_recv(I2CSlave *s) > +{ > + I2CEchoState *state = I2C_ECHO(s); > + > + if (state->pos > 2) { > + return 0xff; > + } > + > + return state->data[state->pos++]; > +} > + > +static int i2c_echo_send(I2CSlave *s, uint8_t data) > +{ > + I2CEchoState *state = I2C_ECHO(s); > + > + if (state->pos > 2) { > + return -1; > + } > + > + state->data[state->pos++] = data; > + > + return 0; > +} > + > +static void i2c_echo_realize(DeviceState *dev, Error **errp) > +{ > + I2CEchoState *state = I2C_ECHO(dev); > + BusState *bus = qdev_get_parent_bus(dev); > + > + state->bus = I2C_BUS(bus); > + state->bh = qemu_bh_new(i2c_echo_bh, state); > + > + return; > +} > + > +static void i2c_echo_class_init(ObjectClass *oc, void *data) > +{ > + I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc); > + DeviceClass *dc = DEVICE_CLASS(oc); > + > + dc->realize = i2c_echo_realize; > + > + sc->event = i2c_echo_event; > + sc->recv = i2c_echo_recv; > + sc->send = i2c_echo_send; > +} > + > +static const TypeInfo i2c_echo = { > + .name = TYPE_I2C_ECHO, > + .parent = TYPE_I2C_SLAVE, > + .instance_size = sizeof(I2CEchoState), > + .class_init = i2c_echo_class_init, > +}; > + > +static void register_types(void) > +{ > + type_register_static(&i2c_echo); > +} > + > +type_init(register_types); > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index 0135d1975ceb..4132fe5e0bf7 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -125,6 +125,8 @@ softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) > > softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) > > +softmmu_ss.add(when: 'CONFIG_I2C', if_true: files('i2c-echo.c')) > + > specific_ss.add(when: 'CONFIG_AVR_POWER', if_true: files('avr_power.c')) > > specific_ss.add(when: 'CONFIG_IMX', if_true: files('imx6_src.c'))
diff --git a/hw/misc/i2c-echo.c b/hw/misc/i2c-echo.c new file mode 100644 index 000000000000..27992eff8c5b --- /dev/null +++ b/hw/misc/i2c-echo.c @@ -0,0 +1,162 @@ +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "qemu/main-loop.h" +#include "block/aio.h" +#include "hw/i2c/i2c.h" + +#define TYPE_I2C_ECHO "i2c-echo" +OBJECT_DECLARE_SIMPLE_TYPE(I2CEchoState, I2C_ECHO) + +enum i2c_echo_state { + I2C_ECHO_STATE_IDLE, + I2C_ECHO_STATE_REQUEST_MASTER, + I2C_ECHO_STATE_START_SEND, + I2C_ECHO_STATE_ACK, +}; + +typedef struct I2CEchoState { + I2CSlave parent_obj; + + I2CBus *bus; + + enum i2c_echo_state state; + QEMUBH *bh; + + unsigned int pos; + uint8_t data[3]; +} I2CEchoState; + +static void i2c_echo_bh(void *opaque) +{ + I2CEchoState *state = opaque; + + switch (state->state) { + case I2C_ECHO_STATE_IDLE: + return; + + case I2C_ECHO_STATE_REQUEST_MASTER: + i2c_bus_master(state->bus, state->bh); + state->state = I2C_ECHO_STATE_START_SEND; + return; + + case I2C_ECHO_STATE_START_SEND: + if (i2c_start_send_async(state->bus, state->data[0])) { + goto release_bus; + } + + state->pos++; + state->state = I2C_ECHO_STATE_ACK; + return; + + case I2C_ECHO_STATE_ACK: + if (state->pos > 2) { + break; + } + + if (i2c_send_async(state->bus, state->data[state->pos++])) { + break; + } + + return; + } + + + i2c_end_transfer(state->bus); +release_bus: + i2c_bus_release(state->bus); + + state->state = I2C_ECHO_STATE_IDLE; +} + +static int i2c_echo_event(I2CSlave *s, enum i2c_event event) +{ + I2CEchoState *state = I2C_ECHO(s); + + switch (event) { + case I2C_START_RECV: + state->pos = 0; + + break; + + case I2C_START_SEND: + state->pos = 0; + + break; + + case I2C_FINISH: + state->pos = 0; + state->state = I2C_ECHO_STATE_REQUEST_MASTER; + qemu_bh_schedule(state->bh); + + break; + + case I2C_NACK: + break; + + default: + return -1; + } + + return 0; +} + +static uint8_t i2c_echo_recv(I2CSlave *s) +{ + I2CEchoState *state = I2C_ECHO(s); + + if (state->pos > 2) { + return 0xff; + } + + return state->data[state->pos++]; +} + +static int i2c_echo_send(I2CSlave *s, uint8_t data) +{ + I2CEchoState *state = I2C_ECHO(s); + + if (state->pos > 2) { + return -1; + } + + state->data[state->pos++] = data; + + return 0; +} + +static void i2c_echo_realize(DeviceState *dev, Error **errp) +{ + I2CEchoState *state = I2C_ECHO(dev); + BusState *bus = qdev_get_parent_bus(dev); + + state->bus = I2C_BUS(bus); + state->bh = qemu_bh_new(i2c_echo_bh, state); + + return; +} + +static void i2c_echo_class_init(ObjectClass *oc, void *data) +{ + I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = i2c_echo_realize; + + sc->event = i2c_echo_event; + sc->recv = i2c_echo_recv; + sc->send = i2c_echo_send; +} + +static const TypeInfo i2c_echo = { + .name = TYPE_I2C_ECHO, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(I2CEchoState), + .class_init = i2c_echo_class_init, +}; + +static void register_types(void) +{ + type_register_static(&i2c_echo); +} + +type_init(register_types); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 0135d1975ceb..4132fe5e0bf7 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -125,6 +125,8 @@ softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) +softmmu_ss.add(when: 'CONFIG_I2C', if_true: files('i2c-echo.c')) + specific_ss.add(when: 'CONFIG_AVR_POWER', if_true: files('avr_power.c')) specific_ss.add(when: 'CONFIG_IMX', if_true: files('imx6_src.c'))