X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FModules%2FUSB%2FEHCI%2Fehci.c;h=8a316e350b6e2416df71d7fc1389dc497fab6692;hb=07f5b8fe9112ab19cf7a4794026764b8ea7a8e91;hp=87d84b6960bbf376eb801766153b1ac42e36bbe0;hpb=bd1e8e83753ed047a4a6b561e3049b2414e8205f;p=tpg%2Facess2.git diff --git a/KernelLand/Modules/USB/EHCI/ehci.c b/KernelLand/Modules/USB/EHCI/ehci.c index 87d84b69..8a316e35 100644 --- a/KernelLand/Modules/USB/EHCI/ehci.c +++ b/KernelLand/Modules/USB/EHCI/ehci.c @@ -12,6 +12,7 @@ #include #include "ehci.h" #include +#include // === CONSTANTS === #define EHCI_MAX_CONTROLLERS 4 @@ -23,17 +24,38 @@ 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_RemEndpoint(void *Ptr, void *Handle); -void *EHCI_SETUP(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, void *Data, size_t Length); -void *EHCI_IN (void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, void *Data, size_t Length); -void *EHCI_OUT (void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, void *Data, size_t Length); +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, + 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); +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); tEHCI_Controller gaEHCI_Controllers[EHCI_MAX_CONTROLLERS]; +tUSBHostDef gEHCI_HostDef = { + .InitInterrupt = EHCI_InitInterrupt, + .InitIsoch = EHCI_InitIsoch, + .InitBulk = EHCI_InitBulk, + .RemEndpoint = EHCI_RemEndpoint, + .SendIsoch = NULL, + .SendControl = EHCI_SendControl, + .SendBulk = EHCI_SendBulk, + .FreeOp = EHCI_FreeOp + }; // === CODE === int EHCI_Initialise(char **Arguments) @@ -87,6 +109,7 @@ int EHCI_InitController(tPAddr BaseAddress, Uint8 InterruptNum) // - Allocate periodic queue cont->PeriodicQueue = (void*)MM_AllocDMA(1, 32, NULL); // TODO: Error check + // > Populate queue // -- Bind IRQ -- IRQ_AddHandler(InterruptNum, EHCI_InterruptHandler, cont); @@ -98,7 +121,7 @@ int EHCI_InitController(tPAddr BaseAddress, Uint8 InterruptNum) // - Set USBINTR cont->OpRegs->USBIntr = USBINTR_IOC|USBINTR_PortChange|USBINTR_FrameRollover; // - Set PERIODICLIST BASE - cont->OpRegs->PeridocListBase = MM_GetPhysAddr( (tVAddr) cont->PeriodicQueue ); + cont->OpRegs->PeridocListBase = MM_GetPhysAddr( cont->PeriodicQueue ); // - Enable controller cont->OpRegs->USBCmd = (0x40 << 16) | USBCMD_PeriodicEnable | USBCMD_Run; // - Route all ports @@ -109,5 +132,220 @@ int EHCI_InitController(tPAddr BaseAddress, Uint8 InterruptNum) void EHCI_InterruptHandler(int IRQ, void *Ptr) { + tEHCI_Controller *cont = Ptr; + Uint32 sts = cont->OpRegs->USBSts; + + // Clear interrupts + cont->OpRegs->USBSts = sts; + + if( sts & USBINTR_IOC ) { + // IOC + sts &= ~USBINTR_IOC; + } + + if( sts & USBINTR_PortChange ) { + // Port change, determine what port and poke helper thread + sts &= ~USBINTR_PortChange; + } + + if( sts & USBINTR_FrameRollover ) { + // Frame rollover, used to aid timing (trigger per-second operations) + sts &= ~USBINTR_FrameRollover; + } + + if( sts ) { + // Unhandled interupt bits + // TODO: Warn + } +} + +// -------------------------------------------------------------------- +// USB API +// -------------------------------------------------------------------- +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, size_t MaxPacketSize) +{ + return (void*)(tVAddr)(Endpoint + 1); +} +void *EHCI_InitControl(void *Ptr, int Endpoint, size_t MaxPacketSize) +{ + 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, size_t MaxPacketSize) +{ + return EHCI_InitControl(Ptr, Endpoint, MaxPacketSize); +} +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 + ) +{ + 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) +{ +} +