Usb port stuff (wip)

This commit is contained in:
ergo720 2018-05-28 21:14:21 +02:00
parent 685227d874
commit 421a5b8754
4 changed files with 234 additions and 73 deletions

View File

@ -47,18 +47,13 @@ typedef enum _USB_SPEED
}
USB_SPEED;
// global pointers to the two USB host controllers available on the Xbox
OHCI* g_pHostController1 = nullptr;
OHCI* g_pHostController2 = nullptr;
OHCI::OHCI(USBDevice* UsbObj, int Irq)
OHCI::OHCI(int Irq)
{
UsbInstance = UsbObj;
Irq_n = Irq;
for (int i = 0; i < 2; i++) {
UsbInstance->USB_RegisterPort(&Registers.RhPort[i].Port, this, i, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
USB_RegisterPort(&Registers.RhPort[i].UsbPort, i, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
}
OHCI_PacketInit(&UsbPacket);
@ -79,7 +74,7 @@ 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
// reset or cold boot
// TODO: stop all the list processing here
OHCI_BusStop();
// Reset all registers
// Remark: the standard says that RemoteWakeupConnected bit should be set during POST, cleared during hw reset
@ -112,10 +107,12 @@ void OHCI::OHCI_StateReset()
{
OHCIPort* Port = &Registers.RhPort[i];
Port->HcRhPortStatus = 0;
//if (Port->port.device && Port->port.device->attached) {
//usb_port_reset(&Port->port);
//}
if (Port->UsbPort.Dev && Port->UsbPort.Dev->Attached) {
USB_PortReset(&Port->UsbPort);
}
}
OHCI_StopEndpoints();
DbgPrintf("Ohci: Reset event.\n");
}
@ -443,3 +440,82 @@ void OHCI::OHCI_SetInterrupt(uint32_t Value)
Registers.HcInterruptStatus |= Value;
OHCI_UpdateInterrupt();
}
void OHCI::OHCI_StopEndpoints()
{
USBDev* dev;
int i, j;
for (i = 0; i < 2; i++) {
dev = Registers.RhPort[i].UsbPort.Dev;
if (dev && dev->Attached) {
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]);
}
}
}
}
void OHCI::OHCI_Detach(USBPort* Port)
{
OHCIPort* port = &Registers.RhPort[Port->PortIndex];
uint32_t old_state = port->HcRhPortStatus;
ohci_async_cancel_device(Port->Dev);
// set connect status
if (port->HcRhPortStatus & OHCI_PORT_CCS) {
port->HcRhPortStatus &= ~OHCI_PORT_CCS;
port->HcRhPortStatus |= OHCI_PORT_CSC;
}
// disable port
if (port->HcRhPortStatus & OHCI_PORT_PES) {
port->HcRhPortStatus &= ~OHCI_PORT_PES;
port->HcRhPortStatus |= OHCI_PORT_PESC;
}
DbgPrintf("Ohci: Detached port %d\n", Port->PortIndex);
if (old_state != port->HcRhPortStatus) {
OHCI_SetInterrupt(OHCI_INTR_RHSC);
}
}
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_device_reset(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;
}

View File

@ -66,7 +66,15 @@
#define OHCI_STATUS_OCR (1<<3) // OwnershipChangeRequest
#define OHCI_STATUS_SOC ((1<<6)|(1<<7)) // SchedulingOverrunCount
// HcInterruptStatus
#define OHCI_INTR_SO (1<<0) // Scheduling overrun
#define OHCI_INTR_WD (1<<1) // HcDoneHead writeback
#define OHCI_INTR_SF (1<<2) // Start of frame
#define OHCI_INTR_RD (1<<3) // Resume detect
#define OHCI_INTR_UE (1<<4) // Unrecoverable error
#define OHCI_INTR_FNO (1<<5) // Frame number overflow
#define OHCI_INTR_RHSC (1<<6) // Root hub status change
#define OHCI_INTR_OC (1<<30) // Ownership change
#define OHCI_INTR_MIE (1<<31) // Master Interrupt Enable
// HcInterruptEnable, HcInterruptDisable
#define OHCI_INTR_MIE (1<<31) // MasterInterruptEnable
// HcHCCA
@ -84,8 +92,20 @@
#define OHCI_RHA_OCPM (1<<11) // OverCurrentProtectionMode
#define OHCI_RHA_NOCP (1<<12) // NoOverCurrentProtection
// HcRhPortStatus
#define OHCI_PORT_CCS (1<<0) // CurrentConnectStatus
#define OHCI_PORT_PES (1<<1) // PortEnableStatus
#define OHCI_PORT_PSS (1<<2) // PortSuspendStatus
#define OHCI_PORT_POCI (1<<3) // PortOverCurrentIndicator
#define OHCI_PORT_PRS (1<<4) // PortResetStatus
#define OHCI_PORT_PPS (1<<8) // PortPowerStatus
#define OHCI_PORT_LSDA (1<<9) // LowSpeedDeviceAttached
#define OHCI_PORT_CSC (1<<16) // ConnectStatusChange
#define OHCI_PORT_PESC (1<<17) // PortEnableStatusChange
#define OHCI_PORT_PSSC (1<<18) // PortSuspendStatusChange
#define OHCI_PORT_OCIC (1<<19) // PortOverCurrentIndicatorChange
#define OHCI_PORT_PRSC (1<<20) // PortResetStatusChange
#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \
|OHCI_PORT_OCIC|OHCI_PORT_PRSC)
// enum indicating the current HC state
typedef enum _OHCI_State
@ -100,7 +120,7 @@ OHCI_State;
// Small struct used to hold the HcRhPortStatus register and the usb port status
typedef struct _OHCIPort
{
USBPort Port;
USBPort UsbPort;
uint32_t HcRhPortStatus;
}
OHCIPort;
@ -135,6 +155,8 @@ typedef struct _OHCI_Registers
uint32_t HcRhDescriptorA;
uint32_t HcRhDescriptorB;
uint32_t HcRhStatus;
// 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;
@ -145,7 +167,7 @@ class OHCI
{
public:
// constructor
OHCI(USBDevice* UsbObj, int Irqn);
OHCI(int Irqn);
// destructor
~OHCI() {}
// read a register
@ -165,8 +187,6 @@ class OHCI
uint64_t UsbFrameTime;
// ticks per usb tick
uint64_t TicksPerUsbTick;
// the usb device instance of this HC
USBDevice* UsbInstance;
// usb packet
USBPacket UsbPacket;
// irq number
@ -192,9 +212,27 @@ class OHCI
void OHCI_UpdateInterrupt();
// fire an interrupt
void OHCI_SetInterrupt(uint32_t Value);
//
void OHCI_StopEndpoints();
// update ohci registers during a device detach
void OHCI_Detach(USBPort* Port);
// 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 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);
};
extern OHCI* g_pHostController1;
extern OHCI* g_pHostController2;
#endif

View File

@ -36,7 +36,6 @@
#include "USBDevice.h"
#include "OHCI.h"
#include <assert.h>
void USBDevice::Init(unsigned int address)
@ -53,11 +52,11 @@ void USBDevice::Init(unsigned int address)
m_VendorId = PCI_VENDOR_ID_NVIDIA;
if (address == USB0_BASE) {
g_pHostController1 = new OHCI(this, 1);
m_pHostController1 = new OHCI(1);
return;
}
g_pHostController2 = new OHCI(this, 9);
m_pHostController2 = new OHCI(9);
}
uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
@ -66,13 +65,13 @@ uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
assert(barIndex == 0);
// Figure out the correct OHCI object and read the register
if (addr >= USB1_BASE) {
// USB1 queried
return g_pHostController2->OHCI_ReadRegister(addr);
if (addr >= USB0_BASE) {
// USB0 queried
return m_pHostController1->OHCI_ReadRegister(addr);
}
// USB0 queried
return g_pHostController1->OHCI_ReadRegister(addr);
// USB1 queried
return m_pHostController2->OHCI_ReadRegister(addr);
}
void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size)
@ -81,21 +80,12 @@ void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned
assert(barIndex == 0);
// Figure out the correct OHCI object and write the value to the register
if (addr >= USB1_BASE) {
// USB1 queried
g_pHostController2->OHCI_WriteRegister(addr, value);
if (addr >= USB0_BASE) {
// USB0 queried
m_pHostController1->OHCI_WriteRegister(addr, value);
return;
}
// USB0 queried
g_pHostController1->OHCI_WriteRegister(addr, value);
}
void USBDevice::USB_RegisterPort(USBPort* Port, OHCI* Obj, int Index, int SpeedMask)
{
Port->Opaque = Obj;
Port->PortIndex = Index;
Port->SpeedMask = SpeedMask;
Port->HubCount = 0;
std::snprintf(Port->Path, sizeof(Port->Path), "%d", Index + 1);
// USB1 queried
m_pHostController2->OHCI_WriteRegister(addr, value);
}

View File

@ -37,7 +37,23 @@
#define USBDEVICE_H_
#include "..\PCIDevice.h"
#include <assert.h>
#define USB_MAX_ENDPOINTS 15
#define USB_MAX_INTERFACES 16
#define USB_STATE_NOTATTACHED 0
#define USB_STATE_ATTACHED 1
typedef enum USBPacketState {
USB_PACKET_UNDEFINED = 0,
USB_PACKET_SETUP,
USB_PACKET_QUEUED,
USB_PACKET_ASYNC,
USB_PACKET_COMPLETE,
USB_PACKET_CANCELED,
}
USBPacketState;
// This is a linux struct for vectored I/O. See readv() and writev()
typedef struct _IoVec
@ -56,6 +72,20 @@ typedef struct _IOVector
}
IOVector;
typedef struct _USBEndpoint
{
uint8_t nr;
uint8_t pid;
uint8_t type;
uint8_t ifnum;
int max_packet_size;
bool pipeline;
bool halted;
USBDev *dev;
//QTAILQ_HEAD(, USBPacket) queue;
}
USBEndpoint;
// definition of a USB device
typedef struct _USBDev
{
@ -73,9 +103,9 @@ typedef struct _USBDev
uint8_t addr;
char product_desc[32];
int auto_attach;
int attached;
int Attached; // device is attached
int32_t state;
int32_t State; // current state of device
uint8_t setup_buf[8];
uint8_t data_buf[4096];
int32_t remote_wakeup;
@ -83,9 +113,9 @@ typedef struct _USBDev
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;
USBEndpoint EP_in[USB_MAX_ENDPOINTS];
USBEndpoint EP_out[USB_MAX_ENDPOINTS];
//QLIST_HEAD(, USBDescString) strings;
const USBDesc *usb_desc; // Overrides class usb_desc if not NULL
@ -99,19 +129,56 @@ typedef struct _USBDev
}
USBDev;
typedef struct _USBEndpoint
typedef struct USBDeviceClass
{
uint8_t nr;
uint8_t pid;
uint8_t type;
uint8_t ifnum;
int max_packet_size;
bool pipeline;
bool halted;
USBDevice *dev;
//QTAILQ_HEAD(, USBPacket) queue;
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;
}
USBEndpoint;
USBDeviceClass;
// Structure used to hold information about an active USB packet
typedef struct _USBPacket
@ -137,23 +204,12 @@ USBPacket;
// Struct describing the status of a usb port
typedef struct _USBPort {
USBDev *Dev;
USBDev* Dev; // usb device (if present)
int SpeedMask; // usb speeds supported
int HubCount; // number of hubs attached
char Path[16]; // the number of the port
OHCI* Opaque; // OHCI* to let USBPort access it
int PortIndex; // internal port index, may be used with the Opaque
int PortIndex; // internal port index
//QTAILQ_ENTRY(USBPort) next;
// a device is attched
void Attach(USBPort* port);
// a device is detached
void 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);
}
USBPort;
@ -174,9 +230,10 @@ class USBDevice : public PCIDevice {
uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size);
void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size);
// USB specific functions
// register a port with the HC
void USB_RegisterPort(USBPort* Port, OHCI* Obj, int Index, int SpeedMask);
// pointers to the two USB host controllers available on the Xbox
OHCI* m_pHostController1 = nullptr;
OHCI* m_pHostController2 = nullptr;
};
#endif