1 | /* |
2 | * QEMU SMBus device emulation. |
3 | * |
4 | * This code is a helper for SMBus device emulation. It implements an |
5 | * I2C device inteface and runs the SMBus protocol from the device |
6 | * point of view and maps those to simple calls to emulate. |
7 | * |
8 | * Copyright (c) 2007 CodeSourcery. |
9 | * Written by Paul Brook |
10 | * |
11 | * This code is licensed under the LGPL. |
12 | */ |
13 | |
14 | /* TODO: Implement PEC. */ |
15 | |
16 | #include "qemu/osdep.h" |
17 | #include "hw/i2c/i2c.h" |
18 | #include "hw/i2c/smbus_slave.h" |
19 | #include "migration/vmstate.h" |
20 | #include "qemu/module.h" |
21 | |
22 | //#define DEBUG_SMBUS 1 |
23 | |
24 | #ifdef DEBUG_SMBUS |
25 | #define DPRINTF(fmt, ...) \ |
26 | do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) |
27 | #define BADF(fmt, ...) \ |
28 | do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) |
29 | #else |
30 | #define DPRINTF(fmt, ...) do {} while(0) |
31 | #define BADF(fmt, ...) \ |
32 | do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) |
33 | #endif |
34 | |
35 | enum { |
36 | SMBUS_IDLE, |
37 | SMBUS_WRITE_DATA, |
38 | SMBUS_READ_DATA, |
39 | SMBUS_DONE, |
40 | SMBUS_CONFUSED = -1 |
41 | }; |
42 | |
43 | static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) |
44 | { |
45 | SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); |
46 | |
47 | DPRINTF("Quick Command %d\n" , recv); |
48 | if (sc->quick_cmd) { |
49 | sc->quick_cmd(dev, recv); |
50 | } |
51 | } |
52 | |
53 | static void smbus_do_write(SMBusDevice *dev) |
54 | { |
55 | SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); |
56 | |
57 | DPRINTF("Command %d len %d\n" , dev->data_buf[0], dev->data_len); |
58 | if (sc->write_data) { |
59 | sc->write_data(dev, dev->data_buf, dev->data_len); |
60 | } |
61 | } |
62 | |
63 | static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) |
64 | { |
65 | SMBusDevice *dev = SMBUS_DEVICE(s); |
66 | |
67 | switch (event) { |
68 | case I2C_START_SEND: |
69 | switch (dev->mode) { |
70 | case SMBUS_IDLE: |
71 | DPRINTF("Incoming data\n" ); |
72 | dev->mode = SMBUS_WRITE_DATA; |
73 | break; |
74 | |
75 | default: |
76 | BADF("Unexpected send start condition in state %d\n" , dev->mode); |
77 | dev->mode = SMBUS_CONFUSED; |
78 | break; |
79 | } |
80 | break; |
81 | |
82 | case I2C_START_RECV: |
83 | switch (dev->mode) { |
84 | case SMBUS_IDLE: |
85 | DPRINTF("Read mode\n" ); |
86 | dev->mode = SMBUS_READ_DATA; |
87 | break; |
88 | |
89 | case SMBUS_WRITE_DATA: |
90 | if (dev->data_len == 0) { |
91 | BADF("Read after write with no data\n" ); |
92 | dev->mode = SMBUS_CONFUSED; |
93 | } else { |
94 | smbus_do_write(dev); |
95 | DPRINTF("Read mode\n" ); |
96 | dev->mode = SMBUS_READ_DATA; |
97 | } |
98 | break; |
99 | |
100 | default: |
101 | BADF("Unexpected recv start condition in state %d\n" , dev->mode); |
102 | dev->mode = SMBUS_CONFUSED; |
103 | break; |
104 | } |
105 | break; |
106 | |
107 | case I2C_FINISH: |
108 | if (dev->data_len == 0) { |
109 | if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) { |
110 | smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA); |
111 | } |
112 | } else { |
113 | switch (dev->mode) { |
114 | case SMBUS_WRITE_DATA: |
115 | smbus_do_write(dev); |
116 | break; |
117 | |
118 | case SMBUS_READ_DATA: |
119 | BADF("Unexpected stop during receive\n" ); |
120 | break; |
121 | |
122 | default: |
123 | /* Nothing to do. */ |
124 | break; |
125 | } |
126 | } |
127 | dev->mode = SMBUS_IDLE; |
128 | dev->data_len = 0; |
129 | break; |
130 | |
131 | case I2C_NACK: |
132 | switch (dev->mode) { |
133 | case SMBUS_DONE: |
134 | /* Nothing to do. */ |
135 | break; |
136 | |
137 | case SMBUS_READ_DATA: |
138 | dev->mode = SMBUS_DONE; |
139 | break; |
140 | |
141 | default: |
142 | BADF("Unexpected NACK in state %d\n" , dev->mode); |
143 | dev->mode = SMBUS_CONFUSED; |
144 | break; |
145 | } |
146 | } |
147 | |
148 | return 0; |
149 | } |
150 | |
151 | static uint8_t smbus_i2c_recv(I2CSlave *s) |
152 | { |
153 | SMBusDevice *dev = SMBUS_DEVICE(s); |
154 | SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); |
155 | uint8_t ret = 0xff; |
156 | |
157 | switch (dev->mode) { |
158 | case SMBUS_READ_DATA: |
159 | if (sc->receive_byte) { |
160 | ret = sc->receive_byte(dev); |
161 | } |
162 | DPRINTF("Read data %02x\n" , ret); |
163 | break; |
164 | |
165 | default: |
166 | BADF("Unexpected read in state %d\n" , dev->mode); |
167 | dev->mode = SMBUS_CONFUSED; |
168 | break; |
169 | } |
170 | |
171 | return ret; |
172 | } |
173 | |
174 | static int smbus_i2c_send(I2CSlave *s, uint8_t data) |
175 | { |
176 | SMBusDevice *dev = SMBUS_DEVICE(s); |
177 | |
178 | switch (dev->mode) { |
179 | case SMBUS_WRITE_DATA: |
180 | DPRINTF("Write data %02x\n" , data); |
181 | if (dev->data_len >= sizeof(dev->data_buf)) { |
182 | BADF("Too many bytes sent\n" ); |
183 | } else { |
184 | dev->data_buf[dev->data_len++] = data; |
185 | } |
186 | break; |
187 | |
188 | default: |
189 | BADF("Unexpected write in state %d\n" , dev->mode); |
190 | break; |
191 | } |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static void smbus_device_class_init(ObjectClass *klass, void *data) |
197 | { |
198 | I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); |
199 | |
200 | sc->event = smbus_i2c_event; |
201 | sc->recv = smbus_i2c_recv; |
202 | sc->send = smbus_i2c_send; |
203 | } |
204 | |
205 | bool smbus_vmstate_needed(SMBusDevice *dev) |
206 | { |
207 | return dev->mode != SMBUS_IDLE; |
208 | } |
209 | |
210 | const VMStateDescription vmstate_smbus_device = { |
211 | .name = TYPE_SMBUS_DEVICE, |
212 | .version_id = 1, |
213 | .minimum_version_id = 1, |
214 | .fields = (VMStateField[]) { |
215 | VMSTATE_I2C_SLAVE(i2c, SMBusDevice), |
216 | VMSTATE_INT32(mode, SMBusDevice), |
217 | VMSTATE_INT32(data_len, SMBusDevice), |
218 | VMSTATE_UINT8_ARRAY(data_buf, SMBusDevice, SMBUS_DATA_MAX_LEN), |
219 | VMSTATE_END_OF_LIST() |
220 | } |
221 | }; |
222 | |
223 | static const TypeInfo smbus_device_type_info = { |
224 | .name = TYPE_SMBUS_DEVICE, |
225 | .parent = TYPE_I2C_SLAVE, |
226 | .instance_size = sizeof(SMBusDevice), |
227 | .abstract = true, |
228 | .class_size = sizeof(SMBusDeviceClass), |
229 | .class_init = smbus_device_class_init, |
230 | }; |
231 | |
232 | static void smbus_device_register_types(void) |
233 | { |
234 | type_register_static(&smbus_device_type_info); |
235 | } |
236 | |
237 | type_init(smbus_device_register_types) |
238 | |