More packet processing stuff (WIP)
This commit is contained in:
parent
245bedadc3
commit
412f6ca686
|
@ -37,6 +37,7 @@
|
|||
// The intent of this file is to add general functions which are not kernel specific (for those CxbxKrnl.h should be used instead)
|
||||
|
||||
#include "Cxbx.h"
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
// Disable a compiler warning relative to uint64_t -> uint32_t conversions in Muldiv64. This function is taken from
|
||||
|
@ -44,6 +45,7 @@
|
|||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
|
||||
// Compute (a*b)/c with a 96 bit intermediate result
|
||||
uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
||||
{
|
||||
union {
|
||||
|
@ -64,3 +66,25 @@ uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
|||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
void IoVecReset(IOVector* qiov)
|
||||
{
|
||||
assert(qiov->AllocNumber != -1);
|
||||
|
||||
qiov->IoVecNumber = 0;
|
||||
qiov->Size = 0;
|
||||
}
|
||||
|
||||
void IoVecAdd(IOVector* qiov, void* base, size_t len)
|
||||
{
|
||||
assert(qiov->AllocNumber != -1);
|
||||
|
||||
if (qiov->IoVecNumber == qiov->AllocNumber) {
|
||||
qiov->AllocNumber = 2 * qiov->AllocNumber + 1;
|
||||
qiov->IoVecStruct = static_cast<IoVec*>(std::realloc(qiov->IoVecStruct, qiov->AllocNumber * sizeof(IOVector)));
|
||||
}
|
||||
qiov->IoVecStruct[qiov->IoVecNumber].Iov_Base = base;
|
||||
qiov->IoVecStruct[qiov->IoVecNumber].Iov_Len = len;
|
||||
qiov->Size += len;
|
||||
++qiov->IoVecNumber;
|
||||
}
|
||||
|
|
23
src/Cxbx.h
23
src/Cxbx.h
|
@ -35,6 +35,7 @@
|
|||
#define CXBX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define FUNC_EXPORTS __pragma(comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__))
|
||||
|
||||
|
@ -159,7 +160,27 @@ extern volatile bool g_bPrintfOn;
|
|||
#define CxbxSetThreadName(Name)
|
||||
#endif
|
||||
|
||||
// Compute (a*b)/c with a 96 bit intermediate result
|
||||
|
||||
/* This is a linux struct for vectored I/O. See readv() and writev() */
|
||||
typedef struct _IoVec
|
||||
{
|
||||
void* Iov_Base; // Starting address
|
||||
size_t Iov_Len; // Number of bytes to transfer
|
||||
}
|
||||
IoVec;
|
||||
|
||||
typedef struct _IOVector
|
||||
{
|
||||
IoVec* IoVecStruct;
|
||||
int IoVecNumber; // TODO
|
||||
int AllocNumber; // TODO
|
||||
size_t Size;
|
||||
}
|
||||
IOVector;
|
||||
|
||||
inline uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c);
|
||||
|
||||
void IoVecReset(IOVector* qiov);
|
||||
void IoVecAdd(IOVector* qiov, void* base, size_t len);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -639,14 +639,14 @@ int OHCI::OHCI_ServiceTD(OHCI_ED* Ed)
|
|||
#ifdef DEBUG_PACKET
|
||||
DPRINTF("Too many pending packets\n");
|
||||
#endif
|
||||
DbgPrintf("Too many pending packets\n");
|
||||
DbgPrintf("Ohci: too many pending packets\n");
|
||||
return 1;
|
||||
}
|
||||
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);
|
||||
m_UsbDevice->USB_PacketAddBuffer(&m_UsbPacket, m_UsbBuffer, packetlen);
|
||||
m_UsbDevice->USB_HandlePacket(dev, &m_UsbPacket);
|
||||
#ifdef DEBUG_PACKET
|
||||
DPRINTF("status=%d\n", ohci->usb_packet.status);
|
||||
#endif
|
||||
|
@ -905,7 +905,7 @@ void OHCI::OHCI_ChangeState(uint32_t Value)
|
|||
void OHCI::OHCI_PacketInit(USBPacket* packet)
|
||||
{
|
||||
IOVector* vec = &packet->IoVec;
|
||||
vec->IoVec = new IoVec;
|
||||
vec->IoVecStruct = new IoVec;
|
||||
vec->IoVecNumber = 0;
|
||||
vec->AllocNumber = 1;
|
||||
vec->Size = 0;
|
||||
|
@ -917,7 +917,7 @@ uint32_t OHCI::OHCI_ReadRegister(xbaddr Addr)
|
|||
|
||||
if (Addr & 3) {
|
||||
// The standard allows only aligned reads to the registers
|
||||
DbgPrintf("Ohci: Unaligned read. Ignoring.");
|
||||
DbgPrintf("Ohci: Unaligned read. Ignoring.\n");
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
|
@ -1024,7 +1024,7 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value)
|
|||
{
|
||||
if (Addr & 3) {
|
||||
// The standard allows only aligned writes to the registers
|
||||
DbgPrintf("Ohci: Unaligned write. Ignoring.");
|
||||
DbgPrintf("Ohci: Unaligned write. Ignoring.\n");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#ifndef OHCI_H_
|
||||
#define OHCI_H_
|
||||
|
||||
#include "Cxbx.h"
|
||||
#include "USBDevice.h"
|
||||
#include "..\CxbxKrnl\Timer.h"
|
||||
|
||||
|
@ -165,7 +164,7 @@ class OHCI
|
|||
|
||||
private:
|
||||
// pointer to g_USB0 or g_USB1
|
||||
USBDevice* m_UsbDevice;
|
||||
USBDevice* m_UsbDevice = nullptr;
|
||||
// all the registers available on the OHCI standard
|
||||
OHCI_Registers m_Registers;
|
||||
// end-of-frame timer
|
||||
|
@ -177,9 +176,9 @@ class OHCI
|
|||
// ticks per usb tick
|
||||
uint64_t m_TicksPerUsbTick;
|
||||
// pending usb packet to process
|
||||
USBPacket m_UsbPacket;
|
||||
USBPacket m_UsbPacket = {};
|
||||
// temporary buffer that holds the user data to transfer in a packet
|
||||
uint8_t m_UsbBuffer[8192];
|
||||
uint8_t m_UsbBuffer[8192] = {};
|
||||
// ergo720: I believe it's the value of HcControl in the last frame
|
||||
uint32_t old_ctl;
|
||||
// irq number
|
||||
|
|
|
@ -36,6 +36,13 @@
|
|||
|
||||
#include "USBDevice.h"
|
||||
#include "OHCI.h"
|
||||
#include "CxbxKrnl\EmuKrnl.h" // For EmuWarning
|
||||
|
||||
#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
|
||||
|
||||
|
||||
void USBDevice::Init(unsigned int address)
|
||||
|
@ -52,11 +59,11 @@ void USBDevice::Init(unsigned int address)
|
|||
m_VendorId = PCI_VENDOR_ID_NVIDIA;
|
||||
|
||||
if (address == USB0_BASE) {
|
||||
m_pHostController1 = new OHCI(1, this);
|
||||
m_HostController = new OHCI(1, this);
|
||||
return;
|
||||
}
|
||||
|
||||
m_pHostController2 = new OHCI(9, this);
|
||||
m_HostController = new OHCI(9, this);
|
||||
}
|
||||
|
||||
uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
|
||||
|
@ -64,14 +71,8 @@ uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)
|
|||
// barIndex must be zero since we created the USB devices with a zero index in Init()
|
||||
assert(barIndex == 0);
|
||||
|
||||
// Figure out the correct OHCI object and read the register
|
||||
if (addr < USB1_BASE) {
|
||||
// USB0 queried
|
||||
return m_pHostController1->OHCI_ReadRegister(addr);
|
||||
}
|
||||
|
||||
// USB1 queried
|
||||
return m_pHostController2->OHCI_ReadRegister(addr);
|
||||
// read the register of the corresponding HC
|
||||
return m_HostController->OHCI_ReadRegister(addr);
|
||||
}
|
||||
|
||||
void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size)
|
||||
|
@ -79,15 +80,8 @@ void USBDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned
|
|||
// barIndex must be zero since we created the USB devices with a zero index in Init()
|
||||
assert(barIndex == 0);
|
||||
|
||||
// Figure out the correct OHCI object and write the value to the register
|
||||
if (addr < USB1_BASE) {
|
||||
// USB0 queried
|
||||
m_pHostController1->OHCI_WriteRegister(addr, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// USB1 queried
|
||||
m_pHostController2->OHCI_WriteRegister(addr, value);
|
||||
// write the register of the corresponding HC
|
||||
m_HostController->OHCI_WriteRegister(addr, value);
|
||||
}
|
||||
|
||||
void USBDevice::USB_RegisterPort(USBPort* Port, int Index, int SpeedMask)
|
||||
|
@ -195,8 +189,8 @@ USBEndpoint* USBDevice::USB_GetEP(XboxDevice* Dev, int Pid, int Ep)
|
|||
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);
|
||||
assert(!USB_IsPacketInflight(p));
|
||||
assert(p->IoVec.IoVecStruct != nullptr);
|
||||
p->Id = Id;
|
||||
p->Pid = Pid;
|
||||
p->Endpoint = Ep;
|
||||
|
@ -206,7 +200,109 @@ void USBDevice::USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, unsigned
|
|||
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);
|
||||
p->Combined = nullptr;
|
||||
IoVecReset(&p->IoVec);
|
||||
p->State = USB_PACKET_SETUP;
|
||||
}
|
||||
|
||||
bool USBDevice::USB_IsPacketInflight(USBPacket* p)
|
||||
{
|
||||
return (p->State == USB_PACKET_QUEUED || p->State == USB_PACKET_ASYNC);
|
||||
}
|
||||
|
||||
void USBDevice::USB_PacketAddBuffer(USBPacket* p, void* ptr, size_t len)
|
||||
{
|
||||
IoVecAdd(&p->IoVec, ptr, len);
|
||||
}
|
||||
|
||||
void USBDevice::USB_HandlePacket(XboxDevice* dev, USBPacket* p)
|
||||
{
|
||||
if (dev == nullptr) {
|
||||
p->Status = USB_RET_NODEV;
|
||||
return;
|
||||
}
|
||||
assert(dev == p->Endpoint->Dev);
|
||||
assert(dev->State == USB_STATE_DEFAULT);
|
||||
USB_PacketCheckState(p, USB_PACKET_SETUP);
|
||||
assert(p->Endpoint != nullptr);
|
||||
|
||||
// Submitting a new packet clears halt
|
||||
if (p->Endpoint->Halted) {
|
||||
assert(QTAILQ_EMPTY(&p->Endpoint->Queue));
|
||||
p->Endpoint->Halted = false;
|
||||
}
|
||||
|
||||
if (QTAILQ_EMPTY(&p->Endpoint->Queue) || p->Endpoint->Pipeline || p->Stream) {
|
||||
USB_ProcessOne(p);
|
||||
if (p->Status == USB_RET_ASYNC) {
|
||||
// hcd drivers cannot handle async for isoc
|
||||
assert(p->Endpoint->Type != USB_ENDPOINT_XFER_ISOC);
|
||||
// using async for interrupt packets breaks migration
|
||||
assert(p->Endpoint->Type != USB_ENDPOINT_XFER_INT ||
|
||||
(dev->flags & (1 << USB_DEV_FLAG_IS_HOST)));
|
||||
p->State = USB_PACKET_ASYNC;
|
||||
QTAILQ_INSERT_TAIL(&p->Endpoint->Queue, p, Queue);
|
||||
}
|
||||
else if (p->Status == USB_RET_ADD_TO_QUEUE) {
|
||||
usb_queue_one(p);
|
||||
}
|
||||
else {
|
||||
// When pipelining is enabled usb-devices must always return async,
|
||||
// otherwise packets can complete out of order!
|
||||
assert(p->stream || !p->Endpoint->pipeline ||
|
||||
QTAILQ_EMPTY(&p->Endpoint->Queue));
|
||||
if (p->Status != USB_RET_NAK) {
|
||||
p->State = USB_PACKET_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
usb_queue_one(p);
|
||||
}
|
||||
}
|
||||
|
||||
void USBDevice::USB_PacketCheckState(USBPacket* p, USBPacketState expected)
|
||||
{
|
||||
if (p->State == expected) {
|
||||
return;
|
||||
}
|
||||
|
||||
EmuWarning("Usb: packet state check failed!");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void USBDevice::USB_ProcessOne(USBPacket* p)
|
||||
{
|
||||
XboxDevice* 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,
|
||||
// or USB_RET_ASYNC from going through usb_queue_one().
|
||||
p->Status = USB_RET_SUCCESS;
|
||||
|
||||
if (p->Endpoint->Num == 0) {
|
||||
// Info: All devices must support endpoint zero. This is the endpoint which receives all of the devices control
|
||||
// and status requests during enumeration and throughout the duration while the device is operational on the bus
|
||||
if (p->Parameter) {
|
||||
do_parameter(dev, p);
|
||||
return;
|
||||
}
|
||||
switch (p->Pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
do_token_setup(dev, p);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
do_token_in(dev, p);
|
||||
break;
|
||||
case USB_TOKEN_OUT:
|
||||
do_token_out(dev, p);
|
||||
break;
|
||||
default:
|
||||
p->Status = USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// data pipe
|
||||
usb_device_handle_data(dev, p);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
#ifndef USBDEVICE_H_
|
||||
#define USBDEVICE_H_
|
||||
|
||||
#include "Cxbx.h"
|
||||
#include "..\PCIDevice.h"
|
||||
#include "..\devices\video\queue.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define USB_MAX_ENDPOINTS 15
|
||||
#define USB_MAX_INTERFACES 16
|
||||
|
@ -57,37 +57,20 @@ typedef enum USBPacketState {
|
|||
}
|
||||
USBPacketState;
|
||||
|
||||
/* 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;
|
||||
|
||||
typedef struct _USBPacket USBPacket;
|
||||
typedef struct _XboxDevice XboxDevice;
|
||||
|
||||
/* USB endpoint */
|
||||
typedef struct _USBEndpoint
|
||||
{
|
||||
uint8_t nr;
|
||||
uint8_t Num; // endpoint number
|
||||
uint8_t pid;
|
||||
uint8_t type;
|
||||
uint8_t Type; // the type of this endpoint
|
||||
uint8_t ifnum;
|
||||
int max_packet_size;
|
||||
bool pipeline;
|
||||
bool halted;
|
||||
XboxDevice* Dev; // device this endpoint belongs to
|
||||
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;
|
||||
|
@ -187,10 +170,16 @@ typedef struct USBDeviceClass
|
|||
}
|
||||
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
|
||||
{
|
||||
// Data fields for use by the driver
|
||||
int Pid; // Packet ID
|
||||
uint32_t Id; // Paddr of the TD for this packet
|
||||
USBEndpoint* Endpoint; // endpoint this packet is transferred to
|
||||
|
@ -203,22 +192,24 @@ struct _USBPacket
|
|||
int ActualLength; // Number of bytes actually transferred
|
||||
// Internal use by the USB layer
|
||||
USBPacketState State;
|
||||
//USBCombinedPacket *Combined;
|
||||
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;
|
||||
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 device class
|
||||
class OHCI;
|
||||
|
||||
class USBDevice : public PCIDevice {
|
||||
public:
|
||||
|
@ -238,9 +229,8 @@ class USBDevice : public PCIDevice {
|
|||
|
||||
|
||||
// USBDevice-specific functions/variables
|
||||
// pointers to the two USB host controllers available on the Xbox
|
||||
OHCI* m_pHostController1 = nullptr;
|
||||
OHCI* m_pHostController2 = nullptr;
|
||||
// pointer to the host controller this device refears to
|
||||
OHCI* m_HostController = nullptr;
|
||||
|
||||
// register a port with the HC
|
||||
void USB_RegisterPort(USBPort* Port, int Index, int SpeedMask);
|
||||
|
@ -269,6 +259,16 @@ class USBDevice : public PCIDevice {
|
|||
// setup a packet for transfer
|
||||
void USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, unsigned int Stream,
|
||||
uint64_t Id, bool ShortNotOK, bool IntReq);
|
||||
// check if the state of the packet is queued or async
|
||||
bool USB_IsPacketInflight(USBPacket* p);
|
||||
// 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);
|
||||
// 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);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue