typedef void (*tUSBHostCb)(void *DataPtr, void *Data, size_t Length);
typedef void *(*tUSBInitInt)(void *Ptr, int Endpt, int bOutbound, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len);
-typedef void *(*tUSBInit)(void *Ptr, int Endpt);
+typedef void *(*tUSBInit)(void *Ptr, int Endpt, size_t MaxPacketSize);
typedef void *(*tUSBDataOp)(void *Dest, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
typedef void *(*tUSBControlOp)(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData,
host->RootHubDev.ParentHub = NULL;
host->RootHubDev.Host = host;
host->RootHubDev.Address = 0;
- host->RootHubDev.EndpointHandles[0] = HostDef->InitControl(ControllerPtr, 0);
+ host->RootHubDev.EndpointHandles[0] = HostDef->InitControl(ControllerPtr, 0, 64);
// host->RootHubIf.Next = NULL;
host->RootHubIf.Dev = &host->RootHubDev;
USB_int_SendSetupSetAddress(dev->Host, dev->Address);
LOG("Assigned address %i", dev->Address);
- dev->EndpointHandles[0] = dev->Host->HostDef->InitControl(dev->Host->Ptr, dev->Address << 4);
+ dev->EndpointHandles[0] = dev->Host->HostDef->InitControl(dev->Host->Ptr, dev->Address << 4, 64);
for( int i = 1; i < 16; i ++ )
dev->EndpointHandles[i] = 0;
LOG(" .MaxPacketSize = %i", LittleEndian16(endpt->MaxPacketSize));
LOG(" .PollingInterval = %i", endpt->PollingInterval);
LOG("}");
-
+
+ // Make sure things don't break
+ if( !((endpt->Address & 0x7F) < 15) ) {
+ Log_Error("USB", "Endpoint number %i>16", endpt->Address & 0x7F);
+ k --;
+ continue ;
+ }
+
dev_if->Endpoints[k].Next = NULL;
dev_if->Endpoints[k].Interface = dev_if;
dev_if->Endpoints[k].EndpointIdx = k;
dev_if->Endpoints[k].InputData = NULL;
// TODO: Register endpoint early
+ int ep_num = endpt->Address & 15;
+ if( dev->EndpointHandles[ep_num] ) {
+ Log_Notice("USB", "Device %p:%i ep %i reused", dev->Host->Ptr, dev->Address, ep_num);
+ }
+ else {
+ int addr = dev->Address*16+ep_num;
+ int mps = dev_if->Endpoints[k].MaxPacketSize;
+ void *handle = NULL;
+ switch( endpt->Attributes & 3)
+ {
+ case 0: // Control
+ handle = dev->Host->HostDef->InitControl(dev->Host->Ptr, addr, mps);
+ break;
+ case 1: // Isochronous
+ // handle = dev->Host->HostDef->InitIsoch(dev->Host->Ptr, addr, mps);
+ break;
+ case 2: // Bulk
+ handle = dev->Host->HostDef->InitBulk(dev->Host->Ptr, addr, mps);
+ break;
+ case 3: // Interrupt
+ // handle = dev->Host->HostDef->InitInterrupt(dev->Host->Ptr, addr, ...);
+ break;
+ }
+ dev->EndpointHandles[ep_num] = handle;
+ }
}
// Initialise driver
dest_hdl = Dev->Dev->EndpointHandles[ep->EndpointNum];
LOG("dest_hdl = %p", dest_hdl);
if( !dest_hdl ) {
- dest_hdl = host->HostDef->InitControl(host->Ptr, Dev->Dev->Address*16 + ep->EndpointNum);
- Dev->Dev->EndpointHandles[ep->EndpointNum] = dest_hdl;
- LOG("dest_hdl = %p (allocated)", dest_hdl);
+ Log_Notice("USB", "_SendData on uninitialised enpoint (%p#%i)", Dev->Dev, ep->EndpointNum);
+ LEAVE('-');
+ return;
}
Threads_ClearEvent(THREAD_EVENT_SHORTWAIT);
dest_hdl = Dev->Dev->EndpointHandles[ep->EndpointNum];
LOG("dest_hdl = %p", dest_hdl);
if( !dest_hdl ) {
- dest_hdl = host->HostDef->InitControl(host->Ptr, Dev->Dev->Address*16 + ep->EndpointNum);
- Dev->Dev->EndpointHandles[ep->EndpointNum] = dest_hdl;
- LOG("dest_hdl = %p (allocated)", dest_hdl);
+ Log_Notice("USB", "_RecvData on uninitialised enpoint (%p#%i)", Dev->Dev, ep->EndpointNum);
+ LEAVE('-');
+ return;
}
Threads_ClearEvent(THREAD_EVENT_SHORTWAIT);
host = Dev->Dev->Host;
dest_hdl = Dev->Dev->EndpointHandles[op->Endpt->EndpointNum];
if( !dest_hdl ) {
- dest_hdl = host->HostDef->InitControl(host->Ptr, Dev->Dev->Address*16 + op->Endpt->EndpointNum);
- Dev->Dev->EndpointHandles[op->Endpt->EndpointNum] = dest_hdl;
- LOG("dest_hdl = %p (allocated)", dest_hdl);
+ Log_Notice("USB", "_SendData on uninitialised enpoint (%p#%i)", Dev->Dev, op->Endpt->EndpointNum);
+ LEAVE('-');
+ return;
}
LOG("IN from %p %i:%i", host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum);
dest_hdl = Device->EndpointHandles[EndPt];
if( !dest_hdl ) {
- dest_hdl = Host->HostDef->InitControl(Host->Ptr, Device->Address*16 + EndPt);
- Device->EndpointHandles[EndPt] = dest_hdl;
+ LEAVE('n');
+ return NULL;
}
req.ReqType = Type;
dest_hdl = Dev->EndpointHandles[Endpoint];
if( !dest_hdl ) {
- dest_hdl = Dev->Host->HostDef->InitControl(Dev->Host->Ptr, Dev->Address*16 + Endpoint);
- Dev->EndpointHandles[Endpoint] = dest_hdl;
+ return -1;
}
ENTER("pDev xEndpoint iType iIndex iLength pDest",
#include <usb_host.h>
#include "ehci.h"
#include <drv_pci.h>
+#include <limits.h>
// === CONSTANTS ===
#define EHCI_MAX_CONTROLLERS 4
void EHCI_InterruptHandler(int IRQ, void *Ptr);
// -- API ---
void *EHCI_InitInterrupt(void *Ptr, int Endpoint, int bInput, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void *EHCI_InitIsoch (void *Ptr, int Endpoint);
-void *EHCI_InitControl(void *Ptr, int Endpoint);
-void *EHCI_InitBulk (void *Ptr, int Endpoint);
+void *EHCI_InitIsoch (void *Ptr, int Endpoint, size_t MaxPacketSize);
+void *EHCI_InitControl(void *Ptr, int Endpoint, size_t MaxPacketSize);
+void *EHCI_InitBulk (void *Ptr, int Endpoint, size_t MaxPacketSize);
void EHCI_RemEndpoint(void *Ptr, void *Handle);
void *EHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
int isOutbound,
void *InData, size_t InLength
);
void *EHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length);
+void EHCI_FreeOp(void *Ptr, void *Handle);
+// --- Internals ---
+tEHCI_qTD *EHCI_int_AllocateTD(tEHCI_Controller *Cont, int PID, void *Data, size_t Length, tUSBHostCb Cb, void *CbData);
+void EHCI_int_DeallocateTD(tEHCI_Controller *Cont, tEHCI_qTD *TD);
+void EHCI_int_AppendTD(tEHCI_QH *QH, tEHCI_qTD *TD);
+tEHCI_QH *EHCI_int_AllocateQH(tEHCI_Controller *Cont, int Endpoint, size_t MaxPacketSize);
+void EHCI_int_DeallocateQH(tEHCI_Controller *Cont, tEHCI_QH *QH);
// === GLOBALS ===
MODULE_DEFINE(0, VERSION, USB_EHCI, EHCI_Initialise, NULL, "USB_Core", NULL);
.SendIsoch = NULL,
.SendControl = EHCI_SendControl,
.SendBulk = EHCI_SendBulk,
- .FreeOp = NULL
+ .FreeOp = EHCI_FreeOp
};
// === CODE ===
// - Allocate periodic queue
cont->PeriodicQueue = (void*)MM_AllocDMA(1, 32, NULL);
// TODO: Error check
+ // > Populate queue
// -- Bind IRQ --
IRQ_AddHandler(InterruptNum, EHCI_InterruptHandler, cont);
// --------------------------------------------------------------------
// USB API
// --------------------------------------------------------------------
-void *EHCI_InitInterrupt(void *Ptr, int Endpoint, int bInput, int Period,
+void *EHCI_InitInterrupt(void *Ptr, int Endpoint, int bOutbound, int Period,
tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
{
+ tEHCI_Controller *Cont = Ptr;
+ int pow2period, period_pow;
+ if( Endpoint >= 256*16 )
+ return NULL;
+ if( Period <= 0 )
+ return NULL;
+ if( Period > 256 )
+ Period = 256;
+
+ // Round the period to the closest power of two
+ pow2period = 1;
+ period_pow = 0;
+ // - Find the first power above the period
+ while( pow2period < Period )
+ {
+ pow2period *= 2;
+ period_pow ++;
+ }
+ // - Check which is closest
+ if( Period - pow2period / 2 > pow2period - Period )
+ Period = pow2period;
+ else {
+ Period = pow2period/2;
+ period_pow --;
+ }
+
+ // Allocate a QH
+ tEHCI_QH *qh = EHCI_int_AllocateQH(Cont, Endpoint, Length);
+ qh->Impl.IntPeriodPow = period_pow;
+
+ // Choose an interrupt slot to use
+ int minslot = 0, minslot_load = INT_MAX;
+ for( int slot = 0; slot < Period; slot ++ )
+ {
+ int load = 0;
+ for( int i = 0; i < PERIODIC_SIZE; i += Period )
+ load += Cont->InterruptLoad[i+slot];
+ if( load == 0 ) break;
+ if( load < minslot_load ) {
+ minslot = slot;
+ minslot_load = load;
+ }
+ }
+ // Increase loading on the selected slot
+ for( int i = 0; i < PERIODIC_SIZE; i += Period )
+ Cont->InterruptLoad[i+minslot] += Length;
+ qh->Impl.IntOfs = minslot;
+
+ // Allocate TD for the data
+ tEHCI_qTD *td = EHCI_int_AllocateTD(Cont, (bOutbound ? PID_OUT : PID_IN), Buf, Length, Cb, CbData);
+ EHCI_int_AppendTD(qh, td);
+
+ // Insert into the periodic list
+
+ return qh;
}
-void *EHCI_InitIsoch(void *Ptr, int Endpoint)
+void *EHCI_InitIsoch(void *Ptr, int Endpoint, size_t MaxPacketSize)
{
- return NULL;
+ return (void*)(Endpoint + 1);
}
-void *EHCI_InitControl(void *Ptr, int Endpoint)
+void *EHCI_InitControl(void *Ptr, int Endpoint, size_t MaxPacketSize)
{
- return NULL;
+ tEHCI_Controller *Cont = Ptr;
+
+ // Allocate a QH
+ tEHCI_QH *qh = EHCI_int_AllocateQH(Cont, Endpoint, MaxPacketSize);
+
+ // Append to async list
+ if( Cont->LastAsyncHead ) {
+ Cont->LastAsyncHead->HLink = MM_GetPhysAddr(qh)|2;
+ Cont->LastAsyncHead->Impl.Next = qh;
+ }
+ else
+ Cont->OpRegs->AsyncListAddr = MM_GetPhysAddr(qh)|2;
+ Cont->LastAsyncHead = qh;
+
+ return qh;
}
-void *EHCI_InitBulk(void *Ptr, int Endpoint)
+void *EHCI_InitBulk(void *Ptr, int Endpoint, size_t MaxPacketSize)
{
- return NULL;
+ return EHCI_InitControl(Ptr, Endpoint, MaxPacketSize);
}
-void EHCI_RemEndpoint(void *Ptr, void *Handle);
-void *EHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
+void EHCI_RemEndpoint(void *Ptr, void *Handle)
+{
+ if( Handle == NULL )
+ return ;
+ else if( (tVAddr)Handle <= 256*16 )
+ return ; // Isoch
+ else {
+ // Remove QH from list
+ // - If it's a polling endpoint, need to remove from all periodic lists
+
+ // Deallocate QH
+ EHCI_int_DeallocateQH(Ptr, Handle);
+ }
+}
+
+void *EHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
int isOutbound,
const void *SetupData, size_t SetupLength,
const void *OutData, size_t OutLength,
void *InData, size_t InLength
- );
-void *EHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length);
+ )
+{
+ tEHCI_Controller *Cont = Ptr;
+ tEHCI_qTD *td_setup, *td_data, *td_status;
+
+ // Sanity checks
+ if( (tVAddr)Dest <= 256*16 )
+ return NULL;
+
+ // Check size of SETUP and status
+
+ // Allocate TDs
+ td_setup = EHCI_int_AllocateTD(Cont, PID_SETUP, (void*)SetupData, SetupLength, NULL, NULL);
+ if( isOutbound )
+ {
+ td_data = EHCI_int_AllocateTD(Cont, PID_OUT, (void*)OutData, OutLength, NULL, NULL);
+ td_status = EHCI_int_AllocateTD(Cont, PID_IN, InData, InLength, Cb, CbData);
+ }
+ else
+ {
+ td_data = EHCI_int_AllocateTD(Cont, PID_IN, InData, InLength, NULL, NULL);
+ td_status = EHCI_int_AllocateTD(Cont, PID_OUT, (void*)OutData, OutLength, Cb, CbData);
+ }
+
+ // Append TDs
+ EHCI_int_AppendTD(Dest, td_setup);
+ EHCI_int_AppendTD(Dest, td_data);
+ EHCI_int_AppendTD(Dest, td_status);
+
+ return td_status;
+}
+
+void *EHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length)
+{
+ tEHCI_Controller *Cont = Ptr;
+
+ // Sanity check the pointer
+ // - Can't be NULL or an isoch
+ if( (tVAddr)Dest <= 256*16 )
+ return NULL;
+
+ // Allocate single TD
+ tEHCI_qTD *td = EHCI_int_AllocateTD(Cont, (Dir ? PID_OUT : PID_IN), Data, Length, Cb, CbData);
+ EHCI_int_AppendTD(Dest, td);
+ return td;
+}
+
+void EHCI_FreeOp(void *Ptr, void *Handle)
+{
+ tEHCI_Controller *Cont = Ptr;
+
+ EHCI_int_DeallocateTD(Cont, Handle);
+}
+
+// --------------------------------------------------------------------
+// Internals
+// --------------------------------------------------------------------
+tEHCI_qTD *EHCI_int_AllocateTD(tEHCI_Controller *Cont, int PID, void *Data, size_t Length, tUSBHostCb Cb, void *CbData)
+{
+ return NULL;
+}
+
+void EHCI_int_DeallocateTD(tEHCI_Controller *Cont, tEHCI_qTD *TD)
+{
+}
+
+void EHCI_int_AppendTD(tEHCI_QH *QH, tEHCI_qTD *TD)
+{
+}
+
+tEHCI_QH *EHCI_int_AllocateQH(tEHCI_Controller *Cont, int Endpoint, size_t MaxPacketSize)
+{
+ return NULL;
+}
+
+void EHCI_int_DeallocateQH(tEHCI_Controller *Cont, tEHCI_QH *QH)
+{
+}
#ifndef _EHCI_H_
#define _EHCI_H_
+#define PERIODIC_SIZE 1024
+
typedef struct sEHCI_CapRegs tEHCI_CapRegs;
typedef struct sEHCI_OpRegs tEHCI_OpRegs;
typedef struct sEHCI_iTD tEHCI_iTD;
#define USBINTR_HostSystemError 0x0010
#define USBINTR_AsyncAdvance 0x0020
+// Isochronous (High-Speed) Transfer Descriptor
struct sEHCI_iTD
{
Uint32 Link;
Uint32 BufferPointers[8]; // Page aligned, low 12 bits are overloaded
};
+// Split Transaction Isochronous Transfer Descriptor
struct sEHCI_siTD
{
Uint32 Link;
Uint32 BackLink;
};
+// Queue Element Transfer Descriptor
struct sEHCI_qTD
{
Uint32 Link;
Uint32 Link2; // Used when there's a short packet
Uint32 Token;
Uint32 Pages[5]; //First has offset in low 12 bits
-};
+
+ // Internals (32 bytes = 4x 64-bit pointers)
+ tUSBHostCb *Callback;
+ void *CallbackData;
+ tEHCI_qTD *Next;
+} __attribute__((aligned(32)));
+// sizeof = 64
+// Queue Head
struct sEHCI_QH
{
Uint32 HLink; // Horizontal link
Uint32 EndpointExt;
Uint32 CurrentTD;
tEHCI_qTD Overlay;
-};
+ struct {
+ Uint8 IntOfs;
+ Uint8 IntPeriodPow;
+ tEHCI_QH *Next;
+ } Impl;
+} __attribute__((aligned(32)));
+// sizeof = 48 (64)
+
+#define PID_OUT 0
+#define PID_IN 1
+#define PID_SETUP 2
struct sEHCI_Controller
{
tEHCI_CapRegs *CapRegs;
tEHCI_OpRegs *OpRegs;
+ int InterruptLoad[PERIODIC_SIZE];
+ tEHCI_QH *LastAsyncHead;
+
Uint32 *PeriodicQueue;
+ tEHCI_QH PeriodicQueueV[PERIODIC_SIZE];
+
+ tEHCI_QH *QHPools[(256*16)*sizeof(tEHCI_QH)/PAGE_SIZE]; // [PAGE_SIZE/64]
+ tEHCI_qTD *TDPool[PAGE_SIZE/sizeof(tEHCI_qTD)];
};
#endif
tUHCI_TD *UHCI_int_CreateTD(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
// --- API
void *UHCI_InitInterrupt(void *Ptr, int Endpt, int bOutbound, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len);
-void *UHCI_InitIsoch(void *Ptr, int Endpt);
-void *UHCI_InitControl(void *Ptr, int Endpt);
-void *UHCI_InitBulk(void *Ptr, int Endpt);
+void *UHCI_InitIsoch(void *Ptr, int Endpt, size_t MaxPacketSize);
+void *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize);
+void *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize);
void UHCI_RemoveEndpoint(void *Ptr, void *EndptHandle);
void *UHCI_SendIsoch(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length, int When);
void *UHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
return td;
}
-void *UHCI_InitControl(void *Ptr, int Endpt)
+void *UHCI_int_InitEndpt(tUHCI_Controller *Cont, int Type, int Endpt, size_t MaxPacketSize)
{
- // TODO: Bitmap of tgl values
+ if( Endpt >= 256*16 )
+ return NULL;
+
+ if( MaxPacketSize > MAX_PACKET_SIZE) {
+ Log_Warning("UHCI", "MaxPacketSize for %x greater than controller max (%i > %i)",
+ Endpt, MaxPacketSize, MAX_PACKET_SIZE);
+ return NULL;
+ }
+
+ if( Cont->DevInfo[Endpt / 16] == NULL ) {
+ Cont->DevInfo[Endpt / 16] = calloc( 1, sizeof(*Cont->DevInfo[0]) );
+ }
+ tUHCI_EndpointInfo *epi = &Cont->DevInfo[Endpt/16]->EndpointInfo[Endpt%16];
+ if( epi->Type ) {
+ // oops, in use
+ Log_Warning("UHCI", "Endpoint %x reused?", Endpt);
+ return NULL;
+ }
+
+ epi->MaxPacketSize = MaxPacketSize;
+ epi->Type = Type;
+ epi->Tgl = 0;
+
return (void*)(Endpt+1);
+
}
-void *UHCI_InitBulk(void *Ptr, int Endpt)
+void *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize)
{
- // TODO: Bitmap of tgl values
- return (void*)(Endpt+1);
+ return UHCI_int_InitEndpt(Ptr, 1, Endpt, MaxPacketSize);
+}
+
+void *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize)
+{
+ return UHCI_int_InitEndpt(Ptr, 2, Endpt, MaxPacketSize);
}
void UHCI_RemoveEndpoint(void *Ptr, void *Handle)
{
- if( (int)Handle < 0x7FF ) {
-
+ tUHCI_Controller *Cont = Ptr;
+ if( Handle == NULL )
+ return ;
+
+ if( (tVAddr)Handle <= 256*16 ) {
+ int addr = (tVAddr)Handle;
+ Cont->DevInfo[addr/16]->EndpointInfo[addr%16].Type = 0;
}
else {
// TODO: Stop interrupt transaction
tUHCI_Controller *Cont = Ptr;
tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
tUHCI_TD *td;
- int Dest = (int)Endpt-1;
- int Tgl = 0;
+ tUHCI_EndpointInfo *epi;
+ int dest, tgl;
+ size_t mps;
if( Endpt == NULL ) {
Log_Error("UHCI", "Passed a NULL Endpoint handle");
LEAVE('n');
return NULL;
}
- // if( Cont->Devs[Dest/16] == NULL ) LEAVE_RET('n', NULL);
- // if( Cont->Devs[Dest/16].EndPt[Dest%16].Type != 1 ) LEAVE_RET('n', NULL);
- // MAX_PACKET_SIZE = Cont->Devs[Dest/16].EndPt[Dest%16].MPS;
- // Tgl = Cont->Devs[Dest/16].EndPt[Dest%16].Tgl;
+ dest = (tVAddr)Endpt - 1;
+ if( Cont->DevInfo[dest/16] == NULL ) LEAVE_RET('n', NULL);
+ epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
+ if( epi->Type != 1 ) LEAVE_RET('n', NULL);
+ mps = epi->MaxPacketSize;
+ tgl = epi->Tgl;
// TODO: Build up list and then append to QH in one operation
}
// Sanity check data lengths
- if( SetupLength > MAX_PACKET_SIZE ) LEAVE_RET('n', NULL);
- if( status_len > MAX_PACKET_SIZE ) LEAVE_RET('n', NULL);
+ if( SetupLength > mps ) LEAVE_RET('n', NULL);
+ if( status_len > mps ) LEAVE_RET('n', NULL);
// Create and append SETUP packet
- td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, (void*)SetupData, SetupLength);
+ td = UHCI_int_CreateTD(Cont, dest, PID_SETUP, tgl, NULL, NULL, (void*)SetupData, SetupLength);
UHCI_int_AppendTD(Cont, qh, td);
- Tgl = !Tgl;
+ tgl = !tgl;
- // Data packets
+ // Send data packets
while( data_len > 0 )
{
- size_t len = MIN(data_len, MAX_PACKET_SIZE);
- td = UHCI_int_CreateTD(Cont, Dest, data_pid, Tgl, NULL, NULL, data_ptr, len);
+ size_t len = MIN(data_len, mps);
+ td = UHCI_int_CreateTD(Cont, dest, data_pid, tgl, NULL, NULL, data_ptr, len);
UHCI_int_AppendTD(Cont, qh, td);
- Tgl = !Tgl;
+ tgl = !tgl;
data_ptr += len;
data_len -= len;
- // TODO: Handle multi-packet
}
- td = UHCI_int_CreateTD(Cont, Dest, status_pid, Tgl, Cb, CbData, status_ptr, status_len);
+ // Send status
+ td = UHCI_int_CreateTD(Cont, dest, status_pid, tgl, Cb, CbData, status_ptr, status_len);
UHCI_int_AppendTD(Cont, qh, td);
- Tgl = !Tgl;
+ tgl = !tgl;
- // Cont->Devs[Dest/16].EndPt[Dest%16].Tgl = !Tgl;
+ // Update toggle value
+ epi->Tgl = tgl;
LEAVE('p', td);
return td;
tUHCI_Controller *Cont = Ptr;
tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
tUHCI_TD *td = NULL;
- int Dest = (int)Endpt - 1;
- int Tgl = 0;
+ tUHCI_EndpointInfo *epi;
+ int dest, tgl;
+ size_t mps;
ENTER("pPtr pEndpt pCb pCbData bOutbound pData iLength", Ptr, Dest, Cb, CbData, bOutbound, Data, Length);
if( Endpt == NULL ) {
- Log_Error("UHCI", "Passed a NULL Endpoint handle");
+ Log_Error("UHCI", "_SendBulk passed a NULL endpoint handle");
LEAVE('n');
return NULL;
}
// Sanity check Endpt
- // TODO: Validation
- // TODO: Data toggle
+ if( (tVAddr)Endpt > 256*16 ) {
+ Log_Error("UHCI", "_SendBulk passed an interrupt endpoint handle");
+ LEAVE('n');
+ return NULL;
+ }
+ dest = (tVAddr)Endpt - 1;
+ if( Cont->DevInfo[dest/16] == NULL ) {
+ Log_Error("UHCI", "_SendBulk passed an uninitialised handle");
+ LEAVE('n');
+ return NULL;
+ }
+ epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
+ if( epi->Type != 2 ) {
+ Log_Error("UHCI", "_SendBulk passed an invalid endpoint type (%i!=2)", epi->Type);
+ LEAVE('n');
+ return NULL;
+ }
+ tgl = epi->Tgl;
+ mps = epi->MaxPacketSize;
Uint8 pid = (bOutbound ? PID_OUT : PID_IN);
char *pos = Data;
while( Length > 0 )
{
- size_t len = MIN(MAX_PACKET_SIZE, Length);
+ size_t len = MIN(mps, Length);
- td = UHCI_int_CreateTD(Cont, Dest, pid, Tgl, Cb, (len == Length ? CbData : NULL), pos, len);
+ td = UHCI_int_CreateTD(Cont, dest, pid, tgl, Cb, (len == Length ? CbData : NULL), pos, len);
UHCI_int_AppendTD(Cont, qh, td);
pos += len;
Length -= len;
- Tgl = !Tgl;
+ tgl = !tgl;
}
+
+ epi->Tgl = tgl;
LEAVE('p', td);
return td;
// === TYPES ===
typedef struct sUHCI_Controller tUHCI_Controller;
+typedef struct sUHCI_EndpointInfo tUHCI_EndpointInfo;
typedef struct sUHCI_ExtraTDInfo tUHCI_ExtraTDInfo;
typedef struct sUHCI_TD tUHCI_TD;
void *CallbackPtr;
};
-#define TD_CTL_IOC (1 << 24)
+struct sUHCI_EndpointInfo
+{
+ unsigned MaxPacketSize : 12;
+ unsigned Type : 3;
+ unsigned Tgl : 1;
+};
+#define TD_CTL_IOC (1 << 24)
#define TD_CTL_ACTIVE (1 << 23)
#define TD_CTL_STALLED (1 << 22)
#define TD_CTL_DATABUFERR (1 << 21)
*/
Uint32 Next;
-
/**
* \brief Next Entry in list
*
tUHCI_TD LocalTDPool[ (4096-(128+2)*sizeof(tUHCI_QH)) / sizeof(tUHCI_TD) ];
} *TDQHPage;
+
+ struct {
+ tUHCI_EndpointInfo EndpointInfo[16];
+ } *DevInfo[256];
};
// === ENUMERATIONS ===