HCCA stuff (WIP)
This commit is contained in:
parent
e79c7fa185
commit
716e20fbc4
|
@ -72,6 +72,120 @@ void OHCI::OHCI_FrameBoundaryWrapper(void* pVoid)
|
|||
static_cast<OHCI*>(pVoid)->OHCI_FrameBoundaryWorker();
|
||||
}
|
||||
|
||||
void OHCI::OHCI_FrameBoundaryWorker()
|
||||
{
|
||||
OHCI_HCCA hcca;
|
||||
|
||||
if (OHCI_ReadHCCA(m_Registers.HcHCCA, &hcca)) {
|
||||
EmuWarning("Ohci: HCCA read error at physical address 0x%X", m_Registers.HcHCCA);
|
||||
OHCI_FatalError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Process all the lists at the end of the frame
|
||||
if (m_Registers.HcControl & OHCI_CTL_PLE) {
|
||||
int n = m_Registers.HcFmNumber & 0x1f;
|
||||
ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
|
||||
}
|
||||
|
||||
// Cancel all pending packets if either of the lists has been disabled
|
||||
if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
|
||||
if (ohci->async_td) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
}
|
||||
OHCI_StopEndpoints();
|
||||
}
|
||||
ohci->old_ctl = ohci->ctl;
|
||||
ohci_process_lists(ohci, 0);
|
||||
|
||||
// Stop if UnrecoverableError happened or OHCI_SOF will crash
|
||||
if (m_Registers.HcInterruptStatus & OHCI_INTR_UE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// From the standard: "This bit is loaded from the FrameIntervalToggle field of
|
||||
// HcFmInterval whenever FrameRemaining reaches 0."
|
||||
m_Registers.HcFmRemaining = (m_Registers.HcFmRemaining & ~OHCI_FMR_FRT) | (m_Registers.HcFmInterval & OHCI_FMI_FIT);
|
||||
|
||||
// Increment frame number
|
||||
m_Registers.HcFmNumber = (m_Registers.HcFmNumber + 1) & 0xFFFF; // prevent overflow
|
||||
hcca.HccaFrameNumber = m_Registers.HcFmNumber; // dropped big -> little endian conversion from XQEMU
|
||||
|
||||
if (m_DoneCount == 0 && !(m_Registers.HcInterruptStatus & OHCI_INTR_WD)) {
|
||||
if (!m_Registers.HcDoneHead) {
|
||||
// From the standard: "This is set to zero whenever HC writes the content of this
|
||||
// register to HCCA. It also sets the WritebackDoneHead of HcInterruptStatus."
|
||||
CxbxKrnlCleanup("Ohci: HcDoneHead is zero but WritebackDoneHead interrupt is not set!\n");
|
||||
}
|
||||
|
||||
if (m_Registers.HcInterrupt & m_Registers.HcInterruptStatus) {
|
||||
// From the standard: "The least significant bit of this entry is set to 1 to indicate whether an
|
||||
// unmasked HcInterruptStatus was set when HccaDoneHead was written." It's tecnically incorrect to
|
||||
// do this to HcDoneHead instead of HccaDoneHead however it doesn't matter since HcDoneHead is
|
||||
// zeroed below
|
||||
m_Registers.HcDoneHead |= 1;
|
||||
}
|
||||
|
||||
hcca.HccaDoneHead = m_Registers.HcDoneHead; // dropped big -> little endian conversion from XQEMU
|
||||
m_Registers.HcDoneHead = 0;
|
||||
m_DoneCount = 7;
|
||||
OHCI_SetInterrupt(OHCI_INTR_WD);
|
||||
}
|
||||
|
||||
if (m_DoneCount != 7 && m_DoneCount != 0) {
|
||||
// decrease DelayInterrupt counter
|
||||
m_DoneCount--;
|
||||
}
|
||||
|
||||
// Do SOF stuff here
|
||||
OHCI_SOF();
|
||||
|
||||
// Writeback HCCA
|
||||
if (OHCI_WriteHCCA(m_Registers.HcHCCA, &hcca)) {
|
||||
EmuWarning("Ohci: HCCA write error at physical address 0x%X", m_Registers.HcHCCA);
|
||||
OHCI_FatalError();
|
||||
}
|
||||
}
|
||||
|
||||
void OHCI::OHCI_FatalError()
|
||||
{
|
||||
// According to the standard, an OHCI will stop operating, and set itself into error state
|
||||
// (which can be queried by MMIO). Instead of calling directly CxbxKrnlCleanup, we let the
|
||||
// HCD know the problem so it can try to solve it
|
||||
|
||||
OHCI_SetInterrupt(OHCI_INTR_UE);
|
||||
OHCI_BusStop();
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_ReadHCCA(uint32_t Paddr, OHCI_HCCA* Hcca)
|
||||
{
|
||||
// ergo720: I disassembled various xbe's of my games and discovered that the shared memory between
|
||||
// HCD and HC is allocated with MmAllocateContiguousMemory which means we can access it from
|
||||
// the contiguous region. Hopefully XDK revisions didn't alter this...
|
||||
|
||||
if (Paddr != xbnull) {
|
||||
std::memcpy(Hcca, reinterpret_cast<void*>(Paddr + CONTIGUOUS_MEMORY_BASE), sizeof(OHCI_HCCA));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // error
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_WriteHCCA(uint32_t Paddr, OHCI_HCCA* Hcca)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
// We need to calculate the offset of the HccaFrameNumber member to avoid overwriting HccaInterrruptTable
|
||||
size_t OffsetoOfFrameNumber = offsetof(OHCI_HCCA, HccaFrameNumber);
|
||||
|
||||
std::memcpy(reinterpret_cast<void*>(Paddr + OffsetoOfFrameNumber + CONTIGUOUS_MEMORY_BASE),
|
||||
reinterpret_cast<uint8_t*>(Hcca) + OffsetoOfFrameNumber, 8);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // error
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -114,6 +228,7 @@ void OHCI::OHCI_StateReset()
|
|||
USB_PortReset(&Port->UsbPort);
|
||||
}
|
||||
}
|
||||
m_DoneCount = 7;
|
||||
|
||||
OHCI_StopEndpoints();
|
||||
|
||||
|
@ -122,7 +237,7 @@ void OHCI::OHCI_StateReset()
|
|||
|
||||
void OHCI::OHCI_BusStart()
|
||||
{
|
||||
// Create the end-of-frame timer. Let's try a factor of 50 (1 virtual ms -> 50 real ms)
|
||||
// Create the EOF timer. Let's try a factor of 50 (1 virtual ms -> 50 real ms)
|
||||
m_pEOFtimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, 50);
|
||||
|
||||
DbgPrintf("Ohci: Operational mode event\n");
|
||||
|
|
|
@ -46,9 +46,12 @@
|
|||
// OHCI: Open Host Controller Interface; the standard used on the xbox to comunicate with the usb devices
|
||||
// HC: Host Controller; the hardware which interfaces with the usb device and the usb driver
|
||||
// HCD: Host Controller Driver; software which talks to the HC, it's linked in the xbe
|
||||
// SOF: start of frame; the beginning of a USB-defined frame
|
||||
// EOF: end of frame; the end of a USB-defined frame
|
||||
// TD: transfer descriptor; a memory structure used by the HC to transfer a block of data to/from a device endpoint
|
||||
|
||||
|
||||
// These macros are used to access the bits in the various registers
|
||||
// These macros are used to access the bits of the various registers
|
||||
// HcControl
|
||||
#define OHCI_CTL_CBSR ((1<<0)|(1<<1)) // ControlBulkServiceRatio
|
||||
#define OHCI_CTL_PLE (1<<2) // PeriodicListEnable
|
||||
|
@ -66,15 +69,14 @@
|
|||
#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
|
||||
#define OHCI_INTR_SO (1<<0) // SchedulingOverrun
|
||||
#define OHCI_INTR_WD (1<<1) // WritebackDoneHead
|
||||
#define OHCI_INTR_SF (1<<2) // StartofFrame
|
||||
#define OHCI_INTR_RD (1<<3) // ResumeDetected
|
||||
#define OHCI_INTR_UE (1<<4) // UnrecoverableError
|
||||
#define OHCI_INTR_FNO (1<<5) // FrameNumberOverflow
|
||||
#define OHCI_INTR_RHSC (1<<6) // RootHubStatusChange
|
||||
#define OHCI_INTR_OC (1<<30) // OwnershipChange
|
||||
// HcInterruptEnable, HcInterruptDisable
|
||||
#define OHCI_INTR_MIE (1<<31) // MasterInterruptEnable
|
||||
// HcHCCA
|
||||
|
@ -84,6 +86,9 @@
|
|||
// HcFmInterval
|
||||
#define OHCI_FMI_FI 0x00003FFF // FrameInterval
|
||||
#define OHCI_FMI_FIT 0x80000000 // FrameIntervalToggle
|
||||
// HcFmRemaining
|
||||
#define OHCI_FMR_FR 0x00003FFF // FrameRemaining
|
||||
#define OHCI_FMR_FRT 0x80000000 // FrameRemainingToggle
|
||||
// HcRhDescriptorA
|
||||
#define OHCI_RHA_RW_MASK 0x00000000 // Mask of supported features
|
||||
#define OHCI_RHA_PSM (1<<8) // PowerSwitchingMode
|
||||
|
@ -124,6 +129,15 @@ typedef enum _OHCI_State
|
|||
}
|
||||
OHCI_State;
|
||||
|
||||
// Host Controller Communications Area
|
||||
typedef struct _OHCI_HCCA
|
||||
{
|
||||
uint32_t HccaInterrruptTable[32];
|
||||
uint16_t HccaFrameNumber, HccaPad1;
|
||||
uint32_t HccaDoneHead;
|
||||
}
|
||||
OHCI_HCCA;
|
||||
|
||||
// Small struct used to hold the HcRhPortStatus register and the usb port status
|
||||
typedef struct _OHCIPort
|
||||
{
|
||||
|
@ -140,7 +154,8 @@ typedef struct _OHCI_Registers
|
|||
uint32_t HcControl;
|
||||
uint32_t HcCommandStatus;
|
||||
uint32_t HcInterruptStatus;
|
||||
uint32_t HcInterrupt; // HcInterruptEnable/Disable are the same so we can merge them together
|
||||
// HcInterruptEnable/Disable are the same so we can merge them together
|
||||
uint32_t HcInterrupt;
|
||||
|
||||
// Memory Pointer partition
|
||||
uint32_t HcHCCA;
|
||||
|
@ -162,7 +177,7 @@ 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.
|
||||
// ergo720: 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
|
||||
}
|
||||
|
@ -196,13 +211,20 @@ class OHCI
|
|||
uint64_t m_TicksPerUsbTick;
|
||||
// usb packet
|
||||
USBPacket m_UsbPacket;
|
||||
// ergo720: I believe it's the value of HcControl in the last frame
|
||||
uint32_t old_ctl;
|
||||
// irq number
|
||||
int m_IrqNum;
|
||||
// ergo720: I think it's the DelayInterrupt flag in a TD
|
||||
// -> num of frames to wait before generating an interrupt for this TD
|
||||
int m_DoneCount;
|
||||
|
||||
// EOF callback wrapper
|
||||
static void OHCI_FrameBoundaryWrapper(void* pVoid);
|
||||
// EOF callback function
|
||||
void OHCI_FrameBoundaryWorker();
|
||||
// inform the HCD that we got a problem here...
|
||||
void OHCI_FatalError();
|
||||
// initialize packet struct
|
||||
void OHCI_PacketInit(USBPacket* packet);
|
||||
// change usb state mode
|
||||
|
@ -234,6 +256,10 @@ class OHCI
|
|||
// 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);
|
||||
// read the HCCA structure in memory
|
||||
bool OHCI_ReadHCCA(uint32_t Paddr, OHCI_HCCA* Hcca);
|
||||
// write the HCCA structure in memory
|
||||
bool OHCI_WriteHCCA(uint32_t Paddr, OHCI_HCCA* Hcca);
|
||||
|
||||
// register a port with the HC
|
||||
void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask);
|
||||
|
|
|
@ -65,7 +65,7 @@ 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 >= USB0_BASE) {
|
||||
if (addr < USB1_BASE) {
|
||||
// USB0 queried
|
||||
return m_pHostController1->OHCI_ReadRegister(addr);
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ 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 >= USB0_BASE) {
|
||||
if (addr < USB1_BASE) {
|
||||
// USB0 queried
|
||||
m_pHostController1->OHCI_WriteRegister(addr, value);
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue