xref: /qemu/hw/input/adb.c (revision 244a0ee96545e79af7ade39b510e459ef5a1c366)
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;
44*244a0ee9SMark Cave-Ayland     ADBDeviceClass *adc;
45267002cdSbellard     int devaddr, cmd, 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         }
53bec9d989Sbellard         return 0;
54bec9d989Sbellard     }
55*244a0ee9SMark Cave-Ayland 
56*244a0ee9SMark Cave-Ayland     s->pending = 0;
57*244a0ee9SMark Cave-Ayland     for (i = 0; i < s->nb_devices; i++) {
58*244a0ee9SMark Cave-Ayland         d = s->devices[i];
59*244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
60*244a0ee9SMark Cave-Ayland 
61*244a0ee9SMark Cave-Ayland         if (adc->devhasdata(d)) {
62*244a0ee9SMark Cave-Ayland             s->pending |= (1 << d->devaddr);
63*244a0ee9SMark Cave-Ayland         }
64*244a0ee9SMark Cave-Ayland     }
65*244a0ee9SMark Cave-Ayland 
66819e712bSbellard     devaddr = buf[0] >> 4;
67267002cdSbellard     for (i = 0; i < s->nb_devices; i++) {
682e4a7c9cSAndreas Färber         d = s->devices[i];
69*244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
70*244a0ee9SMark Cave-Ayland 
71267002cdSbellard         if (d->devaddr == devaddr) {
722e4a7c9cSAndreas Färber             return adc->devreq(d, obuf, buf, len);
73267002cdSbellard         }
74267002cdSbellard     }
75*244a0ee9SMark Cave-Ayland 
76bec9d989Sbellard     return ADB_RET_NOTPRESENT;
77e2733d20Sbellard }
78e2733d20Sbellard 
79bec9d989Sbellard /* XXX: move that to cuda ? */
80216c906eSHervé Poussineau int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
81e2733d20Sbellard {
82e2733d20Sbellard     ADBDevice *d;
83e2733d20Sbellard     int olen, i;
84bec9d989Sbellard     uint8_t buf[1];
85e2733d20Sbellard 
86e2733d20Sbellard     olen = 0;
87e2733d20Sbellard     for (i = 0; i < s->nb_devices; i++) {
88bcaaefdbSMark Cave-Ayland         if (s->poll_index >= s->nb_devices) {
89e2733d20Sbellard             s->poll_index = 0;
90bcaaefdbSMark Cave-Ayland         }
912e4a7c9cSAndreas Färber         d = s->devices[s->poll_index];
92216c906eSHervé Poussineau         if ((1 << d->devaddr) & poll_mask) {
93bec9d989Sbellard             buf[0] = ADB_READREG | (d->devaddr << 4);
94bec9d989Sbellard             olen = adb_request(s, obuf + 1, buf, 1);
95bec9d989Sbellard             /* if there is data, we poll again the same device */
96bec9d989Sbellard             if (olen > 0) {
97bec9d989Sbellard                 obuf[0] = buf[0];
98bec9d989Sbellard                 olen++;
99e2733d20Sbellard                 break;
100e2733d20Sbellard             }
101216c906eSHervé Poussineau         }
102bec9d989Sbellard         s->poll_index++;
103bec9d989Sbellard     }
104e2733d20Sbellard     return olen;
105267002cdSbellard }
106267002cdSbellard 
107da52c083SMark Cave-Ayland void adb_set_autopoll_enabled(ADBBusState *s, bool enabled)
108da52c083SMark Cave-Ayland {
109da52c083SMark Cave-Ayland     if (s->autopoll_enabled != enabled) {
110da52c083SMark Cave-Ayland         s->autopoll_enabled = enabled;
111da52c083SMark Cave-Ayland         if (s->autopoll_enabled) {
112da52c083SMark Cave-Ayland             timer_mod(s->autopoll_timer,
113da52c083SMark Cave-Ayland                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
114da52c083SMark Cave-Ayland                       s->autopoll_rate_ms);
115da52c083SMark Cave-Ayland         } else {
116da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
117da52c083SMark Cave-Ayland         }
118da52c083SMark Cave-Ayland     }
119da52c083SMark Cave-Ayland }
120da52c083SMark Cave-Ayland 
121da52c083SMark Cave-Ayland void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms)
122da52c083SMark Cave-Ayland {
123da52c083SMark Cave-Ayland     s->autopoll_rate_ms = rate_ms;
124da52c083SMark Cave-Ayland 
125da52c083SMark Cave-Ayland     if (s->autopoll_enabled) {
126da52c083SMark Cave-Ayland         timer_mod(s->autopoll_timer,
127da52c083SMark Cave-Ayland                   qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
128da52c083SMark Cave-Ayland                   s->autopoll_rate_ms);
129da52c083SMark Cave-Ayland     }
130da52c083SMark Cave-Ayland }
131da52c083SMark Cave-Ayland 
132da52c083SMark Cave-Ayland void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask)
133da52c083SMark Cave-Ayland {
134da52c083SMark Cave-Ayland     if (s->autopoll_mask != mask) {
135da52c083SMark Cave-Ayland         s->autopoll_mask = mask;
136da52c083SMark Cave-Ayland         if (s->autopoll_enabled && s->autopoll_mask) {
137da52c083SMark Cave-Ayland             timer_mod(s->autopoll_timer,
138da52c083SMark Cave-Ayland                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
139da52c083SMark Cave-Ayland                       s->autopoll_rate_ms);
140da52c083SMark Cave-Ayland         } else {
141da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
142da52c083SMark Cave-Ayland         }
143da52c083SMark Cave-Ayland     }
144da52c083SMark Cave-Ayland }
145da52c083SMark Cave-Ayland 
146da52c083SMark Cave-Ayland static void adb_autopoll(void *opaque)
147da52c083SMark Cave-Ayland {
148da52c083SMark Cave-Ayland     ADBBusState *s = opaque;
149da52c083SMark Cave-Ayland 
150da52c083SMark Cave-Ayland     s->autopoll_cb(s->autopoll_cb_opaque);
151da52c083SMark Cave-Ayland 
152da52c083SMark Cave-Ayland     timer_mod(s->autopoll_timer,
153da52c083SMark Cave-Ayland               qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
154da52c083SMark Cave-Ayland               s->autopoll_rate_ms);
155da52c083SMark Cave-Ayland }
156da52c083SMark Cave-Ayland 
157da52c083SMark Cave-Ayland void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque),
158da52c083SMark Cave-Ayland                                     void *opaque)
159da52c083SMark Cave-Ayland {
160da52c083SMark Cave-Ayland     s->autopoll_cb = cb;
161da52c083SMark Cave-Ayland     s->autopoll_cb_opaque = opaque;
162da52c083SMark Cave-Ayland }
163da52c083SMark Cave-Ayland 
1640606b288SMark Cave-Ayland static const VMStateDescription vmstate_adb_bus = {
1650606b288SMark Cave-Ayland     .name = "adb_bus",
1660606b288SMark Cave-Ayland     .version_id = 0,
1670606b288SMark Cave-Ayland     .minimum_version_id = 0,
1680606b288SMark Cave-Ayland     .fields = (VMStateField[]) {
169da52c083SMark Cave-Ayland         VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState),
170da52c083SMark Cave-Ayland         VMSTATE_BOOL(autopoll_enabled, ADBBusState),
171da52c083SMark Cave-Ayland         VMSTATE_UINT8(autopoll_rate_ms, ADBBusState),
172da52c083SMark Cave-Ayland         VMSTATE_UINT16(autopoll_mask, ADBBusState),
1730606b288SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1740606b288SMark Cave-Ayland     }
1750606b288SMark Cave-Ayland };
1760606b288SMark Cave-Ayland 
177da52c083SMark Cave-Ayland static void adb_bus_reset(BusState *qbus)
178da52c083SMark Cave-Ayland {
179da52c083SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
180da52c083SMark Cave-Ayland 
181da52c083SMark Cave-Ayland     adb_bus->autopoll_enabled = false;
182da52c083SMark Cave-Ayland     adb_bus->autopoll_mask = 0xffff;
183da52c083SMark Cave-Ayland     adb_bus->autopoll_rate_ms = 20;
184da52c083SMark Cave-Ayland }
185da52c083SMark Cave-Ayland 
1860606b288SMark Cave-Ayland static void adb_bus_realize(BusState *qbus, Error **errp)
1870606b288SMark Cave-Ayland {
1880606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
1890606b288SMark Cave-Ayland 
190da52c083SMark Cave-Ayland     adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll,
191da52c083SMark Cave-Ayland                                            adb_bus);
192da52c083SMark Cave-Ayland 
1930606b288SMark Cave-Ayland     vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus);
1940606b288SMark Cave-Ayland }
1950606b288SMark Cave-Ayland 
1960606b288SMark Cave-Ayland static void adb_bus_unrealize(BusState *qbus)
1970606b288SMark Cave-Ayland {
1980606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
1990606b288SMark Cave-Ayland 
200da52c083SMark Cave-Ayland     timer_del(adb_bus->autopoll_timer);
201da52c083SMark Cave-Ayland 
2020606b288SMark Cave-Ayland     vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus);
2030606b288SMark Cave-Ayland }
2040606b288SMark Cave-Ayland 
2050606b288SMark Cave-Ayland static void adb_bus_class_init(ObjectClass *klass, void *data)
2060606b288SMark Cave-Ayland {
2070606b288SMark Cave-Ayland     BusClass *k = BUS_CLASS(klass);
2080606b288SMark Cave-Ayland 
2090606b288SMark Cave-Ayland     k->realize = adb_bus_realize;
2100606b288SMark Cave-Ayland     k->unrealize = adb_bus_unrealize;
211da52c083SMark Cave-Ayland     k->reset = adb_bus_reset;
2120606b288SMark Cave-Ayland }
2130606b288SMark Cave-Ayland 
21484ede329SAndreas Färber static const TypeInfo adb_bus_type_info = {
21584ede329SAndreas Färber     .name = TYPE_ADB_BUS,
21684ede329SAndreas Färber     .parent = TYPE_BUS,
21784ede329SAndreas Färber     .instance_size = sizeof(ADBBusState),
2180606b288SMark Cave-Ayland     .class_init = adb_bus_class_init,
21984ede329SAndreas Färber };
22084ede329SAndreas Färber 
22177cb0f5aSLaurent Vivier const VMStateDescription vmstate_adb_device = {
222e5dffaa5SMark Cave-Ayland     .name = "adb_device",
223e5dffaa5SMark Cave-Ayland     .version_id = 0,
224e5dffaa5SMark Cave-Ayland     .minimum_version_id = 0,
225e5dffaa5SMark Cave-Ayland     .fields = (VMStateField[]) {
226e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(devaddr, ADBDevice),
227e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(handler, ADBDevice),
228e5dffaa5SMark Cave-Ayland         VMSTATE_END_OF_LIST()
229e5dffaa5SMark Cave-Ayland     }
230e5dffaa5SMark Cave-Ayland };
231e5dffaa5SMark Cave-Ayland 
2322e4a7c9cSAndreas Färber static void adb_device_realizefn(DeviceState *dev, Error **errp)
2332e4a7c9cSAndreas Färber {
2342e4a7c9cSAndreas Färber     ADBDevice *d = ADB_DEVICE(dev);
2352e4a7c9cSAndreas Färber     ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev));
2362e4a7c9cSAndreas Färber 
2372e4a7c9cSAndreas Färber     if (bus->nb_devices >= MAX_ADB_DEVICES) {
2382e4a7c9cSAndreas Färber         return;
2392e4a7c9cSAndreas Färber     }
2402e4a7c9cSAndreas Färber 
2412e4a7c9cSAndreas Färber     bus->devices[bus->nb_devices++] = d;
2422e4a7c9cSAndreas Färber }
2432e4a7c9cSAndreas Färber 
2442e4a7c9cSAndreas Färber static void adb_device_class_init(ObjectClass *oc, void *data)
2452e4a7c9cSAndreas Färber {
2462e4a7c9cSAndreas Färber     DeviceClass *dc = DEVICE_CLASS(oc);
2472e4a7c9cSAndreas Färber 
2482e4a7c9cSAndreas Färber     dc->realize = adb_device_realizefn;
2492e4a7c9cSAndreas Färber     dc->bus_type = TYPE_ADB_BUS;
2502e4a7c9cSAndreas Färber }
2512e4a7c9cSAndreas Färber 
2522e4a7c9cSAndreas Färber static const TypeInfo adb_device_type_info = {
2532e4a7c9cSAndreas Färber     .name = TYPE_ADB_DEVICE,
2542e4a7c9cSAndreas Färber     .parent = TYPE_DEVICE,
2552e4a7c9cSAndreas Färber     .instance_size = sizeof(ADBDevice),
2562e4a7c9cSAndreas Färber     .abstract = true,
2572e4a7c9cSAndreas Färber     .class_init = adb_device_class_init,
2582e4a7c9cSAndreas Färber };
2592e4a7c9cSAndreas Färber 
26084ede329SAndreas Färber static void adb_register_types(void)
26184ede329SAndreas Färber {
26284ede329SAndreas Färber     type_register_static(&adb_bus_type_info);
2632e4a7c9cSAndreas Färber     type_register_static(&adb_device_type_info);
26484ede329SAndreas Färber }
26584ede329SAndreas Färber 
26684ede329SAndreas Färber type_init(adb_register_types)
267