Sync OHCI code up to Cxbx-R commit 169806b2

This is the latest OHCI related code. Nothing related to SDL2 was ported. Needs major improvements.
This commit is contained in:
StrikerX3 2018-12-03 01:58:23 -02:00
parent b81de821a9
commit e1c24c52b9
9 changed files with 398 additions and 295 deletions

View file

@ -62,7 +62,7 @@ uint64_t GetTime_NS(TimerObject* Timer) {
// Calculates the next expire time of the timer
static inline uint64_t GetNextExpireTime(TimerObject* Timer) {
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load() * SCALE_MS;
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load();
}
// Deallocates the memory of the timer
@ -123,6 +123,7 @@ TimerObject* Timer_Create(pTimerCB Callback, void* Arg, unsigned int Factor) {
}
// Starts the timer
// Expire_MS must be expressed in NS
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS) {
Timer->ExpireTime_MS.store(Expire_MS);
std::thread(ClockThread, Timer).detach();

View file

@ -140,7 +140,7 @@ static inline uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c) {
#define OHCI_ED_EN_MASK (0xF<<OHCI_ED_EN_SHIFT) // EndpointNumber
#define OHCI_ED_D_SHIFT 11
#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT) // Direction
#define OHCI_ED_S (1<<13)
#define OHCI_ED_S (1<<13) // Speed
#define OHCI_ED_K (1<<14) // sKip
#define OHCI_ED_F (1<<15) // Format
#define OHCI_ED_MPS_SHIFT 16
@ -148,7 +148,7 @@ static inline uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c) {
/* Flags in the HeadP field of an ED */
#define OHCI_ED_H 1 // Halted
#define OHCI_ED_C 2
#define OHCI_ED_C 2 // toggleCarry
/* Bitfields for the first word of a TD */
#define OHCI_TD_R (1<<18) // bufferRounding
@ -159,7 +159,7 @@ static inline uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c) {
#define OHCI_TD_T0 (1<<24)
#define OHCI_TD_T1 (1<<25) // DataToggle (T0 and T1)
#define OHCI_TD_EC_SHIFT 26
#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT) // ErrorCount
#define OHCI_TD_CC_SHIFT 28
#define OHCI_TD_CC_MASK (0xF<<OHCI_TD_CC_SHIFT) // ConditionCode
/* Bitfields for the first word of an Isochronous Transfer Desciptor. */
@ -233,15 +233,15 @@ OHCI::OHCI(Cpu* cpu, int Irq, USBPCIDevice* UsbObj)
}
if (m_IrqNum == 9) {
offset = 2;
offset = 4;
}
for (int i = 0; i < 2; i++) {
for (int i = 0; i < 4; i++) {
m_UsbDevice->USB_RegisterPort(&m_Registers.RhPort[i].UsbPort, i + offset, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, ops);
}
OHCI_PacketInit(&m_UsbPacket);
m_UsbFrameTime = 1000000ULL; // 1 ms
m_UsbFrameTime = 1000000ULL; // 1 ms expressed in ns
m_TicksPerUsbTick = 1000000000ULL / USB_HZ; // 83
// Do a hardware reset
@ -294,7 +294,8 @@ void OHCI::OHCI_FrameBoundaryWorker()
// From the standard: "This bit is loaded from the FrameIntervalToggle field of
// HcFmInterval whenever FrameRemaining reaches 0."
m_Registers.HcFmRemaining = (m_Registers.HcFmRemaining & ~OHCI_FMR_FRT) | (m_Registers.HcFmInterval & OHCI_FMI_FIT);
m_Registers.HcFmRemaining = (m_Registers.HcFmInterval & OHCI_FMI_FIT) == 0 ?
m_Registers.HcFmRemaining & ~OHCI_FMR_FRT : m_Registers.HcFmRemaining | OHCI_FMR_FRT;
// Increment frame number
m_Registers.HcFmNumber = (m_Registers.HcFmNumber + 1) & 0xFFFF; // prevent overflow
@ -341,7 +342,7 @@ void OHCI::OHCI_FatalError()
{
// According to the standard, an OHCI will stop operating, and set itself into error state
// (which can be queried by MMIO). Instead of calling directly CxbxKrnlCleanup, we let the
// HCD know the problem so it can try to solve it
// HCD know the problem so that it can try to solve it
OHCI_SetInterrupt(OHCI_INTR_UE);
OHCI_BusStop();
@ -618,7 +619,7 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
addr = Ed->HeadP & OHCI_DPTR_MASK;
// See if this TD has already been submitted to the device
completion = (addr == m_AsyncTD);
if (completion && !m_AsyncComplete) { // ??
if (completion && !m_AsyncComplete) {
#ifdef DEBUG_PACKET
log_spew("OHCI: Skipping async TD\n");
#endif
@ -859,7 +860,7 @@ XboxDeviceState* OHCI::OHCI_FindDevice(uint8_t Addr)
XboxDeviceState* dev;
int i;
for (i = 0; i < 2; i++) {
for (i = 0; i < 4; i++) {
if ((m_Registers.RhPort[i].HcRhPortStatus & OHCI_PORT_PES) == 0) {
continue; // port is disabled
}
@ -881,13 +882,8 @@ void OHCI::OHCI_StateReset()
m_OldHcControl = 0;
// Reset all registers
// Remark: the standard says that RemoteWakeupConnected bit should be set during POST, cleared during hw reset
// and ignored during a sw reset. However, VBox sets it on hw reset and XQEMU clears it. Considering that the Xbox
// doesn't do POST, I will clear it.
m_Registers.HcRevision = 0x10;
m_Registers.HcControl = 0;
m_Registers.HcControl &= ~OHCI_CTL_HCFS;
m_Registers.HcControl |= Reset;
m_Registers.HcCommandStatus = 0;
m_Registers.HcInterruptStatus = 0;
m_Registers.HcInterrupt = OHCI_INTR_MIE; // enable interrupts
@ -906,13 +902,13 @@ void OHCI::OHCI_StateReset()
m_Registers.HcPeriodicStart = 0;
m_Registers.HcLSThreshold = OHCI_LS_THRESH;
m_Registers.HcRhDescriptorA = OHCI_RHA_NPS | 2; // The xbox lacks the hw to switch off the power on the ports and has 2 ports per HC
m_Registers.HcRhDescriptorA = OHCI_RHA_NOCP | OHCI_RHA_NPS | 4; // The xbox lacks the hw to switch off the power on the ports and has 4 ports per HC
m_Registers.HcRhDescriptorB = 0; // The attached devices are removable and use PowerSwitchingMode to control the power on the ports
m_Registers.HcRhStatus = 0;
m_DoneCount = 7;
for (int i = 0; i < 2; i++)
for (int i = 0; i < 4; i++)
{
OHCIPort* Port = &m_Registers.RhPort[i];
Port->HcRhPortStatus = 0;
@ -1109,6 +1105,14 @@ uint32_t OHCI::OHCI_ReadRegister(uint32_t Addr)
ret = m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS;
break;
case 23: // RhPort 2
ret = m_Registers.RhPort[2].HcRhPortStatus | OHCI_PORT_PPS;
break;
case 24: // RhPort 3
ret = m_Registers.RhPort[3].HcRhPortStatus | OHCI_PORT_PPS;
break;
default:
log_warning("OHCI: Read register operation with bad offset %u. Ignoring.\n", Addr >> 2);
}
@ -1240,6 +1244,14 @@ void OHCI::OHCI_WriteRegister(uint32_t Addr, uint32_t Value)
OHCI_PortSetStatus(1, Value);
break;
case 23: // RhPort 2
OHCI_PortSetStatus(2, Value);
break;
case 24: // RhPort 3
OHCI_PortSetStatus(3, Value);
break;
default:
log_warning("OHCI: Write register operation with bad offset %u. Ignoring.\n", Addr >> 2);
}
@ -1250,11 +1262,9 @@ void OHCI::OHCI_UpdateInterrupt()
{
// TODO: interrupts
if ((m_Registers.HcInterrupt & OHCI_INTR_MIE) && (m_Registers.HcInterruptStatus & m_Registers.HcInterrupt)) {
//HalSystemInterrupts[m_IrqNum].Assert(false);
//HalSystemInterrupts[m_IrqNum].Assert(true);
}
else {
//HalSystemInterrupts[m_IrqNum].Assert(false);
}
}
void OHCI::OHCI_SetInterrupt(uint32_t Value)
@ -1291,7 +1301,7 @@ void OHCI::OHCI_StopEndpoints()
XboxDeviceState* dev;
int i, j;
for (i = 0; i < 2; i++) {
for (i = 0; i < 4; i++) {
dev = m_Registers.RhPort[i].UsbPort.Dev;
if (dev && dev->Attached) {
m_UsbDevice->USB_DeviceEPstopped(dev, &dev->EP_ctl);
@ -1317,7 +1327,7 @@ void OHCI::OHCI_SetHubStatus(uint32_t Value)
if (Value & OHCI_RHS_LPS) {
int i;
for (i = 0; i < 2; i++) {
for (i = 0; i < 4; i++) {
OHCI_PortPower(i, 0);
}
log_debug("OHCI: powered down all ports\n");
@ -1326,7 +1336,7 @@ void OHCI::OHCI_SetHubStatus(uint32_t Value)
if (Value & OHCI_RHS_LPSC) {
int i;
for (i = 0; i < 2; i++) {
for (i = 0; i < 4; i++) {
OHCI_PortPower(i, 1);
}
log_debug("OHCI: powered up all ports\n");
@ -1525,7 +1535,7 @@ void OHCI::OHCI_AsyncCancelDevice(XboxDeviceState* dev)
m_UsbDevice->USB_IsPacketInflight(&m_UsbPacket) &&
m_UsbPacket.Endpoint->Dev == dev) {
m_UsbDevice->USB_CancelPacket(&m_UsbPacket);
m_AsyncTD = 0;
m_AsyncTD = NULL;
}
}

View file

@ -56,6 +56,7 @@ using namespace openxbox::cpu;
// EOF: end of frame; the end of a USB-defined frame
// ED: endpoint descriptor; a memory structure used by the HC to communicate with an endpoint
// TD: transfer descriptor; a memory structure used by the HC to transfer a block of data to/from a device endpoint
// HCCA: Host Controller Communications Area; shared memory between the HC and HCD
/* endpoint descriptor */
@ -135,7 +136,7 @@ struct OHCI_Registers {
uint32_t HcRhDescriptorA;
uint32_t HcRhDescriptorB;
uint32_t HcRhStatus;
OHCIPort RhPort[2]; // 2 ports per HC, for a total of 4 USB ports
OHCIPort RhPort[4]; // 4 ports per HC
};
@ -143,7 +144,7 @@ struct OHCI_Registers {
class OHCI {
public:
// Indicates that the timer thread is accessing the OHCI object. Necessary because the input thread from the
// InputDeviceManager will access us when it needs to destroy a device
// InputDeviceManager will access us when it needs to create or destroy a device
std::atomic_bool m_bFrameTime;
// constructor
@ -159,7 +160,7 @@ private:
Cpu* m_cpu;
// pointer to g_USB0 or g_USB1
USBPCIDevice* m_UsbDevice = nullptr;
// all the registers available on the OHCI standard
// all the registers available in the OHCI standard
OHCI_Registers m_Registers;
// end-of-frame timer
TimerObject* m_pEOFtimer = nullptr;
@ -181,8 +182,8 @@ private:
int m_DoneCount;
// the address of the pending TD
uint32_t m_AsyncTD = NULL;
// ergo720: I think it signals that a TD has been processed completely
bool m_AsyncComplete;
// indicates if there is a pending asynchronous packet to process
bool m_AsyncComplete = 0;
// EOF callback wrapper
static void OHCI_FrameBoundaryWrapper(void* pVoid);
@ -248,7 +249,7 @@ private:
// process an isochronous TD
int OHCI_ServiceIsoTD(OHCI_ED* ed, int completion);
// find the usb device with the supplied address
XboxDeviceState* OHCI::OHCI_FindDevice(uint8_t Addr);
XboxDeviceState* OHCI_FindDevice(uint8_t Addr);
// cancel a packet when a device is removed
void OHCI_AsyncCancelDevice(XboxDeviceState* dev);
// Process Control and Bulk lists

View file

@ -158,12 +158,14 @@ typedef enum {
STR_MANUFACTURER = 1,
STR_PRODUCT,
STR_SERIALNUMBER,
};
} STRING_DESC_INDEX;
// Forward declarations
struct USBPacket;
struct USBPort;
struct USBDeviceClass;
struct XboxDeviceState;
struct USBPortOps;
/* String descriptor */
struct USBDescString {
@ -206,9 +208,7 @@ struct USBDescIface {
uint8_t ndesc; // number of device-specific class descriptors (if any)
USBDescOther* descs; // pointer to the extra class descriptors
USBDescEndpoint* eps; // endpoints supported by this interface
USBDescIface();
~USBDescIface();
const USBDescEndpoint* eps; // endpoints supported by this interface
};
/*
@ -239,8 +239,6 @@ struct USBDescDevice {
uint8_t bMaxPacketSize0; // maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid)
uint8_t bNumConfigurations; // number of possible configurations
const USBDescConfig* confs; // configurations supported by this device
USBDescDevice();
~USBDescDevice();
};
/* Device descriptor part 2 */
@ -258,7 +256,6 @@ struct USBDesc {
USBDescID id; // id-specific info of the device descriptor
const USBDescDevice* full; // remaining fields of the device descriptor
const char* const* str;
USBDesc();
};
#pragma pack(1)
@ -332,91 +329,13 @@ struct USBEndpoint {
QTAILQ_HEAD(, USBPacket) Queue; // queue of packets to this endpoint
};
/* definition of an Xbox usb device */
struct XboxDeviceState {
USBPort* Port; // usb port struct of this device
int PortPath; // port index to which this device is attached to
uint32_t flags;
USBDeviceClass* klass; // usb class struct of this device
int Speed; // actual speed of the connected device
int SpeedMask; // supported speeds, not in info because it may be variable (hostdevs)
uint8_t Addr; // device function address
std::string ProductDesc; // the friendly name of this device
int Attached; // device is attached
int32_t State; // current state of device
uint8_t SetupBuffer[8]; // setup packet buffer - 8 bytes (control transfers only)
uint8_t DataBuffer[4096]; // buffer where to write the data requested during usb requests
int32_t RemoteWakeup; // wakeup flag
int32_t SetupState; // result of a control transfer processing operation
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
QLIST_HEAD(, USBDescString) Strings; // strings of the string descriptors
const USBDesc* UsbDesc; // Overrides class usb_desc if not nullptr
const USBDescDevice* Device; // device descriptor part 1
int Configuration; // number of the selected configuration descriptor
int NumInterfaces; // number of available interface descriptors
int AltSetting[USB_MAX_INTERFACES]; // alternate setting numbers for the current interface
const USBDescConfig* Config; // configuration descriptor in use
const USBDescIface* Ifaces[USB_MAX_INTERFACES]; // interface in use
};
struct USBCombinedPacket {
USBPacket* First;
QTAILQ_HEAD(packets_head, USBPacket) Packets;
IOVector IoVec;
};
/* 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)
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; // control transfers
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
int ActualLength; // number of bytes actually written to DataBuffer
// Internal use by the USB layer
USBPacketState State;
USBCombinedPacket* Combined;
QTAILQ_ENTRY(USBPacket) Queue;
QTAILQ_ENTRY(USBPacket) CombinedEntry;
};
struct USBPortOps {
std::function<void(USBPort* port)> attach;
std::function<void(USBPort* port)> detach;
/*
* This gets called when a device downstream from the device attached to
* the port (attached through a hub) gets detached.
*/
std::function<void(XboxDeviceState* child)> child_detach;
std::function<void(USBPort* port)> wakeup;
/*
* Note that port->dev will be different then the device from which
* the packet originated when a hub is involved.
*/
std::function<void(USBPort* port, USBPacket* p)> complete;
};
/* Struct describing the status of a usb port */
struct USBPort {
XboxDeviceState* Dev = nullptr; // usb device (if present)
USBPortOps* Operations = nullptr; // functions to call when a port event happens
int SpeedMask; // usb speeds supported
int HubCount; // number of hubs chained
std::string Path; // the number of the port + 1, used to create a serial number for this device
int PortIndex; // internal port index
XboxDeviceState* Dev = nullptr; // usb device(if present)
USBPortOps* Operations; // functions to call when a port event happens
int SpeedMask; // usb speeds supported
std::string Path; // the number of the port + 1, used to create a serial number for this device
int PortIndex; // internal port index
};
/* Struct which stores general functions/variables regarding the peripheral */
@ -467,4 +386,73 @@ struct USBDeviceClass {
const USBDesc* usb_desc; // device descriptor
};
/* definition of an Xbox usb device */
struct XboxDeviceState {
USBPort* Port; // usb port struct of this device
int PortPath; // port index to which this device is attached to
uint32_t flags;
USBDeviceClass* klass; // usb class struct of this device
int Speed; // actual speed of the connected device
int SpeedMask; // supported speeds, not in info because it may be variable (hostdevs)
uint8_t Addr; // device function address
std::string ProductDesc; // the friendly name of this device
int Attached; // device is attached
int32_t State; // current state of device
uint8_t SetupBuffer[8]; // setup packet buffer - 8 bytes (control transfers only)
uint8_t DataBuffer[4096]; // buffer where to write the data requested during usb requests
int32_t RemoteWakeup; // wakeup flag
int32_t SetupState; // result of a control transfer processing operation
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
QLIST_HEAD(, USBDescString) Strings; // strings of the string descriptors
const USBDesc* UsbDesc; // Overrides class usb_desc if not nullptr
const USBDescDevice* Device; // device descriptor part 1
int Configuration; // number of the selected configuration descriptor
int NumInterfaces; // number of available interface descriptors
int AltSetting[USB_MAX_INTERFACES]; // alternate setting numbers for the current interface
const USBDescConfig* Config; // configuration descriptor in use
const USBDescIface* Ifaces[USB_MAX_INTERFACES]; // interface in use
};
/* 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)
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
int ActualLength; // number of bytes actually written to DataBuffer
// Internal use by the USB layer
USBPacketState State;
QTAILQ_ENTRY(USBPacket) Queue;
};
struct USBPortOps {
std::function<void(USBPort* port)> attach;
std::function<void(USBPort* port)> detach;
/*
* This gets called when a device downstream from the device attached to
* the port (attached through a hub) gets detached.
*/
std::function<void(XboxDeviceState* child)> child_detach;
std::function<void(USBPort* port)> wakeup;
/*
* Note that port->dev will be different then the device from which
* the packet originated when a hub is involved.
*/
std::function<void(USBPort* port, USBPacket* p)> complete;
};
}

View file

@ -49,57 +49,61 @@ namespace openxbox {
Hub* g_HubObjArray[4] = { nullptr };
USBDescIface::USBDescIface() {
std::memset(this, 0, sizeof(USBDescIface));
eps = new USBDescEndpoint();
bInterfaceNumber = 0;
bNumEndpoints = 1;
bInterfaceClass = USB_CLASS_HUB;
eps->bEndpointAddress = USB_DIR_IN | 0x01;
eps->bmAttributes = USB_ENDPOINT_XFER_INT;
eps->wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8;
eps->bInterval = 0xFF;
}
static const USBDescEndpoint desc_endp_hub = {
USB_DIR_IN | 0x01, // bEndpointAddress;
USB_ENDPOINT_XFER_INT, // bmAttributes;
1 + (NUM_PORTS + 7) / 8, // wMaxPacketSize;
0xFF, // bInterval;
0, // bRefresh;
0, // bSynchAddress
0, // is_audio
nullptr // extra
};
USBDescIface::~USBDescIface() {
delete eps;
}
static const USBDescIface desc_iface_hub = {
0, // bInterfaceNumber;
0, // bAlternateSetting;
1, // bNumEndpoints;
USB_CLASS_HUB, // bInterfaceClass;
0, // bInterfaceSubClass
0, // bInterfaceProtocol
0, // iInterface
0, // ndesc
nullptr, // descs
&desc_endp_hub
};
static const USBDescIface desc_iface_hub;
static const USBDescConfig desc_config_hub = {
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0xE0, // bmAttributes
0, // bMaxPower
1, // nif
&desc_iface_hub
};
USBDescDevice::USBDescDevice() {
std::memset(this, 0, sizeof(USBDescDevice));
USBDescConfig* pUSBDescConfig = new USBDescConfig();
bcdUSB = 0x0110;
bDeviceClass = USB_CLASS_HUB;
bMaxPacketSize0 = 8;
bNumConfigurations = 1;
pUSBDescConfig->bNumInterfaces = 1;
pUSBDescConfig->bConfigurationValue = 1;
pUSBDescConfig->bmAttributes = 0xE0;
pUSBDescConfig->nif = 1;
pUSBDescConfig->ifs = &desc_iface_hub;
confs = pUSBDescConfig;
}
static const USBDescDevice desc_device_hub = {
0x0110, // bcdUSB
USB_CLASS_HUB, // bDeviceClass
0, // bDeviceSubClass
0, // bDeviceProtocol
8, // bMaxPacketSize0
1, // bNumConfigurations
&desc_config_hub
};
USBDescDevice::~USBDescDevice() {
delete confs;
}
static const USBDescDevice desc_device_hub;
USBDesc::USBDesc() {
std::memset(this, 0, sizeof(USBDesc));
id.idVendor = 0x0409;
id.idProduct = 0x55AA;
id.bcdDevice = 0x0101;
id.iManufacturer = STR_MANUFACTURER;
id.iProduct = STR_PRODUCT;
id.iSerialNumber = STR_SERIALNUMBER;
full = &desc_device_hub;
}
static const USBDesc desc_hub;
static const USBDesc desc_hub = {
{
0x0409, // idVendor
0x55AA, // idProduct
0x0101, // bcdDevice
STR_MANUFACTURER, // iManufacturer
STR_PRODUCT, // iProduct
STR_SERIALNUMBER // iSerialNumber
},
&desc_device_hub
};
// Class-specific hub descriptor. Remember to update DeviceRemovable and PortPwrCtrlMask if you change NUM_PORTS since their values depend on
// the number of downstream ports available on the hub! Also note that this descriptor cannot be put in the descs member of the interface descriptor
@ -125,11 +129,15 @@ int Hub::Init(int port) {
XboxDeviceState* dev = ClassInitFn();
int rc = UsbHubClaimPort(dev, port);
if (rc != 0) {
m_UsbDev->m_HostController->m_bFrameTime = false;
return rc;
}
m_UsbDev->USB_EpInit(dev);
m_UsbDev->USB_DeviceInit(dev);
m_UsbDev->USB_DeviceAttach(dev);
m_UsbDev->m_HostController->m_bFrameTime = false;
return 0;
}
@ -165,24 +173,27 @@ int Hub::UsbHubClaimPort(XboxDeviceState* dev, int port) {
assert(dev->Port == nullptr);
if (port > 2) {
//m_UsbDev = g_USB0; // FIXME: how to retrieve these?
}
else {
//m_UsbDev = g_USB1; // FIXME: how to retrieve these?
}
else {
//m_UsbDev = g_USB0; // FIXME: how to retrieve these?
}
while (m_UsbDev->m_HostController->m_bFrameTime) {}
m_UsbDev->m_HostController->m_bFrameTime = true;
i = 0;
for (auto usb_port : m_UsbDev->m_FreePorts) {
if (usb_port->Path == std::to_string(port)) {
it = m_UsbDev->m_FreePorts.begin() + i;
break;
}
i++;
}
if (i == 2) {
if (it == m_UsbDev->m_FreePorts.end()) {
log_warning("OHCI: Port requested %d not found (in use?)", port);
return -1;
}
it = m_UsbDev->m_FreePorts.begin() + i;
dev->Port = *it;
(*it)->Dev = dev;
m_UsbDev->m_FreePorts.erase(it);

View file

@ -203,10 +203,9 @@ void USBPCIDevice::USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, unsig
p->Stream = Stream;
p->Status = USB_RET_SUCCESS;
p->ActualLength = 0;
p->Parameter = 0;
p->Parameter = 0ull;
p->ShortNotOK = ShortNotOK;
p->IntReq = IntReq;
p->Combined = nullptr;
IoVecReset(&p->IoVec);
p->State = USB_PACKET_SETUP;
}
@ -495,7 +494,7 @@ void USBPCIDevice::DoTokenOut(XboxDeviceState* s, USBPacket* p) {
}
void USBPCIDevice::USB_PacketCopy(USBPacket* p, void* ptr, size_t bytes) {
IOVector* iov = p->Combined ? &p->Combined->IoVec : &p->IoVec;
IOVector* iov = &p->IoVec;
assert(p->ActualLength >= 0);
assert(p->ActualLength + bytes <= iov->Size);
@ -705,7 +704,6 @@ void USBPCIDevice::USB_EpReset(XboxDeviceState* dev) {
void USBPCIDevice::USB_CreateSerial(XboxDeviceState* dev, std::string&& str) {
const USBDesc* desc = USBDesc_GetUsbDeviceDesc(dev);
int index = desc->id.iSerialNumber;
std::string str2;
assert(index != 0 && str.empty() == false);
str += '-';
@ -1009,7 +1007,7 @@ int USBPCIDevice::USBDesc_HandleStandardGetDescriptor(XboxDeviceState* dev, USBP
break;
}
// Dropped from XQEMU descriptor types USB_DT_DEVICE_QUALIFIER (6), USB_DT_OTHER_SPEED_CONFIG (7) -> usb 2.0 only and reserved on usb 3.0,
// Dropped from XQEMU descriptor types USB_DT_DEVICE_QUALIFIER (6), USB_DT_OTHER_SPEED_CONFIG (7) -> usb 2.0 only and reserved in usb 3.0,
// USB_DT_BOS (15) and USB_DT_DEBUG (10) -> usb 3.0 only
default:
@ -1143,8 +1141,8 @@ int USBPCIDevice::USB_ReadInterfaceDesc(const USBDescIface* iface, int flags, ui
return pos;
}
int USBPCIDevice::USB_ReadOtherDesc(const USBDescOther* desc, uint8_t* dest, size_t len) {
int bLength = desc->length ? desc->length : desc->data[0];
size_t USBPCIDevice::USB_ReadOtherDesc(const USBDescOther* desc, uint8_t* dest, size_t len) {
size_t bLength = desc->length ? desc->length : desc->data[0];
if (len < bLength) {
return -1;
@ -1155,15 +1153,15 @@ int USBPCIDevice::USB_ReadOtherDesc(const USBDescOther* desc, uint8_t* dest, siz
}
int USBPCIDevice::USB_ReadEndpointDesc(const USBDescEndpoint* ep, int flags, uint8_t* dest, size_t len) {
uint8_t bLength = ep->is_audio ? 0x09 : 0x07; // an endpoint descriptor is 7 bytes large (or 9 if it is an audio device)
uint8_t extralen = ep->extra ? ep->extra[0] : 0;
size_t bLength = ep->is_audio ? 0x09 : 0x07; // an endpoint descriptor is 7 bytes large (or 9 if it is an audio device)
size_t extralen = ep->extra ? ep->extra[0] : 0;
USBDescriptor* d = reinterpret_cast<USBDescriptor*>(dest);
if (len < bLength + extralen) {
return -1;
}
d->bLength = bLength;
d->bLength = static_cast<uint8_t>(bLength);
d->bDescriptorType = USB_DT_ENDPOINT;
d->u.endpoint.bEndpointAddress = ep->bEndpointAddress;
@ -1186,7 +1184,8 @@ int USBPCIDevice::USB_ReadEndpointDesc(const USBDescEndpoint* ep, int flags, uin
}
int USBPCIDevice::USB_ReadStringDesc(XboxDeviceState* dev, int index, uint8_t* dest, size_t len) {
uint8_t bLength, pos, i;
size_t bLength, i;
unsigned int pos;
const char* str;
if (len < 4) {
@ -1213,7 +1212,7 @@ int USBPCIDevice::USB_ReadStringDesc(XboxDeviceState* dev, int index, uint8_t* d
// From the 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 = strlen(str) * 2 + 2;
bLength = std::strlen(str) * 2 + 2;
dest[0] = bLength;
dest[1] = USB_DT_STRING;
i = 0; pos = 2;

View file

@ -120,7 +120,7 @@ public:
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);
@ -196,7 +196,7 @@ public:
// return the binary rapresentation of interface descriptors
int USB_ReadInterfaceDesc(const USBDescIface* iface, int flags, uint8_t* dest, size_t len);
// return the binary rapresentation of class-specific descriptors
int USB_ReadOtherDesc(const USBDescOther* desc, uint8_t* dest, size_t len);
size_t USB_ReadOtherDesc(const USBDescOther* desc, uint8_t* dest, size_t len);
// return the binary rapresentation of endpoint descriptors
int USB_ReadEndpointDesc(const USBDescEndpoint* ep, int flags, uint8_t* dest, size_t len);
// return the binary rapresentation of string descriptors

View file

@ -39,6 +39,7 @@
// ******************************************************************
#include "xid_gamepad.h"
#include "..\ohci\ohci.h"
#include "../pci/usb_pci.h"
#include <string>
@ -67,7 +68,6 @@ struct XIDDesc {
uint8_t bMaxInputReportSize;
uint8_t bMaxOutputReportSize;
uint16_t wAlternateProductIds[4];
XIDDesc();
};
/* Struct used by the Get_Report request -> state of the buttons */
@ -98,97 +98,104 @@ struct USBXIDState {
const XIDDesc* xid_desc; // xid-specific descriptor
bool in_dirty; // indicates a change in the button's state
XIDGamepadReport in_state; // Get_Report struct
XIDGamepadOutputReport out_state; // Ser_Report struct
XIDGamepadReport in_state; // Get_Report struct
XIDGamepadReport in_state_capabilities; // Get_Capabilities struct (in)
XIDGamepadOutputReport out_state; // Set_Report struct
XIDGamepadOutputReport out_state_capabilities; // Get_Capabilities struct (out)
};
USBDescIface::USBDescIface() {
std::memset(this, 0, sizeof(USBDescIface));
eps = new USBDescEndpoint[2]();
bInterfaceNumber = 0;
bNumEndpoints = 2;
bInterfaceClass = USB_CLASS_XID;
bInterfaceSubClass = 0x42;
bInterfaceProtocol = 0x00;
eps->bEndpointAddress = USB_DIR_IN | 0x02;
eps->bmAttributes = USB_ENDPOINT_XFER_INT;
eps->wMaxPacketSize = 0x20;
eps->bInterval = 4;
eps++;
eps->bEndpointAddress = USB_DIR_OUT | 0x02;
eps->bmAttributes = USB_ENDPOINT_XFER_INT;
eps->wMaxPacketSize = 0x20;
eps->bInterval = 4;
}
static const USBDescEndpoint desc_endp_xbox_gamepad[2] = {
{
USB_DIR_IN | 0x02, // bEndpointAddress;
USB_ENDPOINT_XFER_INT, // bmAttributes;
0x20, // wMaxPacketSize;
4, // bInterval;
0, // bRefresh;
0, // bSynchAddress
0, // is_audio
nullptr // extra
},
{
USB_DIR_OUT | 0x02,
USB_ENDPOINT_XFER_INT,
0x20,
4,
0,
0,
0,
nullptr
}
};
USBDescIface::~USBDescIface() {
delete[] eps;
}
static const USBDescIface desc_iface_xbox_gamepad = {
0, // bInterfaceNumber;
0, // bAlternateSetting;
2, // bNumEndpoints;
USB_CLASS_XID, // bInterfaceClass;
0x42, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0, // iInterface
0, // ndesc
nullptr, // descs
desc_endp_xbox_gamepad
};
static const USBDescIface desc_iface_xbox_gamepad;
static const USBDescConfig desc_config_xbox_gamepad = {
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0x80, // bmAttributes
50, // bMaxPower
1, // nif
&desc_iface_xbox_gamepad
};
USBDescDevice::USBDescDevice() {
std::memset(this, 0, sizeof(USBDescDevice));
USBDescConfig* pUSBDescConfig = new USBDescConfig();
bcdUSB = 0x0110;
bMaxPacketSize0 = 0x40;
bNumConfigurations = 1;
pUSBDescConfig->bNumInterfaces = 1;
pUSBDescConfig->bConfigurationValue = 1;
pUSBDescConfig->bmAttributes = 0x80;
pUSBDescConfig->bMaxPower = 50;
pUSBDescConfig->nif = 1;
pUSBDescConfig->ifs = &desc_iface_xbox_gamepad;
confs = pUSBDescConfig;
}
static const USBDescDevice desc_device_xbox_gamepad = {
0x0110, // bcdUSB
0, // bDeviceClass
0, // bDeviceSubClass
0, // bDeviceProtocol
0x40, // bMaxPacketSize0
1, // bNumConfigurations
&desc_config_xbox_gamepad
};
USBDescDevice::~USBDescDevice() {
delete confs;
}
static const USBDesc desc_xbox_gamepad = {
{
0x045E, // idVendor
0x0202, // idProduct
0x0100, // bcdDevice
STR_MANUFACTURER, // iManufacturer
STR_PRODUCT, // iProduct
STR_SERIALNUMBER // iSerialNumber
},
&desc_device_xbox_gamepad
};
static const USBDescDevice desc_device_xbox_gamepad;
USBDesc::USBDesc() {
std::memset(this, 0, sizeof(USBDesc));
id.idVendor = 0x045E;
id.idProduct = 0x0202;
id.bcdDevice = 0x0100;
id.iManufacturer = STR_MANUFACTURER;
id.iProduct = STR_PRODUCT;
id.iSerialNumber = STR_SERIALNUMBER;
full = &desc_device_xbox_gamepad;
}
static const USBDesc desc_xbox_gamepad;
XIDDesc::XIDDesc() {
bLength = 0x10;
bDescriptorType = USB_DT_XID;
bcdXid = 1;
bType = 1;
bSubType = 1;
bMaxInputReportSize = 0x20;
bMaxOutputReportSize = 0x6;
wAlternateProductIds[0] = -1;
wAlternateProductIds[1] = -1;
wAlternateProductIds[2] = -1;
wAlternateProductIds[3] = -1;
}
static const XIDDesc desc_xid_xbox_gamepad;
static const XIDDesc desc_xid_xbox_gamepad = {
0x10, // bLength
USB_DT_XID, // bDescriptorType
0x100, // bcdXid
1, // bType
1, // bSubType
20, // bMaxInputReportSize
6, // bMaxOutputReportSize
{ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF } // wAlternateProductIds
};
int XidGamepad::Init(int port) {
if (port > 4 || port < 1) { return -1; };
if (port > 4 || port < 1) { return -1; }
XboxDeviceState* dev = ClassInitFn();
int rc = UsbXidClaimPort(dev, port);
if (rc != 0) {
m_UsbDev->m_HostController->m_bFrameTime = false;
return rc;
}
m_UsbDev->USB_EpInit(dev);
m_UsbDev->USB_DeviceInit(dev);
m_UsbDev->USB_DeviceAttach(dev);
m_UsbDev->m_HostController->m_bFrameTime = false;
return 0;
}
@ -241,6 +248,10 @@ int XidGamepad::UsbXidClaimPort(XboxDeviceState* dev, int port) {
log_warning("XID: Port requested %d.2 not found (in use?)", port);
return -1;
}
while (m_UsbDev->m_HostController->m_bFrameTime) {}
m_UsbDev->m_HostController->m_bFrameTime = true;
m_Port = port;
it = m_UsbDev->m_FreePorts.begin() + i;
dev->Port = *it;
@ -267,7 +278,17 @@ int XidGamepad::UsbXid_Initfn(XboxDeviceState* dev) {
m_XidState->intr = m_UsbDev->USB_GetEP(dev, USB_TOKEN_IN, 2);
m_XidState->in_state.bLength = sizeof(m_XidState->in_state);
m_XidState->in_state.bReportId = 0;
m_XidState->out_state.length = sizeof(m_XidState->out_state);
m_XidState->out_state.report_id = 0;
std::memset(&m_XidState->in_state_capabilities, 0xFF, sizeof(m_XidState->in_state_capabilities));
m_XidState->in_state_capabilities.bLength = sizeof(m_XidState->in_state_capabilities);
m_XidState->in_state_capabilities.bReportId = 0;
std::memset(&m_XidState->out_state_capabilities, 0xFF, sizeof(m_XidState->out_state_capabilities));
m_XidState->out_state_capabilities.length = sizeof(m_XidState->out_state_capabilities);
m_XidState->out_state_capabilities.report_id = 0;
m_XidState->xid_desc = &desc_xid_xbox_gamepad;
return 0;
@ -308,21 +329,37 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
// 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"
log_debug("XID: Gamepad GET_REPORT 0x%X", value);
if (value == 0x100) {
assert(m_XidState->in_state.bLength <= length);
// m_XidState->in_state.bReportId++; /* FIXME: I'm not sure if bReportId is just a counter */
// TODO: implement input devices
/*SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port);
if (controller != nullptr) {
controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons,
&m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX,
&m_XidState->in_state.sThumbRY);
}*/
std::memcpy(data, &m_XidState->in_state, m_XidState->in_state.bLength);
p->ActualLength = m_XidState->in_state.bLength;
// 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 :
// If the buffer provided by the Xbox is too small, the controller will cut the transfer when the buffer is full (actual_length is patched).
// If the buffer is too large the controller will STALL instead.
// If the buffer has the correct length the full input data is transferred."
if (value == 0x0100) {
if (length <= m_XidState->in_state.bLength) {
// TODO: implement input devices
//SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port);
//if (controller != nullptr) {
// controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons,
// &m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX,
// &m_XidState->in_state.sThumbRY);
//}
//else
//{
// // ergo720: this shouldn't really happen. If it does, it either means that m_Port is wrong or there's a bug
// // in the InputDeviceManager
// p->Status = USB_RET_STALL;
// assert(0);
//}
std::memcpy(data, &m_XidState->in_state, m_XidState->in_state.bLength);
p->ActualLength = length;
}
else {
p->Status = USB_RET_STALL;
}
}
else {
assert(0);
p->Status = USB_RET_STALL;
}
break;
}
@ -334,18 +371,27 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
// 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."
log_debug("XID: Gamepad SET_REPORT 0x%X", value);
if (value == 0x200) {
// Read length, then the entire packet
std::memcpy(&m_XidState->out_state, data, sizeof(m_XidState->out_state));
assert(m_XidState->out_state.length == sizeof(m_XidState->out_state));
assert(m_XidState->out_state.length <= length);
//FIXME: Check actuator endianess
log_debug("XID: Set rumble power to left: 0x%X and right: 0x%X",
m_XidState->out_state.left_actuator_strength,
m_XidState->out_state.right_actuator_strength);
p->ActualLength = m_XidState->out_state.length;
// 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 :
// If the buffer provided by the Xbox is too small the transfer will STALL.
// If the buffer is too large the transfer will STALL.
// If the buffer has the correct length the full output data is transferred."
if (value == 0x0200) {
if (length == m_XidState->out_state.length) {
// Read length, then the entire packet
std::memcpy(&m_XidState->out_state, data, sizeof(m_XidState->out_state));
/* FIXME: This should also be a STALL */
assert(m_XidState->out_state.length == sizeof(m_XidState->out_state));
p->ActualLength = length;
}
else {
p->Status = USB_RET_STALL;
}
UpdateForceFeedback();
}
else {
p->Status = USB_RET_STALL;
assert(0);
}
break;
@ -361,6 +407,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
p->ActualLength = m_XidState->xid_desc->bLength;
}
else {
p->Status = USB_RET_STALL;
assert(0);
}
break;
@ -369,9 +416,24 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
case VendorInterfaceRequest | XID_GET_CAPABILITIES:
{
log_debug("XID: Gamepad XID_GET_CAPABILITIES 0x%x", value);
/* FIXME: ! */
p->Status = USB_RET_STALL;
//assert(false);
if (value == 0x0100) {
if (length > m_XidState->in_state_capabilities.bLength) {
length = m_XidState->in_state_capabilities.bLength;
}
std::memcpy(data, &m_XidState->in_state_capabilities, length);
p->ActualLength = length;
}
else if (value == 0x0200) {
if (length > m_XidState->out_state_capabilities.length) {
length = m_XidState->out_state_capabilities.length;
}
std::memcpy(data, &m_XidState->out_state_capabilities, length);
p->ActualLength = length;
}
else {
p->Status = USB_RET_STALL;
assert(0);
}
break;
}
@ -408,13 +470,24 @@ void XidGamepad::UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p) {
case USB_TOKEN_IN:
{
if (p->Endpoint->Num == 2) {
if (m_XidState->in_dirty) {
m_UsbDev->USB_PacketCopy(p, &m_XidState->in_state, m_XidState->in_state.bLength);
m_XidState->in_dirty = false;
}
else {
p->Status = USB_RET_NAK;
}
//SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port);
//if (controller != nullptr) {
// bool ret;
// ret = controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons,
// &m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX,
// &m_XidState->in_state.sThumbRY);
// if (ret) {
// m_UsbDev->USB_PacketCopy(p, &m_XidState->in_state, m_XidState->in_state.bLength);
// }
// else {
p->Status = USB_RET_NAK;
// }
//}
//else
//{
// p->Status = USB_RET_STALL;
// assert(0);
//}
}
else {
assert(0);
@ -424,7 +497,13 @@ void XidGamepad::UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p) {
case USB_TOKEN_OUT:
{
p->Status = USB_RET_STALL;
if (p->Endpoint->Num == 2) {
m_UsbDev->USB_PacketCopy(p, &m_XidState->out_state, m_XidState->out_state.length);
UpdateForceFeedback();
}
else {
assert(0);
}
break;
}
@ -442,4 +521,15 @@ void XidGamepad::XidCleanUp() {
m_XidState = nullptr;
}
void XidGamepad::UpdateForceFeedback() {
// JayFoxRox's remarks: "Xbox -> XID packets were not tested
// The handling of output packets / force feedback was not checked."
// For the above reason we don't implement vibration support for now since the current
// implementation is untested and could potentially contain errors
/* FIXME: Check actuator endianess */
log_debug("XID: Set rumble power to left: 0x%X and right: 0x%X\n",
m_XidState->out_state.left_actuator_strength,
m_XidState->out_state.right_actuator_strength);
}
}

View file

@ -79,6 +79,9 @@ private:
void UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
int request, int value, int index, int length, uint8_t* data);
void UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p);
// this should update the vibration strength of the real controller this gamepad represents.
// It doesn't do anything at the moment
void UpdateForceFeedback();
};
extern XidGamepad* g_XidControllerObjArray[4];