xref: /qemu/hw/input/adb.c (revision 12d1a768bdfea6e27a3a829228840d72507613a1)
1267002cdSbellard /*
2267002cdSbellard  * QEMU ADB support
3267002cdSbellard  *
4267002cdSbellard  * Copyright (c) 2004 Fabrice Bellard
5267002cdSbellard  *
6267002cdSbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
7267002cdSbellard  * of this software and associated documentation files (the "Software"), to deal
8267002cdSbellard  * in the Software without restriction, including without limitation the rights
9267002cdSbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10267002cdSbellard  * copies of the Software, and to permit persons to whom the Software is
11267002cdSbellard  * furnished to do so, subject to the following conditions:
12267002cdSbellard  *
13267002cdSbellard  * The above copyright notice and this permission notice shall be included in
14267002cdSbellard  * all copies or substantial portions of the Software.
15267002cdSbellard  *
16267002cdSbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17267002cdSbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18267002cdSbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19267002cdSbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20267002cdSbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21267002cdSbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22267002cdSbellard  * THE SOFTWARE.
23267002cdSbellard  */
240b8fa32fSMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
260d09e41aSPaolo Bonzini #include "hw/input/adb.h"
27a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
28d6454270SMarkus Armbruster #include "migration/vmstate.h"
290b8fa32fSMarkus Armbruster #include "qemu/module.h"
30da52c083SMark Cave-Ayland #include "qemu/timer.h"
3177cb0f5aSLaurent Vivier #include "adb-internal.h"
32e590e7f0SMark Cave-Ayland #include "trace.h"
33267002cdSbellard 
34bec9d989Sbellard /* error codes */
35bec9d989Sbellard #define ADB_RET_NOTPRESENT (-2)
36bec9d989Sbellard 
37e590e7f0SMark Cave-Ayland static const char *adb_commands[] = {
38e590e7f0SMark Cave-Ayland     "RESET", "FLUSH", "(Reserved 0x2)", "(Reserved 0x3)",
39e590e7f0SMark Cave-Ayland     "Reserved (0x4)", "(Reserved 0x5)", "(Reserved 0x6)", "(Reserved 0x7)",
40e590e7f0SMark Cave-Ayland     "LISTEN r0", "LISTEN r1", "LISTEN r2", "LISTEN r3",
41e590e7f0SMark Cave-Ayland     "TALK r0", "TALK r1", "TALK r2", "TALK r3",
42e590e7f0SMark Cave-Ayland };
43e590e7f0SMark Cave-Ayland 
442e4a7c9cSAndreas Färber static void adb_device_reset(ADBDevice *d)
452e4a7c9cSAndreas Färber {
46dfa6ba6bSPeter Maydell     device_cold_reset(DEVICE(d));
472e4a7c9cSAndreas Färber }
482e4a7c9cSAndreas Färber 
49d2288b75SMark Cave-Ayland static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf,
50d2288b75SMark Cave-Ayland                           int len)
51267002cdSbellard {
52267002cdSbellard     ADBDevice *d;
53244a0ee9SMark Cave-Ayland     ADBDeviceClass *adc;
543fe02cc8SMark Cave-Ayland     int devaddr, cmd, olen, i;
55267002cdSbellard 
56819e712bSbellard     cmd = buf[0] & 0xf;
57bec9d989Sbellard     if (cmd == ADB_BUSRESET) {
58bec9d989Sbellard         for (i = 0; i < s->nb_devices; i++) {
592e4a7c9cSAndreas Färber             d = s->devices[i];
602e4a7c9cSAndreas Färber             adb_device_reset(d);
61bec9d989Sbellard         }
623fe02cc8SMark Cave-Ayland         s->status = 0;
63bec9d989Sbellard         return 0;
64bec9d989Sbellard     }
65244a0ee9SMark Cave-Ayland 
66244a0ee9SMark Cave-Ayland     s->pending = 0;
67244a0ee9SMark Cave-Ayland     for (i = 0; i < s->nb_devices; i++) {
68244a0ee9SMark Cave-Ayland         d = s->devices[i];
69244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
70244a0ee9SMark Cave-Ayland 
71244a0ee9SMark Cave-Ayland         if (adc->devhasdata(d)) {
72244a0ee9SMark Cave-Ayland             s->pending |= (1 << d->devaddr);
73244a0ee9SMark Cave-Ayland         }
74244a0ee9SMark Cave-Ayland     }
75244a0ee9SMark Cave-Ayland 
763fe02cc8SMark Cave-Ayland     s->status = 0;
77819e712bSbellard     devaddr = buf[0] >> 4;
78267002cdSbellard     for (i = 0; i < s->nb_devices; i++) {
792e4a7c9cSAndreas Färber         d = s->devices[i];
80244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
81244a0ee9SMark Cave-Ayland 
82267002cdSbellard         if (d->devaddr == devaddr) {
833fe02cc8SMark Cave-Ayland             olen = adc->devreq(d, obuf, buf, len);
843fe02cc8SMark Cave-Ayland             if (!olen) {
853fe02cc8SMark Cave-Ayland                 s->status |= ADB_STATUS_BUSTIMEOUT;
863fe02cc8SMark Cave-Ayland             }
873fe02cc8SMark Cave-Ayland             return olen;
88267002cdSbellard         }
89267002cdSbellard     }
90244a0ee9SMark Cave-Ayland 
913fe02cc8SMark Cave-Ayland     s->status |= ADB_STATUS_BUSTIMEOUT;
92bec9d989Sbellard     return ADB_RET_NOTPRESENT;
93e2733d20Sbellard }
94e2733d20Sbellard 
95d2288b75SMark Cave-Ayland int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
96d2288b75SMark Cave-Ayland {
97e590e7f0SMark Cave-Ayland     int ret;
98e590e7f0SMark Cave-Ayland 
99e590e7f0SMark Cave-Ayland     trace_adb_bus_request(buf[0] >> 4, adb_commands[buf[0] & 0xf], len);
100e590e7f0SMark Cave-Ayland 
101913f47efSMark Cave-Ayland     assert(s->autopoll_blocked);
102913f47efSMark Cave-Ayland 
103e590e7f0SMark Cave-Ayland     ret = do_adb_request(s, obuf, buf, len);
104e590e7f0SMark Cave-Ayland 
105e590e7f0SMark Cave-Ayland     trace_adb_bus_request_done(buf[0] >> 4, adb_commands[buf[0] & 0xf], ret);
106e590e7f0SMark Cave-Ayland     return ret;
107d2288b75SMark Cave-Ayland }
108d2288b75SMark Cave-Ayland 
109216c906eSHervé Poussineau int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
110e2733d20Sbellard {
111e2733d20Sbellard     ADBDevice *d;
112e2733d20Sbellard     int olen, i;
113bec9d989Sbellard     uint8_t buf[1];
114e2733d20Sbellard 
115e2733d20Sbellard     olen = 0;
116e2733d20Sbellard     for (i = 0; i < s->nb_devices; i++) {
117bcaaefdbSMark Cave-Ayland         if (s->poll_index >= s->nb_devices) {
118e2733d20Sbellard             s->poll_index = 0;
119bcaaefdbSMark Cave-Ayland         }
1202e4a7c9cSAndreas Färber         d = s->devices[s->poll_index];
121216c906eSHervé Poussineau         if ((1 << d->devaddr) & poll_mask) {
122bec9d989Sbellard             buf[0] = ADB_READREG | (d->devaddr << 4);
123d2288b75SMark Cave-Ayland             olen = do_adb_request(s, obuf + 1, buf, 1);
124bec9d989Sbellard             /* if there is data, we poll again the same device */
125bec9d989Sbellard             if (olen > 0) {
1263fe02cc8SMark Cave-Ayland                 s->status |= ADB_STATUS_POLLREPLY;
127bec9d989Sbellard                 obuf[0] = buf[0];
128bec9d989Sbellard                 olen++;
1293fe02cc8SMark Cave-Ayland                 return olen;
130e2733d20Sbellard             }
131216c906eSHervé Poussineau         }
132bec9d989Sbellard         s->poll_index++;
133bec9d989Sbellard     }
134e2733d20Sbellard     return olen;
135267002cdSbellard }
136267002cdSbellard 
137da52c083SMark Cave-Ayland void adb_set_autopoll_enabled(ADBBusState *s, bool enabled)
138da52c083SMark Cave-Ayland {
139da52c083SMark Cave-Ayland     if (s->autopoll_enabled != enabled) {
140da52c083SMark Cave-Ayland         s->autopoll_enabled = enabled;
141da52c083SMark Cave-Ayland         if (s->autopoll_enabled) {
142da52c083SMark Cave-Ayland             timer_mod(s->autopoll_timer,
143da52c083SMark Cave-Ayland                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
144da52c083SMark Cave-Ayland                       s->autopoll_rate_ms);
145da52c083SMark Cave-Ayland         } else {
146da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
147da52c083SMark Cave-Ayland         }
148da52c083SMark Cave-Ayland     }
149da52c083SMark Cave-Ayland }
150da52c083SMark Cave-Ayland 
151da52c083SMark Cave-Ayland void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms)
152da52c083SMark Cave-Ayland {
153da52c083SMark Cave-Ayland     s->autopoll_rate_ms = rate_ms;
154da52c083SMark Cave-Ayland 
155da52c083SMark Cave-Ayland     if (s->autopoll_enabled) {
156da52c083SMark Cave-Ayland         timer_mod(s->autopoll_timer,
157da52c083SMark Cave-Ayland                   qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
158da52c083SMark Cave-Ayland                   s->autopoll_rate_ms);
159da52c083SMark Cave-Ayland     }
160da52c083SMark Cave-Ayland }
161da52c083SMark Cave-Ayland 
162da52c083SMark Cave-Ayland void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask)
163da52c083SMark Cave-Ayland {
164da52c083SMark Cave-Ayland     if (s->autopoll_mask != mask) {
165da52c083SMark Cave-Ayland         s->autopoll_mask = mask;
166da52c083SMark Cave-Ayland         if (s->autopoll_enabled && s->autopoll_mask) {
167da52c083SMark Cave-Ayland             timer_mod(s->autopoll_timer,
168da52c083SMark Cave-Ayland                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
169da52c083SMark Cave-Ayland                       s->autopoll_rate_ms);
170da52c083SMark Cave-Ayland         } else {
171da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
172da52c083SMark Cave-Ayland         }
173da52c083SMark Cave-Ayland     }
174da52c083SMark Cave-Ayland }
175da52c083SMark Cave-Ayland 
1764e5df036SMark Cave-Ayland void adb_autopoll_block(ADBBusState *s)
1774e5df036SMark Cave-Ayland {
1784e5df036SMark Cave-Ayland     s->autopoll_blocked = true;
179e590e7f0SMark Cave-Ayland     trace_adb_bus_autopoll_block(s->autopoll_blocked);
1804e5df036SMark Cave-Ayland 
1814e5df036SMark Cave-Ayland     if (s->autopoll_enabled) {
1824e5df036SMark Cave-Ayland         timer_del(s->autopoll_timer);
1834e5df036SMark Cave-Ayland     }
1844e5df036SMark Cave-Ayland }
1854e5df036SMark Cave-Ayland 
1864e5df036SMark Cave-Ayland void adb_autopoll_unblock(ADBBusState *s)
1874e5df036SMark Cave-Ayland {
1884e5df036SMark Cave-Ayland     s->autopoll_blocked = false;
189e590e7f0SMark Cave-Ayland     trace_adb_bus_autopoll_block(s->autopoll_blocked);
1904e5df036SMark Cave-Ayland 
1914e5df036SMark Cave-Ayland     if (s->autopoll_enabled) {
1924e5df036SMark Cave-Ayland         timer_mod(s->autopoll_timer,
1934e5df036SMark Cave-Ayland                   qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
1944e5df036SMark Cave-Ayland                   s->autopoll_rate_ms);
1954e5df036SMark Cave-Ayland     }
1964e5df036SMark Cave-Ayland }
1974e5df036SMark Cave-Ayland 
198da52c083SMark Cave-Ayland static void adb_autopoll(void *opaque)
199da52c083SMark Cave-Ayland {
200da52c083SMark Cave-Ayland     ADBBusState *s = opaque;
201da52c083SMark Cave-Ayland 
202913f47efSMark Cave-Ayland     if (!s->autopoll_blocked) {
203e590e7f0SMark Cave-Ayland         trace_adb_bus_autopoll_cb(s->autopoll_mask);
204da52c083SMark Cave-Ayland         s->autopoll_cb(s->autopoll_cb_opaque);
205e590e7f0SMark Cave-Ayland         trace_adb_bus_autopoll_cb_done(s->autopoll_mask);
206913f47efSMark Cave-Ayland     }
207da52c083SMark Cave-Ayland 
208da52c083SMark Cave-Ayland     timer_mod(s->autopoll_timer,
209da52c083SMark Cave-Ayland               qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
210da52c083SMark Cave-Ayland               s->autopoll_rate_ms);
211da52c083SMark Cave-Ayland }
212da52c083SMark Cave-Ayland 
213da52c083SMark Cave-Ayland void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque),
214da52c083SMark Cave-Ayland                                     void *opaque)
215da52c083SMark Cave-Ayland {
216da52c083SMark Cave-Ayland     s->autopoll_cb = cb;
217da52c083SMark Cave-Ayland     s->autopoll_cb_opaque = opaque;
218da52c083SMark Cave-Ayland }
219da52c083SMark Cave-Ayland 
2200606b288SMark Cave-Ayland static const VMStateDescription vmstate_adb_bus = {
2210606b288SMark Cave-Ayland     .name = "adb_bus",
2220606b288SMark Cave-Ayland     .version_id = 0,
2230606b288SMark Cave-Ayland     .minimum_version_id = 0,
224af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
225da52c083SMark Cave-Ayland         VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState),
226da52c083SMark Cave-Ayland         VMSTATE_BOOL(autopoll_enabled, ADBBusState),
227da52c083SMark Cave-Ayland         VMSTATE_UINT8(autopoll_rate_ms, ADBBusState),
228da52c083SMark Cave-Ayland         VMSTATE_UINT16(autopoll_mask, ADBBusState),
2294e5df036SMark Cave-Ayland         VMSTATE_BOOL(autopoll_blocked, ADBBusState),
2300606b288SMark Cave-Ayland         VMSTATE_END_OF_LIST()
2310606b288SMark Cave-Ayland     }
2320606b288SMark Cave-Ayland };
2330606b288SMark Cave-Ayland 
234ad80e367SPeter Maydell static void adb_bus_reset_hold(Object *obj, ResetType type)
235da52c083SMark Cave-Ayland {
2365c9ca5d7SPeter Maydell     ADBBusState *adb_bus = ADB_BUS(obj);
237da52c083SMark Cave-Ayland 
238da52c083SMark Cave-Ayland     adb_bus->autopoll_enabled = false;
239da52c083SMark Cave-Ayland     adb_bus->autopoll_mask = 0xffff;
240da52c083SMark Cave-Ayland     adb_bus->autopoll_rate_ms = 20;
241da52c083SMark Cave-Ayland }
242da52c083SMark Cave-Ayland 
2430606b288SMark Cave-Ayland static void adb_bus_realize(BusState *qbus, Error **errp)
2440606b288SMark Cave-Ayland {
2450606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
2460606b288SMark Cave-Ayland 
247da52c083SMark Cave-Ayland     adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll,
248da52c083SMark Cave-Ayland                                            adb_bus);
249da52c083SMark Cave-Ayland 
25099b16e8eSJuan Quintela     vmstate_register_any(NULL, &vmstate_adb_bus, adb_bus);
2510606b288SMark Cave-Ayland }
2520606b288SMark Cave-Ayland 
2530606b288SMark Cave-Ayland static void adb_bus_unrealize(BusState *qbus)
2540606b288SMark Cave-Ayland {
2550606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
2560606b288SMark Cave-Ayland 
257da52c083SMark Cave-Ayland     timer_del(adb_bus->autopoll_timer);
258da52c083SMark Cave-Ayland 
2590606b288SMark Cave-Ayland     vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus);
2600606b288SMark Cave-Ayland }
2610606b288SMark Cave-Ayland 
262*12d1a768SPhilippe Mathieu-Daudé static void adb_bus_class_init(ObjectClass *klass, const void *data)
2630606b288SMark Cave-Ayland {
2640606b288SMark Cave-Ayland     BusClass *k = BUS_CLASS(klass);
2655c9ca5d7SPeter Maydell     ResettableClass *rc = RESETTABLE_CLASS(klass);
2660606b288SMark Cave-Ayland 
2670606b288SMark Cave-Ayland     k->realize = adb_bus_realize;
2680606b288SMark Cave-Ayland     k->unrealize = adb_bus_unrealize;
2695c9ca5d7SPeter Maydell     rc->phases.hold = adb_bus_reset_hold;
2700606b288SMark Cave-Ayland }
2710606b288SMark Cave-Ayland 
27284ede329SAndreas Färber static const TypeInfo adb_bus_type_info = {
27384ede329SAndreas Färber     .name = TYPE_ADB_BUS,
27484ede329SAndreas Färber     .parent = TYPE_BUS,
27584ede329SAndreas Färber     .instance_size = sizeof(ADBBusState),
2760606b288SMark Cave-Ayland     .class_init = adb_bus_class_init,
27784ede329SAndreas Färber };
27884ede329SAndreas Färber 
27977cb0f5aSLaurent Vivier const VMStateDescription vmstate_adb_device = {
280e5dffaa5SMark Cave-Ayland     .name = "adb_device",
281e5dffaa5SMark Cave-Ayland     .version_id = 0,
282e5dffaa5SMark Cave-Ayland     .minimum_version_id = 0,
283af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
284e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(devaddr, ADBDevice),
285e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(handler, ADBDevice),
286e5dffaa5SMark Cave-Ayland         VMSTATE_END_OF_LIST()
287e5dffaa5SMark Cave-Ayland     }
288e5dffaa5SMark Cave-Ayland };
289e5dffaa5SMark Cave-Ayland 
2902e4a7c9cSAndreas Färber static void adb_device_realizefn(DeviceState *dev, Error **errp)
2912e4a7c9cSAndreas Färber {
2922e4a7c9cSAndreas Färber     ADBDevice *d = ADB_DEVICE(dev);
2932e4a7c9cSAndreas Färber     ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev));
2942e4a7c9cSAndreas Färber 
2952e4a7c9cSAndreas Färber     if (bus->nb_devices >= MAX_ADB_DEVICES) {
2962e4a7c9cSAndreas Färber         return;
2972e4a7c9cSAndreas Färber     }
2982e4a7c9cSAndreas Färber 
2992e4a7c9cSAndreas Färber     bus->devices[bus->nb_devices++] = d;
3002e4a7c9cSAndreas Färber }
3012e4a7c9cSAndreas Färber 
302*12d1a768SPhilippe Mathieu-Daudé static void adb_device_class_init(ObjectClass *oc, const void *data)
3032e4a7c9cSAndreas Färber {
3042e4a7c9cSAndreas Färber     DeviceClass *dc = DEVICE_CLASS(oc);
3052e4a7c9cSAndreas Färber 
3062e4a7c9cSAndreas Färber     dc->realize = adb_device_realizefn;
3072e4a7c9cSAndreas Färber     dc->bus_type = TYPE_ADB_BUS;
3082e4a7c9cSAndreas Färber }
3092e4a7c9cSAndreas Färber 
3102e4a7c9cSAndreas Färber static const TypeInfo adb_device_type_info = {
3112e4a7c9cSAndreas Färber     .name = TYPE_ADB_DEVICE,
3122e4a7c9cSAndreas Färber     .parent = TYPE_DEVICE,
3137e26c92bSDavid Gibson     .class_size = sizeof(ADBDeviceClass),
3142e4a7c9cSAndreas Färber     .instance_size = sizeof(ADBDevice),
3152e4a7c9cSAndreas Färber     .abstract = true,
3162e4a7c9cSAndreas Färber     .class_init = adb_device_class_init,
3172e4a7c9cSAndreas Färber };
3182e4a7c9cSAndreas Färber 
31984ede329SAndreas Färber static void adb_register_types(void)
32084ede329SAndreas Färber {
32184ede329SAndreas Färber     type_register_static(&adb_bus_type_info);
3222e4a7c9cSAndreas Färber     type_register_static(&adb_device_type_info);
32384ede329SAndreas Färber }
32484ede329SAndreas Färber 
32584ede329SAndreas Färber type_init(adb_register_types)
326