mirror of
https://github.com/StrikerX3/StrikeBox.git
synced 2024-06-23 14:53:22 -04:00
Sync code with ergo720's branch up to commit 549195f
This commit is contained in:
parent
8fc9059dd1
commit
ef1f03a6df
|
@ -61,11 +61,7 @@ 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);
|
||||
|
||||
// Calculate a struct base address from a pointer to a member of it
|
||||
#ifndef container_of
|
||||
#define container_of(address, type, field) ((type *)(\
|
||||
(int8_t*)(address) - \
|
||||
(uint32_t)(&((type *)0)->field)))
|
||||
#endif
|
||||
#define GET_WORD_LOW(value) (uint8_t)((value) & 0xFF)
|
||||
#define GET_WORD_HIGH(value) (uint8_t)(((value) >> 8) & 0xFF)
|
||||
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ OHCI::OHCI(Cpu* cpu, int Irq, USBPCIDevice* UsbObj)
|
|||
|
||||
ops->attach = std::bind(&OHCI::OHCI_Attach, this, _1);
|
||||
ops->detach = std::bind(&OHCI::OHCI_Detach, this, _1);
|
||||
ops->child_detach = std::bind(&OHCI::OHCI_ChildDetach, this, nullptr, _2);
|
||||
ops->child_detach = std::bind(&OHCI::OHCI_ChildDetach, this, _1);
|
||||
ops->wakeup = std::bind(&OHCI::OHCI_Wakeup, this, _1);
|
||||
ops->complete = std::bind(&OHCI::OHCI_AsyncCompletePacket, this, _1, _2);
|
||||
}
|
||||
|
@ -1448,7 +1448,7 @@ void OHCI::OHCI_Attach(USBPort* Port)
|
|||
}
|
||||
}
|
||||
|
||||
void OHCI::OHCI_ChildDetach(USBPort* port, XboxDeviceState* child) {
|
||||
void OHCI::OHCI_ChildDetach(XboxDeviceState* child) {
|
||||
OHCI_AsyncCancelDevice(child);
|
||||
}
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ private:
|
|||
// see USBPortOps struct for info
|
||||
void OHCI_Attach(USBPort* Port);
|
||||
void OHCI_Detach(USBPort* Port);
|
||||
void OHCI_ChildDetach(USBPort* port, XboxDeviceState* child);
|
||||
void OHCI_ChildDetach(XboxDeviceState* child);
|
||||
void OHCI_Wakeup(USBPort* port1);
|
||||
void OHCI_AsyncCompletePacket(USBPort* port, USBPacket* packet);
|
||||
};
|
||||
|
|
|
@ -72,6 +72,61 @@ namespace openxbox {
|
|||
#define USB_SPEED_LOW 0
|
||||
#define USB_SPEED_FULL 1
|
||||
|
||||
#define USB_DEVICE_SELF_POWERED 0
|
||||
#define USB_DEVICE_REMOTE_WAKEUP 1
|
||||
|
||||
#define USB_TYPE_MASK (0x03 << 5)
|
||||
#define USB_TYPE_STANDARD (0x00 << 5)
|
||||
#define USB_TYPE_CLASS (0x01 << 5)
|
||||
#define USB_TYPE_VENDOR (0x02 << 5)
|
||||
#define USB_TYPE_RESERVED (0x03 << 5)
|
||||
|
||||
#define USB_RECIP_MASK 0x1F
|
||||
#define USB_RECIP_DEVICE 0x00
|
||||
#define USB_RECIP_INTERFACE 0x01
|
||||
#define USB_RECIP_ENDPOINT 0x02
|
||||
#define USB_RECIP_OTHER 0x03
|
||||
|
||||
#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define VendorDeviceRequest ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
|
||||
#define VendorDeviceOutRequest ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
|
||||
|
||||
#define InterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define InterfaceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define ClassInterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
|
||||
#define ClassInterfaceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
|
||||
#define VendorInterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8)
|
||||
#define VendorInterfaceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
|
||||
#define EndpointOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
|
||||
|
||||
#define USB_REQ_GET_STATUS 0x00
|
||||
#define USB_REQ_CLEAR_FEATURE 0x01
|
||||
#define USB_REQ_SET_FEATURE 0x03
|
||||
#define USB_REQ_SET_ADDRESS 0x05
|
||||
#define USB_REQ_GET_DESCRIPTOR 0x06
|
||||
#define USB_REQ_SET_DESCRIPTOR 0x07
|
||||
#define USB_REQ_GET_CONFIGURATION 0x08
|
||||
#define USB_REQ_SET_CONFIGURATION 0x09
|
||||
#define USB_REQ_GET_INTERFACE 0x0A
|
||||
#define USB_REQ_SET_INTERFACE 0x0B
|
||||
#define USB_REQ_SYNCH_FRAME 0x0C
|
||||
|
||||
#define USB_DT_DEVICE 0x01
|
||||
#define USB_DT_CONFIG 0x02
|
||||
#define USB_DT_STRING 0x03
|
||||
#define USB_DT_INTERFACE 0x04
|
||||
#define USB_DT_ENDPOINT 0x05
|
||||
|
||||
typedef enum _USB_SPEED {
|
||||
USB_SPEED_MASK_LOW = 1 << 0,
|
||||
USB_SPEED_MASK_FULL = 1 << 1,
|
||||
|
@ -93,8 +148,6 @@ struct USBPort;
|
|||
struct USBDeviceClass;
|
||||
struct XboxDeviceState;
|
||||
|
||||
typedef const char* USBDescStrings[256];
|
||||
|
||||
/* String descriptor */
|
||||
struct USBDescString {
|
||||
uint8_t index; // index of this string descriptor
|
||||
|
@ -102,6 +155,7 @@ struct USBDescString {
|
|||
QLIST_ENTRY(USBDescString) next;
|
||||
};
|
||||
|
||||
// Device-specific class descriptors, if any. No idea if some Xbox devices use this but, if not, this can be removed
|
||||
struct USBDescOther {
|
||||
uint8_t length;
|
||||
const uint8_t *data;
|
||||
|
@ -116,8 +170,11 @@ struct USBDescEndpoint {
|
|||
uint8_t bRefresh; // for audio devices only: the rate at which synchronization feedback is provided
|
||||
uint8_t bSynchAddress; // for audio devices only: the address of the synchronization endpoint
|
||||
|
||||
uint8_t is_audio; /* has bRefresh + bSynchAddress */
|
||||
uint8_t* extra;
|
||||
uint8_t is_audio; // has bRefresh + bSynchAddress
|
||||
uint8_t* extra; // class-specific descriptors (if any) associated with this endpoint
|
||||
|
||||
// Dropped from XQEMU the parameters bMaxBurst, bmAttributes_super and wBytesPerInterval because those are only defined for
|
||||
// superspeed (usb 3.0) devices in the superspeed endpoint companion
|
||||
};
|
||||
|
||||
/* Interface descriptor */
|
||||
|
@ -130,8 +187,8 @@ struct USBDescIface {
|
|||
uint8_t bInterfaceProtocol; // protocol code (assigned by the USB)
|
||||
uint8_t iInterface; // index of string descriptor describing this interface
|
||||
|
||||
uint8_t ndesc;
|
||||
USBDescOther* descs;
|
||||
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(bool bDefault);
|
||||
~USBDescIface();
|
||||
|
@ -187,6 +244,64 @@ struct USBDesc {
|
|||
USBDesc(bool bDefault);
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
// Binary representation of the descriptors
|
||||
// Dropped from XQEMU usb 2.0 and 3.0 only descriptors
|
||||
struct USBDescriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
union {
|
||||
struct {
|
||||
uint8_t bcdUSB_lo;
|
||||
uint8_t bcdUSB_hi;
|
||||
uint8_t bDeviceClass;
|
||||
uint8_t bDeviceSubClass;
|
||||
uint8_t bDeviceProtocol;
|
||||
uint8_t bMaxPacketSize0;
|
||||
uint8_t idVendor_lo;
|
||||
uint8_t idVendor_hi;
|
||||
uint8_t idProduct_lo;
|
||||
uint8_t idProduct_hi;
|
||||
uint8_t bcdDevice_lo;
|
||||
uint8_t bcdDevice_hi;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
} device; // device descriptor
|
||||
struct {
|
||||
uint8_t wTotalLength_lo;
|
||||
uint8_t wTotalLength_hi;
|
||||
uint8_t bNumInterfaces;
|
||||
uint8_t bConfigurationValue;
|
||||
uint8_t iConfiguration;
|
||||
uint8_t bmAttributes;
|
||||
uint8_t bMaxPower;
|
||||
} config; // configuration descriptor
|
||||
struct {
|
||||
uint8_t bInterfaceNumber;
|
||||
uint8_t bAlternateSetting;
|
||||
uint8_t bNumEndpoints;
|
||||
uint8_t bInterfaceClass;
|
||||
uint8_t bInterfaceSubClass;
|
||||
uint8_t bInterfaceProtocol;
|
||||
uint8_t iInterface;
|
||||
} iface; // interface descriptor
|
||||
struct {
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bmAttributes;
|
||||
uint8_t wMaxPacketSize_lo;
|
||||
uint8_t wMaxPacketSize_hi;
|
||||
uint8_t bInterval;
|
||||
uint8_t bRefresh; // only audio ep
|
||||
uint8_t bSynchAddress; // only audio ep
|
||||
} endpoint; // endpoint descriptor
|
||||
} u;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
/* USB endpoint */
|
||||
struct USBEndpoint {
|
||||
uint8_t Num; // endpoint number
|
||||
|
@ -208,25 +323,25 @@ struct XboxDeviceState {
|
|||
uint32_t flags;
|
||||
USBDeviceClass* klass; // usb class struct of this device
|
||||
|
||||
int Speed; // actual connected speed
|
||||
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]; // holds the IoVec structs copied (control transfers only?)
|
||||
uint8_t data_buf[4096];
|
||||
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 setup tken processing operation
|
||||
int32_t SetupLength; // number of bytes to transfer as specified by a setup token
|
||||
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 descriptor
|
||||
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
|
||||
|
||||
|
@ -254,7 +369,7 @@ struct USBPacket {
|
|||
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; // before copy: offset inside IoVec structs; after copy: number of bytes actually transferred
|
||||
int ActualLength; // number of bytes actually written to DataBuffer
|
||||
// Internal use by the USB layer
|
||||
USBPacketState State;
|
||||
USBCombinedPacket* Combined;
|
||||
|
@ -269,7 +384,7 @@ struct USBPortOps {
|
|||
* This gets called when a device downstream from the device attached to
|
||||
* the port (attached through a hub) gets detached.
|
||||
*/
|
||||
std::function<void(USBPort* port, XboxDeviceState* child)> child_detach;
|
||||
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
|
||||
|
@ -285,7 +400,7 @@ struct USBPort {
|
|||
int SpeedMask; // usb speeds supported
|
||||
int HubCount; // number of hubs chained
|
||||
char Path[16]; // the number of the port + 1, used to create a serial number for this device
|
||||
int PortIndex; // internal port index against this HC
|
||||
int PortIndex; // internal port index
|
||||
};
|
||||
|
||||
/* Struct which stores general functions/variables regarding the peripheral */
|
||||
|
@ -336,14 +451,4 @@ struct USBDeviceClass {
|
|||
const USBDesc* usb_desc; // device descriptor
|
||||
};
|
||||
|
||||
|
||||
/* Abstract class representing a usb peripheral */
|
||||
class UsbPeripheral {
|
||||
protected:
|
||||
USBDeviceClass * m_pPeripheralFuncStruct;
|
||||
XboxDeviceState* m_pDeviceStruct;
|
||||
|
||||
virtual int Init(int pport) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -111,8 +111,6 @@ USBDescDevice::~USBDescDevice() {
|
|||
|
||||
static const USBDescDevice desc_device_hub(true);
|
||||
|
||||
static USBDescStrings desc_strings = { nullptr, "Cxbx-Reloaded", "Cxbx-Reloaded USB Hub", "314159" };
|
||||
|
||||
USBDesc::USBDesc(bool bDefault) {
|
||||
std::memset(this, 0, sizeof(USBDesc));
|
||||
if (bDefault) {
|
||||
|
@ -123,25 +121,24 @@ USBDesc::USBDesc(bool bDefault) {
|
|||
id.iProduct = STR_PRODUCT;
|
||||
id.iSerialNumber = STR_SERIALNUMBER;
|
||||
full = &desc_device_hub;
|
||||
str = desc_strings;
|
||||
}
|
||||
}
|
||||
|
||||
static const USBDesc desc_hub(true);
|
||||
|
||||
int Hub::Init(int pport) {
|
||||
ClassInitFn();
|
||||
UsbEpInit();
|
||||
int rc = UsbClaimPort(pport);
|
||||
XboxDeviceState* dev = ClassInitFn();
|
||||
m_UsbDev->USB_EpInit(dev);
|
||||
int rc = UsbHubClaimPort(dev, pport);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
rc = m_UsbDev->USB_DeviceInit(m_pDeviceStruct);
|
||||
rc = m_UsbDev->USB_DeviceInit(dev);
|
||||
if (rc != 0) {
|
||||
UsbReleasePort(m_pDeviceStruct);
|
||||
UsbHubReleasePort(dev);
|
||||
return rc;
|
||||
}
|
||||
m_UsbDev->USB_DeviceAttach(m_pDeviceStruct);
|
||||
m_UsbDev->USB_DeviceAttach(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -150,17 +147,17 @@ Hub::~Hub() {
|
|||
delete m_HubState->ports[0].port.Operations;
|
||||
delete m_HubState;
|
||||
m_pPeripheralFuncStruct = nullptr;
|
||||
m_pDeviceStruct = nullptr;
|
||||
m_HubState = nullptr;
|
||||
}
|
||||
|
||||
void Hub::ClassInitFn() {
|
||||
XboxDeviceState* Hub::ClassInitFn() {
|
||||
m_pPeripheralFuncStruct = new USBDeviceClass();
|
||||
m_HubState = new USBHubState();
|
||||
m_pDeviceStruct = &m_HubState->dev;
|
||||
XboxDeviceState* dev = &m_HubState->dev;
|
||||
|
||||
m_pDeviceStruct->ProductDesc = "Cxbx-Reloaded USB Hub";
|
||||
QLIST_INIT(&m_pDeviceStruct->Strings);
|
||||
m_pDeviceStruct->klass = m_pPeripheralFuncStruct;
|
||||
dev->ProductDesc = "Cxbx-Reloaded USB Hub";
|
||||
QLIST_INIT(&dev->Strings);
|
||||
dev->klass = m_pPeripheralFuncStruct;
|
||||
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
@ -171,66 +168,34 @@ void Hub::ClassInitFn() {
|
|||
m_pPeripheralFuncStruct->handle_control = std::bind(&Hub::UsbHub_HandleControl, this, _1, _2, _3, _4, _5, _6, _7);
|
||||
m_pPeripheralFuncStruct->handle_data = std::bind(&Hub::UsbHub_HandleData, this, _1, _2);
|
||||
m_pPeripheralFuncStruct->handle_destroy = std::bind(&Hub::UsbHub_HandleDestroy, this, _1);
|
||||
m_pPeripheralFuncStruct->product_desc = m_pDeviceStruct->ProductDesc.c_str();
|
||||
m_pPeripheralFuncStruct->product_desc = dev->ProductDesc.c_str();
|
||||
m_pPeripheralFuncStruct->usb_desc = &desc_hub;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void Hub::UsbEpInit() {
|
||||
UsbEpReset();
|
||||
QTAILQ_INIT(&m_pDeviceStruct->EP_ctl.Queue);
|
||||
for (int ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
|
||||
QTAILQ_INIT(&m_pDeviceStruct->EP_in[ep].Queue);
|
||||
QTAILQ_INIT(&m_pDeviceStruct->EP_out[ep].Queue);
|
||||
}
|
||||
}
|
||||
|
||||
void Hub::UsbEpReset() {
|
||||
m_pDeviceStruct->EP_ctl.Num = 0;
|
||||
m_pDeviceStruct->EP_ctl.Type = USB_ENDPOINT_XFER_CONTROL;
|
||||
m_pDeviceStruct->EP_ctl.IfNum = 0;
|
||||
m_pDeviceStruct->EP_ctl.MaxPacketSize = 64;
|
||||
m_pDeviceStruct->EP_ctl.Dev = m_pDeviceStruct;
|
||||
m_pDeviceStruct->EP_ctl.Pipeline = false;
|
||||
for (int ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
|
||||
m_pDeviceStruct->EP_in[ep].Num = ep + 1;
|
||||
m_pDeviceStruct->EP_out[ep].Num = ep + 1;
|
||||
m_pDeviceStruct->EP_in[ep].pid = USB_TOKEN_IN;
|
||||
m_pDeviceStruct->EP_out[ep].pid = USB_TOKEN_OUT;
|
||||
m_pDeviceStruct->EP_in[ep].Type = USB_ENDPOINT_XFER_INVALID;
|
||||
m_pDeviceStruct->EP_out[ep].Type = USB_ENDPOINT_XFER_INVALID;
|
||||
m_pDeviceStruct->EP_in[ep].IfNum = USB_INTERFACE_INVALID;
|
||||
m_pDeviceStruct->EP_out[ep].IfNum = USB_INTERFACE_INVALID;
|
||||
m_pDeviceStruct->EP_in[ep].MaxPacketSize = 0;
|
||||
m_pDeviceStruct->EP_out[ep].MaxPacketSize = 0;
|
||||
m_pDeviceStruct->EP_in[ep].Dev = m_pDeviceStruct;
|
||||
m_pDeviceStruct->EP_out[ep].Dev = m_pDeviceStruct;
|
||||
m_pDeviceStruct->EP_in[ep].Pipeline = false;
|
||||
m_pDeviceStruct->EP_out[ep].Pipeline = false;
|
||||
}
|
||||
}
|
||||
|
||||
int Hub::UsbClaimPort(int pport) {
|
||||
int Hub::UsbHubClaimPort(XboxDeviceState* dev, int pport) {
|
||||
int usb_port;
|
||||
|
||||
assert(m_pDeviceStruct->Port == nullptr);
|
||||
assert(dev->Port == nullptr);
|
||||
|
||||
if (pport > 4 || pport < 1) { return -1; };
|
||||
|
||||
usb_port = PlayerToUsbArray[pport];
|
||||
if (usb_port > 2) {
|
||||
m_UsbDev = g_USB0; // FIXME: how to retrieve these?
|
||||
m_UsbDev->m_HostController->OHCI_AssignUsbPortStruct(usb_port - 3, m_pDeviceStruct);
|
||||
m_UsbDev->m_HostController->OHCI_AssignUsbPortStruct(usb_port - 3, dev);
|
||||
}
|
||||
else {
|
||||
m_UsbDev = g_USB1; // FIXME: how to retrieve these?
|
||||
m_UsbDev->m_HostController->OHCI_AssignUsbPortStruct(usb_port - 1, m_pDeviceStruct);
|
||||
m_UsbDev->m_HostController->OHCI_AssignUsbPortStruct(usb_port - 1, dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Hub::UsbReleasePort(XboxDeviceState* dev) {
|
||||
void Hub::UsbHubReleasePort(XboxDeviceState* dev) {
|
||||
USBPort * port = dev->Port;
|
||||
|
||||
assert(port != nullptr);
|
||||
|
@ -249,8 +214,10 @@ int Hub::UsbHub_Initfn(XboxDeviceState* dev) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
CreateSerial(dev);
|
||||
UsbDescInit(dev);
|
||||
m_UsbDev->USB_CreateSerial(dev, "314159");
|
||||
m_UsbDev->USBDesc_SetString(dev, STR_MANUFACTURER, "Cxbx-Reloaded");
|
||||
m_UsbDev->USBDesc_SetString(dev, STR_PRODUCT, "Cxbx-Reloaded USB Hub");
|
||||
m_UsbDev->USBDesc_Init(dev);
|
||||
m_HubState->intr = m_UsbDev->USB_GetEP(dev, USB_TOKEN_IN, 1);
|
||||
|
||||
ops = new USBPortOps();
|
||||
|
@ -259,7 +226,7 @@ int Hub::UsbHub_Initfn(XboxDeviceState* dev) {
|
|||
|
||||
ops->attach = std::bind(&Hub::UsbHub_Attach, this, _1);
|
||||
ops->detach = std::bind(&Hub::UsbHub_Detach, this, _1);
|
||||
ops->child_detach = std::bind(&Hub::UsbHub_ChildDetach, this, _1, _2);
|
||||
ops->child_detach = std::bind(&Hub::UsbHub_ChildDetach, this, _1);
|
||||
ops->wakeup = std::bind(&Hub::UsbHub_Wakeup, this, _1);
|
||||
ops->complete = std::bind(&Hub::UsbHub_Complete, this, _1, _2);
|
||||
}
|
||||
|
@ -273,6 +240,23 @@ int Hub::UsbHub_Initfn(XboxDeviceState* dev) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
XboxDeviceState* Hub::UsbHub_FindDevice(XboxDeviceState* dev, uint8_t addr) {
|
||||
USBHubPort * port;
|
||||
XboxDeviceState * downstream;
|
||||
|
||||
for (int i = 0; i < NUM_PORTS; i++) {
|
||||
port = &m_HubState->ports[i];
|
||||
if (!(port->wPortStatus & PORT_STAT_ENABLE)) {
|
||||
continue;
|
||||
}
|
||||
downstream = m_UsbDev->USB_FindDevice(&port->port, addr);
|
||||
if (downstream != nullptr) {
|
||||
return downstream;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Hub::UsbHub_HandleReset() {
|
||||
USBHubPort* port;
|
||||
|
||||
|
@ -289,181 +273,159 @@ void Hub::UsbHub_HandleReset() {
|
|||
}
|
||||
}
|
||||
}
|
||||
void Hub::UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) {
|
||||
USBHubState * s = (USBHubState *)dev;
|
||||
int ret;
|
||||
ret = m_UsbDev->USBDesc_HandleControl(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* From XQEMU:
|
||||
* This function creates a serial number for a usb device.
|
||||
* The serial number should:
|
||||
* (a) Be unique within the emulator.
|
||||
* (b) Be constant, so you don't get a new one each
|
||||
* time the guest is started.
|
||||
* So we are using the physical location to generate a serial number
|
||||
* from it. It has three pieces: First a fixed, device-specific
|
||||
* prefix. Second the device path of the host controller (which is
|
||||
* the pci address in most cases). Third the physical port path.
|
||||
* Results in serial numbers like this: "314159-0000:00:1d.7-3".
|
||||
*/
|
||||
void Hub::CreateSerial(XboxDeviceState* dev) {
|
||||
const USBDesc* desc = GetUsbDeviceDesc(dev);
|
||||
int index = desc->id.iSerialNumber;
|
||||
USBDescString* s;
|
||||
char serial[64];
|
||||
char* path;
|
||||
int dst;
|
||||
|
||||
assert(index != 0 && desc->str[index] != NULL);
|
||||
dst = std::snprintf(serial, sizeof(serial), "%s", desc->str[index]);
|
||||
dst += std::snprintf(serial + dst, sizeof(serial) - dst, "-%s", m_UsbDev->m_PciPath);
|
||||
dst += std::snprintf(serial + dst, sizeof(serial) - dst, "-%s", dev->Port->Path);
|
||||
|
||||
QLIST_FOREACH(s, &dev->Strings, next) {
|
||||
if (s->index == index) {
|
||||
switch (request) {
|
||||
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
if (value == 0 && index != 0x81) { /* clear ep halt */
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
/* usb specific requests */
|
||||
case GetHubStatus:
|
||||
data[0] = 0;
|
||||
data[1] = 0;
|
||||
data[2] = 0;
|
||||
data[3] = 0;
|
||||
p->actual_length = 4;
|
||||
break;
|
||||
case GetPortStatus:
|
||||
{
|
||||
unsigned int n = index - 1;
|
||||
USBHubPort * port;
|
||||
if (n >= NUM_PORTS) {
|
||||
goto fail;
|
||||
}
|
||||
port = &s->ports[n];
|
||||
trace_usb_hub_get_port_status(s->dev.addr, index,
|
||||
port->wPortStatus,
|
||||
port->wPortChange);
|
||||
data[0] = port->wPortStatus;
|
||||
data[1] = port->wPortStatus >> 8;
|
||||
data[2] = port->wPortChange;
|
||||
data[3] = port->wPortChange >> 8;
|
||||
p->actual_length = 4;
|
||||
}
|
||||
break;
|
||||
case SetHubFeature:
|
||||
case ClearHubFeature:
|
||||
if (value != 0 && value != 1) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case SetPortFeature:
|
||||
{
|
||||
unsigned int n = index - 1;
|
||||
USBHubPort * port;
|
||||
USBDevice * dev;
|
||||
trace_usb_hub_set_port_feature(s->dev.addr, index, feature_name(value));
|
||||
if (n >= NUM_PORTS) {
|
||||
goto fail;
|
||||
}
|
||||
port = &s->ports[n];
|
||||
dev = port->port.dev;
|
||||
switch (value) {
|
||||
case PORT_SUSPEND:
|
||||
port->wPortStatus |= PORT_STAT_SUSPEND;
|
||||
break;
|
||||
case PORT_RESET:
|
||||
if (dev && dev->attached) {
|
||||
usb_device_reset(dev);
|
||||
port->wPortChange |= PORT_STAT_C_RESET;
|
||||
/* set enable bit */
|
||||
port->wPortStatus |= PORT_STAT_ENABLE;
|
||||
usb_wakeup(s->intr, 0);
|
||||
}
|
||||
break;
|
||||
case PORT_POWER:
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (s == nullptr) {
|
||||
s = new USBDescString;
|
||||
s->index = index;
|
||||
QLIST_INSERT_HEAD(&dev->Strings, s, next);
|
||||
}
|
||||
|
||||
s->str = serial;
|
||||
}
|
||||
|
||||
const USBDesc* Hub::GetUsbDeviceDesc(XboxDeviceState* dev) {
|
||||
USBDeviceClass* klass = dev->klass;
|
||||
if (dev->UsbDesc) {
|
||||
return dev->UsbDesc;
|
||||
}
|
||||
return klass->usb_desc;
|
||||
}
|
||||
|
||||
void Hub::UsbDescInit(XboxDeviceState* dev) {
|
||||
const USBDesc* desc = GetUsbDeviceDesc(dev);
|
||||
|
||||
assert(desc != NULL);
|
||||
dev->Speed = USB_SPEED_FULL;
|
||||
dev->SpeedMask = 0;
|
||||
if (desc->full) {
|
||||
dev->SpeedMask |= USB_SPEED_MASK_FULL;
|
||||
}
|
||||
UsbDescSetDefaults(dev);
|
||||
}
|
||||
|
||||
void Hub::UsbDescSetDefaults(XboxDeviceState* dev) {
|
||||
const USBDesc *desc = GetUsbDeviceDesc(dev);
|
||||
|
||||
assert(desc != NULL);
|
||||
switch (dev->Speed) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
{
|
||||
dev->Device = desc->full;
|
||||
unsigned int n, limit, var_hub_size = 0;
|
||||
memcpy(data, qemu_hub_hub_descriptor,
|
||||
sizeof(qemu_hub_hub_descriptor));
|
||||
data[2] = NUM_PORTS;
|
||||
/* fill DeviceRemovable bits */
|
||||
limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
|
||||
for (n = 7; n < limit; n++) {
|
||||
data[n] = 0x00;
|
||||
var_hub_size++;
|
||||
}
|
||||
/* fill PortPwrCtrlMask bits */
|
||||
limit = limit + ((NUM_PORTS + 7) / 8);
|
||||
for (; n < limit; n++) {
|
||||
data[n] = 0xff;
|
||||
var_hub_size++;
|
||||
} p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
|
||||
data[0] = p->actual_length;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_warning("Unknown speed parameter %d set in %s", dev->ProductDesc.c_str());
|
||||
fail:
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
UsbDescSetConfig(dev, 0);
|
||||
}
|
||||
|
||||
int Hub::UsbDescSetConfig(XboxDeviceState* dev, int value) {
|
||||
int i;
|
||||
void Hub::UsbHub_Attach(USBPort* port1) {
|
||||
USBHubPort* port = &m_HubState->ports[port1->PortIndex];
|
||||
|
||||
if (value == 0) { // default configuration
|
||||
dev->Configuration = 0;
|
||||
dev->NumInterfaces = 0;
|
||||
dev->Config = nullptr;
|
||||
port->wPortStatus |= PORT_STAT_CONNECTION;
|
||||
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
||||
if (port->port.Dev->Speed == USB_SPEED_LOW) {
|
||||
port->wPortStatus |= PORT_STAT_LOW_SPEED;
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < dev->Device->bNumConfigurations; i++) { // select the configuration specified
|
||||
if (dev->Device->confs[i].bConfigurationValue == value) {
|
||||
dev->Configuration = value;
|
||||
dev->NumInterfaces = dev->Device->confs[i].bNumInterfaces;
|
||||
dev->Config = dev->Device->confs + i;
|
||||
assert(dev->NumInterfaces <= USB_MAX_INTERFACES);
|
||||
}
|
||||
}
|
||||
if (i < dev->Device->bNumConfigurations) {
|
||||
return -1;
|
||||
}
|
||||
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
|
||||
}
|
||||
|
||||
for (i = 0; i < dev->NumInterfaces; i++) { // setup all interfaces for the selected configuration
|
||||
UsbDescSetInterface(dev, i, 0);
|
||||
}
|
||||
for (; i < USB_MAX_INTERFACES; i++) { // null the remaining interfaces
|
||||
dev->AltSetting[i] = 0;
|
||||
dev->Ifaces[i] = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
m_UsbDev->USB_Wakeup(m_HubState->intr);
|
||||
}
|
||||
|
||||
int Hub::UsbDescSetInterface(XboxDeviceState* dev, int index, int value) {
|
||||
const USBDescIface* iface;
|
||||
int old;
|
||||
void Hub::UsbHub_Detach(USBPort* port1) {
|
||||
USBHubPort* port = &m_HubState->ports[port1->PortIndex];
|
||||
|
||||
iface = UsbDescFindInterface(dev, index, value);
|
||||
if (iface == nullptr) {
|
||||
return -1;
|
||||
m_UsbDev->USB_Wakeup(m_HubState->intr);
|
||||
|
||||
// Let upstream know the device on this port is gone
|
||||
m_HubState->dev.Port->Operations->child_detach(port1->Dev);
|
||||
|
||||
port->wPortStatus &= ~PORT_STAT_CONNECTION;
|
||||
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
||||
if (port->wPortStatus & PORT_STAT_ENABLE) {
|
||||
port->wPortStatus &= ~PORT_STAT_ENABLE;
|
||||
port->wPortChange |= PORT_STAT_C_ENABLE;
|
||||
}
|
||||
|
||||
old = dev->AltSetting[index];
|
||||
dev->AltSetting[index] = value;
|
||||
dev->Ifaces[index] = iface;
|
||||
UsbDescEpInit(dev);
|
||||
|
||||
if (old != value) {
|
||||
m_UsbDev->USB_DeviceSetInterface(dev, index, old, value);
|
||||
}
|
||||
return 0;
|
||||
m_UsbDev->USB_Wakeup(m_HubState->intr);
|
||||
}
|
||||
|
||||
const USBDescIface* Hub::UsbDescFindInterface(XboxDeviceState* dev, int nif, int alt) {
|
||||
const USBDescIface* iface;
|
||||
int i;
|
||||
|
||||
if (!dev->Config) { // no configuration descriptor here, nothing to search
|
||||
return nullptr;
|
||||
}
|
||||
for (i = 0; i < dev->Config->nif; i++) { // find the desired interface
|
||||
iface = &dev->Config->ifs[i];
|
||||
if (iface->bInterfaceNumber == nif &&
|
||||
iface->bAlternateSetting == alt) {
|
||||
return iface;
|
||||
}
|
||||
}
|
||||
return nullptr; // not found
|
||||
void Hub::UsbHub_ChildDetach(XboxDeviceState* child) {
|
||||
// Pass along to upstream
|
||||
m_HubState->dev.Port->Operations->child_detach(child);
|
||||
}
|
||||
|
||||
void Hub::UsbDescEpInit(XboxDeviceState* dev) {
|
||||
const USBDescIface *iface;
|
||||
int i, e, pid, ep;
|
||||
void Hub::UsbHub_Wakeup(USBPort* port1) {
|
||||
USBHubPort* port = &m_HubState->ports[port1->PortIndex];
|
||||
|
||||
UsbEpInit(); // reset endpoints (because we changed descriptors in use?)
|
||||
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:
|
||||
// "bEndpointAddress:
|
||||
// Bit 3...0: The endpoint number
|
||||
// Bit 6...4: Reserved, reset to zero
|
||||
// Bit 7: Direction -> 0 = OUT endpoint, 1 = IN endpoint
|
||||
// bmAttributes:
|
||||
// Bit 1..0: Transfer Type
|
||||
// 00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt. All other bits are reserved"
|
||||
pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
||||
ep = iface->eps[e].bEndpointAddress & 0xF;
|
||||
m_UsbDev->USB_EPsetType(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
|
||||
m_UsbDev->USB_EPsetIfnum(dev, pid, ep, iface->bInterfaceNumber);
|
||||
m_UsbDev->USB_EPsetMaxPacketSize(dev, pid, ep, iface->eps[e].wMaxPacketSize);
|
||||
}
|
||||
if (port->wPortStatus & PORT_STAT_SUSPEND) {
|
||||
port->wPortChange |= PORT_STAT_C_SUSPEND;
|
||||
m_UsbDev->USB_Wakeup(m_HubState->intr);
|
||||
}
|
||||
}
|
||||
|
||||
void Hub::UsbHub_Complete(USBPort* port, USBPacket* packet) {
|
||||
// Just pass it along to upstream
|
||||
m_HubState->dev.Port->Operations->complete(m_HubState->dev.Port, packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,10 +81,10 @@ struct USBHubState {
|
|||
};
|
||||
|
||||
/* Class which implements a usb hub */
|
||||
class Hub final : public UsbPeripheral {
|
||||
class Hub {
|
||||
public:
|
||||
// initialize this peripheral
|
||||
int Init(int pport) override;
|
||||
int Init(int pport);
|
||||
// destructor
|
||||
~Hub();
|
||||
|
||||
|
@ -94,9 +94,11 @@ private:
|
|||
USBPCIDevice* m_UsbDev = nullptr;
|
||||
// hub state
|
||||
USBHubState* m_HubState = nullptr;
|
||||
// hub class functions
|
||||
USBDeviceClass* m_pPeripheralFuncStruct = nullptr;
|
||||
|
||||
// initialize various member variables/functions
|
||||
void ClassInitFn();
|
||||
XboxDeviceState* ClassInitFn();
|
||||
// see USBDeviceClass for comments about these functions
|
||||
int UsbHub_Initfn(XboxDeviceState* dev);
|
||||
XboxDeviceState* UsbHub_FindDevice(XboxDeviceState* dev, uint8_t addr);
|
||||
|
@ -108,34 +110,13 @@ private:
|
|||
// see USBPortOps struct for info
|
||||
void UsbHub_Attach(USBPort* port1);
|
||||
void UsbHub_Detach(USBPort* port1);
|
||||
void UsbHub_ChildDetach(USBPort* port1, XboxDeviceState* child);
|
||||
void UsbHub_ChildDetach(XboxDeviceState* child);
|
||||
void UsbHub_Wakeup(USBPort* port1);
|
||||
void UsbHub_Complete(USBPort* port, USBPacket* packet);
|
||||
// TODO: perhaps these can be put in UsbPeripheral or USBDevice...
|
||||
// initialize the endpoints of this peripheral
|
||||
void UsbEpInit();
|
||||
// reset all endpoints of this peripheral
|
||||
void UsbEpReset();
|
||||
// reserve a usb port for this hub
|
||||
int UsbClaimPort(int port);
|
||||
//
|
||||
void UsbReleasePort(XboxDeviceState* dev);
|
||||
// get device descriptor
|
||||
const USBDesc* GetUsbDeviceDesc(XboxDeviceState* dev);
|
||||
// create a serial number for the device
|
||||
void CreateSerial(XboxDeviceState* dev);
|
||||
// start descriptors initialization
|
||||
void UsbDescInit(XboxDeviceState* dev);
|
||||
// set the descriptors to use for this device
|
||||
void UsbDescSetDefaults(XboxDeviceState* dev);
|
||||
// set the configuration to use
|
||||
int UsbDescSetConfig(XboxDeviceState* dev, int value);
|
||||
// set the interface to use
|
||||
int UsbDescSetInterface(XboxDeviceState* dev, int index, int value);
|
||||
// find the interface to use
|
||||
const USBDescIface* UsbDescFindInterface(XboxDeviceState* dev, int nif, int alt);
|
||||
// setup endpoints and their descriptors
|
||||
void UsbDescEpInit(XboxDeviceState* dev);
|
||||
int UsbHubClaimPort(XboxDeviceState* dev, int port);
|
||||
// free the usb port used by this hub
|
||||
void UsbHubReleasePort(XboxDeviceState* dev);
|
||||
};
|
||||
|
||||
extern Hub* g_HubObjArray[4];
|
||||
|
|
|
@ -116,15 +116,6 @@ void USBPCIDevice::USB_PortReset(USBPort* Port) {
|
|||
USB_DeviceReset(dev);
|
||||
}
|
||||
|
||||
void USBPCIDevice::USB_Detach(USBPort* Port) {
|
||||
XboxDeviceState* dev = Port->Dev;
|
||||
|
||||
assert(dev != nullptr);
|
||||
assert(dev->State != USB_STATE_NOTATTACHED);
|
||||
m_HostController->OHCI_Detach(Port);
|
||||
dev->State = USB_STATE_NOTATTACHED;
|
||||
}
|
||||
|
||||
void USBPCIDevice::USB_Attach(USBPort* Port) {
|
||||
XboxDeviceState* dev = Port->Dev;
|
||||
|
||||
|
@ -136,6 +127,23 @@ void USBPCIDevice::USB_Attach(USBPort* Port) {
|
|||
USB_DeviceHandleAttach(dev);
|
||||
}
|
||||
|
||||
void USBPCIDevice::USB_Detach(USBPort* Port) {
|
||||
XboxDeviceState* dev = Port->Dev;
|
||||
|
||||
assert(dev != nullptr);
|
||||
assert(dev->State != USB_STATE_NOTATTACHED);
|
||||
Port->Operations->detach(Port);
|
||||
dev->State = USB_STATE_NOTATTACHED;
|
||||
}
|
||||
|
||||
void USBPCIDevice::USB_Wakeup(USBEndpoint* ep) {
|
||||
XboxDeviceState* dev = ep->Dev;
|
||||
|
||||
if (dev->RemoteWakeup && dev->Port && dev->Port->Operations->wakeup) {
|
||||
dev->Port->Operations->wakeup(dev->Port);
|
||||
}
|
||||
}
|
||||
|
||||
void USBPCIDevice::USB_DeviceReset(XboxDeviceState* dev) {
|
||||
if (dev == nullptr || !dev->Attached) {
|
||||
return;
|
||||
|
@ -312,17 +320,17 @@ void USBPCIDevice::USB_DoParameter(XboxDeviceState* s, USBPacket* p) {
|
|||
value = (s->SetupBuffer[3] << 8) | s->SetupBuffer[2];
|
||||
index = (s->SetupBuffer[5] << 8) | s->SetupBuffer[4];
|
||||
|
||||
if (s->SetupLength > sizeof(s->data_buf)) {
|
||||
log_debug("USB: ctrl buffer too small (%d > %zu)\n", s->SetupLength, sizeof(s->data_buf));
|
||||
if (s->SetupLength > sizeof(s->DataBuffer)) {
|
||||
log_debug("USB: 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->data_buf, s->SetupLength);
|
||||
USB_PacketCopy(p, s->DataBuffer, s->SetupLength);
|
||||
}
|
||||
|
||||
USB_DeviceHandleControl(s, p, request, value, index, s->SetupLength, s->data_buf);
|
||||
USB_DeviceHandleControl(s, p, request, value, index, s->SetupLength, s->DataBuffer);
|
||||
if (p->Status == USB_RET_ASYNC) {
|
||||
return;
|
||||
}
|
||||
|
@ -332,7 +340,7 @@ void USBPCIDevice::USB_DoParameter(XboxDeviceState* s, USBPacket* p) {
|
|||
}
|
||||
if (p->Pid == USB_TOKEN_IN) {
|
||||
p->ActualLength = 0;
|
||||
USB_PacketCopy(p, s->data_buf, s->SetupLength);
|
||||
USB_PacketCopy(p, s->DataBuffer, s->SetupLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,7 +371,7 @@ void USBPCIDevice::USB_DoTokenSetup(XboxDeviceState* s, USBPacket* p) {
|
|||
index = (s->SetupBuffer[5] << 8) | s->SetupBuffer[4];
|
||||
|
||||
if (s->SetupBuffer[0] & USB_DIR_IN) {
|
||||
USB_DeviceHandleControl(s, p, request, value, index, s->SetupLength, s->data_buf);
|
||||
USB_DeviceHandleControl(s, p, request, value, index, s->SetupLength, s->DataBuffer);
|
||||
if (p->Status == USB_RET_ASYNC) {
|
||||
s->SetupState = SETUP_STATE_SETUP;
|
||||
}
|
||||
|
@ -377,8 +385,8 @@ void USBPCIDevice::USB_DoTokenSetup(XboxDeviceState* s, USBPacket* p) {
|
|||
s->SetupState = SETUP_STATE_DATA;
|
||||
}
|
||||
else {
|
||||
if (s->SetupLength > sizeof(s->data_buf)) {
|
||||
log_debug("USB: ctrl buffer too small (%d > %zu)\n", s->SetupLength, sizeof(s->data_buf));
|
||||
if (s->SetupLength > sizeof(s->DataBuffer)) {
|
||||
log_debug("USB: ctrl buffer too small (%d > %zu)\n", s->SetupLength, sizeof(s->DataBuffer));
|
||||
p->Status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
@ -405,7 +413,7 @@ void USBPCIDevice::DoTokenIn(XboxDeviceState* s, USBPacket* p) {
|
|||
switch (s->SetupState) {
|
||||
case SETUP_STATE_ACK:
|
||||
if (!(s->SetupBuffer[0] & USB_DIR_IN)) {
|
||||
USB_DeviceHandleControl(s, p, request, value, index, s->SetupLength, s->data_buf);
|
||||
USB_DeviceHandleControl(s, p, request, value, index, s->SetupLength, s->DataBuffer);
|
||||
if (p->Status == USB_RET_ASYNC) {
|
||||
return;
|
||||
}
|
||||
|
@ -420,7 +428,7 @@ void USBPCIDevice::DoTokenIn(XboxDeviceState* s, USBPacket* p) {
|
|||
if (len > p->IoVec.Size) {
|
||||
len = p->IoVec.Size;
|
||||
}
|
||||
USB_PacketCopy(p, s->data_buf + s->SetupIndex, len);
|
||||
USB_PacketCopy(p, s->DataBuffer + s->SetupIndex, len);
|
||||
s->SetupIndex += len;
|
||||
if (s->SetupIndex >= s->SetupLength) {
|
||||
s->SetupState = SETUP_STATE_ACK;
|
||||
|
@ -456,7 +464,7 @@ void USBPCIDevice::DoTokenOut(XboxDeviceState* s, USBPacket* p) {
|
|||
if (len > p->IoVec.Size) {
|
||||
len = p->IoVec.Size;
|
||||
}
|
||||
USB_PacketCopy(p, s->data_buf + s->SetupIndex, len);
|
||||
USB_PacketCopy(p, s->DataBuffer + s->SetupIndex, len);
|
||||
s->SetupIndex += len;
|
||||
if (s->SetupIndex >= s->SetupLength) {
|
||||
s->SetupState = SETUP_STATE_ACK;
|
||||
|
@ -626,4 +634,605 @@ void USBPCIDevice::USB_DeviceAttach(XboxDeviceState* dev) {
|
|||
USB_Attach(port);
|
||||
}
|
||||
|
||||
void USBPCIDevice::USB_EpInit(XboxDeviceState* dev) {
|
||||
USB_EpReset(dev);
|
||||
QTAILQ_INIT(&dev->EP_ctl.Queue);
|
||||
for (int ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
|
||||
QTAILQ_INIT(&dev->EP_in[ep].Queue);
|
||||
QTAILQ_INIT(&dev->EP_out[ep].Queue);
|
||||
}
|
||||
}
|
||||
|
||||
void USBPCIDevice::USB_EpReset(XboxDeviceState* dev) {
|
||||
dev->EP_ctl.Num = 0;
|
||||
dev->EP_ctl.Type = USB_ENDPOINT_XFER_CONTROL;
|
||||
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;
|
||||
dev->EP_out[ep].IfNum = USB_INTERFACE_INVALID;
|
||||
dev->EP_in[ep].MaxPacketSize = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* From XQEMU:
|
||||
* This function creates a serial number for a usb device.
|
||||
* The serial number should:
|
||||
* (a) Be unique within the emulator.
|
||||
* (b) Be constant, so you don't get a new one each
|
||||
* time the guest is started.
|
||||
* So we are using the physical location to generate a serial number
|
||||
* from it. It has three pieces: First a fixed, device-specific
|
||||
* prefix. Second the device path of the host controller (which is
|
||||
* the pci address in most cases). Third the physical port path.
|
||||
* Results in serial numbers like this: "314159-0000:00:1d.7-3".
|
||||
*/
|
||||
void USBPCIDevice::USB_CreateSerial(XboxDeviceState* dev, const char* str) {
|
||||
const USBDesc* desc = USBDesc_GetUsbDeviceDesc(dev);
|
||||
int index = desc->id.iSerialNumber;
|
||||
USBDescString* s;
|
||||
char serial[64];
|
||||
char* path;
|
||||
int dst;
|
||||
|
||||
assert(index != 0 && str != nullptr);
|
||||
dst = std::snprintf(serial, sizeof(serial), "%s", str);
|
||||
dst += std::snprintf(serial + dst, sizeof(serial) - dst, "-%s", m_PciPath);
|
||||
std::snprintf(serial + dst, sizeof(serial) - dst, "-%s", dev->Port->Path);
|
||||
|
||||
USBDesc_SetString(dev, index, serial);
|
||||
}
|
||||
|
||||
const USBDesc* USBPCIDevice::USBDesc_GetUsbDeviceDesc(XboxDeviceState* dev) {
|
||||
USBDeviceClass* klass = dev->klass;
|
||||
if (dev->UsbDesc) {
|
||||
return dev->UsbDesc;
|
||||
}
|
||||
return klass->usb_desc;
|
||||
}
|
||||
|
||||
void USBPCIDevice::USBDesc_Init(XboxDeviceState* dev) {
|
||||
const USBDesc* desc = USBDesc_GetUsbDeviceDesc(dev);
|
||||
|
||||
assert(desc != NULL);
|
||||
dev->Speed = USB_SPEED_FULL;
|
||||
dev->SpeedMask = 0;
|
||||
if (desc->full) {
|
||||
dev->SpeedMask |= USB_SPEED_MASK_FULL;
|
||||
}
|
||||
USBDesc_SetDefaults(dev);
|
||||
}
|
||||
|
||||
void USBPCIDevice::USBDesc_SetDefaults(XboxDeviceState* dev) {
|
||||
const USBDesc *desc = USBDesc_GetUsbDeviceDesc(dev);
|
||||
|
||||
assert(desc != NULL);
|
||||
switch (dev->Speed) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
{
|
||||
dev->Device = desc->full;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_warning("Unknown speed parameter %d set in %s", dev->ProductDesc.c_str());
|
||||
}
|
||||
USBDesc_SetConfig(dev, 0);
|
||||
}
|
||||
|
||||
int USBPCIDevice::USBDesc_SetConfig(XboxDeviceState* dev, int value) {
|
||||
int i;
|
||||
|
||||
if (value == 0) { // default configuration
|
||||
dev->Configuration = 0;
|
||||
dev->NumInterfaces = 0;
|
||||
dev->Config = nullptr;
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < dev->Device->bNumConfigurations; i++) { // select the configuration specified
|
||||
if (dev->Device->confs[i].bConfigurationValue == value) {
|
||||
dev->Configuration = value;
|
||||
dev->NumInterfaces = dev->Device->confs[i].bNumInterfaces;
|
||||
dev->Config = dev->Device->confs + i;
|
||||
assert(dev->NumInterfaces <= USB_MAX_INTERFACES);
|
||||
}
|
||||
}
|
||||
if (i < dev->Device->bNumConfigurations) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < dev->NumInterfaces; i++) { // setup all interfaces for the selected configuration
|
||||
USBDesc_SetInterface(dev, i, 0);
|
||||
}
|
||||
for (; i < USB_MAX_INTERFACES; i++) { // null the remaining interfaces
|
||||
dev->AltSetting[i] = 0;
|
||||
dev->Ifaces[i] = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int USBPCIDevice::USBDesc_SetInterface(XboxDeviceState* dev, int index, int value) {
|
||||
const USBDescIface* iface;
|
||||
int old;
|
||||
|
||||
iface = USBDesc_FindInterface(dev, index, value);
|
||||
if (iface == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
old = dev->AltSetting[index];
|
||||
dev->AltSetting[index] = value;
|
||||
dev->Ifaces[index] = iface;
|
||||
USBDesc_EpInit(dev);
|
||||
|
||||
if (old != value) {
|
||||
USB_DeviceSetInterface(dev, index, old, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const USBDescIface* USBPCIDevice::USBDesc_FindInterface(XboxDeviceState* dev, int nif, int alt) {
|
||||
const USBDescIface* iface;
|
||||
int i;
|
||||
|
||||
if (!dev->Config) { // no configuration descriptor here, nothing to search
|
||||
return nullptr;
|
||||
}
|
||||
for (i = 0; i < dev->Config->nif; i++) { // find the desired interface
|
||||
iface = &dev->Config->ifs[i];
|
||||
if (iface->bInterfaceNumber == nif &&
|
||||
iface->bAlternateSetting == alt) {
|
||||
return iface;
|
||||
}
|
||||
}
|
||||
return nullptr; // not found
|
||||
}
|
||||
|
||||
void USBPCIDevice::USBDesc_EpInit(XboxDeviceState* dev) {
|
||||
const USBDescIface *iface;
|
||||
int i, e, pid, ep;
|
||||
|
||||
USB_EpInit(dev); // reset endpoints (because we changed descriptors in use?)
|
||||
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:
|
||||
// "bEndpointAddress:
|
||||
// Bit 3...0: The endpoint number
|
||||
// Bit 6...4: Reserved, reset to zero
|
||||
// Bit 7: Direction -> 0 = OUT endpoint, 1 = IN endpoint
|
||||
// bmAttributes:
|
||||
// Bit 1..0: Transfer Type
|
||||
// 00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt. All other bits are reserved"
|
||||
pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
||||
ep = iface->eps[e].bEndpointAddress & 0xF;
|
||||
USB_EPsetType(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
|
||||
USB_EPsetIfnum(dev, pid, ep, iface->bInterfaceNumber);
|
||||
USB_EPsetMaxPacketSize(dev, pid, ep, iface->eps[e].wMaxPacketSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int USBPCIDevice::USBDesc_HandleControl(XboxDeviceState* dev, USBPacket *p, int request, int value, int index, int length, uint8_t* data) {
|
||||
const USBDesc* desc = USBDesc_GetUsbDeviceDesc(dev);
|
||||
int ret = -1;
|
||||
|
||||
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.
|
||||
// The wValue field specifies the device address to use for all subsequent accesses"
|
||||
dev->Addr = value;
|
||||
log_debug("Address 0x%X set for device %s", 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.
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
||||
{
|
||||
// From the 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;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
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.
|
||||
// This configuration value must be zero or match a configuration value from a configuration descriptor"
|
||||
ret = USBDesc_SetConfig(dev, value);
|
||||
log_debug("Received standard SetConfiguration() request for device at address 0x%X. Configuration selected is %d and returned %d",
|
||||
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
|
||||
// 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
|
||||
* the device is in the Default state is not specified.
|
||||
* We return the same value that a configured device would return if
|
||||
* it used the first configuration. */
|
||||
const USBDescConfig* config = dev->Config ? dev->Config : &dev->Device->confs[0];
|
||||
data[0] = 0;
|
||||
if (config->bmAttributes & 0x40) {
|
||||
data[0] |= 1 << USB_DEVICE_SELF_POWERED;
|
||||
}
|
||||
if (dev->RemoteWakeup) {
|
||||
data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
|
||||
}
|
||||
data[1] = 0x00;
|
||||
p->ActualLength = 2;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
{
|
||||
// From the 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;
|
||||
}
|
||||
log_debug("Received standard ClearFeature() request for device at address 0x%X. Feature selected is %d and returned %d",
|
||||
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.
|
||||
// Feature selector values in wValue must be appropriate to the recipient"
|
||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||
dev->RemoteWakeup = 1;
|
||||
ret = 0;
|
||||
}
|
||||
log_debug("Received standard SetFeature() request for device at address 0x%X. Feature selected is %d and returned %d",
|
||||
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.
|
||||
// wValue = Zero; wIndex = Interface"
|
||||
if (index < 0 || index >= dev->NumInterfaces) {
|
||||
break;
|
||||
}
|
||||
data[0] = dev->AltSetting[index];
|
||||
p->ActualLength = 1;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
||||
{
|
||||
// From the 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);
|
||||
log_debug("Received standard SetInterface() request for device at address 0x%X. Interface selected is %d, Alternative Setting is %d and returned %d", dev->Addr, index, value, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int USBPCIDevice::USBDesc_HandleStandardGetDescriptor(XboxDeviceState* dev, USBPacket* p,
|
||||
int value, uint8_t* dest, size_t len) {
|
||||
const USBDesc* desc = USBDesc_GetUsbDeviceDesc(dev);
|
||||
uint8_t buf[256];
|
||||
uint8_t type = value >> 8; // recover descriptor type from wValue
|
||||
uint8_t index = value & 0xFF; // recover descriptor index from wValue
|
||||
int ret = -1;
|
||||
int flags = 0;
|
||||
|
||||
// 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."
|
||||
|
||||
switch (type) {
|
||||
case USB_DT_DEVICE:
|
||||
{
|
||||
ret = USB_ReadDeviceDesc(&desc->id, dev->Device, buf, sizeof(buf));
|
||||
log_debug("Read operation of device descriptor of device 0x%X returns %d", dev->Addr, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_DT_CONFIG:
|
||||
{
|
||||
if (index < dev->Device->bNumConfigurations) {
|
||||
ret = USB_ReadConfigurationDesc(dev->Device->confs + index, flags, buf, sizeof(buf));
|
||||
}
|
||||
log_debug("Read operation of configuration descriptor %d of device 0x%X returns %d", index, dev->Addr, ret);
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_DT_STRING:
|
||||
{
|
||||
ret = USB_ReadStringDesc(dev, index, buf, sizeof(buf));
|
||||
log_debug("Read operation of string descriptor %d of device 0x%X returns %d", index, dev->Addr, ret);
|
||||
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,
|
||||
// USB_DT_BOS (15) and USB_DT_DEBUG (10) -> usb 3.0 only
|
||||
|
||||
default:
|
||||
log_warning("%s: device address %d unknown type %d (len %zd)", __func__, dev->Addr, type, len);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
if (ret > len) {
|
||||
ret = len;
|
||||
}
|
||||
std::memcpy(dest, buf, ret);
|
||||
p->ActualLength = ret;
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int USBPCIDevice::USB_ReadDeviceDesc(const USBDescID* id, const USBDescDevice* dev, uint8_t* dest, size_t len) {
|
||||
uint8_t bLength = 0x12; // a device descriptor is 18 bytes large
|
||||
USBDescriptor* d = reinterpret_cast<USBDescriptor*>(dest);
|
||||
|
||||
if (len < bLength) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
d->bLength = bLength;
|
||||
d->bDescriptorType = USB_DT_DEVICE;
|
||||
|
||||
d->u.device.bcdUSB_lo = GET_WORD_LOW(dev->bcdUSB);
|
||||
d->u.device.bcdUSB_hi = GET_WORD_HIGH(dev->bcdUSB);
|
||||
d->u.device.bDeviceClass = dev->bDeviceClass;
|
||||
d->u.device.bDeviceSubClass = dev->bDeviceSubClass;
|
||||
d->u.device.bDeviceProtocol = dev->bDeviceProtocol;
|
||||
d->u.device.bMaxPacketSize0 = dev->bMaxPacketSize0;
|
||||
|
||||
d->u.device.idVendor_lo = GET_WORD_LOW(id->idVendor);
|
||||
d->u.device.idVendor_hi = GET_WORD_HIGH(id->idVendor);
|
||||
d->u.device.idProduct_lo = GET_WORD_LOW(id->idProduct);
|
||||
d->u.device.idProduct_hi = GET_WORD_HIGH(id->idProduct);
|
||||
d->u.device.bcdDevice_lo = GET_WORD_LOW(id->bcdDevice);
|
||||
d->u.device.bcdDevice_hi = GET_WORD_HIGH(id->bcdDevice);
|
||||
d->u.device.iManufacturer = id->iManufacturer;
|
||||
d->u.device.iProduct = id->iProduct;
|
||||
d->u.device.iSerialNumber = id->iSerialNumber;
|
||||
|
||||
d->u.device.bNumConfigurations = dev->bNumConfigurations;
|
||||
|
||||
return bLength;
|
||||
}
|
||||
|
||||
int USBPCIDevice::USB_ReadConfigurationDesc(const USBDescConfig* conf, int flags, uint8_t* dest, size_t len) {
|
||||
uint8_t bLength = 0x09; // a configuration descriptor is 9 bytes large
|
||||
uint16_t wTotalLength = 0;
|
||||
USBDescriptor* d = reinterpret_cast<USBDescriptor*>(dest);
|
||||
int i, rc;
|
||||
|
||||
if (len < bLength) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// From the 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;
|
||||
d->bDescriptorType = USB_DT_CONFIG;
|
||||
|
||||
d->u.config.bNumInterfaces = conf->bNumInterfaces;
|
||||
d->u.config.bConfigurationValue = conf->bConfigurationValue;
|
||||
d->u.config.iConfiguration = conf->iConfiguration;
|
||||
d->u.config.bmAttributes = conf->bmAttributes;
|
||||
d->u.config.bMaxPower = conf->bMaxPower;
|
||||
wTotalLength += bLength;
|
||||
|
||||
for (i = 0; i < conf->nif; i++) {
|
||||
rc = USB_ReadInterfaceDesc(conf->ifs + i, flags, dest + wTotalLength, len - wTotalLength);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
wTotalLength += rc;
|
||||
}
|
||||
|
||||
d->u.config.wTotalLength_lo = GET_WORD_LOW(wTotalLength);
|
||||
d->u.config.wTotalLength_hi = GET_WORD_HIGH(wTotalLength);
|
||||
return wTotalLength;
|
||||
}
|
||||
|
||||
int USBPCIDevice::USB_ReadInterfaceDesc(const USBDescIface* iface, int flags, uint8_t* dest, size_t len) {
|
||||
uint8_t bLength = 0x09; // a interface descriptor is 9 bytes large
|
||||
int i, rc, pos = 0;
|
||||
USBDescriptor* d = reinterpret_cast<USBDescriptor*>(dest);
|
||||
|
||||
if (len < bLength) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// From the standard: "The first interface descriptor follows the configuration descriptor.
|
||||
// The endpoint descriptors for the first interface follow the first interface descriptor.
|
||||
// If there are additional interfaces, their interface descriptor and endpoint descriptors
|
||||
// follow the first interface's endpoint descriptors. Class-specific and/or vendor-specific
|
||||
// descriptors follow the standard descriptors they extend or modify."
|
||||
|
||||
d->bLength = bLength;
|
||||
d->bDescriptorType = USB_DT_INTERFACE;
|
||||
|
||||
d->u.iface.bInterfaceNumber = iface->bInterfaceNumber;
|
||||
d->u.iface.bAlternateSetting = iface->bAlternateSetting;
|
||||
d->u.iface.bNumEndpoints = iface->bNumEndpoints;
|
||||
d->u.iface.bInterfaceClass = iface->bInterfaceClass;
|
||||
d->u.iface.bInterfaceSubClass = iface->bInterfaceSubClass;
|
||||
d->u.iface.bInterfaceProtocol = iface->bInterfaceProtocol;
|
||||
d->u.iface.iInterface = iface->iInterface;
|
||||
pos += bLength;
|
||||
|
||||
for (i = 0; i < iface->ndesc; i++) {
|
||||
rc = USB_ReadOtherDesc(iface->descs + i, dest + pos, len - pos);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
pos += rc;
|
||||
}
|
||||
|
||||
for (i = 0; i < iface->bNumEndpoints; i++) {
|
||||
rc = USB_ReadEndpointDesc(iface->eps + i, flags, dest + pos, len - pos);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
pos += rc;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int USBPCIDevice::USB_ReadOtherDesc(const USBDescOther* desc, uint8_t* dest, size_t len) {
|
||||
int bLength = desc->length ? desc->length : desc->data[0];
|
||||
|
||||
if (len < bLength) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::memcpy(dest, desc->data, bLength);
|
||||
return bLength;
|
||||
}
|
||||
|
||||
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;
|
||||
USBDescriptor* d = reinterpret_cast<USBDescriptor*>(dest);
|
||||
|
||||
if (len < bLength + extralen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
d->bLength = bLength;
|
||||
d->bDescriptorType = USB_DT_ENDPOINT;
|
||||
|
||||
d->u.endpoint.bEndpointAddress = ep->bEndpointAddress;
|
||||
d->u.endpoint.bmAttributes = ep->bmAttributes;
|
||||
d->u.endpoint.wMaxPacketSize_lo = GET_WORD_LOW(ep->wMaxPacketSize);
|
||||
d->u.endpoint.wMaxPacketSize_hi = GET_WORD_HIGH(ep->wMaxPacketSize);
|
||||
d->u.endpoint.bInterval = ep->bInterval;
|
||||
if (ep->is_audio) {
|
||||
d->u.endpoint.bRefresh = ep->bRefresh;
|
||||
d->u.endpoint.bSynchAddress = ep->bSynchAddress;
|
||||
}
|
||||
|
||||
// Dropped from XQEMU the reading of SuperSpeed Endpoint Companion descriptors since those are usb 3.0 specific
|
||||
|
||||
if (ep->extra) {
|
||||
std::memcpy(dest + bLength, ep->extra, extralen);
|
||||
}
|
||||
|
||||
return bLength + extralen;
|
||||
}
|
||||
|
||||
int USBPCIDevice::USB_ReadStringDesc(XboxDeviceState* dev, int index, uint8_t* dest, size_t len) {
|
||||
uint8_t bLength, pos, i;
|
||||
const char* str;
|
||||
|
||||
if (len < 4) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// From the 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) {
|
||||
/* language ids */
|
||||
dest[0] = 4;
|
||||
dest[1] = USB_DT_STRING;
|
||||
dest[2] = 0x09;
|
||||
dest[3] = 0x04; // we only support English (United States)
|
||||
return 4;
|
||||
}
|
||||
|
||||
str = USBDesc_GetString(dev, index);
|
||||
if (str == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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;
|
||||
dest[0] = bLength;
|
||||
dest[1] = USB_DT_STRING;
|
||||
i = 0; pos = 2;
|
||||
while (pos + 1 < bLength && pos + 1 < len) {
|
||||
dest[pos++] = str[i++];
|
||||
dest[pos++] = 0;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
void USBPCIDevice::USBDesc_SetString(XboxDeviceState* dev, int index, const char* str) {
|
||||
USBDescString* s;
|
||||
|
||||
QLIST_FOREACH(s, &dev->Strings, next) {
|
||||
if (s->index == index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (s == nullptr) {
|
||||
s = new USBDescString();
|
||||
s->index = index;
|
||||
QLIST_INSERT_HEAD(&dev->Strings, s, next);
|
||||
}
|
||||
|
||||
s->str = str;
|
||||
}
|
||||
|
||||
const char* USBPCIDevice::USBDesc_GetString(XboxDeviceState* dev, int index) {
|
||||
USBDescString* s;
|
||||
|
||||
QLIST_FOREACH(s, &dev->Strings, next) {
|
||||
if (s->index == index) {
|
||||
return s->str.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -94,19 +94,18 @@ public:
|
|||
void USB_Detach(USBPort* Port);
|
||||
// a device downstream from the device attached to the port (attached through a hub) is detached
|
||||
void ChildDetach(USBPort* Port, XboxDeviceState* Child);
|
||||
// TODO
|
||||
void Wakeup(USBPort* Port);
|
||||
// update port status when a device is detached
|
||||
void USB_Wakeup(USBEndpoint* ep);
|
||||
// TODO
|
||||
void Complete(USBPort* Port, USBPacket *P);
|
||||
// reset a device
|
||||
void USB_DeviceReset(XboxDeviceState* Dev);
|
||||
//
|
||||
XboxDeviceState* USB_DeviceFindDevice(XboxDeviceState* Dev, uint8_t Addr);
|
||||
// find the device connected to the supplied port and address
|
||||
XboxDeviceState* USB_FindDevice(USBPort* Port, uint8_t Addr);
|
||||
// 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, unsigned int Stream, 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
|
||||
|
@ -135,7 +134,7 @@ public:
|
|||
// call usb class init function
|
||||
int USB_DeviceInit(XboxDeviceState* dev);
|
||||
// call usb class find_device function
|
||||
XboxDeviceState * USB_FindDevice(USBPort* Port, uint8_t Addr);
|
||||
XboxDeviceState* USB_DeviceFindDevice(XboxDeviceState* Dev, uint8_t Addr);
|
||||
// call usb class cancel_packet function
|
||||
void USB_DeviceCancelPacket(XboxDeviceState* dev, USBPacket* p);
|
||||
// call usb class handle_destroy function
|
||||
|
@ -162,6 +161,46 @@ public:
|
|||
void USB_EPsetMaxPacketSize(XboxDeviceState* dev, int pid, int ep, uint16_t raw);
|
||||
// assign port numbers (also for hubs)
|
||||
void USB_PortLocation(USBPort* downstream, USBPort* upstream, int portnr);
|
||||
// initialize the endpoints of this peripheral
|
||||
void USB_EpInit(XboxDeviceState* dev);
|
||||
// reset all endpoints of this peripheral
|
||||
void USB_EpReset(XboxDeviceState* dev);
|
||||
// create a serial number for the device
|
||||
void USB_CreateSerial(XboxDeviceState* dev, const char* str);
|
||||
// start descriptors initialization
|
||||
void USBDesc_Init(XboxDeviceState* dev);
|
||||
// get device descriptor
|
||||
const USBDesc* USBDesc_GetUsbDeviceDesc(XboxDeviceState* dev);
|
||||
// set the descriptors to use for this device
|
||||
void USBDesc_SetDefaults(XboxDeviceState* dev);
|
||||
// set the configuration to use
|
||||
int USBDesc_SetConfig(XboxDeviceState* dev, int value);
|
||||
// set the interface to use
|
||||
int USBDesc_SetInterface(XboxDeviceState* dev, int index, int value);
|
||||
// find the interface to use
|
||||
const USBDescIface* USBDesc_FindInterface(XboxDeviceState* dev, int nif, int alt);
|
||||
// setup endpoints and their descriptors
|
||||
void USBDesc_EpInit(XboxDeviceState* dev);
|
||||
// handle standard control request
|
||||
int USBDesc_HandleControl(XboxDeviceState *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data);
|
||||
// handle standard GetDescriptor() request
|
||||
int USBDesc_HandleStandardGetDescriptor(XboxDeviceState* dev, USBPacket* p, int value, uint8_t* dest, size_t len);
|
||||
// return the binary rapresentation of a device descriptor
|
||||
int USB_ReadDeviceDesc(const USBDescID* id, const USBDescDevice* dev, uint8_t* dest, size_t len);
|
||||
// return the binary rapresentation of configuration descriptors
|
||||
int USB_ReadConfigurationDesc(const USBDescConfig* conf, int flags, uint8_t* dest, size_t len);
|
||||
// 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);
|
||||
// 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
|
||||
int USB_ReadStringDesc(XboxDeviceState* dev, int index, uint8_t* dest, size_t len);
|
||||
// set a string in the string descriptor with the supplied index
|
||||
void USBDesc_SetString(XboxDeviceState* dev, int index, const char* str);
|
||||
// get a string in the string descriptor with the supplied index
|
||||
const char* USBDesc_GetString(XboxDeviceState* dev, int index);
|
||||
private:
|
||||
uint8_t m_irqn;
|
||||
Cpu* m_cpu;
|
||||
|
|
Loading…
Reference in a new issue