USB/HC initialization stuff
This commit is contained in:
parent
6aafb25dee
commit
7f0bea988c
|
@ -1239,7 +1239,9 @@ __declspec(noreturn) void CxbxKrnlInit
|
|||
|
||||
// for unicode conversions
|
||||
setlocale(LC_ALL, "English");
|
||||
// Initialize time-related variables for the kernel and the timers
|
||||
CxbxInitPerformanceCounters();
|
||||
Timer_Init();
|
||||
#ifdef _DEBUG
|
||||
// CxbxPopupMessage("Attach a Debugger");
|
||||
// Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
// Vector storing all the timers created
|
||||
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;
|
||||
|
||||
|
||||
|
@ -161,7 +161,7 @@ void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
|
|||
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()
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
|
|
|
@ -54,8 +54,8 @@ TimerObject;
|
|||
|
||||
|
||||
/* Timer exported functions */
|
||||
TimerObject* Timer_Create(pTimerCB callback, void* arg, unsigned int Factor);
|
||||
void Timer_Start(TimerObject* timer, uint64_t Expire_MS);
|
||||
TimerObject* Timer_Create(pTimerCB Callback, void* Arg, unsigned int Factor);
|
||||
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS);
|
||||
void Timer_Exit(TimerObject* Timer);
|
||||
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
|
||||
void Timer_Init();
|
||||
|
|
|
@ -37,12 +37,46 @@
|
|||
#include "OHCI.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
|
||||
OHCI_State* g_pHostController1 = nullptr;
|
||||
OHCI_State* g_pHostController2 = nullptr;
|
||||
OHCI* g_pHostController1 = 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
|
||||
// reset or cold boot
|
||||
|
@ -50,39 +84,34 @@ void OHCI_State::HC_ChangeState(USB_State new_state)
|
|||
// TODO: stop all the list processing here
|
||||
|
||||
// Reset all registers
|
||||
if (new_state == USB_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
|
||||
// doesn't do POST, I will clear it.
|
||||
HC_Registers.HcControl = 0;
|
||||
}
|
||||
else {
|
||||
HC_Registers.HcControl &= (OHCI_CTL_IR | OHCI_CTL_RWC);
|
||||
}
|
||||
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
|
||||
// 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
|
||||
// doesn't do POST, I will clear it.
|
||||
Registers.HcControl = 0;
|
||||
Registers.HcControl &= ~OHCI_CTL_HCFS;
|
||||
Registers.HcControl |= Reset;
|
||||
Registers.HcCommandStatus = 0;
|
||||
Registers.HcInterruptStatus = 0;
|
||||
Registers.HcInterruptEnable = OHCI_INTR_MASTER_INTERRUPT_ENABLED; // enable interrupts
|
||||
|
||||
HC_Registers.HcHCCA = 0;
|
||||
HC_Registers.HcPeriodCurrentED = 0;
|
||||
HC_Registers.HcControlHeadED = HC_Registers.HcControlCurrentED = 0;
|
||||
HC_Registers.HcBulkHeadED = HC_Registers.HcBulkCurrentED = 0;
|
||||
HC_Registers.HcDoneHead = 0;
|
||||
Registers.HcHCCA = 0;
|
||||
Registers.HcPeriodCurrentED = 0;
|
||||
Registers.HcControlHeadED = Registers.HcControlCurrentED = 0;
|
||||
Registers.HcBulkHeadED = Registers.HcBulkCurrentED = 0;
|
||||
Registers.HcDoneHead = 0;
|
||||
|
||||
HC_Registers.HcFmInterval = 0;
|
||||
HC_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)
|
||||
HC_Registers.HcFmRemaining = 0;
|
||||
HC_Registers.HcFmNumber = 0;
|
||||
HC_Registers.HcPeriodicStart = 0;
|
||||
Registers.HcFmInterval = 0;
|
||||
Registers.HcFmInterval |= (0x2778 << 16); // TBD according to the standard, using what XQEMU sets (FSLargestDataPacket)
|
||||
Registers.HcFmInterval |= 0x2EDF; // bit-time of a frame. 1 frame = 1 ms (FrameInterval)
|
||||
Registers.HcFmRemaining = 0;
|
||||
Registers.HcFmNumber = 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
|
||||
HC_Registers.HcRhDescriptorB = 0; // The attached devices are removable and use PowerSwitchingMode to control the power on the ports
|
||||
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.HcRhDescriptorB = 0; // The attached devices are removable and use PowerSwitchingMode to control the power on the ports
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
OHCIPort* Port = &HC_Registers.RhPort[i];
|
||||
OHCIPort* Port = &Registers.RhPort[i];
|
||||
Port->HcRhPortStatus = 0;
|
||||
//if (Port->port.device && Port->port.device->attached) {
|
||||
//usb_port_reset(&Port->port);
|
||||
|
@ -90,4 +119,13 @@ void OHCI_State::HC_ChangeState(USB_State new_state)
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "Cxbx.h"
|
||||
#include "USBDevice.h"
|
||||
#include "..\CxbxKrnl\Timer.h"
|
||||
|
||||
|
||||
// Abbreviations used:
|
||||
|
@ -67,16 +69,21 @@
|
|||
#define OHCI_RHA_OCPM (1<<11) // OverCurrentProtectionMode
|
||||
#define OHCI_RHA_NOCP (1<<12) // NoOverCurrentProtection
|
||||
|
||||
// Struct describing the status of a usb port
|
||||
struct USBPort {
|
||||
//USBDevice* device;
|
||||
//TODO
|
||||
};
|
||||
|
||||
// enum indicating the current HC state
|
||||
typedef enum _OHCI_State
|
||||
{
|
||||
Reset = 0x00,
|
||||
Resume = 0x40,
|
||||
Operational = 0x80,
|
||||
Suspend = 0xC0,
|
||||
}
|
||||
OHCI_State;
|
||||
|
||||
// Small struct used to hold the HcRhPortStatus register and the usb port status
|
||||
typedef struct _OHCIPort
|
||||
{
|
||||
USBPort port;
|
||||
USBPort Port;
|
||||
uint32_t HcRhPortStatus;
|
||||
}
|
||||
OHCIPort;
|
||||
|
@ -116,38 +123,46 @@ typedef struct _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 */
|
||||
class OHCI_State
|
||||
class OHCI
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
OHCI_State() {}
|
||||
OHCI(USBDevice* UsbObj);
|
||||
// destructor
|
||||
~OHCI_State() {}
|
||||
~OHCI() {}
|
||||
// read a register
|
||||
uint32_t HC_ReadRegister(xbaddr addr);
|
||||
uint32_t OHCI_ReadRegister(xbaddr Addr);
|
||||
// write a register
|
||||
void HC_WriteRegister(xbaddr addr, uint32_t value);
|
||||
// reset the HC to the default state
|
||||
void HC_ChangeState(USB_State new_state);
|
||||
void OHCI_WriteRegister(xbaddr Addr, uint32_t Value);
|
||||
// switch the HC to the reset state
|
||||
void OHCI_StateReset();
|
||||
|
||||
|
||||
private:
|
||||
// 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_State* g_pHostController2;
|
||||
extern OHCI* g_pHostController1;
|
||||
extern OHCI* g_pHostController2;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -52,13 +52,11 @@ void USBDevice::Init(unsigned int address)
|
|||
m_VendorId = PCI_VENDOR_ID_NVIDIA;
|
||||
|
||||
if (address == USB0_BASE) {
|
||||
g_pHostController1 = new OHCI_State;
|
||||
g_pHostController1->HC_ChangeState(USB_Reset);
|
||||
g_pHostController1 = new OHCI(this);
|
||||
return;
|
||||
}
|
||||
|
||||
g_pHostController2 = new OHCI_State;
|
||||
g_pHostController2->HC_ChangeState(USB_Reset);
|
||||
g_pHostController2 = new OHCI(this);
|
||||
}
|
||||
|
||||
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
|
||||
if (addr >= USB1_BASE) {
|
||||
// USB1 queried
|
||||
return g_pHostController2->HC_ReadRegister(addr);
|
||||
return g_pHostController2->OHCI_ReadRegister(addr);
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
@ -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
|
||||
if (addr >= USB1_BASE) {
|
||||
// USB1 queried
|
||||
g_pHostController2->HC_WriteRegister(addr, value);
|
||||
g_pHostController2->OHCI_WriteRegister(addr, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,126 @@
|
|||
#include "..\PCIDevice.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 {
|
||||
public:
|
||||
// constructor
|
||||
|
@ -54,6 +174,10 @@ class USBDevice : public PCIDevice {
|
|||
void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) {}
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue