xref: /qemu/hw/misc/i2c-echo.c (revision ec6f3fc3)
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 
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         break;
84 
85     case I2C_START_SEND:
86         state->pos = 0;
87 
88         break;
89 
90     case I2C_FINISH:
91         state->pos = 0;
92         state->state = I2C_ECHO_STATE_START_SEND;
93         i2c_bus_master(state->bus, state->bh);
94 
95         break;
96 
97     case I2C_NACK:
98         break;
99 
100     default:
101         return -1;
102     }
103 
104     return 0;
105 }
106 
107 static uint8_t i2c_echo_recv(I2CSlave *s)
108 {
109     I2CEchoState *state = I2C_ECHO(s);
110 
111     if (state->pos > 2) {
112         return 0xff;
113     }
114 
115     return state->data[state->pos++];
116 }
117 
118 static int i2c_echo_send(I2CSlave *s, uint8_t data)
119 {
120     I2CEchoState *state = I2C_ECHO(s);
121 
122     if (state->pos > 2) {
123         return -1;
124     }
125 
126     state->data[state->pos++] = data;
127 
128     return 0;
129 }
130 
131 static void i2c_echo_realize(DeviceState *dev, Error **errp)
132 {
133     I2CEchoState *state = I2C_ECHO(dev);
134     BusState *bus = qdev_get_parent_bus(dev);
135 
136     state->bus = I2C_BUS(bus);
137     state->bh = qemu_bh_new(i2c_echo_bh, state);
138 
139     return;
140 }
141 
142 static void i2c_echo_class_init(ObjectClass *oc, void *data)
143 {
144     I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc);
145     DeviceClass *dc = DEVICE_CLASS(oc);
146 
147     dc->realize = i2c_echo_realize;
148 
149     sc->event = i2c_echo_event;
150     sc->recv = i2c_echo_recv;
151     sc->send = i2c_echo_send;
152 }
153 
154 static const TypeInfo i2c_echo = {
155     .name = TYPE_I2C_ECHO,
156     .parent = TYPE_I2C_SLAVE,
157     .instance_size = sizeof(I2CEchoState),
158     .class_init = i2c_echo_class_init,
159 };
160 
161 static void register_types(void)
162 {
163     type_register_static(&i2c_echo);
164 }
165 
166 type_init(register_types);
167