|
|
|
@ -123,7 +123,7 @@ namespace xboxkrnl
|
|
|
|
|
#define OHCI_ED_EN_MASK (0xF<<OHCI_ED_EN_SHIFT) // EndpointNumber
|
|
|
|
|
#define OHCI_ED_D_SHIFT 11
|
|
|
|
|
#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT) // Direction
|
|
|
|
|
#define OHCI_ED_S (1<<13)
|
|
|
|
|
#define OHCI_ED_S (1<<13) // Speed
|
|
|
|
|
#define OHCI_ED_K (1<<14) // sKip
|
|
|
|
|
#define OHCI_ED_F (1<<15) // Format
|
|
|
|
|
#define OHCI_ED_MPS_SHIFT 16
|
|
|
|
@ -131,7 +131,7 @@ namespace xboxkrnl
|
|
|
|
|
|
|
|
|
|
/* Flags in the HeadP field of an ED */
|
|
|
|
|
#define OHCI_ED_H 1 // Halted
|
|
|
|
|
#define OHCI_ED_C 2
|
|
|
|
|
#define OHCI_ED_C 2 // toggleCarry
|
|
|
|
|
|
|
|
|
|
/* Bitfields for the first word of a TD */
|
|
|
|
|
#define OHCI_TD_R (1<<18) // bufferRounding
|
|
|
|
@ -142,7 +142,7 @@ namespace xboxkrnl
|
|
|
|
|
#define OHCI_TD_T0 (1<<24)
|
|
|
|
|
#define OHCI_TD_T1 (1<<25) // DataToggle (T0 and T1)
|
|
|
|
|
#define OHCI_TD_EC_SHIFT 26
|
|
|
|
|
#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
|
|
|
|
|
#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT) // ErrorCount
|
|
|
|
|
#define OHCI_TD_CC_SHIFT 28
|
|
|
|
|
#define OHCI_TD_CC_MASK (0xF<<OHCI_TD_CC_SHIFT) // ConditionCode
|
|
|
|
|
|
|
|
|
@ -195,6 +195,16 @@ namespace xboxkrnl
|
|
|
|
|
|
|
|
|
|
#define OHCI_PAGE_MASK 0xFFFFF000
|
|
|
|
|
#define OHCI_OFFSET_MASK 0xFFF
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Temporary output (useful for debugging LLE USB)
|
|
|
|
|
#define DEBUG
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
|
|
#define TestOut printf
|
|
|
|
|
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OHCI::OHCI(int Irq, USBDevice* UsbObj)
|
|
|
|
@ -225,7 +235,7 @@ OHCI::OHCI(int Irq, USBDevice* UsbObj)
|
|
|
|
|
}
|
|
|
|
|
OHCI_PacketInit(&m_UsbPacket);
|
|
|
|
|
|
|
|
|
|
m_UsbFrameTime = 1000000ULL; // 1 ms
|
|
|
|
|
m_UsbFrameTime = 1000000ULL; // 1 ms expressed in ns
|
|
|
|
|
m_TicksPerUsbTick = 1000000000ULL / USB_HZ; // 83
|
|
|
|
|
|
|
|
|
|
// Do a hardware reset
|
|
|
|
@ -241,8 +251,11 @@ void OHCI::OHCI_FrameBoundaryWorker()
|
|
|
|
|
{
|
|
|
|
|
OHCI_HCCA hcca;
|
|
|
|
|
|
|
|
|
|
while (m_bFrameTime) {}
|
|
|
|
|
m_bFrameTime = true;
|
|
|
|
|
while (m_bFrameTime) { Sleep(1); }
|
|
|
|
|
m_bFrameTime = true;
|
|
|
|
|
|
|
|
|
|
TestOut("Frame processing started\n");
|
|
|
|
|
TestOut("HcControl: 0x%X, HcFmNumber: 0x%X, HcInterruptStatus: 0x%X\n", m_Registers.HcControl, m_Registers.HcFmNumber, m_Registers.HcInterruptStatus);
|
|
|
|
|
|
|
|
|
|
if (OHCI_ReadHCCA(m_Registers.HcHCCA, &hcca)) {
|
|
|
|
|
EmuWarning("%s: HCCA read error at physical address 0x%X", m_Registers.HcHCCA, LOG_STR_OHCI);
|
|
|
|
@ -275,10 +288,13 @@ void OHCI::OHCI_FrameBoundaryWorker()
|
|
|
|
|
m_bFrameTime = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TestOut("HcFmRemaining: 0x%X, HcFmInterval: 0x%X - before\n", m_Registers.HcFmRemaining, m_Registers.HcFmInterval);
|
|
|
|
|
// 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);
|
|
|
|
|
m_Registers.HcFmRemaining = (m_Registers.HcFmInterval & OHCI_FMI_FIT) == 0 ?
|
|
|
|
|
m_Registers.HcFmRemaining & ~OHCI_FMR_FRT : m_Registers.HcFmRemaining | OHCI_FMR_FRT;
|
|
|
|
|
TestOut("HcFmRemaining: 0x%X, HcFmInterval: 0x%X - after\n", m_Registers.HcFmRemaining, m_Registers.HcFmInterval);
|
|
|
|
|
|
|
|
|
|
// Increment frame number
|
|
|
|
|
m_Registers.HcFmNumber = (m_Registers.HcFmNumber + 1) & 0xFFFF; // prevent overflow
|
|
|
|
@ -318,6 +334,10 @@ void OHCI::OHCI_FrameBoundaryWorker()
|
|
|
|
|
EmuWarning("%s: HCCA write error at physical address 0x%X", LOG_STR_OHCI, m_Registers.HcHCCA);
|
|
|
|
|
OHCI_FatalError();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestOut("Frame processing ended\n");
|
|
|
|
|
TestOut("HcControl: 0x%X, HcFmNumber: 0x%X, HcInterruptStatus: 0x%X\n", m_Registers.HcControl, m_Registers.HcFmNumber, m_Registers.HcInterruptStatus);
|
|
|
|
|
|
|
|
|
|
m_bFrameTime = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -325,11 +345,12 @@ 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
|
|
|
|
|
// HCD know the problem so that it can try to solve it
|
|
|
|
|
|
|
|
|
|
OHCI_SetInterrupt(OHCI_INTR_UE);
|
|
|
|
|
OHCI_BusStop();
|
|
|
|
|
DbgPrintf("%s: an unrecoverable error occoured!\n", LOG_STR_OHCI);
|
|
|
|
|
DbgPrintf("%s: an unrecoverable error occoured!\n", LOG_STR_OHCI);
|
|
|
|
|
TestOut("Fatal error");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool OHCI::OHCI_ReadHCCA(xbaddr Paddr, OHCI_HCCA* Hcca)
|
|
|
|
@ -469,7 +490,7 @@ bool OHCI::OHCI_CopyIsoTD(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffe
|
|
|
|
|
if (n == Length) {
|
|
|
|
|
return false; // no bytes left to copy
|
|
|
|
|
}
|
|
|
|
|
ptr = end_addr & ~0xfffu;
|
|
|
|
|
ptr = end_addr & ~0xFFFu;
|
|
|
|
|
Buffer += n;
|
|
|
|
|
if (OHCI_FindAndCopyTD(ptr, Buffer, Length - n, bIsWrite)) {
|
|
|
|
|
return true; // error
|
|
|
|
@ -864,13 +885,8 @@ void OHCI::OHCI_StateReset()
|
|
|
|
|
m_OldHcControl = 0;
|
|
|
|
|
|
|
|
|
|
// Reset all registers
|
|
|
|
|
// Remark: the standard says that RemoteWakeupConnected bit should be set during POST, cleared during hw reset
|
|
|
|
|
// and ignored during a sw reset. However, VBox sets it on hw reset and XQEMU clears it. Considering that the Xbox
|
|
|
|
|
// doesn't do POST, I will clear it.
|
|
|
|
|
m_Registers.HcRevision = 0x10;
|
|
|
|
|
m_Registers.HcControl = 0;
|
|
|
|
|
m_Registers.HcControl &= ~OHCI_CTL_HCFS;
|
|
|
|
|
m_Registers.HcControl |= Reset;
|
|
|
|
|
m_Registers.HcCommandStatus = 0;
|
|
|
|
|
m_Registers.HcInterruptStatus = 0;
|
|
|
|
|
m_Registers.HcInterrupt = OHCI_INTR_MIE; // enable interrupts
|
|
|
|
@ -916,7 +932,8 @@ void OHCI::OHCI_StateReset()
|
|
|
|
|
void OHCI::OHCI_BusStart()
|
|
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
m_pEOFtimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, 100);
|
|
|
|
|
TestOut("Bus start.\n");
|
|
|
|
|
|
|
|
|
|
DbgPrintf("%s: Operational mode event\n", LOG_STR_OHCI);
|
|
|
|
|
|
|
|
|
@ -930,13 +947,16 @@ void OHCI::OHCI_BusStop()
|
|
|
|
|
// Delete existing EOF timer
|
|
|
|
|
Timer_Exit(m_pEOFtimer);
|
|
|
|
|
}
|
|
|
|
|
m_pEOFtimer = nullptr;
|
|
|
|
|
m_pEOFtimer = nullptr;
|
|
|
|
|
TestOut("Bus stop\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OHCI::OHCI_SOF(bool bCreate)
|
|
|
|
|
{
|
|
|
|
|
// set current SOF time
|
|
|
|
|
m_SOFtime = GetTime_NS(m_pEOFtimer);
|
|
|
|
|
m_SOFtime = GetTime_NS(m_pEOFtimer);
|
|
|
|
|
|
|
|
|
|
TestOut("OHCI_SOF -> m_SOFtime is 0x%X\n", m_SOFtime);
|
|
|
|
|
|
|
|
|
|
// make timer expire at SOF + 1 virtual ms from now
|
|
|
|
|
if (bCreate) {
|
|
|
|
@ -950,7 +970,9 @@ void OHCI::OHCI_ChangeState(uint32_t Value)
|
|
|
|
|
{
|
|
|
|
|
uint32_t OldState = m_Registers.HcControl & OHCI_CTL_HCFS;
|
|
|
|
|
m_Registers.HcControl = Value;
|
|
|
|
|
uint32_t NewState = m_Registers.HcControl & OHCI_CTL_HCFS;
|
|
|
|
|
uint32_t NewState = m_Registers.HcControl & OHCI_CTL_HCFS;
|
|
|
|
|
|
|
|
|
|
TestOut("OldState 0x%X\n", OldState);
|
|
|
|
|
|
|
|
|
|
// no state change
|
|
|
|
|
if (OldState == NewState) {
|
|
|
|
@ -960,20 +982,24 @@ void OHCI::OHCI_ChangeState(uint32_t Value)
|
|
|
|
|
switch (NewState)
|
|
|
|
|
{
|
|
|
|
|
case Operational:
|
|
|
|
|
OHCI_BusStart();
|
|
|
|
|
OHCI_BusStart();
|
|
|
|
|
TestOut("NewState Operational\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Suspend:
|
|
|
|
|
OHCI_BusStop();
|
|
|
|
|
DbgPrintf("%s: Suspend mode event\n", LOG_STR_OHCI);
|
|
|
|
|
DbgPrintf("%s: Suspend mode event\n", LOG_STR_OHCI);
|
|
|
|
|
TestOut("NewState Suspend\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Resume:
|
|
|
|
|
DbgPrintf("%s: Resume mode event\n", LOG_STR_OHCI);
|
|
|
|
|
DbgPrintf("%s: Resume mode event\n", LOG_STR_OHCI);
|
|
|
|
|
TestOut("NewState Resume\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Reset:
|
|
|
|
|
OHCI_StateReset();
|
|
|
|
|
OHCI_StateReset();
|
|
|
|
|
TestOut("NewState Reset\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
@ -1003,101 +1029,125 @@ uint32_t OHCI::OHCI_ReadRegister(xbaddr Addr)
|
|
|
|
|
switch (Addr >> 2) // read the register
|
|
|
|
|
{
|
|
|
|
|
case 0: // HcRevision
|
|
|
|
|
ret = m_Registers.HcRevision;
|
|
|
|
|
ret = m_Registers.HcRevision;
|
|
|
|
|
TestOut("m_Registers.HcRevision: 0x%X\n", m_Registers.HcRevision);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1: // HcControl
|
|
|
|
|
ret = m_Registers.HcControl;
|
|
|
|
|
ret = m_Registers.HcControl;
|
|
|
|
|
TestOut("m_Registers.HcControl: 0x%X\n", m_Registers.HcControl);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2: // HcCommandStatus
|
|
|
|
|
ret = m_Registers.HcCommandStatus;
|
|
|
|
|
ret = m_Registers.HcCommandStatus;
|
|
|
|
|
TestOut("m_Registers.HcCommandStatus: 0x%X\n", m_Registers.HcCommandStatus);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3: // HcInterruptStatus
|
|
|
|
|
ret = m_Registers.HcInterruptStatus;
|
|
|
|
|
ret = m_Registers.HcInterruptStatus;
|
|
|
|
|
TestOut("m_Registers.HcInterruptStatus: 0x%X\n", m_Registers.HcInterruptStatus);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 4: // HcInterruptEnable
|
|
|
|
|
case 5: // HcInterruptDisable
|
|
|
|
|
ret = m_Registers.HcInterrupt;
|
|
|
|
|
ret = m_Registers.HcInterrupt;
|
|
|
|
|
TestOut("m_Registers.HcInterrupt: 0x%X\n", m_Registers.HcInterrupt);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6: // HcHCCA
|
|
|
|
|
ret = m_Registers.HcHCCA;
|
|
|
|
|
ret = m_Registers.HcHCCA;
|
|
|
|
|
TestOut("m_Registers.HcHCCA: 0x%X\n", m_Registers.HcHCCA);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 7: // HcPeriodCurrentED
|
|
|
|
|
ret = m_Registers.HcPeriodCurrentED;
|
|
|
|
|
ret = m_Registers.HcPeriodCurrentED;
|
|
|
|
|
TestOut("m_Registers.HcPeriodCurrentED: 0x%X\n", m_Registers.HcPeriodCurrentED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 8: // HcControlHeadED
|
|
|
|
|
ret = m_Registers.HcControlHeadED;
|
|
|
|
|
ret = m_Registers.HcControlHeadED;
|
|
|
|
|
TestOut("m_Registers.HcControlHeadED: 0x%X\n", m_Registers.HcControlHeadED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 9: // HcControlCurrentED
|
|
|
|
|
ret = m_Registers.HcControlCurrentED;
|
|
|
|
|
ret = m_Registers.HcControlCurrentED;
|
|
|
|
|
TestOut("m_Registers.HcControlCurrentED: 0x%X\n", m_Registers.HcControlCurrentED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 10: // HcBulkHeadED
|
|
|
|
|
ret = m_Registers.HcBulkHeadED;
|
|
|
|
|
ret = m_Registers.HcBulkHeadED;
|
|
|
|
|
TestOut("m_Registers.HcBulkHeadED: 0x%X\n", m_Registers.HcBulkHeadED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 11: // HcBulkCurrentED
|
|
|
|
|
ret = m_Registers.HcBulkCurrentED;
|
|
|
|
|
ret = m_Registers.HcBulkCurrentED;
|
|
|
|
|
TestOut("m_Registers.HcBulkCurrentED: 0x%X\n", m_Registers.HcBulkCurrentED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 12: // HcDoneHead
|
|
|
|
|
ret = m_Registers.HcDoneHead;
|
|
|
|
|
ret = m_Registers.HcDoneHead;
|
|
|
|
|
TestOut("m_Registers.HcDoneHead: 0x%X\n", m_Registers.HcDoneHead);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 13: // HcFmInterval
|
|
|
|
|
ret = m_Registers.HcFmInterval;
|
|
|
|
|
ret = m_Registers.HcFmInterval;
|
|
|
|
|
TestOut("m_Registers.HcFmInterval: 0x%X\n", m_Registers.HcFmInterval);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 14: // HcFmRemaining
|
|
|
|
|
ret = OHCI_GetFrameRemaining();
|
|
|
|
|
ret = OHCI_GetFrameRemaining();
|
|
|
|
|
TestOut("m_Registers.HcFmRemaining: 0x%X\n", m_Registers.HcFmRemaining);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 15: // HcFmNumber
|
|
|
|
|
ret = m_Registers.HcFmNumber;
|
|
|
|
|
ret = m_Registers.HcFmNumber;
|
|
|
|
|
TestOut("m_Registers.HcFmNumber: 0x%X\n", m_Registers.HcFmNumber);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 16: // HcPeriodicStart
|
|
|
|
|
ret = m_Registers.HcPeriodicStart;
|
|
|
|
|
ret = m_Registers.HcPeriodicStart;
|
|
|
|
|
TestOut("m_Registers.HcPeriodicStart: 0x%X\n", m_Registers.HcPeriodicStart);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 17: // HcLSThreshold
|
|
|
|
|
ret = m_Registers.HcLSThreshold;
|
|
|
|
|
ret = m_Registers.HcLSThreshold;
|
|
|
|
|
TestOut("m_Registers.HcLSThreshold: 0x%X\n", m_Registers.HcLSThreshold);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 18: // HcRhDescriptorA
|
|
|
|
|
ret = m_Registers.HcRhDescriptorA;
|
|
|
|
|
ret = m_Registers.HcRhDescriptorA;
|
|
|
|
|
TestOut("m_Registers.HcRhDescriptorA: 0x%X\n", m_Registers.HcRhDescriptorA);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 19: // HcRhDescriptorB
|
|
|
|
|
ret = m_Registers.HcRhDescriptorB;
|
|
|
|
|
ret = m_Registers.HcRhDescriptorB;
|
|
|
|
|
TestOut("m_Registers.HcRhDescriptorB: 0x%X\n", m_Registers.HcRhDescriptorB);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 20: // HcRhStatus
|
|
|
|
|
ret = m_Registers.HcRhStatus;
|
|
|
|
|
ret = m_Registers.HcRhStatus;
|
|
|
|
|
TestOut("m_Registers.HcRhStatus: 0x%X\n", m_Registers.HcRhStatus);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Always report that the port power is on since the Xbox cannot switch off the electrical current to it
|
|
|
|
|
case 21: // RhPort 0
|
|
|
|
|
ret = m_Registers.RhPort[0].HcRhPortStatus | OHCI_PORT_PPS;
|
|
|
|
|
ret = m_Registers.RhPort[0].HcRhPortStatus | OHCI_PORT_PPS;
|
|
|
|
|
TestOut("m_Registers.RhPort[0].HcRhPortStatus: 0x%X\n", m_Registers.RhPort[0].HcRhPortStatus | OHCI_PORT_PPS);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 22: // RhPort 1
|
|
|
|
|
ret = m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS;
|
|
|
|
|
ret = m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS;
|
|
|
|
|
TestOut("m_Registers.RhPort[1].HcRhPortStatus: 0x%X\n", m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 23:
|
|
|
|
|
ret = 0;
|
|
|
|
|
ret = 0 | OHCI_PORT_PPS;
|
|
|
|
|
TestOut("m_Registers.RhPort[2].HcRhPortStatus: 0x%X\n", ret);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 24:
|
|
|
|
|
ret = 0;
|
|
|
|
|
ret = 0 | OHCI_PORT_PPS;
|
|
|
|
|
TestOut("m_Registers.RhPort[3].HcRhPortStatus: 0x%X\n", ret);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
@ -1118,16 +1168,20 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value)
|
|
|
|
|
switch (Addr >> 2)
|
|
|
|
|
{
|
|
|
|
|
case 0: // HcRevision
|
|
|
|
|
// This register is read-only
|
|
|
|
|
// This register is read-only
|
|
|
|
|
TestOut("W m_Registers.HcRevision: 0x%X\n", Value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1: // HcControl
|
|
|
|
|
OHCI_ChangeState(Value);
|
|
|
|
|
case 1: // HcControl
|
|
|
|
|
TestOut("W m_Registers.HcControl: 0x%X\n", Value);
|
|
|
|
|
OHCI_ChangeState(Value);
|
|
|
|
|
TestOut("W m_Registers.HcControl: 0x%X\n", m_Registers.HcControl);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2: // HcCommandStatus
|
|
|
|
|
{
|
|
|
|
|
// SOC is read-only
|
|
|
|
|
// SOC is read-only
|
|
|
|
|
TestOut("W m_Registers.HcCommandStatus: 0x%X\n", Value);
|
|
|
|
|
Value &= ~OHCI_STATUS_SOC;
|
|
|
|
|
|
|
|
|
|
// From the standard: "The Host Controller must ensure that bits written as 1 become set
|
|
|
|
@ -1137,104 +1191,142 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value)
|
|
|
|
|
if (m_Registers.HcCommandStatus & OHCI_STATUS_HCR) {
|
|
|
|
|
// Do a hardware reset
|
|
|
|
|
OHCI_StateReset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TestOut("W m_Registers.HcCommandStatus: 0x%X\n", m_Registers.HcCommandStatus);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3: // HcInterruptStatus
|
|
|
|
|
case 3: // HcInterruptStatus
|
|
|
|
|
TestOut("W m_Registers.HcInterruptStatus: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcInterruptStatus &= ~Value;
|
|
|
|
|
OHCI_UpdateInterrupt();
|
|
|
|
|
OHCI_UpdateInterrupt();
|
|
|
|
|
TestOut("W m_Registers.HcInterruptStatus: 0x%X\n", m_Registers.HcInterruptStatus);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 4: // HcInterruptEnable
|
|
|
|
|
case 4: // HcInterruptEnable
|
|
|
|
|
TestOut("W m_Registers.HcInterruptEnable: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcInterrupt |= Value;
|
|
|
|
|
OHCI_UpdateInterrupt();
|
|
|
|
|
OHCI_UpdateInterrupt();
|
|
|
|
|
TestOut("W m_Registers.HcInterruptEnable: 0x%X\n", m_Registers.HcInterrupt);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 5: // HcInterruptDisable
|
|
|
|
|
case 5: // HcInterruptDisable
|
|
|
|
|
TestOut("W m_Registers.HcInterruptDisable: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcInterrupt &= ~Value;
|
|
|
|
|
OHCI_UpdateInterrupt();
|
|
|
|
|
OHCI_UpdateInterrupt();
|
|
|
|
|
TestOut("W m_Registers.HcInterruptDisable: 0x%X\n", m_Registers.HcInterrupt);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6: // HcHCCA
|
|
|
|
|
// The standard says the minimum alignment is 256 bytes and so bits 0 through 7 are always zero
|
|
|
|
|
m_Registers.HcHCCA = Value & OHCI_HCCA_MASK;
|
|
|
|
|
// The standard says the minimum alignment is 256 bytes and so bits 0 through 7 are always zero
|
|
|
|
|
TestOut("W m_Registers.HcHCCA: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcHCCA = Value & OHCI_HCCA_MASK;
|
|
|
|
|
TestOut("W m_Registers.HcHCCA: 0x%X\n", m_Registers.HcHCCA);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 7: // HcPeriodCurrentED
|
|
|
|
|
// This register is read-only
|
|
|
|
|
// This register is read-only
|
|
|
|
|
TestOut("W m_Registers.HcPeriodCurrentED: 0x%X\n", Value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 8: // HcControlHeadED
|
|
|
|
|
m_Registers.HcControlHeadED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
case 8: // HcControlHeadED
|
|
|
|
|
TestOut("W m_Registers.HcControlHeadED: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcControlHeadED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
TestOut("W m_Registers.HcControlHeadED: 0x%X\n", m_Registers.HcControlHeadED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 9: // HcControlCurrentED
|
|
|
|
|
m_Registers.HcControlCurrentED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
case 9: // HcControlCurrentED
|
|
|
|
|
TestOut("W m_Registers.HcControlCurrentED: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcControlCurrentED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
TestOut("W m_Registers.HcControlCurrentED: 0x%X\n", m_Registers.HcControlCurrentED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 10: // HcBulkHeadED
|
|
|
|
|
m_Registers.HcBulkHeadED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
case 10: // HcBulkHeadED
|
|
|
|
|
TestOut("W m_Registers.HcBulkHeadED: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcBulkHeadED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
TestOut("W m_Registers.HcBulkHeadED: 0x%X\n", m_Registers.HcBulkHeadED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 11: // HcBulkCurrentED
|
|
|
|
|
m_Registers.HcBulkCurrentED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
case 11: // HcBulkCurrentED
|
|
|
|
|
TestOut("W m_Registers.HcBulkCurrentED: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcBulkCurrentED = Value & OHCI_DPTR_MASK;
|
|
|
|
|
TestOut("W m_Registers.HcBulkCurrentED: 0x%X\n", m_Registers.HcBulkCurrentED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 12: // HcDoneHead
|
|
|
|
|
case 12: // HcDoneHead
|
|
|
|
|
TestOut("W m_Registers.HcDoneHead: 0x%X\n", Value);
|
|
|
|
|
// This register is read-only
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 13: // HcFmInterval
|
|
|
|
|
{
|
|
|
|
|
if ((Value & OHCI_FMI_FIT) != (m_Registers.HcFmInterval & OHCI_FMI_FIT)) {
|
|
|
|
|
{
|
|
|
|
|
TestOut("W m_Registers.HcFmInterval: 0x%X\n", Value);
|
|
|
|
|
if ((Value & OHCI_FMI_FI) != (m_Registers.HcFmInterval & OHCI_FMI_FI)) {
|
|
|
|
|
DbgPrintf("%s: Changing frame interval duration. New value is %u\n", LOG_STR_OHCI, Value & OHCI_FMI_FI);
|
|
|
|
|
}
|
|
|
|
|
m_Registers.HcFmInterval = Value & ~0xC000;
|
|
|
|
|
m_Registers.HcFmInterval = Value & ~0xC000;
|
|
|
|
|
TestOut("W m_Registers.HcFmInterval: 0x%X\n", m_Registers.HcFmInterval);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 14: // HcFmRemaining
|
|
|
|
|
// This register is read-only
|
|
|
|
|
// This register is read-only
|
|
|
|
|
TestOut("W m_Registers.HcFmRemaining: 0x%X\n", Value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 15: // HcFmNumber
|
|
|
|
|
// This register is read-only
|
|
|
|
|
// This register is read-only
|
|
|
|
|
TestOut("W m_Registers.HcFmNumber: 0x%X\n", Value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 16: // HcPeriodicStart
|
|
|
|
|
m_Registers.HcPeriodicStart = Value & 0x3FFF;
|
|
|
|
|
case 16: // HcPeriodicStart
|
|
|
|
|
TestOut("W m_Registers.HcPeriodicStart: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcPeriodicStart = Value & 0x3FFF;
|
|
|
|
|
TestOut("W m_Registers.HcPeriodicStart: 0x%X\n", m_Registers.HcPeriodicStart);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 17: // HcLSThreshold
|
|
|
|
|
m_Registers.HcLSThreshold = Value & 0xFFF;
|
|
|
|
|
case 17: // HcLSThreshold
|
|
|
|
|
TestOut("W m_Registers.HcLSThreshold: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcLSThreshold = Value & 0xFFF;
|
|
|
|
|
TestOut("W m_Registers.HcLSThreshold: 0x%X\n", m_Registers.HcLSThreshold);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 18: // HcRhDescriptorA
|
|
|
|
|
case 18: // HcRhDescriptorA
|
|
|
|
|
TestOut("W m_Registers.HcRhDescriptorA: 0x%X\n", Value);
|
|
|
|
|
m_Registers.HcRhDescriptorA &= ~OHCI_RHA_RW_MASK;
|
|
|
|
|
m_Registers.HcRhDescriptorA |= Value & OHCI_RHA_RW_MASK; // ??
|
|
|
|
|
m_Registers.HcRhDescriptorA |= Value & OHCI_RHA_RW_MASK; // ??
|
|
|
|
|
TestOut("W m_Registers.HcRhDescriptorA: 0x%X\n", m_Registers.HcRhDescriptorA);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 19: // HcRhDescriptorB
|
|
|
|
|
case 19: // HcRhDescriptorB
|
|
|
|
|
TestOut("W m_Registers.HcRhDescriptorB: 0x%X\n", Value);
|
|
|
|
|
// Don't do anything, the attached devices are all removable and PowerSwitchingMode is always 0
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 20: // HcRhStatus
|
|
|
|
|
OHCI_SetHubStatus(Value);
|
|
|
|
|
case 20: // HcRhStatus
|
|
|
|
|
TestOut("W m_Registers.HcRhStatus: 0x%X\n", Value);
|
|
|
|
|
OHCI_SetHubStatus(Value);
|
|
|
|
|
TestOut("W m_Registers.HcRhStatus: 0x%X\n", m_Registers.HcRhStatus);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 21: // RhPort 0
|
|
|
|
|
OHCI_PortSetStatus(0, Value);
|
|
|
|
|
case 21: // RhPort 0
|
|
|
|
|
TestOut("W m_Registers.RhPort 0: 0x%X\n", Value);
|
|
|
|
|
OHCI_PortSetStatus(0, Value);
|
|
|
|
|
TestOut("W m_Registers.RhPort 0: 0x%X\n", m_Registers.RhPort[0].HcRhPortStatus);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 22: // RhPort 1
|
|
|
|
|
OHCI_PortSetStatus(1, Value);
|
|
|
|
|
case 22: // RhPort 1
|
|
|
|
|
TestOut("W m_Registers.RhPort 1: 0x%X\n", Value);
|
|
|
|
|
OHCI_PortSetStatus(1, Value);
|
|
|
|
|
TestOut("W m_Registers.RhPort 1: 0x%X\n", m_Registers.RhPort[1].HcRhPortStatus);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 23:
|
|
|
|
|
TestOut("W m_Registers.RhPort 2: 0x%X\n", Value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 24:
|
|
|
|
|
TestOut("W m_Registers.RhPort 3: 0x%X\n", Value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
@ -1245,10 +1337,11 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value)
|
|
|
|
|
|
|
|
|
|
void OHCI::OHCI_UpdateInterrupt()
|
|
|
|
|
{
|
|
|
|
|
if ((m_Registers.HcInterrupt & OHCI_INTR_MIE) && (m_Registers.HcInterruptStatus & m_Registers.HcInterrupt)) {
|
|
|
|
|
HalSystemInterrupts[m_IrqNum].Assert(true);
|
|
|
|
|
if ((m_Registers.HcInterrupt & OHCI_INTR_MIE) && (m_Registers.HcInterruptStatus & m_Registers.HcInterrupt)) {
|
|
|
|
|
HalSystemInterrupts[m_IrqNum].Assert(false);
|
|
|
|
|
HalSystemInterrupts[m_IrqNum].Assert(true);
|
|
|
|
|
TestOut("Fired interrupt -> m_Registers.HcInterruptStatus is 0x%X\n", m_Registers.HcInterruptStatus);
|
|
|
|
|
}
|
|
|
|
|
else { HalSystemInterrupts[m_IrqNum].Assert(false); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OHCI::OHCI_SetInterrupt(uint32_t Value)
|
|
|
|
@ -1428,7 +1521,9 @@ int OHCI::OHCI_PortSetIfConnected(int i, uint32_t Value)
|
|
|
|
|
void OHCI::OHCI_Detach(USBPort* Port)
|
|
|
|
|
{
|
|
|
|
|
OHCIPort* port = &m_Registers.RhPort[Port->PortIndex];
|
|
|
|
|
uint32_t old_state = port->HcRhPortStatus;
|
|
|
|
|
uint32_t old_state = port->HcRhPortStatus;
|
|
|
|
|
|
|
|
|
|
TestOut("OHCI_Detach\n");
|
|
|
|
|
|
|
|
|
|
OHCI_AsyncCancelDevice(Port->Dev);
|
|
|
|
|
|
|
|
|
@ -1454,7 +1549,9 @@ void OHCI::OHCI_Detach(USBPort* Port)
|
|
|
|
|
void OHCI::OHCI_Attach(USBPort* Port)
|
|
|
|
|
{
|
|
|
|
|
OHCIPort* port = &m_Registers.RhPort[Port->PortIndex];
|
|
|
|
|
uint32_t old_state = port->HcRhPortStatus;
|
|
|
|
|
uint32_t old_state = port->HcRhPortStatus;
|
|
|
|
|
|
|
|
|
|
TestOut("OHCI_Attach\n");
|
|
|
|
|
|
|
|
|
|
// set connect status
|
|
|
|
|
port->HcRhPortStatus |= OHCI_PORT_CCS | OHCI_PORT_CSC;
|
|
|
|
@ -1480,12 +1577,14 @@ void OHCI::OHCI_Attach(USBPort* Port)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OHCI::OHCI_ChildDetach(XboxDeviceState* child)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
TestOut("OHCI_ChildDetach\n");
|
|
|
|
|
OHCI_AsyncCancelDevice(child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OHCI::OHCI_Wakeup(USBPort* port1)
|
|
|
|
|
{
|
|
|
|
|
TestOut("OHCI_Wakeup\n");
|
|
|
|
|
OHCIPort* port = &m_Registers.RhPort[port1->PortIndex];
|
|
|
|
|
uint32_t intr = 0;
|
|
|
|
|
if (port->HcRhPortStatus & OHCI_PORT_PSS) {
|
|
|
|
@ -1522,7 +1621,7 @@ void OHCI::OHCI_AsyncCancelDevice(XboxDeviceState* dev)
|
|
|
|
|
m_UsbDevice->USB_IsPacketInflight(&m_UsbPacket) &&
|
|
|
|
|
m_UsbPacket.Endpoint->Dev == dev) {
|
|
|
|
|
m_UsbDevice->USB_CancelPacket(&m_UsbPacket);
|
|
|
|
|
m_AsyncTD = 0;
|
|
|
|
|
m_AsyncTD = xbnull;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|