xref: /qemu/hw/input/adb.c (revision 3fe02cc8b3a5b50fad6d5e7cc3a65fd52d1fad36)
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"
32267002cdSbellard 
33bec9d989Sbellard /* error codes */
34bec9d989Sbellard #define ADB_RET_NOTPRESENT (-2)
35bec9d989Sbellard 
362e4a7c9cSAndreas Färber static void adb_device_reset(ADBDevice *d)
372e4a7c9cSAndreas Färber {
382e4a7c9cSAndreas Färber     qdev_reset_all(DEVICE(d));
392e4a7c9cSAndreas Färber }
402e4a7c9cSAndreas Färber 
41e2733d20Sbellard int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
42267002cdSbellard {
43267002cdSbellard     ADBDevice *d;
44244a0ee9SMark Cave-Ayland     ADBDeviceClass *adc;
45*3fe02cc8SMark Cave-Ayland     int devaddr, cmd, olen, i;
46267002cdSbellard 
47819e712bSbellard     cmd = buf[0] & 0xf;
48bec9d989Sbellard     if (cmd == ADB_BUSRESET) {
49bec9d989Sbellard         for (i = 0; i < s->nb_devices; i++) {
502e4a7c9cSAndreas Färber             d = s->devices[i];
512e4a7c9cSAndreas Färber             adb_device_reset(d);
52bec9d989Sbellard         }
53*3fe02cc8SMark Cave-Ayland         s->status = 0;
54bec9d989Sbellard         return 0;
55bec9d989Sbellard     }
56244a0ee9SMark Cave-Ayland 
57244a0ee9SMark Cave-Ayland     s->pending = 0;
58244a0ee9SMark Cave-Ayland     for (i = 0; i < s->nb_devices; i++) {
59244a0ee9SMark Cave-Ayland         d = s->devices[i];
60244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
61244a0ee9SMark Cave-Ayland 
62244a0ee9SMark Cave-Ayland         if (adc->devhasdata(d)) {
63244a0ee9SMark Cave-Ayland             s->pending |= (1 << d->devaddr);
64244a0ee9SMark Cave-Ayland         }
65244a0ee9SMark Cave-Ayland     }
66244a0ee9SMark Cave-Ayland 
67*3fe02cc8SMark Cave-Ayland     s->status = 0;
68819e712bSbellard     devaddr = buf[0] >> 4;
69267002cdSbellard     for (i = 0; i < s->nb_devices; i++) {
702e4a7c9cSAndreas Färber         d = s->devices[i];
71244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
72244a0ee9SMark Cave-Ayland 
73267002cdSbellard         if (d->devaddr == devaddr) {
74*3fe02cc8SMark Cave-Ayland             olen = adc->devreq(d, obuf, buf, len);
75*3fe02cc8SMark Cave-Ayland             if (!olen) {
76*3fe02cc8SMark Cave-Ayland                 s->status |= ADB_STATUS_BUSTIMEOUT;
77*3fe02cc8SMark Cave-Ayland             }
78*3fe02cc8SMark Cave-Ayland             return olen;
79267002cdSbellard         }
80267002cdSbellard     }
81244a0ee9SMark Cave-Ayland 
82*3fe02cc8SMark Cave-Ayland     s->status |= ADB_STATUS_BUSTIMEOUT;
83bec9d989Sbellard     return ADB_RET_NOTPRESENT;
84e2733d20Sbellard }
85e2733d20Sbellard 
86bec9d989Sbellard /* XXX: move that to cuda ? */
87216c906eSHervé Poussineau int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
88e2733d20Sbellard {
89e2733d20Sbellard     ADBDevice *d;
90e2733d20Sbellard     int olen, i;
91bec9d989Sbellard     uint8_t buf[1];
92e2733d20Sbellard 
93e2733d20Sbellard     olen = 0;
94e2733d20Sbellard     for (i = 0; i < s->nb_devices; i++) {
95bcaaefdbSMark Cave-Ayland         if (s->poll_index >= s->nb_devices) {
96e2733d20Sbellard             s->poll_index = 0;
97bcaaefdbSMark Cave-Ayland         }
982e4a7c9cSAndreas Färber         d = s->devices[s->poll_index];
99216c906eSHervé Poussineau         if ((1 << d->devaddr) & poll_mask) {
100bec9d989Sbellard             buf[0] = ADB_READREG | (d->devaddr << 4);
101bec9d989Sbellard             olen = adb_request(s, obuf + 1, buf, 1);
102bec9d989Sbellard             /* if there is data, we poll again the same device */
103bec9d989Sbellard             if (olen > 0) {
104*3fe02cc8SMark Cave-Ayland                 s->status |= ADB_STATUS_POLLREPLY;
105bec9d989Sbellard                 obuf[0] = buf[0];
106bec9d989Sbellard                 olen++;
107*3fe02cc8SMark Cave-Ayland                 return olen;
108e2733d20Sbellard             }
109216c906eSHervé Poussineau         }
110bec9d989Sbellard         s->poll_index++;
111bec9d989Sbellard     }
112e2733d20Sbellard     return olen;
113267002cdSbellard }
114267002cdSbellard 
115da52c083SMark Cave-Ayland void adb_set_autopoll_enabled(ADBBusState *s, bool enabled)
116da52c083SMark Cave-Ayland {
117da52c083SMark Cave-Ayland     if (s->autopoll_enabled != enabled) {
118da52c083SMark Cave-Ayland         s->autopoll_enabled = enabled;
119da52c083SMark Cave-Ayland         if (s->autopoll_enabled) {
120da52c083SMark Cave-Ayland             timer_mod(s->autopoll_timer,
121da52c083SMark Cave-Ayland                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
122da52c083SMark Cave-Ayland                       s->autopoll_rate_ms);
123da52c083SMark Cave-Ayland         } else {
124da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
125da52c083SMark Cave-Ayland         }
126da52c083SMark Cave-Ayland     }
127da52c083SMark Cave-Ayland }
128da52c083SMark Cave-Ayland 
129da52c083SMark Cave-Ayland void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms)
130da52c083SMark Cave-Ayland {
131da52c083SMark Cave-Ayland     s->autopoll_rate_ms = rate_ms;
132da52c083SMark Cave-Ayland 
133da52c083SMark Cave-Ayland     if (s->autopoll_enabled) {
134da52c083SMark Cave-Ayland         timer_mod(s->autopoll_timer,
135da52c083SMark Cave-Ayland                   qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
136da52c083SMark Cave-Ayland                   s->autopoll_rate_ms);
137da52c083SMark Cave-Ayland     }
138da52c083SMark Cave-Ayland }
139da52c083SMark Cave-Ayland 
140da52c083SMark Cave-Ayland void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask)
141da52c083SMark Cave-Ayland {
142da52c083SMark Cave-Ayland     if (s->autopoll_mask != mask) {
143da52c083SMark Cave-Ayland         s->autopoll_mask = mask;
144da52c083SMark Cave-Ayland         if (s->autopoll_enabled && s->autopoll_mask) {
145da52c083SMark Cave-Ayland             timer_mod(s->autopoll_timer,
146da52c083SMark Cave-Ayland                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
147da52c083SMark Cave-Ayland                       s->autopoll_rate_ms);
148da52c083SMark Cave-Ayland         } else {
149da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
150da52c083SMark Cave-Ayland         }
151da52c083SMark Cave-Ayland     }
152da52c083SMark Cave-Ayland }
153da52c083SMark Cave-Ayland 
154da52c083SMark Cave-Ayland static void adb_autopoll(void *opaque)
155da52c083SMark Cave-Ayland {
156da52c083SMark Cave-Ayland     ADBBusState *s = opaque;
157da52c083SMark Cave-Ayland 
158da52c083SMark Cave-Ayland     s->autopoll_cb(s->autopoll_cb_opaque);
159da52c083SMark Cave-Ayland 
160da52c083SMark Cave-Ayland     timer_mod(s->autopoll_timer,
161da52c083SMark Cave-Ayland               qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
162da52c083SMark Cave-Ayland               s->autopoll_rate_ms);
163da52c083SMark Cave-Ayland }
164da52c083SMark Cave-Ayland 
165da52c083SMark Cave-Ayland void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque),
166da52c083SMark Cave-Ayland                                     void *opaque)
167da52c083SMark Cave-Ayland {
168da52c083SMark Cave-Ayland     s->autopoll_cb = cb;
169da52c083SMark Cave-Ayland     s->autopoll_cb_opaque = opaque;
170da52c083SMark Cave-Ayland }
171da52c083SMark Cave-Ayland 
1720606b288SMark Cave-Ayland static const VMStateDescription vmstate_adb_bus = {
1730606b288SMark Cave-Ayland     .name = "adb_bus",
1740606b288SMark Cave-Ayland     .version_id = 0,
1750606b288SMark Cave-Ayland     .minimum_version_id = 0,
1760606b288SMark Cave-Ayland     .fields = (VMStateField[]) {
177da52c083SMark Cave-Ayland         VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState),
178da52c083SMark Cave-Ayland         VMSTATE_BOOL(autopoll_enabled, ADBBusState),
179da52c083SMark Cave-Ayland         VMSTATE_UINT8(autopoll_rate_ms, ADBBusState),
180da52c083SMark Cave-Ayland         VMSTATE_UINT16(autopoll_mask, ADBBusState),
1810606b288SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1820606b288SMark Cave-Ayland     }
1830606b288SMark Cave-Ayland };
1840606b288SMark Cave-Ayland 
185da52c083SMark Cave-Ayland static void adb_bus_reset(BusState *qbus)
186da52c083SMark Cave-Ayland {
187da52c083SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
188da52c083SMark Cave-Ayland 
189da52c083SMark Cave-Ayland     adb_bus->autopoll_enabled = false;
190da52c083SMark Cave-Ayland     adb_bus->autopoll_mask = 0xffff;
191da52c083SMark Cave-Ayland     adb_bus->autopoll_rate_ms = 20;
192da52c083SMark Cave-Ayland }
193da52c083SMark Cave-Ayland 
1940606b288SMark Cave-Ayland static void adb_bus_realize(BusState *qbus, Error **errp)
1950606b288SMark Cave-Ayland {
1960606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
1970606b288SMark Cave-Ayland 
198da52c083SMark Cave-Ayland     adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll,
199da52c083SMark Cave-Ayland                                            adb_bus);
200da52c083SMark Cave-Ayland 
2010606b288SMark Cave-Ayland     vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus);
2020606b288SMark Cave-Ayland }
2030606b288SMark Cave-Ayland 
2040606b288SMark Cave-Ayland static void adb_bus_unrealize(BusState *qbus)
2050606b288SMark Cave-Ayland {
2060606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
2070606b288SMark Cave-Ayland 
208da52c083SMark Cave-Ayland     timer_del(adb_bus->autopoll_timer);
209da52c083SMark Cave-Ayland 
2100606b288SMark Cave-Ayland     vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus);
2110606b288SMark Cave-Ayland }
2120606b288SMark Cave-Ayland 
2130606b288SMark Cave-Ayland static void adb_bus_class_init(ObjectClass *klass, void *data)
2140606b288SMark Cave-Ayland {
2150606b288SMark Cave-Ayland     BusClass *k = BUS_CLASS(klass);
2160606b288SMark Cave-Ayland 
2170606b288SMark Cave-Ayland     k->realize = adb_bus_realize;
2180606b288SMark Cave-Ayland     k->unrealize = adb_bus_unrealize;
219da52c083SMark Cave-Ayland     k->reset = adb_bus_reset;
2200606b288SMark Cave-Ayland }
2210606b288SMark Cave-Ayland 
22284ede329SAndreas Färber static const TypeInfo adb_bus_type_info = {
22384ede329SAndreas Färber     .name = TYPE_ADB_BUS,
22484ede329SAndreas Färber     .parent = TYPE_BUS,
22584ede329SAndreas Färber     .instance_size = sizeof(ADBBusState),
2260606b288SMark Cave-Ayland     .class_init = adb_bus_class_init,
22784ede329SAndreas Färber };
22884ede329SAndreas Färber 
22977cb0f5aSLaurent Vivier const VMStateDescription vmstate_adb_device = {
230e5dffaa5SMark Cave-Ayland     .name = "adb_device",
231e5dffaa5SMark Cave-Ayland     .version_id = 0,
232e5dffaa5SMark Cave-Ayland     .minimum_version_id = 0,
233e5dffaa5SMark Cave-Ayland     .fields = (VMStateField[]) {
234e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(devaddr, ADBDevice),
235e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(handler, ADBDevice),
236e5dffaa5SMark Cave-Ayland         VMSTATE_END_OF_LIST()
237e5dffaa5SMark Cave-Ayland     }
238e5dffaa5SMark Cave-Ayland };
239e5dffaa5SMark Cave-Ayland 
2402e4a7c9cSAndreas Färber static void adb_device_realizefn(DeviceState *dev, Error **errp)
2412e4a7c9cSAndreas Färber {
2422e4a7c9cSAndreas Färber     ADBDevice *d = ADB_DEVICE(dev);
2432e4a7c9cSAndreas Färber     ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev));
2442e4a7c9cSAndreas Färber 
2452e4a7c9cSAndreas Färber     if (bus->nb_devices >= MAX_ADB_DEVICES) {
2462e4a7c9cSAndreas Färber         return;
2472e4a7c9cSAndreas Färber     }
2482e4a7c9cSAndreas Färber 
2492e4a7c9cSAndreas Färber     bus->devices[bus->nb_devices++] = d;
2502e4a7c9cSAndreas Färber }
2512e4a7c9cSAndreas Färber 
2522e4a7c9cSAndreas Färber static void adb_device_class_init(ObjectClass *oc, void *data)
2532e4a7c9cSAndreas Färber {
2542e4a7c9cSAndreas Färber     DeviceClass *dc = DEVICE_CLASS(oc);
2552e4a7c9cSAndreas Färber 
2562e4a7c9cSAndreas Färber     dc->realize = adb_device_realizefn;
2572e4a7c9cSAndreas Färber     dc->bus_type = TYPE_ADB_BUS;
2582e4a7c9cSAndreas Färber }
2592e4a7c9cSAndreas Färber 
2602e4a7c9cSAndreas Färber static const TypeInfo adb_device_type_info = {
2612e4a7c9cSAndreas Färber     .name = TYPE_ADB_DEVICE,
2622e4a7c9cSAndreas Färber     .parent = TYPE_DEVICE,
2632e4a7c9cSAndreas Färber     .instance_size = sizeof(ADBDevice),
2642e4a7c9cSAndreas Färber     .abstract = true,
2652e4a7c9cSAndreas Färber     .class_init = adb_device_class_init,
2662e4a7c9cSAndreas Färber };
2672e4a7c9cSAndreas Färber 
26884ede329SAndreas Färber static void adb_register_types(void)
26984ede329SAndreas Färber {
27084ede329SAndreas Färber     type_register_static(&adb_bus_type_info);
2712e4a7c9cSAndreas Färber     type_register_static(&adb_device_type_info);
27284ede329SAndreas Färber }
27384ede329SAndreas Färber 
27484ede329SAndreas Färber type_init(adb_register_types)
275