xref: /qemu/hw/misc/i2c-echo.c (revision c0fb8e88cbbfc140800e614586b446193a3fd93a)
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 
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 
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 
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 
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 
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     return;
148 }
149 
150 static void i2c_echo_class_init(ObjectClass *oc, void *data)
151 {
152     I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc);
153     DeviceClass *dc = DEVICE_CLASS(oc);
154 
155     dc->realize = i2c_echo_realize;
156 
157     sc->event = i2c_echo_event;
158     sc->recv = i2c_echo_recv;
159     sc->send = i2c_echo_send;
160 }
161 
162 static const TypeInfo i2c_echo = {
163     .name = TYPE_I2C_ECHO,
164     .parent = TYPE_I2C_SLAVE,
165     .instance_size = sizeof(I2CEchoState),
166     .class_init = i2c_echo_class_init,
167 };
168 
169 static void register_types(void)
170 {
171     type_register_static(&i2c_echo);
172 }
173 
174 type_init(register_types);
175