Sync code with ergo720's branch up to commit bc5283b

This commit is contained in:
StrikerX3 2018-07-03 21:32:52 -03:00
parent 7c9eee188c
commit 8f2747a530
9 changed files with 1279 additions and 364 deletions

View file

@ -42,19 +42,17 @@
namespace openxbox {
/* This is a linux struct for vectored I/O. See readv() and writev() */
typedef struct _IoVec {
struct IoVec {
void* Iov_Base; // Starting address
size_t Iov_Len; // Number of bytes to transfer
}
IoVec;
};
typedef struct _IOVector {
struct IOVector {
IoVec* IoVecStruct;
int IoVecNumber; // number of I/O buffers supplied
int AllocNumber; // number of IoVec structs currently allocated
size_t Size; // total size of all I/O buffers supplied
}
IOVector;
};
inline uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c);
@ -63,4 +61,11 @@ 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
}

View file

@ -39,37 +39,6 @@
namespace openxbox {
// ergo720: note that GetDwords and WriteDwords will reliably work only if the allocation was served by MapViewOfFileEx and not
// by VirtualAlloc (see comment on OHCI_ReadHCCA for more details). Once LLE CPU and MMU are implemented, this will no
// longer be the case. Also note that the physical pages can be modified while being read/written
// read an array of DWORDs in memory
void GetDwords(uint32_t Paddr, uint32_t* Buffer, int Number) {
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
std::memcpy(Buffer, reinterpret_cast<void*>(Paddr + CONTIGUOUS_MEMORY_BASE), 4); // dropped little -> big endian conversion from XQEMU
}
}
// write an array of DWORDs in memory
void WriteDwords(uint32_t Paddr, uint32_t* Buffer, int Number) {
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
std::memcpy(reinterpret_cast<void*>(Paddr + CONTIGUOUS_MEMORY_BASE), Buffer, 4); // dropped big -> little endian conversion from XQEMU
}
}
// read an array of WORDs in memory
void GetWords(uint32_t Paddr, uint16_t* Buffer, int Number) {
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
std::memcpy(Buffer, reinterpret_cast<void*>(Paddr + CONTIGUOUS_MEMORY_BASE), 2); // dropped little -> big endian conversion from XQEMU
}
}
// write an array of WORDs in memory
void WriteWords(uint32_t Paddr, uint16_t* Buffer, int Number) {
for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) {
std::memcpy(reinterpret_cast<void*>(Paddr + CONTIGUOUS_MEMORY_BASE), Buffer, 2); // dropped big -> little endian conversion from XQEMU
}
}
/* These macros are used to access the bits of the various registers */
// HcControl
@ -211,29 +180,36 @@ void WriteWords(uint32_t Paddr, uint16_t* Buffer, int Number) {
#define USB_HZ 12000000
#define USB_SPEED_LOW 0
#define USB_SPEED_FULL 1
#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
#define OHCI_PAGE_MASK 0xFFFFF000
#define OHCI_OFFSET_MASK 0xFFF
typedef enum _USB_SPEED
OHCI::OHCI(Cpu* cpu, int Irq, USBPCIDevice* UsbObj)
: m_cpu(cpu)
{
USB_SPEED_MASK_LOW = 1 << 0,
USB_SPEED_MASK_FULL = 1 << 1,
}
USB_SPEED;
int offset = 0;
USBPortOps* ops;
OHCI::OHCI(int Irq, USBPCIDevice* UsbObj)
{
m_IrqNum = Irq;
m_UsbDevice = UsbObj;
ops = new USBPortOps();
{
using namespace std::placeholders;
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->wakeup = std::bind(&OHCI::OHCI_Wakeup, this, _1);
ops->complete = std::bind(&OHCI::OHCI_AsyncCompletePacket, this, _1, _2);
}
if (m_IrqNum == 9) {
offset = 2;
}
for (int i = 0; i < 2; i++) {
m_UsbDevice->USB_RegisterPort(&m_Registers.RhPort[i].UsbPort, i, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
m_UsbDevice->USB_RegisterPort(&m_Registers.RhPort[i].UsbPort, i + offset, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, ops);
}
OHCI_PacketInit(&m_UsbPacket);
@ -313,7 +289,7 @@ void OHCI::OHCI_FrameBoundaryWorker()
}
if (m_DoneCount != 7 && m_DoneCount != 0) {
// decrease DelayInterrupt counter
// decrease Done Queue counter
m_DoneCount--;
}
@ -374,7 +350,7 @@ bool OHCI::OHCI_WriteHCCA(uint32_t Paddr, OHCI_HCCA* Hcca)
bool OHCI::OHCI_ReadED(uint32_t Paddr, OHCI_ED* Ed)
{
if (Paddr != NULL) {
GetDwords(Paddr, reinterpret_cast<uint32_t*>(Ed), sizeof(*Ed) >> 2); // ED is 16 bytes large
m_cpu->VMemRead(Paddr, sizeof(*Ed), Ed);
return false;
}
return true; // error
@ -385,7 +361,7 @@ bool OHCI::OHCI_WriteED(uint32_t Paddr, OHCI_ED* Ed)
if (Paddr != NULL) {
// According to the standard, only the HeadP field is writable by the HC, so we'll write just that
size_t OffsetOfHeadP = offsetof(OHCI_ED, HeadP);
WriteDwords(Paddr + OffsetOfHeadP, reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(Ed) + OffsetOfHeadP), 1);
m_cpu->VMemWrite(Paddr, 4, Ed + OffsetOfHeadP);
return false;
}
return true; // error
@ -394,7 +370,7 @@ bool OHCI::OHCI_WriteED(uint32_t Paddr, OHCI_ED* Ed)
bool OHCI::OHCI_ReadTD(uint32_t Paddr, OHCI_TD* Td)
{
if (Paddr != NULL) {
GetDwords(Paddr, reinterpret_cast<uint32_t*>(Td), sizeof(*Td) >> 2); // TD is 16 bytes large
m_cpu->VMemRead(Paddr, sizeof(*Td), Td);
return false;
}
return true; // error
@ -403,7 +379,7 @@ bool OHCI::OHCI_ReadTD(uint32_t Paddr, OHCI_TD* Td)
bool OHCI::OHCI_WriteTD(uint32_t Paddr, OHCI_TD* Td)
{
if (Paddr != NULL) {
WriteDwords(Paddr, reinterpret_cast<uint32_t*>(Td), sizeof(*Td) >> 2);
m_cpu->VMemWrite(Paddr, sizeof(*Td), Td);
return false;
}
return true; // error
@ -411,8 +387,7 @@ bool OHCI::OHCI_WriteTD(uint32_t Paddr, OHCI_TD* Td)
bool OHCI::OHCI_ReadIsoTD(uint32_t Paddr, OHCI_ISO_TD* td) {
if (Paddr != NULL) {
GetDwords(Paddr, reinterpret_cast<uint32_t*>(td), 4);
GetWords(Paddr + 16, td->Offset, 8);
m_cpu->VMemRead(Paddr, sizeof(*td), td);
return false;
}
return true; // error
@ -420,8 +395,7 @@ bool OHCI::OHCI_ReadIsoTD(uint32_t Paddr, OHCI_ISO_TD* td) {
bool OHCI::OHCI_WriteIsoTD(uint32_t Paddr, OHCI_ISO_TD* td) {
if (Paddr != NULL) {
WriteDwords(Paddr, reinterpret_cast<uint32_t*>(td), 4);
WriteWords(Paddr + 16, td->Offset, 8);
m_cpu->VMemWrite(Paddr, sizeof(*td), td);
return false;
}
return true; // error
@ -599,7 +573,7 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
int pid;
int ret;
int i;
XboxDevice* dev;
XboxDeviceState* dev;
USBEndpoint* ep;
OHCI_TD td;
uint32_t addr;
@ -845,9 +819,9 @@ exit_no_retire:
return OHCI_BM(td.Flags, TD_CC) != OHCI_CC_NOERROR;
}
XboxDevice* OHCI::OHCI_FindDevice(uint8_t Addr)
XboxDeviceState* OHCI::OHCI_FindDevice(uint8_t Addr)
{
XboxDevice* dev;
XboxDeviceState* dev;
int i;
for (i = 0; i < 2; i++) {
@ -1277,7 +1251,7 @@ uint32_t OHCI::OHCI_GetFrameRemaining()
void OHCI::OHCI_StopEndpoints()
{
XboxDevice* dev;
XboxDeviceState* dev;
int i, j;
for (i = 0; i < 2; i++) {
@ -1455,7 +1429,7 @@ void OHCI::OHCI_Attach(USBPort* Port)
port->HcRhPortStatus |= OHCI_PORT_CCS | OHCI_PORT_CSC;
// update speed
if (port->UsbPort.Dev->speed == USB_SPEED_LOW) {
if (port->UsbPort.Dev->Speed == USB_SPEED_LOW) {
port->HcRhPortStatus |= OHCI_PORT_LSDA;
}
else {
@ -1467,14 +1441,48 @@ void OHCI::OHCI_Attach(USBPort* Port)
OHCI_SetInterrupt(OHCI_INTR_RD);
}
log_debug("OHCI: Attached port %d\n", Port->PortIndex);
log_debug("OHCI: Attached port %d", Port->PortIndex);
if (old_state != port->HcRhPortStatus) {
OHCI_SetInterrupt(OHCI_INTR_RHSC);
}
}
void OHCI::OHCI_AsyncCancelDevice(XboxDevice* dev)
void OHCI::OHCI_ChildDetach(USBPort* port, XboxDeviceState* child) {
OHCI_AsyncCancelDevice(child);
}
void OHCI::OHCI_Wakeup(USBPort* port1) {
OHCIPort* port = &m_Registers.RhPort[port1->PortIndex];
uint32_t intr = 0;
if (port->HcRhPortStatus & OHCI_PORT_PSS) {
log_debug("OHCI: port %d: wakeup", port1->PortIndex);
port->HcRhPortStatus |= OHCI_PORT_PSSC;
port->HcRhPortStatus &= ~OHCI_PORT_PSS;
intr = OHCI_INTR_RHSC;
}
// Note that the controller can be suspended even if this port is not
if ((m_Registers.HcControl & OHCI_CTL_HCFS) == Suspend) {
log_debug("OHCI: remote-wakeup: SUSPEND->RESUME");
// From the standard: "The only interrupts possible in the USBSUSPEND state are ResumeDetected (the
// Host Controller will have changed the HostControllerFunctionalState to the USBRESUME state)
// and OwnershipChange."
m_Registers.HcControl &= ~OHCI_CTL_HCFS;
m_Registers.HcControl |= Resume;
intr = OHCI_INTR_RD;
}
OHCI_SetInterrupt(intr);
}
void OHCI::OHCI_AsyncCompletePacket(USBPort* port, USBPacket* packet) {
#ifdef DEBUG_PACKET
log_debug("Async packet complete");
#endif
m_AsyncComplete = 1;
OHCI_ProcessLists(1);
}
void OHCI::OHCI_AsyncCancelDevice(XboxDeviceState* dev)
{
if (m_AsyncTD &&
m_UsbDevice->USB_IsPacketInflight(&m_UsbPacket) &&
@ -1518,7 +1526,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
int pid;
int ret;
int i;
XboxDevice* dev;
XboxDeviceState* dev;
USBEndpoint* ep;
OHCI_ISO_TD iso_td;
uint32_t addr;
@ -1670,7 +1678,7 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
}
else {
// From the standard: "If, however, the data packet is the last in an Isochronous TD(R = FrameCount),
// then the value of BufferEnd is the address of the last byte in the buffer."
// then the value of BufferEnd is the address of the last byte in the buffer."
end_addr = iso_td.BufferEnd;
}
@ -1716,7 +1724,8 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
// From the standard: "After each data packet transfer, the Rth Offset is replaced with a value that indicates the status of
// the data packet transfer.The upper 4 bits of the value are the ConditionCode for the transfer and the lower 12 bits
// represent the size of the transfer.Together, these two fields constitute the Packet Status Word(PacketStatusWord)."
// Writeback
// Writeback
if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
// IN transfer succeeded
if (OHCI_CopyIsoTD(start_addr, end_addr, m_UsbBuffer, ret, true)) {
@ -1781,4 +1790,9 @@ int OHCI::OHCI_ServiceIsoTD(OHCI_ED* ed, int completion)
return 1;
}
void OHCI::OHCI_AssignUsbPortStruct(int port, XboxDeviceState* dev) {
dev->Port = &m_Registers.RhPort[port].UsbPort;
m_Registers.RhPort[port].UsbPort.Dev = dev;
}
}

View file

@ -42,13 +42,10 @@
#include "usb_pci.h"
#include "openxbox/cxbxtimer.h"
#include "openxbox/cpu.h"
namespace openxbox {
#define USB_TOKEN_SETUP 0x2D
#define USB_TOKEN_IN 0x69 // device -> host
#define USB_TOKEN_OUT 0xE1 // host -> device
#define USB_RET_SUCCESS (0)
#define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2)
@ -71,32 +68,29 @@ namespace openxbox {
/* endpoint descriptor */
typedef struct _OHCI_ED {
struct OHCI_ED {
uint32_t Flags;
uint32_t TailP;
uint32_t HeadP;
uint32_t NextED;
}
OHCI_ED;
};
/* general transfer descriptor */
typedef struct _OHCI_TD {
struct OHCI_TD {
uint32_t Flags;
uint32_t CurrentBufferPointer;
uint32_t NextTD;
uint32_t BufferEnd;
}
OHCI_TD;
};
/* Isochronous transfer descriptor */
typedef struct _OHCI_ISO_TD {
struct OHCI_ISO_TD {
uint32_t Flags;
uint32_t BufferPage0;
uint32_t NextTD;
uint32_t BufferEnd;
uint16_t Offset[8];
}
OHCI_ISO_TD;
};
/* enum indicating the current HC state */
typedef enum _OHCI_State {
@ -108,22 +102,20 @@ typedef enum _OHCI_State {
OHCI_State;
/* Host Controller Communications Area */
typedef struct _OHCI_HCCA {
struct OHCI_HCCA {
uint32_t HccaInterrruptTable[32];
uint16_t HccaFrameNumber, HccaPad1;
uint32_t HccaDoneHead;
}
OHCI_HCCA;
};
/* Small struct used to hold the HcRhPortStatus register and the usb port status */
typedef struct _OHCIPort {
struct OHCIPort {
USBPort UsbPort;
uint32_t HcRhPortStatus;
}
OHCIPort;
};
/* All these registers are well documented in the OHCI standard */
typedef struct _OHCI_Registers {
struct OHCI_Registers {
// Control and Status partition
uint32_t HcRevision;
uint32_t HcControl;
@ -152,32 +144,28 @@ typedef struct _OHCI_Registers {
uint32_t HcRhDescriptorA;
uint32_t HcRhDescriptorB;
uint32_t HcRhStatus;
// ergo720: I have some doubts here. Both XQEMU and OpenXbox set 4 ports per HC, for a total of 8 usb ports.
// Could it be becasue each gamepad can host 2 memory units?
OHCIPort RhPort[2]; // 2 ports per HC, for a total of 4 USB ports
}
OHCI_Registers;
};
/* OHCI class representing the state of the HC */
class OHCI {
public:
// constructor
OHCI(int Irqn, USBPCIDevice* UsbObj);
OHCI(Cpu* cpu, int Irqn, USBPCIDevice* UsbObj);
// destructor
~OHCI() {}
// read a register
uint32_t OHCI_ReadRegister(uint32_t Addr);
// write a register
void OHCI_WriteRegister(uint32_t Addr, uint32_t Value);
// update ohci registers during a device attach
void OHCI_Attach(USBPort* Port);
// update ohci registers during a device detach
void OHCI_Detach(USBPort* Port);
// update the USBPort struct with the device attached
void OHCI_AssignUsbPortStruct(int port, XboxDeviceState* dev);
private:
Cpu* m_cpu;
// pointer to g_USB0 or g_USB1
USBPCIDevice * m_UsbDevice = nullptr;
USBPCIDevice* m_UsbDevice = nullptr;
// all the registers available on the OHCI standard
OHCI_Registers m_Registers;
// end-of-frame timer
@ -267,11 +255,17 @@ private:
// process an isochronous TD
int OHCI_ServiceIsoTD(OHCI_ED* ed, int completion);
// find the usb device with the supplied address
XboxDevice* OHCI::OHCI_FindDevice(uint8_t Addr);
XboxDeviceState* OHCI::OHCI_FindDevice(uint8_t Addr);
// cancel a packet when a device is removed
void OHCI_AsyncCancelDevice(XboxDevice* dev);
void OHCI_AsyncCancelDevice(XboxDeviceState* dev);
// Process Control and Bulk lists
void OHCI_ProcessLists(int completion);
// see USBPortOps struct for info
void OHCI_Attach(USBPort* Port);
void OHCI_Detach(USBPort* Port);
void OHCI_ChildDetach(USBPort* port, XboxDeviceState* child);
void OHCI_Wakeup(USBPort* port1);
void OHCI_AsyncCompletePacket(USBPort* port, USBPacket* packet);
};
}

View file

@ -0,0 +1,349 @@
/*
* Portions of the code are based on Cxbx-Reloaded's OHCI LLE implementation.
* The original copyright header is included below.
*/
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->devices->usb->UsbCommon.h
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2018 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include "openxbox/queue.h"
#include "openxbox/iovec.h"
namespace openxbox {
#define USB_MAX_ENDPOINTS 15
#define USB_MAX_INTERFACES 16
#define USB_STATE_NOTATTACHED 0
#define USB_STATE_ATTACHED 1
#define USB_STATE_DEFAULT 2
#define USB_CLASS_HUB 9
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_XFER_INVALID 255
#define USB_INTERFACE_INVALID 255
#define USB_DIR_OUT 0
#define USB_DIR_IN 0x80
#define USB_TOKEN_SETUP 0x2D
#define USB_TOKEN_IN 0x69 // device -> host
#define USB_TOKEN_OUT 0xE1 // host -> device
#define USB_SPEED_LOW 0
#define USB_SPEED_FULL 1
typedef enum _USB_SPEED {
USB_SPEED_MASK_LOW = 1 << 0,
USB_SPEED_MASK_FULL = 1 << 1,
}
USB_SPEED;
typedef enum USBPacketState {
USB_PACKET_UNDEFINED = 0,
USB_PACKET_SETUP,
USB_PACKET_QUEUED,
USB_PACKET_ASYNC,
USB_PACKET_COMPLETE,
USB_PACKET_CANCELED,
}
USBPacketState;
struct USBPacket;
struct USBPort;
struct USBDeviceClass;
struct XboxDeviceState;
typedef const char* USBDescStrings[256];
/* String descriptor */
struct USBDescString {
uint8_t index; // index of this string descriptor
std::string str; // the string of this string descriptor
QLIST_ENTRY(USBDescString) next;
};
struct USBDescOther {
uint8_t length;
const uint8_t *data;
};
/* Endpoint descriptor */
struct USBDescEndpoint {
uint8_t bEndpointAddress; // the address of the endpoint on the USB device described by this descriptor
uint8_t bmAttributes; // this field describes the endpoint's attributes when it is configured using the bConfigurationValue
uint16_t wMaxPacketSize; // maximum packet size this endpoint is capable of sending or receiving when this configuration is selected
uint8_t bInterval; // interval for polling endpoint for data transfers, expressed in milliseconds.
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;
};
/* Interface descriptor */
struct USBDescIface {
uint8_t bInterfaceNumber; // number of interface
uint8_t bAlternateSetting; // value used to select the alternate setting for the interface identified by bInterfaceNumber
uint8_t bNumEndpoints; // number of endpoints used by this interface (excluding endpoint zero)
uint8_t bInterfaceClass; // class code (assigned by the USB)
uint8_t bInterfaceSubClass; // subclass code (assigned by the USB)
uint8_t bInterfaceProtocol; // protocol code (assigned by the USB)
uint8_t iInterface; // index of string descriptor describing this interface
uint8_t ndesc;
USBDescOther* descs;
USBDescEndpoint* eps; // endpoints supported by this interface
USBDescIface(bool bDefault);
~USBDescIface();
};
/*
* ergo720: I removed the Interface Association Descriptor (IAD) since, at the time of this writing (2018), the xboxdevwiki documents that all
* known xid devices don't use them and also, according to the corresponding standard, IAD applies to usb revision 2.0 while the xbox uses
* usb revision 1.1 so it shouldn't support them either. If this turns out to be incorrect, then IAD support will have to be added
*/
/* Configuration descriptor */
struct USBDescConfig {
uint8_t bNumInterfaces; // number of interfaces supported by this configuration
uint8_t bConfigurationValue; // value to use as an argument to the SetConfiguration() request to select this configuration
uint8_t iConfiguration; // index of string descriptor describing this configuration
uint8_t bmAttributes; // configuration characteristics
uint8_t bMaxPower; // maximum power consumption of the USB device in this configuration expressed in 2mA units
/* "normal" interfaces */
uint8_t nif; // number of interfaces (again)
const USBDescIface* ifs; // interfaces supported by this configuration
};
/* Device descriptor part 1 */
struct USBDescDevice {
uint16_t bcdUSB; // USB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H)
uint8_t bDeviceClass; // class code (assigned by the USB)
uint8_t bDeviceSubClass; // subclass code (assigned by the USB)
uint8_t bDeviceProtocol; // protocol code (assigned by the USB)
uint8_t bMaxPacketSize0; // maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid)
uint8_t bNumConfigurations; // number of possible configurations
const USBDescConfig* confs; // configurations supported by this device
USBDescDevice(bool bDefault);
~USBDescDevice();
};
/* Device descriptor part 2 */
struct USBDescID {
uint16_t idVendor; // vendor ID (assigned by the USB)
uint16_t idProduct; // product ID (assigned by the manufacturer)
uint16_t bcdDevice; // device release number in binary-coded decimal
uint8_t iManufacturer; // index of string descriptor describing manufacturer
uint8_t iProduct; // index of string descriptor describing product
uint8_t iSerialNumber; // index of string descriptor describing the devices serial number
};
/* Global USB Descriptor struct */
struct USBDesc {
USBDescID id; // id-specific info of the device descriptor
const USBDescDevice* full; // remaining fields of the device descriptor
const char* const* str;
USBDesc(bool bDefault);
};
/* USB endpoint */
struct USBEndpoint {
uint8_t Num; // endpoint number
uint8_t pid;
uint8_t Type; // the type of this endpoint
uint8_t IfNum; // interface number this endpoint belongs to
int MaxPacketSize; // maximum packet size supported by this endpoint
bool Pipeline;
bool Halted; // indicates that the endpoint is halted
XboxDeviceState* Dev; // device this endpoint belongs to
QTAILQ_HEAD(, USBPacket) Queue; // queue of packets to this endpoint
};
/* definition of an Xbox usb device */
struct XboxDeviceState {
USBPort* Port; // usb port struct of this device
int PortPath; // port index to which this device is attached to
char* Serial;
uint32_t flags;
USBDeviceClass* klass; // usb class struct of this device
int Speed; // actual connected speed
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];
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 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
const USBDesc* UsbDesc; // Overrides class usb_desc if not nullptr
const USBDescDevice* Device; // device descriptor part 1
int Configuration; // number of the selected configuration descriptor
int NumInterfaces; // number of available interface descriptors
int AltSetting[USB_MAX_INTERFACES]; // alternate setting numbers for the current interface
const USBDescConfig* Config; // configuration descriptor in use
const USBDescIface* Ifaces[USB_MAX_INTERFACES]; // interface in use
};
struct USBCombinedPacket {
USBPacket* First;
QTAILQ_HEAD(packets_head, USBPacket) Packets;
IOVector IoVec;
};
/* Structure used to hold information about an active USB packet */
struct USBPacket {
int Pid; // Packet ID (used to identify the type of packet that is being sent)
uint32_t Id; // Paddr of the TD for this packet
USBEndpoint* Endpoint; // endpoint this packet is transferred to
unsigned int Stream;
IOVector IoVec; // used to perform vectored I/O
uint64_t Parameter; // control transfers
bool ShortNotOK; // the bufferRounding mode of the TD for this packet
bool IntReq; // whether or not to generate an interrupt for this packet (DelayInterrupt of the TD is zero)
int Status; // USB_RET_* status code
int ActualLength; // before copy: offset inside IoVec structs; after copy: number of bytes actually transferred
// Internal use by the USB layer
USBPacketState State;
USBCombinedPacket* Combined;
QTAILQ_ENTRY(USBPacket) Queue;
QTAILQ_ENTRY(USBPacket) CombinedEntry;
};
struct USBPortOps {
std::function<void(USBPort* port)> attach;
std::function<void(USBPort* port)> detach;
/*
* This gets called when a device downstream from the device attached to
* the port (attached through a hub) gets detached.
*/
std::function<void(USBPort* port, XboxDeviceState* child)> child_detach;
std::function<void(USBPort* port)> wakeup;
/*
* Note that port->dev will be different then the device from which
* the packet originated when a hub is involved.
*/
std::function<void(USBPort* port, USBPacket* p)> complete;
};
/* Struct describing the status of a usb port */
struct USBPort {
XboxDeviceState* Dev; // usb device (if present)
USBPortOps* Operations; // functions to call when a port event happens
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
};
/* Struct which stores general functions/variables regarding the peripheral */
struct USBDeviceClass {
std::function<int(XboxDeviceState* dev)> init;
// Walk (enabled) downstream ports, check for a matching device.
// Only hubs implement this.
std::function<XboxDeviceState*(XboxDeviceState* dev, uint8_t addr)> find_device;
// Called when a packet is canceled.
std::function<void(XboxDeviceState* dev, USBPacket* p)> cancel_packet;
// Called when device is destroyed.
std::function<void(XboxDeviceState* dev)> handle_destroy;
// Attach the device
std::function<void(XboxDeviceState* dev)> handle_attach;
// Reset the device
std::function<void(XboxDeviceState* dev)> handle_reset;
// Process control request.
// Called from handle_packet().
// Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
// then the number of bytes transferred is stored in p->actual_length
std::function<void(XboxDeviceState* dev, USBPacket* p, int request, int value,
int index, int length, uint8_t *data)> handle_control;
// Process data transfers (both BULK and ISOC).
// Called from handle_packet().
// Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
// then the number of bytes transferred is stored in p->actual_length
std::function<void(XboxDeviceState* dev, USBPacket* p)> handle_data;
std::function<void(XboxDeviceState* dev, int Interface,
int alt_old, int alt_new)> set_interface;
// Called when the hcd is done queuing packets for an endpoint, only
// necessary for devices which can return USB_RET_ADD_TO_QUEUE.
std::function<void(XboxDeviceState* dev, USBEndpoint* ep)> flush_ep_queue;
// Called by the hcd to let the device know the queue for an endpoint
// has been unlinked / stopped. Optional may be NULL.
std::function<void(XboxDeviceState* Dev, USBEndpoint* EP)> ep_stopped;
const char* product_desc; // friendly name of the device
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;
};
}

View file

@ -0,0 +1,469 @@
/*
* Portions of the code are based on Cxbx-Reloaded's OHCI LLE implementation.
* The original copyright header is included below.
*/
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->devices->usb->Hub.cpp
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2018 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#include <cassert>
#include "openxbox/log.h"
#include "ohci.h"
#include "ohci_hub.h"
namespace openxbox {
// This array is used to translate an xbox player to the corresponding usb port
// The port associations are taken from XQEMU
int PlayerToUsbArray[] = {
-1,
3,
4,
1,
2,
};
USBDescIface::USBDescIface(bool bDefault) {
std::memset(this, 0, sizeof(USBDescIface));
if (bDefault) {
descs = new USBDescOther();
eps = new USBDescEndpoint();
bInterfaceNumber = 0;
bNumEndpoints = 1;
bInterfaceClass = USB_CLASS_HUB;
eps = new USBDescEndpoint();
eps->bEndpointAddress = USB_DIR_IN | 0x01;
eps->bmAttributes = USB_ENDPOINT_XFER_INT;
eps->wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8;
eps->bInterval = 0xFF;
}
}
USBDescIface::~USBDescIface() {
delete descs;
if (bNumEndpoints != 1) {
delete[] eps;
return;
}
delete eps;
}
static const USBDescIface desc_iface_hub(true);
USBDescDevice::USBDescDevice(bool bDefault) {
std::memset(this, 0, sizeof(USBDescDevice));
if (bDefault) {
USBDescConfig* pUSBDescConfig = new USBDescConfig();
bcdUSB = 0x0110;
bDeviceClass = USB_CLASS_HUB;
bMaxPacketSize0 = 8;
bNumConfigurations = 1;
pUSBDescConfig->bNumInterfaces = 1;
pUSBDescConfig->bConfigurationValue = 1;
pUSBDescConfig->bmAttributes = 0xE0;
pUSBDescConfig->nif = 1;
pUSBDescConfig->ifs = &desc_iface_hub;
confs = pUSBDescConfig;
}
}
USBDescDevice::~USBDescDevice() {
delete confs;
if (bNumConfigurations != 1) {
delete[] confs;
return;
}
}
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) {
id.idVendor = 0x0409;
id.idProduct = 0x55AA;
id.bcdDevice = 0x0101;
id.iManufacturer = STR_MANUFACTURER;
id.iProduct = STR_PRODUCT;
id.iSerialNumber = STR_SERIALNUMBER;
full = &desc_device_hub;
str = desc_strings;
}
}
static const USBDesc desc_hub(true);
int Hub::Init(int pport) {
ClassInitFn();
UsbEpInit();
int rc = UsbClaimPort(pport);
if (rc != 0) {
return rc;
}
rc = m_UsbDev->USB_DeviceInit(m_pDeviceStruct);
if (rc != 0) {
UsbReleasePort(m_pDeviceStruct);
return rc;
}
m_UsbDev->USB_DeviceAttach(m_pDeviceStruct);
return 0;
}
Hub::~Hub() {
delete m_pPeripheralFuncStruct;
delete m_HubState->ports[0].port.Operations;
delete m_HubState;
m_pPeripheralFuncStruct = nullptr;
m_pDeviceStruct = nullptr;
}
void 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;
{
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->usb_desc = &desc_hub;
}
}
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 usb_port;
assert(m_pDeviceStruct->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);
}
else {
m_UsbDev = g_USB1; // FIXME: how to retrieve these?
m_UsbDev->m_HostController->OHCI_AssignUsbPortStruct(usb_port - 1, m_pDeviceStruct);
}
return 0;
}
void Hub::UsbReleasePort(XboxDeviceState* dev) {
USBPort * port = dev->Port;
assert(port != nullptr);
port->Dev = nullptr;
dev->Port = nullptr;
}
int Hub::UsbHub_Initfn(XboxDeviceState* dev) {
USBHubPort* port;
USBPortOps* ops;
int i;
if (dev->Port->HubCount == 5) {
log_debug("Hub: chain too deep");
return -1;
}
CreateSerial(dev);
UsbDescInit(dev);
m_HubState->intr = m_UsbDev->USB_GetEP(dev, USB_TOKEN_IN, 1);
ops = new USBPortOps();
{
using namespace std::placeholders;
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->wakeup = std::bind(&Hub::UsbHub_Wakeup, this, _1);
ops->complete = std::bind(&Hub::UsbHub_Complete, this, _1, _2);
}
for (i = 0; i < NUM_PORTS; i++) {
port = &m_HubState->ports[i];
m_UsbDev->USB_RegisterPort(&port->port, i, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, ops);
m_UsbDev->USB_PortLocation(&port->port, dev->Port, i + 1);
}
UsbHub_HandleReset();
return 0;
}
void Hub::UsbHub_HandleReset() {
USBHubPort* port;
for (int i = 0; i < NUM_PORTS; i++) {
port = m_HubState->ports + i;
port->wPortStatus = PORT_STAT_POWER;
port->wPortChange = 0;
if (port->port.Dev && port->port.Dev->Attached) {
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;
}
}
}
}
/*
* 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) {
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:
log_warning("Unknown speed parameter %d set in %s", dev->ProductDesc.c_str());
}
UsbDescSetConfig(dev, 0);
}
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);
}
}
}
}

View file

@ -0,0 +1,143 @@
/*
* Portions of the code are based on Cxbx-Reloaded's OHCI LLE implementation.
* The original copyright header is included below.
*/
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->devices->usb->Hub.h
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2018 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include "ohci_common.h"
namespace openxbox {
#define NUM_PORTS 8
#define PORT_STAT_CONNECTION 0x0001
#define PORT_STAT_ENABLE 0x0002
#define PORT_STAT_SUSPEND 0x0004
#define PORT_STAT_OVERCURRENT 0x0008
#define PORT_STAT_RESET 0x0010
#define PORT_STAT_POWER 0x0100
#define PORT_STAT_LOW_SPEED 0x0200
#define PORT_STAT_C_CONNECTION 0x0001
#define PORT_STAT_C_ENABLE 0x0002
#define PORT_STAT_C_SUSPEND 0x0004
#define PORT_STAT_C_OVERCURRENT 0x0008
#define PORT_STAT_C_RESET 0x0010
/* same as Linux kernel root hubs */
typedef enum {
STR_MANUFACTURER = 1,
STR_PRODUCT,
STR_SERIALNUMBER,
};
struct USBHubPort {
USBPort port; // downstream port status
uint16_t wPortStatus; // Port Status Field, in accordance with the standard
uint16_t wPortChange; // Port Change Field, in accordance with the standard
};
struct USBHubState {
XboxDeviceState dev; // hub device status
USBEndpoint* intr; // interrupt endpoint of the hub
USBHubPort ports[NUM_PORTS]; // downstream ports of the hub
};
/* Class which implements a usb hub */
class Hub final : public UsbPeripheral {
public:
// initialize this peripheral
int Init(int pport) override;
// destructor
~Hub();
private:
// usb device this hub is attached to
USBPCIDevice* m_UsbDev = nullptr;
// hub state
USBHubState* m_HubState = nullptr;
// initialize various member variables/functions
void ClassInitFn();
// see USBDeviceClass for comments about these functions
int UsbHub_Initfn(XboxDeviceState* dev);
XboxDeviceState* UsbHub_FindDevice(XboxDeviceState* dev, uint8_t addr);
void UsbHub_HandleReset();
void UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p,
int request, int value, int index, int length, uint8_t* data);
void UsbHub_HandleData(XboxDeviceState* dev, USBPacket* p);
void UsbHub_HandleDestroy(XboxDeviceState* dev);
// see USBPortOps struct for info
void UsbHub_Attach(USBPort* port1);
void UsbHub_Detach(USBPort* port1);
void UsbHub_ChildDetach(USBPort* port1, 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);
};
extern Hub* g_HubObjArray[4];
}

View file

@ -37,32 +37,28 @@
// * All rights reserved
// *
// ******************************************************************
#include <cassert>
#include <functional>
#include "openxbox/log.h"
#include "usb_pci.h"
#include "ohci.h"
#include "openxbox/log.h"
#include <cassert>
namespace openxbox {
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_XFER_INVALID 255
#define USB_DIR_OUT 0
#define USB_DIR_IN 0x80
#define SETUP_STATE_IDLE 0
#define SETUP_STATE_SETUP 1
#define SETUP_STATE_DATA 2
#define SETUP_STATE_ACK 3
#define SETUP_STATE_PARAM 4
USBPCIDevice::USBPCIDevice(uint16_t vendorID, uint16_t deviceID, uint8_t revisionID, uint8_t irqn)
USBPCIDevice::USBPCIDevice(uint16_t vendorID, uint16_t deviceID, uint8_t revisionID, uint8_t irqn, Cpu* cpu)
: PCIDevice(PCI_HEADER_TYPE_NORMAL, vendorID, deviceID, revisionID,
0x0c, 0x03, 0x10) // USB OHCI
, m_irqn(irqn)
, m_cpu(cpu)
{
}
@ -74,7 +70,14 @@ USBPCIDevice::~USBPCIDevice() {
void USBPCIDevice::Init() {
RegisterBAR(0, 0x1000, PCI_BAR_TYPE_MEMORY); // 0xFED00000 - 0xFED00FFF and 0xFED08000 - 0xFED08FFF
m_HostController = new OHCI(m_irqn, this);
if (m_irqn == 1) {
m_PciPath = "pci.0:02.0";
}
else {
m_PciPath = "pci.0:03.0";
}
m_HostController = new OHCI(m_cpu, m_irqn, this);
}
void USBPCIDevice::Reset() {
@ -97,23 +100,15 @@ void USBPCIDevice::PCIMMIOWrite(int barIndex, uint32_t addr, uint32_t value, uin
}
void USBPCIDevice::USB_RegisterPort(USBPort* Port, int Index, int SpeedMask) {
void USBPCIDevice::USB_RegisterPort(USBPort* Port, int Index, int SpeedMask, USBPortOps* Ops) {
Port->PortIndex = Index;
Port->SpeedMask = SpeedMask;
Port->HubCount = 0;
std::snprintf(Port->Path, sizeof(Port->Path), "%d", Index + 1);
}
void USBPCIDevice::USB_DeviceEPstopped(XboxDevice* Dev, USBEndpoint* EP) {
// This seems to be a nop in XQEMU since it doesn't assign the EP_Stopped function (it's nullptr)
USBDeviceClass* klass = USB_DEVICE_GET_CLASS(Dev);
if (klass->EP_Stopped) {
klass->EP_Stopped(Dev, EP);
}
Port->Operations = Ops;
USB_PortLocation(Port, nullptr, Index + 1);
}
void USBPCIDevice::USB_PortReset(USBPort* Port) {
XboxDevice* dev = Port->Dev;
XboxDeviceState* dev = Port->Dev;
assert(dev != nullptr);
USB_Detach(Port);
@ -122,7 +117,7 @@ void USBPCIDevice::USB_PortReset(USBPort* Port) {
}
void USBPCIDevice::USB_Detach(USBPort* Port) {
XboxDevice* dev = Port->Dev;
XboxDeviceState* dev = Port->Dev;
assert(dev != nullptr);
assert(dev->State != USB_STATE_NOTATTACHED);
@ -131,17 +126,17 @@ void USBPCIDevice::USB_Detach(USBPort* Port) {
}
void USBPCIDevice::USB_Attach(USBPort* Port) {
XboxDevice *dev = Port->Dev;
XboxDeviceState* dev = Port->Dev;
assert(dev != nullptr);
assert(dev->Attached);
assert(dev->State == USB_STATE_NOTATTACHED);
m_HostController->OHCI_Attach(Port);
Port->Operations->attach(Port);
dev->State = USB_STATE_ATTACHED;
usb_device_handle_attach(dev);
USB_DeviceHandleAttach(dev);
}
void USBPCIDevice::USB_DeviceReset(XboxDevice* dev) {
void USBPCIDevice::USB_DeviceReset(XboxDeviceState* dev) {
if (dev == nullptr || !dev->Attached) {
return;
}
@ -152,8 +147,8 @@ void USBPCIDevice::USB_DeviceReset(XboxDevice* dev) {
usb_device_handle_reset(dev);
}
XboxDevice* USBPCIDevice::USB_FindDevice(USBPort* Port, uint8_t Addr) {
XboxDevice* dev = Port->Dev;
XboxDeviceState* USBPCIDevice::USB_FindDevice(USBPort* Port, uint8_t Addr) {
XboxDeviceState* dev = Port->Dev;
if (dev == nullptr || !dev->Attached || dev->State != USB_STATE_DEFAULT) {
return nullptr;
@ -165,16 +160,7 @@ XboxDevice* USBPCIDevice::USB_FindDevice(USBPort* Port, uint8_t Addr) {
return USB_DeviceFindDevice(dev, Addr);
}
XboxDevice* USBPCIDevice::USB_DeviceFindDevice(XboxDevice* Dev, uint8_t Addr) {
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(Dev);
if (klass->find_device) {
return klass->find_device(Dev, Addr); // TODO: usb_hub_find_device
}
return nullptr;
}
USBEndpoint* USBPCIDevice::USB_GetEP(XboxDevice* Dev, int Pid, int Ep) {
USBEndpoint* USBPCIDevice::USB_GetEP(XboxDeviceState* Dev, int Pid, int Ep) {
USBEndpoint* eps;
if (Dev == nullptr) {
@ -216,7 +202,7 @@ void USBPCIDevice::USB_PacketAddBuffer(USBPacket* p, void* ptr, size_t len) {
IoVecAdd(&p->IoVec, ptr, len);
}
void USBPCIDevice::USB_HandlePacket(XboxDevice* dev, USBPacket* p) {
void USBPCIDevice::USB_HandlePacket(XboxDeviceState* dev, USBPacket* p) {
if (dev == nullptr) {
p->Status = USB_RET_NODEV;
return;
@ -277,7 +263,7 @@ void USBPCIDevice::USB_PacketCheckState(USBPacket* p, USBPacketState expected) {
}
void USBPCIDevice::USB_ProcessOne(USBPacket* p) {
XboxDevice* dev = p->Endpoint->Dev;
XboxDeviceState* dev = p->Endpoint->Dev;
// Handlers expect status to be initialized to USB_RET_SUCCESS, but it
// can be USB_RET_NAK here from a previous usb_process_one() call,
@ -311,7 +297,7 @@ void USBPCIDevice::USB_ProcessOne(USBPacket* p) {
}
}
void USBPCIDevice::USB_DoParameter(XboxDevice* s, USBPacket* p) {
void USBPCIDevice::USB_DoParameter(XboxDeviceState* s, USBPacket* p) {
int i, request, value, index;
for (i = 0; i < 8; i++) {
@ -350,7 +336,7 @@ void USBPCIDevice::USB_DoParameter(XboxDevice* s, USBPacket* p) {
}
}
void USBPCIDevice::USB_DoTokenSetup(XboxDevice* s, USBPacket* p) {
void USBPCIDevice::USB_DoTokenSetup(XboxDeviceState* s, USBPacket* p) {
int request, value, index;
// From the standard "Every Setup packet has eight bytes."
@ -407,7 +393,7 @@ void USBPCIDevice::USB_DoTokenSetup(XboxDevice* s, USBPacket* p) {
p->ActualLength = 8;
}
void USBPCIDevice::DoTokenIn(XboxDevice* s, USBPacket* p) {
void USBPCIDevice::DoTokenIn(XboxDeviceState* s, USBPacket* p) {
int request, value, index;
assert(p->Endpoint->Num == 0);
@ -450,7 +436,7 @@ void USBPCIDevice::DoTokenIn(XboxDevice* s, USBPacket* p) {
}
}
void USBPCIDevice::DoTokenOut(XboxDevice* s, USBPacket* p) {
void USBPCIDevice::DoTokenOut(XboxDeviceState* s, USBPacket* p) {
assert(p->Endpoint->Num == 0);
switch (s->SetupState) {
@ -506,31 +492,87 @@ void USBPCIDevice::USB_PacketCopy(USBPacket* p, void* ptr, size_t bytes) {
p->ActualLength += bytes;
}
void USBPCIDevice::USB_DeviceHandleControl(XboxDevice* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) {
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_control) {
klass->handle_control(dev, p, request, value, index, length, data); // TODO: usb_hub_handle_control
int USBPCIDevice::USB_DeviceInit(XboxDeviceState* dev) {
USBDeviceClass * klass = dev->klass;
if (klass->init) {
return klass->init(dev);
}
return 0;
}
void USBPCIDevice::USB_DeviceHandleData(XboxDevice* dev, USBPacket* p) {
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_data) {
klass->handle_data(dev, p); // TODO: usb_hub_handle_data
XboxDeviceState* USBPCIDevice::USB_DeviceFindDevice(XboxDeviceState* dev, uint8_t Addr) {
USBDeviceClass* klass = dev->klass;
if (klass->find_device) {
return klass->find_device(dev, Addr);
}
return nullptr;
}
void USBPCIDevice::USB_DeviceFlushEPqueue(XboxDevice* dev, USBEndpoint* ep) {
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->flush_ep_queue) {
klass->flush_ep_queue(dev, ep); // TODO: it's nullptr in XQEMU...
}
}
void USBPCIDevice::USB_DeviceCancelPacket(XboxDevice* dev, USBPacket* p) {
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
void USBPCIDevice::USB_DeviceCancelPacket(XboxDeviceState* dev, USBPacket* p) {
USBDeviceClass* klass = dev->klass;
if (klass->cancel_packet) {
klass->cancel_packet(dev, p); // TODO: it's nullptr in XQEMU...
klass->cancel_packet(dev, p);
}
}
void USBPCIDevice::USB_DeviceHandleDestroy(XboxDeviceState* dev) {
USBDeviceClass* klass = dev->klass;
if (klass->handle_destroy) {
klass->handle_destroy(dev);
}
}
void USBPCIDevice::USB_DeviceHandleAttach(XboxDeviceState* dev) {
USBDeviceClass* klass = dev->klass;
if (klass->handle_attach) {
klass->handle_attach(dev);
}
}
void USBPCIDevice::USB_DeviceHandleReset(XboxDeviceState* dev) {
USBDeviceClass* klass = dev->klass;
if (klass->handle_reset) {
klass->handle_reset(dev);
}
}
void USBPCIDevice::USB_DeviceHandleControl(XboxDeviceState* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) {
USBDeviceClass* klass = dev->klass;
if (klass->handle_control) {
klass->handle_control(dev, p, request, value, index, length, data);
}
}
void USBPCIDevice::USB_DeviceHandleData(XboxDeviceState* dev, USBPacket* p) {
USBDeviceClass* klass = dev->klass;
if (klass->handle_data) {
klass->handle_data(dev, p);
}
}
void USBPCIDevice::USB_DeviceSetInterface(XboxDeviceState* dev, int iface, int alt_old, int alt_new) {
USBDeviceClass* klass = dev->klass;
if (klass->set_interface) {
klass->set_interface(dev, iface, alt_old, alt_new);
}
}
void USBPCIDevice::USB_DeviceFlushEPqueue(XboxDeviceState* dev, USBEndpoint* ep) {
USBDeviceClass *klass = dev->klass;
if (klass->flush_ep_queue) {
klass->flush_ep_queue(dev, ep);
}
}
void USBPCIDevice::USB_DeviceEPstopped(XboxDeviceState* dev, USBEndpoint* EP) {
USBDeviceClass* klass = dev->klass;
if (klass->ep_stopped) {
klass->ep_stopped(dev, EP);
}
}
@ -544,5 +586,44 @@ void USBPCIDevice::USB_CancelPacket(USBPacket* p) {
}
}
void USBPCIDevice::USB_EPsetType(XboxDeviceState* dev, int pid, int ep, uint8_t type) {
USBEndpoint* uep = USB_GetEP(dev, pid, ep);
uep->Type = type;
}
uint8_t USBPCIDevice::USB_EPsetIfnum(XboxDeviceState* dev, int pid, int ep, uint8_t ifnum) {
USBEndpoint* uep = USB_GetEP(dev, pid, ep);
uep->IfNum = ifnum;
}
void USBPCIDevice::USB_EPsetMaxPacketSize(XboxDeviceState* dev, int pid, int ep, uint16_t raw) {
USBEndpoint* uep = USB_GetEP(dev, pid, ep);
// Dropped from XQEMU the calculation max_packet_size = size * microframes since that's only true
// for high speed (usb 2.0) devices
uep->MaxPacketSize = raw & 0x7FF;
}
void USBPCIDevice::USB_PortLocation(USBPort* downstream, USBPort* upstream, int portnr) {
if (upstream) {
std::snprintf(downstream->Path, sizeof(downstream->Path), "%s.%d",
upstream->Path, portnr);
downstream->HubCount = upstream->HubCount + 1;
}
else {
std::snprintf(downstream->Path, sizeof(downstream->Path), "%d", portnr);
downstream->HubCount = 0;
}
}
void USBPCIDevice::USB_DeviceAttach(XboxDeviceState* dev) {
USBPort * port = dev->Port;
assert(port != nullptr);
assert(!dev->Attached);
dev->Attached++;
USB_Attach(port);
}
}

View file

@ -44,28 +44,11 @@
#include "../defs.h"
#include "pci.h"
#include "openxbox/queue.h"
#include "openxbox/iovec.h"
#include "ohci_common.h"
#include "openxbox/cpu.h"
namespace openxbox {
#define USB_MAX_ENDPOINTS 15
#define USB_MAX_INTERFACES 16
#define USB_STATE_NOTATTACHED 0
#define USB_STATE_ATTACHED 1
#define USB_STATE_DEFAULT 2
typedef enum USBPacketState {
USB_PACKET_UNDEFINED = 0,
USB_PACKET_SETUP,
USB_PACKET_QUEUED,
USB_PACKET_ASYNC,
USB_PACKET_COMPLETE,
USB_PACKET_CANCELED,
}
USBPacketState;
typedef enum USBDeviceFlags {
USB_DEV_FLAG_FULL_PATH,
USB_DEV_FLAG_IS_HOST,
@ -74,160 +57,14 @@ typedef enum USBDeviceFlags {
}
USBDeviceFlags;
typedef struct _USBPacket USBPacket;
typedef struct _XboxDevice XboxDevice;
/* USB endpoint */
typedef struct _USBEndpoint {
uint8_t Num; // endpoint number
uint8_t pid;
uint8_t Type; // the type of this endpoint
uint8_t ifnum;
int max_packet_size;
bool Pipeline;
bool Halted; // indicates that the endpoint is halted
XboxDevice* Dev; // device this endpoint belongs to
QTAILQ_HEAD(, _USBPacket) Queue; // queue of packets to this endpoint
}
USBEndpoint;
/* definition of an Xbox usb device */
typedef struct _XboxDevice {
DeviceState qdev;
USBPort *port;
char *port_path;
char *serial;
void *opaque;
uint32_t flags;
// Actual connected speed
int speed;
// Supported speeds, not in info because it may be variable (hostdevs)
int speedmask;
uint8_t Addr; // device function address
char product_desc[32];
int auto_attach;
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];
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 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;
const USBDesc *usb_desc; // Overrides class usb_desc if not NULL
const USBDescDevice *device;
int configuration;
int ninterfaces;
int altsetting[USB_MAX_INTERFACES];
const USBDescConfig *config;
const USBDescIface *ifaces[USB_MAX_INTERFACES];
}
XboxDevice;
// ergo720: this could be merged in the OHCI or USBDevice class if possible
typedef struct USBDeviceClass {
DeviceClass parent_class;
int(*init)(USBDev *dev);
// Walk (enabled) downstream ports, check for a matching device.
// Only hubs implement this.
USBDev *(*find_device)(USBDev *dev, uint8_t addr);
// Called when a packet is canceled.
void(*cancel_packet)(USBDev *dev, USBPacket *p);
// Called when device is destroyed.
void(*handle_destroy)(USBDev *dev);
// Attach the device
void(*handle_attach)(USBDev *dev);
// Reset the device
void(*handle_reset)(USBDev *dev);
// Process control request.
// Called from handle_packet().
// Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
// then the number of bytes transferred is stored in p->actual_length
void(*handle_control)(USBDev *dev, USBPacket *p, int request, int value,
int index, int length, uint8_t *data);
// Process data transfers (both BULK and ISOC).
// Called from handle_packet().
// Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
// then the number of bytes transferred is stored in p->actual_length
void(*handle_data)(USBDev *dev, USBPacket *p);
void(*set_interface)(USBDev *dev, int Interface,
int alt_old, int alt_new);
// Called when the hcd is done queuing packets for an endpoint, only
// necessary for devices which can return USB_RET_ADD_TO_QUEUE.
void(*flush_ep_queue)(USBDev *dev, USBEndpoint *ep);
// Called by the hcd to let the device know the queue for an endpoint
// has been unlinked / stopped. Optional may be NULL.
void(*EP_Stopped)(USBDev* Dev, USBEndpoint* EP);
const char *product_desc;
const USBDesc *usb_desc;
}
USBDeviceClass;
typedef struct _USBCombinedPacket {
_USBPacket* First;
QTAILQ_HEAD(packets_head, _USBPacket) Packets;
IOVector IoVec;
}
USBCombinedPacket;
/* Structure used to hold information about an active USB packet */
struct _USBPacket {
int Pid; // Packet ID (used to identify the type of packet that is being sent)
uint32_t Id; // Paddr of the TD for this packet
USBEndpoint* Endpoint; // endpoint this packet is transferred to
unsigned int Stream;
IOVector IoVec; // used to perform vectored I/O
uint64_t Parameter; // control transfers
bool ShortNotOK; // the bufferRounding mode of the TD for this packet
bool IntReq; // whether or not to generate an interrupt for this packet (DelayInterrupt of the TD is zero)
int Status; // USB_RET_* status code
int ActualLength; // before copy: offset inside IoVec structs; after copy: number of bytes actually transferred
// Internal use by the USB layer
USBPacketState State;
USBCombinedPacket* Combined;
QTAILQ_ENTRY(_USBPacket) Queue;
QTAILQ_ENTRY(_USBPacket) CombinedEntry;
};
/* Struct describing the status of a usb port */
typedef struct _USBPort {
XboxDevice* Dev; // usb device (if present)
int SpeedMask; // usb speeds supported
int HubCount; // number of hubs attached
char Path[16]; // the number of the port
int PortIndex; // internal port index
QTAILQ_ENTRY(_USBPort) Next;
}
USBPort;
// Forward declare OHCI class for USBDevice class
// Forward declare OHCI class for m_HostController pointer
class OHCI;
/* Helper class which provides various functionality to both OHCI and usb device classes */
class USBPCIDevice : public PCIDevice {
public:
// constructor
USBPCIDevice(uint16_t vendorID, uint16_t deviceID, uint8_t revisionID, uint8_t m_irqn);
USBPCIDevice(uint16_t vendorID, uint16_t deviceID, uint8_t revisionID, uint8_t m_irqn, Cpu *cpu);
virtual ~USBPCIDevice();
// PCI Device functions
@ -241,32 +78,32 @@ public:
// USBDevice-specific functions/variables
// pointer to the host controller this device refers to
OHCI* m_HostController = nullptr;
OHCI* m_HostController;
// PCI path of this usb device
const char* m_PciPath;
// register a port with the HC
void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask);
//
void USB_DeviceEPstopped(XboxDevice* Dev, USBEndpoint* Ep);
void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask, USBPortOps* Ops);
// reset a usb port
void USB_PortReset(USBPort* Port);
// a device is attched
// update device status during an attach
void USB_DeviceAttach(XboxDeviceState* dev);
// update port status when a device is attached
void USB_Attach(USBPort* Port);
// a device is detached
// update port status when a device is detached
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, XboxDevice* Child);
void ChildDetach(USBPort* Port, XboxDeviceState* Child);
// TODO
void Wakeup(USBPort* Port);
// TODO
void Complete(USBPort* Port, USBPacket *P);
// reset a device
void USB_DeviceReset(XboxDevice* Dev);
// find the usb device with the supplied address
XboxDevice* USB_FindDevice(USBPort* Port, uint8_t Addr);
// ergo720: can probably be removed by calling directly usb_hub_find_device
XboxDevice* USB_DeviceFindDevice(XboxDevice* Dev, uint8_t Addr);
void USB_DeviceReset(XboxDeviceState* Dev);
//
XboxDeviceState* USB_DeviceFindDevice(XboxDeviceState* Dev, uint8_t Addr);
// find the requested endpoint in the supplied device
USBEndpoint* USB_GetEP(XboxDevice* Dev, int Pid, int Ep);
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);
@ -275,36 +112,59 @@ public:
// append the user buffer to the packet
void USB_PacketAddBuffer(USBPacket* p, void* ptr, size_t len);
// transfer and process the packet
void USB_HandlePacket(XboxDevice* dev, USBPacket* p);
void USB_HandlePacket(XboxDeviceState* dev, USBPacket* p);
// check if the packet has the expected state and assert if not
void USB_PacketCheckState(USBPacket* p, USBPacketState expected);
// process the packet
void USB_ProcessOne(USBPacket* p);
//
void USB_DoParameter(XboxDevice* s, USBPacket* p);
void USB_DoParameter(XboxDeviceState* s, USBPacket* p);
// process a setup token
void USB_DoTokenSetup(XboxDevice* s, USBPacket* p);
void USB_DoTokenSetup(XboxDeviceState* s, USBPacket* p);
// process an input token
void DoTokenIn(XboxDevice* s, USBPacket* p);
void DoTokenIn(XboxDeviceState* s, USBPacket* p);
// process an output token
void DoTokenOut(XboxDevice* s, USBPacket* p);
void DoTokenOut(XboxDeviceState* s, USBPacket* p);
// copy the packet data to the buffer pointed to by ptr
void USB_PacketCopy(USBPacket* p, void* ptr, size_t bytes);
// queue a packet to an endpoint
void USB_QueueOne(USBPacket* p);
//
void USB_DeviceHandleControl(XboxDevice* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data);
//
void USB_DeviceHandleData(XboxDevice* dev, USBPacket* p);
//
void USB_DeviceFlushEPqueue(XboxDevice* dev, USBEndpoint* ep);
//
void USB_DeviceCancelPacket(XboxDevice* dev, USBPacket* p);
// Cancel an active packet. The packed must have been deferred by
// returning USB_RET_ASYNC from handle_packet, and not yet completed
void USB_CancelPacket(USBPacket* p);
// queue a packet to an endpoint
void USB_QueueOne(USBPacket* p);
// call usb class init function
int USB_DeviceInit(XboxDeviceState* dev);
// call usb class find_device function
XboxDeviceState * USB_FindDevice(USBPort* Port, uint8_t Addr);
// call usb class cancel_packet function
void USB_DeviceCancelPacket(XboxDeviceState* dev, USBPacket* p);
// call usb class handle_destroy function
void USB_DeviceHandleDestroy(XboxDeviceState* dev);
// call usb class handle_attach function
void USB_DeviceHandleAttach(XboxDeviceState* dev);
// call usb class handle_reset function
void USB_DeviceHandleReset(XboxDeviceState* dev);
// call usb class handle_control function
void USB_DeviceHandleControl(XboxDeviceState* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data);
// call usb class handle_data function
void USB_DeviceHandleData(XboxDeviceState* dev, USBPacket* p);
// call usb class set_interface function
void USB_DeviceSetInterface(XboxDeviceState* dev, int iface, int alt_old, int alt_new);
// call usb class flush_ep_queue function
void USB_DeviceFlushEPqueue(XboxDeviceState* dev, USBEndpoint* ep);
// call usb class ep_stopped function
void USB_DeviceEPstopped(XboxDeviceState* Dev, USBEndpoint* Ep);
// set the type of the endpoint
void USB_EPsetType(XboxDeviceState* dev, int pid, int ep, uint8_t type);
// set the interface number of the endpoint
uint8_t USB_EPsetIfnum(XboxDeviceState* dev, int pid, int ep, uint8_t ifnum);
// set the maximum packet size parameter of the endpoint
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);
private:
uint8_t m_irqn;
Cpu* m_cpu;
};
}

View file

@ -315,8 +315,8 @@ int Xbox::Initialize(OpenXBOXSettings *settings)
m_HostBridge = new HostBridgeDevice(PCI_VENDOR_ID_NVIDIA, 0x02A5, 0xA1);
m_MCPXRAM = new MCPXRAMDevice(PCI_VENDOR_ID_NVIDIA, 0x02A6, 0xA1, mcpxRevision);
m_LPC = new LPCDevice(PCI_VENDOR_ID_NVIDIA, 0x01B2, 0xD4, m_IRQs);
m_USB1 = new USBPCIDevice(PCI_VENDOR_ID_NVIDIA, 0x02A5, 0xA1, 1);
m_USB2 = new USBPCIDevice(PCI_VENDOR_ID_NVIDIA, 0x02A5, 0xA1, 9);
m_USB1 = new USBPCIDevice(PCI_VENDOR_ID_NVIDIA, 0x02A5, 0xA1, 1, m_cpu);
m_USB2 = new USBPCIDevice(PCI_VENDOR_ID_NVIDIA, 0x02A5, 0xA1, 9, m_cpu);
m_NVNet = new NVNetDevice(PCI_VENDOR_ID_NVIDIA, 0x01C3, 0xD2);
m_NVAPU = new NVAPUDevice(PCI_VENDOR_ID_NVIDIA, 0x01B0, 0xD2);
m_AC97 = new AC97Device(PCI_VENDOR_ID_NVIDIA, 0x01B1, 0xD2);