More packet processing stuff (WIP)

This commit is contained in:
ergo720 2018-06-08 00:03:43 +02:00
parent 245bedadc3
commit 412f6ca686
6 changed files with 209 additions and 69 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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);
}
}

View File

@ -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