Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
2 changed files
with
158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters