diff --git a/src/devices/USBController/OHCI.cpp b/src/devices/USBController/OHCI.cpp index 669fad9cb..4f014fda3 100644 --- a/src/devices/USBController/OHCI.cpp +++ b/src/devices/USBController/OHCI.cpp @@ -39,6 +39,9 @@ #define USB_HZ 12000000 +#define USB_SPEED_LOW 0 +#define USB_SPEED_FULL 1 + typedef enum _USB_SPEED { @@ -50,7 +53,7 @@ USB_SPEED; OHCI::OHCI(int Irq) { - Irq_n = Irq; + IrqNum = Irq; for (int i = 0; i < 2; i++) { USB_RegisterPort(&Registers.RhPort[i].UsbPort, i, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); @@ -114,7 +117,7 @@ void OHCI::OHCI_StateReset() OHCI_StopEndpoints(); - DbgPrintf("Ohci: Reset event.\n"); + DbgPrintf("Ohci: Reset mode event.\n"); } void OHCI::OHCI_BusStart() @@ -122,7 +125,7 @@ void OHCI::OHCI_BusStart() // Create the end-of-frame timer. Let's try a factor of 50 (1 virtual ms -> 50 real ms) pEOFtimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, 50); - DbgPrintf("Ohci: Operational event\n"); + DbgPrintf("Ohci: Operational mode event\n"); // SOF event OHCI_SOF(); @@ -163,11 +166,11 @@ void OHCI::OHCI_ChangeState(uint32_t Value) case Suspend: OHCI_BusStop(); - DbgPrintf("Ohci: Suspend event\n"); + DbgPrintf("Ohci: Suspend mode event\n"); break; case Resume: - DbgPrintf("Ohci: Resume event\n"); + DbgPrintf("Ohci: Resume mode event\n"); break; case Reset: @@ -175,7 +178,7 @@ void OHCI::OHCI_ChangeState(uint32_t Value) break; default: - EmuWarning("Ohci: Unknown USB state mode!"); + EmuWarning("Ohci: Unknown USB mode!"); } } @@ -410,15 +413,15 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value) break; case 20: // HcRhStatus - // TODO + OHCI_SetHubStatus(Value); break; case 21: // RhPort 0 - // TODO + OHCI_PortSetStatus(0, Value); break; case 22: // RhPort 1 - // TODO + OHCI_PortSetStatus(1, Value); break; default: @@ -430,9 +433,9 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value) void OHCI::OHCI_UpdateInterrupt() { if ((Registers.HcInterrupt & OHCI_INTR_MIE) && (Registers.HcInterruptStatus & Registers.HcInterrupt)) { - HalSystemInterrupts[Irq_n].Assert(true); + HalSystemInterrupts[IrqNum].Assert(true); } - else { HalSystemInterrupts[Irq_n].Assert(false); } + else { HalSystemInterrupts[IrqNum].Assert(false); } } void OHCI::OHCI_SetInterrupt(uint32_t Value) @@ -458,6 +461,133 @@ void OHCI::OHCI_StopEndpoints() } } +void OHCI::OHCI_SetHubStatus(uint32_t Value) +{ + uint32_t old_state; + + old_state = Registers.HcRhStatus; + + // write 1 to clear OCIC + if (Value & OHCI_RHS_OCIC) { + Registers.HcRhStatus &= ~OHCI_RHS_OCIC; + } + + if (Value & OHCI_RHS_LPS) { + int i; + + for (i = 0; i < 2; i++) { + OHCI_PortPower(i, 0); + } + DbgPrintf("Ohci: powered down all ports\n"); + } + + if (Value & OHCI_RHS_LPSC) { + int i; + + for (i = 0; i < 2; i++) { + OHCI_PortPower(i, 1); + } + DbgPrintf("Ohci: powered up all ports\n"); + } + + if (Value & OHCI_RHS_DRWE) { + Registers.HcRhStatus |= OHCI_RHS_DRWE; + } + + if (Value & OHCI_RHS_CRWE) { + Registers.HcRhStatus &= ~OHCI_RHS_DRWE; + } + + if (old_state != Registers.HcRhStatus) { + OHCI_SetInterrupt(OHCI_INTR_RHSC); + } +} + +void OHCI::OHCI_PortPower(int i, int p) +{ + if (p) { + Registers.RhPort[i].HcRhPortStatus |= OHCI_PORT_PPS; + } + else { + Registers.RhPort[i].HcRhPortStatus &= ~(OHCI_PORT_PPS | + OHCI_PORT_CCS | + OHCI_PORT_PSS | + OHCI_PORT_PRS); + } +} + +void OHCI::OHCI_PortSetStatus(int PortNum, uint32_t Value) +{ + uint32_t old_state; + OHCIPort* port; + + port = &Registers.RhPort[PortNum]; + old_state = port->HcRhPortStatus; + + // Write to clear CSC, PESC, PSSC, OCIC, PRSC + if (Value & OHCI_PORT_WTC) { + port->HcRhPortStatus &= ~(Value & OHCI_PORT_WTC); + } + + if (Value & OHCI_PORT_CCS) { + port->HcRhPortStatus &= ~OHCI_PORT_PES; + } + + OHCI_PortSetIfConnected(PortNum, Value & OHCI_PORT_PES); + + if (OHCI_PortSetIfConnected(PortNum, Value & OHCI_PORT_PSS)) { + DbgPrintf("Ohci: port %d: SUSPEND\n", PortNum); + } + + if (OHCI_PortSetIfConnected(PortNum, Value & OHCI_PORT_PRS)) { + DbgPrintf("Ohci: port %d: RESET\n", PortNum); + USB_DeviceReset(port->UsbPort.Dev); + port->HcRhPortStatus &= ~OHCI_PORT_PRS; + // ??? Should this also set OHCI_PORT_PESC + port->HcRhPortStatus |= OHCI_PORT_PES | OHCI_PORT_PRSC; + } + + // Invert order here to ensure in ambiguous case, device is powered up... + if (Value & OHCI_PORT_LSDA) { + OHCI_PortPower(PortNum, 0); + } + + if (Value & OHCI_PORT_PPS) { + OHCI_PortPower(PortNum, 1); + } + + if (old_state != port->HcRhPortStatus) { + OHCI_SetInterrupt(OHCI_INTR_RHSC); + } +} + +int OHCI::OHCI_PortSetIfConnected(int i, uint32_t Value) +{ + int ret = 1; + + // writing a 0 has no effect + if (Value == 0) { + return 0; + } + + // If CurrentConnectStatus is cleared we set ConnectStatusChange + if (!(Registers.RhPort[i].HcRhPortStatus & OHCI_PORT_CCS)) { + Registers.RhPort[i].HcRhPortStatus |= OHCI_PORT_CSC; + if (Registers.HcRhStatus & OHCI_RHS_DRWE) { + // TODO: CSC is a wakeup event + } + return 0; + } + + if (Registers.RhPort[i].HcRhPortStatus & Value) + ret = 0; + + // set the bit + Registers.RhPort[i].HcRhPortStatus |= Value; + + return ret; +} + void OHCI::OHCI_Detach(USBPort* Port) { OHCIPort* port = &Registers.RhPort[Port->PortIndex]; @@ -483,6 +613,34 @@ void OHCI::OHCI_Detach(USBPort* Port) } } +void OHCI::OHCI_Attach(USBPort* Port) +{ + OHCIPort* port = &Registers.RhPort[Port->PortIndex]; + uint32_t old_state = port->HcRhPortStatus; + + // set connect status + port->HcRhPortStatus |= OHCI_PORT_CCS | OHCI_PORT_CSC; + + // update speed + if (port->UsbPort.Dev->speed == USB_SPEED_LOW) { + port->HcRhPortStatus |= OHCI_PORT_LSDA; + } + else { + port->HcRhPortStatus &= ~OHCI_PORT_LSDA; + } + + // notify of remote-wakeup + if ((Registers.HcControl & OHCI_CTL_HCFS) == Suspend) { + OHCI_SetInterrupt(OHCI_INTR_RD); + } + + DbgPrintf("Ohci: Attached 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; @@ -506,8 +664,8 @@ void OHCI::USB_PortReset(USBPort* Port) assert(dev != nullptr); USB_Detach(Port); - usb_attach(port); - usb_device_reset(dev); + USB_Attach(Port); + USB_DeviceReset(dev); } void OHCI::USB_Detach(USBPort* Port) @@ -518,4 +676,28 @@ void OHCI::USB_Detach(USBPort* Port) assert(dev->State != USB_STATE_NOTATTACHED); OHCI_Detach(Port); dev->State = USB_STATE_NOTATTACHED; -} \ No newline at end of file +} + +void OHCI::USB_Attach(USBPort* Port) +{ + USBDev *dev = Port->Dev; + + assert(dev != nullptr); + assert(dev->Attached); + assert(dev->State == USB_STATE_NOTATTACHED); + OHCI_Attach(Port); + dev->State = USB_STATE_ATTACHED; + usb_device_handle_attach(dev); +} + +void OHCI::USB_DeviceReset(USBDev* dev) +{ + if (dev == nullptr || !dev->Attached) { + return; + } + + dev->RemoteWakeup = 0; + dev->Addr = 0; + dev->State = USB_STATE_DEFAULT; + usb_device_handle_reset(dev); +} diff --git a/src/devices/USBController/OHCI.h b/src/devices/USBController/OHCI.h index b0cea7eee..20ec3458e 100644 --- a/src/devices/USBController/OHCI.h +++ b/src/devices/USBController/OHCI.h @@ -91,6 +91,13 @@ #define OHCI_RHA_DT (1<<10) // DeviceType #define OHCI_RHA_OCPM (1<<11) // OverCurrentProtectionMode #define OHCI_RHA_NOCP (1<<12) // NoOverCurrentProtection +// HcRhStatus +#define OHCI_RHS_LPS (1<<0) // LocalPowerStatus +#define OHCI_RHS_OCI (1<<1) // OverCurrentIndicator +#define OHCI_RHS_DRWE (1<<15) // DeviceRemoteWakeupEnable +#define OHCI_RHS_LPSC (1<<16) // LocalPowerStatusChange +#define OHCI_RHS_OCIC (1<<17) // OverCurrentIndicatorChange +#define OHCI_RHS_CRWE (1<<31) // ClearRemoteWakeupEnable // HcRhPortStatus #define OHCI_PORT_CCS (1<<0) // CurrentConnectStatus #define OHCI_PORT_PES (1<<1) // PortEnableStatus @@ -190,7 +197,7 @@ class OHCI // usb packet USBPacket UsbPacket; // irq number - int Irq_n; + int IrqNum; // EOF callback wrapper static void OHCI_FrameBoundaryWrapper(void* pVoid); @@ -214,8 +221,19 @@ class OHCI void OHCI_SetInterrupt(uint32_t Value); // void OHCI_StopEndpoints(); + // update ohci registers during a device attach + void OHCI_Attach(USBPort* Port); // update ohci registers during a device detach void OHCI_Detach(USBPort* Port); + // set root hub status + void OHCI_SetHubStatus(uint32_t Value); + // update power related bits in HcRhPortStatus + void OHCI_PortPower(int i, int p); + // set root hub port status + void OHCI_PortSetStatus(int PortNum, uint32_t Value); + // set a flag in a port status register but only set it if the port is connected, + // if not set ConnectStatusChange flag; if flag is enabled return 1 + int OHCI_PortSetIfConnected(int i, uint32_t Value); // register a port with the HC void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask); @@ -224,7 +242,7 @@ class OHCI // reset a usb port void USB_PortReset(USBPort* Port); // a device is attched - void Attach(USBPort* port); + void USB_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 @@ -233,6 +251,8 @@ class OHCI void Wakeup(USBPort* port); // TODO void Complete(USBPort* port, USBPacket *p); + // reset a device + void USB_DeviceReset(USBDev* dev); }; #endif diff --git a/src/devices/USBController/USBDevice.h b/src/devices/USBController/USBDevice.h index 0759b718c..0ae8fedac 100644 --- a/src/devices/USBController/USBDevice.h +++ b/src/devices/USBController/USBDevice.h @@ -44,6 +44,7 @@ #define USB_STATE_NOTATTACHED 0 #define USB_STATE_ATTACHED 1 +#define USB_STATE_DEFAULT 2 typedef enum USBPacketState { USB_PACKET_UNDEFINED = 0, @@ -100,7 +101,7 @@ typedef struct _USBDev int speed; // Supported speeds, not in info because it may be variable (hostdevs) int speedmask; - uint8_t addr; + uint8_t Addr; // device address char product_desc[32]; int auto_attach; int Attached; // device is attached @@ -108,7 +109,7 @@ typedef struct _USBDev int32_t State; // current state of device uint8_t setup_buf[8]; uint8_t data_buf[4096]; - int32_t remote_wakeup; + int32_t RemoteWakeup; // wakeup flag int32_t setup_state; int32_t setup_len; int32_t setup_index;