From 421a5b8754be11a881fe1e97bc38f07a33826675 Mon Sep 17 00:00:00 2001 From: ergo720 Date: Mon, 28 May 2018 21:14:21 +0200 Subject: [PATCH] Usb port stuff (wip) --- src/devices/USBController/OHCI.cpp | 98 ++++++++++++++++--- src/devices/USBController/OHCI.h | 54 +++++++++-- src/devices/USBController/USBDevice.cpp | 34 +++---- src/devices/USBController/USBDevice.h | 121 +++++++++++++++++------- 4 files changed, 234 insertions(+), 73 deletions(-) diff --git a/src/devices/USBController/OHCI.cpp b/src/devices/USBController/OHCI.cpp index b76c1c398..669fad9cb 100644 --- a/src/devices/USBController/OHCI.cpp +++ b/src/devices/USBController/OHCI.cpp @@ -47,18 +47,13 @@ typedef enum _USB_SPEED } USB_SPEED; -// global pointers to the two USB host controllers available on the Xbox -OHCI* g_pHostController1 = nullptr; -OHCI* g_pHostController2 = nullptr; - -OHCI::OHCI(USBDevice* UsbObj, int Irq) +OHCI::OHCI(int Irq) { - UsbInstance = UsbObj; Irq_n = Irq; for (int i = 0; i < 2; i++) { - UsbInstance->USB_RegisterPort(&Registers.RhPort[i].Port, this, i, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + USB_RegisterPort(&Registers.RhPort[i].UsbPort, i, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); } OHCI_PacketInit(&UsbPacket); @@ -79,7 +74,7 @@ void OHCI::OHCI_StateReset() // The usb state can be USB_Suspend if it is a software reset, and USB_Reset if it is a hardware // reset or cold boot - // TODO: stop all the list processing here + OHCI_BusStop(); // Reset all registers // Remark: the standard says that RemoteWakeupConnected bit should be set during POST, cleared during hw reset @@ -112,11 +107,13 @@ void OHCI::OHCI_StateReset() { OHCIPort* Port = &Registers.RhPort[i]; Port->HcRhPortStatus = 0; - //if (Port->port.device && Port->port.device->attached) { - //usb_port_reset(&Port->port); - //} + if (Port->UsbPort.Dev && Port->UsbPort.Dev->Attached) { + USB_PortReset(&Port->UsbPort); + } } + OHCI_StopEndpoints(); + DbgPrintf("Ohci: Reset event.\n"); } @@ -443,3 +440,82 @@ void OHCI::OHCI_SetInterrupt(uint32_t Value) Registers.HcInterruptStatus |= Value; OHCI_UpdateInterrupt(); } + +void OHCI::OHCI_StopEndpoints() +{ + USBDev* dev; + int i, j; + + for (i = 0; i < 2; i++) { + dev = Registers.RhPort[i].UsbPort.Dev; + if (dev && dev->Attached) { + USB_DeviceEPstopped(dev, &dev->EP_ctl); + for (j = 0; j < USB_MAX_ENDPOINTS; j++) { + USB_DeviceEPstopped(dev, &dev->EP_in[j]); + USB_DeviceEPstopped(dev, &dev->EP_out[j]); + } + } + } +} + +void OHCI::OHCI_Detach(USBPort* Port) +{ + OHCIPort* port = &Registers.RhPort[Port->PortIndex]; + uint32_t old_state = port->HcRhPortStatus; + + ohci_async_cancel_device(Port->Dev); + + // set connect status + if (port->HcRhPortStatus & OHCI_PORT_CCS) { + port->HcRhPortStatus &= ~OHCI_PORT_CCS; + port->HcRhPortStatus |= OHCI_PORT_CSC; + } + // disable port + if (port->HcRhPortStatus & OHCI_PORT_PES) { + port->HcRhPortStatus &= ~OHCI_PORT_PES; + port->HcRhPortStatus |= OHCI_PORT_PESC; + } + + DbgPrintf("Ohci: Detached port %d\n", Port->PortIndex); + + if (old_state != port->HcRhPortStatus) { + OHCI_SetInterrupt(OHCI_INTR_RHSC); + } +} + +void OHCI::USB_RegisterPort(USBPort* Port, int Index, int SpeedMask) +{ + Port->PortIndex = Index; + Port->SpeedMask = SpeedMask; + Port->HubCount = 0; + std::snprintf(Port->Path, sizeof(Port->Path), "%d", Index + 1); +} + +void OHCI::USB_DeviceEPstopped(USBDev* Dev, USBEndpoint* EP) +{ + // This seems to be a nop in XQEMU since it doesn't assign the EP_Stopped function (it's nullptr) + USBDeviceClass* klass = USB_DEVICE_GET_CLASS(Dev); + if (klass->EP_Stopped) { + klass->EP_Stopped(Dev, EP); + } +} + +void OHCI::USB_PortReset(USBPort* Port) +{ + USBDev* dev = Port->Dev; + + assert(dev != nullptr); + USB_Detach(Port); + usb_attach(port); + usb_device_reset(dev); +} + +void OHCI::USB_Detach(USBPort* Port) +{ + USBDev* dev = Port->Dev; + + assert(dev != nullptr); + assert(dev->State != USB_STATE_NOTATTACHED); + OHCI_Detach(Port); + dev->State = USB_STATE_NOTATTACHED; +} \ No newline at end of file diff --git a/src/devices/USBController/OHCI.h b/src/devices/USBController/OHCI.h index fe5d70c86..b0cea7eee 100644 --- a/src/devices/USBController/OHCI.h +++ b/src/devices/USBController/OHCI.h @@ -66,7 +66,15 @@ #define OHCI_STATUS_OCR (1<<3) // OwnershipChangeRequest #define OHCI_STATUS_SOC ((1<<6)|(1<<7)) // SchedulingOverrunCount // HcInterruptStatus +#define OHCI_INTR_SO (1<<0) // Scheduling overrun +#define OHCI_INTR_WD (1<<1) // HcDoneHead writeback #define OHCI_INTR_SF (1<<2) // Start of frame +#define OHCI_INTR_RD (1<<3) // Resume detect +#define OHCI_INTR_UE (1<<4) // Unrecoverable error +#define OHCI_INTR_FNO (1<<5) // Frame number overflow +#define OHCI_INTR_RHSC (1<<6) // Root hub status change +#define OHCI_INTR_OC (1<<30) // Ownership change +#define OHCI_INTR_MIE (1<<31) // Master Interrupt Enable // HcInterruptEnable, HcInterruptDisable #define OHCI_INTR_MIE (1<<31) // MasterInterruptEnable // HcHCCA @@ -84,8 +92,20 @@ #define OHCI_RHA_OCPM (1<<11) // OverCurrentProtectionMode #define OHCI_RHA_NOCP (1<<12) // NoOverCurrentProtection // HcRhPortStatus +#define OHCI_PORT_CCS (1<<0) // CurrentConnectStatus +#define OHCI_PORT_PES (1<<1) // PortEnableStatus +#define OHCI_PORT_PSS (1<<2) // PortSuspendStatus +#define OHCI_PORT_POCI (1<<3) // PortOverCurrentIndicator +#define OHCI_PORT_PRS (1<<4) // PortResetStatus #define OHCI_PORT_PPS (1<<8) // PortPowerStatus - +#define OHCI_PORT_LSDA (1<<9) // LowSpeedDeviceAttached +#define OHCI_PORT_CSC (1<<16) // ConnectStatusChange +#define OHCI_PORT_PESC (1<<17) // PortEnableStatusChange +#define OHCI_PORT_PSSC (1<<18) // PortSuspendStatusChange +#define OHCI_PORT_OCIC (1<<19) // PortOverCurrentIndicatorChange +#define OHCI_PORT_PRSC (1<<20) // PortResetStatusChange +#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \ + |OHCI_PORT_OCIC|OHCI_PORT_PRSC) // enum indicating the current HC state typedef enum _OHCI_State @@ -100,7 +120,7 @@ OHCI_State; // Small struct used to hold the HcRhPortStatus register and the usb port status typedef struct _OHCIPort { - USBPort Port; + USBPort UsbPort; uint32_t HcRhPortStatus; } OHCIPort; @@ -135,6 +155,8 @@ typedef struct _OHCI_Registers uint32_t HcRhDescriptorA; uint32_t HcRhDescriptorB; uint32_t HcRhStatus; + // I have some doubts here. Both XQEMU and OpenXbox set 4 ports per HC, for a total of 8 usb ports. + // Could it be becasue each gamepad can host 2 memory units? OHCIPort RhPort[2]; // 2 ports per HC, for a total of 4 USB ports } OHCI_Registers; @@ -145,7 +167,7 @@ class OHCI { public: // constructor - OHCI(USBDevice* UsbObj, int Irqn); + OHCI(int Irqn); // destructor ~OHCI() {} // read a register @@ -165,8 +187,6 @@ class OHCI uint64_t UsbFrameTime; // ticks per usb tick uint64_t TicksPerUsbTick; - // the usb device instance of this HC - USBDevice* UsbInstance; // usb packet USBPacket UsbPacket; // irq number @@ -192,9 +212,27 @@ class OHCI void OHCI_UpdateInterrupt(); // fire an interrupt void OHCI_SetInterrupt(uint32_t Value); + // + void OHCI_StopEndpoints(); + // update ohci registers during a device detach + void OHCI_Detach(USBPort* Port); + + // register a port with the HC + void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask); + // + void USB_DeviceEPstopped(USBDev* Dev, USBEndpoint* EP); + // reset a usb port + void USB_PortReset(USBPort* Port); + // a device is attched + void Attach(USBPort* port); + // a device is detached + void USB_Detach(USBPort* port); + // a device downstream from the device attached to the port (attached through a hub) is detached + void ChildDetach(USBPort* port, USBDev* child); + // TODO + void Wakeup(USBPort* port); + // TODO + void Complete(USBPort* port, USBPacket *p); }; -extern OHCI* g_pHostController1; -extern OHCI* g_pHostController2; - #endif diff --git a/src/devices/USBController/USBDevice.cpp b/src/devices/USBController/USBDevice.cpp index 4454d0f11..4e8f2a2c9 100644 --- a/src/devices/USBController/USBDevice.cpp +++ b/src/devices/USBController/USBDevice.cpp @@ -36,7 +36,6 @@ #include "USBDevice.h" #include "OHCI.h" -#include void USBDevice::Init(unsigned int address) @@ -53,11 +52,11 @@ void USBDevice::Init(unsigned int address) m_VendorId = PCI_VENDOR_ID_NVIDIA; if (address == USB0_BASE) { - g_pHostController1 = new OHCI(this, 1); + m_pHostController1 = new OHCI(1); return; } - g_pHostController2 = new OHCI(this, 9); + m_pHostController2 = new OHCI(9); } uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size) @@ -66,13 +65,13 @@ uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size) assert(barIndex == 0); // Figure out the correct OHCI object and read the register - if (addr >= USB1_BASE) { - // USB1 queried - return g_pHostController2->OHCI_ReadRegister(addr); + if (addr >= USB0_BASE) { + // USB0 queried + return m_pHostController1->OHCI_ReadRegister(addr); } - // USB0 queried - return g_pHostController1->OHCI_ReadRegister(addr); + // USB1 queried + return m_pHostController2->OHCI_ReadRegister(addr); } void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) @@ -81,21 +80,12 @@ void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned assert(barIndex == 0); // Figure out the correct OHCI object and write the value to the register - if (addr >= USB1_BASE) { - // USB1 queried - g_pHostController2->OHCI_WriteRegister(addr, value); + if (addr >= USB0_BASE) { + // USB0 queried + m_pHostController1->OHCI_WriteRegister(addr, value); return; } - // USB0 queried - g_pHostController1->OHCI_WriteRegister(addr, value); -} - -void USBDevice::USB_RegisterPort(USBPort* Port, OHCI* Obj, int Index, int SpeedMask) -{ - Port->Opaque = Obj; - Port->PortIndex = Index; - Port->SpeedMask = SpeedMask; - Port->HubCount = 0; - std::snprintf(Port->Path, sizeof(Port->Path), "%d", Index + 1); + // USB1 queried + m_pHostController2->OHCI_WriteRegister(addr, value); } diff --git a/src/devices/USBController/USBDevice.h b/src/devices/USBController/USBDevice.h index 2839b0809..0759b718c 100644 --- a/src/devices/USBController/USBDevice.h +++ b/src/devices/USBController/USBDevice.h @@ -37,7 +37,23 @@ #define USBDEVICE_H_ #include "..\PCIDevice.h" +#include +#define USB_MAX_ENDPOINTS 15 +#define USB_MAX_INTERFACES 16 + +#define USB_STATE_NOTATTACHED 0 +#define USB_STATE_ATTACHED 1 + +typedef enum USBPacketState { + USB_PACKET_UNDEFINED = 0, + USB_PACKET_SETUP, + USB_PACKET_QUEUED, + USB_PACKET_ASYNC, + USB_PACKET_COMPLETE, + USB_PACKET_CANCELED, +} +USBPacketState; // This is a linux struct for vectored I/O. See readv() and writev() typedef struct _IoVec @@ -56,6 +72,20 @@ typedef struct _IOVector } IOVector; +typedef struct _USBEndpoint +{ + uint8_t nr; + uint8_t pid; + uint8_t type; + uint8_t ifnum; + int max_packet_size; + bool pipeline; + bool halted; + USBDev *dev; + //QTAILQ_HEAD(, USBPacket) queue; +} +USBEndpoint; + // definition of a USB device typedef struct _USBDev { @@ -73,9 +103,9 @@ typedef struct _USBDev uint8_t addr; char product_desc[32]; int auto_attach; - int attached; + int Attached; // device is attached - int32_t state; + int32_t State; // current state of device uint8_t setup_buf[8]; uint8_t data_buf[4096]; int32_t remote_wakeup; @@ -83,9 +113,9 @@ typedef struct _USBDev int32_t setup_len; int32_t setup_index; - USBEndpoint ep_ctl; - USBEndpoint ep_in[USB_MAX_ENDPOINTS]; - USBEndpoint ep_out[USB_MAX_ENDPOINTS]; + USBEndpoint EP_ctl; + USBEndpoint EP_in[USB_MAX_ENDPOINTS]; + USBEndpoint EP_out[USB_MAX_ENDPOINTS]; //QLIST_HEAD(, USBDescString) strings; const USBDesc *usb_desc; // Overrides class usb_desc if not NULL @@ -99,19 +129,56 @@ typedef struct _USBDev } USBDev; -typedef struct _USBEndpoint +typedef struct USBDeviceClass { - uint8_t nr; - uint8_t pid; - uint8_t type; - uint8_t ifnum; - int max_packet_size; - bool pipeline; - bool halted; - USBDevice *dev; - //QTAILQ_HEAD(, USBPacket) queue; + DeviceClass parent_class; + + int(*init)(USBDev *dev); + + // Walk (enabled) downstream ports, check for a matching device. + // Only hubs implement this. + USBDev *(*find_device)(USBDev *dev, uint8_t addr); + + // Called when a packet is canceled. + void(*cancel_packet)(USBDev *dev, USBPacket *p); + + // Called when device is destroyed. + void(*handle_destroy)(USBDev *dev); + + // Attach the device + void(*handle_attach)(USBDev *dev); + + // Reset the device + void(*handle_reset)(USBDev *dev); + + // Process control request. + // Called from handle_packet(). + // Status gets stored in p->status, and if p->status == USB_RET_SUCCESS + // then the number of bytes transferred is stored in p->actual_length + void(*handle_control)(USBDev *dev, USBPacket *p, int request, int value, + int index, int length, uint8_t *data); + + // Process data transfers (both BULK and ISOC). + // Called from handle_packet(). + // Status gets stored in p->status, and if p->status == USB_RET_SUCCESS + // then the number of bytes transferred is stored in p->actual_length + void(*handle_data)(USBDev *dev, USBPacket *p); + + void(*set_interface)(USBDev *dev, int Interface, + int alt_old, int alt_new); + + // Called when the hcd is done queuing packets for an endpoint, only + // necessary for devices which can return USB_RET_ADD_TO_QUEUE. + void(*flush_ep_queue)(USBDev *dev, USBEndpoint *ep); + + // Called by the hcd to let the device know the queue for an endpoint + // has been unlinked / stopped. Optional may be NULL. + void(*EP_Stopped)(USBDev* Dev, USBEndpoint* EP); + + const char *product_desc; + const USBDesc *usb_desc; } -USBEndpoint; +USBDeviceClass; // Structure used to hold information about an active USB packet typedef struct _USBPacket @@ -137,23 +204,12 @@ USBPacket; // Struct describing the status of a usb port typedef struct _USBPort { - USBDev *Dev; + USBDev* Dev; // usb device (if present) int SpeedMask; // usb speeds supported int HubCount; // number of hubs attached char Path[16]; // the number of the port - OHCI* Opaque; // OHCI* to let USBPort access it - int PortIndex; // internal port index, may be used with the Opaque + int PortIndex; // internal port index //QTAILQ_ENTRY(USBPort) next; - // a device is attched - void Attach(USBPort* port); - // a device is detached - void Detach(USBPort* port); - // a device downstream from the device attached to the port (attached through a hub) is detached - void ChildDetach(USBPort* port, USBDev* child); - // TODO - void Wakeup(USBPort* port); - // TODO - void Complete(USBPort* port, USBPacket *p); } USBPort; @@ -174,9 +230,10 @@ class USBDevice : public PCIDevice { uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size); void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size); - // USB specific functions - // register a port with the HC - void USB_RegisterPort(USBPort* Port, OHCI* Obj, int Index, int SpeedMask); + + // pointers to the two USB host controllers available on the Xbox + OHCI* m_pHostController1 = nullptr; + OHCI* m_pHostController2 = nullptr; }; #endif