xref: /qemu/hw/input/adb.c (revision 913f47ef96990fc72135ec604b6bb1fe2d1e389a)
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 
41d2288b75SMark Cave-Ayland static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf,
42d2288b75SMark Cave-Ayland                           int len)
43267002cdSbellard {
44267002cdSbellard     ADBDevice *d;
45244a0ee9SMark Cave-Ayland     ADBDeviceClass *adc;
463fe02cc8SMark Cave-Ayland     int devaddr, cmd, olen, i;
47267002cdSbellard 
48819e712bSbellard     cmd = buf[0] & 0xf;
49bec9d989Sbellard     if (cmd == ADB_BUSRESET) {
50bec9d989Sbellard         for (i = 0; i < s->nb_devices; i++) {
512e4a7c9cSAndreas Färber             d = s->devices[i];
522e4a7c9cSAndreas Färber             adb_device_reset(d);
53bec9d989Sbellard         }
543fe02cc8SMark Cave-Ayland         s->status = 0;
55bec9d989Sbellard         return 0;
56bec9d989Sbellard     }
57244a0ee9SMark Cave-Ayland 
58244a0ee9SMark Cave-Ayland     s->pending = 0;
59244a0ee9SMark Cave-Ayland     for (i = 0; i < s->nb_devices; i++) {
60244a0ee9SMark Cave-Ayland         d = s->devices[i];
61244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
62244a0ee9SMark Cave-Ayland 
63244a0ee9SMark Cave-Ayland         if (adc->devhasdata(d)) {
64244a0ee9SMark Cave-Ayland             s->pending |= (1 << d->devaddr);
65244a0ee9SMark Cave-Ayland         }
66244a0ee9SMark Cave-Ayland     }
67244a0ee9SMark Cave-Ayland 
683fe02cc8SMark Cave-Ayland     s->status = 0;
69819e712bSbellard     devaddr = buf[0] >> 4;
70267002cdSbellard     for (i = 0; i < s->nb_devices; i++) {
712e4a7c9cSAndreas Färber         d = s->devices[i];
72244a0ee9SMark Cave-Ayland         adc = ADB_DEVICE_GET_CLASS(d);
73244a0ee9SMark Cave-Ayland 
74267002cdSbellard         if (d->devaddr == devaddr) {
753fe02cc8SMark Cave-Ayland             olen = adc->devreq(d, obuf, buf, len);
763fe02cc8SMark Cave-Ayland             if (!olen) {
773fe02cc8SMark Cave-Ayland                 s->status |= ADB_STATUS_BUSTIMEOUT;
783fe02cc8SMark Cave-Ayland             }
793fe02cc8SMark Cave-Ayland             return olen;
80267002cdSbellard         }
81267002cdSbellard     }
82244a0ee9SMark Cave-Ayland 
833fe02cc8SMark Cave-Ayland     s->status |= ADB_STATUS_BUSTIMEOUT;
84bec9d989Sbellard     return ADB_RET_NOTPRESENT;
85e2733d20Sbellard }
86e2733d20Sbellard 
87d2288b75SMark Cave-Ayland int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
88d2288b75SMark Cave-Ayland {
89*913f47efSMark Cave-Ayland     assert(s->autopoll_blocked);
90*913f47efSMark Cave-Ayland 
91d2288b75SMark Cave-Ayland     return do_adb_request(s, obuf, buf, len);
92d2288b75SMark Cave-Ayland }
93d2288b75SMark Cave-Ayland 
94216c906eSHervé Poussineau int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
95e2733d20Sbellard {
96e2733d20Sbellard     ADBDevice *d;
97e2733d20Sbellard     int olen, i;
98bec9d989Sbellard     uint8_t buf[1];
99e2733d20Sbellard 
100e2733d20Sbellard     olen = 0;
101e2733d20Sbellard     for (i = 0; i < s->nb_devices; i++) {
102bcaaefdbSMark Cave-Ayland         if (s->poll_index >= s->nb_devices) {
103e2733d20Sbellard             s->poll_index = 0;
104bcaaefdbSMark Cave-Ayland         }
1052e4a7c9cSAndreas Färber         d = s->devices[s->poll_index];
106216c906eSHervé Poussineau         if ((1 << d->devaddr) & poll_mask) {
107bec9d989Sbellard             buf[0] = ADB_READREG | (d->devaddr << 4);
108d2288b75SMark Cave-Ayland             olen = do_adb_request(s, obuf + 1, buf, 1);
109bec9d989Sbellard             /* if there is data, we poll again the same device */
110bec9d989Sbellard             if (olen > 0) {
1113fe02cc8SMark Cave-Ayland                 s->status |= ADB_STATUS_POLLREPLY;
112bec9d989Sbellard                 obuf[0] = buf[0];
113bec9d989Sbellard                 olen++;
1143fe02cc8SMark Cave-Ayland                 return olen;
115e2733d20Sbellard             }
116216c906eSHervé Poussineau         }
117bec9d989Sbellard         s->poll_index++;
118bec9d989Sbellard     }
119e2733d20Sbellard     return olen;
120267002cdSbellard }
121267002cdSbellard 
122da52c083SMark Cave-Ayland void adb_set_autopoll_enabled(ADBBusState *s, bool enabled)
123da52c083SMark Cave-Ayland {
124da52c083SMark Cave-Ayland     if (s->autopoll_enabled != enabled) {
125da52c083SMark Cave-Ayland         s->autopoll_enabled = enabled;
126da52c083SMark Cave-Ayland         if (s->autopoll_enabled) {
127da52c083SMark Cave-Ayland             timer_mod(s->autopoll_timer,
128da52c083SMark Cave-Ayland                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
129da52c083SMark Cave-Ayland                       s->autopoll_rate_ms);
130da52c083SMark Cave-Ayland         } else {
131da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
132da52c083SMark Cave-Ayland         }
133da52c083SMark Cave-Ayland     }
134da52c083SMark Cave-Ayland }
135da52c083SMark Cave-Ayland 
136da52c083SMark Cave-Ayland void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms)
137da52c083SMark Cave-Ayland {
138da52c083SMark Cave-Ayland     s->autopoll_rate_ms = rate_ms;
139da52c083SMark Cave-Ayland 
140da52c083SMark Cave-Ayland     if (s->autopoll_enabled) {
141da52c083SMark Cave-Ayland         timer_mod(s->autopoll_timer,
142da52c083SMark Cave-Ayland                   qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
143da52c083SMark Cave-Ayland                   s->autopoll_rate_ms);
144da52c083SMark Cave-Ayland     }
145da52c083SMark Cave-Ayland }
146da52c083SMark Cave-Ayland 
147da52c083SMark Cave-Ayland void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask)
148da52c083SMark Cave-Ayland {
149da52c083SMark Cave-Ayland     if (s->autopoll_mask != mask) {
150da52c083SMark Cave-Ayland         s->autopoll_mask = mask;
151da52c083SMark Cave-Ayland         if (s->autopoll_enabled && s->autopoll_mask) {
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         } else {
156da52c083SMark Cave-Ayland             timer_del(s->autopoll_timer);
157da52c083SMark Cave-Ayland         }
158da52c083SMark Cave-Ayland     }
159da52c083SMark Cave-Ayland }
160da52c083SMark Cave-Ayland 
1614e5df036SMark Cave-Ayland void adb_autopoll_block(ADBBusState *s)
1624e5df036SMark Cave-Ayland {
1634e5df036SMark Cave-Ayland     s->autopoll_blocked = true;
1644e5df036SMark Cave-Ayland 
1654e5df036SMark Cave-Ayland     if (s->autopoll_enabled) {
1664e5df036SMark Cave-Ayland         timer_del(s->autopoll_timer);
1674e5df036SMark Cave-Ayland     }
1684e5df036SMark Cave-Ayland }
1694e5df036SMark Cave-Ayland 
1704e5df036SMark Cave-Ayland void adb_autopoll_unblock(ADBBusState *s)
1714e5df036SMark Cave-Ayland {
1724e5df036SMark Cave-Ayland     s->autopoll_blocked = false;
1734e5df036SMark Cave-Ayland 
1744e5df036SMark Cave-Ayland     if (s->autopoll_enabled) {
1754e5df036SMark Cave-Ayland         timer_mod(s->autopoll_timer,
1764e5df036SMark Cave-Ayland                   qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
1774e5df036SMark Cave-Ayland                   s->autopoll_rate_ms);
1784e5df036SMark Cave-Ayland     }
1794e5df036SMark Cave-Ayland }
1804e5df036SMark Cave-Ayland 
181da52c083SMark Cave-Ayland static void adb_autopoll(void *opaque)
182da52c083SMark Cave-Ayland {
183da52c083SMark Cave-Ayland     ADBBusState *s = opaque;
184da52c083SMark Cave-Ayland 
185*913f47efSMark Cave-Ayland     if (!s->autopoll_blocked) {
186da52c083SMark Cave-Ayland         s->autopoll_cb(s->autopoll_cb_opaque);
187*913f47efSMark Cave-Ayland     }
188da52c083SMark Cave-Ayland 
189da52c083SMark Cave-Ayland     timer_mod(s->autopoll_timer,
190da52c083SMark Cave-Ayland               qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
191da52c083SMark Cave-Ayland               s->autopoll_rate_ms);
192da52c083SMark Cave-Ayland }
193da52c083SMark Cave-Ayland 
194da52c083SMark Cave-Ayland void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque),
195da52c083SMark Cave-Ayland                                     void *opaque)
196da52c083SMark Cave-Ayland {
197da52c083SMark Cave-Ayland     s->autopoll_cb = cb;
198da52c083SMark Cave-Ayland     s->autopoll_cb_opaque = opaque;
199da52c083SMark Cave-Ayland }
200da52c083SMark Cave-Ayland 
2010606b288SMark Cave-Ayland static const VMStateDescription vmstate_adb_bus = {
2020606b288SMark Cave-Ayland     .name = "adb_bus",
2030606b288SMark Cave-Ayland     .version_id = 0,
2040606b288SMark Cave-Ayland     .minimum_version_id = 0,
2050606b288SMark Cave-Ayland     .fields = (VMStateField[]) {
206da52c083SMark Cave-Ayland         VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState),
207da52c083SMark Cave-Ayland         VMSTATE_BOOL(autopoll_enabled, ADBBusState),
208da52c083SMark Cave-Ayland         VMSTATE_UINT8(autopoll_rate_ms, ADBBusState),
209da52c083SMark Cave-Ayland         VMSTATE_UINT16(autopoll_mask, ADBBusState),
2104e5df036SMark Cave-Ayland         VMSTATE_BOOL(autopoll_blocked, ADBBusState),
2110606b288SMark Cave-Ayland         VMSTATE_END_OF_LIST()
2120606b288SMark Cave-Ayland     }
2130606b288SMark Cave-Ayland };
2140606b288SMark Cave-Ayland 
215da52c083SMark Cave-Ayland static void adb_bus_reset(BusState *qbus)
216da52c083SMark Cave-Ayland {
217da52c083SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
218da52c083SMark Cave-Ayland 
219da52c083SMark Cave-Ayland     adb_bus->autopoll_enabled = false;
220da52c083SMark Cave-Ayland     adb_bus->autopoll_mask = 0xffff;
221da52c083SMark Cave-Ayland     adb_bus->autopoll_rate_ms = 20;
222da52c083SMark Cave-Ayland }
223da52c083SMark Cave-Ayland 
2240606b288SMark Cave-Ayland static void adb_bus_realize(BusState *qbus, Error **errp)
2250606b288SMark Cave-Ayland {
2260606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
2270606b288SMark Cave-Ayland 
228da52c083SMark Cave-Ayland     adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll,
229da52c083SMark Cave-Ayland                                            adb_bus);
230da52c083SMark Cave-Ayland 
2310606b288SMark Cave-Ayland     vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus);
2320606b288SMark Cave-Ayland }
2330606b288SMark Cave-Ayland 
2340606b288SMark Cave-Ayland static void adb_bus_unrealize(BusState *qbus)
2350606b288SMark Cave-Ayland {
2360606b288SMark Cave-Ayland     ADBBusState *adb_bus = ADB_BUS(qbus);
2370606b288SMark Cave-Ayland 
238da52c083SMark Cave-Ayland     timer_del(adb_bus->autopoll_timer);
239da52c083SMark Cave-Ayland 
2400606b288SMark Cave-Ayland     vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus);
2410606b288SMark Cave-Ayland }
2420606b288SMark Cave-Ayland 
2430606b288SMark Cave-Ayland static void adb_bus_class_init(ObjectClass *klass, void *data)
2440606b288SMark Cave-Ayland {
2450606b288SMark Cave-Ayland     BusClass *k = BUS_CLASS(klass);
2460606b288SMark Cave-Ayland 
2470606b288SMark Cave-Ayland     k->realize = adb_bus_realize;
2480606b288SMark Cave-Ayland     k->unrealize = adb_bus_unrealize;
249da52c083SMark Cave-Ayland     k->reset = adb_bus_reset;
2500606b288SMark Cave-Ayland }
2510606b288SMark Cave-Ayland 
25284ede329SAndreas Färber static const TypeInfo adb_bus_type_info = {
25384ede329SAndreas Färber     .name = TYPE_ADB_BUS,
25484ede329SAndreas Färber     .parent = TYPE_BUS,
25584ede329SAndreas Färber     .instance_size = sizeof(ADBBusState),
2560606b288SMark Cave-Ayland     .class_init = adb_bus_class_init,
25784ede329SAndreas Färber };
25884ede329SAndreas Färber 
25977cb0f5aSLaurent Vivier const VMStateDescription vmstate_adb_device = {
260e5dffaa5SMark Cave-Ayland     .name = "adb_device",
261e5dffaa5SMark Cave-Ayland     .version_id = 0,
262e5dffaa5SMark Cave-Ayland     .minimum_version_id = 0,
263e5dffaa5SMark Cave-Ayland     .fields = (VMStateField[]) {
264e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(devaddr, ADBDevice),
265e5dffaa5SMark Cave-Ayland         VMSTATE_INT32(handler, ADBDevice),
266e5dffaa5SMark Cave-Ayland         VMSTATE_END_OF_LIST()
267e5dffaa5SMark Cave-Ayland     }
268e5dffaa5SMark Cave-Ayland };
269e5dffaa5SMark Cave-Ayland 
2702e4a7c9cSAndreas Färber static void adb_device_realizefn(DeviceState *dev, Error **errp)
2712e4a7c9cSAndreas Färber {
2722e4a7c9cSAndreas Färber     ADBDevice *d = ADB_DEVICE(dev);
2732e4a7c9cSAndreas Färber     ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev));
2742e4a7c9cSAndreas Färber 
2752e4a7c9cSAndreas Färber     if (bus->nb_devices >= MAX_ADB_DEVICES) {
2762e4a7c9cSAndreas Färber         return;
2772e4a7c9cSAndreas Färber     }
2782e4a7c9cSAndreas Färber 
2792e4a7c9cSAndreas Färber     bus->devices[bus->nb_devices++] = d;
2802e4a7c9cSAndreas Färber }
2812e4a7c9cSAndreas Färber 
2822e4a7c9cSAndreas Färber static void adb_device_class_init(ObjectClass *oc, void *data)
2832e4a7c9cSAndreas Färber {
2842e4a7c9cSAndreas Färber     DeviceClass *dc = DEVICE_CLASS(oc);
2852e4a7c9cSAndreas Färber 
2862e4a7c9cSAndreas Färber     dc->realize = adb_device_realizefn;
2872e4a7c9cSAndreas Färber     dc->bus_type = TYPE_ADB_BUS;
2882e4a7c9cSAndreas Färber }
2892e4a7c9cSAndreas Färber 
2902e4a7c9cSAndreas Färber static const TypeInfo adb_device_type_info = {
2912e4a7c9cSAndreas Färber     .name = TYPE_ADB_DEVICE,
2922e4a7c9cSAndreas Färber     .parent = TYPE_DEVICE,
2932e4a7c9cSAndreas Färber     .instance_size = sizeof(ADBDevice),
2942e4a7c9cSAndreas Färber     .abstract = true,
2952e4a7c9cSAndreas Färber     .class_init = adb_device_class_init,
2962e4a7c9cSAndreas Färber };
2972e4a7c9cSAndreas Färber 
29884ede329SAndreas Färber static void adb_register_types(void)
29984ede329SAndreas Färber {
30084ede329SAndreas Färber     type_register_static(&adb_bus_type_info);
3012e4a7c9cSAndreas Färber     type_register_static(&adb_device_type_info);
30284ede329SAndreas Färber }
30384ede329SAndreas Färber 
30484ede329SAndreas Färber type_init(adb_register_types)
305