|
@@ -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);
|