commit
e463f473b9
|
@ -34,7 +34,7 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
// Acknowledgment: some the functions present are from XQEMU (GPLv2)
|
||||
// Acknowledgment: some of the functions present are from XQEMU (GPLv2)
|
||||
// https://xqemu.com/
|
||||
|
||||
// The intent of this file is to add general functions which are not kernel specific (for those CxbxKrnl.h should be used instead)
|
||||
|
@ -140,34 +140,90 @@ size_t IoVecFromBuffer(const IoVec* iov, unsigned int iov_cnt, size_t offset, vo
|
|||
}
|
||||
|
||||
// Read an array of DWORDs in memory
|
||||
void GetDwords(xbaddr Paddr, uint32_t* Buffer, int Number)
|
||||
bool GetDwords(xbaddr Paddr, uint32_t* Buffer, int Number)
|
||||
{
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
std::memcpy(Buffer, reinterpret_cast<void*>(Paddr), 4); // dropped little -> big endian conversion from XQEMU
|
||||
}
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
// dropped little -> big endian conversion from XQEMU
|
||||
if (Memory_R(reinterpret_cast<void*>(Paddr), Buffer, 4)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write an array of DWORDs in memory
|
||||
void WriteDwords(xbaddr Paddr, uint32_t* Buffer, int Number)
|
||||
bool WriteDwords(xbaddr Paddr, uint32_t* Buffer, int Number)
|
||||
{
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
std::memcpy(reinterpret_cast<void*>(Paddr), Buffer, 4); // dropped big -> little endian conversion from XQEMU
|
||||
}
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
// dropped big -> little endian conversion from XQEMU
|
||||
if (Memory_W(reinterpret_cast<void*>(Paddr), Buffer, 4)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read an array of WORDs in memory
|
||||
void GetWords(xbaddr Paddr, uint16_t* Buffer, int Number)
|
||||
bool GetWords(xbaddr Paddr, uint16_t* Buffer, int Number)
|
||||
{
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
std::memcpy(Buffer, reinterpret_cast<void*>(Paddr), 2); // dropped little -> big endian conversion from XQEMU
|
||||
}
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
// dropped little -> big endian conversion from XQEMU
|
||||
if (Memory_R(reinterpret_cast<void*>(Paddr), Buffer, 2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write an array of WORDs in memory
|
||||
void WriteWords(xbaddr Paddr, uint16_t* Buffer, int Number)
|
||||
bool WriteWords(xbaddr Paddr, uint16_t* Buffer, int Number)
|
||||
{
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
std::memcpy(reinterpret_cast<void*>(Paddr), Buffer, 2); // dropped big -> little endian conversion from XQEMU
|
||||
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
|
||||
// dropped big -> little endian conversion from XQEMU
|
||||
if (Memory_W(reinterpret_cast<void*>(Paddr), Buffer, 2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Memory_R(void* Addr, void* Buf, size_t Num)
|
||||
{
|
||||
bool Error = false;
|
||||
|
||||
if (Num != 0) {
|
||||
if (Addr == nullptr) {
|
||||
Error = true;
|
||||
}
|
||||
else {
|
||||
std::memcpy(Buf, Addr, Num);
|
||||
}
|
||||
}
|
||||
return Error;
|
||||
}
|
||||
|
||||
bool Memory_W(void* Addr, void* Buf, size_t Num)
|
||||
{
|
||||
bool Error = false;
|
||||
|
||||
if (Num != 0) {
|
||||
if (Addr == nullptr) {
|
||||
Error = true;
|
||||
}
|
||||
else {
|
||||
std::memcpy(Addr, Buf, Num);
|
||||
}
|
||||
}
|
||||
return Error;
|
||||
}
|
||||
|
||||
bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite)
|
||||
{
|
||||
if (bIsWrite) {
|
||||
return Memory_W(Addr, Buf, Num);
|
||||
}
|
||||
else {
|
||||
return Memory_R(Addr, Buf, Num);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,10 +63,13 @@ void IoVecAdd(IOVector* qiov, void* base, size_t len);
|
|||
size_t IoVecTobuffer(const IoVec* iov, const unsigned int iov_cnt, size_t offset, void *buf, size_t bytes);
|
||||
size_t IoVecFromBuffer(const IoVec* iov, unsigned int iov_cnt, size_t offset, void* buf, size_t bytes);
|
||||
|
||||
void WriteDwords(xbaddr Paddr, uint32_t* Buffer, int Number);
|
||||
void GetDwords(xbaddr Paddr, uint32_t* Buffer, int Number);
|
||||
void GetWords(xbaddr Paddr, uint16_t* Buffer, int Number);
|
||||
void WriteWords(xbaddr Paddr, uint16_t* Buffer, int Number);
|
||||
bool WriteDwords(xbaddr Paddr, uint32_t* Buffer, int Number);
|
||||
bool GetDwords(xbaddr Paddr, uint32_t* Buffer, int Number);
|
||||
bool GetWords(xbaddr Paddr, uint16_t* Buffer, int Number);
|
||||
bool WriteWords(xbaddr Paddr, uint16_t* Buffer, int Number);
|
||||
bool Memory_R(void* Addr, void* Buf, size_t Num);
|
||||
bool Memory_W(void* Addr, void* Buf, size_t Num);
|
||||
bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite);
|
||||
|
||||
void unix2dos(std::string& string);
|
||||
|
||||
|
|
|
@ -2413,8 +2413,11 @@ PAddr VMManager::TranslateVAddrToPAddr(const VAddr addr)
|
|||
|
||||
Lock();
|
||||
|
||||
// ergo720: horrendous hack, this identity maps all allocations done by the VMManager to keep the LLE OHCI working
|
||||
// (see OHCI_ReadHCCA for more details). Once LLE CPU and MMU are implemented, this can be removed
|
||||
// ergo720: horrendous hack, this identity maps all allocations done by the VMManager to keep the LLE USB working.
|
||||
// The problem is that if the user buffer pointed to by the TD is allocated by the VMManager with VirtualAlloc, then
|
||||
// the physical allocation will not reside in memory.bin and if we tried to access the physical address of it, we
|
||||
// would access a random page with undefined contents.
|
||||
// NOTE: Once LLE CPU and MMU are implemented, this can be removed.
|
||||
|
||||
//if (IS_USER_ADDRESS(addr)) { Type = UserRegion; }
|
||||
//else if (IS_PHYSICAL_ADDRESS(addr)) { Type = ContiguousRegion; }
|
||||
|
|
|
@ -172,7 +172,7 @@ static const uint8_t HubDescriptor[] =
|
|||
0x0A, // u8 bDescLength; 10 bytes
|
||||
0x29, // u8 bDescriptorType; Hub-descriptor
|
||||
NUM_PORTS, // u8 bNbrPorts; 0x08
|
||||
0x0a, // u16 wHubCharacteristics; (individual port over-current protection, no power switching)
|
||||
0x0A, // u16 wHubCharacteristics; (individual port over-current protection, no power switching)
|
||||
0x00,
|
||||
0x01, // u8 bPwrOn2pwrGood; 2 ms
|
||||
0x00, // u8 bHubContrCurrent; 0 mA
|
||||
|
@ -355,8 +355,8 @@ void Hub::UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
}
|
||||
|
||||
case GetHubStatus: {
|
||||
// From the standard: "This request returns the current hub status and the states that have changed since the previous acknowledgment.
|
||||
// The first word of data contains wHubStatus. The second word of data contains wHubChange"
|
||||
// From the USB 1.1 standard: "This request returns the current hub status and the states that have changed since the
|
||||
// previous acknowledgment. The first word of data contains wHubStatus. The second word of data contains wHubChange"
|
||||
// We always report that the local power supply is good and that currently there is no over-power condition
|
||||
data[0] = 0;
|
||||
data[1] = 0;
|
||||
|
@ -367,7 +367,7 @@ void Hub::UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
}
|
||||
|
||||
case GetPortStatus: {
|
||||
// From the standard: "This request returns the current port status and the current value of the port status change bits.
|
||||
// From the USB 1.1 standard: "This request returns the current port status and the current value of the port status change bits.
|
||||
// The first word of data contains wPortStatus. The second word of data contains wPortChange"
|
||||
unsigned int n = index - 1;
|
||||
USBHubPort* port;
|
||||
|
@ -375,8 +375,8 @@ void Hub::UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
goto fail;
|
||||
}
|
||||
port = &m_HubState->ports[n];
|
||||
DBG_PRINTF("%s GetPortStatus -> Address 0x%X, wIndex %d, wPortStatus %d, wPortChange %d\n",
|
||||
__func__, m_HubState->dev.Addr, index, port->wPortStatus, port->wPortChange);
|
||||
DBG_PRINTF("GetPortStatus -> Address 0x%X, wIndex %d, wPortStatus %d, wPortChange %d\n",
|
||||
m_HubState->dev.Addr, index, port->wPortStatus, port->wPortChange);
|
||||
data[0] = port->wPortStatus;
|
||||
data[1] = port->wPortStatus >> 8;
|
||||
data[2] = port->wPortChange;
|
||||
|
@ -394,14 +394,14 @@ void Hub::UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
}
|
||||
|
||||
case SetPortFeature: {
|
||||
// From the standard: "This request sets a value reported in the port status. Features that can be set with this request are PORT_RESET,
|
||||
// PORT_SUSPEND and PORT_POWER; others features are not required to be set by this request"
|
||||
// From the USB 1.1 standard: "This request sets a value reported in the port status. Features that can be set with this request are
|
||||
// PORT_RESET, PORT_SUSPEND and PORT_POWER; others features are not required to be set by this request"
|
||||
unsigned int n = index - 1;
|
||||
USBHubPort* port;
|
||||
XboxDeviceState* dev;
|
||||
|
||||
DBG_PRINTF("%s SetPortFeature -> Address 0x%X, wIndex %d, Feature %s\n",
|
||||
__func__, m_HubState->dev.Addr, index, GetFeatureName(value));
|
||||
DBG_PRINTF("SetPortFeature -> Address 0x%X, wIndex %d, Feature %s\n",
|
||||
m_HubState->dev.Addr, index, GetFeatureName(value).c_str());
|
||||
|
||||
if (n >= NUM_PORTS) {
|
||||
goto fail;
|
||||
|
@ -434,12 +434,12 @@ void Hub::UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
}
|
||||
|
||||
case ClearPortFeature: {
|
||||
// From the standard: "This request resets a value reported in the port status"
|
||||
// From USB 1.1 the standard: "This request resets a value reported in the port status"
|
||||
unsigned int n = index - 1;
|
||||
USBHubPort *port;
|
||||
|
||||
DBG_PRINTF("%s ClearPortFeature -> Address 0x%X, wIndex %d, Feature %s\n",
|
||||
__func__, m_HubState->dev.Addr, index, GetFeatureName(value));
|
||||
DBG_PRINTF("ClearPortFeature -> Address 0x%X, wIndex %d, Feature %s\n",
|
||||
m_HubState->dev.Addr, index, GetFeatureName(value).c_str());
|
||||
|
||||
if (n >= NUM_PORTS) {
|
||||
goto fail;
|
||||
|
@ -525,7 +525,7 @@ void Hub::UsbHub_HandleData(XboxDeviceState* dev, USBPacket* p)
|
|||
p->Status = USB_RET_BABBLE;
|
||||
return;
|
||||
}
|
||||
DBG_PRINTF("%s Address 0x%X, Status %d\n", __func__, m_HubState->dev.Addr, status);
|
||||
DBG_PRINTF("Address 0x%X, Status %d\n", m_HubState->dev.Addr, status);
|
||||
for (i = 0; i < n; i++) {
|
||||
buf[i] = status >> (8 * i);
|
||||
}
|
||||
|
|
|
@ -50,11 +50,50 @@ namespace xboxkrnl
|
|||
#include "OHCI.h"
|
||||
#include "core\kernel\exports\EmuKrnl.h" // For HalSystemInterrupt
|
||||
#include "common\util\CxbxUtil.h"
|
||||
#include "Logging.h"
|
||||
#include "Logging.h"
|
||||
|
||||
static const char* OHCI_RegNames[] = {
|
||||
"HcRevision",
|
||||
"HcControl",
|
||||
"HcCommandStatus",
|
||||
"HcInterruptStatus",
|
||||
"HcInterruptEnable",
|
||||
"HcInterruptDisable",
|
||||
"HcHCCA",
|
||||
"HcPeriodCurrentED",
|
||||
"HcControlHeadED",
|
||||
"HcControlCurrentED",
|
||||
"HcBulkHeadED",
|
||||
"HcBulkCurrentED",
|
||||
"HcDoneHead",
|
||||
"HcFmInterval",
|
||||
"HcFmRemaining",
|
||||
"HcFmNumber",
|
||||
"HcPeriodicStart",
|
||||
"HcLSThreshold",
|
||||
"HcRhDescriptorA",
|
||||
"HcRhDescriptorB",
|
||||
"HcRhStatus",
|
||||
"HcRhPortStatus[0]",
|
||||
"HcRhPortStatus[1]",
|
||||
"HcRhPortStatus[2]",
|
||||
"HcRhPortStatus[3]"
|
||||
};
|
||||
|
||||
/* Define these two if you want to dump usb packets */
|
||||
/* Define these two if you want to dump usb packets and OHCI registers */
|
||||
//#define DEBUG_ISOCH
|
||||
//#define DEBUG_PACKET
|
||||
//#define DEBUG_PACKET
|
||||
//#define DEBUG_OHCI_REG
|
||||
|
||||
#ifdef DEBUG_OHCI_REG
|
||||
#define DUMP_REG_R(reg_val) DBG_PRINTF("%s, R, reg_val: 0x%X\n", OHCI_RegNames[Addr >> 2], reg_val)
|
||||
#define DUMP_REG_W(reg_val, val) DBG_PRINTF("%s, W, reg_val: 0x%X, val: 0x%X\n", OHCI_RegNames[Addr >> 2], reg_val, val)
|
||||
#define DUMP_REG_RO(reg_val, val) DBG_PRINTF("%s, RO, reg_val: 0x%X, val: 0x%X\n", OHCI_RegNames[Addr >> 2], reg_val, val)
|
||||
#else
|
||||
#define DUMP_REG_R(reg_val)
|
||||
#define DUMP_REG_W(reg_val, val)
|
||||
#define DUMP_REG_RO(reg_val, val)
|
||||
#endif
|
||||
|
||||
/* These macros are used to access the bits of the various registers */
|
||||
// HcControl
|
||||
|
@ -254,7 +293,7 @@ void OHCI::OHCI_FrameBoundaryWorker()
|
|||
|
||||
// Process all the lists at the end of the frame
|
||||
if (m_Registers.HcControl & OHCI_CTL_PLE) {
|
||||
// From the standard: "The head pointer used for a particular frame is determined by using the last 5 bits of the
|
||||
// From the OHCI standard: "The head pointer used for a particular frame is determined by using the last 5 bits of the
|
||||
// Frame Counter as an offset into the interrupt array within the HCCA."
|
||||
int n = m_Registers.HcFmNumber & 0x1F;
|
||||
OHCI_ServiceEDlist(hcca.HccaInterrruptTable[n], 0); // dropped little -> big endian conversion from XQEMU
|
||||
|
@ -277,7 +316,7 @@ void OHCI::OHCI_FrameBoundaryWorker()
|
|||
return;
|
||||
}
|
||||
|
||||
// From the standard: "This bit is loaded from the FrameIntervalToggle field of
|
||||
// From the OHCI standard: "This bit is loaded from the FrameIntervalToggle field of
|
||||
// HcFmInterval whenever FrameRemaining reaches 0."
|
||||
m_Registers.HcFmRemaining = (m_Registers.HcFmInterval & OHCI_FMI_FIT) == 0 ?
|
||||
m_Registers.HcFmRemaining & ~OHCI_FMR_FRT : m_Registers.HcFmRemaining | OHCI_FMR_FRT;
|
||||
|
@ -288,13 +327,13 @@ void OHCI::OHCI_FrameBoundaryWorker()
|
|||
|
||||
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
|
||||
// From the OHCI standard: "This is set to zero whenever HC writes the content of this
|
||||
// register to HCCA. It also sets the WritebackDoneHead of HcInterruptStatus."
|
||||
CxbxKrnlCleanup("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
|
||||
// From the OHCI 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
|
||||
|
@ -332,102 +371,54 @@ void OHCI::OHCI_FatalError()
|
|||
|
||||
OHCI_SetInterrupt(OHCI_INTR_UE);
|
||||
OHCI_BusStop();
|
||||
EmuLog(LOG_LEVEL::WARNING, "an unrecoverable error occoured!\n");
|
||||
EmuLog(LOG_LEVEL::WARNING, "An unrecoverable error has occoured!\n");
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_ReadHCCA(xbaddr Paddr, OHCI_HCCA* Hcca)
|
||||
{
|
||||
// ergo720: what's written below is true if we identity map only allocations served by VirtualAlloc.
|
||||
// There could be a peculiar problem if the shared memory between HCD and HC is allocated by the
|
||||
// VMManager with VirtualAlloc: the physical allocation would not reside in memory.bin and if we tried to
|
||||
// access the physical address of it, we would access an empty page. In practice, I disassembled various
|
||||
// xbe's of my games and discovered that this shared memory is allocated with MmAllocateContiguousMemory
|
||||
// which means we can access it from the contiguous region just fine (lucky)
|
||||
// ... provided that XDK revisions didn't alter this
|
||||
|
||||
// NOTE: this shared memory contains the HCCA + EDs and TDs
|
||||
|
||||
if (Paddr != xbnull) {
|
||||
std::memcpy(Hcca, reinterpret_cast<void*>(Paddr), sizeof(OHCI_HCCA));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // error
|
||||
return Memory_R(reinterpret_cast<void*>(Paddr), Hcca, sizeof(OHCI_HCCA));
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_WriteHCCA(xbaddr Paddr, OHCI_HCCA* Hcca)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
// We need to calculate the offset of the HccaFrameNumber member to avoid overwriting HccaInterrruptTable
|
||||
size_t OffsetOfFrameNumber = offsetof(OHCI_HCCA, HccaFrameNumber);
|
||||
|
||||
std::memcpy(reinterpret_cast<void*>(Paddr + OffsetOfFrameNumber),
|
||||
reinterpret_cast<uint8_t*>(Hcca) + OffsetOfFrameNumber, 8);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // error
|
||||
// We need to calculate the offset of the HccaFrameNumber member to avoid overwriting HccaInterrruptTable
|
||||
size_t OffsetOfFrameNumber = offsetof(OHCI_HCCA, HccaFrameNumber);
|
||||
return Memory_W(reinterpret_cast<void*>(Paddr + OffsetOfFrameNumber), reinterpret_cast<uint8_t*>(Hcca) + OffsetOfFrameNumber, 8);
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_ReadED(xbaddr Paddr, OHCI_ED* Ed)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
GetDwords(Paddr, reinterpret_cast<uint32_t*>(Ed), sizeof(*Ed) >> 2); // ED is 16 bytes large
|
||||
return false;
|
||||
}
|
||||
return true; // error
|
||||
return GetDwords(Paddr, reinterpret_cast<uint32_t*>(Ed), sizeof(*Ed) >> 2); // ED is 16 bytes large
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_WriteED(xbaddr Paddr, OHCI_ED* Ed)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
// According to the standard, only the HeadP field is writable by the HC, so we'll write just that
|
||||
size_t OffsetOfHeadP = offsetof(OHCI_ED, HeadP);
|
||||
WriteDwords(Paddr + OffsetOfHeadP, reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(Ed) + OffsetOfHeadP), 1);
|
||||
return false;
|
||||
}
|
||||
return true; // error
|
||||
// According to the standard, only the HeadP field is writable by the HC, so we'll write just that
|
||||
size_t OffsetOfHeadP = offsetof(OHCI_ED, HeadP);
|
||||
return WriteDwords(Paddr + OffsetOfHeadP, reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(Ed) + OffsetOfHeadP), 1);
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_ReadTD(xbaddr Paddr, OHCI_TD* Td)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
GetDwords(Paddr, reinterpret_cast<uint32_t*>(Td), sizeof(*Td) >> 2); // TD is 16 bytes large
|
||||
return false;
|
||||
}
|
||||
return true; // error
|
||||
return GetDwords(Paddr, reinterpret_cast<uint32_t*>(Td), sizeof(*Td) >> 2); // TD is 16 bytes large
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_WriteTD(xbaddr Paddr, OHCI_TD* Td)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
WriteDwords(Paddr, reinterpret_cast<uint32_t*>(Td), sizeof(*Td) >> 2);
|
||||
return false;
|
||||
}
|
||||
return true; // error
|
||||
return WriteDwords(Paddr, reinterpret_cast<uint32_t*>(Td), sizeof(*Td) >> 2);
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_ReadIsoTD(xbaddr Paddr, OHCI_ISO_TD* td)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
GetDwords(Paddr, reinterpret_cast<uint32_t*>(td), 4);
|
||||
GetWords(Paddr + 16, td->Offset, 8);
|
||||
return false;
|
||||
}
|
||||
return true; // error
|
||||
return GetDwords(Paddr, reinterpret_cast<uint32_t*>(td), 4) || GetWords(Paddr + 16, td->Offset, 8);
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_WriteIsoTD(xbaddr Paddr, OHCI_ISO_TD* td)
|
||||
{
|
||||
if (Paddr != xbnull) {
|
||||
WriteDwords(Paddr, reinterpret_cast<uint32_t*>(td), 4);
|
||||
WriteWords(Paddr + 16, td->Offset, 8);
|
||||
return false;
|
||||
}
|
||||
return true; // error
|
||||
return WriteDwords(Paddr, reinterpret_cast<uint32_t*>(td), 4) || WriteWords(Paddr + 16, td->Offset, 8);
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_CopyTD(OHCI_TD* Td, uint8_t* Buffer, int Length, bool bIsWrite)
|
||||
bool OHCI::OHCI_CopyTDBuffer(OHCI_TD* Td, uint8_t* Buffer, int Length, bool bIsWrite)
|
||||
{
|
||||
uint32_t ptr, n;
|
||||
|
||||
|
@ -438,26 +429,26 @@ bool OHCI::OHCI_CopyTD(OHCI_TD* Td, uint8_t* Buffer, int Length, bool bIsWrite)
|
|||
n = Length;
|
||||
}
|
||||
|
||||
if (OHCI_FindAndCopyTD(ptr, Buffer, n, bIsWrite)) {
|
||||
if (Memory_RW(reinterpret_cast<void*>(ptr), Buffer, n, bIsWrite)) {
|
||||
return true; // error
|
||||
}
|
||||
if (n == Length) {
|
||||
return false; // no bytes left to copy
|
||||
}
|
||||
|
||||
// From the standard: "If during the data transfer the buffer address contained in the HC<48>fs working copy of
|
||||
// From the OHCI standard: "If during the data transfer the buffer address contained in the HC<48>fs working copy of
|
||||
// CurrentBufferPointer crosses a 4K boundary, the upper 20 bits of BufferEnd are copied to the
|
||||
// working value of CurrentBufferPointer causing the next buffer address to be the 0th byte in the
|
||||
// same 4K page that contains the last byte of the buffer."
|
||||
ptr = Td->BufferEnd & ~0xFFFu;
|
||||
Buffer += n;
|
||||
if (OHCI_FindAndCopyTD(ptr, Buffer, Length - n, bIsWrite)) {
|
||||
if (Memory_RW(reinterpret_cast<void*>(ptr), Buffer, Length - n, bIsWrite)) {
|
||||
return true; // error
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_CopyIsoTD(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffer, int Length, bool bIsWrite)
|
||||
bool OHCI::OHCI_CopyIsoTDBuffer(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffer, int Length, bool bIsWrite)
|
||||
{
|
||||
uint32_t ptr, n;
|
||||
|
||||
|
@ -467,7 +458,7 @@ bool OHCI::OHCI_CopyIsoTD(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffe
|
|||
n = Length;
|
||||
}
|
||||
|
||||
if (OHCI_FindAndCopyTD(ptr, Buffer, n, bIsWrite)) {
|
||||
if (Memory_RW(reinterpret_cast<void*>(ptr), Buffer, n, bIsWrite)) {
|
||||
return true; // error
|
||||
}
|
||||
if (n == Length) {
|
||||
|
@ -475,28 +466,12 @@ bool OHCI::OHCI_CopyIsoTD(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffe
|
|||
}
|
||||
ptr = end_addr & ~0xFFFu;
|
||||
Buffer += n;
|
||||
if (OHCI_FindAndCopyTD(ptr, Buffer, Length - n, bIsWrite)) {
|
||||
if (Memory_RW(reinterpret_cast<void*>(ptr), Buffer, Length - n, bIsWrite)) {
|
||||
return true; // error
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OHCI::OHCI_FindAndCopyTD(xbaddr Paddr, uint8_t* Buffer, int Length, bool bIsWrite)
|
||||
{
|
||||
if (Paddr == xbnull) {
|
||||
return true; // error
|
||||
}
|
||||
|
||||
if (bIsWrite) {
|
||||
std::memcpy(reinterpret_cast<void*>(Paddr), Buffer, Length);
|
||||
}
|
||||
else {
|
||||
std::memcpy(Buffer, reinterpret_cast<void*>(Paddr), Length);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int OHCI::OHCI_ServiceEDlist(xbaddr Head, int Completion)
|
||||
{
|
||||
OHCI_ED ed;
|
||||
|
@ -518,7 +493,7 @@ int OHCI::OHCI_ServiceEDlist(xbaddr Head, int Completion)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// From the standard "An Endpoint Descriptor (ED) is a 16-byte, memory resident structure that must be aligned to a
|
||||
// From the OHCI standard "An Endpoint Descriptor (ED) is a 16-byte, memory resident structure that must be aligned to a
|
||||
// 16-byte boundary."
|
||||
next_ed = ed.NextED & OHCI_DPTR_MASK;
|
||||
|
||||
|
@ -600,7 +575,7 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// From the standard: "This 2-bit field indicates the direction of data flow and the PID
|
||||
// From the OHCI standard: "This 2-bit field indicates the direction of data flow and the PID
|
||||
// to be used for the token. This field is only relevant to the HC if the D field in the ED
|
||||
// was set to 00b or 11b indicating that the PID determination is deferred to the TD."
|
||||
direction = OHCI_BM(Ed->Flags, ED_D);
|
||||
|
@ -666,7 +641,7 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
|
|||
packetlen = length;
|
||||
}
|
||||
if (!completion) {
|
||||
if (OHCI_CopyTD(&td, m_UsbBuffer, packetlen, false)) {
|
||||
if (OHCI_CopyTDBuffer(&td, m_UsbBuffer, packetlen, false)) {
|
||||
OHCI_FatalError();
|
||||
}
|
||||
}
|
||||
|
@ -700,7 +675,7 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
|
|||
}
|
||||
dev = OHCI_FindDevice(OHCI_BM(Ed->Flags, ED_FA));
|
||||
ep = m_UsbDevice->USB_GetEP(dev, pid, OHCI_BM(Ed->Flags, ED_EN));
|
||||
m_UsbDevice->USB_PacketSetup(&m_UsbPacket, pid, ep, 0, addr, !flag_r, OHCI_BM(td.Flags, TD_DI) == 0);
|
||||
m_UsbDevice->USB_PacketSetup(&m_UsbPacket, pid, ep, addr, !flag_r, OHCI_BM(td.Flags, TD_DI) == 0);
|
||||
m_UsbDevice->USB_PacketAddBuffer(&m_UsbPacket, m_UsbBuffer, packetlen);
|
||||
m_UsbDevice->USB_HandlePacket(dev, &m_UsbPacket);
|
||||
#ifdef DEBUG_PACKET
|
||||
|
@ -721,7 +696,7 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
|
|||
|
||||
if (ret >= 0) {
|
||||
if (direction == OHCI_TD_DIR_IN) {
|
||||
if (OHCI_CopyTD(&td, m_UsbBuffer, ret, true)) {
|
||||
if (OHCI_CopyTDBuffer(&td, m_UsbBuffer, ret, true)) {
|
||||
OHCI_FatalError();
|
||||
}
|
||||
#ifdef DEBUG_PACKET
|
||||
|
@ -888,7 +863,7 @@ void OHCI::OHCI_StateReset()
|
|||
|
||||
OHCI_StopEndpoints();
|
||||
|
||||
DBG_PRINTF("Reset mode event.\n");
|
||||
DBG_PRINTF("Reset event.\n");
|
||||
}
|
||||
|
||||
void OHCI::OHCI_BusStart()
|
||||
|
@ -896,7 +871,7 @@ void OHCI::OHCI_BusStart()
|
|||
// Create the EOF timer.
|
||||
m_pEOFtimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, "", nullptr);
|
||||
|
||||
DBG_PRINTF("Operational mode event\n");
|
||||
DBG_PRINTF("Operational event\n");
|
||||
|
||||
// SOF event
|
||||
OHCI_SOF(true);
|
||||
|
@ -943,11 +918,11 @@ void OHCI::OHCI_ChangeState(uint32_t Value)
|
|||
|
||||
case Suspend:
|
||||
OHCI_BusStop();
|
||||
DBG_PRINTF("Suspend mode event\n");
|
||||
DBG_PRINTF("Suspend event\n");
|
||||
break;
|
||||
|
||||
case Resume:
|
||||
DBG_PRINTF("Resume mode event\n");
|
||||
DBG_PRINTF("Resume event\n");
|
||||
break;
|
||||
|
||||
case Reset:
|
||||
|
@ -981,101 +956,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;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 1: // HcControl
|
||||
ret = m_Registers.HcControl;
|
||||
ret = m_Registers.HcControl;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 2: // HcCommandStatus
|
||||
ret = m_Registers.HcCommandStatus;
|
||||
ret = m_Registers.HcCommandStatus;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 3: // HcInterruptStatus
|
||||
ret = m_Registers.HcInterruptStatus;
|
||||
ret = m_Registers.HcInterruptStatus;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 4: // HcInterruptEnable
|
||||
case 5: // HcInterruptDisable
|
||||
ret = m_Registers.HcInterrupt;
|
||||
ret = m_Registers.HcInterrupt;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 6: // HcHCCA
|
||||
ret = m_Registers.HcHCCA;
|
||||
ret = m_Registers.HcHCCA;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 7: // HcPeriodCurrentED
|
||||
ret = m_Registers.HcPeriodCurrentED;
|
||||
ret = m_Registers.HcPeriodCurrentED;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 8: // HcControlHeadED
|
||||
ret = m_Registers.HcControlHeadED;
|
||||
ret = m_Registers.HcControlHeadED;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 9: // HcControlCurrentED
|
||||
ret = m_Registers.HcControlCurrentED;
|
||||
ret = m_Registers.HcControlCurrentED;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 10: // HcBulkHeadED
|
||||
ret = m_Registers.HcBulkHeadED;
|
||||
ret = m_Registers.HcBulkHeadED;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 11: // HcBulkCurrentED
|
||||
ret = m_Registers.HcBulkCurrentED;
|
||||
ret = m_Registers.HcBulkCurrentED;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 12: // HcDoneHead
|
||||
ret = m_Registers.HcDoneHead;
|
||||
ret = m_Registers.HcDoneHead;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 13: // HcFmInterval
|
||||
ret = m_Registers.HcFmInterval;
|
||||
ret = m_Registers.HcFmInterval;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 14: // HcFmRemaining
|
||||
ret = OHCI_GetFrameRemaining();
|
||||
ret = OHCI_GetFrameRemaining();
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 15: // HcFmNumber
|
||||
ret = m_Registers.HcFmNumber;
|
||||
ret = m_Registers.HcFmNumber;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 16: // HcPeriodicStart
|
||||
ret = m_Registers.HcPeriodicStart;
|
||||
ret = m_Registers.HcPeriodicStart;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 17: // HcLSThreshold
|
||||
ret = m_Registers.HcLSThreshold;
|
||||
ret = m_Registers.HcLSThreshold;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 18: // HcRhDescriptorA
|
||||
ret = m_Registers.HcRhDescriptorA;
|
||||
ret = m_Registers.HcRhDescriptorA;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 19: // HcRhDescriptorB
|
||||
ret = m_Registers.HcRhDescriptorB;
|
||||
ret = m_Registers.HcRhDescriptorB;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 20: // HcRhStatus
|
||||
ret = m_Registers.HcRhStatus;
|
||||
ret = m_Registers.HcRhStatus;
|
||||
DUMP_REG_R(ret);
|
||||
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;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 22: // RhPort 1
|
||||
ret = m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS;
|
||||
ret = m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 23:
|
||||
ret = m_Registers.RhPort[2].HcRhPortStatus | OHCI_PORT_PPS;
|
||||
case 23: // RhPort 2
|
||||
ret = m_Registers.RhPort[2].HcRhPortStatus | OHCI_PORT_PPS;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
case 24:
|
||||
ret = m_Registers.RhPort[3].HcRhPortStatus | OHCI_PORT_PPS;
|
||||
case 24: // RhPort 3
|
||||
ret = m_Registers.RhPort[3].HcRhPortStatus | OHCI_PORT_PPS;
|
||||
DUMP_REG_R(ret);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1096,11 +1095,13 @@ 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
|
||||
DUMP_REG_RO(m_Registers.HcRevision, Value);
|
||||
break;
|
||||
|
||||
case 1: // HcControl
|
||||
OHCI_ChangeState(Value);
|
||||
OHCI_ChangeState(Value);
|
||||
DUMP_REG_W(m_Registers.HcControl, Value);
|
||||
break;
|
||||
|
||||
case 2: // HcCommandStatus
|
||||
|
@ -1108,59 +1109,70 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value)
|
|||
// SOC is read-only
|
||||
Value &= ~OHCI_STATUS_SOC;
|
||||
|
||||
// From the standard: "The Host Controller must ensure that bits written as 1 become set
|
||||
// From the OHCI standard: "The Host Controller must ensure that bits written as 1 become set
|
||||
// in the register while bits written as 0 remain unchanged in the register."
|
||||
m_Registers.HcCommandStatus |= Value;
|
||||
|
||||
if (m_Registers.HcCommandStatus & OHCI_STATUS_HCR) {
|
||||
// Do a hardware reset
|
||||
OHCI_StateReset();
|
||||
}
|
||||
}
|
||||
DUMP_REG_W(m_Registers.HcCommandStatus, Value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // HcInterruptStatus
|
||||
m_Registers.HcInterruptStatus &= ~Value;
|
||||
OHCI_UpdateInterrupt();
|
||||
OHCI_UpdateInterrupt();
|
||||
DUMP_REG_W(m_Registers.HcInterruptStatus, Value);
|
||||
break;
|
||||
|
||||
case 4: // HcInterruptEnable
|
||||
m_Registers.HcInterrupt |= Value;
|
||||
OHCI_UpdateInterrupt();
|
||||
OHCI_UpdateInterrupt();
|
||||
DUMP_REG_W(m_Registers.HcInterrupt, Value);
|
||||
break;
|
||||
|
||||
case 5: // HcInterruptDisable
|
||||
m_Registers.HcInterrupt &= ~Value;
|
||||
OHCI_UpdateInterrupt();
|
||||
OHCI_UpdateInterrupt();
|
||||
DUMP_REG_W(m_Registers.HcInterrupt, Value);
|
||||
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;
|
||||
m_Registers.HcHCCA = Value & OHCI_HCCA_MASK;
|
||||
DUMP_REG_W(m_Registers.HcHCCA, Value);
|
||||
break;
|
||||
|
||||
case 7: // HcPeriodCurrentED
|
||||
// This register is read-only
|
||||
// This register is read-only
|
||||
DUMP_REG_RO(m_Registers.HcPeriodCurrentED, Value);
|
||||
break;
|
||||
|
||||
case 8: // HcControlHeadED
|
||||
m_Registers.HcControlHeadED = Value & OHCI_DPTR_MASK;
|
||||
m_Registers.HcControlHeadED = Value & OHCI_DPTR_MASK;
|
||||
DUMP_REG_W(m_Registers.HcControlHeadED, Value);
|
||||
break;
|
||||
|
||||
case 9: // HcControlCurrentED
|
||||
m_Registers.HcControlCurrentED = Value & OHCI_DPTR_MASK;
|
||||
m_Registers.HcControlCurrentED = Value & OHCI_DPTR_MASK;
|
||||
DUMP_REG_W(m_Registers.HcControlCurrentED, Value);
|
||||
break;
|
||||
|
||||
case 10: // HcBulkHeadED
|
||||
m_Registers.HcBulkHeadED = Value & OHCI_DPTR_MASK;
|
||||
m_Registers.HcBulkHeadED = Value & OHCI_DPTR_MASK;
|
||||
DUMP_REG_W(m_Registers.HcBulkHeadED, Value);
|
||||
break;
|
||||
|
||||
case 11: // HcBulkCurrentED
|
||||
m_Registers.HcBulkCurrentED = Value & OHCI_DPTR_MASK;
|
||||
m_Registers.HcBulkCurrentED = Value & OHCI_DPTR_MASK;
|
||||
DUMP_REG_W(m_Registers.HcBulkCurrentED, Value);
|
||||
break;
|
||||
|
||||
case 12: // HcDoneHead
|
||||
// This register is read-only
|
||||
// This register is read-only
|
||||
DUMP_REG_RO(m_Registers.HcDoneHead, Value);
|
||||
break;
|
||||
|
||||
case 13: // HcFmInterval
|
||||
|
@ -1168,29 +1180,35 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value)
|
|||
if ((Value & OHCI_FMI_FI) != (m_Registers.HcFmInterval & OHCI_FMI_FI)) {
|
||||
DBG_PRINTF("Changing frame interval duration. New value is %u\n", Value & OHCI_FMI_FI);
|
||||
}
|
||||
m_Registers.HcFmInterval = Value & ~0xC000;
|
||||
m_Registers.HcFmInterval = Value & ~0xC000;
|
||||
DUMP_REG_W(m_Registers.HcFmInterval, Value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 14: // HcFmRemaining
|
||||
// This register is read-only
|
||||
// This register is read-only
|
||||
DUMP_REG_RO(m_Registers.HcFmRemaining, Value);
|
||||
break;
|
||||
|
||||
case 15: // HcFmNumber
|
||||
// This register is read-only
|
||||
// This register is read-only
|
||||
DUMP_REG_RO(m_Registers.HcFmNumber, Value);
|
||||
break;
|
||||
|
||||
case 16: // HcPeriodicStart
|
||||
m_Registers.HcPeriodicStart = Value & 0x3FFF;
|
||||
m_Registers.HcPeriodicStart = Value & 0x3FFF;
|
||||
DUMP_REG_W(m_Registers.HcPeriodicStart, Value);
|
||||
break;
|
||||
|
||||
case 17: // HcLSThreshold
|
||||
m_Registers.HcLSThreshold = Value & 0xFFF;
|
||||
m_Registers.HcLSThreshold = Value & 0xFFF;
|
||||
DUMP_REG_W(m_Registers.HcLSThreshold, Value);
|
||||
break;
|
||||
|
||||
case 18: // HcRhDescriptorA
|
||||
m_Registers.HcRhDescriptorA &= ~OHCI_RHA_RW_MASK;
|
||||
m_Registers.HcRhDescriptorA |= Value & OHCI_RHA_RW_MASK; // ??
|
||||
m_Registers.HcRhDescriptorA |= Value & OHCI_RHA_RW_MASK; // ??
|
||||
DUMP_REG_W(m_Registers.HcRhDescriptorA, Value);
|
||||
break;
|
||||
|
||||
case 19: // HcRhDescriptorB
|
||||
|
@ -1198,23 +1216,28 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value)
|
|||
break;
|
||||
|
||||
case 20: // HcRhStatus
|
||||
OHCI_SetHubStatus(Value);
|
||||
OHCI_SetHubStatus(Value);
|
||||
DUMP_REG_W(m_Registers.HcRhStatus, Value);
|
||||
break;
|
||||
|
||||
case 21: // RhPort 0
|
||||
OHCI_PortSetStatus(0, Value);
|
||||
OHCI_PortSetStatus(0, Value);
|
||||
DUMP_REG_W(m_Registers.RhPort[0].HcRhPortStatus, Value);
|
||||
break;
|
||||
|
||||
case 22: // RhPort 1
|
||||
OHCI_PortSetStatus(1, Value);
|
||||
OHCI_PortSetStatus(1, Value);
|
||||
DUMP_REG_W(m_Registers.RhPort[1].HcRhPortStatus, Value);
|
||||
break;
|
||||
|
||||
case 23: // RhPort 2
|
||||
OHCI_PortSetStatus(2, Value);
|
||||
OHCI_PortSetStatus(2, Value);
|
||||
DUMP_REG_W(m_Registers.RhPort[2].HcRhPortStatus, Value);
|
||||
break;
|
||||
|
||||
case 24: // RhPort 3
|
||||
OHCI_PortSetStatus(3, Value);
|
||||
OHCI_PortSetStatus(3, Value);
|
||||
DUMP_REG_W(m_Registers.RhPort[3].HcRhPortStatus, Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1477,7 +1500,7 @@ void OHCI::OHCI_Wakeup(USBPort* port1)
|
|||
// Note that the controller can be suspended even if this port is not
|
||||
if ((m_Registers.HcControl & OHCI_CTL_HCFS) == Suspend) {
|
||||
DBG_PRINTF("remote-wakeup: SUSPEND->RESUME\n");
|
||||
// From the standard: "The only interrupts possible in the USBSUSPEND state are ResumeDetected (the
|
||||
// From the OHCI standard: "The only interrupts possible in the USBSUSPEND state are ResumeDetected (the
|
||||
// Host Controller will have changed the HostControllerFunctionalState to the USBRESUME state)
|
||||
// and OwnershipChange."
|
||||
m_Registers.HcControl &= ~OHCI_CTL_HCFS;
|
||||
|
@ -1558,7 +1581,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
|
||||
starting_frame = OHCI_BM(iso_td.Flags, TD_SF);
|
||||
frame_count = OHCI_BM(iso_td.Flags, TD_FC);
|
||||
// From the standard: "The Host Controller does an unsigned subtraction of StartingFrame from the 16 bits of
|
||||
// From the OHCI standard: "The Host Controller does an unsigned subtraction of StartingFrame from the 16 bits of
|
||||
// HcFmNumber to arrive at a signed value for a relative frame number (frame R)."
|
||||
relative_frame_number = USUB(m_Registers.HcFmNumber & 0xFFFF, starting_frame);
|
||||
|
||||
|
@ -1580,13 +1603,13 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
#endif
|
||||
|
||||
if (relative_frame_number < 0) {
|
||||
// From the standard: "If the relative frame number is negative, then the current frame is earlier than the 0th frame
|
||||
// From the OHCI standard: "If the relative frame number is negative, then the current frame is earlier than the 0th frame
|
||||
// of the Isochronous TD and the Host Controller advances to the next ED."
|
||||
DBG_PRINTF("ISO_TD R=%d < 0\n", relative_frame_number);
|
||||
return 1;
|
||||
}
|
||||
else if (relative_frame_number > frame_count) {
|
||||
// From the standard: "If the relative frame number is greater than
|
||||
// From the OHCI standard: "If the relative frame number is greater than
|
||||
// FrameCount, then the Isochronous TD has expired and a error condition exists."
|
||||
DBG_PRINTF("ISO_TD R=%d > FC=%d\n", relative_frame_number, frame_count);
|
||||
OHCI_SET_BM(iso_td.Flags, TD_CC, OHCI_CC_DATAOVERRUN);
|
||||
|
@ -1605,7 +1628,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// From the standard: "If the relative frame number is between 0 and FrameCount, then the Host Controller issues
|
||||
// From the OHCI standard: "If the relative frame number is between 0 and FrameCount, then the Host Controller issues
|
||||
// a token to the endpoint and attempts a data transfer using the buffer described by the Isochronous TD."
|
||||
|
||||
dir = OHCI_BM(ed->Flags, ED_D);
|
||||
|
@ -1641,7 +1664,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
start_offset = iso_td.Offset[relative_frame_number];
|
||||
next_offset = iso_td.Offset[relative_frame_number + 1];
|
||||
|
||||
// From the standard: "If the Host Controller supports checking of the Offsets, if either Offset[R] or Offset[R+1] does
|
||||
// From the OHCI standard: "If the Host Controller supports checking of the Offsets, if either Offset[R] or Offset[R+1] does
|
||||
// not have a ConditionCode of NOT ACCESSED or if the Offset[R + 1] is not greater than or equal to Offset[R], then
|
||||
// an Unrecoverable Error is indicated."
|
||||
// ergo720: I have a doubt here: according to the standard, the error condition is set if ConditionCode (bits 12-15 of
|
||||
|
@ -1661,7 +1684,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
return 1;
|
||||
}
|
||||
|
||||
// From the standard: "Bit 12 of offset R then selects the upper 20 bits of the physical address
|
||||
// From the OHCI standard: "Bit 12 of offset R then selects the upper 20 bits of the physical address
|
||||
// as either BufferPage0 when bit 12 = 0 or the upper 20 bits of BufferEnd when bit 12 = 1."
|
||||
|
||||
if ((start_offset & 0x1000) == 0) {
|
||||
|
@ -1673,7 +1696,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
(start_offset & OHCI_OFFSET_MASK);
|
||||
}
|
||||
|
||||
// From the standard: "If the data packet is not the last in an Isochronous TD (R not equal to FrameCount),
|
||||
// From the OHCI standard: "If the data packet is not the last in an Isochronous TD (R not equal to FrameCount),
|
||||
// then the ending address of the buffer is found by using Offset[R + 1] - 1. This value is then used to create a
|
||||
// physical address in the same manner as the Offset[R] was used to create the starting physical address."
|
||||
|
||||
|
@ -1689,7 +1712,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
}
|
||||
}
|
||||
else {
|
||||
// From the standard: "If, however, the data packet is the last in an Isochronous TD(R = FrameCount),
|
||||
// From the OHCI standard: "If, however, the data packet is the last in an Isochronous TD(R = FrameCount),
|
||||
// then the value of BufferEnd is the address of the last byte in the buffer."
|
||||
end_addr = iso_td.BufferEnd;
|
||||
}
|
||||
|
@ -1703,7 +1726,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
}
|
||||
|
||||
if (len && dir != OHCI_TD_DIR_IN) {
|
||||
if (OHCI_CopyIsoTD(start_addr, end_addr, m_UsbBuffer, len, false)) {
|
||||
if (OHCI_CopyIsoTDBuffer(start_addr, end_addr, m_UsbBuffer, len, false)) {
|
||||
OHCI_FatalError();
|
||||
return 1;
|
||||
}
|
||||
|
@ -1713,7 +1736,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
bool int_req = relative_frame_number == frame_count && OHCI_BM(iso_td.Flags, TD_DI) == 0;
|
||||
dev = OHCI_FindDevice(OHCI_BM(ed->Flags, ED_FA));
|
||||
ep = m_UsbDevice->USB_GetEP(dev, pid, OHCI_BM(ed->Flags, ED_EN));
|
||||
m_UsbDevice->USB_PacketSetup(&m_UsbPacket, pid, ep, 0, addr, false, int_req);
|
||||
m_UsbDevice->USB_PacketSetup(&m_UsbPacket, pid, ep, addr, false, int_req);
|
||||
m_UsbDevice->USB_PacketAddBuffer(&m_UsbPacket, m_UsbBuffer, len);
|
||||
m_UsbDevice->USB_HandlePacket(dev, &m_UsbPacket);
|
||||
if (m_UsbPacket.Status == USB_RET_ASYNC) {
|
||||
|
@ -1733,14 +1756,14 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
|
|||
start_offset, end_offset, start_addr, end_addr, str, len, ret);
|
||||
#endif
|
||||
|
||||
// From the standard: "After each data packet transfer, the Rth Offset is replaced with a value that indicates the status of
|
||||
// From the OHCI standard: "After each data packet transfer, the Rth Offset is replaced with a value that indicates the status of
|
||||
// the data packet transfer.The upper 4 bits of the value are the ConditionCode for the transfer and the lower 12 bits
|
||||
// represent the size of the transfer.Together, these two fields constitute the Packet Status Word(PacketStatusWord)."
|
||||
|
||||
// Writeback
|
||||
if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
|
||||
// IN transfer succeeded
|
||||
if (OHCI_CopyIsoTD(start_addr, end_addr, m_UsbBuffer, ret, true)) {
|
||||
if (OHCI_CopyIsoTDBuffer(start_addr, end_addr, m_UsbBuffer, ret, true)) {
|
||||
OHCI_FatalError();
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ class OHCI
|
|||
|
||||
|
||||
private:
|
||||
// pointer to g_USB0 or g_USB1
|
||||
// pointer to g_USB0
|
||||
USBDevice* m_UsbDevice = nullptr;
|
||||
// all the registers available in the OHCI standard
|
||||
OHCI_Registers m_Registers;
|
||||
|
@ -234,11 +234,9 @@ class OHCI
|
|||
// write an iso TD in memory
|
||||
bool OHCI_WriteIsoTD(xbaddr Paddr, OHCI_ISO_TD* td);
|
||||
// read/write the user buffer pointed to by a TD from/to main memory
|
||||
bool OHCI_CopyTD(OHCI_TD* Td, uint8_t* Buffer, int Length, bool bIsWrite);
|
||||
bool OHCI_CopyTDBuffer(OHCI_TD* Td, uint8_t* Buffer, int Length, bool bIsWrite);
|
||||
// read/write the user buffer pointed to by a ISO TD from/to main memory
|
||||
bool OHCI_CopyIsoTD(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffer, int Length, bool bIsWrite);
|
||||
// find a TD buffer in memory and copy it
|
||||
bool OHCI_FindAndCopyTD(xbaddr Paddr, uint8_t* Buffer, int Length, bool bIsWrite);
|
||||
bool OHCI_CopyIsoTDBuffer(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffer, int Length, bool bIsWrite);
|
||||
// process an ED list. Returns nonzero if active TD was found
|
||||
int OHCI_ServiceEDlist(xbaddr Head, int Completion);
|
||||
// process a TD. Returns nonzero to terminate processing of this endpoint
|
||||
|
|
|
@ -57,7 +57,6 @@ namespace xboxkrnl
|
|||
#define SETUP_STATE_SETUP 1
|
||||
#define SETUP_STATE_DATA 2
|
||||
#define SETUP_STATE_ACK 3
|
||||
#define SETUP_STATE_PARAM 4
|
||||
|
||||
|
||||
void USBDevice::Init()
|
||||
|
@ -82,7 +81,7 @@ uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
|
|||
// barIndex must be zero since we created the USB devices with a zero index in Init()
|
||||
assert(barIndex == 0);
|
||||
|
||||
// read the register of the corresponding HC
|
||||
// read the register of the HC
|
||||
return m_HostController->OHCI_ReadRegister(addr);
|
||||
}
|
||||
|
||||
|
@ -91,7 +90,7 @@ void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned
|
|||
// barIndex must be zero since we created the USB devices with a zero index in Init()
|
||||
assert(barIndex == 0);
|
||||
|
||||
// write the register of the corresponding HC
|
||||
// write the register of the HC
|
||||
m_HostController->OHCI_WriteRegister(addr, value);
|
||||
}
|
||||
|
||||
|
@ -196,18 +195,16 @@ USBEndpoint* USBDevice::USB_GetEP(XboxDeviceState* Dev, int Pid, int Ep)
|
|||
return eps + Ep - 1;
|
||||
}
|
||||
|
||||
void USBDevice::USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, unsigned int Stream,
|
||||
uint64_t Id, bool ShortNotOK, bool IntReq)
|
||||
void USBDevice::USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, uint64_t Id,
|
||||
bool ShortNotOK, bool IntReq)
|
||||
{
|
||||
assert(!USB_IsPacketInflight(p));
|
||||
assert(p->IoVec.IoVecStruct != nullptr);
|
||||
p->Id = Id;
|
||||
p->Pid = Pid;
|
||||
p->Endpoint = Ep;
|
||||
p->Stream = Stream;
|
||||
p->Status = USB_RET_SUCCESS;
|
||||
p->ActualLength = 0;
|
||||
p->Parameter = 0ULL;
|
||||
p->ShortNotOK = ShortNotOK;
|
||||
p->IntReq = IntReq;
|
||||
IoVecReset(&p->IoVec);
|
||||
|
@ -241,7 +238,7 @@ void USBDevice::USB_HandlePacket(XboxDeviceState* dev, USBPacket* p)
|
|||
p->Endpoint->Halted = false;
|
||||
}
|
||||
|
||||
if (QTAILQ_EMPTY(&p->Endpoint->Queue) || p->Endpoint->Pipeline || p->Stream) {
|
||||
if (QTAILQ_EMPTY(&p->Endpoint->Queue)) {
|
||||
USB_ProcessOne(p);
|
||||
if (p->Status == USB_RET_ASYNC) {
|
||||
// hcd drivers cannot handle async for isoc
|
||||
|
@ -257,8 +254,7 @@ void USBDevice::USB_HandlePacket(XboxDeviceState* dev, USBPacket* p)
|
|||
else {
|
||||
// When pipelining is enabled usb-devices must always return async,
|
||||
// otherwise packets can complete out of order!
|
||||
assert(p->Stream || !p->Endpoint->Pipeline ||
|
||||
QTAILQ_EMPTY(&p->Endpoint->Queue));
|
||||
assert(QTAILQ_EMPTY(&p->Endpoint->Queue));
|
||||
if (p->Status != USB_RET_NAK) {
|
||||
p->State = USB_PACKET_COMPLETE;
|
||||
}
|
||||
|
@ -282,7 +278,7 @@ void USBDevice::USB_PacketCheckState(USBPacket* p, USBPacketState expected)
|
|||
return;
|
||||
}
|
||||
|
||||
EmuLog(LOG_LEVEL::WARNING, "packet state check failed!");
|
||||
EmuLog(LOG_LEVEL::WARNING, "Packet state check failed!");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
|
@ -298,10 +294,6 @@ void USBDevice::USB_ProcessOne(USBPacket* p)
|
|||
if (p->Endpoint->Num == 0) {
|
||||
// Info: All devices must support endpoint zero. This is the endpoint which receives all of the devices control
|
||||
// and status requests during enumeration and throughout the duration while the device is operational on the bus
|
||||
if (p->Parameter) {
|
||||
USB_DoParameter(dev, p);
|
||||
return;
|
||||
}
|
||||
switch (p->Pid) {
|
||||
case USB_TOKEN_SETUP: {
|
||||
USB_DoTokenSetup(dev, p);
|
||||
|
@ -328,51 +320,11 @@ void USBDevice::USB_ProcessOne(USBPacket* p)
|
|||
}
|
||||
}
|
||||
|
||||
void USBDevice::USB_DoParameter(XboxDeviceState* s, USBPacket* p)
|
||||
{
|
||||
int i, request, value, index;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
s->SetupBuffer[i] = p->Parameter >> (i * 8);
|
||||
}
|
||||
|
||||
s->SetupState = SETUP_STATE_PARAM;
|
||||
s->SetupLength = (s->SetupBuffer[7] << 8) | s->SetupBuffer[6];
|
||||
s->SetupIndex = 0;
|
||||
|
||||
request = (s->SetupBuffer[0] << 8) | s->SetupBuffer[1];
|
||||
value = (s->SetupBuffer[3] << 8) | s->SetupBuffer[2];
|
||||
index = (s->SetupBuffer[5] << 8) | s->SetupBuffer[4];
|
||||
|
||||
if (s->SetupLength > sizeof(s->DataBuffer)) {
|
||||
DBG_PRINTF("ctrl buffer too small (%d > %zu)\n", s->SetupLength, sizeof(s->DataBuffer));
|
||||
p->Status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->Pid == USB_TOKEN_OUT) {
|
||||
USB_PacketCopy(p, s->DataBuffer, s->SetupLength);
|
||||
}
|
||||
|
||||
USB_DeviceHandleControl(s, p, request, value, index, s->SetupLength, s->DataBuffer);
|
||||
if (p->Status == USB_RET_ASYNC) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->ActualLength < s->SetupLength) {
|
||||
s->SetupLength = p->ActualLength;
|
||||
}
|
||||
if (p->Pid == USB_TOKEN_IN) {
|
||||
p->ActualLength = 0;
|
||||
USB_PacketCopy(p, s->DataBuffer, s->SetupLength);
|
||||
}
|
||||
}
|
||||
|
||||
void USBDevice::USB_DoTokenSetup(XboxDeviceState* s, USBPacket* p)
|
||||
{
|
||||
int request, value, index;
|
||||
|
||||
// From the standard "Every Setup packet has eight bytes."
|
||||
// From the USB 1.1 standard "Every Setup packet has eight bytes."
|
||||
if (p->IoVec.Size != 8) {
|
||||
p->Status = USB_RET_STALL;
|
||||
return;
|
||||
|
@ -410,7 +362,7 @@ void USBDevice::USB_DoTokenSetup(XboxDeviceState* s, USBPacket* p)
|
|||
}
|
||||
else {
|
||||
if (s->SetupLength > sizeof(s->DataBuffer)) {
|
||||
DBG_PRINTF("ctrl buffer too small (%d > %zu)\n", s->SetupLength, sizeof(s->DataBuffer));
|
||||
DBG_PRINTF("CTRL buffer too small (%d > %zu)\n", s->SetupLength, sizeof(s->DataBuffer));
|
||||
p->Status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
@ -700,12 +652,9 @@ void USBDevice::USB_EpReset(XboxDeviceState* dev)
|
|||
dev->EP_ctl.IfNum = 0;
|
||||
dev->EP_ctl.MaxPacketSize = 64;
|
||||
dev->EP_ctl.Dev = dev;
|
||||
dev->EP_ctl.Pipeline = false;
|
||||
for (int ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
|
||||
dev->EP_in[ep].Num = ep + 1;
|
||||
dev->EP_out[ep].Num = ep + 1;
|
||||
dev->EP_in[ep].pid = USB_TOKEN_IN;
|
||||
dev->EP_out[ep].pid = USB_TOKEN_OUT;
|
||||
dev->EP_in[ep].Type = USB_ENDPOINT_XFER_INVALID;
|
||||
dev->EP_out[ep].Type = USB_ENDPOINT_XFER_INVALID;
|
||||
dev->EP_in[ep].IfNum = USB_INTERFACE_INVALID;
|
||||
|
@ -714,8 +663,6 @@ void USBDevice::USB_EpReset(XboxDeviceState* dev)
|
|||
dev->EP_out[ep].MaxPacketSize = 0;
|
||||
dev->EP_in[ep].Dev = dev;
|
||||
dev->EP_out[ep].Dev = dev;
|
||||
dev->EP_in[ep].Pipeline = false;
|
||||
dev->EP_out[ep].Pipeline = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -779,7 +726,7 @@ void USBDevice::USBDesc_SetDefaults(XboxDeviceState* dev)
|
|||
break;
|
||||
}
|
||||
default:
|
||||
EmuLog(LOG_LEVEL::WARNING, "unknown speed parameter %d set in %s", dev->Speed, dev->ProductDesc.c_str());
|
||||
EmuLog(LOG_LEVEL::WARNING, "Unknown speed parameter %d set in %s", dev->Speed, dev->ProductDesc.c_str());
|
||||
}
|
||||
USBDesc_SetConfig(dev, 0);
|
||||
}
|
||||
|
@ -862,14 +809,14 @@ void USBDevice::USBDesc_EpInit(XboxDeviceState* dev)
|
|||
const USBDescIface *iface;
|
||||
int i, e, pid, ep;
|
||||
|
||||
USB_EpInit(dev); // reset endpoints (because we changed descriptors in use?)
|
||||
USB_EpInit(dev); // reset endpoints
|
||||
for (i = 0; i < dev->NumInterfaces; i++) {
|
||||
iface = dev->Ifaces[i];
|
||||
if (iface == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (e = 0; e < iface->bNumEndpoints; e++) {
|
||||
// From the standard:
|
||||
// From the USB 1.1 standard:
|
||||
// "bEndpointAddress:
|
||||
// Bit 3...0: The endpoint number
|
||||
// Bit 6...4: Reserved, reset to zero
|
||||
|
@ -895,16 +842,16 @@ int USBDevice::USBDesc_HandleControl(XboxDeviceState* dev, USBPacket *p,
|
|||
assert(desc != nullptr);
|
||||
switch (request) {
|
||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS: {
|
||||
// From the standard: "This request sets the device address for all future device accesses.
|
||||
// From the USB 1.1 standard: "This request sets the device address for all future device accesses.
|
||||
// The wValue field specifies the device address to use for all subsequent accesses"
|
||||
dev->Addr = value;
|
||||
DBG_PRINTF("address 0x%X set for device %s\n", dev->Addr, dev->ProductDesc.c_str());
|
||||
DBG_PRINTF("Address 0x%X set for device %s\n", dev->Addr, dev->ProductDesc.c_str());
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR: {
|
||||
// From the standard: "This request returns the specified descriptor if the descriptor exists.
|
||||
// From the USB 1.1 standard: "This request returns the specified descriptor if the descriptor exists.
|
||||
// The wValue field specifies the descriptor type in the high byte and the descriptor index in the low byte.
|
||||
// The wIndex field specifies the Language ID for string descriptors or is reset to zero for other descriptors"
|
||||
ret = USBDesc_HandleStandardGetDescriptor(dev, p, value, data, length);
|
||||
|
@ -912,7 +859,7 @@ int USBDevice::USBDesc_HandleControl(XboxDeviceState* dev, USBPacket *p,
|
|||
}
|
||||
|
||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION: {
|
||||
// From the standard: "This request returns the current device configuration value.
|
||||
// From the USB 1.1 standard: "This request returns the current device configuration value.
|
||||
// If the returned value is zero, the device is not configured"
|
||||
data[0] = dev->Config ? dev->Config->bConfigurationValue : 0;
|
||||
p->ActualLength = 1;
|
||||
|
@ -921,16 +868,16 @@ int USBDevice::USBDesc_HandleControl(XboxDeviceState* dev, USBPacket *p,
|
|||
}
|
||||
|
||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: {
|
||||
// From the standard: "This request sets the device configuration. The lower byte of the wValue field specifies the desired configuration.
|
||||
// From the USB 1.1 standard: "This request sets the device configuration. The lower byte of the wValue field specifies the desired configuration.
|
||||
// This configuration value must be zero or match a configuration value from a configuration descriptor"
|
||||
ret = USBDesc_SetConfig(dev, value);
|
||||
DBG_PRINTF("received standard SetConfiguration() request for device at address 0x%X. Configuration selected is %d and returned %d\n",
|
||||
DBG_PRINTF("Received standard SetConfiguration() request for device at address 0x%X. Configuration selected is %d and returned %d\n",
|
||||
dev->Addr, value, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceRequest | USB_REQ_GET_STATUS: {
|
||||
// From the standard: "This request returns the status for the specified recipient. The Recipient bits of the bmRequestType field
|
||||
// From the USB 1.1 standard: "This request returns the status for the specified recipient. The Recipient bits of the bmRequestType field
|
||||
// specify the desired recipient. The data returned is the current status of the specified recipient."
|
||||
// From XQEMU:
|
||||
/* Default state: Device behavior when this request is received while
|
||||
|
@ -952,31 +899,31 @@ int USBDevice::USBDesc_HandleControl(XboxDeviceState* dev, USBPacket *p,
|
|||
}
|
||||
|
||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: {
|
||||
// From the standard: "This request is used to clear or disable a specific feature.
|
||||
// From the USB 1.1 standard: "This request is used to clear or disable a specific feature.
|
||||
// Feature selector values in wValue must be appropriate to the recipient"
|
||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||
dev->RemoteWakeup = 0;
|
||||
ret = 0;
|
||||
}
|
||||
DBG_PRINTF("received standard ClearFeature() request for device at address 0x%X. Feature selected is %d and returned %d\n",
|
||||
DBG_PRINTF("Received standard ClearFeature() request for device at address 0x%X. Feature selected is %d and returned %d\n",
|
||||
dev->Addr, value, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceOutRequest | USB_REQ_SET_FEATURE: {
|
||||
// From the standard: "This request is used to set or enable a specific feature.
|
||||
// From the USB 1.1 standard: "This request is used to set or enable a specific feature.
|
||||
// Feature selector values in wValue must be appropriate to the recipient"
|
||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||
dev->RemoteWakeup = 1;
|
||||
ret = 0;
|
||||
}
|
||||
DBG_PRINTF("received standard SetFeature() request for device at address 0x%X. Feature selected is %d and returned %d\n",
|
||||
DBG_PRINTF("Received standard SetFeature() request for device at address 0x%X. Feature selected is %d and returned %d\n",
|
||||
dev->Addr, value, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
case InterfaceRequest | USB_REQ_GET_INTERFACE: {
|
||||
// From the standard: "This request returns the selected alternate setting for the specified interface.
|
||||
// From the USB 1.1 standard: "This request returns the selected alternate setting for the specified interface.
|
||||
// wValue = Zero; wIndex = Interface"
|
||||
if (index < 0 || index >= dev->NumInterfaces) {
|
||||
break;
|
||||
|
@ -988,10 +935,10 @@ int USBDevice::USBDesc_HandleControl(XboxDeviceState* dev, USBPacket *p,
|
|||
}
|
||||
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE: {
|
||||
// From the standard: "This request allows the host to select an alternate setting for the specified interface"
|
||||
// From the USB 1.1 standard: "This request allows the host to select an alternate setting for the specified interface"
|
||||
// wValue = Alternative Setting; wIndex = Interface
|
||||
ret = USBDesc_SetInterface(dev, index, value);
|
||||
DBG_PRINTF("received standard SetInterface() request for device at address 0x%X. Interface selected is %d, Alternative Setting \
|
||||
DBG_PRINTF("Received standard SetInterface() request for device at address 0x%X. Interface selected is %d, Alternative Setting \
|
||||
is %d and returned %d\n", dev->Addr, index, value, ret);
|
||||
break;
|
||||
}
|
||||
|
@ -1014,12 +961,12 @@ int USBDevice::USBDesc_HandleStandardGetDescriptor(XboxDeviceState* dev, USBPack
|
|||
|
||||
// Dropped from XQEMU bcdUSB check for usb 3.0 devices
|
||||
|
||||
// From the standard: "The standard request to a device supports three types of descriptors: DEVICE, CONFIGURATION, and STRING."
|
||||
// From the USB 1.1 standard: "The standard request to a device supports three types of descriptors: DEVICE, CONFIGURATION, and STRING."
|
||||
|
||||
switch (type) {
|
||||
case USB_DT_DEVICE: {
|
||||
ret = USB_ReadDeviceDesc(&desc->id, dev->Device, buf, sizeof(buf));
|
||||
DBG_PRINTF("read operation of device descriptor of device 0x%X returns %d\n", dev->Addr, ret);
|
||||
DBG_PRINTF("Read operation of device descriptor of device 0x%X returns %d\n", dev->Addr, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1027,13 +974,13 @@ int USBDevice::USBDesc_HandleStandardGetDescriptor(XboxDeviceState* dev, USBPack
|
|||
if (index < dev->Device->bNumConfigurations) {
|
||||
ret = USB_ReadConfigurationDesc(dev->Device->confs + index, flags, buf, sizeof(buf));
|
||||
}
|
||||
DBG_PRINTF("read operation of configuration descriptor %d of device 0x%X returns %d\n", index, dev->Addr, ret);
|
||||
DBG_PRINTF("Read operation of configuration descriptor %d of device 0x%X returns %d\n", index, dev->Addr, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_DT_STRING: {
|
||||
ret = USB_ReadStringDesc(dev, index, buf, sizeof(buf));
|
||||
DBG_PRINTF("read operation of string descriptor %d of device 0x%X returns %d\n", index, dev->Addr, ret);
|
||||
DBG_PRINTF("Read operation of string descriptor %d of device 0x%X returns %d\n", index, dev->Addr, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1101,7 +1048,7 @@ int USBDevice::USB_ReadConfigurationDesc(const USBDescConfig* conf, int flags, u
|
|||
return -1;
|
||||
}
|
||||
|
||||
// From the standard: "A request for a configuration descriptor returns the configuration descriptor, all interface
|
||||
// From the USB 1.1 standard: "A request for a configuration descriptor returns the configuration descriptor, all interface
|
||||
// descriptors, and endpoint descriptors for all of the interfaces in a single request."
|
||||
|
||||
d->bLength = bLength;
|
||||
|
@ -1137,7 +1084,7 @@ int USBDevice::USB_ReadInterfaceDesc(const USBDescIface* iface, int flags, uint8
|
|||
return -1;
|
||||
}
|
||||
|
||||
// From the standard: "The first interface descriptor follows the configuration descriptor.
|
||||
// From the USB 1.1 standard: "The first interface descriptor follows the configuration descriptor.
|
||||
// The endpoint descriptors for the first interface follow the first interface descriptor.
|
||||
// If there are additional interfaces, their interface descriptor and endpoint descriptors
|
||||
// follow the first interface’s endpoint descriptors. Class-specific and/or vendor-specific
|
||||
|
@ -1209,7 +1156,7 @@ int USBDevice::USB_ReadEndpointDesc(const USBDescEndpoint* ep, int flags, uint8_
|
|||
d->u.endpoint.bSynchAddress = ep->bSynchAddress;
|
||||
}
|
||||
|
||||
// Dropped from XQEMU the reading of SuperSpeed Endpoint Companion descriptors since those are usb 3.0 specific
|
||||
// Dropped from XQEMU the reading of the SuperSpeed Endpoint Companion descriptors since those are usb 3.0 specific
|
||||
|
||||
if (ep->extra) {
|
||||
std::memcpy(dest + bLength, ep->extra, extralen);
|
||||
|
@ -1228,7 +1175,7 @@ int USBDevice::USB_ReadStringDesc(XboxDeviceState* dev, int index, uint8_t* dest
|
|||
return -1;
|
||||
}
|
||||
|
||||
// From the standard: "String index zero for all languages returns a string descriptor
|
||||
// From the USB 1.1 standard: "String index zero for all languages returns a string descriptor
|
||||
// that contains an array of two-byte LANGID codes supported by the device"
|
||||
|
||||
if (index == 0) {
|
||||
|
@ -1245,7 +1192,7 @@ int USBDevice::USB_ReadStringDesc(XboxDeviceState* dev, int index, uint8_t* dest
|
|||
return 0;
|
||||
}
|
||||
|
||||
// From the standard: "The UNICODE string descriptor is not NULL-terminated. The string length is
|
||||
// From the USB 1.1 standard: "The UNICODE string descriptor is not NULL-terminated. The string length is
|
||||
// computed by subtracting two from the value of the first byte of the descriptor"
|
||||
|
||||
bLength = std::strlen(str) * 2 + 2;
|
||||
|
|
|
@ -94,8 +94,8 @@ class USBDevice : public PCIDevice {
|
|||
// find the requested endpoint in the supplied device
|
||||
USBEndpoint* USB_GetEP(XboxDeviceState* Dev, int Pid, int Ep);
|
||||
// setup a packet for transfer
|
||||
void USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, unsigned int Stream,
|
||||
uint64_t Id, bool ShortNotOK, bool IntReq);
|
||||
void USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, uint64_t Id,
|
||||
bool ShortNotOK, bool IntReq);
|
||||
// check if the state of the packet is queued or async
|
||||
bool USB_IsPacketInflight(USBPacket* p);
|
||||
// append the user buffer to the packet
|
||||
|
@ -106,8 +106,6 @@ class USBDevice : public PCIDevice {
|
|||
void USB_PacketCheckState(USBPacket* p, USBPacketState expected);
|
||||
// process the packet
|
||||
void USB_ProcessOne(USBPacket* p);
|
||||
// xhci only?
|
||||
void USB_DoParameter(XboxDeviceState* s, USBPacket* p);
|
||||
// process a setup token
|
||||
void USB_DoTokenSetup(XboxDeviceState* s, USBPacket* p);
|
||||
// process an input token
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
// Acknowledgment: some the functions present are from XQEMU (GPLv2)
|
||||
// Acknowledgment: some of the functions present are from XQEMU (GPLv2)
|
||||
// https://xqemu.com/
|
||||
|
||||
#ifndef USBCOMMON_H_
|
||||
|
@ -316,11 +316,9 @@ struct USBDescriptor {
|
|||
/* USB endpoint */
|
||||
struct USBEndpoint {
|
||||
uint8_t Num; // endpoint number
|
||||
uint8_t pid;
|
||||
uint8_t Type; // the type of this endpoint
|
||||
uint8_t IfNum; // interface number this endpoint belongs to
|
||||
int MaxPacketSize; // maximum packet size supported by this endpoint
|
||||
bool Pipeline;
|
||||
bool Halted; // indicates that the endpoint is halted
|
||||
XboxDeviceState* Dev; // device this endpoint belongs to
|
||||
QTAILQ_HEAD(, USBPacket) Queue; // queue of packets to this endpoint
|
||||
|
@ -403,9 +401,9 @@ struct XboxDeviceState {
|
|||
int32_t SetupLength; // this field specifies the length of the data transferred during the second phase of the control transfer
|
||||
int32_t SetupIndex; // index of the parameter in a setup token?
|
||||
|
||||
USBEndpoint EP_ctl; // endpoints for SETUP tokens
|
||||
USBEndpoint EP_in[USB_MAX_ENDPOINTS]; // endpoints for OUT tokens
|
||||
USBEndpoint EP_out[USB_MAX_ENDPOINTS]; // endpoints for IN tokens
|
||||
USBEndpoint EP_ctl; // control endpoint
|
||||
USBEndpoint EP_in[USB_MAX_ENDPOINTS]; // device endpoint (input direction)
|
||||
USBEndpoint EP_out[USB_MAX_ENDPOINTS]; // device endpoint (output direction)
|
||||
|
||||
QLIST_HEAD(, USBDescString) Strings; // strings of the string descriptors
|
||||
const USBDesc* UsbDesc; // Overrides class usb_desc if not nullptr
|
||||
|
@ -420,12 +418,10 @@ struct XboxDeviceState {
|
|||
|
||||
/* Structure used to hold information about an active USB packet */
|
||||
struct USBPacket {
|
||||
int Pid; // Packet ID (used to identify the type of packet that is being sent)
|
||||
int Pid; // Packet ID (used to identify the type of packet that is being processed)
|
||||
uint32_t Id; // Paddr of the TD for this packet
|
||||
USBEndpoint* Endpoint; // endpoint this packet is transferred to
|
||||
unsigned int Stream;
|
||||
IOVector IoVec; // used to perform vectored I/O
|
||||
uint64_t Parameter; // this seems to be used only in xhci and it's 0 otherwise. If so, this can be removed
|
||||
bool ShortNotOK; // the bufferRounding mode of the TD for this packet
|
||||
bool IntReq; // whether or not to generate an interrupt for this packet (DelayInterrupt of the TD is zero)
|
||||
int Status; // USB_RET_* status code
|
||||
|
|
|
@ -312,7 +312,7 @@ int XidGamepad::UsbXid_Initfn(XboxDeviceState* dev)
|
|||
void XidGamepad::UsbXid_HandleDestroy()
|
||||
{
|
||||
UsbXidReleasePort(&m_XidState->dev);
|
||||
XidCleanUp();
|
||||
XpadCleanUp();
|
||||
}
|
||||
|
||||
void XidGamepad::UsbXid_Attach(XboxDeviceState* dev)
|
||||
|
@ -328,7 +328,7 @@ void XidGamepad::UsbXid_Attach(XboxDeviceState* dev)
|
|||
|
||||
void XidGamepad::UsbXid_HandleReset()
|
||||
{
|
||||
DBG_PRINTF("reset event\n");
|
||||
DBG_PRINTF("Reset event\n");
|
||||
}
|
||||
|
||||
void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
||||
|
@ -336,7 +336,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
{
|
||||
int ret = m_UsbDev->USBDesc_HandleControl(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
DBG_PRINTF("handled by USBDesc_HandleControl, ret is %d\n", ret);
|
||||
DBG_PRINTF("Handled by USBDesc_HandleControl, ret is %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
// From the HID standard: "The Get_Report request allows the host to receive a report via the Control pipe.
|
||||
// The wValue field specifies the Report Type in the high byte and the Report ID in the low byte. Set Report ID
|
||||
// to 0 (zero) if Report IDs are not used. 01 = input, 02 = output, 03 = feature, 04-FF = reserved"
|
||||
DBG_PRINTF("GET_REPORT 0x%X\n", value);
|
||||
DBG_PRINTF("GET_REPORT xpad request 0x%X\n", value);
|
||||
// JayFoxRox's analysis: "This 0x0100 case is for input.
|
||||
// This is the case where the Xbox wants to read input data from the controller.
|
||||
// Confirmed with a real Duke controller :
|
||||
|
@ -387,7 +387,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
// setting the state of input, output, or feature controls. The meaning of the request fields for the Set_Report
|
||||
// request is the same as for the Get_Report request, however the data direction is reversed and the Report
|
||||
// Data is sent from host to device."
|
||||
DBG_PRINTF("SET_REPORT 0x%X\n", value);
|
||||
DBG_PRINTF("SET_REPORT xpad request 0x%X\n", value);
|
||||
// JayFoxRox's analysis: "The 0x0200 case below is for output.
|
||||
// This is the case where the Xbox wants to write rumble data to the controller.
|
||||
// To my knowledge :
|
||||
|
@ -417,7 +417,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
|
||||
// XID-specific requests
|
||||
case VendorInterfaceRequest | USB_REQ_GET_DESCRIPTOR: {
|
||||
DBG_PRINTF("GET_DESCRIPTOR 0x%x\n", value);
|
||||
DBG_PRINTF("GET_DESCRIPTOR xpad request 0x%x\n", value);
|
||||
if (value == 0x4200) {
|
||||
assert(m_XidState->xid_desc->bLength <= length);
|
||||
std::memcpy(data, m_XidState->xid_desc, m_XidState->xid_desc->bLength);
|
||||
|
@ -431,7 +431,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
}
|
||||
|
||||
case VendorInterfaceRequest | XID_GET_CAPABILITIES: {
|
||||
DBG_PRINTF("XID_GET_CAPABILITIES 0x%x\n", value);
|
||||
DBG_PRINTF("XID_GET_CAPABILITIES xpad request 0x%x\n", value);
|
||||
if (value == 0x0100) {
|
||||
if (length > m_XidState->in_state_capabilities.bLength) {
|
||||
length = m_XidState->in_state_capabilities.bLength;
|
||||
|
@ -455,7 +455,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
|
||||
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8) | USB_REQ_GET_DESCRIPTOR: {
|
||||
/* FIXME: ! */
|
||||
DBG_PRINTF("unknown xpad request 0x%X: value = 0x%X\n", request, value);
|
||||
DBG_PRINTF("Unknown xpad request 0x%X: value = 0x%X\n", request, value);
|
||||
std::memset(data, 0x00, length);
|
||||
//FIXME: Intended for the hub: usbd_get_hub_descriptor, UT_READ_CLASS?!
|
||||
p->Status = USB_RET_STALL;
|
||||
|
@ -465,7 +465,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|||
|
||||
case ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT) << 8) | USB_REQ_CLEAR_FEATURE: {
|
||||
/* FIXME: ! */
|
||||
DBG_PRINTF("unknown xpad request 0x%X: value = 0x%X\n", request, value);
|
||||
DBG_PRINTF("Unknown xpad request 0x%X: value = 0x%X\n", request, value);
|
||||
std::memset(data, 0x00, length);
|
||||
p->Status = USB_RET_STALL;
|
||||
break;
|
||||
|
@ -530,7 +530,7 @@ void XidGamepad::UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p)
|
|||
}
|
||||
}
|
||||
|
||||
void XidGamepad::XidCleanUp()
|
||||
void XidGamepad::XpadCleanUp()
|
||||
{
|
||||
delete m_pPeripheralFuncStruct;
|
||||
delete m_XidState;
|
||||
|
|
|
@ -49,7 +49,7 @@ class XidGamepad
|
|||
// initialize this peripheral
|
||||
int Init(int port);
|
||||
// destroy gamepad resources
|
||||
void XidCleanUp();
|
||||
void XpadCleanUp();
|
||||
|
||||
|
||||
private:
|
||||
|
|
Loading…
Reference in New Issue