From nobody Tue Feb 10 10:57:40 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1592946256; cv=none; d=zohomail.com; s=zohoarc; b=kWsK832x4MDEE7/k/t0wiDM+JNlHjLuE+pZiKh1ne27SGTW96jFQ53Hsd7ykD8xHL3KtlvqqtErh7i210ReETQTYlKCKs6Ca7OXRd7jdF3egM3AZ/x6L2NRFU78HKgpRIvfu4jTgRjwuqp4DdfCYiAfW5e2a602GkC7Xkbl+Who= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1592946256; h=Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=g4wMeI4SylSOJjFjSRSh2UVCNgOVrpa+6UK/XMQOw84=; b=bz1VmRoOee5S+FqFoRn3p+cLPyApM3tK31YGooJn5YcGnkxvKPJy5FAdkg8BGLzbv4XwvHPCBB+jmIRi6pB3Ht6OTYdCjaci4zzazbB2CaRDihlIUwSISuPlaqRk1vPV2STOY+0DUwt8OohgPQ7YpfiSYaCa82iE8GY33LskIZs= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1592946256557237.76316524672052; Tue, 23 Jun 2020 14:04:16 -0700 (PDT) Received: from localhost ([::1]:58004 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jnq5W-0002ob-Rq for importer@patchew.org; Tue, 23 Jun 2020 17:04:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:45728) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jnpt8-0005X7-Nz; Tue, 23 Jun 2020 16:51:26 -0400 Received: from mail.ilande.co.uk ([2001:41c9:1:41f::167]:56484 helo=mail.default.ilande.uk0.bigv.io) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jnpt5-0002B8-T0; Tue, 23 Jun 2020 16:51:26 -0400 Received: from host86-158-109-79.range86-158.btcentralplus.com ([86.158.109.79] helo=kentang.home) by mail.default.ilande.uk0.bigv.io with esmtpsa (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jnpt2-0007T1-L8; Tue, 23 Jun 2020 21:51:25 +0100 From: Mark Cave-Ayland To: qemu-devel@nongnu.org, qemu-ppc@nongnu.org, laurent@vivier.eu, fthain@telegraphics.com.au Date: Tue, 23 Jun 2020 21:49:33 +0100 Message-Id: <20200623204936.24064-20-mark.cave-ayland@ilande.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200623204936.24064-1-mark.cave-ayland@ilande.co.uk> References: <20200623204936.24064-1-mark.cave-ayland@ilande.co.uk> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-SA-Exim-Connect-IP: 86.158.109.79 X-SA-Exim-Mail-From: mark.cave-ayland@ilande.co.uk Subject: [PATCH v2 19/22] mac_via: rework ADB state machine to be compatible with both MacOS and Linux X-SA-Exim-Version: 4.2.1 (built Wed, 08 May 2019 21:11:16 +0000) X-SA-Exim-Scanned: Yes (on mail.default.ilande.uk0.bigv.io) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:41c9:1:41f::167; envelope-from=mark.cave-ayland@ilande.co.uk; helo=mail.default.ilande.uk0.bigv.io X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=_AUTOLEARN X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" The existing ADB state machine is designed to work with Linux which has a d= ifferent interpretation of the state machine detailed in "Guide to the Macintosh Fam= ily Hardware". In particular the current Linux implementation includes an extra= change to IDLE state when switching the VIA between send and receive modes which d= oes 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 autop= oll the ADB under both Linux and MacOS, including the addition of the new adb_autop= oll_block() and adb_autopoll_unblock() functions. Signed-off-by: Mark Cave-Ayland Tested-by: Finn Thain --- hw/misc/mac_via.c | 375 ++++++++++++++++++++++++++------------ hw/misc/trace-events | 3 + include/hw/misc/mac_via.h | 1 + 3 files changed, 260 insertions(+), 119 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 =3D REG_EMPTY; } =20 -static int adb_via_poll(MacVIAState *s, int state, uint8_t *data) +static void adb_via_poll(void *opaque) { - ADBBusState *adb_bus =3D &s->adb_bus; + MacVIAState *m =3D opaque; + MOS6522Q800VIA1State *v1s =3D MOS6522_Q800_VIA1(&m->mos6522_via1); + MOS6522State *s =3D MOS6522(v1s); + ADBBusState *adb_bus =3D &m->adb_bus; + uint8_t obuf[9]; + uint8_t *data =3D &s->sr; + int olen; + uint16_t pending; =20 - if (state !=3D ADB_STATE_IDLE) { - return 0; + /* + * 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); } =20 - if (s->adb_data_in_size < s->adb_data_in_index) { - return 0; - } + m->adb_data_in_index =3D 0; + m->adb_data_out_index =3D 0; + olen =3D adb_poll(adb_bus, obuf, adb_bus->autopoll_mask); + + if (olen > 0) { + /* Autopoll response */ + *data =3D obuf[0]; + olen--; + memcpy(m->adb_data_in, &obuf[1], olen); + m->adb_data_in_size =3D olen; + + s->b &=3D ~VIA1B_vADBInt; + qemu_irq_raise(m->adb_data_ready); + } else if (olen < 0) { + /* Bus timeout (device does not exist) */ + *data =3D 0xff; + s->b |=3D VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + } else { + pending =3D adb_bus->pending & ~(1 << (m->adb_autopoll_cmd >> 4)); + + if (pending) { + /* + * Bus timeout (device exists but another device has data). Bl= ock + * autopoll so the OS can read out the first EVEN and first ODD + * byte to determine bus timeout and SRQ status + */ + *data =3D m->adb_autopoll_cmd; + s->b &=3D ~VIA1B_vADBInt; =20 - if (s->adb_data_out_index !=3D 0) { - return 0; - } + obuf[0] =3D 0xff; + obuf[1] =3D 0xff; + olen =3D 2; =20 - s->adb_data_in_index =3D 0; - s->adb_data_out_index =3D 0; - s->adb_data_in_size =3D adb_poll(adb_bus, s->adb_data_in, - adb_bus->autopoll_mask); + memcpy(m->adb_data_in, obuf, olen); + m->adb_data_in_size =3D olen; =20 - if (s->adb_data_in_size) { - *data =3D s->adb_data_in[s->adb_data_in_index++]; - qemu_irq_raise(s->adb_data_ready); + qemu_irq_raise(m->adb_data_ready); + } else { + /* Bus timeout (device exists but no other device has data) */ + *data =3D 0; + s->b |=3D VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + } } =20 - return s->adb_data_in_size; + trace_via1_adb_poll(*data, (s->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, m->adb_data_in_index, olen); } =20 -static int adb_via_send(MacVIAState *s, int state, uint8_t data) +static int adb_via_send_len(uint8_t data) { - switch (state) { - case ADB_STATE_NEW: - s->adb_data_out_index =3D 0; - break; - case ADB_STATE_EVEN: - if ((s->adb_data_out_index & 1) =3D=3D 0) { - return 0; - } - break; - case ADB_STATE_ODD: - if (s->adb_data_out_index & 1) { - return 0; + /* Determine the send length from the given ADB command */ + uint8_t cmd =3D data & 0xc; + uint8_t reg =3D 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; } - break; - case ADB_STATE_IDLE: - return 0; + default: + /* Talk, BusReset */ + return 1; } - - assert(s->adb_data_out_index < sizeof(s->adb_data_out) - 1); - - s->adb_data_out[s->adb_data_out_index++] =3D data; - qemu_irq_raise(s->adb_data_ready); - return 1; } =20 -static int adb_via_receive(MacVIAState *s, int state, uint8_t *data) +static void adb_via_send(MacVIAState *s, int state, uint8_t data) { + MOS6522Q800VIA1State *v1s =3D MOS6522_Q800_VIA1(&s->mos6522_via1); + MOS6522State *ms =3D MOS6522(v1s); + ADBBusState *adb_bus =3D &s->adb_bus; + uint16_t autopoll_mask; + switch (state) { case ADB_STATE_NEW: - return 0; - - case ADB_STATE_EVEN: - if (s->adb_data_in_size <=3D 0) { - qemu_irq_raise(s->adb_data_ready); - return 0; - } - - if (s->adb_data_in_index >=3D s->adb_data_in_size) { - *data =3D 0; - qemu_irq_raise(s->adb_data_ready); - return 1; - } - - if ((s->adb_data_in_index & 1) =3D=3D 0) { - return 0; + /* + * Command byte: vADBInt tells host autopoll data already present + * in VIA shift register and ADB transceiver + */ + adb_autopoll_block(adb_bus); + + if (adb_bus->status & ADB_STATUS_POLLREPLY) { + /* Tell the host the existing data is from autopoll */ + ms->b &=3D ~VIA1B_vADBInt; + } else { + ms->b |=3D VIA1B_vADBInt; + s->adb_data_out_index =3D 0; + s->adb_data_out[s->adb_data_out_index++] =3D data; } =20 + trace_via1_adb_send(" NEW", data, (ms->b & VIA1B_vADBInt) ? "+" : = "-"); + qemu_irq_raise(s->adb_data_ready); break; =20 + case ADB_STATE_EVEN: case ADB_STATE_ODD: - if (s->adb_data_in_size <=3D 0) { - qemu_irq_raise(s->adb_data_ready); - return 0; - } - - if (s->adb_data_in_index >=3D s->adb_data_in_size) { - *data =3D 0; - qemu_irq_raise(s->adb_data_ready); - return 1; - } - - if (s->adb_data_in_index & 1) { - return 0; - } + ms->b |=3D VIA1B_vADBInt; + s->adb_data_out[s->adb_data_out_index++] =3D data; =20 + trace_via1_adb_send(state =3D=3D ADB_STATE_EVEN ? "EVEN" : " ODD", + data, (ms->b & VIA1B_vADBInt) ? "+" : "-"); + qemu_irq_raise(s->adb_data_ready); break; =20 case ADB_STATE_IDLE: - if (s->adb_data_out_index =3D=3D 0) { - return 0; - } + return; + } =20 - s->adb_data_in_size =3D adb_request(&s->adb_bus, s->adb_data_in, + /* If the command is complete, execute it */ + if (s->adb_data_out_index =3D=3D adb_via_send_len(s->adb_data_out[0]))= { + s->adb_data_in_size =3D adb_request(adb_bus, s->adb_data_in, s->adb_data_out, s->adb_data_out_index); - s->adb_data_out_index =3D 0; s->adb_data_in_index =3D 0; - if (s->adb_data_in_size < 0) { - *data =3D 0xff; - qemu_irq_raise(s->adb_data_ready); - return -1; - } =20 - if (s->adb_data_in_size =3D=3D 0) { - return 0; + 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] =3D 0xff; + s->adb_data_in[1] =3D 0xff; + s->adb_data_in_size =3D 2; } =20 - break; - } - - assert(s->adb_data_in_index < sizeof(s->adb_data_in) - 1); + /* + * If last command is TALK, store it for use by autopoll and adjust + * the autopoll mask accordingly + */ + if ((s->adb_data_out[0] & 0xc) =3D=3D 0xc) { + s->adb_autopoll_cmd =3D s->adb_data_out[0]; =20 - *data =3D s->adb_data_in[s->adb_data_in_index++]; - qemu_irq_raise(s->adb_data_ready); - if (*data =3D=3D 0xff || *data =3D=3D 0) { - return 0; + autopoll_mask =3D 1 << (s->adb_autopoll_cmd >> 4); + adb_set_autopoll_mask(adb_bus, autopoll_mask); + } } - return 1; } =20 -static void via1_adb_update(MacVIAState *m) +static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) { - MOS6522Q800VIA1State *v1s =3D MOS6522_Q800_VIA1(&m->mos6522_via1); - MOS6522State *s =3D MOS6522(v1s); - int state; - int ret; + MOS6522Q800VIA1State *v1s =3D MOS6522_Q800_VIA1(&s->mos6522_via1); + MOS6522State *ms =3D MOS6522(v1s); + ADBBusState *adb_bus =3D &s->adb_bus; + uint16_t pending; =20 - state =3D (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; + switch (state) { + case ADB_STATE_NEW: + ms->b |=3D VIA1B_vADBInt; + return; =20 - if (s->acr & VIA1ACR_vShiftOut) { - /* output mode */ - ret =3D adb_via_send(m, state, s->sr); - if (ret > 0) { - s->b &=3D ~VIA1B_vADBInt; + 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 =3D=3D 0) { + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + *data =3D 0xff; + ms->b |=3D VIA1B_vADBInt; + qemu_irq_raise(s->adb_data_ready); + } else if (s->adb_data_in_size > 0) { + adb_bus->status =3D ADB_STATUS_POLLREPLY; + *data =3D s->adb_autopoll_cmd; + ms->b &=3D ~VIA1B_vADBInt; + qemu_irq_raise(s->adb_data_ready); + } } else { - s->b |=3D VIA1B_vADBInt; + ms->b |=3D VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); } - } else { - /* input mode */ - ret =3D adb_via_receive(m, state, &s->sr); - if (ret > 0 && s->sr !=3D 0xff) { - s->b &=3D ~VIA1B_vADBInt; - } else { - s->b |=3D VIA1B_vADBInt; + + trace_via1_adb_receive("IDLE", *data, + (ms->b & VIA1B_vADBInt) ? "+" : "-", adb_bus->stat= us, + s->adb_data_in_index, s->adb_data_in_size); + + break; + + 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 =3D=3D ADB_STATE_EVEN ? "EVEN" : = " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : = "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + *data =3D s->adb_data_in[s->adb_data_in_index++]; + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + ms->b &=3D ~VIA1B_vADBInt; + } else { + ms->b |=3D VIA1B_vADBInt; + } + break; + + case 1: + /* First ODD byte: vADBInt indicates SRQ */ + trace_via1_adb_receive(state =3D=3D ADB_STATE_EVEN ? "EVEN" : = " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : = "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + *data =3D s->adb_data_in[s->adb_data_in_index++]; + pending =3D adb_bus->pending & ~(1 << (s->adb_autopoll_cmd >> = 4)); + if (pending) { + ms->b &=3D ~VIA1B_vADBInt; + } else { + ms->b |=3D 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 =3D=3D 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 =3D s->adb_data_in[s->adb_data_in_index++]; + ms->b |=3D VIA1B_vADBInt; + } else if (s->adb_data_in_index =3D=3D s->adb_data_in_size) { + if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { + /* Bus timeout (no more data) */ + *data =3D 0xff; + } else { + /* Return 0x0 after reply */ + *data =3D 0; + } + s->adb_data_in_index++; + ms->b &=3D ~VIA1B_vADBInt; + } else { + /* Bus timeout (no more data) */ + *data =3D 0xff; + ms->b &=3D ~VIA1B_vADBInt; + adb_bus->status =3D 0; + adb_autopoll_unblock(adb_bus); + } + break; } + + qemu_irq_raise(s->adb_data_ready); + break; } } =20 -static void via_adb_poll(void *opaque) +static void via1_adb_update(MacVIAState *m) { - MacVIAState *m =3D opaque; MOS6522Q800VIA1State *v1s =3D MOS6522_Q800_VIA1(&m->mos6522_via1); MOS6522State *s =3D MOS6522(v1s); - int state; + int oldstate, state; =20 - if (s->b & VIA1B_vADBInt) { - state =3D (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; - if (adb_via_poll(m, state, &s->sr)) { - s->b &=3D ~VIA1B_vADBInt; + oldstate =3D (v1s->last_b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateS= hift; + state =3D (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; + + if (state !=3D oldstate) { + if (s->acr & VIA1ACR_vShiftOut) { + /* output mode */ + adb_via_send(m, state, s->sr); + } else { + /* 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 =3D (uint32_t)mktimegm(&tm) + RTC_OFFSET; =20 - adb_register_autopoll_callback(adb_bus, via_adb_poll, m); + adb_register_autopoll_callback(adb_bus, adb_via_poll, m); m->adb_data_ready =3D qdev_get_gpio_in_named(dev, "via1-irq", VIA1_IRQ_ADB_READY_BIT); =20 @@ -1019,6 +1155,7 @@ static const VMStateDescription vmstate_mac_via =3D { 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=3D%u = value=3D0x%02x" via1_rtc_cmd_pram_write(int addr, int value) "addr=3D%u value=3D0x%02x" via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "= sector=3D%u offset=3D%u addr=3D%d value=3D0x%02x" via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) = "sector=3D%u offset=3D%u addr=3D%d value=3D0x%02x" +via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state= %s data=3D0x%02x vADBInt=3D%s" +via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int= status, int index, int size) "state %s data=3D0x%02x vADBInt=3D%s status= =3D0x%x index=3D%d size=3D%d" +via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, in= t size) "data=3D0x%02x vADBInt=3D%s status=3D0x%x index=3D%d size=3D%d" =20 # 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; =20 #endif --=20 2.20.1