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