From bcaaefdbb2167858d3b92745b79473deb49c417a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:15 +0100 Subject: [PATCH 01/22] adb: coding style update to fix checkpatch errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will help ensure that style guidelines are being maintained during subsequent changes. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-2-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/input/adb.c b/hw/input/adb.c index b1ac4a3852..bf1bc30d19 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -44,14 +44,14 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) cmd = buf[0] & 0xf; if (cmd == ADB_BUSRESET) { - for(i = 0; i < s->nb_devices; i++) { + for (i = 0; i < s->nb_devices; i++) { d = s->devices[i]; adb_device_reset(d); } return 0; } devaddr = buf[0] >> 4; - for(i = 0; i < s->nb_devices; i++) { + for (i = 0; i < s->nb_devices; i++) { d = s->devices[i]; if (d->devaddr == devaddr) { ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d); @@ -69,9 +69,10 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) uint8_t buf[1]; olen = 0; - for(i = 0; i < s->nb_devices; i++) { - if (s->poll_index >= s->nb_devices) + for (i = 0; i < s->nb_devices; i++) { + if (s->poll_index >= s->nb_devices) { s->poll_index = 0; + } d = s->devices[s->poll_index]; if ((1 << d->devaddr) & poll_mask) { buf[0] = ADB_READREG | (d->devaddr << 4); From 167f1667b120ddac301163da64b57b19ce7cf622 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:16 +0100 Subject: [PATCH 02/22] adb: fix adb-mouse read length and revert disable-reg3-direct-writes workaround Commit 84051eb400 "adb: add property to disable direct reg 3 writes" introduced a workaround for spurious writes to ADB register 3 when MacOS 9 enables autopoll on the mouse device. Further analysis shows that the problem is that only a partial request is sent, and since the len parameter is ignored then stale data from the previous request is used causing the incorrect address assignment. Remove the disable-reg3-direct-writes workaround and instead check the length parameter when the write is attempted, discarding the invalid request. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-3-mark.cave-ayland@ilande.co.uk> --- hw/input/adb-kbd.c | 26 +++++++++++------------ hw/input/adb-mouse.c | 48 ++++++++++++++++++++++++------------------ hw/input/adb.c | 7 ------ hw/ppc/mac_newworld.c | 2 -- include/hw/input/adb.h | 1 - 5 files changed, 40 insertions(+), 44 deletions(-) diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index a6d5c9b7c9..027dd3e531 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -259,21 +259,19 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, trace_adb_kbd_request_change_addr(d->devaddr); break; default: - if (!d->disable_direct_reg3_writes) { - d->devaddr = buf[1] & 0xf; - - /* we support handlers: - * 1: Apple Standard Keyboard - * 2: Apple Extended Keyboard (LShift = RShift) - * 3: Apple Extended Keyboard (LShift != RShift) - */ - if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { - d->handler = buf[2]; - } - - trace_adb_kbd_request_change_addr_and_handler(d->devaddr, - d->handler); + d->devaddr = buf[1] & 0xf; + /* + * we support handlers: + * 1: Apple Standard Keyboard + * 2: Apple Extended Keyboard (LShift = RShift) + * 3: Apple Extended Keyboard (LShift != RShift) + */ + if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { + d->handler = buf[2]; } + + trace_adb_kbd_request_change_addr_and_handler(d->devaddr, + d->handler); break; } } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index aeba41bddd..78b6f5030c 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -135,6 +135,16 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, case 2: break; case 3: + /* + * MacOS 9 has a bug in its ADB driver whereby after configuring + * the ADB bus devices it sends another write of invalid length + * to reg 3. Make sure we ignore it to prevent an address clash + * with the previous device. + */ + if (len != 3) { + return 0; + } + switch (buf[2]) { case ADB_CMD_SELF_TEST: break; @@ -145,27 +155,25 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, trace_adb_mouse_request_change_addr(d->devaddr); break; default: - if (!d->disable_direct_reg3_writes) { - d->devaddr = buf[1] & 0xf; - - /* we support handlers: - * 0x01: Classic Apple Mouse Protocol / 100 cpi operations - * 0x02: Classic Apple Mouse Protocol / 200 cpi operations - * we don't support handlers (at least): - * 0x03: Mouse systems A3 trackball - * 0x04: Extended Apple Mouse Protocol - * 0x2f: Microspeed mouse - * 0x42: Macally - * 0x5f: Microspeed mouse - * 0x66: Microspeed mouse - */ - if (buf[2] == 1 || buf[2] == 2) { - d->handler = buf[2]; - } - - trace_adb_mouse_request_change_addr_and_handler( - d->devaddr, d->handler); + d->devaddr = buf[1] & 0xf; + /* + * we support handlers: + * 0x01: Classic Apple Mouse Protocol / 100 cpi operations + * 0x02: Classic Apple Mouse Protocol / 200 cpi operations + * we don't support handlers (at least): + * 0x03: Mouse systems A3 trackball + * 0x04: Extended Apple Mouse Protocol + * 0x2f: Microspeed mouse + * 0x42: Macally + * 0x5f: Microspeed mouse + * 0x66: Microspeed mouse + */ + if (buf[2] == 1 || buf[2] == 2) { + d->handler = buf[2]; } + + trace_adb_mouse_request_change_addr_and_handler(d->devaddr, + d->handler); break; } } diff --git a/hw/input/adb.c b/hw/input/adb.c index bf1bc30d19..d85278a7b7 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -118,18 +118,11 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp) bus->devices[bus->nb_devices++] = d; } -static Property adb_device_properties[] = { - DEFINE_PROP_BOOL("disable-direct-reg3-writes", ADBDevice, - disable_direct_reg3_writes, false), - DEFINE_PROP_END_OF_LIST(), -}; - static void adb_device_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = adb_device_realizefn; - device_class_set_props(dc, adb_device_properties); dc->bus_type = TYPE_ADB_BUS; } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 5f3a028e6a..828c5992ae 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -404,11 +404,9 @@ static void ppc_core99_init(MachineState *machine) adb_bus = qdev_get_child_bus(dev, "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); - qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); qdev_realize_and_unref(dev, adb_bus, &error_fatal); dev = qdev_new(TYPE_ADB_MOUSE); - qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); qdev_realize_and_unref(dev, adb_bus, &error_fatal); } diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index b7b32e2b16..4d2c565f54 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -49,7 +49,6 @@ struct ADBDevice { int devaddr; int handler; - bool disable_direct_reg3_writes; }; #define ADB_DEVICE_CLASS(cls) \ From d9b898943dbcce7c513739febc05ccaa7142c49e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:17 +0100 Subject: [PATCH 03/22] cuda: convert ADB autopoll timer from ns to ms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is in preparation for consolidating all of the ADB autopoll management in one place. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-4-mark.cave-ayland@ilande.co.uk> --- hw/misc/macio/cuda.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 47aa3b0552..01bf47327c 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -210,8 +210,9 @@ static void cuda_adb_poll(void *opaque) obuf[1] = 0x40; /* polled data */ cuda_send_packet_to_host(s, obuf, olen + 2); } - timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); + + timer_mod(s->adb_poll_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); } /* description of commands */ @@ -238,8 +239,8 @@ static bool cuda_cmd_autopoll(CUDAState *s, s->autopoll = autopoll; if (autopoll) { timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); } else { timer_del(s->adb_poll_timer); } @@ -264,8 +265,8 @@ static bool cuda_cmd_set_autorate(CUDAState *s, s->autopoll_rate_ms = in_data[0]; if (s->autopoll) { timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); } return true; } @@ -544,7 +545,7 @@ static void cuda_realize(DeviceState *dev, Error **errp) s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); s->sr_delay_ns = 20 * SCALE_US; - s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); + s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); s->adb_poll_mask = 0xffff; s->autopoll_rate_ms = 20; } From dcb091c40ef8c77590b1c5b7160ec428f2a2a053 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:18 +0100 Subject: [PATCH 04/22] pmu: fix duplicate autopoll mask variable It seems that during the initial work to introduce the via-pmu ADB support a duplicate autopoll mask variable was accidentally left in place. Remove the duplicate autopoll_mask variable and switch everything over to use adb_poll_mask instead. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-5-mark.cave-ayland@ilande.co.uk> --- hw/misc/macio/pmu.c | 15 +++++++-------- include/hw/misc/macio/pmu.h | 1 - 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 41b626c46c..cae2845936 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -175,11 +175,11 @@ static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) { trace_pmu_cmd_set_adb_autopoll(mask); - if (s->autopoll_mask == mask) { + if (s->adb_poll_mask == mask) { return; } - s->autopoll_mask = mask; + s->adb_poll_mask = mask; if (mask) { timer_mod(s->adb_poll_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); @@ -274,9 +274,9 @@ static void pmu_cmd_adb_poll_off(PMUState *s, return; } - if (s->has_adb && s->autopoll_mask) { + if (s->has_adb && s->adb_poll_mask) { timer_del(s->adb_poll_timer); - s->autopoll_mask = false; + s->adb_poll_mask = 0; } } @@ -698,8 +698,8 @@ static const VMStateDescription vmstate_pmu_adb = { static const VMStateDescription vmstate_pmu = { .name = "pmu", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522, MOS6522State), @@ -715,7 +715,6 @@ static const VMStateDescription vmstate_pmu = { VMSTATE_UINT8(intbits, PMUState), VMSTATE_UINT8(intmask, PMUState), VMSTATE_UINT8(autopoll_rate_ms, PMUState), - VMSTATE_UINT8(autopoll_mask, PMUState), VMSTATE_UINT32(tick_offset, PMUState), VMSTATE_TIMER_PTR(one_sec_timer, PMUState), VMSTATE_INT64(one_sec_target, PMUState), @@ -735,7 +734,7 @@ static void pmu_reset(DeviceState *dev) s->intbits = 0; s->cmd_state = pmu_state_idle; - s->autopoll_mask = 0; + s->adb_poll_mask = 0; } static void pmu_realize(DeviceState *dev, Error **errp) diff --git a/include/hw/misc/macio/pmu.h b/include/hw/misc/macio/pmu.h index 7ef83dee4c..4f34b6f9e7 100644 --- a/include/hw/misc/macio/pmu.h +++ b/include/hw/misc/macio/pmu.h @@ -220,7 +220,6 @@ typedef struct PMUState { ADBBusState adb_bus; uint16_t adb_poll_mask; uint8_t autopoll_rate_ms; - uint8_t autopoll_mask; QEMUTimer *adb_poll_timer; uint8_t adb_reply_size; uint8_t adb_reply[ADB_MAX_OUT_LEN]; From 414eb1d500b94b2774440656f5bde4bf2e0f4a29 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:19 +0100 Subject: [PATCH 05/22] pmu: honour autopoll_rate_ms when rearming the ADB autopoll timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't use a fixed value but instead use the default value from the ADB bus state. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-6-mark.cave-ayland@ilande.co.uk> --- hw/misc/macio/pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index cae2845936..bae0b440d0 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -106,7 +106,7 @@ static void pmu_adb_poll(void *opaque) } timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->autopoll_rate_ms); } static void pmu_one_sec_timer(void *opaque) @@ -182,7 +182,7 @@ static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) s->adb_poll_mask = mask; if (mask) { timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->autopoll_rate_ms); } else { timer_del(s->adb_poll_timer); } From 0606b2883038b4b9a31a46fd1275a31323ec22d0 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:20 +0100 Subject: [PATCH 06/22] adb: introduce realize/unrealize and VMStateDescription for ADB bus Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-7-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hw/input/adb.c b/hw/input/adb.c index d85278a7b7..21a9b3aa96 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -89,10 +89,42 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) return olen; } +static const VMStateDescription vmstate_adb_bus = { + .name = "adb_bus", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +static void adb_bus_realize(BusState *qbus, Error **errp) +{ + ADBBusState *adb_bus = ADB_BUS(qbus); + + vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus); +} + +static void adb_bus_unrealize(BusState *qbus) +{ + ADBBusState *adb_bus = ADB_BUS(qbus); + + vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus); +} + +static void adb_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->realize = adb_bus_realize; + k->unrealize = adb_bus_unrealize; +} + static const TypeInfo adb_bus_type_info = { .name = TYPE_ADB_BUS, .parent = TYPE_BUS, .instance_size = sizeof(ADBBusState), + .class_init = adb_bus_class_init, }; const VMStateDescription vmstate_adb_device = { From da52c083ac4af9bf934898470dfdeef0f6ead343 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:21 +0100 Subject: [PATCH 07/22] adb: create autopoll variables directly within ADBBusState Rather than each ADB implementation requiring its own functions to manage autopoll state, timers, and autopoll masks prepare to move this information directly into ADBBusState. Add external functions within adb.h to allow each ADB implementation to manage the new autopoll variables. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-8-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 77 ++++++++++++++++++++++++++++++++++++++++++ include/hw/input/adb.h | 13 +++++++ 2 files changed, 90 insertions(+) diff --git a/hw/input/adb.c b/hw/input/adb.c index 21a9b3aa96..bb36ce6fad 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -27,6 +27,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "qemu/timer.h" #include "adb-internal.h" /* error codes */ @@ -89,19 +90,92 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) return olen; } +void adb_set_autopoll_enabled(ADBBusState *s, bool enabled) +{ + if (s->autopoll_enabled != enabled) { + s->autopoll_enabled = enabled; + if (s->autopoll_enabled) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } else { + timer_del(s->autopoll_timer); + } + } +} + +void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms) +{ + s->autopoll_rate_ms = rate_ms; + + if (s->autopoll_enabled) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } +} + +void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask) +{ + if (s->autopoll_mask != mask) { + s->autopoll_mask = mask; + if (s->autopoll_enabled && s->autopoll_mask) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } else { + timer_del(s->autopoll_timer); + } + } +} + +static void adb_autopoll(void *opaque) +{ + ADBBusState *s = opaque; + + s->autopoll_cb(s->autopoll_cb_opaque); + + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); +} + +void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque), + void *opaque) +{ + s->autopoll_cb = cb; + s->autopoll_cb_opaque = opaque; +} + static const VMStateDescription vmstate_adb_bus = { .name = "adb_bus", .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState), + VMSTATE_BOOL(autopoll_enabled, ADBBusState), + VMSTATE_UINT8(autopoll_rate_ms, ADBBusState), + VMSTATE_UINT16(autopoll_mask, ADBBusState), VMSTATE_END_OF_LIST() } }; +static void adb_bus_reset(BusState *qbus) +{ + ADBBusState *adb_bus = ADB_BUS(qbus); + + adb_bus->autopoll_enabled = false; + adb_bus->autopoll_mask = 0xffff; + adb_bus->autopoll_rate_ms = 20; +} + static void adb_bus_realize(BusState *qbus, Error **errp) { ADBBusState *adb_bus = ADB_BUS(qbus); + adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll, + adb_bus); + vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus); } @@ -109,6 +183,8 @@ static void adb_bus_unrealize(BusState *qbus) { ADBBusState *adb_bus = ADB_BUS(qbus); + timer_del(adb_bus->autopoll_timer); + vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus); } @@ -118,6 +194,7 @@ static void adb_bus_class_init(ObjectClass *klass, void *data) k->realize = adb_bus_realize; k->unrealize = adb_bus_unrealize; + k->reset = adb_bus_reset; } static const TypeInfo adb_bus_type_info = { diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index 4d2c565f54..15b1874a3d 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -75,12 +75,25 @@ struct ADBBusState { ADBDevice *devices[MAX_ADB_DEVICES]; int nb_devices; int poll_index; + + QEMUTimer *autopoll_timer; + bool autopoll_enabled; + uint8_t autopoll_rate_ms; + uint16_t autopoll_mask; + void (*autopoll_cb)(void *opaque); + void *autopoll_cb_opaque; }; int adb_request(ADBBusState *s, uint8_t *buf_out, const uint8_t *buf, int len); int adb_poll(ADBBusState *s, uint8_t *buf_out, uint16_t poll_mask); +void adb_set_autopoll_enabled(ADBBusState *s, bool enabled); +void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms); +void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask); +void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque), + void *opaque); + #define TYPE_ADB_KEYBOARD "adb-keyboard" #define TYPE_ADB_MOUSE "adb-mouse" From b12a0b164c6de824b2aa3a22b4670e93e3b24e4a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:22 +0100 Subject: [PATCH 08/22] cuda: convert to use ADBBusState internal autopoll variables Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-9-mark.cave-ayland@ilande.co.uk> --- hw/misc/macio/cuda.c | 56 +++++++++++++++--------------------- include/hw/misc/macio/cuda.h | 4 --- 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 01bf47327c..b7071e89d5 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -201,18 +201,16 @@ static void cuda_send_packet_to_host(CUDAState *s, static void cuda_adb_poll(void *opaque) { CUDAState *s = opaque; + ADBBusState *adb_bus = &s->adb_bus; uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask); + olen = adb_poll(adb_bus, obuf + 2, adb_bus->autopoll_mask); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x40; /* polled data */ cuda_send_packet_to_host(s, obuf, olen + 2); } - - timer_mod(s->adb_poll_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - s->autopoll_rate_ms); } /* description of commands */ @@ -228,23 +226,16 @@ static bool cuda_cmd_autopoll(CUDAState *s, const uint8_t *in_data, int in_len, uint8_t *out_data, int *out_len) { - int autopoll; + ADBBusState *adb_bus = &s->adb_bus; + bool autopoll; if (in_len != 1) { return false; } - autopoll = (in_data[0] != 0); - if (autopoll != s->autopoll) { - s->autopoll = autopoll; - if (autopoll) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - s->autopoll_rate_ms); - } else { - timer_del(s->adb_poll_timer); - } - } + autopoll = (in_data[0] != 0) ? true : false; + + adb_set_autopoll_enabled(adb_bus, autopoll); return true; } @@ -252,6 +243,8 @@ static bool cuda_cmd_set_autorate(CUDAState *s, const uint8_t *in_data, int in_len, uint8_t *out_data, int *out_len) { + ADBBusState *adb_bus = &s->adb_bus; + if (in_len != 1) { return false; } @@ -262,12 +255,7 @@ static bool cuda_cmd_set_autorate(CUDAState *s, return false; } - s->autopoll_rate_ms = in_data[0]; - if (s->autopoll) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - s->autopoll_rate_ms); - } + adb_set_autopoll_rate_ms(adb_bus, in_data[0]); return true; } @@ -275,11 +263,16 @@ static bool cuda_cmd_set_device_list(CUDAState *s, const uint8_t *in_data, int in_len, uint8_t *out_data, int *out_len) { + ADBBusState *adb_bus = &s->adb_bus; + uint16_t mask; + if (in_len != 2) { return false; } - s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1]; + mask = (((uint16_t)in_data[0]) << 8) | in_data[1]; + + adb_set_autopoll_mask(adb_bus, mask); return true; } @@ -490,8 +483,8 @@ static const MemoryRegionOps mos6522_cuda_ops = { static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 5, - .minimum_version_id = 5, + .version_id = 6, + .minimum_version_id = 6, .fields = (VMStateField[]) { VMSTATE_STRUCT(mos6522_cuda.parent_obj, CUDAState, 0, vmstate_mos6522, MOS6522State), @@ -500,13 +493,9 @@ static const VMStateDescription vmstate_cuda = { VMSTATE_INT32(data_in_size, CUDAState), VMSTATE_INT32(data_in_index, CUDAState), VMSTATE_INT32(data_out_index, CUDAState), - VMSTATE_UINT8(autopoll, CUDAState), - VMSTATE_UINT8(autopoll_rate_ms, CUDAState), - VMSTATE_UINT16(adb_poll_mask, CUDAState), VMSTATE_BUFFER(data_in, CUDAState), VMSTATE_BUFFER(data_out, CUDAState), VMSTATE_UINT32(tick_offset, CUDAState), - VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState), VMSTATE_END_OF_LIST() } @@ -515,11 +504,13 @@ static const VMStateDescription vmstate_cuda = { static void cuda_reset(DeviceState *dev) { CUDAState *s = CUDA(dev); + ADBBusState *adb_bus = &s->adb_bus; s->data_in_size = 0; s->data_in_index = 0; s->data_out_index = 0; - s->autopoll = 0; + + adb_set_autopoll_enabled(adb_bus, false); } static void cuda_realize(DeviceState *dev, Error **errp) @@ -527,6 +518,7 @@ static void cuda_realize(DeviceState *dev, Error **errp) CUDAState *s = CUDA(dev); Error *err = NULL; SysBusDevice *sbd; + ADBBusState *adb_bus = &s->adb_bus; struct tm tm; sysbus_realize(SYS_BUS_DEVICE(&s->mos6522_cuda), &err); @@ -545,9 +537,7 @@ static void cuda_realize(DeviceState *dev, Error **errp) s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); s->sr_delay_ns = 20 * SCALE_US; - s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); - s->adb_poll_mask = 0xffff; - s->autopoll_rate_ms = 20; + adb_register_autopoll_callback(adb_bus, cuda_adb_poll, s); } static void cuda_init(Object *obj) diff --git a/include/hw/misc/macio/cuda.h b/include/hw/misc/macio/cuda.h index 5768075ac5..a8cf0be1ec 100644 --- a/include/hw/misc/macio/cuda.h +++ b/include/hw/misc/macio/cuda.h @@ -95,12 +95,8 @@ typedef struct CUDAState { int data_out_index; qemu_irq irq; - uint16_t adb_poll_mask; - uint8_t autopoll_rate_ms; - uint8_t autopoll; uint8_t data_in[128]; uint8_t data_out[16]; - QEMUTimer *adb_poll_timer; } CUDAState; #endif /* CUDA_H */ From df381d584ca2b33106eb78aff4c1d6b61cd0558b Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:23 +0100 Subject: [PATCH 09/22] pmu: convert to use ADBBusState internal autopoll variables Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-10-mark.cave-ayland@ilande.co.uk> --- hw/misc/macio/pmu.c | 39 ++++++++++++++----------------------- include/hw/misc/macio/pmu.h | 3 --- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index bae0b440d0..01d49e6695 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -92,10 +92,11 @@ static void pmu_update_extirq(PMUState *s) static void pmu_adb_poll(void *opaque) { PMUState *s = opaque; + ADBBusState *adb_bus = &s->adb_bus; int olen; if (!(s->intbits & PMU_INT_ADB)) { - olen = adb_poll(&s->adb_bus, s->adb_reply, s->adb_poll_mask); + olen = adb_poll(adb_bus, s->adb_reply, adb_bus->autopoll_mask); trace_pmu_adb_poll(olen); if (olen > 0) { @@ -104,9 +105,6 @@ static void pmu_adb_poll(void *opaque) pmu_update_extirq(s); } } - - timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->autopoll_rate_ms); } static void pmu_one_sec_timer(void *opaque) @@ -173,18 +171,15 @@ static void pmu_cmd_set_int_mask(PMUState *s, static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) { + ADBBusState *adb_bus = &s->adb_bus; + trace_pmu_cmd_set_adb_autopoll(mask); - if (s->adb_poll_mask == mask) { - return; - } - - s->adb_poll_mask = mask; if (mask) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->autopoll_rate_ms); + adb_set_autopoll_mask(adb_bus, mask); + adb_set_autopoll_enabled(adb_bus, true); } else { - timer_del(s->adb_poll_timer); + adb_set_autopoll_enabled(adb_bus, false); } } @@ -267,6 +262,8 @@ static void pmu_cmd_adb_poll_off(PMUState *s, const uint8_t *in_data, uint8_t in_len, uint8_t *out_data, uint8_t *out_len) { + ADBBusState *adb_bus = &s->adb_bus; + if (in_len != 0) { qemu_log_mask(LOG_GUEST_ERROR, "PMU: ADB POLL OFF command, invalid len: %d want: 0\n", @@ -274,9 +271,8 @@ static void pmu_cmd_adb_poll_off(PMUState *s, return; } - if (s->has_adb && s->adb_poll_mask) { - timer_del(s->adb_poll_timer); - s->adb_poll_mask = 0; + if (s->has_adb) { + adb_set_autopoll_enabled(adb_bus, false); } } @@ -684,12 +680,10 @@ static bool pmu_adb_state_needed(void *opaque) static const VMStateDescription vmstate_pmu_adb = { .name = "pmu/adb", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .needed = pmu_adb_state_needed, .fields = (VMStateField[]) { - VMSTATE_UINT16(adb_poll_mask, PMUState), - VMSTATE_TIMER_PTR(adb_poll_timer, PMUState), VMSTATE_UINT8(adb_reply_size, PMUState), VMSTATE_BUFFER(adb_reply, PMUState), VMSTATE_END_OF_LIST() @@ -714,7 +708,6 @@ static const VMStateDescription vmstate_pmu = { VMSTATE_BUFFER(cmd_rsp, PMUState), VMSTATE_UINT8(intbits, PMUState), VMSTATE_UINT8(intmask, PMUState), - VMSTATE_UINT8(autopoll_rate_ms, PMUState), VMSTATE_UINT32(tick_offset, PMUState), VMSTATE_TIMER_PTR(one_sec_timer, PMUState), VMSTATE_INT64(one_sec_target, PMUState), @@ -734,7 +727,6 @@ static void pmu_reset(DeviceState *dev) s->intbits = 0; s->cmd_state = pmu_state_idle; - s->adb_poll_mask = 0; } static void pmu_realize(DeviceState *dev, Error **errp) @@ -742,6 +734,7 @@ static void pmu_realize(DeviceState *dev, Error **errp) PMUState *s = VIA_PMU(dev); Error *err = NULL; SysBusDevice *sbd; + ADBBusState *adb_bus = &s->adb_bus; struct tm tm; sysbus_realize(SYS_BUS_DEVICE(&s->mos6522_pmu), &err); @@ -763,9 +756,7 @@ static void pmu_realize(DeviceState *dev, Error **errp) if (s->has_adb) { qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, dev, "adb.0"); - s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_adb_poll, s); - s->adb_poll_mask = 0xffff; - s->autopoll_rate_ms = 20; + adb_register_autopoll_callback(adb_bus, pmu_adb_poll, s); } } diff --git a/include/hw/misc/macio/pmu.h b/include/hw/misc/macio/pmu.h index 4f34b6f9e7..72f75612b6 100644 --- a/include/hw/misc/macio/pmu.h +++ b/include/hw/misc/macio/pmu.h @@ -218,9 +218,6 @@ typedef struct PMUState { /* ADB */ bool has_adb; ADBBusState adb_bus; - uint16_t adb_poll_mask; - uint8_t autopoll_rate_ms; - QEMUTimer *adb_poll_timer; uint8_t adb_reply_size; uint8_t adb_reply[ADB_MAX_OUT_LEN]; From f3d61457e86cf43eb5322b57e3f9fcd1bb087591 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:24 +0100 Subject: [PATCH 10/22] mac_via: convert to use ADBBusState internal autopoll variables Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-11-mark.cave-ayland@ilande.co.uk> --- hw/misc/mac_via.c | 22 ++++++++++------------ include/hw/misc/mac_via.h | 1 - 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 9cd313c812..7a28bb37ac 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -601,6 +601,8 @@ static void via1_rtc_update(MacVIAState *m) static int adb_via_poll(MacVIAState *s, int state, uint8_t *data) { + ADBBusState *adb_bus = &s->adb_bus; + if (state != ADB_STATE_IDLE) { return 0; } @@ -615,7 +617,8 @@ static int adb_via_poll(MacVIAState *s, int state, uint8_t *data) s->adb_data_in_index = 0; s->adb_data_out_index = 0; - s->adb_data_in_size = adb_poll(&s->adb_bus, s->adb_data_in, 0xffff); + s->adb_data_in_size = adb_poll(adb_bus, s->adb_data_in, + adb_bus->autopoll_mask); if (s->adb_data_in_size) { *data = s->adb_data_in[s->adb_data_in_index++]; @@ -768,10 +771,6 @@ static void via_adb_poll(void *opaque) s->b &= ~VIA1B_vADBInt; } } - - timer_mod(m->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ)); } static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size) @@ -854,10 +853,9 @@ static void mac_via_reset(DeviceState *dev) { MacVIAState *m = MAC_VIA(dev); MOS6522Q800VIA1State *v1s = &m->mos6522_via1; + ADBBusState *adb_bus = &m->adb_bus; - timer_mod(m->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ)); + adb_set_autopoll_enabled(adb_bus, true); timer_del(v1s->VBL_timer); v1s->next_VBL = 0; @@ -872,6 +870,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp) { MacVIAState *m = MAC_VIA(dev); MOS6522State *ms; + ADBBusState *adb_bus = &m->adb_bus; struct tm tm; int ret; @@ -907,7 +906,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp) qemu_get_timedate(&tm, 0); m->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; - m->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via_adb_poll, m); + adb_register_autopoll_callback(adb_bus, via_adb_poll, m); m->adb_data_ready = qdev_get_gpio_in_named(dev, "via1-irq", VIA1_IRQ_ADB_READY_BIT); @@ -980,8 +979,8 @@ static int mac_via_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_mac_via = { .name = "mac-via", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .post_load = mac_via_post_load, .fields = (VMStateField[]) { /* VIAs */ @@ -1005,7 +1004,6 @@ static const VMStateDescription vmstate_mac_via = { VMSTATE_INT32(wprotect, MacVIAState), VMSTATE_INT32(alt, MacVIAState), /* ADB */ - VMSTATE_TIMER_PTR(adb_poll_timer, MacVIAState), VMSTATE_INT32(adb_data_in_size, MacVIAState), VMSTATE_INT32(adb_data_in_index, MacVIAState), VMSTATE_INT32(adb_data_out_index, MacVIAState), diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index e74f85be0f..2aaf9e27bf 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -106,7 +106,6 @@ typedef struct MacVIAState { /* ADB */ ADBBusState adb_bus; - QEMUTimer *adb_poll_timer; qemu_irq adb_data_ready; int adb_data_in_size; int adb_data_in_index; From 969ca2f7a1a41123f08ce67d7a51075c3ee2fd6e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:25 +0100 Subject: [PATCH 11/22] adb: introduce new ADBDeviceHasData method to ADBDeviceClass This is required later to allow devices to assert a service request (SRQ) signal to indicate that it has data to send, without having to consume it. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-12-mark.cave-ayland@ilande.co.uk> --- hw/input/adb-kbd.c | 8 ++++++++ hw/input/adb-mouse.c | 9 +++++++++ include/hw/input/adb.h | 3 +++ 3 files changed, 20 insertions(+) diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 027dd3e531..23760ecf7b 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -300,6 +300,13 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, return olen; } +static bool adb_kbd_has_data(ADBDevice *d) +{ + KBDState *s = ADB_KEYBOARD(d); + + return s->count > 0; +} + /* This is where keyboard events enter this file */ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) @@ -382,6 +389,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_kbd_request; + adc->devhasdata = adb_kbd_has_data; dc->reset = adb_kbd_reset; dc->vmsd = &vmstate_adb_kbd; } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 78b6f5030c..e2359fd74d 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -197,6 +197,14 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, return olen; } +static bool adb_mouse_has_data(ADBDevice *d) +{ + MouseState *s = ADB_MOUSE(d); + + return !(s->last_buttons_state == s->buttons_state && + s->dx == 0 && s->dy == 0); +} + static void adb_mouse_reset(DeviceState *dev) { ADBDevice *d = ADB_DEVICE(dev); @@ -252,6 +260,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); adc->devreq = adb_mouse_request; + adc->devhasdata = adb_mouse_has_data; dc->reset = adb_mouse_reset; dc->vmsd = &vmstate_adb_mouse; } diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index 15b1874a3d..9b80204e43 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -39,6 +39,8 @@ typedef struct ADBDevice ADBDevice; typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, const uint8_t *buf, int len); +typedef bool ADBDeviceHasData(ADBDevice *d); + #define TYPE_ADB_DEVICE "adb-device" #define ADB_DEVICE(obj) OBJECT_CHECK(ADBDevice, (obj), TYPE_ADB_DEVICE) @@ -62,6 +64,7 @@ typedef struct ADBDeviceClass { /*< public >*/ ADBDeviceRequest *devreq; + ADBDeviceHasData *devhasdata; } ADBDeviceClass; #define TYPE_ADB_BUS "apple-desktop-bus" From 244a0ee96545e79af7ade39b510e459ef5a1c366 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:26 +0100 Subject: [PATCH 12/22] adb: keep track of devices with pending data Add a new pending variable to ADBBusState which is a bitmask indicating which ADB devices have data to send. Update the bitmask every time that an ADB request is executed. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-13-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 16 +++++++++++++++- include/hw/input/adb.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/input/adb.c b/hw/input/adb.c index bb36ce6fad..c1adb21e6b 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -41,6 +41,7 @@ static void adb_device_reset(ADBDevice *d) int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) { ADBDevice *d; + ADBDeviceClass *adc; int devaddr, cmd, i; cmd = buf[0] & 0xf; @@ -51,14 +52,27 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) } return 0; } + + s->pending = 0; + for (i = 0; i < s->nb_devices; i++) { + d = s->devices[i]; + adc = ADB_DEVICE_GET_CLASS(d); + + if (adc->devhasdata(d)) { + s->pending |= (1 << d->devaddr); + } + } + devaddr = buf[0] >> 4; for (i = 0; i < s->nb_devices; i++) { d = s->devices[i]; + adc = ADB_DEVICE_GET_CLASS(d); + if (d->devaddr == devaddr) { - ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d); return adc->devreq(d, obuf, buf, len); } } + return ADB_RET_NOTPRESENT; } diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index 9b80204e43..f1bc358d8e 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -76,6 +76,7 @@ struct ADBBusState { /*< public >*/ ADBDevice *devices[MAX_ADB_DEVICES]; + uint16_t pending; int nb_devices; int poll_index; From 3fe02cc8b3a5b50fad6d5e7cc3a65fd52d1fad36 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:27 +0100 Subject: [PATCH 13/22] adb: add status field for holding information about the last ADB request Currently only 2 bits are defined: one to indicate if the request timed out (no reply) and another to indicate whether the request was the result of an autopoll operation. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-14-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 14 +++++++++++--- include/hw/input/adb.h | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/input/adb.c b/hw/input/adb.c index c1adb21e6b..a7a482fdfa 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -42,7 +42,7 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) { ADBDevice *d; ADBDeviceClass *adc; - int devaddr, cmd, i; + int devaddr, cmd, olen, i; cmd = buf[0] & 0xf; if (cmd == ADB_BUSRESET) { @@ -50,6 +50,7 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) d = s->devices[i]; adb_device_reset(d); } + s->status = 0; return 0; } @@ -63,16 +64,22 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) } } + s->status = 0; devaddr = buf[0] >> 4; for (i = 0; i < s->nb_devices; i++) { d = s->devices[i]; adc = ADB_DEVICE_GET_CLASS(d); if (d->devaddr == devaddr) { - return adc->devreq(d, obuf, buf, len); + olen = adc->devreq(d, obuf, buf, len); + if (!olen) { + s->status |= ADB_STATUS_BUSTIMEOUT; + } + return olen; } } + s->status |= ADB_STATUS_BUSTIMEOUT; return ADB_RET_NOTPRESENT; } @@ -94,9 +101,10 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) olen = adb_request(s, obuf + 1, buf, 1); /* if there is data, we poll again the same device */ if (olen > 0) { + s->status |= ADB_STATUS_POLLREPLY; obuf[0] = buf[0]; olen++; - break; + return olen; } } s->poll_index++; diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index f1bc358d8e..cff264739c 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -70,6 +70,9 @@ typedef struct ADBDeviceClass { #define TYPE_ADB_BUS "apple-desktop-bus" #define ADB_BUS(obj) OBJECT_CHECK(ADBBusState, (obj), TYPE_ADB_BUS) +#define ADB_STATUS_BUSTIMEOUT 0x1 +#define ADB_STATUS_POLLREPLY 0x2 + struct ADBBusState { /*< private >*/ BusState parent_obj; @@ -79,6 +82,7 @@ struct ADBBusState { uint16_t pending; int nb_devices; int poll_index; + uint8_t status; QEMUTimer *autopoll_timer; bool autopoll_enabled; From d2288b75845d86d4a9486e72f966676ce5c3ed1e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:28 +0100 Subject: [PATCH 14/22] adb: use adb_request() only for explicit requests Currently adb_request() is called both for explicit ADB requests and internal autopoll requests via adb_poll(). Move the current functionality into do_adb_request() to be used internally and add a simple adb_request() wrapper for explicit requests. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-15-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/input/adb.c b/hw/input/adb.c index a7a482fdfa..b3ad7c5fca 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -38,7 +38,8 @@ static void adb_device_reset(ADBDevice *d) qdev_reset_all(DEVICE(d)); } -int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) +static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, + int len) { ADBDevice *d; ADBDeviceClass *adc; @@ -83,6 +84,11 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) return ADB_RET_NOTPRESENT; } +int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) +{ + return do_adb_request(s, obuf, buf, len); +} + /* XXX: move that to cuda ? */ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) { @@ -98,7 +104,7 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) d = s->devices[s->poll_index]; if ((1 << d->devaddr) & poll_mask) { buf[0] = ADB_READREG | (d->devaddr << 4); - olen = adb_request(s, obuf + 1, buf, 1); + olen = do_adb_request(s, obuf + 1, buf, 1); /* if there is data, we poll again the same device */ if (olen > 0) { s->status |= ADB_STATUS_POLLREPLY; From 4e5df0369fb5a49ed26518dccffeb03a252352db Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:29 +0100 Subject: [PATCH 15/22] adb: add autopoll_blocked variable to block autopoll Whilst autopoll is enabled it is necessary to prevent the ADB buffer contents from being overwritten until the host has read back the response in its entirety. Add adb_autopoll_block() and adb_autopoll_unblock() functions in preparation for ensuring that the ADB buffer contents are protected for explicit ADB requests. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-16-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 21 +++++++++++++++++++++ include/hw/input/adb.h | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/hw/input/adb.c b/hw/input/adb.c index b3ad7c5fca..70aa1f4570 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -157,6 +157,26 @@ void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask) } } +void adb_autopoll_block(ADBBusState *s) +{ + s->autopoll_blocked = true; + + if (s->autopoll_enabled) { + timer_del(s->autopoll_timer); + } +} + +void adb_autopoll_unblock(ADBBusState *s) +{ + s->autopoll_blocked = false; + + if (s->autopoll_enabled) { + timer_mod(s->autopoll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + s->autopoll_rate_ms); + } +} + static void adb_autopoll(void *opaque) { ADBBusState *s = opaque; @@ -184,6 +204,7 @@ static const VMStateDescription vmstate_adb_bus = { VMSTATE_BOOL(autopoll_enabled, ADBBusState), VMSTATE_UINT8(autopoll_rate_ms, ADBBusState), VMSTATE_UINT16(autopoll_mask, ADBBusState), + VMSTATE_BOOL(autopoll_blocked, ADBBusState), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index cff264739c..bb75a7b1e3 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -86,6 +86,7 @@ struct ADBBusState { QEMUTimer *autopoll_timer; bool autopoll_enabled; + bool autopoll_blocked; uint8_t autopoll_rate_ms; uint16_t autopoll_mask; void (*autopoll_cb)(void *opaque); @@ -96,6 +97,9 @@ int adb_request(ADBBusState *s, uint8_t *buf_out, const uint8_t *buf, int len); int adb_poll(ADBBusState *s, uint8_t *buf_out, uint16_t poll_mask); +void adb_autopoll_block(ADBBusState *s); +void adb_autopoll_unblock(ADBBusState *s); + void adb_set_autopoll_enabled(ADBBusState *s, bool enabled); void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms); void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask); From 45c9d721ef9ea4e50dbede53bc97922767db70b8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:30 +0100 Subject: [PATCH 16/22] cuda: add adb_autopoll_block() and adb_autopoll_unblock() functions Ensure that the CUDA buffer is protected from autopoll requests overwriting its contents whilst existing CUDA requests are in progress. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-17-mark.cave-ayland@ilande.co.uk> --- hw/misc/macio/cuda.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index b7071e89d5..5bbc7770fa 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -116,6 +116,7 @@ static void cuda_update(CUDAState *s) { MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); + ADBBusState *adb_bus = &s->adb_bus; int packet_received, len; packet_received = 0; @@ -126,6 +127,9 @@ static void cuda_update(CUDAState *s) /* data output */ if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { if (s->data_out_index < sizeof(s->data_out)) { + if (s->data_out_index == 0) { + adb_autopoll_block(adb_bus); + } trace_cuda_data_send(ms->sr); s->data_out[s->data_out_index++] = ms->sr; cuda_delay_set_sr_int(s); @@ -140,6 +144,7 @@ static void cuda_update(CUDAState *s) /* indicate end of transfer */ if (s->data_in_index >= s->data_in_size) { ms->b = (ms->b | TREQ); + adb_autopoll_unblock(adb_bus); } cuda_delay_set_sr_int(s); } From cf093b0772ad43538ba45c1dececec7113ab3d03 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:31 +0100 Subject: [PATCH 17/22] pmu: add adb_autopoll_block() and adb_autopoll_unblock() functions Ensure that the PMU buffer is protected from autopoll requests overwriting its contents whilst existing PMU requests are in progress. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-18-mark.cave-ayland@ilande.co.uk> --- hw/misc/macio/pmu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 01d49e6695..598d8e7517 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -517,6 +517,7 @@ static void pmu_update(PMUState *s) { MOS6522PMUState *mps = &s->mos6522_pmu; MOS6522State *ms = MOS6522(mps); + ADBBusState *adb_bus = &s->adb_bus; /* Only react to changes in reg B */ if (ms->b == s->last_b) { @@ -578,6 +579,7 @@ static void pmu_update(PMUState *s) s->cmd_rsp_pos = 0; s->cmd_state = pmu_state_cmd; + adb_autopoll_block(adb_bus); trace_pmu_debug_protocol_cmd(s->cmd, s->cmdlen, s->rsplen); break; @@ -636,6 +638,7 @@ static void pmu_update(PMUState *s) if (s->cmd_state == pmu_state_rsp && s->rsplen == s->cmd_rsp_pos) { trace_pmu_debug_protocol_cmd_resp_complete(ms->ier); + adb_autopoll_unblock(adb_bus); s->cmd_state = pmu_state_idle; } } From 378a50347931e71b15aaec44c4dc0f9adfa346bf Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:32 +0100 Subject: [PATCH 18/22] mac_via: move VIA1 portB write logic into mos6522_q800_via1_write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the logic is split between the mos6522 portB_write() callback and the memory region used to capture the VIA1 MMIO accesses. Move everything into the latter mos6522_q800_via1_write() function to keep all the logic in one place to make it easier to follow. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-19-mark.cave-ayland@ilande.co.uk> --- hw/misc/mac_via.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 7a28bb37ac..a1dc00d9f6 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -801,11 +801,21 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque); + MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1); MOS6522State *ms = MOS6522(v1s); addr = (addr >> 9) & 0xf; mos6522_write(ms, addr, val, size); + switch (addr) { + case VIA_REG_B: + via1_rtc_update(m); + via1_adb_update(m); + + v1s->last_b = ms->b; + break; + } + via1_one_second_update(v1s); via1_VBL_update(v1s); } @@ -1037,18 +1047,6 @@ static TypeInfo mac_via_info = { }; /* VIA 1 */ -static void mos6522_q800_via1_portB_write(MOS6522State *s) -{ - MOS6522Q800VIA1State *v1s = container_of(s, MOS6522Q800VIA1State, - parent_obj); - MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1); - - via1_rtc_update(m); - via1_adb_update(m); - - v1s->last_b = s->b; -} - static void mos6522_q800_via1_reset(DeviceState *dev) { MOS6522State *ms = MOS6522(dev); @@ -1071,10 +1069,8 @@ static void mos6522_q800_via1_init(Object *obj) static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); dc->reset = mos6522_q800_via1_reset; - mdc->portB_write = mos6522_q800_via1_portB_write; } static const TypeInfo mos6522_q800_via1_type_info = { From 975fcedd3194b486916a4b13afbf8308edf5b1a1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:33 +0100 Subject: [PATCH 19/22] mac_via: rework ADB state machine to be compatible with both MacOS and Linux The existing ADB state machine is designed to work with Linux which has a different interpretation of the state machine detailed in "Guide to the Macintosh Family Hardware". In particular the current Linux implementation includes an extra change to IDLE state when switching the VIA between send and receive modes which does not occur in MacOS, and omitting this transition causes the current mac_via ADB state machine to fail. Rework the ADB state machine accordingly so that it can enumerate and autopoll the ADB under both Linux and MacOS, including the addition of the new adb_autopoll_block() and adb_autopoll_unblock() functions. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-20-mark.cave-ayland@ilande.co.uk> --- hw/misc/mac_via.c | 407 +++++++++++++++++++++++++------------- hw/misc/trace-events | 3 + include/hw/misc/mac_via.h | 1 + 3 files changed, 276 insertions(+), 135 deletions(-) diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index a1dc00d9f6..71b6f92645 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -599,176 +599,312 @@ static void via1_rtc_update(MacVIAState *m) m->cmd = REG_EMPTY; } -static int adb_via_poll(MacVIAState *s, int state, uint8_t *data) +static void adb_via_poll(void *opaque) { + MacVIAState *m = opaque; + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); + MOS6522State *s = MOS6522(v1s); + ADBBusState *adb_bus = &m->adb_bus; + uint8_t obuf[9]; + uint8_t *data = &s->sr; + int olen; + uint16_t pending; + + /* + * Setting vADBInt below indicates that an autopoll reply has been + * received, however we must block autopoll until the point where + * the entire reply has been read back to the host + */ + if (adb_bus->autopoll_blocked) { + return; + } else { + adb_autopoll_block(adb_bus); + } + + m->adb_data_in_index = 0; + m->adb_data_out_index = 0; + olen = adb_poll(adb_bus, obuf, adb_bus->autopoll_mask); + + if (olen > 0) { + /* Autopoll response */ + *data = obuf[0]; + olen--; + memcpy(m->adb_data_in, &obuf[1], olen); + m->adb_data_in_size = olen; + + s->b &= ~VIA1B_vADBInt; + qemu_irq_raise(m->adb_data_ready); + } else if (olen < 0) { + /* Bus timeout (device does not exist) */ + *data = 0xff; + s->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + } else { + pending = adb_bus->pending & ~(1 << (m->adb_autopoll_cmd >> 4)); + + if (pending) { + /* + * Bus timeout (device exists but another device has data). Block + * autopoll so the OS can read out the first EVEN and first ODD + * byte to determine bus timeout and SRQ status + */ + *data = m->adb_autopoll_cmd; + s->b &= ~VIA1B_vADBInt; + + obuf[0] = 0xff; + obuf[1] = 0xff; + olen = 2; + + memcpy(m->adb_data_in, obuf, olen); + m->adb_data_in_size = olen; + + qemu_irq_raise(m->adb_data_ready); + } else { + /* Bus timeout (device exists but no other device has data) */ + *data = 0; + s->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + } + } + + trace_via1_adb_poll(*data, (s->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, m->adb_data_in_index, olen); +} + +static int adb_via_send_len(uint8_t data) +{ + /* Determine the send length from the given ADB command */ + uint8_t cmd = data & 0xc; + uint8_t reg = data & 0x3; + + switch (cmd) { + case 0x8: + /* Listen command */ + switch (reg) { + case 2: + /* Register 2 is only used for the keyboard */ + return 3; + case 3: + /* + * Fortunately our devices only implement writes + * to register 3 which is fixed at 2 bytes + */ + return 3; + default: + qemu_log_mask(LOG_UNIMP, "ADB unknown length for register %d\n", + reg); + return 1; + } + default: + /* Talk, BusReset */ + return 1; + } +} + +static void adb_via_send(MacVIAState *s, int state, uint8_t data) +{ + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1); + MOS6522State *ms = MOS6522(v1s); ADBBusState *adb_bus = &s->adb_bus; + uint16_t autopoll_mask; - if (state != ADB_STATE_IDLE) { - return 0; - } + switch (state) { + case ADB_STATE_NEW: + /* + * Command byte: vADBInt tells host autopoll data already present + * in VIA shift register and ADB transceiver + */ + adb_autopoll_block(adb_bus); - if (s->adb_data_in_size < s->adb_data_in_index) { - return 0; - } + if (adb_bus->status & ADB_STATUS_POLLREPLY) { + /* Tell the host the existing data is from autopoll */ + ms->b &= ~VIA1B_vADBInt; + } else { + ms->b |= VIA1B_vADBInt; + s->adb_data_out_index = 0; + s->adb_data_out[s->adb_data_out_index++] = data; + } - if (s->adb_data_out_index != 0) { - return 0; - } - - s->adb_data_in_index = 0; - s->adb_data_out_index = 0; - s->adb_data_in_size = adb_poll(adb_bus, s->adb_data_in, - adb_bus->autopoll_mask); - - if (s->adb_data_in_size) { - *data = s->adb_data_in[s->adb_data_in_index++]; + trace_via1_adb_send(" NEW", data, (ms->b & VIA1B_vADBInt) ? "+" : "-"); qemu_irq_raise(s->adb_data_ready); - } - - return s->adb_data_in_size; -} - -static int adb_via_send(MacVIAState *s, int state, uint8_t data) -{ - switch (state) { - case ADB_STATE_NEW: - s->adb_data_out_index = 0; break; - case ADB_STATE_EVEN: - if ((s->adb_data_out_index & 1) == 0) { - return 0; - } - break; - case ADB_STATE_ODD: - if (s->adb_data_out_index & 1) { - return 0; - } - break; - case ADB_STATE_IDLE: - return 0; - } - - assert(s->adb_data_out_index < sizeof(s->adb_data_out) - 1); - - s->adb_data_out[s->adb_data_out_index++] = data; - qemu_irq_raise(s->adb_data_ready); - return 1; -} - -static int adb_via_receive(MacVIAState *s, int state, uint8_t *data) -{ - switch (state) { - case ADB_STATE_NEW: - return 0; case ADB_STATE_EVEN: - if (s->adb_data_in_size <= 0) { - qemu_irq_raise(s->adb_data_ready); - return 0; - } - - if (s->adb_data_in_index >= s->adb_data_in_size) { - *data = 0; - qemu_irq_raise(s->adb_data_ready); - return 1; - } - - if ((s->adb_data_in_index & 1) == 0) { - return 0; - } - - break; - case ADB_STATE_ODD: - if (s->adb_data_in_size <= 0) { - qemu_irq_raise(s->adb_data_ready); - return 0; - } - - if (s->adb_data_in_index >= s->adb_data_in_size) { - *data = 0; - qemu_irq_raise(s->adb_data_ready); - return 1; - } - - if (s->adb_data_in_index & 1) { - return 0; - } + ms->b |= VIA1B_vADBInt; + s->adb_data_out[s->adb_data_out_index++] = data; + trace_via1_adb_send(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + data, (ms->b & VIA1B_vADBInt) ? "+" : "-"); + qemu_irq_raise(s->adb_data_ready); break; case ADB_STATE_IDLE: - if (s->adb_data_out_index == 0) { - return 0; - } + return; + } - s->adb_data_in_size = adb_request(&s->adb_bus, s->adb_data_in, + /* If the command is complete, execute it */ + if (s->adb_data_out_index == adb_via_send_len(s->adb_data_out[0])) { + s->adb_data_in_size = adb_request(adb_bus, s->adb_data_in, s->adb_data_out, s->adb_data_out_index); - s->adb_data_out_index = 0; s->adb_data_in_index = 0; - if (s->adb_data_in_size < 0) { - *data = 0xff; - qemu_irq_raise(s->adb_data_ready); - return -1; + + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + /* + * Bus timeout (but allow first EVEN and ODD byte to indicate + * timeout via vADBInt and SRQ status) + */ + s->adb_data_in[0] = 0xff; + s->adb_data_in[1] = 0xff; + s->adb_data_in_size = 2; } - if (s->adb_data_in_size == 0) { - return 0; + /* + * If last command is TALK, store it for use by autopoll and adjust + * the autopoll mask accordingly + */ + if ((s->adb_data_out[0] & 0xc) == 0xc) { + s->adb_autopoll_cmd = s->adb_data_out[0]; + + autopoll_mask = 1 << (s->adb_autopoll_cmd >> 4); + adb_set_autopoll_mask(adb_bus, autopoll_mask); } + } +} + +static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) +{ + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1); + MOS6522State *ms = MOS6522(v1s); + ADBBusState *adb_bus = &s->adb_bus; + uint16_t pending; + + switch (state) { + case ADB_STATE_NEW: + ms->b |= VIA1B_vADBInt; + return; + + case ADB_STATE_IDLE: + /* + * Since adb_request() will have already consumed the data from the + * device, we must detect this extra state change and re-inject the + * reponse as either a "fake" autopoll reply or bus timeout + * accordingly + */ + if (s->adb_data_in_index == 0) { + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + *data = 0xff; + ms->b |= VIA1B_vADBInt; + qemu_irq_raise(s->adb_data_ready); + } else if (s->adb_data_in_size > 0) { + adb_bus->status = ADB_STATUS_POLLREPLY; + *data = s->adb_autopoll_cmd; + ms->b &= ~VIA1B_vADBInt; + qemu_irq_raise(s->adb_data_ready); + } + } else { + ms->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + } + + trace_via1_adb_receive("IDLE", *data, + (ms->b & VIA1B_vADBInt) ? "+" : "-", adb_bus->status, + s->adb_data_in_index, s->adb_data_in_size); break; - } - assert(s->adb_data_in_index < sizeof(s->adb_data_in) - 1); + case ADB_STATE_EVEN: + case ADB_STATE_ODD: + switch (s->adb_data_in_index) { + case 0: + /* First EVEN byte: vADBInt indicates bus timeout */ + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); - *data = s->adb_data_in[s->adb_data_in_index++]; - qemu_irq_raise(s->adb_data_ready); - if (*data == 0xff || *data == 0) { - return 0; + *data = s->adb_data_in[s->adb_data_in_index++]; + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + ms->b &= ~VIA1B_vADBInt; + } else { + ms->b |= VIA1B_vADBInt; + } + break; + + case 1: + /* First ODD byte: vADBInt indicates SRQ */ + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + *data = s->adb_data_in[s->adb_data_in_index++]; + pending = adb_bus->pending & ~(1 << (s->adb_autopoll_cmd >> 4)); + if (pending) { + ms->b &= ~VIA1B_vADBInt; + } else { + ms->b |= VIA1B_vADBInt; + } + break; + + default: + /* + * Otherwise vADBInt indicates end of data. Note that Linux + * specifically checks for the sequence 0x0 0xff to confirm the + * end of the poll reply, so provide these extra bytes below to + * keep it happy + */ + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + if (s->adb_data_in_index < s->adb_data_in_size) { + /* Next data byte */ + *data = s->adb_data_in[s->adb_data_in_index++]; + ms->b |= VIA1B_vADBInt; + } else if (s->adb_data_in_index == s->adb_data_in_size) { + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + /* Bus timeout (no more data) */ + *data = 0xff; + } else { + /* Return 0x0 after reply */ + *data = 0; + } + s->adb_data_in_index++; + ms->b &= ~VIA1B_vADBInt; + } else { + /* Bus timeout (no more data) */ + *data = 0xff; + ms->b &= ~VIA1B_vADBInt; + adb_bus->status = 0; + adb_autopoll_unblock(adb_bus); + } + break; + } + + qemu_irq_raise(s->adb_data_ready); + break; } - return 1; } static void via1_adb_update(MacVIAState *m) { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); MOS6522State *s = MOS6522(v1s); - int state; - int ret; + int oldstate, state; + oldstate = (v1s->last_b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; - if (s->acr & VIA1ACR_vShiftOut) { - /* output mode */ - ret = adb_via_send(m, state, s->sr); - if (ret > 0) { - s->b &= ~VIA1B_vADBInt; + if (state != oldstate) { + if (s->acr & VIA1ACR_vShiftOut) { + /* output mode */ + adb_via_send(m, state, s->sr); } else { - s->b |= VIA1B_vADBInt; - } - } else { - /* input mode */ - ret = adb_via_receive(m, state, &s->sr); - if (ret > 0 && s->sr != 0xff) { - s->b &= ~VIA1B_vADBInt; - } else { - s->b |= VIA1B_vADBInt; - } - } -} - -static void via_adb_poll(void *opaque) -{ - MacVIAState *m = opaque; - MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); - MOS6522State *s = MOS6522(v1s); - int state; - - if (s->b & VIA1B_vADBInt) { - state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; - if (adb_via_poll(m, state, &s->sr)) { - s->b &= ~VIA1B_vADBInt; + /* input mode */ + adb_via_receive(m, state, &s->sr); } } } @@ -916,7 +1052,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp) qemu_get_timedate(&tm, 0); m->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; - adb_register_autopoll_callback(adb_bus, via_adb_poll, m); + adb_register_autopoll_callback(adb_bus, adb_via_poll, m); m->adb_data_ready = qdev_get_gpio_in_named(dev, "via1-irq", VIA1_IRQ_ADB_READY_BIT); @@ -1019,6 +1155,7 @@ static const VMStateDescription vmstate_mac_via = { VMSTATE_INT32(adb_data_out_index, MacVIAState), VMSTATE_BUFFER(adb_data_in, MacVIAState), VMSTATE_BUFFER(adb_data_out, MacVIAState), + VMSTATE_UINT8(adb_autopoll_cmd, MacVIAState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 5561746866..68a6d9f2ab 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -202,6 +202,9 @@ via1_rtc_cmd_pram_read(int addr, int value) "addr=%u value=0x%02x" via1_rtc_cmd_pram_write(int addr, int value) "addr=%u value=0x%02x" via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" +via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state %s data=0x%02x vADBInt=%s" +via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" +via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" # grlib_ahb_apb_pnp.c grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x" diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 2aaf9e27bf..0be05d649b 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -112,6 +112,7 @@ typedef struct MacVIAState { int adb_data_out_index; uint8_t adb_data_in[128]; uint8_t adb_data_out[16]; + uint8_t adb_autopoll_cmd; } MacVIAState; #endif From 913f47ef96990fc72135ec604b6bb1fe2d1e389a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:34 +0100 Subject: [PATCH 20/22] adb: only call autopoll callbacks when autopoll is not blocked Handle this at the ADB bus level so that individual implementations do not need to handle this themselves. Finally add an assert() into adb_request() to prevent developers from accidentally making an explicit ADB request without blocking autopoll. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-21-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 7 +++++-- hw/misc/mac_via.c | 6 +----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/input/adb.c b/hw/input/adb.c index 70aa1f4570..fe0f6c7ef3 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -86,10 +86,11 @@ static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) { + assert(s->autopoll_blocked); + return do_adb_request(s, obuf, buf, len); } -/* XXX: move that to cuda ? */ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) { ADBDevice *d; @@ -181,7 +182,9 @@ static void adb_autopoll(void *opaque) { ADBBusState *s = opaque; - s->autopoll_cb(s->autopoll_cb_opaque); + if (!s->autopoll_blocked) { + s->autopoll_cb(s->autopoll_cb_opaque); + } timer_mod(s->autopoll_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 71b6f92645..d76d7b28d3 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -615,11 +615,7 @@ static void adb_via_poll(void *opaque) * received, however we must block autopoll until the point where * the entire reply has been read back to the host */ - if (adb_bus->autopoll_blocked) { - return; - } else { - adb_autopoll_block(adb_bus); - } + adb_autopoll_block(adb_bus); m->adb_data_in_index = 0; m->adb_data_out_index = 0; From fa6c9539645a2e2df9ef5c423ccfa7d849dee41a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:35 +0100 Subject: [PATCH 21/22] adb: use adb_device prefix for ADB device trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to allow us to distinguish between ADB device events and ADB bus events separately. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-22-mark.cave-ayland@ilande.co.uk> --- hw/input/adb-kbd.c | 12 ++++++------ hw/input/adb-mouse.c | 12 ++++++------ hw/input/trace-events | 20 ++++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 23760ecf7b..3cfb6a7a20 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -243,7 +243,7 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, olen = 0; switch (cmd) { case ADB_WRITEREG: - trace_adb_kbd_writereg(reg, buf[1]); + trace_adb_device_kbd_writereg(reg, buf[1]); switch (reg) { case 2: /* LED status */ @@ -256,7 +256,7 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, case ADB_CMD_CHANGE_ID_AND_ACT: case ADB_CMD_CHANGE_ID_AND_ENABLE: d->devaddr = buf[1] & 0xf; - trace_adb_kbd_request_change_addr(d->devaddr); + trace_adb_device_kbd_request_change_addr(d->devaddr); break; default: d->devaddr = buf[1] & 0xf; @@ -270,8 +270,8 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, d->handler = buf[2]; } - trace_adb_kbd_request_change_addr_and_handler(d->devaddr, - d->handler); + trace_adb_device_kbd_request_change_addr_and_handler( + d->devaddr, d->handler); break; } } @@ -294,7 +294,7 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, olen = 2; break; } - trace_adb_kbd_readreg(reg, obuf[0], obuf[1]); + trace_adb_device_kbd_readreg(reg, obuf[0], obuf[1]); break; } return olen; @@ -321,7 +321,7 @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, /* FIXME: take handler into account when translating qcode */ keycode = qcode_to_adb_keycode[qcode]; if (keycode == NO_KEY) { /* We don't want to send this to the guest */ - trace_adb_kbd_no_key(); + trace_adb_device_kbd_no_key(); return; } if (evt->u.key.data->down == false) { /* if key release event */ diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index e2359fd74d..577a38ff2e 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -121,7 +121,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, s->dx = 0; s->dy = 0; s->dz = 0; - trace_adb_mouse_flush(); + trace_adb_device_mouse_flush(); return 0; } @@ -130,7 +130,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, olen = 0; switch (cmd) { case ADB_WRITEREG: - trace_adb_mouse_writereg(reg, buf[1]); + trace_adb_device_mouse_writereg(reg, buf[1]); switch (reg) { case 2: break; @@ -152,7 +152,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, case ADB_CMD_CHANGE_ID_AND_ACT: case ADB_CMD_CHANGE_ID_AND_ENABLE: d->devaddr = buf[1] & 0xf; - trace_adb_mouse_request_change_addr(d->devaddr); + trace_adb_device_mouse_request_change_addr(d->devaddr); break; default: d->devaddr = buf[1] & 0xf; @@ -172,8 +172,8 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, d->handler = buf[2]; } - trace_adb_mouse_request_change_addr_and_handler(d->devaddr, - d->handler); + trace_adb_device_mouse_request_change_addr_and_handler( + d->devaddr, d->handler); break; } } @@ -191,7 +191,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, olen = 2; break; } - trace_adb_mouse_readreg(reg, obuf[0], obuf[1]); + trace_adb_device_mouse_readreg(reg, obuf[0], obuf[1]); break; } return olen; diff --git a/hw/input/trace-events b/hw/input/trace-events index a2888fd10c..6f0d78241c 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -1,18 +1,18 @@ # See docs/devel/tracing.txt for syntax documentation. # adb-kbd.c -adb_kbd_no_key(void) "Ignoring NO_KEY" -adb_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" -adb_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" -adb_kbd_request_change_addr(int devaddr) "change addr to 0x%x" -adb_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" +adb_device_kbd_no_key(void) "Ignoring NO_KEY" +adb_device_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" +adb_device_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" +adb_device_kbd_request_change_addr(int devaddr) "change addr to 0x%x" +adb_device_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" # adb-mouse.c -adb_mouse_flush(void) "flush" -adb_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" -adb_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" -adb_mouse_request_change_addr(int devaddr) "change addr to 0x%x" -adb_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" +adb_device_mouse_flush(void) "flush" +adb_device_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" +adb_device_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" +adb_device_mouse_request_change_addr(int devaddr) "change addr to 0x%x" +adb_device_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" # pckbd.c pckbd_kbd_read_data(uint32_t val) "0x%02x" From e590e7f01479a1d4544aac062fe9fdb986502294 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 23 Jun 2020 21:49:36 +0100 Subject: [PATCH 22/22] adb: add ADB bus trace events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Finn Thain Acked-by: Laurent Vivier Message-Id: <20200623204936.24064-23-mark.cave-ayland@ilande.co.uk> --- hw/input/adb.c | 21 ++++++++++++++++++++- hw/input/trace-events | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/hw/input/adb.c b/hw/input/adb.c index fe0f6c7ef3..013fcc9c54 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -29,10 +29,18 @@ #include "qemu/module.h" #include "qemu/timer.h" #include "adb-internal.h" +#include "trace.h" /* error codes */ #define ADB_RET_NOTPRESENT (-2) +static const char *adb_commands[] = { + "RESET", "FLUSH", "(Reserved 0x2)", "(Reserved 0x3)", + "Reserved (0x4)", "(Reserved 0x5)", "(Reserved 0x6)", "(Reserved 0x7)", + "LISTEN r0", "LISTEN r1", "LISTEN r2", "LISTEN r3", + "TALK r0", "TALK r1", "TALK r2", "TALK r3", +}; + static void adb_device_reset(ADBDevice *d) { qdev_reset_all(DEVICE(d)); @@ -86,9 +94,16 @@ static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) { + int ret; + + trace_adb_bus_request(buf[0] >> 4, adb_commands[buf[0] & 0xf], len); + assert(s->autopoll_blocked); - return do_adb_request(s, obuf, buf, len); + ret = do_adb_request(s, obuf, buf, len); + + trace_adb_bus_request_done(buf[0] >> 4, adb_commands[buf[0] & 0xf], ret); + return ret; } int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) @@ -161,6 +176,7 @@ void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask) void adb_autopoll_block(ADBBusState *s) { s->autopoll_blocked = true; + trace_adb_bus_autopoll_block(s->autopoll_blocked); if (s->autopoll_enabled) { timer_del(s->autopoll_timer); @@ -170,6 +186,7 @@ void adb_autopoll_block(ADBBusState *s) void adb_autopoll_unblock(ADBBusState *s) { s->autopoll_blocked = false; + trace_adb_bus_autopoll_block(s->autopoll_blocked); if (s->autopoll_enabled) { timer_mod(s->autopoll_timer, @@ -183,7 +200,9 @@ static void adb_autopoll(void *opaque) ADBBusState *s = opaque; if (!s->autopoll_blocked) { + trace_adb_bus_autopoll_cb(s->autopoll_mask); s->autopoll_cb(s->autopoll_cb_opaque); + trace_adb_bus_autopoll_cb_done(s->autopoll_mask); } timer_mod(s->autopoll_timer, diff --git a/hw/input/trace-events b/hw/input/trace-events index 6f0d78241c..1dd8ad6018 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -14,6 +14,13 @@ adb_device_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x adb_device_mouse_request_change_addr(int devaddr) "change addr to 0x%x" adb_device_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" +# adb.c +adb_bus_request(uint8_t addr, const char *cmd, int size) "device 0x%x %s cmdsize=%d" +adb_bus_request_done(uint8_t addr, const char *cmd, int size) "device 0x%x %s replysize=%d" +adb_bus_autopoll_block(bool blocked) "blocked: %d" +adb_bus_autopoll_cb(uint16_t mask) "executing autopoll_cb with autopoll mask 0x%x" +adb_bus_autopoll_cb_done(uint16_t mask) "done executing autopoll_cb with autopoll mask 0x%x" + # pckbd.c pckbd_kbd_read_data(uint32_t val) "0x%02x" pckbd_kbd_read_status(int status) "0x%02x"