From 36038db4facc0cba368f1b22c9244e57e4b44e24 Mon Sep 17 00:00:00 2001 From: ergo720 Date: Tue, 3 Jul 2018 23:11:41 +0200 Subject: [PATCH] Implemented some hub commands + major refactoring --- src/devices/usb/Hub.cpp | 562 ++++++++++++++++++++-------------- src/devices/usb/Hub.h | 41 +-- src/devices/usb/OHCI.cpp | 4 +- src/devices/usb/OHCI.h | 2 +- src/devices/usb/USBDevice.cpp | 248 ++++++++++++++- src/devices/usb/USBDevice.h | 30 +- src/devices/usb/UsbCommon.h | 21 +- 7 files changed, 612 insertions(+), 296 deletions(-) diff --git a/src/devices/usb/Hub.cpp b/src/devices/usb/Hub.cpp index 166281a40..d0b2ab894 100644 --- a/src/devices/usb/Hub.cpp +++ b/src/devices/usb/Hub.cpp @@ -159,18 +159,18 @@ static const USBDesc desc_hub(true); int Hub::Init(int pport) { - ClassInitFn(); - UsbEpInit(); - int rc = UsbClaimPort(pport); + XboxDeviceState* dev = ClassInitFn(); + m_UsbDev->UsbEpInit(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; } @@ -181,91 +181,57 @@ 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; - - m_pDeviceStruct->ProductDesc = "Cxbx-Reloaded USB Hub"; - QLIST_INIT(&m_pDeviceStruct->Strings); - m_pDeviceStruct->klass = m_pPeripheralFuncStruct; + XboxDeviceState* dev = &m_HubState->dev; + + dev->ProductDesc = "Cxbx-Reloaded USB Hub"; + QLIST_INIT(&dev->Strings); + dev->klass = m_pPeripheralFuncStruct; { using namespace std::placeholders; - + m_pPeripheralFuncStruct->init = std::bind(&Hub::UsbHub_Initfn, this, _1); m_pPeripheralFuncStruct->find_device = std::bind(&Hub::UsbHub_FindDevice, this, _1, _2); m_pPeripheralFuncStruct->handle_reset = std::bind(&Hub::UsbHub_HandleReset, this); 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; - 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; - 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; @@ -286,8 +252,8 @@ int Hub::UsbHub_Initfn(XboxDeviceState* dev) return -1; } - CreateSerial(dev); - UsbDescInit(dev); + m_UsbDev->CreateSerial(dev); + m_UsbDev->UsbDescInit(dev); m_HubState->intr = m_UsbDev->USB_GetEP(dev, USB_TOKEN_IN, 1); ops = new USBPortOps(); @@ -296,7 +262,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); } @@ -310,6 +276,24 @@ 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; @@ -328,185 +312,297 @@ void Hub::UsbHub_HandleReset() } } -/* -* 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) { - break; - } - } - - 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) +void Hub::UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p, + int request, int value, int index, int length, uint8_t* data) { - const USBDesc *desc = GetUsbDeviceDesc(dev); + USBHubState *s = (USBHubState *)dev; + int ret; + + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); + if (ret >= 0) { + return; + } + + 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; + } + } + break; + case ClearPortFeature: + { + unsigned int n = index - 1; + USBHubPort *port; + + trace_usb_hub_clear_port_feature(s->dev.addr, index, + feature_name(value)); + + if (n >= NUM_PORTS) { + goto fail; + } + port = &s->ports[n]; + switch (value) { + case PORT_ENABLE: + port->wPortStatus &= ~PORT_STAT_ENABLE; + break; + case PORT_C_ENABLE: + port->wPortChange &= ~PORT_STAT_C_ENABLE; + break; + case PORT_SUSPEND: + port->wPortStatus &= ~PORT_STAT_SUSPEND; + break; + case PORT_C_SUSPEND: + port->wPortChange &= ~PORT_STAT_C_SUSPEND; + break; + case PORT_C_CONNECTION: + port->wPortChange &= ~PORT_STAT_C_CONNECTION; + break; + case PORT_C_OVERCURRENT: + port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; + break; + case PORT_C_RESET: + port->wPortChange &= ~PORT_STAT_C_RESET; + break; + default: + goto fail; + } + } + break; + case GetHubDescriptor: + { + 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: + fail: + p->status = USB_RET_STALL; + break; + } +} + +void Hub::UsbHub_Attach(USBPort* port1) +{ + USBHubPort* port = &m_HubState->ports[port1->PortIndex]; + + 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 { + port->wPortStatus &= ~PORT_STAT_LOW_SPEED; + } + m_UsbDev->USB_Wakeup(m_HubState->intr); +} + +void Hub::UsbHub_Detach(USBPort* port1) +{ + USBHubPort* port = &m_HubState->ports[port1->PortIndex]; + + 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; + } + m_UsbDev->USB_Wakeup(m_HubState->intr); +} + +void Hub::UsbHub_ChildDetach(XboxDeviceState* child) +{ + // Pass along to upstream + m_HubState->dev.Port->Operations->child_detach(child); +} + +void Hub::UsbHub_Wakeup(USBPort* port1) +{ + USBHubPort* port = &m_HubState->ports[port1->PortIndex]; + + 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); +} + +int usb_desc_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) +{ + const USBDesc *desc = usb_device_get_usb_desc(dev); + int ret = -1; assert(desc != NULL); - switch (dev->Speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: { - dev->Device = desc->full; + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + trace_usb_set_addr(dev->addr); + ret = 0; + break; + + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + ret = usb_desc_get_descriptor(dev, p, value, data, length); + break; + + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + /* + * 9.4.2: 0 should be returned if the device is unconfigured, otherwise + * the non zero value of bConfigurationValue. + */ + data[0] = dev->config ? dev->config->bConfigurationValue : 0; + p->actual_length = 1; + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = usb_desc_set_config(dev, value); + trace_usb_set_config(dev->addr, value, ret); + break; + + case DeviceRequest | USB_REQ_GET_STATUS: { + const USBDescConfig *config = dev->config ? + dev->config : &dev->device->confs[0]; + + data[0] = 0; + /* + * 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. + */ + if (config->bmAttributes & 0x40) { + data[0] |= 1 << USB_DEVICE_SELF_POWERED; + } + if (dev->remote_wakeup) { + data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; + } + data[1] = 0x00; + p->actual_length = 2; + ret = 0; + break; + } + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + ret = 0; + } + trace_usb_clear_device_feature(dev->addr, value, ret); + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + ret = 0; + } + trace_usb_set_device_feature(dev->addr, value, ret); + break; + + case InterfaceRequest | USB_REQ_GET_INTERFACE: + if (index < 0 || index >= dev->ninterfaces) { break; } - default: - EmuWarning("Unknown speed parameter %d set in %s", dev->ProductDesc.c_str()); - } - UsbDescSetConfig(dev, 0); -} + data[0] = dev->altsetting[index]; + p->actual_length = 1; + ret = 0; + break; + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + ret = usb_desc_set_interface(dev, index, value); + trace_usb_set_interface(dev->addr, index, value, ret); + break; -int Hub::UsbDescSetConfig(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 - UsbDescSetInterface(dev, i, 0); - } - for (; i < USB_MAX_INTERFACES; i++) { // null the remaining interfaces - dev->AltSetting[i] = 0; - dev->Ifaces[i] = nullptr; - } - - return 0; -} - -int Hub::UsbDescSetInterface(XboxDeviceState* dev, int index, int value) -{ - const USBDescIface* iface; - int old; - - iface = UsbDescFindInterface(dev, index, value); - if (iface == nullptr) { - return -1; - } - - 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; -} - -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::UsbDescEpInit(XboxDeviceState* dev) -{ - const USBDescIface *iface; - int i, e, pid, ep; - - 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); - } } + return ret; } diff --git a/src/devices/usb/Hub.h b/src/devices/usb/Hub.h index 1e908ad7a..60ff83ec3 100644 --- a/src/devices/usb/Hub.h +++ b/src/devices/usb/Hub.h @@ -49,23 +49,25 @@ typedef enum { /* 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(); private: // usb device this hub is attached to - USBDevice* m_UsbDev = nullptr; + USBDevice* m_UsbDev; // hub state - USBHubState* m_HubState = nullptr; + USBHubState* m_HubState; + // hub class functions + USBDeviceClass* m_pPeripheralFuncStruct; // 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); @@ -77,34 +79,13 @@ class Hub final : public UsbPeripheral // 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]; diff --git a/src/devices/usb/OHCI.cpp b/src/devices/usb/OHCI.cpp index 765455423..2b24b5a44 100644 --- a/src/devices/usb/OHCI.cpp +++ b/src/devices/usb/OHCI.cpp @@ -197,7 +197,7 @@ OHCI::OHCI(int Irq, USBDevice* 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); } diff --git a/src/devices/usb/OHCI.h b/src/devices/usb/OHCI.h index 232d4acc8..85efceca7 100644 --- a/src/devices/usb/OHCI.h +++ b/src/devices/usb/OHCI.h @@ -262,7 +262,7 @@ class OHCI // 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); }; diff --git a/src/devices/usb/USBDevice.cpp b/src/devices/usb/USBDevice.cpp index 3b5414603..c9ca86702 100644 --- a/src/devices/usb/USBDevice.cpp +++ b/src/devices/usb/USBDevice.cpp @@ -104,16 +104,6 @@ void USBDevice::USB_PortReset(USBPort* Port) USB_DeviceReset(dev); } -void USBDevice::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 USBDevice::USB_Attach(USBPort* Port) { XboxDeviceState* dev = Port->Dev; @@ -126,6 +116,25 @@ void USBDevice::USB_Attach(USBPort* Port) USB_DeviceHandleAttach(dev); } +void USBDevice::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 USBDevice::USB_Wakeup(USBEndpoint* ep) +{ + XboxDeviceState* dev = ep->Dev; + + if (dev->RemoteWakeup && dev->Port && dev->Port->Operations->wakeup) { + dev->Port->Operations->wakeup(dev->Port); + } +} + void USBDevice::USB_DeviceReset(XboxDeviceState* dev) { if (dev == nullptr || !dev->Attached) { @@ -644,3 +653,222 @@ void USBDevice::USB_DeviceAttach(XboxDeviceState* dev) dev->Attached++; USB_Attach(port); } + +void USBDevice::UsbEpInit(XboxDeviceState* dev) +{ + UsbEpReset(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 USBDevice::UsbEpReset(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 USBDevice::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_PciPath); + dst += std::snprintf(serial + dst, sizeof(serial) - dst, "-%s", dev->Port->Path); + + 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 = serial; +} + +const USBDesc* USBDevice::GetUsbDeviceDesc(XboxDeviceState* dev) +{ + USBDeviceClass* klass = dev->klass; + if (dev->UsbDesc) { + return dev->UsbDesc; + } + return klass->usb_desc; +} + +void USBDevice::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 USBDevice::UsbDescSetDefaults(XboxDeviceState* dev) +{ + const USBDesc *desc = GetUsbDeviceDesc(dev); + + assert(desc != NULL); + switch (dev->Speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: { + dev->Device = desc->full; + break; + } + default: + EmuWarning("Unknown speed parameter %d set in %s", dev->ProductDesc.c_str()); + } + UsbDescSetConfig(dev, 0); +} + +int USBDevice::UsbDescSetConfig(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 + UsbDescSetInterface(dev, i, 0); + } + for (; i < USB_MAX_INTERFACES; i++) { // null the remaining interfaces + dev->AltSetting[i] = 0; + dev->Ifaces[i] = nullptr; + } + + return 0; +} + +int USBDevice::UsbDescSetInterface(XboxDeviceState* dev, int index, int value) +{ + const USBDescIface* iface; + int old; + + iface = UsbDescFindInterface(dev, index, value); + if (iface == nullptr) { + return -1; + } + + old = dev->AltSetting[index]; + dev->AltSetting[index] = value; + dev->Ifaces[index] = iface; + UsbDescEpInit(dev); + + if (old != value) { + USB_DeviceSetInterface(dev, index, old, value); + } + return 0; +} + +const USBDescIface* USBDevice::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 USBDevice::UsbDescEpInit(XboxDeviceState* dev) +{ + const USBDescIface *iface; + int i, e, pid, ep; + + UsbEpInit(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); + } + } +} diff --git a/src/devices/usb/USBDevice.h b/src/devices/usb/USBDevice.h index 150fd082e..6c75903ab 100644 --- a/src/devices/usb/USBDevice.h +++ b/src/devices/usb/USBDevice.h @@ -80,14 +80,14 @@ class USBDevice : public PCIDevice { 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 @@ -121,7 +121,7 @@ class USBDevice : public PCIDevice { // 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 @@ -148,6 +148,26 @@ class USBDevice : public PCIDevice { 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 UsbEpInit(XboxDeviceState* dev); + // reset all endpoints of this peripheral + void UsbEpReset(XboxDeviceState* dev); + // create a serial number for the device + void CreateSerial(XboxDeviceState* dev); + // start descriptors initialization + void UsbDescInit(XboxDeviceState* dev); + // get device descriptor + const USBDesc* GetUsbDeviceDesc(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); }; #endif diff --git a/src/devices/usb/UsbCommon.h b/src/devices/usb/UsbCommon.h index ee826f3ce..69d1e5dc3 100644 --- a/src/devices/usb/UsbCommon.h +++ b/src/devices/usb/UsbCommon.h @@ -113,6 +113,9 @@ struct USBDescEndpoint { uint8_t is_audio; /* has bRefresh + bSynchAddress */ uint8_t* extra; + + // 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 */ @@ -201,7 +204,7 @@ 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 @@ -262,7 +265,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 child_detach; + std::function child_detach; std::function wakeup; /* * Note that port->dev will be different then the device from which @@ -278,7 +281,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 */ @@ -329,16 +332,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; - -}; - #endif