Packet processing stuff and refactored USBDevice class (WIP)

This commit is contained in:
ergo720 2018-06-05 19:04:35 +02:00
parent 8ca5cff089
commit 0b5c2c911c
4 changed files with 250 additions and 142 deletions

View File

@ -105,9 +105,9 @@
/* Bitfields for the first word of an ED */
#define OHCI_ED_FA_SHIFT 0
#define OHCI_ED_FA_MASK (0x7F<<OHCI_ED_FA_SHIFT)
#define OHCI_ED_FA_MASK (0x7F<<OHCI_ED_FA_SHIFT) // FunctionAddress
#define OHCI_ED_EN_SHIFT 7
#define OHCI_ED_EN_MASK (0xF<<OHCI_ED_EN_SHIFT)
#define OHCI_ED_EN_MASK (0xF<<OHCI_ED_EN_SHIFT) // EndpointNumber
#define OHCI_ED_D_SHIFT 11
#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT) // Direction
#define OHCI_ED_S (1<<13)
@ -163,20 +163,6 @@
#define OHCI_CC_BUFFEROVERRUN 0xC
#define OHCI_CC_BUFFERUNDERRUN 0xD
#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)
#define USB_RET_STALL (-3)
#define USB_RET_BABBLE (-4)
#define USB_RET_IOERROR (-5)
#define USB_RET_ASYNC (-6)
#define USB_RET_ADD_TO_QUEUE (-7)
#define USB_RET_REMOVE_FROM_QUEUE (-8)
#define USB_HZ 12000000
#define USB_SPEED_LOW 0
@ -191,12 +177,13 @@ typedef enum _USB_SPEED
USB_SPEED;
OHCI::OHCI(int Irq)
OHCI::OHCI(int Irq, USBDevice* UsbObj)
{
m_IrqNum = Irq;
m_UsbDevice = UsbObj;
for (int i = 0; i < 2; i++) {
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, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
}
OHCI_PacketInit(&m_UsbPacket);
@ -479,7 +466,7 @@ int OHCI::OHCI_ServiceEDlist(xbaddr Head, int Completion)
if (m_AsyncTD && addr == m_AsyncTD) {
usb_cancel_packet(&ohci->usb_packet);
m_AsyncTD = xbnull;
USB_DeviceEPstopped(m_UsbPacket.Endpoint->Dev, m_UsbPacket.Endpoint);
m_UsbDevice->USB_DeviceEPstopped(m_UsbPacket.Endpoint->Dev, m_UsbPacket.Endpoint);
}
continue;
}
@ -530,7 +517,7 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
int pid;
int ret;
int i;
USBDev* dev;
XboxDevice* dev;
USBEndpoint* ep;
OHCI_TD td;
xbaddr addr;
@ -566,6 +553,16 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
direction = OHCI_BM(td.Flags, TD_DP);
}
// Info: Each USB transaction consists of a
// 1. Token Packet, (Header defining what it expects to follow).
// 2. Optional Data Packet, (Containing the payload).
// 3. Status Packet, (Used to acknowledge transactions and to provide a means of error correction).
// There are three types of token packets:
// In - Informs the USB device that the host wishes to read information.
// Out - Informs the USB device that the host wishes to send information.
// Setup - Used to begin control transfers.
switch (direction) {
case OHCI_TD_DIR_IN:
#ifdef DEBUG_PACKET
@ -645,9 +642,9 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
DbgPrintf("Too many pending packets\n");
return 1;
}
dev = ohci_find_device(ohci, OHCI_BM(Ed->Flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(Ed->Flags, ED_EN));
usb_packet_setup(&m_UsbPacket, pid, ep, 0, addr, !flag_r, OHCI_BM(td.Flags, TD_DI) == 0);
dev = OHCI_FindDevice(OHCI_BM(Ed->Flags, ED_FA));
ep = m_UsbDevice->USB_GetEP(dev, pid, OHCI_BM(Ed->Flags, ED_EN));
m_UsbDevice->USB_PacketSetup(&m_UsbPacket, pid, ep, 0, addr, !flag_r, OHCI_BM(td.Flags, TD_DI) == 0);
usb_packet_addbuf(&m_UsbPacket, ohci->usb_buf, packetlen);
usb_handle_packet(dev, &m_UsbPacket);
#ifdef DEBUG_PACKET
@ -764,6 +761,24 @@ exit_no_retire:
return OHCI_BM(td.Flags, TD_CC) != OHCI_CC_NOERROR;
}
XboxDevice* OHCI::OHCI_FindDevice(uint8_t Addr)
{
XboxDevice* dev;
int i;
for (i = 0; i < 2; i++) {
if ((m_Registers.RhPort[i].HcRhPortStatus & OHCI_PORT_PES) == 0) {
continue; // port is disabled
}
dev = m_UsbDevice->USB_FindDevice(&m_Registers.RhPort[i].UsbPort, Addr);
if (dev != nullptr) {
return dev; // return found device
}
}
return nullptr;
}
void OHCI::OHCI_StateReset()
{
// The usb state can be USB_Suspend if it is a software reset, and USB_Reset if it is a hardware
@ -806,7 +821,7 @@ void OHCI::OHCI_StateReset()
OHCIPort* Port = &m_Registers.RhPort[i];
Port->HcRhPortStatus = 0;
if (Port->UsbPort.Dev && Port->UsbPort.Dev->Attached) {
USB_PortReset(&Port->UsbPort);
m_UsbDevice->USB_PortReset(&Port->UsbPort);
}
}
if (m_AsyncTD) {
@ -1174,16 +1189,16 @@ uint32_t OHCI::OHCI_GetFrameRemaining()
void OHCI::OHCI_StopEndpoints()
{
USBDev* dev;
XboxDevice* dev;
int i, j;
for (i = 0; i < 2; i++) {
dev = m_Registers.RhPort[i].UsbPort.Dev;
if (dev && dev->Attached) {
USB_DeviceEPstopped(dev, &dev->EP_ctl);
m_UsbDevice->USB_DeviceEPstopped(dev, &dev->EP_ctl);
for (j = 0; j < USB_MAX_ENDPOINTS; j++) {
USB_DeviceEPstopped(dev, &dev->EP_in[j]);
USB_DeviceEPstopped(dev, &dev->EP_out[j]);
m_UsbDevice->USB_DeviceEPstopped(dev, &dev->EP_in[j]);
m_UsbDevice->USB_DeviceEPstopped(dev, &dev->EP_out[j]);
}
}
}
@ -1269,7 +1284,7 @@ void OHCI::OHCI_PortSetStatus(int PortNum, uint32_t Value)
if (OHCI_PortSetIfConnected(PortNum, Value & OHCI_PORT_PRS)) {
DbgPrintf("Ohci: port %d: RESET\n", PortNum);
USB_DeviceReset(port->UsbPort.Dev);
m_UsbDevice->USB_DeviceReset(port->UsbPort.Dev);
port->HcRhPortStatus &= ~OHCI_PORT_PRS;
// ??? Should this also set OHCI_PORT_PESC
port->HcRhPortStatus |= OHCI_PORT_PES | OHCI_PORT_PRSC;
@ -1371,63 +1386,3 @@ void OHCI::OHCI_Attach(USBPort* Port)
}
}
void OHCI::USB_RegisterPort(USBPort* Port, int Index, int SpeedMask)
{
Port->PortIndex = Index;
Port->SpeedMask = SpeedMask;
Port->HubCount = 0;
std::snprintf(Port->Path, sizeof(Port->Path), "%d", Index + 1);
}
void OHCI::USB_DeviceEPstopped(USBDev* 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);
}
}
void OHCI::USB_PortReset(USBPort* Port)
{
USBDev* dev = Port->Dev;
assert(dev != nullptr);
USB_Detach(Port);
USB_Attach(Port);
USB_DeviceReset(dev);
}
void OHCI::USB_Detach(USBPort* Port)
{
USBDev* dev = Port->Dev;
assert(dev != nullptr);
assert(dev->State != USB_STATE_NOTATTACHED);
OHCI_Detach(Port);
dev->State = USB_STATE_NOTATTACHED;
}
void OHCI::USB_Attach(USBPort* Port)
{
USBDev *dev = Port->Dev;
assert(dev != nullptr);
assert(dev->Attached);
assert(dev->State == USB_STATE_NOTATTACHED);
OHCI_Attach(Port);
dev->State = USB_STATE_ATTACHED;
usb_device_handle_attach(dev);
}
void OHCI::USB_DeviceReset(USBDev* dev)
{
if (dev == nullptr || !dev->Attached) {
return;
}
dev->RemoteWakeup = 0;
dev->Addr = 0;
dev->State = USB_STATE_DEFAULT;
usb_device_handle_reset(dev);
}

View File

@ -41,6 +41,20 @@
#include "USBDevice.h"
#include "..\CxbxKrnl\Timer.h"
#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)
#define USB_RET_STALL (-3)
#define USB_RET_BABBLE (-4)
#define USB_RET_IOERROR (-5)
#define USB_RET_ASYNC (-6)
#define USB_RET_ADD_TO_QUEUE (-7)
#define USB_RET_REMOVE_FROM_QUEUE (-8)
// Abbreviations used:
// OHCI: Open Host Controller Interface; the standard used on the Xbox to comunicate with the usb devices
@ -140,7 +154,7 @@ class OHCI
{
public:
// constructor
OHCI(int Irqn);
OHCI(int Irqn, USBDevice* UsbObj);
// destructor
~OHCI() {}
// read a register
@ -150,6 +164,8 @@ class OHCI
private:
// pointer to g_USB0 or g_USB1
USBDevice* m_UsbDevice;
// all the registers available on the OHCI standard
OHCI_Registers m_Registers;
// end-of-frame timer
@ -239,25 +255,8 @@ class OHCI
int OHCI_ServiceEDlist(xbaddr Head, int Completion);
// process a TD. Returns nonzero to terminate processing of this endpoint
int OHCI_ServiceTD(OHCI_ED* Ed);
// register a port with the HC
void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask);
//
void USB_DeviceEPstopped(USBDev* Dev, USBEndpoint* Ep);
// reset a usb port
void USB_PortReset(USBPort* Port);
// a device is attched
void USB_Attach(USBPort* Port);
// 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, USBDev* Child);
// TODO
void Wakeup(USBPort* Port);
// TODO
void Complete(USBPort* Port, USBPacket *P);
// reset a device
void USB_DeviceReset(USBDev* Dev);
// find the usb device with the supplied address
XboxDevice* OHCI::OHCI_FindDevice(uint8_t Addr);
};
#endif

View File

@ -52,11 +52,11 @@ void USBDevice::Init(unsigned int address)
m_VendorId = PCI_VENDOR_ID_NVIDIA;
if (address == USB0_BASE) {
m_pHostController1 = new OHCI(1);
m_pHostController1 = new OHCI(1, this);
return;
}
m_pHostController2 = new OHCI(9);
m_pHostController2 = new OHCI(9, this);
}
uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
@ -89,3 +89,124 @@ void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned
// USB1 queried
m_pHostController2->OHCI_WriteRegister(addr, value);
}
void USBDevice::USB_RegisterPort(USBPort* Port, int Index, int SpeedMask)
{
Port->PortIndex = Index;
Port->SpeedMask = SpeedMask;
Port->HubCount = 0;
std::snprintf(Port->Path, sizeof(Port->Path), "%d", Index + 1);
}
void USBDevice::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);
}
}
void USBDevice::USB_PortReset(USBPort* Port)
{
XboxDevice* dev = Port->Dev;
assert(dev != nullptr);
USB_Detach(Port);
USB_Attach(Port);
USB_DeviceReset(dev);
}
void USBDevice::USB_Detach(USBPort* Port)
{
XboxDevice* dev = Port->Dev;
assert(dev != nullptr);
assert(dev->State != USB_STATE_NOTATTACHED);
OHCI_Detach(Port);
dev->State = USB_STATE_NOTATTACHED;
}
void USBDevice::USB_Attach(USBPort* Port)
{
XboxDevice *dev = Port->Dev;
assert(dev != nullptr);
assert(dev->Attached);
assert(dev->State == USB_STATE_NOTATTACHED);
OHCI_Attach(Port);
dev->State = USB_STATE_ATTACHED;
usb_device_handle_attach(dev);
}
void USBDevice::USB_DeviceReset(XboxDevice* dev)
{
if (dev == nullptr || !dev->Attached) {
return;
}
dev->RemoteWakeup = 0;
dev->Addr = 0;
dev->State = USB_STATE_DEFAULT;
usb_device_handle_reset(dev);
}
XboxDevice* USBDevice::USB_FindDevice(USBPort* Port, uint8_t Addr)
{
XboxDevice* dev = Port->Dev;
if (dev == nullptr || !dev->Attached || dev->State != USB_STATE_DEFAULT) {
return nullptr;
}
if (dev->Addr == Addr) {
return dev;
}
return USB_DeviceFindDevice(dev, Addr);
}
XboxDevice* USBDevice::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* USBDevice::USB_GetEP(XboxDevice* Dev, int Pid, int Ep)
{
USBEndpoint* eps;
if (Dev == nullptr) {
return nullptr;
}
eps = (Pid == USB_TOKEN_IN) ? Dev->EP_in : Dev->EP_out;
if (Ep == 0) {
return &Dev->EP_ctl; // EndpointNumber zero represents the default control endpoint
}
assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
return eps + Ep - 1;
}
void USBDevice::USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, unsigned int Stream,
uint64_t Id, bool ShortNotOK, bool IntReq)
{
assert(!usb_packet_is_inflight(p));
assert(p->iov.iov != NULL);
p->Id = Id;
p->Pid = Pid;
p->Endpoint = Ep;
p->Stream = Stream;
p->Status = USB_RET_SUCCESS;
p->ActualLength = 0;
p->Parameter = 0;
p->ShortNotOK = ShortNotOK;
p->IntReq = IntReq;
p->Combined = NULL;
qemu_iovec_reset(&p->iov);
usb_packet_set_state(p, USB_PACKET_SETUP);
}

View File

@ -37,6 +37,7 @@
#define USBDEVICE_H_
#include "..\PCIDevice.h"
#include "..\devices\video\queue.h"
#include <assert.h>
#define USB_MAX_ENDPOINTS 15
@ -73,6 +74,9 @@ typedef struct _IOVector
}
IOVector;
typedef struct _USBPacket USBPacket;
typedef struct _XboxDevice XboxDevice;
/* USB endpoint */
typedef struct _USBEndpoint
{
@ -83,13 +87,13 @@ typedef struct _USBEndpoint
int max_packet_size;
bool pipeline;
bool halted;
USBDev* Dev; // device this endpoint belongs to
//QTAILQ_HEAD(, USBPacket) queue;
XboxDevice* Dev; // device this endpoint belongs to
QTAILQ_HEAD(, _USBPacket) Queue; // queue of packets to this endpoint
}
USBEndpoint;
/* definition of a USB device */
typedef struct _USBDev
/* definition of an Xbox usb device */
typedef struct _XboxDevice
{
DeviceState qdev;
USBPort *port;
@ -102,25 +106,25 @@ typedef struct _USBDev
int speed;
// Supported speeds, not in info because it may be variable (hostdevs)
int speedmask;
uint8_t Addr; // device address
uint8_t Addr; // device function address
char product_desc[32];
int auto_attach;
int Attached; // device is attached
int Attached; // device is attached
int32_t State; // current state of device
int32_t State; // current state of device
uint8_t setup_buf[8];
uint8_t data_buf[4096];
int32_t RemoteWakeup; // wakeup flag
int32_t RemoteWakeup; // wakeup flag
int32_t setup_state;
int32_t setup_len;
int32_t setup_index;
USBEndpoint EP_ctl;
USBEndpoint EP_in[USB_MAX_ENDPOINTS];
USBEndpoint EP_out[USB_MAX_ENDPOINTS];
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 USBDesc *usb_desc; // Overrides class usb_desc if not NULL
const USBDescDevice *device;
int configuration;
@ -129,8 +133,9 @@ typedef struct _USBDev
const USBDescConfig *config;
const USBDescIface *ifaces[USB_MAX_INTERFACES];
}
USBDev;
XboxDevice;
// ergo720: this could be merged in the OHCI or USBDevice class if possible
typedef struct USBDeviceClass
{
DeviceClass parent_class;
@ -183,30 +188,29 @@ typedef struct USBDeviceClass
USBDeviceClass;
/* Structure used to hold information about an active USB packet */
typedef struct _USBPacket
struct _USBPacket
{
// Data fields for use by the driver
int Pid;
uint64_t Id;
USBEndpoint* Endpoint; // endpoint of the packet
unsigned int stream;
IOVector IoVec; // used to perform vectored I/O
uint64_t parameter; // control transfers
bool short_not_ok;
bool int_req;
int status; // USB_RET_* status code
int actual_length; // Number of bytes actually transferred
int Pid; // Packet ID
uint32_t Id; // Paddr of the TD for this packet
USBEndpoint* Endpoint; // endpoint this packet is transferred to
unsigned int Stream;
IOVector IoVec; // used to perform vectored I/O
uint64_t Parameter; // control transfers
bool ShortNotOK; // the bufferRounding mode of the TD for this packet
bool IntReq; // whether or not to generate an interrupt for this packet (DelayInterrupt of the TD is zero)
int Status; // USB_RET_* status code
int ActualLength; // Number of bytes actually transferred
// Internal use by the USB layer
USBPacketState state;
USBCombinedPacket *combined;
//QTAILQ_ENTRY(USBPacket) queue;
//QTAILQ_ENTRY(USBPacket) combined_entry;
}
USBPacket;
USBPacketState State;
//USBCombinedPacket *Combined;
QTAILQ_ENTRY(_USBPacket) Queue;
QTAILQ_ENTRY(_USBPacket) CombinedEntry;
};
/* Struct describing the status of a usb port */
typedef struct _USBPort {
USBDev* Dev; // usb device (if present)
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
@ -233,9 +237,38 @@ class USBDevice : public PCIDevice {
void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size);
// USBDevice-specific functions/variables
// pointers to the two USB host controllers available on the Xbox
OHCI* m_pHostController1 = nullptr;
OHCI* m_pHostController2 = nullptr;
// register a port with the HC
void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask);
//
void USB_DeviceEPstopped(XboxDevice* Dev, USBEndpoint* Ep);
// reset a usb port
void USB_PortReset(USBPort* Port);
// a device is attched
void USB_Attach(USBPort* Port);
// 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);
// 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);
// find the requested endpoint in the supplied device
USBEndpoint* USB_GetEP(XboxDevice* 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);
};
#endif