xref: /openbmc/qemu/hw/misc/i2c-echo.c (revision bbcad04e)
1 #include "qemu/osdep.h"
2 #include "qemu/timer.h"
3 #include "qemu/main-loop.h"
4 #include "block/aio.h"
5 #include "hw/i2c/i2c.h"
6 
7 #define TYPE_I2C_ECHO "i2c-echo"
8 OBJECT_DECLARE_SIMPLE_TYPE(I2CEchoState, I2C_ECHO)
9 
10 enum i2c_echo_state {
11     I2C_ECHO_STATE_IDLE,
12     I2C_ECHO_STATE_REQUEST_MASTER,
13     I2C_ECHO_STATE_START_SEND,
14     I2C_ECHO_STATE_ACK,
15 };
16 
17 typedef struct I2CEchoState {
18     I2CSlave parent_obj;
19 
20     I2CBus *bus;
21 
22     enum i2c_echo_state state;
23     QEMUBH *bh;
24 
25     unsigned int pos;
26     uint8_t data[3];
27 } I2CEchoState;
28 
29 static void i2c_echo_bh(void *opaque)
30 {
31     I2CEchoState *state = opaque;
32 
33     switch (state->state) {
34     case I2C_ECHO_STATE_IDLE:
35         return;
36 
37     case I2C_ECHO_STATE_REQUEST_MASTER:
38         i2c_bus_master(state->bus, state->bh);
39         state->state = I2C_ECHO_STATE_START_SEND;
40         return;
41 
42     case I2C_ECHO_STATE_START_SEND:
43         if (i2c_start_send_async(state->bus, state->data[0])) {
44             goto release_bus;
45         }
46 
47         state->pos++;
48         state->state = I2C_ECHO_STATE_ACK;
49         return;
50 
51     case I2C_ECHO_STATE_ACK:
52         if (state->pos > 2) {
53             break;
54         }
55 
56         if (i2c_send_async(state->bus, state->data[state->pos++])) {
57             break;
58         }
59 
60         return;
61     }
62 
63 
64     i2c_end_transfer(state->bus);
65 release_bus:
66     i2c_bus_release(state->bus);
67 
68     state->state = I2C_ECHO_STATE_IDLE;
69 }
70 
71 static int i2c_echo_event(I2CSlave *s, enum i2c_event event)
72 {
73     I2CEchoState *state = I2C_ECHO(s);
74 
75     switch (event) {
76     case I2C_START_RECV:
77         state->pos = 0;
78 
79         break;
80 
81     case I2C_START_SEND:
82         state->pos = 0;
83 
84         break;
85 
86     case I2C_FINISH:
87         state->pos = 0;
88         state->state = I2C_ECHO_STATE_REQUEST_MASTER;
89         qemu_bh_schedule(state->bh);
90 
91         break;
92 
93     case I2C_NACK:
94         break;
95 
96     default:
97         return -1;
98     }
99 
100     return 0;
101 }
102 
103 static uint8_t i2c_echo_recv(I2CSlave *s)
104 {
105     I2CEchoState *state = I2C_ECHO(s);
106 
107     if (state->pos > 2) {
108         return 0xff;
109     }
110 
111     return state->data[state->pos++];
112 }
113 
114 static int i2c_echo_send(I2CSlave *s, uint8_t data)
115 {
116     I2CEchoState *state = I2C_ECHO(s);
117 
118     if (state->pos > 2) {
119         return -1;
120     }
121 
122     state->data[state->pos++] = data;
123 
124     return 0;
125 }
126 
127 static void i2c_echo_realize(DeviceState *dev, Error **errp)
128 {
129     I2CEchoState *state = I2C_ECHO(dev);
130     BusState *bus = qdev_get_parent_bus(dev);
131 
132     state->bus = I2C_BUS(bus);
133     state->bh = qemu_bh_new(i2c_echo_bh, state);
134 
135     return;
136 }
137 
138 static void i2c_echo_class_init(ObjectClass *oc, void *data)
139 {
140     I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc);
141     DeviceClass *dc = DEVICE_CLASS(oc);
142 
143     dc->realize = i2c_echo_realize;
144 
145     sc->event = i2c_echo_event;
146     sc->recv = i2c_echo_recv;
147     sc->send = i2c_echo_send;
148 }
149 
150 static const TypeInfo i2c_echo = {
151     .name = TYPE_I2C_ECHO,
152     .parent = TYPE_I2C_SLAVE,
153     .instance_size = sizeof(I2CEchoState),
154     .class_init = i2c_echo_class_init,
155 };
156 
157 static void register_types(void)
158 {
159     type_register_static(&i2c_echo);
160 }
161 
162 type_init(register_types);
163