]>
Commit | Line | Data |
---|---|---|
267002cd FB |
1 | /* |
2 | * QEMU ADB support | |
5fafdf24 | 3 | * |
267002cd | 4 | * Copyright (c) 2004 Fabrice Bellard |
5fafdf24 | 5 | * |
267002cd FB |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
0b8fa32f | 24 | |
0430891c | 25 | #include "qemu/osdep.h" |
0d09e41a | 26 | #include "hw/input/adb.h" |
a27bd6c7 | 27 | #include "hw/qdev-properties.h" |
d6454270 | 28 | #include "migration/vmstate.h" |
0b8fa32f | 29 | #include "qemu/module.h" |
da52c083 | 30 | #include "qemu/timer.h" |
77cb0f5a | 31 | #include "adb-internal.h" |
e590e7f0 | 32 | #include "trace.h" |
267002cd | 33 | |
bec9d989 FB |
34 | /* error codes */ |
35 | #define ADB_RET_NOTPRESENT (-2) | |
36 | ||
e590e7f0 MCA |
37 | static const char *adb_commands[] = { |
38 | "RESET", "FLUSH", "(Reserved 0x2)", "(Reserved 0x3)", | |
39 | "Reserved (0x4)", "(Reserved 0x5)", "(Reserved 0x6)", "(Reserved 0x7)", | |
40 | "LISTEN r0", "LISTEN r1", "LISTEN r2", "LISTEN r3", | |
41 | "TALK r0", "TALK r1", "TALK r2", "TALK r3", | |
42 | }; | |
43 | ||
2e4a7c9c AF |
44 | static void adb_device_reset(ADBDevice *d) |
45 | { | |
dfa6ba6b | 46 | device_cold_reset(DEVICE(d)); |
2e4a7c9c AF |
47 | } |
48 | ||
d2288b75 MCA |
49 | static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, |
50 | int len) | |
267002cd FB |
51 | { |
52 | ADBDevice *d; | |
244a0ee9 | 53 | ADBDeviceClass *adc; |
3fe02cc8 | 54 | int devaddr, cmd, olen, i; |
267002cd | 55 | |
819e712b | 56 | cmd = buf[0] & 0xf; |
bec9d989 | 57 | if (cmd == ADB_BUSRESET) { |
bcaaefdb | 58 | for (i = 0; i < s->nb_devices; i++) { |
2e4a7c9c AF |
59 | d = s->devices[i]; |
60 | adb_device_reset(d); | |
bec9d989 | 61 | } |
3fe02cc8 | 62 | s->status = 0; |
bec9d989 | 63 | return 0; |
267002cd | 64 | } |
244a0ee9 MCA |
65 | |
66 | s->pending = 0; | |
67 | for (i = 0; i < s->nb_devices; i++) { | |
68 | d = s->devices[i]; | |
69 | adc = ADB_DEVICE_GET_CLASS(d); | |
70 | ||
71 | if (adc->devhasdata(d)) { | |
72 | s->pending |= (1 << d->devaddr); | |
73 | } | |
74 | } | |
75 | ||
3fe02cc8 | 76 | s->status = 0; |
bec9d989 | 77 | devaddr = buf[0] >> 4; |
bcaaefdb | 78 | for (i = 0; i < s->nb_devices; i++) { |
2e4a7c9c | 79 | d = s->devices[i]; |
244a0ee9 MCA |
80 | adc = ADB_DEVICE_GET_CLASS(d); |
81 | ||
267002cd | 82 | if (d->devaddr == devaddr) { |
3fe02cc8 MCA |
83 | olen = adc->devreq(d, obuf, buf, len); |
84 | if (!olen) { | |
85 | s->status |= ADB_STATUS_BUSTIMEOUT; | |
86 | } | |
87 | return olen; | |
267002cd FB |
88 | } |
89 | } | |
244a0ee9 | 90 | |
3fe02cc8 | 91 | s->status |= ADB_STATUS_BUSTIMEOUT; |
bec9d989 | 92 | return ADB_RET_NOTPRESENT; |
e2733d20 FB |
93 | } |
94 | ||
d2288b75 MCA |
95 | int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) |
96 | { | |
e590e7f0 MCA |
97 | int ret; |
98 | ||
99 | trace_adb_bus_request(buf[0] >> 4, adb_commands[buf[0] & 0xf], len); | |
100 | ||
913f47ef MCA |
101 | assert(s->autopoll_blocked); |
102 | ||
e590e7f0 MCA |
103 | ret = do_adb_request(s, obuf, buf, len); |
104 | ||
105 | trace_adb_bus_request_done(buf[0] >> 4, adb_commands[buf[0] & 0xf], ret); | |
106 | return ret; | |
d2288b75 MCA |
107 | } |
108 | ||
216c906e | 109 | int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) |
e2733d20 FB |
110 | { |
111 | ADBDevice *d; | |
112 | int olen, i; | |
bec9d989 | 113 | uint8_t buf[1]; |
e2733d20 FB |
114 | |
115 | olen = 0; | |
bcaaefdb MCA |
116 | for (i = 0; i < s->nb_devices; i++) { |
117 | if (s->poll_index >= s->nb_devices) { | |
e2733d20 | 118 | s->poll_index = 0; |
bcaaefdb | 119 | } |
2e4a7c9c | 120 | d = s->devices[s->poll_index]; |
216c906e HP |
121 | if ((1 << d->devaddr) & poll_mask) { |
122 | buf[0] = ADB_READREG | (d->devaddr << 4); | |
d2288b75 | 123 | olen = do_adb_request(s, obuf + 1, buf, 1); |
216c906e HP |
124 | /* if there is data, we poll again the same device */ |
125 | if (olen > 0) { | |
3fe02cc8 | 126 | s->status |= ADB_STATUS_POLLREPLY; |
216c906e HP |
127 | obuf[0] = buf[0]; |
128 | olen++; | |
3fe02cc8 | 129 | return olen; |
216c906e | 130 | } |
bec9d989 FB |
131 | } |
132 | s->poll_index++; | |
e2733d20 FB |
133 | } |
134 | return olen; | |
267002cd FB |
135 | } |
136 | ||
da52c083 MCA |
137 | void adb_set_autopoll_enabled(ADBBusState *s, bool enabled) |
138 | { | |
139 | if (s->autopoll_enabled != enabled) { | |
140 | s->autopoll_enabled = enabled; | |
141 | if (s->autopoll_enabled) { | |
142 | timer_mod(s->autopoll_timer, | |
143 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + | |
144 | s->autopoll_rate_ms); | |
145 | } else { | |
146 | timer_del(s->autopoll_timer); | |
147 | } | |
148 | } | |
149 | } | |
150 | ||
151 | void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms) | |
152 | { | |
153 | s->autopoll_rate_ms = rate_ms; | |
154 | ||
155 | if (s->autopoll_enabled) { | |
156 | timer_mod(s->autopoll_timer, | |
157 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + | |
158 | s->autopoll_rate_ms); | |
159 | } | |
160 | } | |
161 | ||
162 | void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask) | |
163 | { | |
164 | if (s->autopoll_mask != mask) { | |
165 | s->autopoll_mask = mask; | |
166 | if (s->autopoll_enabled && s->autopoll_mask) { | |
167 | timer_mod(s->autopoll_timer, | |
168 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + | |
169 | s->autopoll_rate_ms); | |
170 | } else { | |
171 | timer_del(s->autopoll_timer); | |
172 | } | |
173 | } | |
174 | } | |
175 | ||
4e5df036 MCA |
176 | void adb_autopoll_block(ADBBusState *s) |
177 | { | |
178 | s->autopoll_blocked = true; | |
e590e7f0 | 179 | trace_adb_bus_autopoll_block(s->autopoll_blocked); |
4e5df036 MCA |
180 | |
181 | if (s->autopoll_enabled) { | |
182 | timer_del(s->autopoll_timer); | |
183 | } | |
184 | } | |
185 | ||
186 | void adb_autopoll_unblock(ADBBusState *s) | |
187 | { | |
188 | s->autopoll_blocked = false; | |
e590e7f0 | 189 | trace_adb_bus_autopoll_block(s->autopoll_blocked); |
4e5df036 MCA |
190 | |
191 | if (s->autopoll_enabled) { | |
192 | timer_mod(s->autopoll_timer, | |
193 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + | |
194 | s->autopoll_rate_ms); | |
195 | } | |
196 | } | |
197 | ||
da52c083 MCA |
198 | static void adb_autopoll(void *opaque) |
199 | { | |
200 | ADBBusState *s = opaque; | |
201 | ||
913f47ef | 202 | if (!s->autopoll_blocked) { |
e590e7f0 | 203 | trace_adb_bus_autopoll_cb(s->autopoll_mask); |
913f47ef | 204 | s->autopoll_cb(s->autopoll_cb_opaque); |
e590e7f0 | 205 | trace_adb_bus_autopoll_cb_done(s->autopoll_mask); |
913f47ef | 206 | } |
da52c083 MCA |
207 | |
208 | timer_mod(s->autopoll_timer, | |
209 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + | |
210 | s->autopoll_rate_ms); | |
211 | } | |
212 | ||
213 | void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque), | |
214 | void *opaque) | |
215 | { | |
216 | s->autopoll_cb = cb; | |
217 | s->autopoll_cb_opaque = opaque; | |
218 | } | |
219 | ||
0606b288 MCA |
220 | static const VMStateDescription vmstate_adb_bus = { |
221 | .name = "adb_bus", | |
222 | .version_id = 0, | |
223 | .minimum_version_id = 0, | |
224 | .fields = (VMStateField[]) { | |
da52c083 MCA |
225 | VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState), |
226 | VMSTATE_BOOL(autopoll_enabled, ADBBusState), | |
227 | VMSTATE_UINT8(autopoll_rate_ms, ADBBusState), | |
228 | VMSTATE_UINT16(autopoll_mask, ADBBusState), | |
4e5df036 | 229 | VMSTATE_BOOL(autopoll_blocked, ADBBusState), |
0606b288 MCA |
230 | VMSTATE_END_OF_LIST() |
231 | } | |
232 | }; | |
233 | ||
da52c083 MCA |
234 | static void adb_bus_reset(BusState *qbus) |
235 | { | |
236 | ADBBusState *adb_bus = ADB_BUS(qbus); | |
237 | ||
238 | adb_bus->autopoll_enabled = false; | |
239 | adb_bus->autopoll_mask = 0xffff; | |
240 | adb_bus->autopoll_rate_ms = 20; | |
241 | } | |
242 | ||
0606b288 MCA |
243 | static void adb_bus_realize(BusState *qbus, Error **errp) |
244 | { | |
245 | ADBBusState *adb_bus = ADB_BUS(qbus); | |
246 | ||
da52c083 MCA |
247 | adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll, |
248 | adb_bus); | |
249 | ||
0606b288 MCA |
250 | vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus); |
251 | } | |
252 | ||
253 | static void adb_bus_unrealize(BusState *qbus) | |
254 | { | |
255 | ADBBusState *adb_bus = ADB_BUS(qbus); | |
256 | ||
da52c083 MCA |
257 | timer_del(adb_bus->autopoll_timer); |
258 | ||
0606b288 MCA |
259 | vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus); |
260 | } | |
261 | ||
262 | static void adb_bus_class_init(ObjectClass *klass, void *data) | |
263 | { | |
264 | BusClass *k = BUS_CLASS(klass); | |
265 | ||
266 | k->realize = adb_bus_realize; | |
267 | k->unrealize = adb_bus_unrealize; | |
da52c083 | 268 | k->reset = adb_bus_reset; |
0606b288 MCA |
269 | } |
270 | ||
84ede329 AF |
271 | static const TypeInfo adb_bus_type_info = { |
272 | .name = TYPE_ADB_BUS, | |
273 | .parent = TYPE_BUS, | |
274 | .instance_size = sizeof(ADBBusState), | |
0606b288 | 275 | .class_init = adb_bus_class_init, |
84ede329 AF |
276 | }; |
277 | ||
77cb0f5a | 278 | const VMStateDescription vmstate_adb_device = { |
e5dffaa5 MCA |
279 | .name = "adb_device", |
280 | .version_id = 0, | |
281 | .minimum_version_id = 0, | |
282 | .fields = (VMStateField[]) { | |
283 | VMSTATE_INT32(devaddr, ADBDevice), | |
284 | VMSTATE_INT32(handler, ADBDevice), | |
285 | VMSTATE_END_OF_LIST() | |
286 | } | |
287 | }; | |
288 | ||
2e4a7c9c AF |
289 | static void adb_device_realizefn(DeviceState *dev, Error **errp) |
290 | { | |
291 | ADBDevice *d = ADB_DEVICE(dev); | |
292 | ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev)); | |
293 | ||
294 | if (bus->nb_devices >= MAX_ADB_DEVICES) { | |
295 | return; | |
296 | } | |
297 | ||
298 | bus->devices[bus->nb_devices++] = d; | |
299 | } | |
300 | ||
301 | static void adb_device_class_init(ObjectClass *oc, void *data) | |
302 | { | |
303 | DeviceClass *dc = DEVICE_CLASS(oc); | |
304 | ||
305 | dc->realize = adb_device_realizefn; | |
306 | dc->bus_type = TYPE_ADB_BUS; | |
307 | } | |
308 | ||
309 | static const TypeInfo adb_device_type_info = { | |
310 | .name = TYPE_ADB_DEVICE, | |
311 | .parent = TYPE_DEVICE, | |
7e26c92b | 312 | .class_size = sizeof(ADBDeviceClass), |
2e4a7c9c AF |
313 | .instance_size = sizeof(ADBDevice), |
314 | .abstract = true, | |
315 | .class_init = adb_device_class_init, | |
316 | }; | |
317 | ||
84ede329 AF |
318 | static void adb_register_types(void) |
319 | { | |
320 | type_register_static(&adb_bus_type_info); | |
2e4a7c9c | 321 | type_register_static(&adb_device_type_info); |
84ede329 AF |
322 | } |
323 | ||
324 | type_init(adb_register_types) |