Skip to content

Commit

Permalink
hw/misc: add a toy i2c echo device
Browse files Browse the repository at this point in the history
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>
[ clg: integrated fixes :
  https://lore.kernel.org/qemu-devel/Y3yMKAhOkYGtnkOp@cormorant.local/ ]
Message-Id: <20220601210831.67259-7-its@irrelevant.dk>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
  • Loading branch information
birkelund authored and legoater committed Mar 2, 2023
1 parent 791cb95 commit b14037f
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 0 deletions.
156 changes: 156 additions & 0 deletions hw/misc/i2c-echo.c
@@ -0,0 +1,156 @@
#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_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_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_START_SEND;
i2c_bus_master(state->bus, 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);
2 changes: 2 additions & 0 deletions hw/misc/meson.build
Expand Up @@ -128,6 +128,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_MAC_VIA', if_true: files('mac_via.c'))
Expand Down

0 comments on commit b14037f

Please sign in to comment.