Merge pull request #1538 from ergo720/fix_lle_usb

Functional LLE USB
This commit is contained in:
Luke Usher 2019-01-28 15:46:28 +00:00 committed by GitHub
commit e463f473b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 341 additions and 317 deletions

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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; }

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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 interfaces 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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -49,7 +49,7 @@ class XidGamepad
// initialize this peripheral
int Init(int port);
// destroy gamepad resources
void XidCleanUp();
void XpadCleanUp();
private: