From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:38346) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QOalV-0003zV-9a for qemu-devel@nongnu.org; Mon, 23 May 2011 15:26:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QOalP-0006bt-GD for qemu-devel@nongnu.org; Mon, 23 May 2011 15:26:09 -0400 Received: from mail-qw0-f45.google.com ([209.85.216.45]:48782) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QOalP-0006bo-1b for qemu-devel@nongnu.org; Mon, 23 May 2011 15:26:03 -0400 Received: by qwj8 with SMTP id 8so3590122qwj.4 for ; Mon, 23 May 2011 12:26:02 -0700 (PDT) MIME-Version: 1.0 In-Reply-To: <1306143819-30287-19-git-send-email-kraxel@redhat.com> References: <1306143819-30287-1-git-send-email-kraxel@redhat.com> <1306143819-30287-19-git-send-email-kraxel@redhat.com> From: Blue Swirl Date: Mon, 23 May 2011 22:25:41 +0300 Message-ID: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH 18/18] usb: add ehci adapter List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Gerd Hoffmann Cc: Vincent Palatin , Jan Kiszka , Kevin Wolf , qemu-devel@nongnu.org, "David S. Ahern" On Mon, May 23, 2011 at 12:43 PM, Gerd Hoffmann wrote: > This patch finally merges the EHCI host adapter aka USB 2.0 support. > > Based on the ehci bits collected @ git://git.kiszka.org/qemu.git ehci > > EHCI has a long out-of-tree history. =C2=A0Project was started by Mark > Burkley, with contributions by Niels de Vos. =C2=A0David S. Ahern continu= ed > working on it. =C2=A0Kevin Wolf, Jan Kiszka and Vincent Palatin contribut= ed > bugfixes. > > /me (Gerd Hoffmann) picked it up where it left off, prepared the code > for merge, fixed a few bugs and added basic user docs. > > Cc: David S. Ahern > Cc: Jan Kiszka > Cc: Kevin Wolf > Cc: Vincent Palatin > Signed-off-by: Gerd Hoffmann > --- > =C2=A0Makefile.objs =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | =C2=A0 =C2=A01 + > =C2=A0default-configs/pci.mak | =C2=A0 =C2=A01 + > =C2=A0docs/usb2.txt =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | =C2=A0 38 + > =C2=A0hw/pci_ids.h =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =C2=A0 =C2= =A01 + > =C2=A0hw/usb-ehci.c =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 2038 +++++++++++= ++++++++++++++++++++++++++++++++++++ > =C2=A05 files changed, 2079 insertions(+), 0 deletions(-) > =C2=A0create mode 100644 docs/usb2.txt > =C2=A0create mode 100644 hw/usb-ehci.c > > diff --git a/Makefile.objs b/Makefile.objs > index 4478c61..90838f6 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -193,6 +193,7 @@ hw-obj-$(CONFIG_PCSPK) +=3D pcspk.o > =C2=A0hw-obj-$(CONFIG_PCKBD) +=3D pckbd.o > =C2=A0hw-obj-$(CONFIG_USB_UHCI) +=3D usb-uhci.o > =C2=A0hw-obj-$(CONFIG_USB_OHCI) +=3D usb-ohci.o > +hw-obj-$(CONFIG_USB_EHCI) +=3D usb-ehci.o > =C2=A0hw-obj-$(CONFIG_FDC) +=3D fdc.o > =C2=A0hw-obj-$(CONFIG_ACPI) +=3D acpi.o acpi_piix4.o > =C2=A0hw-obj-$(CONFIG_APM) +=3D pm_smbus.o apm.o > diff --git a/default-configs/pci.mak b/default-configs/pci.mak > index 0471efb..22bd350 100644 > --- a/default-configs/pci.mak > +++ b/default-configs/pci.mak > @@ -3,6 +3,7 @@ CONFIG_VIRTIO_PCI=3Dy > =C2=A0CONFIG_VIRTIO=3Dy > =C2=A0CONFIG_USB_UHCI=3Dy > =C2=A0CONFIG_USB_OHCI=3Dy > +CONFIG_USB_EHCI=3Dy > =C2=A0CONFIG_NE2000_PCI=3Dy > =C2=A0CONFIG_EEPRO100_PCI=3Dy > =C2=A0CONFIG_PCNET_PCI=3Dy > diff --git a/docs/usb2.txt b/docs/usb2.txt > new file mode 100644 > index 0000000..b283c13 > --- /dev/null > +++ b/docs/usb2.txt > @@ -0,0 +1,38 @@ > + > +USB 2.0 Quick Start > +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > + > +The QEMU EHCI Adapter does *not* support companion controllers. =C2=A0Th= at > +implies there are two completely separate USB busses: One USB 1.1 bus > +driven by the UHCI controller and one USB 2.0 bus driven by the EHCI > +controller. =C2=A0Devices must be attached to the correct controller > +manually. > + > +The '-usb' switch will make qemu create the UHCI controller as part of > +the PIIX3 chipset. =C2=A0The USB 1.1 bus will carry the name "usb.0". > + > +You can use the standard -device switch to add a EHCI controller to > +your virtual machine. =C2=A0It is strongly recommended to specify an ID = for > +the controller so the USB 2.0 bus gets a individual name, for example > +'-device usb-ehci,id=3Dehci". =C2=A0This will give you a USB 2.0 bus nam= ed > +"ehci.0". > + > +I strongly recomment to also use -device to attach usb devices because > +you can specify the bus they should be attached to this way. =C2=A0Here = is > +a complete example: > + > + =C2=A0 =C2=A0qemu -M pc ${otheroptions} =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0-drive if=3Dnone,id=3Dusbstick,file=3D/path/= to/image =C2=A0 \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0-usb =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0-device usb-ehci,id=3Dehci =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0-device usb-tablet,bus=3Dusb.0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0-device usb-storage,bus=3Dehci.0,drive=3Dusb= stick > + > +This attaches a usb tablet to the UHCI adapter and a usb mass storage > +device to the EHCI adapter. > + > +enjoy, > + =C2=A0Gerd > + > +-- > +Gerd Hoffmann > diff --git a/hw/pci_ids.h b/hw/pci_ids.h > index ea3418c..d9457ed 100644 > --- a/hw/pci_ids.h > +++ b/hw/pci_ids.h > @@ -100,6 +100,7 @@ > =C2=A0#define PCI_VENDOR_ID_INTEL =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A00x8086 > =C2=A0#define PCI_DEVICE_ID_INTEL_82441 =C2=A0 =C2=A0 =C2=A0 =C2=A00x1237 > =C2=A0#define PCI_DEVICE_ID_INTEL_82801AA_5 =C2=A0 =C2=A00x2415 > +#define PCI_DEVICE_ID_INTEL_82801D =C2=A0 =C2=A0 =C2=A0 0x24CD > =C2=A0#define PCI_DEVICE_ID_INTEL_ESB_9 =C2=A0 =C2=A0 =C2=A0 =C2=A00x25ab > =C2=A0#define PCI_DEVICE_ID_INTEL_82371SB_0 =C2=A0 =C2=A00x7000 > =C2=A0#define PCI_DEVICE_ID_INTEL_82371SB_1 =C2=A0 =C2=A00x7010 > diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c > new file mode 100644 > index 0000000..545c590 > --- /dev/null > +++ b/hw/usb-ehci.c > @@ -0,0 +1,2038 @@ > +/* > + * QEMU USB EHCI Emulation > + * > + * Copyright(c) 2008 =C2=A0Emutex Ltd. (address@hidden) > + * > + * EHCI project was started by Mark Burkley, with contributions by > + * Niels de Vos. =C2=A0David S. Ahern continued working on it. =C2=A0Kev= in Wolf, > + * Jan Kiszka and Vincent Palatin contributed bugfixes. > + * > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or(at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =C2=A0See the GN= U > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA =C2=A002111-= 1307 =C2=A0USA FSF's current address is: 51 Franklin Street, Fifth Floor Boston, MA 02110-1301 USA But please use the interweb address. > + * > + * TODO: > + * =C2=A0o Downstream port handoff > + */ > + > +#include "hw.h" > +#include "qemu-timer.h" > +#include "usb.h" > +#include "pci.h" > +#include "monitor.h" > + > +#define EHCI_DEBUG =C2=A0 0 > +#define STATE_DEBUG =C2=A00 =C2=A0 =C2=A0 =C2=A0 /* state transitions = =C2=A0*/ > + > +#if EHCI_DEBUG || STATE_DEBUG > +#define DPRINTF printf > +#else > +#define DPRINTF(...) Unusual debug macros. Using tracepoints would be nice. > +#endif > + > +#if STATE_DEBUG > +#define DPRINTF_ST DPRINTF > +#else > +#define DPRINTF_ST(...) > +#endif > + > +/* internal processing - reset HC to try and recover */ > +#define USB_RET_PROCERR =C2=A0 (-99) > + > +#define MMIO_SIZE =C2=A0 =C2=A0 =C2=A0 =C2=A00x1000 > + > +/* Capability Registers Base Address - section 2.2 */ > +#define CAPREGBASE =C2=A0 =C2=A0 =C2=A0 0x0000 > +#define CAPLENGTH =C2=A0 =C2=A0 =C2=A0 =C2=A0CAPREGBASE + 0x0000 =C2=A0/= / 1-byte, 0x0001 reserved C99 comments. The expression should be enclosed in parentheses. > +#define HCIVERSION =C2=A0 =C2=A0 =C2=A0 CAPREGBASE + 0x0002 =C2=A0// 2-b= ytes, i/f version # > +#define HCSPARAMS =C2=A0 =C2=A0 =C2=A0 =C2=A0CAPREGBASE + 0x0004 =C2=A0/= / 4-bytes, structural params > +#define HCCPARAMS =C2=A0 =C2=A0 =C2=A0 =C2=A0CAPREGBASE + 0x0008 =C2=A0/= / 4-bytes, capability params > +#define EECP =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 HCCPARAMS + 1 > +#define HCSPPORTROUTE1 =C2=A0 CAPREGBASE + 0x000c > +#define HCSPPORTROUTE2 =C2=A0 CAPREGBASE + 0x0010 > + > +#define OPREGBASE =C2=A0 =C2=A0 =C2=A0 =C2=A00x0020 =C2=A0 =C2=A0 =C2=A0= =C2=A0// Operational Registers Base Address > + > +#define USBCMD =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OPREGBASE + 0x0000 > +#define USBCMD_RUNSTOP =C2=A0 (1 << 0) =C2=A0 =C2=A0 =C2=A0// run / Stop > +#define USBCMD_HCRESET =C2=A0 (1 << 1) =C2=A0 =C2=A0 =C2=A0// HC Reset > +#define USBCMD_FLS =C2=A0 =C2=A0 =C2=A0 (3 << 2) =C2=A0 =C2=A0 =C2=A0// = Frame List Size > +#define USBCMD_FLS_SH =C2=A0 =C2=A02 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 // Frame List Size Shift > +#define USBCMD_PSE =C2=A0 =C2=A0 =C2=A0 (1 << 4) =C2=A0 =C2=A0 =C2=A0// = Periodic Schedule Enable > +#define USBCMD_ASE =C2=A0 =C2=A0 =C2=A0 (1 << 5) =C2=A0 =C2=A0 =C2=A0// = Asynch Schedule Enable > +#define USBCMD_IAAD =C2=A0 =C2=A0 =C2=A0(1 << 6) =C2=A0 =C2=A0 =C2=A0// = Int Asynch Advance Doorbell > +#define USBCMD_LHCR =C2=A0 =C2=A0 =C2=A0(1 << 7) =C2=A0 =C2=A0 =C2=A0// = Light Host Controller Reset > +#define USBCMD_ASPMC =C2=A0 =C2=A0 (3 << 8) =C2=A0 =C2=A0 =C2=A0// Async= Sched Park Mode Count > +#define USBCMD_ASPME =C2=A0 =C2=A0 (1 << 11) =C2=A0 =C2=A0 // Async Sche= d Park Mode Enable > +#define USBCMD_ITC =C2=A0 =C2=A0 =C2=A0 (0x7f << 16) =C2=A0// Int Thresh= old Control > +#define USBCMD_ITC_SH =C2=A0 =C2=A016 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0// Int Threshold Control Shift > + > +#define USBSTS =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OPREGBASE + 0x0004 > +#define USBSTS_RO_MASK =C2=A0 0x0000003f > +#define USBSTS_INT =C2=A0 =C2=A0 =C2=A0 (1 << 0) =C2=A0 =C2=A0 =C2=A0// = USB Interrupt > +#define USBSTS_ERRINT =C2=A0 =C2=A0(1 << 1) =C2=A0 =C2=A0 =C2=A0// Error= Interrupt > +#define USBSTS_PCD =C2=A0 =C2=A0 =C2=A0 (1 << 2) =C2=A0 =C2=A0 =C2=A0// = Port Change Detect > +#define USBSTS_FLR =C2=A0 =C2=A0 =C2=A0 (1 << 3) =C2=A0 =C2=A0 =C2=A0// = Frame List Rollover > +#define USBSTS_HSE =C2=A0 =C2=A0 =C2=A0 (1 << 4) =C2=A0 =C2=A0 =C2=A0// = Host System Error > +#define USBSTS_IAA =C2=A0 =C2=A0 =C2=A0 (1 << 5) =C2=A0 =C2=A0 =C2=A0// = Interrupt on Async Advance > +#define USBSTS_HALT =C2=A0 =C2=A0 =C2=A0(1 << 12) =C2=A0 =C2=A0 // HC Ha= lted > +#define USBSTS_REC =C2=A0 =C2=A0 =C2=A0 (1 << 13) =C2=A0 =C2=A0 // Recla= mation > +#define USBSTS_PSS =C2=A0 =C2=A0 =C2=A0 (1 << 14) =C2=A0 =C2=A0 // Perio= dic Schedule Status > +#define USBSTS_ASS =C2=A0 =C2=A0 =C2=A0 (1 << 15) =C2=A0 =C2=A0 // Async= hronous Schedule Status > + > +/* > + * =C2=A0Interrupt enable bits correspond to the interrupt active bits i= n USBSTS > + * =C2=A0so no need to redefine here. > + */ > +#define USBINTR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0OPREGBAS= E + 0x0008 > +#define USBINTR_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x0000003f > + > +#define FRINDEX =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0OPREGBAS= E + 0x000c > +#define CTRLDSSEGMENT =C2=A0 =C2=A0 =C2=A0 =C2=A0OPREGBASE + 0x0010 > +#define PERIODICLISTBASE =C2=A0 =C2=A0 OPREGBASE + 0x0014 > +#define ASYNCLISTADDR =C2=A0 =C2=A0 =C2=A0 =C2=A0OPREGBASE + 0x0018 > +#define ASYNCLISTADDR_MASK =C2=A0 0xffffffe0 > + > +#define CONFIGFLAG =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 OPREGBASE + 0x0040 > + > +#define PORTSC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (OPREGBA= SE + 0x0044) > +#define PORTSC_BEGIN =C2=A0 =C2=A0 =C2=A0 =C2=A0 PORTSC > +#define PORTSC_END =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (PORTSC + 4 * NB_P= ORTS) > +/* > + * Bits that are reserverd or are read-only are masked out of values > + * written to us by software > + */ > +#define PORTSC_RO_MASK =C2=A0 =C2=A0 =C2=A0 0x007021c5 > +#define PORTSC_RWC_MASK =C2=A0 =C2=A0 =C2=A00x0000002a > +#define PORTSC_WKOC_E =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 22) =C2=A0 =C2=A0= // Wake on Over Current Enable > +#define PORTSC_WKDS_E =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 21) =C2=A0 =C2=A0= // Wake on Disconnect Enable > +#define PORTSC_WKCN_E =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 20) =C2=A0 =C2=A0= // Wake on Connect Enable > +#define PORTSC_PTC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (15 << 16) =C2=A0 = // Port Test Control > +#define PORTSC_PTC_SH =C2=A0 =C2=A0 =C2=A0 =C2=A016 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 // Port Test Control shift > +#define PORTSC_PIC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (3 << 14) =C2=A0 = =C2=A0// Port Indicator Control > +#define PORTSC_PIC_SH =C2=A0 =C2=A0 =C2=A0 =C2=A014 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 // Port Indicator Control Shift > +#define PORTSC_POWNER =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 13) =C2=A0 =C2=A0= // Port Owner > +#define PORTSC_PPOWER =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 12) =C2=A0 =C2=A0= // Port Power > +#define PORTSC_LINESTAT =C2=A0 =C2=A0 =C2=A0(3 << 10) =C2=A0 =C2=A0// Po= rt Line Status > +#define PORTSC_LINESTAT_SH =C2=A0 10 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = // Port Line Status Shift > +#define PORTSC_PRESET =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 8) =C2=A0 =C2=A0 = // Port Reset > +#define PORTSC_SUSPEND =C2=A0 =C2=A0 =C2=A0 (1 << 7) =C2=A0 =C2=A0 // Po= rt Suspend > +#define PORTSC_FPRES =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 6) =C2=A0 =C2=A0 = // Force Port Resume > +#define PORTSC_OCC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 5) =C2=A0 = =C2=A0 // Over Current Change > +#define PORTSC_OCA =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 4) =C2=A0 = =C2=A0 // Over Current Active > +#define PORTSC_PEDC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 3) =C2=A0 = =C2=A0 // Port Enable/Disable Change > +#define PORTSC_PED =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 2) =C2=A0 = =C2=A0 // Port Enable/Disable > +#define PORTSC_CSC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 1) =C2=A0 = =C2=A0 // Connect Status Change > +#define PORTSC_CONNECT =C2=A0 =C2=A0 =C2=A0 (1 << 0) =C2=A0 =C2=A0 // Cu= rrent Connect Status > + > +#define FRAME_TIMER_FREQ 1000 > +#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ) > + > +#define NB_MAXINTRATE =C2=A0 =C2=A08 =C2=A0 =C2=A0 =C2=A0 =C2=A0// Max r= ate at which controller issues ints > +#define NB_PORTS =C2=A0 =C2=A0 =C2=A0 =C2=A0 4 =C2=A0 =C2=A0 =C2=A0 =C2= =A0// Number of downstream ports > +#define BUFF_SIZE =C2=A0 =C2=A0 =C2=A0 =C2=A05*4096 =C2=A0 // Max bytes = to transfer per transaction Also here. > +#define MAX_ITERATIONS =C2=A0 20 =C2=A0 =C2=A0 =C2=A0 // Max number of Q= H before we break the loop > +#define MAX_QH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 100 =C2=A0 =C2=A0 =C2= =A0// Max allowable queue heads in a chain > + > +/* =C2=A0Internal periodic / asynchronous schedule state machine states > + */ > +typedef enum { > + =C2=A0 =C2=A0EST_INACTIVE =3D 1000, > + =C2=A0 =C2=A0EST_ACTIVE, > + =C2=A0 =C2=A0EST_EXECUTING, > + =C2=A0 =C2=A0EST_SLEEPING, > + =C2=A0 =C2=A0/* =C2=A0The following states are internal to the state ma= chine function > + =C2=A0 =C2=A0*/ > + =C2=A0 =C2=A0EST_WAITLISTHEAD, > + =C2=A0 =C2=A0EST_FETCHENTRY, > + =C2=A0 =C2=A0EST_FETCHQH, > + =C2=A0 =C2=A0EST_FETCHITD, > + =C2=A0 =C2=A0EST_ADVANCEQUEUE, > + =C2=A0 =C2=A0EST_FETCHQTD, > + =C2=A0 =C2=A0EST_EXECUTE, > + =C2=A0 =C2=A0EST_WRITEBACK, > + =C2=A0 =C2=A0EST_HORIZONTALQH > +} EHCI_STATES; > + > +/* macros for accessing fields within next link pointer entry */ > +#define NLPTR_GET(x) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ((x) & 0x= ffffffe0) > +#define NLPTR_TYPE_GET(x) =C2=A0 =C2=A0 =C2=A0 =C2=A0(((x) >> 1) & 3) > +#define NLPTR_TBIT(x) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((x) & 1)= =C2=A0// 1=3Dinvalid, 0=3Dvalid > + > +/* link pointer types */ > +#define NLPTR_TYPE_ITD =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0 =C2=A0 =C2= =A0 // isoc xfer descriptor > +#define NLPTR_TYPE_QH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A01 =C2=A0 = =C2=A0 // queue head > +#define NLPTR_TYPE_STITD =C2=A0 =C2=A0 =C2=A0 =C2=A0 2 =C2=A0 =C2=A0 // = split xaction, isoc xfer descriptor > +#define NLPTR_TYPE_FSTN =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A03 =C2=A0 =C2= =A0 // frame span traversal node > + > + > +/* =C2=A0EHCI spec version 1.0 Section 3.3 > + */ > +typedef struct EHCIitd { > + =C2=A0 =C2=A0uint32_t next; > + > + =C2=A0 =C2=A0uint32_t transact[8]; > +#define ITD_XACT_ACTIVE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 31) > +#define ITD_XACT_DBERROR =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 30) > +#define ITD_XACT_BABBLE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 29) > +#define ITD_XACT_XACTERR =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 28) > +#define ITD_XACT_LENGTH_MASK =C2=A0 =C2=A0 0x0fff0000 > +#define ITD_XACT_LENGTH_SH =C2=A0 =C2=A0 =C2=A0 16 > +#define ITD_XACT_IOC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 15) > +#define ITD_XACT_PGSEL_MASK =C2=A0 =C2=A0 =C2=A00x00007000 > +#define ITD_XACT_PGSEL_SH =C2=A0 =C2=A0 =C2=A0 =C2=A012 > +#define ITD_XACT_OFFSET_MASK =C2=A0 =C2=A0 0x00000fff > + > + =C2=A0 =C2=A0uint32_t bufptr[7]; > +#define ITD_BUFPTR_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00xfffff000 > +#define ITD_BUFPTR_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A012 > +#define ITD_BUFPTR_EP_MASK =C2=A0 =C2=A0 =C2=A0 0x00000f00 > +#define ITD_BUFPTR_EP_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 8 > +#define ITD_BUFPTR_DEVADDR_MASK =C2=A00x0000007f > +#define ITD_BUFPTR_DEVADDR_SH =C2=A0 =C2=A00 > +#define ITD_BUFPTR_DIRECTION =C2=A0 =C2=A0 (1 << 11) > +#define ITD_BUFPTR_MAXPKT_MASK =C2=A0 0x000007ff > +#define ITD_BUFPTR_MAXPKT_SH =C2=A0 =C2=A0 0 > +#define ITD_BUFPTR_MULT_MASK =C2=A0 =C2=A0 0x00000003 > +} EHCIitd; > + > +/* =C2=A0EHCI spec version 1.0 Section 3.4 > + */ > +typedef struct EHCIsitd { > + =C2=A0 =C2=A0uint32_t next; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0// Standard next link pointer > + =C2=A0 =C2=A0uint32_t epchar; > +#define SITD_EPCHAR_IO =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(= 1 << 31) > +#define SITD_EPCHAR_PORTNUM_MASK =C2=A0 =C2=A00x7f000000 > +#define SITD_EPCHAR_PORTNUM_SH =C2=A0 =C2=A0 =C2=A024 > +#define SITD_EPCHAR_HUBADD_MASK =C2=A0 =C2=A0 0x007f0000 > +#define SITD_EPCHAR_HUBADDR_SH =C2=A0 =C2=A0 =C2=A016 > +#define SITD_EPCHAR_EPNUM_MASK =C2=A0 =C2=A0 =C2=A00x00000f00 > +#define SITD_EPCHAR_EPNUM_SH =C2=A0 =C2=A0 =C2=A0 =C2=A08 > +#define SITD_EPCHAR_DEVADDR_MASK =C2=A0 =C2=A00x0000007f > + > + =C2=A0 =C2=A0uint32_t uframe; > +#define SITD_UFRAME_CMASK_MASK =C2=A0 =C2=A0 =C2=A00x0000ff00 > +#define SITD_UFRAME_CMASK_SH =C2=A0 =C2=A0 =C2=A0 =C2=A08 > +#define SITD_UFRAME_SMASK_MASK =C2=A0 =C2=A0 =C2=A00x000000ff > + > + =C2=A0 =C2=A0uint32_t results; > +#define SITD_RESULTS_IOC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(1 << 31) > +#define SITD_RESULTS_PGSEL =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 <= < 30) > +#define SITD_RESULTS_TBYTES_MASK =C2=A0 =C2=A0 =C2=A00x03ff0000 > +#define SITD_RESULTS_TYBYTES_SH =C2=A0 =C2=A0 =C2=A0 16 > +#define SITD_RESULTS_CPROGMASK_MASK =C2=A0 0x0000ff00 > +#define SITD_RESULTS_CPROGMASK_SH =C2=A0 =C2=A0 8 > +#define SITD_RESULTS_ACTIVE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 7) > +#define SITD_RESULTS_ERR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(1 << 6) > +#define SITD_RESULTS_DBERR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 <= < 5) > +#define SITD_RESULTS_BABBLE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 4) > +#define SITD_RESULTS_XACTERR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 << 3) > +#define SITD_RESULTS_MISSEDUF =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 2) > +#define SITD_RESULTS_SPLITXSTATE =C2=A0 =C2=A0 =C2=A0(1 << 1) > + > + =C2=A0 =C2=A0uint32_t bufptr[2]; > +#define SITD_BUFPTR_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A00xfffff000 > +#define SITD_BUFPTR_CURROFF_MASK =C2=A0 =C2=A0 =C2=A00x00000fff > +#define SITD_BUFPTR_TPOS_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x00000018 > +#define SITD_BUFPTR_TPOS_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 3 > +#define SITD_BUFPTR_TCNT_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x00000007 > + > + =C2=A0 =C2=A0uint32_t backptr; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 // Standard next link pointer > +} EHCIsitd; > + > +/* =C2=A0EHCI spec version 1.0 Section 3.5 > + */ > +typedef struct EHCIqtd { > + =C2=A0 =C2=A0uint32_t next; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0// Standard next link pointer > + =C2=A0 =C2=A0uint32_t altnext; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 // Standard next link pointer > + =C2=A0 =C2=A0uint32_t token; > +#define QTD_TOKEN_DTOGGLE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 <= < 31) > +#define QTD_TOKEN_TBYTES_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x7fff0000 > +#define QTD_TOKEN_TBYTES_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 16 > +#define QTD_TOKEN_IOC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 (1 << 15) > +#define QTD_TOKEN_CPAGE_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x0000700= 0 > +#define QTD_TOKEN_CPAGE_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A012 > +#define QTD_TOKEN_CERR_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x00000c0= 0 > +#define QTD_TOKEN_CERR_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 10 > +#define QTD_TOKEN_PID_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x00= 000300 > +#define QTD_TOKEN_PID_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A08 > +#define QTD_TOKEN_ACTIVE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(1 << 7) > +#define QTD_TOKEN_HALT =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0(1 << 6) > +#define QTD_TOKEN_DBERR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= (1 << 5) > +#define QTD_TOKEN_BABBLE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(1 << 4) > +#define QTD_TOKEN_XACTERR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 <= < 3) > +#define QTD_TOKEN_MISSEDUF =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1 <= < 2) > +#define QTD_TOKEN_SPLITXSTATE =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1 << 1) > +#define QTD_TOKEN_PING =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0(1 << 0) > + > + =C2=A0 =C2=A0uint32_t bufptr[5]; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 // Standard buffer pointer > +#define QTD_BUFPTR_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 0xfffff000 > +} EHCIqtd; > + > +/* =C2=A0EHCI spec version 1.0 Section 3.6 > + */ > +typedef struct EHCIqh { > + =C2=A0 =C2=A0uint32_t next; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0// Standard next link pointer > + > + =C2=A0 =C2=A0/* endpoint characteristics */ > + =C2=A0 =C2=A0uint32_t epchar; > +#define QH_EPCHAR_RL_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0xf0= 000000 > +#define QH_EPCHAR_RL_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 28 > +#define QH_EPCHAR_C =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (1 << 27) > +#define QH_EPCHAR_MPLEN_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x07FF000= 0 > +#define QH_EPCHAR_MPLEN_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A016 > +#define QH_EPCHAR_H =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (1 << 15) > +#define QH_EPCHAR_DTC =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 (1 << 14) > +#define QH_EPCHAR_EPS_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x00= 003000 > +#define QH_EPCHAR_EPS_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A012 > +#define EHCI_QH_EPS_FULL =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A00 > +#define EHCI_QH_EPS_LOW =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 1 > +#define EHCI_QH_EPS_HIGH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A02 > +#define EHCI_QH_EPS_RESERVED =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A03 > + > +#define QH_EPCHAR_EP_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x00= 000f00 > +#define QH_EPCHAR_EP_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 8 > +#define QH_EPCHAR_I =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (1 << 7) > +#define QH_EPCHAR_DEVADDR_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A00x0000007f > +#define QH_EPCHAR_DEVADDR_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00 > + > + =C2=A0 =C2=A0/* endpoint capabilities */ > + =C2=A0 =C2=A0uint32_t epcap; > +#define QH_EPCAP_MULT_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00xc0= 000000 > +#define QH_EPCAP_MULT_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A030 > +#define QH_EPCAP_PORTNUM_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x3f800000 > +#define QH_EPCAP_PORTNUM_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 23 > +#define QH_EPCAP_HUBADDR_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x007f0000 > +#define QH_EPCAP_HUBADDR_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 16 > +#define QH_EPCAP_CMASK_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x0000ff0= 0 > +#define QH_EPCAP_CMASK_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 8 > +#define QH_EPCAP_SMASK_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x000000f= f > +#define QH_EPCAP_SMASK_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0 > + > + =C2=A0 =C2=A0uint32_t current_qtd; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 // Standard next link pointer > + =C2=A0 =C2=A0uint32_t next_qtd; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0// Standard next link pointer > + =C2=A0 =C2=A0uint32_t altnext_qtd; > +#define QH_ALTNEXT_NAKCNT_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A00x0000001e > +#define QH_ALTNEXT_NAKCNT_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A01 > + > + =C2=A0 =C2=A0uint32_t token; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 // Same as QTD token > + =C2=A0 =C2=A0uint32_t bufptr[5]; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 // Standard buffer pointer > +#define BUFPTR_CPROGMASK_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x000000ff > +#define BUFPTR_FRAMETAG_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x0000001= f > +#define BUFPTR_SBYTES_MASK =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x00= 000fe0 > +#define BUFPTR_SBYTES_SH =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A05 > +} EHCIqh; > + > +/* =C2=A0EHCI spec version 1.0 Section 3.7 > + */ > +typedef struct EHCIfstn { > + =C2=A0 =C2=A0uint32_t next; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0// Standard next link pointer > + =C2=A0 =C2=A0uint32_t backptr; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 // Standard next link pointer > +} EHCIfstn; > + > +typedef struct { > + =C2=A0 =C2=A0PCIDevice dev; > + =C2=A0 =C2=A0qemu_irq irq; > + =C2=A0 =C2=A0target_phys_addr_t mem_base; Is this needed? > + =C2=A0 =C2=A0int mem; > + =C2=A0 =C2=A0int num_ports; > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * =C2=A0EHCI spec version 1.0 Section 2.3 > + =C2=A0 =C2=A0 * =C2=A0Host Controller Operational Registers > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0union { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0uint8_t mmio[MMIO_SIZE]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0struct { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint8_t cap[OPREGBASE]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t usbcmd; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t usbsts; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t usbintr; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t frindex; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t ctrldssegment; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t periodiclistbase; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t asynclistaddr; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t notused[9]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t configflag; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t portsc[NB_PORTS]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0}; This union smells of endianness troubles. > + =C2=A0 =C2=A0}; > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * =C2=A0Internal states, shadow registers, etc > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0uint32_t sofv; > + =C2=A0 =C2=A0QEMUTimer *frame_timer; > + =C2=A0 =C2=A0int attach_poll_counter; > + =C2=A0 =C2=A0int astate; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// Current state in asynchronous sche= dule > + =C2=A0 =C2=A0int pstate; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// Current state in periodic schedule > + =C2=A0 =C2=A0USBPort ports[NB_PORTS]; > + =C2=A0 =C2=A0uint8_t buffer[BUFF_SIZE]; > + =C2=A0 =C2=A0uint32_t usbsts_pending; > + > + =C2=A0 =C2=A0/* cached data from guest - needs to be flushed > + =C2=A0 =C2=A0 * when guest removes an entry (doorbell, handshake sequen= ce) > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0EHCIqh qh; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 // co= py of current QH (being worked on) > + =C2=A0 =C2=A0uint32_t qhaddr; =C2=A0 =C2=A0 =C2=A0 // address QH read f= rom > + > + =C2=A0 =C2=A0EHCIqtd qtd; =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 // copy of= current QTD (being worked on) > + =C2=A0 =C2=A0uint32_t qtdaddr; =C2=A0 =C2=A0 =C2=A0// address QTD read = from > + > + =C2=A0 =C2=A0uint32_t itdaddr; =C2=A0 =C2=A0 =C2=A0// current ITD > + > + =C2=A0 =C2=A0uint32_t fetch_addr; =C2=A0 // which address to look at ne= xt > + > + =C2=A0 =C2=A0USBBus bus; > + =C2=A0 =C2=A0USBPacket usb_packet; > + =C2=A0 =C2=A0int async_complete; > + =C2=A0 =C2=A0uint32_t tbytes; > + =C2=A0 =C2=A0int pid; > + =C2=A0 =C2=A0int exec_status; > + =C2=A0 =C2=A0int isoch_pause; > + =C2=A0 =C2=A0uint32_t last_run_usec; > + =C2=A0 =C2=A0uint32_t frame_end_usec; > +} EHCIState; > + > +#define SET_LAST_RUN_CLOCK(s) \ > + =C2=A0 =C2=A0(s)->last_run_usec =3D qemu_get_clock_ns(vm_clock) / 1000; Inline function, please. The correct way for a macro would be to enclose it within 'do { } while(0)'. > + > +/* nifty macros from Arnon's EHCI version =C2=A0*/ > +#define get_field(data, field) \ > + =C2=A0 =C2=A0(((data) & field##_MASK) >> field##_SH) > + > +#define set_field(data, newval, field) do { \ > + =C2=A0 =C2=A0uint32_t val =3D *data; \ > + =C2=A0 =C2=A0val &=3D ~ field##_MASK; \ > + =C2=A0 =C2=A0val |=3D ((newval) << field##_SH) & field##_MASK; \ > + =C2=A0 =C2=A0*data =3D val; \ > + =C2=A0 =C2=A0} while(0) More macro argument uses without parentheses. Please convert to an inline function. > + > + > +#if EHCI_DEBUG > +static const char *addr2str(unsigned addr) > +{ > + =C2=A0 =C2=A0const char *r =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D= " =C2=A0 unknown"; > + =C2=A0 =C2=A0const char *n[] =3D { 'static' to both above. Otherwise a compiler might construct the full array to stack. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ CAPLENGTH ] =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D= " CAPLENGTH", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ HCIVERSION ] =C2=A0 =C2=A0 =C2=A0 =3D "HCI= VERSION", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ HCSPARAMS ] =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D= " HCSPARAMS", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ HCCPARAMS ] =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D= " HCCPARAMS", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ USBCMD ] =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =3D " =C2=A0 COMMAND", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ USBSTS ] =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =3D " =C2=A0 =C2=A0STATUS", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ USBINTR ] =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0=3D " INTERRUPT", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ FRINDEX ] =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0=3D " FRAME IDX", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ PERIODICLISTBASE ] =3D "P-LIST BASE", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ ASYNCLISTADDR ] =C2=A0 =C2=A0=3D "A-LIST A= DDR", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ PORTSC_BEGIN ... > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0PORTSC_END ] =C2=A0 =C2=A0 =C2=A0 =3D= "PORT STATUS", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0[ CONFIGFLAG ] =C2=A0 =C2=A0 =C2=A0 =3D "CON= FIG FLAG", > + =C2=A0 =C2=A0}; > + > + =C2=A0 =C2=A0if (addr < ARRAY_SIZE(n) && n[addr] !=3D NULL) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return n[addr]; > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return r; > + =C2=A0 =C2=A0} > +} > +#endif > + > + > +static inline void ehci_set_interrupt(EHCIState *s, int intr) > +{ > + =C2=A0 =C2=A0int level =3D 0; > + > + =C2=A0 =C2=A0// TODO honour interrupt threshold requests > + > + =C2=A0 =C2=A0s->usbsts |=3D intr; > + > + =C2=A0 =C2=A0if ((s->usbsts & USBINTR_MASK) & s->usbintr) { Should the second '&' be '&&'? > + =C2=A0 =C2=A0 =C2=A0 =C2=A0level =3D 1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0qemu_set_irq(s->irq, level); > +} > + > +static inline void ehci_record_interrupt(EHCIState *s, int intr) > +{ > + =C2=A0 =C2=A0s->usbsts_pending |=3D intr; > +} > + > +static inline void ehci_commit_interrupt(EHCIState *s) > +{ > + =C2=A0 =C2=A0if (!s->usbsts_pending) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0ehci_set_interrupt(s, s->usbsts_pending); > + =C2=A0 =C2=A0s->usbsts_pending =3D 0; > +} > + > +/* Attach or detach a device on root hub */ > + > +static void ehci_attach(USBPort *port) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D port->opaque; > + =C2=A0 =C2=A0uint32_t *portsc =3D &s->portsc[port->index]; > + > + =C2=A0 =C2=A0DPRINTF("ehci_attach invoked for index %d, portsc 0x%x, de= sc %s\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port->index, *portsc, port->dev->pro= duct_desc); > + > + =C2=A0 =C2=A0*portsc |=3D PORTSC_CONNECT; > + =C2=A0 =C2=A0*portsc |=3D PORTSC_CSC; > + > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * =C2=A0If a high speed device is attached then we own th= is port(indicated > + =C2=A0 =C2=A0 * =C2=A0by zero in the PORTSC_POWNER bit field) so set th= e status bit > + =C2=A0 =C2=A0 * =C2=A0and set an interrupt if enabled. > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0if ( !(*portsc & PORTSC_POWNER)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_set_interrupt(s, USBSTS_PCD); > + =C2=A0 =C2=A0} > +} > + > +static void ehci_detach(USBPort *port) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D port->opaque; > + =C2=A0 =C2=A0uint32_t *portsc =3D &s->portsc[port->index]; > + > + =C2=A0 =C2=A0DPRINTF("ehci_attach invoked for index %d, portsc 0x%x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 port->index, *portsc); > + > + =C2=A0 =C2=A0*portsc &=3D ~PORTSC_CONNECT; > + =C2=A0 =C2=A0*portsc |=3D PORTSC_CSC; > + > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * =C2=A0If a high speed device is attached then we own th= is port(indicated > + =C2=A0 =C2=A0 * =C2=A0by zero in the PORTSC_POWNER bit field) so set th= e status bit > + =C2=A0 =C2=A0 * =C2=A0and set an interrupt if enabled. > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0if ( !(*portsc & PORTSC_POWNER)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_set_interrupt(s, USBSTS_PCD); > + =C2=A0 =C2=A0} > +} > + > +/* 4.1 host controller initialization */ > +static void ehci_reset(void *opaque) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D opaque; > + =C2=A0 =C2=A0uint8_t *pci_conf; > + =C2=A0 =C2=A0int i; > + > + =C2=A0 =C2=A0pci_conf =3D s->dev.config; > + > + =C2=A0 =C2=A0memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); > + > + =C2=A0 =C2=A0s->usbcmd =3D NB_MAXINTRATE << USBCMD_ITC_SH; > + =C2=A0 =C2=A0s->usbsts =3D USBSTS_HALT; > + > + =C2=A0 =C2=A0s->astate =3D EST_INACTIVE; > + =C2=A0 =C2=A0s->pstate =3D EST_INACTIVE; > + =C2=A0 =C2=A0s->async_complete =3D 0; > + =C2=A0 =C2=A0s->isoch_pause =3D -1; > + =C2=A0 =C2=A0s->attach_poll_counter =3D 0; > + > + =C2=A0 =C2=A0for(i =3D 0; i < NB_PORTS; i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->portsc[i] =3D PORTSC_POWNER | PORTSC_PPOW= ER; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->ports[i].dev) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0usb_attach(&s->ports[i], s->po= rts[i].dev); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > +} > + > +static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D ptr; > + =C2=A0 =C2=A0uint32_t val; > + > + =C2=A0 =C2=A0val =3D s->mmio[addr]; > + > + =C2=A0 =C2=A0return val; > +} > + > +static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D ptr; > + =C2=A0 =C2=A0uint32_t val; > + > + =C2=A0 =C2=A0val =3D s->mmio[addr] | (s->mmio[addr+1] << 8); > + > + =C2=A0 =C2=A0return val; > +} > + > +static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D ptr; > + =C2=A0 =C2=A0uint32_t val; > + > + =C2=A0 =C2=A0val =3D s->mmio[addr] | (s->mmio[addr+1] << 8) | > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(s->mmio[addr+2] << 16) | (s->mmio[ad= dr+3] << 24); > + > + =C2=A0 =C2=A0return val; > +} > + > +static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t= val) > +{ > + =C2=A0 =C2=A0fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n= "); > + =C2=A0 =C2=A0exit(1); > +} > + > +static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t= val) > +{ > + =C2=A0 =C2=A0fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO= \n"); > + =C2=A0 =C2=A0exit(1); > +} > + > +static void handle_port_status_write(EHCIState *s, int port, uint32_t va= l) > +{ > + =C2=A0 =C2=A0uint32_t *portsc =3D &s->portsc[port]; > + =C2=A0 =C2=A0int rwc; > + =C2=A0 =C2=A0USBDevice *dev =3D s->ports[port].dev; > + > + =C2=A0 =C2=A0DPRINTF("port_status_write: " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"PORTSC (port %d) curr %08X ne= w %08X rw-clear %08X rw %08X\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0port, *portsc, val, (val & POR= TSC_RWC_MASK), val & PORTSC_RO_MASK); > + > + =C2=A0 =C2=A0rwc =3D val & PORTSC_RWC_MASK; > + =C2=A0 =C2=A0val &=3D PORTSC_RO_MASK; > + > + =C2=A0 =C2=A0// handle_read_write_clear(&val, portsc, PORTSC_PEDC | POR= TSC_CSC); > + > + =C2=A0 =C2=A0*portsc &=3D ~rwc; > + > + =C2=A0 =C2=A0if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("port_status_write: USBTRAN Port %d = reset begin\n", port); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("port_status_write: USBTRAN Port %d = reset done\n", port); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0usb_attach(&s->ports[port], dev); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// TODO how to handle reset of ports with no= device > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (dev) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0usb_send_msg(dev, USB_MSG_RESE= T); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (s->ports[port].dev) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("port_status_write: " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"D= evice was connected before reset, clearing CSC bit\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*portsc &=3D ~PORTSC_CSC; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0Table 2.16 Set the enable bit(and e= nable bit change) to indicate > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 * =C2=A0to SW that this port has a high spe= ed device attached > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 * > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 * =C2=A0TODO - when to disable? > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0val |=3D PORTSC_PED; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0val |=3D PORTSC_PEDC; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0*portsc &=3D ~PORTSC_RO_MASK; > + =C2=A0 =C2=A0*portsc |=3D val; > + =C2=A0 =C2=A0DPRINTF("port_status_write: Port %d status set to 0x%08x\n= ", port, *portsc); > +} > + > +static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t= val) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D ptr; > + =C2=A0 =C2=A0int i; > +#if EHCI_DEBUG > + =C2=A0 =C2=A0const char *str; > +#endif > + > + =C2=A0 =C2=A0/* Only aligned reads are allowed on OHCI */ > + =C2=A0 =C2=A0if (addr & 3) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "usb-ehci: Mis-aligned write= to addr 0x" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0TARGET_FMT_plx "= \n", addr); This and below should become DPRINTFs. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (addr >=3D PORTSC && addr < PORTSC + 4 * NB_PORTS) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0handle_port_status_write(s, (addr-PORTSC)/4,= val); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (addr < OPREGBASE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "usb-ehci: write attempt to = read-only register" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0TARGET_FMT_plx "= \n", addr); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + > + > + =C2=A0 =C2=A0/* Do any register specific pre-write processing here. =C2= =A0*/ > +#if EHCI_DEBUG > + =C2=A0 =C2=A0str =3D addr2str((unsigned) addr); > +#endif > + =C2=A0 =C2=A0switch(addr) { > + =C2=A0 =C2=A0case USBCMD: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: USBCMD val=3D0x%08= X, current cmd=3D0x%08X\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0val, s->usbcmd); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & = USBCMD_RUNSTOP)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: %s r= un, clear halt\n", str); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_mod_timer(s->frame_timer,= qemu_get_clock_ns(vm_clock)); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0SET_LAST_RUN_CLOCK(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->usbsts &=3D ~USBSTS_HALT; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & = USBCMD_RUNSTOP)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF(" =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ** STOP **\= n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_del_timer(s->frame_timer)= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// TODO - should finish out so= me stuff before setting halt > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->usbsts |=3D USBSTS_HALT; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (val & USBCMD_HCRESET) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: %s r= un, resetting\n", str); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_reset(s); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0val &=3D ~USBCMD_HCRESET; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* not supporting dynamic frame list size at= the moment */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((val & USBCMD_FLS) && !(s->usbcmd & USBC= MD_FLS)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "attempt to se= t frame list size -- value %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0va= l & USBCMD_FLS); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0val &=3D ~USBCMD_FLS; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > +#if EHCI_DEBUG > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((val & USBCMD_PSE) && !(s->usbcmd & USBC= MD_PSE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("periodic scheduling e= nabled\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(val & USBCMD_PSE) && (s->usbcmd & USBC= MD_PSE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("periodic scheduling d= isabled\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((val & USBCMD_ASE) && !(s->usbcmd & USBC= MD_ASE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("asynchronous scheduli= ng enabled\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(val & USBCMD_ASE) && (s->usbcmd & USBC= MD_ASE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("asynchronous scheduli= ng disabled\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((val & USBCMD_IAAD) && !(s->usbcmd & USB= CMD_IAAD)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("doorbell request rece= ived\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((val & USBCMD_LHCR) && !(s->usbcmd & USB= CMD_LHCR)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("light host controller= reset received\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((val & USBCMD_ITC) !=3D (s->usbcmd & USB= CMD_ITC)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("interrupt threshold c= ontrol set to %x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(v= al & USBCMD_ITC)>>USBCMD_ITC_SH); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > +#endif > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + > + =C2=A0 =C2=A0case USBSTS: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0val &=3D USBSTS_RO_MASK; =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// bits 6 thru 31 are RO > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: %s RWC set to 0x%0= 8X\n", str, val); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0val =3D (s->usbsts &=3D ~val); =C2=A0 =C2=A0= =C2=A0 =C2=A0 // bits 0 thru 5 are R/WC > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: %s updating interr= upt condition\n", str); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_set_interrupt(s, 0); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + > + =C2=A0 =C2=A0case USBINTR: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0val &=3D USBINTR_MASK; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: %s set to 0x%08X\n= ", str, val); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case FRINDEX: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->sofv =3D val >> 3; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: %s set to 0x%08X\n= ", str, val); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case CONFIGFLAG: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: %s set to 0x%08X\n= ", str, val); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0val &=3D 0x1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (val) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for(i =3D 0; i < NB_PORTS; i++= ) Braces. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->portsc[i] &= =3D ~PORTSC_POWNER; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case PERIODICLISTBASE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd &= USBCMD_RUNSTOP)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"ehci: PERIODIC list ba= se register set while periodic schedule\n" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0" =C2=A0 =C2=A0 =C2=A0i= s enabled and HC is enabled\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: P-LIST BASE set to= 0x%08X\n", val); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case ASYNCLISTADDR: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd &= USBCMD_RUNSTOP)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"ehci: ASYNC list addre= ss register set while async schedule\n" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0" =C2=A0 =C2=A0 =C2=A0i= s enabled and HC is enabled\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ehci_mem_writel: A-LIST ADDR set to= 0x%08X\n", val); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0*(uint32_t *)(&s->mmio[addr]) =3D val; > +} > + > + > +// TODO : Put in common header file, duplication from usb-ohci.c > + > +/* Get an array of dwords from main memory */ > +static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) Type of addr should probably be target_phys_addr_t. > +{ > + =C2=A0 =C2=A0int i; > + > + =C2=A0 =C2=A0for(i =3D 0; i < num; i++, buf++, addr +=3D sizeof(*buf)) = { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_physical_memory_rw(addr,(uint8_t *)buf, = sizeof(*buf), 0); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*buf =3D le32_to_cpu(*buf); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return 1; > +} > + > +/* Put an array of dwords in to main memory */ > +static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) > +{ > + =C2=A0 =C2=A0int i; > + > + =C2=A0 =C2=A0for(i =3D 0; i < num; i++, buf++, addr +=3D sizeof(*buf)) = { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t tmp =3D cpu_to_le32(*buf); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_physical_memory_rw(addr,(uint8_t *)&tmp,= sizeof(tmp), 1); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return 1; > +} > + > +// 4.10.2 > + > +static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd) > +{ > + =C2=A0 =C2=A0int i; > + =C2=A0 =C2=A0int dtoggle; > + =C2=A0 =C2=A0int ping; > + =C2=A0 =C2=A0int eps; > + =C2=A0 =C2=A0int reload; > + > + =C2=A0 =C2=A0// remember values in fields to preserve in qh after overl= ay > + > + =C2=A0 =C2=A0dtoggle =3D qh->token & QTD_TOKEN_DTOGGLE; > + =C2=A0 =C2=A0ping =C2=A0 =C2=A0=3D qh->token & QTD_TOKEN_PING; > + > + =C2=A0 =C2=A0DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->cu= rrent_qtd, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qtdaddr); > + =C2=A0 =C2=A0qh->current_qtd =3D ehci->qtdaddr; > + =C2=A0 =C2=A0qh->next_qtd =C2=A0 =C2=A0=3D qtd->next; > + =C2=A0 =C2=A0qh->altnext_qtd =3D qtd->altnext; > + =C2=A0 =C2=A0qh->token =C2=A0 =C2=A0 =C2=A0 =3D qtd->token; > + > + > + =C2=A0 =C2=A0eps =3D get_field(qh->epchar, QH_EPCHAR_EPS); > + =C2=A0 =C2=A0if (eps =3D=3D EHCI_QH_EPS_HIGH) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qh->token &=3D ~QTD_TOKEN_PING; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qh->token |=3D ping; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0reload =3D get_field(qh->epchar, QH_EPCHAR_RL); > + =C2=A0 =C2=A0set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT); > + > + =C2=A0 =C2=A0for (i =3D 0; i < 5; i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qh->bufptr[i] =3D qtd->bufptr[i]; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (!(qh->epchar & QH_EPCHAR_DTC)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// preserve QH DT bit > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qh->token &=3D ~QTD_TOKEN_DTOGGLE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0qh->token |=3D dtoggle; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0qh->bufptr[1] &=3D ~BUFPTR_CPROGMASK_MASK; > + =C2=A0 =C2=A0qh->bufptr[2] &=3D ~BUFPTR_FRAMETAG_MASK; > + > + =C2=A0 =C2=A0put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeo= f(EHCIqh) >> 2); > + > + =C2=A0 =C2=A0return 0; > +} > + > +static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw= ) > +{ > + =C2=A0 =C2=A0int bufpos =3D 0; > + =C2=A0 =C2=A0int cpage, offset; > + =C2=A0 =C2=A0uint32_t head; > + =C2=A0 =C2=A0uint32_t tail; target_phys_addr_t > + > + > + =C2=A0 =C2=A0if (!bytes) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0cpage =3D get_field(qh->token, QTD_TOKEN_CPAGE); > + =C2=A0 =C2=A0if (cpage > 4) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "cpage out of range (%d)\n",= cpage); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_PROCERR; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0offset =3D qh->bufptr[0] & ~QTD_BUFPTR_MASK; > + =C2=A0 =C2=A0DPRINTF("ehci_buffer_rw: %sing %d bytes %08x cpage %d offs= et %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rw ? "writ" : "read", bytes, qh->buf= ptr[0], cpage, offset); > + > + =C2=A0 =C2=A0do { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* start and end of this page */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0head =3D qh->bufptr[cpage] & QTD_BUFPTR_MASK= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0tail =3D head + ~QTD_BUFPTR_MASK + 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* add offset into page */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0head |=3D offset; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (bytes <=3D (tail - head)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tail =3D head + bytes; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("DATA %s cpage:%d head:%08X tail:%08= X target:%08X\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rw ? "WRITE" : "= READ ", cpage, head, tail, bufpos); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_physical_memory_rw(head, &buffer[bufpos]= , tail - head, rw); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0bufpos +=3D (tail - head); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0bytes -=3D (tail - head); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (bytes > 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cpage++; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0offset =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} while (bytes > 0); > + > + =C2=A0 =C2=A0/* save cpage */ > + =C2=A0 =C2=A0set_field(&qh->token, cpage, QTD_TOKEN_CPAGE); > + > + =C2=A0 =C2=A0/* save offset into cpage */ > + =C2=A0 =C2=A0offset =3D tail - head; > + =C2=A0 =C2=A0qh->bufptr[0] &=3D ~QTD_BUFPTR_MASK; > + =C2=A0 =C2=A0qh->bufptr[0] |=3D offset; > + > + =C2=A0 =C2=A0return 0; > +} > + > +static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet= ) > +{ > + =C2=A0 =C2=A0EHCIState *ehci =3D container_of(packet, EHCIState, usb_pa= cket); > + > + =C2=A0 =C2=A0DPRINTF("Async packet complete\n"); > + =C2=A0 =C2=A0ehci->async_complete =3D 1; > + =C2=A0 =C2=A0ehci->exec_status =3D packet->len; > +} > + > +static int ehci_execute_complete(EHCIState *ehci, EHCIqh *qh, int ret) > +{ > + =C2=A0 =C2=A0int c_err, reload; > + > + =C2=A0 =C2=A0if (ret =3D=3D USB_RET_ASYNC && !ehci->async_complete) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("not done yet\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return ret; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0ehci->async_complete =3D 0; > + > + =C2=A0 =C2=A0DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0= x%x, status %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qhaddr, qh->next, ehci->= qtdaddr, ret); > + > + =C2=A0 =C2=A0if (ret < 0) { > +err: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* TO-DO: put this is in a function that can= be invoked below as well */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0c_err =3D get_field(qh->token, QTD_TOKEN_CER= R); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0c_err--; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0set_field(&qh->token, c_err, QTD_TOKEN_CERR)= ; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0switch(ret) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case USB_RET_NODEV: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "USB no device= \n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case USB_RET_STALL: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "USB stall\n")= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qh->token |=3D QTD_TOKEN_HALT; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_record_interrupt(ehci, US= BSTS_ERRINT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case USB_RET_NAK: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* 4.10.3 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0reload =3D get_field(qh->epcha= r, QH_EPCHAR_RL); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((ehci->pid =3D=3D USB_TOKE= N_IN) && reload) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int nakcnt =3D g= et_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0nakcnt--; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0set_field(&qh->a= ltnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else if (!reload) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_N= AK; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case USB_RET_BABBLE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "USB babble TO= DO\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qh->token |=3D QTD_TOKEN_BABBL= E; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_record_interrupt(ehci, US= BSTS_ERRINT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "USB invalid r= esponse %d to handle\n", ret); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* TO-DO: transaction error */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D USB_RET_PROCERR; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// DPRINTF("Short packet condition\n"); Commented out debug statement? > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// TODO check 4.12 for splits > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ((ret > ehci->tbytes) && (ehci->pid =3D= =3D USB_TOKEN_IN)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D USB_RET_BABBLE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto err; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->tbytes && ehci->pid =3D=3D USB_TOK= EN_IN) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci_buffer_rw(ehci->buffe= r, qh, ret, 1) !=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_P= ROCERR; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->tbytes -=3D ret; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->tbytes =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("updating tbytes to %d\n", ehci->tby= tes); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0set_field(&qh->token, ehci->tbytes, QTD_TOKE= N_TBYTES); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0qh->token ^=3D QTD_TOKEN_DTOGGLE; > + =C2=A0 =C2=A0qh->token &=3D ~QTD_TOKEN_ACTIVE; > + > + =C2=A0 =C2=A0if ((ret >=3D 0) && (qh->token & QTD_TOKEN_IOC)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_record_interrupt(ehci, USBSTS_INT); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return ret; > +} > + > +// 4.10.3 > + > +static int ehci_execute(EHCIState *ehci, EHCIqh *qh) > +{ > + =C2=A0 =C2=A0USBPort *port; > + =C2=A0 =C2=A0USBDevice *dev; > + =C2=A0 =C2=A0int ret; > + =C2=A0 =C2=A0int i; > + =C2=A0 =C2=A0int endp; > + =C2=A0 =C2=A0int devadr; > + > + =C2=A0 =C2=A0if ( !(qh->token & QTD_TOKEN_ACTIVE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "Attempting to execute inact= ive QH\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_PROCERR; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0ehci->tbytes =3D (qh->token & QTD_TOKEN_TBYTES_MASK) >> QT= D_TOKEN_TBYTES_SH; > + =C2=A0 =C2=A0if (ehci->tbytes > BUFF_SIZE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "Request for more bytes than= allowed\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_PROCERR; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0ehci->pid =3D (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKE= N_PID_SH; > + =C2=A0 =C2=A0switch(ehci->pid) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case 0: ehci->pid =3D USB_TOKEN_OUT; break; Statements on lines of their own, please. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case 1: ehci->pid =3D USB_TOKEN_IN; break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case 2: ehci->pid =3D USB_TOKEN_SETUP; break= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0default: fprintf(stderr, "bad token\n"); bre= ak; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if ((ehci->tbytes && ehci->pid !=3D USB_TOKEN_IN) && > + =C2=A0 =C2=A0 =C2=A0 =C2=A0(ehci_buffer_rw(ehci->buffer, qh, ehci->tbyt= es, 0) !=3D 0)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_PROCERR; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0endp =3D get_field(qh->epchar, QH_EPCHAR_EP); > + =C2=A0 =C2=A0devadr =3D get_field(qh->epchar, QH_EPCHAR_DEVADDR); > + > + =C2=A0 =C2=A0ret =3D USB_RET_NODEV; > + > + =C2=A0 =C2=A0// TO-DO: associating device with ehci port > + =C2=A0 =C2=A0for(i =3D 0; i < NB_PORTS; i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0port =3D &ehci->ports[i]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0dev =3D port->dev; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// TODO sometime we will also need to check = if we are the port owner > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(ehci->portsc[i] &(PORTSC_CONNECT))) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("Port %d, no exec, not= connected(%08X)\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0i,= ehci->portsc[i]); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0continue; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet.pid =3D ehci->pid; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet.devaddr =3D devadr; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet.devep =3D endp; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet.data =3D ehci->buffer; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet.len =3D ehci->tbytes; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D usb_handle_packet(dev, &ehci->usb_pa= cket); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("submit: qh %x next %x qtd %x pid %x= len %d (total %d) endp %x ret %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qhaddr, qh= ->next, ehci->qtdaddr, ehci->pid, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet= .len, ehci->tbytes, endp, ret); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ret !=3D USB_RET_NODEV) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (ret > BUFF_SIZE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "ret from usb_handle_packet = > BUFF_SIZE\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_PROCERR; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (ret =3D=3D USB_RET_ASYNC) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->async_complete =3D 0; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return ret; > +} > + > +/* =C2=A04.7.2 > + */ > + > +static int ehci_process_itd(EHCIState *ehci, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0EHCIitd *itd) > +{ > + =C2=A0 =C2=A0USBPort *port; > + =C2=A0 =C2=A0USBDevice *dev; > + =C2=A0 =C2=A0int ret; > + =C2=A0 =C2=A0int i, j; > + =C2=A0 =C2=A0int ptr; > + =C2=A0 =C2=A0int pid; > + =C2=A0 =C2=A0int pg; > + =C2=A0 =C2=A0int len; > + =C2=A0 =C2=A0int dir; > + =C2=A0 =C2=A0int devadr; > + =C2=A0 =C2=A0int endp; > + =C2=A0 =C2=A0int maxpkt; > + > + =C2=A0 =C2=A0dir =3D(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); > + =C2=A0 =C2=A0devadr =3D get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR); > + =C2=A0 =C2=A0endp =3D get_field(itd->bufptr[0], ITD_BUFPTR_EP); > + =C2=A0 =C2=A0maxpkt =3D get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT); > + > + =C2=A0 =C2=A0for(i =3D 0; i < 8; i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (itd->transact[i] & ITD_XACT_ACTIVE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ISOCHRONOUS active fo= r frame %d, interval %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0eh= ci->frindex >> 3, i); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pg =3D get_field(itd->transact= [i], ITD_XACT_PGSEL); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ptr =3D (itd->bufptr[pg] & ITD= _BUFPTR_MASK) | > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(itd->transact[i= ] & ITD_XACT_OFFSET_MASK); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0len =3D get_field(itd->transac= t[i], ITD_XACT_LENGTH); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (len > BUFF_SIZE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return USB_RET_P= ROCERR; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ISOCH: buffer %08X le= n %d\n", ptr, len); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!dir) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_physical_mem= ory_rw(ptr, &ehci->buffer[0], len, 0); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pid =3D USB_TOKE= N_OUT; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pid =3D USB_TOKE= N_IN; b-word > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D USB_RET_NODEV; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for (j =3D 0; j < NB_PORTS; j+= +) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0port =3D &ehci->= ports[j]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev =3D port->de= v; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// TODO sometime= we will also need to check if we are the port owner > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(ehci->port= sc[j] &(PORTSC_CONNECT))) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DP= RINTF("Port %d, no exec, not connected(%08X)\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0j, ehci->portsc[j]); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0co= ntinue; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet= .pid =3D ehci->pid; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet= .devaddr =3D devadr; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet= .devep =3D endp; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet= .data =3D ehci->buffer; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usb_packet= .len =3D len; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("calling= usb_handle_packet\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D usb_hand= le_packet(dev, &ehci->usb_packet); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ret !=3D USB= _RET_NODEV) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0br= eak; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0In isoch, there is no= facility to indicate a NAK so let's > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * =C2=A0instead just complete= a zero-byte transaction. =C2=A0Setting > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * =C2=A0DBERR seems too draco= nian. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ret =3D=3D USB_RET_NAK) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->isoch_= pause > 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DP= RINTF("ISOCH: received a NAK but paused so returning\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0eh= ci->isoch_pause--; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0re= turn 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else if (ehci-= >isoch_pause =3D=3D -1) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DP= RINTF("ISOCH: recv NAK & isoch pause inactive, setting\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0//= Pause frindex for up to 50 msec waiting for data from > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0//= remote > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0eh= ci->isoch_pause =3D 50; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0re= turn 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DP= RINTF("ISOCH: isoch pause timeout! return 0\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0re= t =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ISOCH: = received ACK, clearing pause\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->isoch_paus= e =3D -1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ret >=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0itd->transact[i]= &=3D ~ITD_XACT_ACTIVE; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (itd->transac= t[i] & ITD_XACT_IOC) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0eh= ci_record_interrupt(ehci, USBSTS_INT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ret >=3D 0 && dir) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_physical_mem= ory_rw(ptr, &ehci->buffer[0], len, 1); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ret !=3D len= ) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DP= RINTF("ISOCH IN expected %d, got %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0len, ret); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0se= t_field(&itd->transact[i], ret, ITD_XACT_LENGTH); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return 0; > +} > + > +/* =C2=A0This state is the entry point for asynchronous schedule > + * =C2=A0processing. =C2=A0Entry here consitutes a EHCI start event stat= e (4.8.5) > + */ > +static int ehci_state_waitlisthead(EHCIState *ehci, =C2=A0int async, int= *state) > +{ > + =C2=A0 =C2=A0EHCIqh *qh =3D &ehci->qh; > + =C2=A0 =C2=A0int i =3D 0; > + =C2=A0 =C2=A0int again =3D 0; > + =C2=A0 =C2=A0uint32_t entry =3D ehci->asynclistaddr; > + > + =C2=A0 =C2=A0/* set reclamation flag at start event (4.8.6) */ > + =C2=A0 =C2=A0if (async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbsts |=3D USBSTS_REC; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0/* =C2=A0Find the head of the list (4.9.1.1) */ > + =C2=A0 =C2=A0for(i =3D 0; i < MAX_QH; i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0get_dwords(NLPTR_GET(entry), (uint32_t *) qh= , sizeof(EHCIqh) >> 2); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (qh->epchar & QH_EPCHAR_H) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("WAITLISTHEAD: QH %= 08X is the HEAD of the list\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 entry); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0entry |=3D (NLPT= R_TYPE_QH << 1); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->fetch_addr =3D entry; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_FETCHENTRY; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the= HEAD of the list\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 entry); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0entry =3D qh->next; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (entry =3D=3D ehci->asynclistaddr) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("WAITLISTHEAD: reached= beginning of QH list\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0/* no head found for list. */ > + > + =C2=A0 =C2=A0*state =3D EST_ACTIVE; > + > +out: > + =C2=A0 =C2=A0return again; > +} > + > + > +/* =C2=A0This state is the entry point for periodic schedule processing = as > + * =C2=A0well as being a continuation state for async processing. > + */ > +static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0int again =3D 0; > + =C2=A0 =C2=A0uint32_t entry =3D ehci->fetch_addr; > + > +#if EHCI_DEBUG =3D=3D 0 > + =C2=A0 =C2=A0if (qemu_get_clock_ns(vm_clock) / 1000 >=3D ehci->frame_en= d_usec) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("FETCHENTRY: FRAME tim= er elapsed, exit state machine\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("FETCHENTRY: WARNING " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"-= frame timer elapsed during periodic\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > +#endif > + =C2=A0 =C2=A0if (entry < 0x1000) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("fetchentry: entry invalid (0x%08x)\= n", entry); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_ACTIVE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0/* section 4.8, only QH in async schedule */ > + =C2=A0 =C2=A0if (async && (NLPTR_TYPE_GET(entry) !=3D NLPTR_TYPE_QH)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "non queue head request in a= sync schedule\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0switch (NLPTR_TYPE_GET(entry)) { > + =C2=A0 =C2=A0case NLPTR_TYPE_QH: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("FETCHENTRY: entry %X is a Queue = Head\n", entry); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_FETCHQH; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qhaddr =3D entry; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case NLPTR_TYPE_ITD: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n= ", entry); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_FETCHITD; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->itdaddr =3D entry; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// TODO: handle siTD and FSTN types > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "FETCHENTRY: entry at %X is = of type %d " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"which is not su= pported yet\n", entry, NLPTR_TYPE_GET(entry)); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > +out: > + =C2=A0 =C2=A0return again; > +} > + > +static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0EHCIqh *qh =3D &ehci->qh; > + =C2=A0 =C2=A0int reload; > + =C2=A0 =C2=A0int again =3D 0; > + > + =C2=A0 =C2=A0get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeo= f(EHCIqh) >> 2); > + > + =C2=A0 =C2=A0if (async && (qh->epchar & QH_EPCHAR_H)) { > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0EHCI spec version 1.0 Section 4.8.3= & 4.10.1 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->usbsts & USBSTS_REC) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbsts &=3D ~USBSTS_REC; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("FETCHQH: =C2=A0QH 0x%= 08x. H-bit set, reclamation status reset" > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 " - done processing\n", ehci->qhaddr); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_ACTIVE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > +#if EHCI_DEBUG > + =C2=A0 =C2=A0if (ehci->qhaddr !=3D qh->next) { > + =C2=A0 =C2=A0DPRINTF("FETCHQH: =C2=A0QH 0x%08x (h %x halt %x active %x)= next 0x%08x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ehci->qhaddr, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qh->epchar & QH_EPCHAR= _H, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qh->token & QTD_TOKEN_= HALT, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qh->token & QTD_TOKEN_= ACTIVE, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qh->next); > + =C2=A0 =C2=A0} > +#endif > + > + =C2=A0 =C2=A0reload =3D get_field(qh->epchar, QH_EPCHAR_RL); > + =C2=A0 =C2=A0if (reload) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("FETCHQH: reloading nakcnt to %d\= n", reload); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0set_field(&qh->altnext_qtd, reload, QH_ALTNE= XT_NAKCNT); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (qh->token & QTD_TOKEN_HALT) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("FETCHQH: QH Halted, go horizonta= l\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_HORIZONTALQH; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + > + =C2=A0 =C2=A0} else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_= qtd > 0x1000)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("FETCHQH: Active, !Halt, execute = - fetch qTD\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qtdaddr =3D qh->current_qtd; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_FETCHQTD; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* =C2=A0EHCI spec version 1.0 Section 4.10.= 2 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("FETCHQH: !Active, !Halt, advance= queue\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_ADVANCEQUEUE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0} > + > +out: > + =C2=A0 =C2=A0return again; > +} > + > +static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0EHCIitd itd; > + > + =C2=A0 =C2=A0get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sizeof(EHCIitd) >> 2); > + =C2=A0 =C2=A0DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next= is %08X)\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ehci->itdaddr, itd.nex= t); > + > + =C2=A0 =C2=A0if (ehci_process_itd(ehci, &itd) !=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sizeof(EHCIitd) = >> 2); > + =C2=A0 =C2=A0ehci->fetch_addr =3D itd.next; > + =C2=A0 =C2=A0*state =3D EST_FETCHENTRY; > + > + =C2=A0 =C2=A0return 1; > +} > + > +/* Section 4.10.2 - paragraph 3 */ > +static int ehci_state_advqueue(EHCIState *ehci, int async, int *state) > +{ > +#if 0 > + =C2=A0 =C2=A0/* TO-DO: 4.10.2 - paragraph 2 > + =C2=A0 =C2=A0 * if I-bit is set to 1 and QH is not active > + =C2=A0 =C2=A0 * go to horizontal QH > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0if (I-bit set) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_HORIZONTALQH; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0} > +#endif > + > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * want data and alt-next qTD is valid > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) !=3D 0) && > + =C2=A0 =C2=A0 =C2=A0 =C2=A0(ehci->qh.altnext_qtd > 0x1000) && > + =C2=A0 =C2=A0 =C2=A0 =C2=A0(NLPTR_TBIT(ehci->qh.altnext_qtd) =3D=3D 0))= { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("ADVQUEUE: goto alt next qTD. " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "curr 0x= %08x next 0x%08x alt 0x%08x (next qh %x)\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ehci->qh= .current_qtd, ehci->qh.altnext_qtd, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ehci->qh= .next_qtd, ehci->qh.next); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qtdaddr =3D ehci->qh.altnext_qtd; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_FETCHQTD; > + > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * =C2=A0next qTD is valid > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0} else if ((ehci->qh.next_qtd > 0x1000) && > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (NLPTR_TBIT(ehci->qh.n= ext_qtd) =3D=3D 0)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("ADVQUEUE: next qTD. " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "curr 0x= %08x next 0x%08x alt 0x%08x (next qh %x)\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ehci->qh= .current_qtd, ehci->qh.altnext_qtd, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ehci->qh= .next_qtd, ehci->qh.next); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qtdaddr =3D ehci->qh.next_qtd; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_FETCHQTD; > + > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * =C2=A0no valid qTD, try next QH > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("ADVQUEUE: go to horizontal QH\n"= ); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_HORIZONTALQH; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return 1; > +} > + > +/* Section 4.10.2 - paragraph 4 */ > +static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0EHCIqtd *qtd =3D &ehci->qtd; > + =C2=A0 =C2=A0int again =3D 0; > + > + =C2=A0 =C2=A0get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, size= of(EHCIqtd) >> 2); > + > + =C2=A0 =C2=A0if (qtd->token & QTD_TOKEN_ACTIVE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_EXECUTE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_HORIZONTALQH; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return again; > +} > + > +static int ehci_state_horizqh(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0int again =3D 0; > + > + =C2=A0 =C2=A0if (ehci->fetch_addr !=3D ehci->qh.next) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->fetch_addr =3D ehci->qh.next; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_FETCHENTRY; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_ACTIVE; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0return again; > +} > + > +static int ehci_state_execute(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0EHCIqh *qh =3D &ehci->qh; > + =C2=A0 =C2=A0EHCIqtd *qtd =3D &ehci->qtd; > + =C2=A0 =C2=A0int again =3D 0; > + =C2=A0 =C2=A0int reload, nakcnt; > + =C2=A0 =C2=A0int smask; > + > + =C2=A0 =C2=A0if (async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST(">>>>> ASYNC STATE MACHINE execut= e QH 0x%08x, QTD 0x%08x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qha= ddr, ehci->qtdaddr); > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST(">>>>> PERIODIC STATE MACHINE exe= cute\n"); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (ehci_qh_do_overlay(ehci, qh, qtd) !=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0smask =3D get_field(qh->epcap, QH_EPCAP_SMASK); > + > + =C2=A0 =C2=A0if (!smask) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0reload =3D get_field(qh->epchar, QH_EPCHAR_R= L); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0nakcnt =3D get_field(qh->altnext_qtd, QH_ALT= NEXT_NAKCNT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (reload && !nakcnt) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("EXECUTE: RL !=3D 0= but NakCnt =3D=3D 0 -- no execute\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_HORIZONTALQH; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0// TODO verify enough time remains in the uframe as in 4.4= .1.1 > + =C2=A0 =C2=A0// TODO write back ptr to async list when done or out of t= ime > + =C2=A0 =C2=A0// TODO Windows does not seem to ever set the MULT field > + > + =C2=A0 =C2=A0if (!async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0int transactCtr =3D get_field(qh->epcap, QH_= EPCAP_MULT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!transactCtr) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ZERO transactctr for = int qh, go HORIZ\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_HORIZONTALQH; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0if (async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbsts |=3D USBSTS_REC; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0ehci->exec_status =3D ehci_execute(ehci, qh); > + =C2=A0 =C2=A0if (ehci->exec_status =3D=3D USB_RET_PROCERR) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D -1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0*state =3D EST_EXECUTING; > + > + =C2=A0 =C2=A0if (ehci->exec_status !=3D USB_RET_ASYNC) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0} > + > +out: > + =C2=A0 =C2=A0return again; > +} > + > +static int ehci_state_executing(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0EHCIqh *qh =3D &ehci->qh; > + =C2=A0 =C2=A0int again =3D 0; > + =C2=A0 =C2=A0int reload, nakcnt; > + > + =C2=A0 =C2=A0ehci->exec_status =3D ehci_execute_complete(ehci, qh, ehci= ->exec_status); > + =C2=A0 =C2=A0if (ehci->exec_status =3D=3D USB_RET_ASYNC) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0if (ehci->exec_status =3D=3D USB_RET_PROCERR) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D -1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0goto out; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0// 4.10.3 > + =C2=A0 =C2=A0if (!async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0int transactCtr =3D get_field(qh->epcap, QH_= EPCAP_MULT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0transactCtr--; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0set_field(&qh->epcap, transactCtr, QH_EPCAP_= MULT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// 4.10.3, bottom of page 82, should exit th= is state when transaction > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// counter decrements to 0 > + =C2=A0 =C2=A0} > + > + > + =C2=A0 =C2=A0reload =3D get_field(qh->epchar, QH_EPCHAR_RL); > + =C2=A0 =C2=A0if (reload) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0nakcnt =3D get_field(qh->altnext_qtd, QH_ALT= NEXT_NAKCNT); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->exec_status =3D=3D USB_RET_NAK) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (nakcnt) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0nakcnt--; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("EXECUTING: Nak occ= ured and RL !=3D 0, dec NakCnt to %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0na= kcnt); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0nakcnt =3D reload; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("EXECUTING: Nak did= n't occur, reloading to %d\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 nakcnt); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0set_field(&qh->altnext_qtd, nakcnt, QH_ALTNE= XT_NAKCNT); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0/* > + =C2=A0 =C2=A0 * =C2=A0Write the qh back to guest physical memory. =C2= =A0This step isn't > + =C2=A0 =C2=A0 * =C2=A0in the EHCI spec but we need to do it since we do= n't share > + =C2=A0 =C2=A0 * =C2=A0physical memory with our guest VM. > + =C2=A0 =C2=A0 */ > + > + =C2=A0 =C2=A0DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, ne= xt 0x%x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->qhaddr, qh->next)= ; > + =C2=A0 =C2=A0put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeo= f(EHCIqh) >> 2); > + > + =C2=A0 =C2=A0/* 4.10.5 */ > + =C2=A0 =C2=A0if ((ehci->exec_status =3D=3D USB_RET_NAK) || (qh->token &= QTD_TOKEN_ACTIVE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_HORIZONTALQH; > + =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_WRITEBACK; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0again =3D 1; > + > +out: > + =C2=A0 =C2=A0return again; > +} > + > + > +static int ehci_state_writeback(EHCIState *ehci, int async, int *state) > +{ > + =C2=A0 =C2=A0EHCIqh *qh =3D &ehci->qh; > + =C2=A0 =C2=A0int again =3D 0; > + > + =C2=A0 =C2=A0/* =C2=A0Write back the QTD from the QH area */ > + =C2=A0 =C2=A0DPRINTF_ST("WRITEBACK: write QTD to VM memory\n"); > + =C2=A0 =C2=A0put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next= _qtd, Space after comma. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sizeof(EHCIqtd) = >> 2); > + > + =C2=A0 =C2=A0/* TODO confirm next state. =C2=A0For now, keep going if a= sync > + =C2=A0 =C2=A0 * but stop after one qtd if periodic > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0//if (async) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0*state =3D EST_ADVANCEQUEUE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 1; > + =C2=A0 =C2=A0//} else { > + =C2=A0 =C2=A0// =C2=A0 =C2=A0*state =3D EST_ACTIVE; > + =C2=A0 =C2=A0//} > + =C2=A0 =C2=A0return again; > +} > + > +/* > + * This is the state machine that is common to both async and periodic > + */ > + > +static int ehci_advance_state(EHCIState *ehci, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int async, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int state) > +{ > + =C2=A0 =C2=A0int again; > + =C2=A0 =C2=A0int iter =3D 0; > + > + =C2=A0 =C2=A0do { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (state =3D=3D EST_FETCHQH) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iter++; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* if we are roaming a lot of = QH without executing a qTD > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * something is wrong with the= linked list. TO-DO: why is > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * this hack needed? > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (iter > MAX_ITERATIONS) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("\n*** a= dvance_state: bailing on MAX ITERATIONS***\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0state =3D EST_AC= TIVE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0switch(state) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_WAITLISTHEAD: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_waitlisth= ead(ehci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_FETCHENTRY: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_fetchentr= y(ehci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_FETCHQH: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_fetchqh(e= hci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_FETCHITD: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_fetchitd(= ehci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_ADVANCEQUEUE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_advqueue(= ehci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_FETCHQTD: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_fetchqtd(= ehci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_HORIZONTALQH: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_horizqh(e= hci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_EXECUTE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iter =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_execute(e= hci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_EXECUTING: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_executing= (ehci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case EST_WRITEBACK: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D ehci_state_writeback= (ehci, async, &state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "Bad state!\n"= ); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D -1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (again < 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "processing er= ror - resetting ehci HC\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_reset(ehci); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0again =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0while (again); > + > + =C2=A0 =C2=A0ehci_commit_interrupt(ehci); > + =C2=A0 =C2=A0return state; > +} > + > +static void ehci_advance_async_state(EHCIState *ehci) > +{ > + =C2=A0 =C2=A0EHCIqh qh; > + =C2=A0 =C2=A0int state =3D ehci->astate; > + > + =C2=A0 =C2=A0switch(state) { > + =C2=A0 =C2=A0case EST_INACTIVE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!(ehci->usbcmd & USBCMD_ASE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbsts |=3D USBSTS_ASS; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->astate =3D EST_ACTIVE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0// No break, fall through to ACTIVE > + > + =C2=A0 =C2=A0case EST_ACTIVE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ( !(ehci->usbcmd & USBCMD_ASE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbsts &=3D ~USBSTS_ASS; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->astate =3D EST_INACTIVE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* If the doorbell is set, the guest wants t= o make a change to the > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 * schedule. The host controller needs to re= lease cached data. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 * (section 4.8.2) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->usbcmd & USBCMD_IAAD) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("ASYNC: doorbell reque= st acknowledged\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbcmd &=3D ~USBCMD_IAAD= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_set_interrupt(ehci, USBST= S_IAA); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* make sure guest has acknowledged */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* TO-DO: is this really needed? */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->usbsts & USBSTS_IAA) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("IAA status bit still = set.\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF_ST("ASYNC: waiting for listhead, sta= rting at %08x\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->asynclista= ddr); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* check that address register has been set = */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->asynclistaddr =3D=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0state =3D EST_WAITLISTHEAD; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* fall through */ > + > + =C2=A0 =C2=A0case EST_FETCHENTRY: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* fall through */ > + > + =C2=A0 =C2=A0case EST_EXECUTING: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_= t *) &qh, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sizeof(E= HCIqh) >> 2); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->astate =3D ehci_advance_state(ehci, 1,= state); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* this should only be due to a developer mi= stake */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "ehci: Bad asynchronous stat= e %d. " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"Resetting to ac= tive\n", ehci->astate); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->astate =3D EST_ACTIVE; > + =C2=A0 =C2=A0} > +} > + > +static void ehci_advance_periodic_state(EHCIState *ehci) > +{ > + =C2=A0 =C2=A0uint32_t entry; > + =C2=A0 =C2=A0uint32_t list; > + > + =C2=A0 =C2=A0// 4.6 > + > + =C2=A0 =C2=A0switch(ehci->pstate) { > + =C2=A0 =C2=A0case EST_INACTIVE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ( !(ehci->frindex & 7) && (ehci->usbcmd &= USBCMD_PSE)) { Extra space here. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("PERIODIC going active= \n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbsts |=3D USBSTS_PSS; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->pstate =3D EST_ACTIVE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// No break, fall through to A= CTIVE > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case EST_ACTIVE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ( !(ehci->frindex & 7) && !(ehci->usbcmd = & USBCMD_PSE)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("PERIODIC going inacti= ve\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->usbsts &=3D ~USBSTS_PSS; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->pstate =3D EST_INACTIVE; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0list =3D ehci->periodiclistbase & 0xfffff000= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* check that register has been set */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (list =3D=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0list |=3D ((ehci->frindex & 0x1ff8) >> 1); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0cpu_physical_memory_rw(list, (uint8_t *) &en= try, sizeof entry, 0); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0entry =3D le32_to_cpu(entry); > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("PERIODIC state adv fr=3D%d. =C2=A0[= %08X] -> %08X\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->frindex / = 8, list, entry); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->fetch_addr =3D entry; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->pstate =3D ehci_advance_state(ehci, 0,= EST_FETCHENTRY); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0case EST_EXECUTING: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("PERIODIC state adv for executing\n"= ); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->pstate =3D ehci_advance_state(ehci, 0,= EST_EXECUTING); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* this should only be due to a developer mi= stake */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0fprintf(stderr, "ehci: Bad periodic state %d= . " > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"Resetting to ac= tive\n", ehci->pstate); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->pstate =3D EST_ACTIVE; > + =C2=A0 =C2=A0} > +} > + > +static void ehci_frame_timer(void *opaque) > +{ > + =C2=A0 =C2=A0EHCIState *ehci =3D opaque; > + =C2=A0 =C2=A0int64_t expire_time, t_now; > + =C2=A0 =C2=A0int usec_elapsed; > + =C2=A0 =C2=A0int frames; > + =C2=A0 =C2=A0int usec_now; > + =C2=A0 =C2=A0int i; > + =C2=A0 =C2=A0int skipped_frames =3D 0; > + > + > + =C2=A0 =C2=A0t_now =3D qemu_get_clock_ns(vm_clock); > + =C2=A0 =C2=A0expire_time =3D t_now + (get_ticks_per_sec() / FRAME_TIMER= _FREQ); > + =C2=A0 =C2=A0if (expire_time =3D=3D t_now) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0expire_time++; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0usec_now =3D t_now / 1000; > + =C2=A0 =C2=A0usec_elapsed =3D usec_now - ehci->last_run_usec; > + =C2=A0 =C2=A0frames =3D usec_elapsed / FRAME_TIMER_USEC; > + =C2=A0 =C2=A0ehci->frame_end_usec =3D usec_now + FRAME_TIMER_USEC - 10; > + > + =C2=A0 =C2=A0for (i =3D 0; i < frames; i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if ( !(ehci->usbsts & USBSTS_HALT)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->isoch_pause <=3D 0) = { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->frindex += =3D 8; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->frindex > 0x00001fff= ) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->frindex = =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_set_interru= pt(ehci, USBSTS_FLR); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->sofv =3D (ehci->frindex = - 1) >> 3; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->sofv &=3D 0x000003ff; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (frames - i > 10) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0skipped_frames++; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// TODO could this cause perio= dic frames to get skipped if async > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// active? > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (ehci->astate !=3D EST_EXEC= UTING) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_advance_per= iodic_state(ehci); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci->last_run_usec +=3D FRAME_TIMER_USEC; > + =C2=A0 =C2=A0} > + > +#if 0 > + =C2=A0 =C2=A0if (skipped_frames) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF("WARNING - EHCI skipped %d frames\n"= , skipped_frames); > + =C2=A0 =C2=A0} > +#endif > + > + =C2=A0 =C2=A0/* =C2=A0Async is not inside loop since it executes everyt= hing it can once > + =C2=A0 =C2=A0 * =C2=A0called > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0if (ehci->pstate !=3D EST_EXECUTING) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_advance_async_state(ehci); > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0qemu_mod_timer(ehci->frame_timer, expire_time); > +} > + > +static CPUReadMemoryFunc *ehci_readfn[3]=3D{ 'const', spaces around '=3D' > + =C2=A0 =C2=A0ehci_mem_readb, > + =C2=A0 =C2=A0ehci_mem_readw, > + =C2=A0 =C2=A0ehci_mem_readl > +}; > + > +static CPUWriteMemoryFunc *ehci_writefn[3]=3D{ > + =C2=A0 =C2=A0ehci_mem_writeb, > + =C2=A0 =C2=A0ehci_mem_writew, > + =C2=A0 =C2=A0ehci_mem_writel > +}; > + > +static void ehci_map(PCIDevice *pci_dev, int region_num, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p= cibus_t addr, pcibus_t size, int type) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D(EHCIState *)pci_dev; > + > + =C2=A0 =C2=A0DPRINTF("ehci_map: region %d, addr %08" PRIx64 ", size %" = PRId64 ", s->mem %08X\n", Instead of PRIx64, please use FMT_PCIBUS. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0region_num, addr, size, s->mem= ); > + =C2=A0 =C2=A0s->mem_base =3D addr; > + =C2=A0 =C2=A0cpu_register_physical_memory(addr, size, s->mem); > +} > + > +static int usb_ehci_initfn(PCIDevice *dev); > + > +static USBPortOps ehci_port_ops =3D { > + =C2=A0 =C2=A0.attach =3D ehci_attach, > + =C2=A0 =C2=A0.detach =3D ehci_detach, > + =C2=A0 =C2=A0.complete =3D ehci_async_complete_packet, > +}; > + > +static PCIDeviceInfo ehci_info =3D { > + =C2=A0 =C2=A0.qdev.name =C2=A0 =C2=A0=3D "usb-ehci", > + =C2=A0 =C2=A0.qdev.size =C2=A0 =C2=A0=3D sizeof(EHCIState), > + =C2=A0 =C2=A0.init =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D usb_ehci_initfn, > +}; > + > +static int usb_ehci_initfn(PCIDevice *dev) > +{ > + =C2=A0 =C2=A0EHCIState *s =3D DO_UPCAST(EHCIState, dev, dev); > + =C2=A0 =C2=A0uint8_t *pci_conf =3D s->dev.config; > + =C2=A0 =C2=A0int i; > + > + =C2=A0 =C2=A0pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); > + =C2=A0 =C2=A0pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_828= 01D); > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[PCI_REVISION_ID], 0x10); > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); > + =C2=A0 =C2=A0pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB); > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[PCI_HEADER_TYPE], PCI_HEADER_TYPE_N= ORMAL); > + > + =C2=A0 =C2=A0/* capabilities pointer */ > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); > + =C2=A0 =C2=A0//pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); > + > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrup= t pin 3 > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); > + > + =C2=A0 =C2=A0// pci_conf[0x50] =3D 0x01; // power management caps > + > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[0x60], 0x20); =C2=A0// spec release= number (2.1.4) > + =C2=A0 =C2=A0pci_set_byte(&pci_conf[0x61], 0x20); =C2=A0// frame length= adjustment (2.1.5) > + =C2=A0 =C2=A0pci_set_word(&pci_conf[0x62], 0x00); =C2=A0// port wake up= capability (2.1.6) > + > + =C2=A0 =C2=A0pci_conf[0x64] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x65] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x66] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x67] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x68] =3D 0x01; > + =C2=A0 =C2=A0pci_conf[0x69] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x6a] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x6b] =3D 0x00; =C2=A0// USBLEGSUP > + =C2=A0 =C2=A0pci_conf[0x6c] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x6d] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x6e] =3D 0x00; > + =C2=A0 =C2=A0pci_conf[0x6f] =3D 0xc0; =C2=A0// USBLEFCTLSTS > + > + =C2=A0 =C2=A0// 2.2 host controller interface version > + =C2=A0 =C2=A0s->mmio[0x00] =3D (uint8_t) OPREGBASE; > + =C2=A0 =C2=A0s->mmio[0x01] =3D 0x00; > + =C2=A0 =C2=A0s->mmio[0x02] =3D 0x00; > + =C2=A0 =C2=A0s->mmio[0x03] =3D 0x01; =C2=A0 =C2=A0 =C2=A0 =C2=A0// HC v= ersion > + =C2=A0 =C2=A0s->mmio[0x04] =3D NB_PORTS; =C2=A0 =C2=A0// Number of down= stream ports > + =C2=A0 =C2=A0s->mmio[0x05] =3D 0x00; =C2=A0 =C2=A0 =C2=A0 =C2=A0// No c= ompanion ports at present > + =C2=A0 =C2=A0s->mmio[0x06] =3D 0x00; > + =C2=A0 =C2=A0s->mmio[0x07] =3D 0x00; > + =C2=A0 =C2=A0s->mmio[0x08] =3D 0x80; =C2=A0 =C2=A0 =C2=A0 =C2=A0// We c= an cache whole frame, not 64-bit capable > + =C2=A0 =C2=A0s->mmio[0x09] =3D 0x68; =C2=A0 =C2=A0 =C2=A0 =C2=A0// EECP > + =C2=A0 =C2=A0s->mmio[0x0a] =3D 0x00; > + =C2=A0 =C2=A0s->mmio[0x0b] =3D 0x00; > + > + =C2=A0 =C2=A0s->irq =3D s->dev.irq[3]; > + > + =C2=A0 =C2=A0usb_bus_new(&s->bus, &s->dev.qdev); > + =C2=A0 =C2=A0for(i =3D 0; i < NB_PORTS; i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0usb_register_port(&s->bus, &s->ports[i], s, = i, &ehci_port_ops, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0USB_SPEED_MASK_HIGH); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0usb_port_location(&s->ports[i], NULL, i+1); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0s->ports[i].dev =3D 0; > + =C2=A0 =C2=A0} > + > + =C2=A0 =C2=A0s->frame_timer =3D qemu_new_timer_ns(vm_clock, ehci_frame_= timer, s); > + > + =C2=A0 =C2=A0qemu_register_reset(ehci_reset, s); qdev.reset > + > + =C2=A0 =C2=A0s->mem =3D cpu_register_io_memory(ehci_readfn, ehci_writef= n, s, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0DEVICE_LITTLE_ENDIAN= ); > + > + =C2=A0 =C2=A0pci_register_bar(&s->dev, 0, MMIO_SIZE, PCI_BASE_ADDRESS_S= PACE_MEMORY, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ehci_map)= ; > + > + =C2=A0 =C2=A0fprintf(stderr, "*** EHCI support is under development ***= \n"); Right... > + > + =C2=A0 =C2=A0return 0; > +} > + > +static void ehci_register(void) > +{ > + =C2=A0 =C2=A0pci_qdev_register(&ehci_info); > +} > +device_init(ehci_register); > + > +/* > + * vim: expandtab ts=3D4 > + */ > -- > 1.7.1 > > >