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