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