xref: /qemu/hw/misc/i2c-echo.c (revision 01499add2ae6529589002860e1880ff193a6578a)
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  
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  
167  static void register_types(void)
168  {
169      type_register_static(&i2c_echo);
170  }
171  
172  type_init(register_types);
173