25
25
* THE SOFTWARE.
26
26
*/
27
27
28
+ /*
29
+ * This device provides a method of performing I2C transactions with bus
30
+ * devices in QEMU through an external chardev interface.
31
+ * The device can issue read or write transactions to devices on the bus.
32
+ * This is intended to be used by OpenTitanTool, but can be driven manually
33
+ * for example with `netcat`.
34
+ *
35
+ * The protocol for issuing commands is as follows:
36
+ *
37
+ * - A write command begins with 'W', followed by an address byte to select the
38
+ * target device. All subsequent bytes are payload bytes to be sent to the
39
+ * device.
40
+ *
41
+ * - A read command begins with 'R', followed by an address byte to select the
42
+ * target device. A byte is read and written back for every subsequent 'B'
43
+ * byte.
44
+ *
45
+ * - All bytes values are encoded as a pair of case-insensitive ASCII hex
46
+ * digits.
47
+ *
48
+ * - End of a command is indicated with an 'S', this ends the transaction.
49
+ *
50
+ * - For both commands, the address byte chosen will be echoed back if a device
51
+ * with that address is found, otherwise '!!' will be sent.
52
+ *
53
+ * - For write commands, a written byte will be echoed back on ACK, otherwise
54
+ * '!!' will be sent back on NACK.
55
+ *
56
+ * - Spaces and newlines in a sent command may appear anywhere except between
57
+ * the pair of hex digits making up a byte, and are ignored. Unexpected
58
+ * characters anywhere in a command will cause the current command to end and
59
+ * command parser restarted.
60
+ *
61
+ * - There are no timing restrictions on the protocol. It is recommended to
62
+ * buffer read commands to the OT I2C target device to allow OT software to
63
+ * fill its FIFO with response data. Readiness of response should be
64
+ * indicated via another mechanism. There is no requirement to do this for
65
+ * other QEMU I2C devices.
66
+ *
67
+ * Examples:
68
+ *
69
+ * - Writing bytes 0xAB, 0xCD, 0xEF, to a device with address 0x51:
70
+ *
71
+ * W 51 ab cd ef S
72
+ *
73
+ * With the following output if the device exists and acknowledged all bytes:
74
+ *
75
+ * 51abcdef (address byte, followed by echoed payload data)
76
+ *
77
+ * - Reading 4 bytes from the device with address 0x51:
78
+ *
79
+ * R 51 B B B B S
80
+ *
81
+ * With the following example output from the device if the device exists:
82
+ *
83
+ * 51aabbccdd (address byte, followed by example read output)
84
+ */
85
+
28
86
#include "qemu/osdep.h"
87
+ #include "qemu/fifo8.h"
29
88
#include "qemu/typedefs.h"
30
89
#include "qom/object.h"
31
90
#include "chardev/char-fe.h"
32
91
#include "hw/i2c/i2c.h"
33
92
#include "hw/qdev-properties-system.h"
34
93
#include "hw/qdev-properties.h"
94
+ #include "trace.h"
35
95
36
96
#define TYPE_OT_I2C_TRANSPORT "ot-i2c-transport"
37
97
OBJECT_DECLARE_SIMPLE_TYPE (OtI2CTransportState , OT_I2C_TRANSPORT )
38
98
99
+ #define CMD_FIFO_SIZE 256u
100
+
101
+ enum i2c_cmd_parser_state {
102
+ OT_I2C_CMD_INIT ,
103
+ OT_I2C_CMD_ADDR_1 ,
104
+ OT_I2C_CMD_ADDR_2 ,
105
+ OT_I2C_CMD_PAYLOAD_1 ,
106
+ OT_I2C_CMD_PAYLOAD_2 ,
107
+ OT_I2C_CMD_BYTE ,
108
+ };
109
+
39
110
struct OtI2CTransportState {
40
111
I2CSlave parent_obj ;
41
112
113
+ Fifo8 cmd_bytes_fifo ;
114
+ enum i2c_cmd_parser_state parser_state ;
115
+ bool is_read_command ;
116
+ uint8_t byte ;
117
+
42
118
I2CBus * bus ;
43
119
CharBackend chr ;
44
120
};
45
121
122
+ static void ot_i2c_transport_put_send_failure (OtI2CTransportState * s )
123
+ {
124
+ uint8_t fmt_byte [2 ] = { '!' , '!' };
125
+ qemu_chr_fe_write (& s -> chr , fmt_byte , ARRAY_SIZE (fmt_byte ));
126
+ }
127
+
128
+ static void
129
+ ot_i2c_transport_put_formatted_byte (OtI2CTransportState * s , uint8_t data )
130
+ {
131
+ char hex [16 ] = "0123456789ABCDEF" ;
132
+ uint8_t fmt_byte [2 ] = { hex [data >> 4u ], hex [data & 0xfu ] };
133
+ qemu_chr_fe_write (& s -> chr , fmt_byte , ARRAY_SIZE (fmt_byte ));
134
+ }
135
+
136
+ static void ot_i2c_transport_start_transfer (OtI2CTransportState * s )
137
+ {
138
+ if (i2c_start_transfer (s -> bus , s -> byte , s -> is_read_command ) != 0 ) {
139
+ ot_i2c_transport_put_send_failure (s );
140
+ } else {
141
+ ot_i2c_transport_put_formatted_byte (s , s -> byte );
142
+ }
143
+ }
144
+
145
+ static void ot_i2c_transport_do_read (OtI2CTransportState * s )
146
+ {
147
+ ot_i2c_transport_put_formatted_byte (s , i2c_recv (s -> bus ));
148
+ }
149
+
150
+ static void ot_i2c_transport_do_write (OtI2CTransportState * s )
151
+ {
152
+ if (i2c_send (s -> bus , s -> byte ) != 0 ) {
153
+ ot_i2c_transport_put_send_failure (s );
154
+ } else {
155
+ ot_i2c_transport_put_formatted_byte (s , s -> byte );
156
+ }
157
+ }
158
+
159
+ static void ot_i2c_transport_command_byte (OtI2CTransportState * s , uint8_t byte )
160
+ {
161
+ int nibble ;
162
+ switch (s -> parser_state ) {
163
+ case OT_I2C_CMD_INIT :
164
+ // parse either a R or W for a read or write command
165
+ if (byte != 'R' && byte != 'W' ) {
166
+ break ;
167
+ }
168
+ s -> is_read_command = byte == 'R' ;
169
+ s -> byte = 0u ;
170
+ s -> parser_state = OT_I2C_CMD_ADDR_1 ;
171
+ break ;
172
+ case OT_I2C_CMD_ADDR_1 :
173
+ // parse first nibble of address byte
174
+ if (byte == '\n' || byte == ' ' ) {
175
+ break ;
176
+ }
177
+ nibble = g_ascii_xdigit_value (byte );
178
+ if (nibble < 0 ) {
179
+ s -> parser_state = OT_I2C_CMD_INIT ;
180
+ break ;
181
+ }
182
+ s -> byte = (uint8_t )nibble ;
183
+ s -> parser_state = OT_I2C_CMD_ADDR_2 ;
184
+ break ;
185
+ case OT_I2C_CMD_ADDR_2 :
186
+ // parse second nibble of address byte
187
+ nibble = g_ascii_xdigit_value (byte );
188
+ if (nibble < 0 ) {
189
+ s -> parser_state = OT_I2C_CMD_INIT ;
190
+ break ;
191
+ }
192
+ s -> byte = (s -> byte << 4u ) | nibble ;
193
+ ot_i2c_transport_start_transfer (s );
194
+ s -> parser_state =
195
+ s -> is_read_command ? OT_I2C_CMD_BYTE : OT_I2C_CMD_PAYLOAD_1 ;
196
+ break ;
197
+ case OT_I2C_CMD_PAYLOAD_1 :
198
+ // parse first nibble of write payload byte
199
+ if (byte == '\n' || byte == ' ' ) {
200
+ break ;
201
+ }
202
+ // signal no more bytes to write with an S
203
+ if (byte == 'S' ) {
204
+ s -> parser_state = OT_I2C_CMD_INIT ;
205
+ i2c_end_transfer (s -> bus );
206
+ break ;
207
+ }
208
+ nibble = g_ascii_xdigit_value (byte );
209
+ if (nibble < 0 ) {
210
+ s -> parser_state = OT_I2C_CMD_INIT ;
211
+ i2c_end_transfer (s -> bus );
212
+ break ;
213
+ }
214
+ s -> byte = (uint8_t )nibble ;
215
+ s -> parser_state = OT_I2C_CMD_PAYLOAD_2 ;
216
+ break ;
217
+ case OT_I2C_CMD_PAYLOAD_2 :
218
+ // parse second nibble of write payload byte
219
+ nibble = g_ascii_xdigit_value (byte );
220
+ if (nibble < 0 ) {
221
+ s -> parser_state = OT_I2C_CMD_INIT ;
222
+ i2c_end_transfer (s -> bus );
223
+ break ;
224
+ }
225
+ s -> byte = (s -> byte << 4u ) | nibble ;
226
+ ot_i2c_transport_do_write (s );
227
+ s -> parser_state = OT_I2C_CMD_PAYLOAD_1 ;
228
+ break ;
229
+ case OT_I2C_CMD_BYTE :
230
+ // parse a B to read a byte from the I2C bus
231
+ if (byte == '\n' || byte == ' ' ) {
232
+ break ;
233
+ }
234
+ if (byte == 'B' ) {
235
+ ot_i2c_transport_do_read (s );
236
+ s -> parser_state = OT_I2C_CMD_BYTE ;
237
+ } else {
238
+ i2c_end_transfer (s -> bus );
239
+ s -> parser_state = OT_I2C_CMD_INIT ;
240
+ }
241
+ break ;
242
+ }
243
+ }
244
+
46
245
static int ot_i2c_transport_can_receive (void * opaque )
47
246
{
48
- ( void ) opaque ;
49
- return 1 ;
247
+ OtI2CTransportState * s = OT_I2C_TRANSPORT ( opaque ) ;
248
+ return fifo8_num_free ( & s -> cmd_bytes_fifo ) ;
50
249
}
51
250
52
251
static void ot_i2c_transport_receive (void * opaque , const uint8_t * buf , int size )
53
252
{
54
- (void )opaque ;
55
- (void )buf ;
56
- (void )size ;
253
+ OtI2CTransportState * s = OT_I2C_TRANSPORT (opaque );
254
+ g_assert (fifo8_num_free (& s -> cmd_bytes_fifo ) >= size );
255
+ fifo8_push_all (& s -> cmd_bytes_fifo , buf , size );
256
+
257
+ // Drive the command state machine with each byte
258
+ while (!fifo8_is_empty (& s -> cmd_bytes_fifo )) {
259
+ uint8_t byte = fifo8_pop (& s -> cmd_bytes_fifo );
260
+ ot_i2c_transport_command_byte (s , byte );
261
+ }
57
262
}
58
263
59
264
static void ot_i2c_transport_realize (DeviceState * dev , Error * * errp )
@@ -69,7 +274,10 @@ static void ot_i2c_transport_realize(DeviceState *dev, Error **errp)
69
274
static void ot_i2c_transport_init (Object * obj )
70
275
{
71
276
OtI2CTransportState * s = OT_I2C_TRANSPORT (obj );
72
- (void )s ;
277
+ fifo8_create (& s -> cmd_bytes_fifo , CMD_FIFO_SIZE );
278
+ s -> parser_state = OT_I2C_CMD_INIT ;
279
+ s -> is_read_command = false;
280
+ s -> byte = 0u ;
73
281
}
74
282
75
283
static Property ot_i2c_transport_properties [] = {
0 commit comments