diff mbox series

[RFC,v2,6/6] hw/misc: add a toy i2c echo device [DO NOT PULL]

Message ID 20220601210831.67259-7-its@irrelevant.dk
State New
Headers show
Series hw/i2c: i2c slave mode support | expand

Commit Message

Klaus Jensen June 1, 2022, 9:08 p.m. UTC
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.

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

Comments

Cédric Le Goater June 2, 2022, 7:37 a.m. UTC | #1
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 mbox series

Patch

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'))