USB/HC initialization stuff

This commit is contained in:
ergo720 2018-05-17 23:08:02 +02:00
parent 6aafb25dee
commit 7f0bea988c
7 changed files with 256 additions and 70 deletions

View File

@ -1239,7 +1239,9 @@ __declspec(noreturn) void CxbxKrnlInit
// for unicode conversions // for unicode conversions
setlocale(LC_ALL, "English"); setlocale(LC_ALL, "English");
// Initialize time-related variables for the kernel and the timers
CxbxInitPerformanceCounters(); CxbxInitPerformanceCounters();
Timer_Init();
#ifdef _DEBUG #ifdef _DEBUG
// CxbxPopupMessage("Attach a Debugger"); // CxbxPopupMessage("Attach a Debugger");
// Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool // Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool

View File

@ -50,7 +50,7 @@
// Vector storing all the timers created // Vector storing all the timers created
static std::vector<TimerObject*> TimerList; static std::vector<TimerObject*> TimerList;
// The frequency of the high resolution timer of the host // The frequency of the high resolution clock of the host
static uint64_t ClockFrequency; static uint64_t ClockFrequency;
@ -161,7 +161,7 @@ void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
std::thread(ClockThread, Timer).detach(); std::thread(ClockThread, Timer).detach();
} }
// Retrives the frequency of the high resolution timer of the host // Retrives the frequency of the high resolution clock of the host
void Timer_Init() void Timer_Init()
{ {
LARGE_INTEGER freq; LARGE_INTEGER freq;

View File

@ -54,8 +54,8 @@ TimerObject;
/* Timer exported functions */ /* Timer exported functions */
TimerObject* Timer_Create(pTimerCB callback, void* arg, unsigned int Factor); TimerObject* Timer_Create(pTimerCB Callback, void* Arg, unsigned int Factor);
void Timer_Start(TimerObject* timer, uint64_t Expire_MS); void Timer_Start(TimerObject* Timer, uint64_t Expire_MS);
void Timer_Exit(TimerObject* Timer); void Timer_Exit(TimerObject* Timer);
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms); void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
void Timer_Init(); void Timer_Init();

View File

@ -37,12 +37,46 @@
#include "OHCI.h" #include "OHCI.h"
#include "..\CxbxKrnl\CxbxKrnl.h" #include "..\CxbxKrnl\CxbxKrnl.h"
#define USB_HZ 12000000
typedef enum _USB_SPEED
{
USB_SPEED_MASK_LOW = 1 << 0,
USB_SPEED_MASK_FULL = 1 << 1,
}
USB_SPEED;
// global pointers to the two USB host controllers available on the Xbox // global pointers to the two USB host controllers available on the Xbox
OHCI_State* g_pHostController1 = nullptr; OHCI* g_pHostController1 = nullptr;
OHCI_State* g_pHostController2 = nullptr; OHCI* g_pHostController2 = nullptr;
void OHCI_State::HC_ChangeState(USB_State new_state) OHCI::OHCI(USBDevice* UsbObj)
{
UsbInstance = UsbObj;
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_PacketInit(&UsbPacket);
// Create the end-of-frame timer. Let's try a factor of 50 (1 virtual ms -> 50 real ms)
pEndOfFrameTimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, 50);
UsbFrameTime = 1000000ULL; // 1 ms
TicksPerUsbTick = 1000000000ULL / USB_HZ; // 83
// Do a hardware reset
OHCI_StateReset();
}
void OHCI::OHCI_FrameBoundaryWrapper(void* pVoid)
{
static_cast<OHCI*>(pVoid)->OHCI_FrameBoundaryWorker();
}
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 // 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 // reset or cold boot
@ -50,39 +84,34 @@ void OHCI_State::HC_ChangeState(USB_State new_state)
// TODO: stop all the list processing here // TODO: stop all the list processing here
// Reset all registers // Reset all registers
if (new_state == USB_Reset) { // Remark: the standard says that RemoteWakeupConnected bit should be set during POST, cleared during hw reset
// Remark: the standard says that RemoteWakeupConnected bit should be set during POST, cleared during hw reset // and ignored during a sw reset. However, VBox sets it on hw reset and XQEMU clears it. Considering that the Xbox
// and ignored during a sw reset. However, VBox sets it on hw reset and XQEMU clears it. Considering that the Xbox // doesn't do POST, I will clear it.
// doesn't do POST, I will clear it. Registers.HcControl = 0;
HC_Registers.HcControl = 0; Registers.HcControl &= ~OHCI_CTL_HCFS;
} Registers.HcControl |= Reset;
else { Registers.HcCommandStatus = 0;
HC_Registers.HcControl &= (OHCI_CTL_IR | OHCI_CTL_RWC); Registers.HcInterruptStatus = 0;
} Registers.HcInterruptEnable = OHCI_INTR_MASTER_INTERRUPT_ENABLED; // enable interrupts
HC_Registers.HcControl &= ~OHCI_CTL_HCFS;
HC_Registers.HcControl |= new_state;
HC_Registers.HcCommandStatus = 0;
HC_Registers.HcInterruptStatus = 0;
HC_Registers.HcInterruptEnable = OHCI_INTR_MASTER_INTERRUPT_ENABLED; // enable interrupts
HC_Registers.HcHCCA = 0; Registers.HcHCCA = 0;
HC_Registers.HcPeriodCurrentED = 0; Registers.HcPeriodCurrentED = 0;
HC_Registers.HcControlHeadED = HC_Registers.HcControlCurrentED = 0; Registers.HcControlHeadED = Registers.HcControlCurrentED = 0;
HC_Registers.HcBulkHeadED = HC_Registers.HcBulkCurrentED = 0; Registers.HcBulkHeadED = Registers.HcBulkCurrentED = 0;
HC_Registers.HcDoneHead = 0; Registers.HcDoneHead = 0;
HC_Registers.HcFmInterval = 0; Registers.HcFmInterval = 0;
HC_Registers.HcFmInterval |= (0x2778 << 16); // TBD according to the standard, using what XQEMU sets (FSLargestDataPacket) Registers.HcFmInterval |= (0x2778 << 16); // TBD according to the standard, using what XQEMU sets (FSLargestDataPacket)
HC_Registers.HcFmInterval |= 0x2EDF; // bit-time of a frame. 1 frame = 1 ms (FrameInterval) Registers.HcFmInterval |= 0x2EDF; // bit-time of a frame. 1 frame = 1 ms (FrameInterval)
HC_Registers.HcFmRemaining = 0; Registers.HcFmRemaining = 0;
HC_Registers.HcFmNumber = 0; Registers.HcFmNumber = 0;
HC_Registers.HcPeriodicStart = 0; Registers.HcPeriodicStart = 0;
HC_Registers.HcRhDescriptorA = OHCI_RHA_NPS | 2; // The xbox lacks the hw to switch off the power on the ports and has 2 ports per HC Registers.HcRhDescriptorA = OHCI_RHA_NPS | 2; // The xbox lacks the hw to switch off the power on the ports and has 2 ports per HC
HC_Registers.HcRhDescriptorB = 0; // The attached devices are removable and use PowerSwitchingMode to control the power on the ports Registers.HcRhDescriptorB = 0; // The attached devices are removable and use PowerSwitchingMode to control the power on the ports
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
OHCIPort* Port = &HC_Registers.RhPort[i]; OHCIPort* Port = &Registers.RhPort[i];
Port->HcRhPortStatus = 0; Port->HcRhPortStatus = 0;
//if (Port->port.device && Port->port.device->attached) { //if (Port->port.device && Port->port.device->attached) {
//usb_port_reset(&Port->port); //usb_port_reset(&Port->port);
@ -90,4 +119,13 @@ void OHCI_State::HC_ChangeState(USB_State new_state)
} }
DbgPrintf("Usb-Ohci: Reset\n"); DbgPrintf("Usb-Ohci: Reset\n");
} }
void OHCI::USB_PacketInit(USBPacket* packet)
{
IOVector* vec = &packet->IoVec;
vec->IoVec = new IoVec;
vec->IoVecNumber = 0;
vec->AllocNumber = 1;
vec->Size = 0;
}

View File

@ -39,6 +39,8 @@
#include <stdint.h> #include <stdint.h>
#include "Cxbx.h" #include "Cxbx.h"
#include "USBDevice.h"
#include "..\CxbxKrnl\Timer.h"
// Abbreviations used: // Abbreviations used:
@ -67,16 +69,21 @@
#define OHCI_RHA_OCPM (1<<11) // OverCurrentProtectionMode #define OHCI_RHA_OCPM (1<<11) // OverCurrentProtectionMode
#define OHCI_RHA_NOCP (1<<12) // NoOverCurrentProtection #define OHCI_RHA_NOCP (1<<12) // NoOverCurrentProtection
// Struct describing the status of a usb port
struct USBPort { // enum indicating the current HC state
//USBDevice* device; typedef enum _OHCI_State
//TODO {
}; Reset = 0x00,
Resume = 0x40,
Operational = 0x80,
Suspend = 0xC0,
}
OHCI_State;
// Small struct used to hold the HcRhPortStatus register and the usb port status // Small struct used to hold the HcRhPortStatus register and the usb port status
typedef struct _OHCIPort typedef struct _OHCIPort
{ {
USBPort port; USBPort Port;
uint32_t HcRhPortStatus; uint32_t HcRhPortStatus;
} }
OHCIPort; OHCIPort;
@ -116,38 +123,46 @@ typedef struct _OHCI_Registers
} }
OHCI_Registers; OHCI_Registers;
// enum indicating the current HC state
typedef enum _USB_State
{
USB_Reset = 0x00,
USB_Resume = 0x40,
USB_Operational = 0x80,
USB_Suspend = 0xC0,
}
USB_State;
/* OHCI class representing the state of the HC */ /* OHCI class representing the state of the HC */
class OHCI_State class OHCI
{ {
public: public:
// constructor // constructor
OHCI_State() {} OHCI(USBDevice* UsbObj);
// destructor // destructor
~OHCI_State() {} ~OHCI() {}
// read a register // read a register
uint32_t HC_ReadRegister(xbaddr addr); uint32_t OHCI_ReadRegister(xbaddr Addr);
// write a register // write a register
void HC_WriteRegister(xbaddr addr, uint32_t value); void OHCI_WriteRegister(xbaddr Addr, uint32_t Value);
// reset the HC to the default state // switch the HC to the reset state
void HC_ChangeState(USB_State new_state); void OHCI_StateReset();
private: private:
// all the registers available on the OHCI standard // all the registers available on the OHCI standard
OHCI_Registers HC_Registers; OHCI_Registers Registers;
// end-of-frame timer
TimerObject* pEndOfFrameTimer;
// the duration of a usb frame
uint64_t UsbFrameTime;
// ticks per usb tick
uint64_t TicksPerUsbTick;
// the usb device instance of this HC
USBDevice* UsbInstance;
// usb packet
USBPacket UsbPacket;
// end-of-frame callback wrapper
static void OHCI_FrameBoundaryWrapper(void* pVoid);
// end-of-frame callback function
void OHCI_FrameBoundaryWorker();
// initialize packet struct
void USB_PacketInit(USBPacket* packet);
}; };
extern OHCI_State* g_pHostController1; extern OHCI* g_pHostController1;
extern OHCI_State* g_pHostController2; extern OHCI* g_pHostController2;
#endif #endif

View File

@ -52,13 +52,11 @@ void USBDevice::Init(unsigned int address)
m_VendorId = PCI_VENDOR_ID_NVIDIA; m_VendorId = PCI_VENDOR_ID_NVIDIA;
if (address == USB0_BASE) { if (address == USB0_BASE) {
g_pHostController1 = new OHCI_State; g_pHostController1 = new OHCI(this);
g_pHostController1->HC_ChangeState(USB_Reset);
return; return;
} }
g_pHostController2 = new OHCI_State; g_pHostController2 = new OHCI(this);
g_pHostController2->HC_ChangeState(USB_Reset);
} }
uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size) uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
@ -69,11 +67,11 @@ uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
// Figure out the correct OHCI object and read the register // Figure out the correct OHCI object and read the register
if (addr >= USB1_BASE) { if (addr >= USB1_BASE) {
// USB1 queried // USB1 queried
return g_pHostController2->HC_ReadRegister(addr); return g_pHostController2->OHCI_ReadRegister(addr);
} }
// USB0 queried // USB0 queried
return g_pHostController1->HC_ReadRegister(addr); return g_pHostController1->OHCI_ReadRegister(addr);
} }
void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size)
@ -84,10 +82,19 @@ void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned
// Figure out the correct OHCI object and write the value to the register // Figure out the correct OHCI object and write the value to the register
if (addr >= USB1_BASE) { if (addr >= USB1_BASE) {
// USB1 queried // USB1 queried
g_pHostController2->HC_WriteRegister(addr, value); g_pHostController2->OHCI_WriteRegister(addr, value);
return; return;
} }
// USB0 queried // USB0 queried
g_pHostController1->HC_WriteRegister(addr, value); 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);
} }

View File

@ -39,6 +39,126 @@
#include "..\PCIDevice.h" #include "..\PCIDevice.h"
#include "OHCI.h" #include "OHCI.h"
// This is a linux struct for vectored I/O. See readv() and writev()
typedef struct _IoVec
{
void* Iov_Base;
size_t Iov_Len;
}
IoVec;
typedef struct _IOVector
{
IoVec* IoVec;
int IoVecNumber; // TODO
int AllocNumber; // TODO
size_t Size;
}
IOVector;
// definition of a USB device
typedef struct _USBDev
{
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;
char product_desc[32];
int auto_attach;
int attached;
int32_t state;
uint8_t setup_buf[8];
uint8_t data_buf[4096];
int32_t remote_wakeup;
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];
//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];
}
USBDev;
typedef struct _USBEndpoint
{
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;
}
USBEndpoint;
// Structure used to hold information about an active USB packet
typedef struct _USBPacket
{
// Data fields for use by the driver
int Pid;
uint64_t Id;
USBEndpoint* Endpoint;
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
// Internal use by the USB layer
USBPacketState state;
USBCombinedPacket *combined;
//QTAILQ_ENTRY(USBPacket) queue;
//QTAILQ_ENTRY(USBPacket) combined_entry;
}
USBPacket;
// Struct describing the status of a usb port
typedef struct _USBPort {
USBDev *Dev;
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
//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;
class USBDevice : public PCIDevice { class USBDevice : public PCIDevice {
public: public:
// constructor // constructor
@ -54,6 +174,10 @@ class USBDevice : public PCIDevice {
void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) {} void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) {}
uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size); uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size);
void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, 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);
}; };
#endif #endif