From 3bcfc9ded1d44d1fbec95f73b5894e26f498b73d Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 26 Feb 2012 21:48:20 +0800 Subject: [PATCH] Kernel/USB - Still broken, reworking host controller API to give driver more information - Moved interrupt polling to host driver - OHCI not converted yet - UHCI is buggy, doesn't manage to read descriptors --- BuildConf/x86/default.mk | 3 +- .../Modules/USB/Core/include/usb_host.h | 22 +- KernelLand/Modules/USB/Core/usb_io.c | 6 +- KernelLand/Modules/USB/Core/usb_lowlevel.c | 50 +- KernelLand/Modules/USB/Core/usb_poll.c | 97 +--- KernelLand/Modules/USB/UHCI/uhci.c | 454 ++++++++++++------ KernelLand/Modules/USB/UHCI/uhci.h | 28 +- 7 files changed, 407 insertions(+), 253 deletions(-) diff --git a/BuildConf/x86/default.mk b/BuildConf/x86/default.mk index 11881b0f..592d7020 100644 --- a/BuildConf/x86/default.mk +++ b/BuildConf/x86/default.mk @@ -7,6 +7,7 @@ MODULES += Display/BochsGA MODULES += Input/PS2KbMouse MODULES += x86/ISADMA x86/VGAText -MODULES += USB/Core USB/UHCI USB/OHCI +MODULES += USB/Core USB/UHCI +#USB/OHCI MODULES += USB/HID #MODULES += Interfaces/UDI diff --git a/KernelLand/Modules/USB/Core/include/usb_host.h b/KernelLand/Modules/USB/Core/include/usb_host.h index eda42cf9..ab372797 100644 --- a/KernelLand/Modules/USB/Core/include/usb_host.h +++ b/KernelLand/Modules/USB/Core/include/usb_host.h @@ -13,24 +13,26 @@ typedef struct sUSBHostDef tUSBHostDef; -typedef void (*tUSBHostCb)(void *DataPtr, void *Data, int Length); +typedef void (*tUSBHostCb)(void *DataPtr, void *Data, size_t Length); typedef void *(*tUSBHostOp)(void *Ptr, int Dest, int DataTgl, tUSBHostCb CB, void *CbData, void *Data, size_t Length); +typedef void *(*tUSBIntOp)(void *Ptr, int Dest, int Period, tUSBHostCb CB, void *CbData, void *Data, size_t Length); /** * \brief Defines a USB Host Controller type */ struct sUSBHostDef { - tUSBHostOp SendIN; - tUSBHostOp SendOUT; - tUSBHostOp SendSETUP; - - /** - * \brief Check if an operation has completed - * \note Only valid to call if CB passed was ERRPTR - */ - int (*IsOpComplete)(void *Ptr, void *OpPtr); + tUSBIntOp InterruptIN; + tUSBIntOp InterruptOUT; + void (*StopInterrupt)(void *Ptr, void *Handle); + + void *(*ControlSETUP)(void *Ptr, int Dest, int DataTgl, void *Data, size_t Length); + tUSBHostOp ControlIN; + tUSBHostOp ControlOUT; + + tUSBHostOp BulkIN; + tUSBHostOp BulkOUT; void (*CheckPorts)(void *Ptr); }; diff --git a/KernelLand/Modules/USB/Core/usb_io.c b/KernelLand/Modules/USB/Core/usb_io.c index dada0deb..745e8a64 100644 --- a/KernelLand/Modules/USB/Core/usb_io.c +++ b/KernelLand/Modules/USB/Core/usb_io.c @@ -25,7 +25,7 @@ struct sAsyncOp // === PROTOTYPES === void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data); void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data); -void USB_AsyncCallback(void *Ptr, void *Buf, int Length); +void USB_AsyncCallback(void *Ptr, void *Buf, size_t Length); void USB_AsyncThread(void *unused); // === GLOBALS === @@ -83,7 +83,7 @@ void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, host = Dev->Dev->Host; LOG("IN from %p %i:%i", host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum); - host->HostDef->SendIN( + host->HostDef->BulkIN( host->Ptr, Dev->Dev->Address*16 + op->Endpt->EndpointNum, 0, USB_AsyncCallback, op, DataBuf, Length @@ -94,7 +94,7 @@ void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, // Log_Warning("USB", "TODO: Implement USB_RecvDataA"); } -void USB_AsyncCallback(void *Ptr, void *Buf, int Length) +void USB_AsyncCallback(void *Ptr, void *Buf, size_t Length) { tAsyncOp *op = Ptr; op->Length = Length; diff --git a/KernelLand/Modules/USB/Core/usb_lowlevel.c b/KernelLand/Modules/USB/Core/usb_lowlevel.c index 033be8dc..67381c0b 100644 --- a/KernelLand/Modules/USB/Core/usb_lowlevel.c +++ b/KernelLand/Modules/USB/Core/usb_lowlevel.c @@ -11,9 +11,11 @@ #include "usb_proto.h" #include "usb_lowlevel.h" #include +#include // === PROTOTYPES === void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data); +void USB_int_WakeThread(void *Thread, void *Data, size_t Length); int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address); int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest); char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index); @@ -26,6 +28,7 @@ void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, in // TODO: Sanity check (and check that Type is valid) struct sDeviceRequest req; int dest = Addr * 16 + EndPt; // TODO: Validate + tThread *thisthread = Proc_GetCurThread(); ENTER("pHost xdest iType iReq iVal iIndx iLen pData", Host, dest, Type, Req, Val, Indx, Len, Data); @@ -36,8 +39,10 @@ void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, in req.Index = LittleEndian16( Indx ); req.Length = LittleEndian16( Len ); + Threads_ClearEvent(THREAD_EVENT_SHORTWAIT); + LOG("SETUP"); - hdl = Host->HostDef->SendSETUP(Host->Ptr, dest, 0, NULL, NULL, &req, sizeof(req)); + hdl = Host->HostDef->ControlSETUP(Host->Ptr, dest, 0, &req, sizeof(req)); // TODO: Data toggle? // TODO: Multi-packet transfers @@ -46,34 +51,39 @@ void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, in if( Type & 0x80 ) { LOG("IN"); - hdl = Host->HostDef->SendIN(Host->Ptr, dest, 1, NULL, NULL, Data, Len); + hdl = Host->HostDef->ControlIN(Host->Ptr, dest, 1, NULL, NULL, Data, Len); LOG("OUT (Status)"); - hdl = Host->HostDef->SendOUT(Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0); + hdl = Host->HostDef->ControlOUT(Host->Ptr, dest, 1, USB_int_WakeThread, thisthread, NULL, 0); } else { LOG("OUT"); - Host->HostDef->SendOUT(Host->Ptr, dest, 1, NULL, NULL, Data, Len); + Host->HostDef->ControlOUT(Host->Ptr, dest, 1, NULL, NULL, Data, Len); // Status phase (DataToggle=1) LOG("IN (Status)"); - hdl = Host->HostDef->SendIN(Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0); + hdl = Host->HostDef->ControlIN(Host->Ptr, dest, 1, USB_int_WakeThread, thisthread, NULL, 0); } } else { // Zero length, IN status LOG("IN (Status)"); - hdl = Host->HostDef->SendIN(Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0); + hdl = Host->HostDef->ControlIN(Host->Ptr, dest, 1, USB_int_WakeThread, thisthread, NULL, 0); } LOG("Wait..."); - while( Host->HostDef->IsOpComplete(Host->Ptr, hdl) == 0 ) - Time_Delay(1); + Threads_WaitEvents(THREAD_EVENT_SHORTWAIT); + LEAVE('p', hdl); return hdl; } +void USB_int_WakeThread(void *Thread, void *Data, size_t Length) +{ + Threads_PostEvent(Thread, THREAD_EVENT_SHORTWAIT); +} + int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address) { USB_int_Request(Host, 0, 0, 0x00, 5, Address & 0x7F, 0, 0, NULL); @@ -101,17 +111,13 @@ int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, i req.Length = LittleEndian16( Length ); LOG("SETUP"); - Dev->Host->HostDef->SendSETUP( - Dev->Host->Ptr, dest, - 0, NULL, NULL, - &req, sizeof(req) - ); + Dev->Host->HostDef->ControlSETUP(Dev->Host->Ptr, dest, 0, &req, sizeof(req)); bToggle = 1; while( Length > ciMaxPacketSize ) { LOG("IN (%i rem)", Length - ciMaxPacketSize); - Dev->Host->HostDef->SendIN( + Dev->Host->HostDef->ControlIN( Dev->Host->Ptr, dest, bToggle, NULL, NULL, Dest, ciMaxPacketSize @@ -121,18 +127,18 @@ int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, i } LOG("IN (final)"); - Dev->Host->HostDef->SendIN( - Dev->Host->Ptr, dest, - bToggle, NULL, NULL, - Dest, Length - ); + Dev->Host->HostDef->ControlIN( Dev->Host->Ptr, dest, bToggle, NULL, NULL, Dest, Length ); + Threads_ClearEvent(THREAD_EVENT_SHORTWAIT); LOG("OUT (Status)"); - final = Dev->Host->HostDef->SendOUT(Dev->Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0); + final = Dev->Host->HostDef->ControlOUT( + Dev->Host->Ptr, dest, 1, + USB_int_WakeThread, Proc_GetCurThread(), + NULL, 0 + ); LOG("Waiting"); - while( Dev->Host->HostDef->IsOpComplete(Dev->Host->Ptr, final) == 0 ) - Threads_Yield(); // BAD BAD BAD + Threads_WaitEvents(THREAD_EVENT_SHORTWAIT); LEAVE('i', 0); return 0; diff --git a/KernelLand/Modules/USB/Core/usb_poll.c b/KernelLand/Modules/USB/Core/usb_poll.c index 6285a11e..d1cbaa93 100644 --- a/KernelLand/Modules/USB/Core/usb_poll.c +++ b/KernelLand/Modules/USB/Core/usb_poll.c @@ -21,10 +21,15 @@ extern tUSBHost *gUSB_Hosts; void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint); // === GLOBALS === -tUSBEndpoint *gUSB_PollQueues[POLL_MAX/POLL_ATOM]; - int giUSB_PollPosition; // Index into gUSB_PollQueues // === CODE === +void USB_int_PollCallback(void *Ptr, void *Data, size_t Length) +{ + tUSBEndpoint *ep = Ptr; + + ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail(ep->Interface, ep->EndpointIdx, Length, Data); +} + void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint) { tUSBEndpoint *endpt; @@ -44,18 +49,14 @@ void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint) endpt->InputData = malloc(endpt->MaxPacketSize); - // Determine polling period in atoms - endpt->PollingAtoms = (endpt->PollingPeriod + POLL_ATOM-1) / POLL_ATOM; - if(endpt->PollingAtoms > POLL_SLOTS) endpt->PollingAtoms = POLL_SLOTS; - LOG("endpt(%p)->PollingAtoms = %i", endpt, endpt->PollingAtoms); - // Add to poll queue - // TODO: Locking - { - int idx = giUSB_PollPosition + 1; - if(idx >= POLL_SLOTS) idx -= POLL_SLOTS; - endpt->Next = gUSB_PollQueues[idx]; - gUSB_PollQueues[idx] = endpt; - } + Iface->Dev->Host->HostDef->InterruptIN( + Iface->Dev->Host->Ptr, + Iface->Dev->Address * 16 + endpt->EndpointNum, + endpt->PollingPeriod, + USB_int_PollCallback, endpt, + endpt->InputData, endpt->MaxPacketSize + ); + LEAVE('-'); } /** @@ -66,74 +67,12 @@ int USB_PollThread(void *unused) Threads_SetName("USB Polling Thread"); for(;;) { - tUSBEndpoint *ep, *prev; - - if(giUSB_PollPosition == 0) + // Check hosts + for( tUSBHost *host = gUSB_Hosts; host; host = host->Next ) { - // Check hosts - for( tUSBHost *host = gUSB_Hosts; host; host = host->Next ) - { - host->HostDef->CheckPorts(host->Ptr); - } + host->HostDef->CheckPorts(host->Ptr); } -// Log_Debug("USBPoll", "giUSB_PollPosition = %i", giUSB_PollPosition); - - // A little evil for neater code - prev = (void*)( (tVAddr)&gUSB_PollQueues[giUSB_PollPosition] - offsetof(tUSBEndpoint, Next) ); - - // Process queue -// LOG("giUSB_PollPosition = %i", giUSB_PollPosition); - for( ep = gUSB_PollQueues[giUSB_PollPosition]; ep; prev = ep, ep = ep->Next ) - { - int period_in_atoms = ep->PollingAtoms; - LOG("%i: ep = %p", giUSB_PollPosition, ep); - - // Check for invalid entries - if(period_in_atoms < 0 || period_in_atoms > POLL_ATOM) - { - Log_Warning("USB", "Endpoint on polling queue with invalid period"); - continue ; - } - // Check for entries to delete - if(period_in_atoms == 0) - { - // Remove - prev->Next = ep->Next; - ep->PollingAtoms = -1; // Mark as removed - ep = prev; // Make sure prev is kept valid - continue ; - } - - // Read data - // TODO: Check the endpoint - // TODO: Async checking? - // - Send the read request on all of them then wait for the first to complete - USB_RecvDataA( - ep->Interface, ep->EndpointIdx+1, - ep->MaxPacketSize, ep->InputData, - ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail - ); - - // Call callback - - // Reschedule - if( period_in_atoms != POLL_SLOTS ) - { - int newqueue_id = (giUSB_PollPosition + period_in_atoms) % POLL_SLOTS; - tUSBEndpoint **newqueue = &gUSB_PollQueues[newqueue_id]; - - prev->Next = ep->Next; - - ep->Next = *newqueue; - *newqueue = ep; - ep = prev; - } - } - giUSB_PollPosition ++; - if(giUSB_PollPosition == POLL_SLOTS) - giUSB_PollPosition = 0; - // TODO: Check for a longer delay Time_Delay(POLL_ATOM); } } diff --git a/KernelLand/Modules/USB/UHCI/uhci.c b/KernelLand/Modules/USB/UHCI/uhci.c index 48960c21..8e840ab5 100644 --- a/KernelLand/Modules/USB/UHCI/uhci.c +++ b/KernelLand/Modules/USB/UHCI/uhci.c @@ -19,18 +19,29 @@ #define MAX_CONTROLLERS 4 //#define NUM_TDs 1024 #define NUM_TDs 64 +#define MAX_PACKET_SIZE 0x400 +#define PID_IN 0x69 +#define PID_OUT 0xE1 +#define PID_SETUP 0x2D // === PROTOTYPES === int UHCI_Initialise(char **Arguments); void UHCI_Cleanup(); + int UHCI_int_InitHost(tUHCI_Controller *Host); +// -- List internals tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont); -void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD); -void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); -void *UHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); -void *UHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); -void *UHCI_SendSetup(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); - int UHCI_IsTransferComplete(void *Ptr, void *Handle); - int UHCI_Int_InitHost(tUHCI_Controller *Host); +void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD); +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_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); +void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); +void UHCI_StopInterrupt(void *Ptr, void *Handle); +void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length); +void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length); +void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length); +void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); +void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); + void UHCI_CheckPortUpdate(void *Ptr); void UHCI_int_InterruptThread(void *Unused); void UHCI_InterruptHandler(int IRQ, void *Ptr); @@ -45,10 +56,17 @@ MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL); tUHCI_TD gaUHCI_TDPool[NUM_TDs]; tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS]; tUSBHostDef gUHCI_HostDef = { - .SendIN = UHCI_DataIN, - .SendOUT = UHCI_DataOUT, - .SendSETUP = UHCI_SendSetup, - .IsOpComplete = UHCI_IsTransferComplete, + .InterruptIN = UHCI_InterruptIN, + .InterruptOUT = UHCI_InterruptOUT, + .StopInterrupt = UHCI_StopInterrupt, + + .ControlSETUP = UHCI_ControlSETUP, + .ControlIN = UHCI_ControlIN, + .ControlOUT = UHCI_ControlOUT, + + .BulkOUT = UHCI_BulkOUT, + .BulkIN = UHCI_BulkIN, + .CheckPorts = UHCI_CheckPortUpdate }; tSemaphore gUHCI_InterruptSempahore; @@ -74,9 +92,6 @@ int UHCI_Initialise(char **Arguments) return MODULE_ERR_NOTNEEDED; } - // Spin off interrupt handling thread - Proc_SpawnWorker( UHCI_int_InterruptThread, NULL ); - // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS ) { @@ -104,12 +119,16 @@ int UHCI_Initialise(char **Arguments) IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo); // Initialise Host - ret = UHCI_Int_InitHost(&gUHCI_Controllers[i]); + ret = UHCI_int_InitHost(cinfo); // Detect an error if(ret != 0) { LEAVE('i', ret); return ret; } + + // Spin off interrupt handling thread + Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo ); + cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2); LOG("cinfo->RootHub = %p", cinfo->RootHub); @@ -132,6 +151,117 @@ void UHCI_Cleanup() { } +/** + * \brief Initialises a UHCI host controller + * \param Host Pointer - Host to initialise + */ +int UHCI_int_InitHost(tUHCI_Controller *Host) +{ + ENTER("pHost", Host); + + _OutWord( Host, USBCMD, 4 ); // GRESET + Time_Delay(10); + _OutWord( Host, USBCMD, 0 ); // GRESET + + // Allocate Frame List + // - 1 Page, 32-bit address + // - 1 page = 1024 4 byte entries + Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList); + if( !Host->FrameList ) { + Log_Warning("UHCI", "Unable to allocate frame list, aborting"); + LEAVE('i', -1); + return -1; + } + + Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage); + if( !Host->TDQHPage ) { + // TODO: Clean up + Log_Warning("UHCI", "Unable to allocate QH page, aborting"); + LEAVE('i', -1); + return -1; + } + + // Fill frame list + // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001) + const int dest_offsets[] = { + 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30, + 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31 + }; + for( int i = 0; i < 1024; i ++ ) { + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH ); + Host->FrameList[i] = addr | 2; + } + for( int i = 0; i < 64; i ++ ) { + int ofs = dest_offsets[ i & (32-1) ]; + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_256ms[ofs] ); + Host->FrameList[ 0 + i*4] = addr | 2; + Host->FrameList[256 + i*4] = addr | 2; + Host->FrameList[512 + i*4] = addr | 2; + Host->FrameList[768 + i*4] = addr | 2; + } + for( int i = 0; i < 32; i ++ ) { + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_128ms[i] ); + Host->TDQHPage->InterruptQHs_256ms[i*2 ].Next = addr | 2; + Host->TDQHPage->InterruptQHs_256ms[i*2+1].Next = addr | 2; + } + for( int i = 0; i < 16; i ++ ) { + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_64ms[i] ); + Host->TDQHPage->InterruptQHs_128ms[i*2 ].Next = addr | 2; + Host->TDQHPage->InterruptQHs_128ms[i*2+1].Next = addr | 2; + } + for( int i = 0; i < 8; i ++ ) { + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_32ms[i] ); + Host->TDQHPage->InterruptQHs_64ms[i*2 ].Next = addr | 2; + Host->TDQHPage->InterruptQHs_64ms[i*2+1].Next = addr | 2; + } + for( int i = 0; i < 4; i ++ ) { + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_16ms[i] ); + Host->TDQHPage->InterruptQHs_32ms[i*2 ].Next = addr | 2; + Host->TDQHPage->InterruptQHs_32ms[i*2+1].Next = addr | 2; + } + for( int i = 0; i < 2; i ++ ) { + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_8ms[i] ); + Host->TDQHPage->InterruptQHs_16ms[i*2 ].Next = addr | 2; + Host->TDQHPage->InterruptQHs_16ms[i*2+1].Next = addr | 2; + } + for( int i = 0; i < 1; i ++ ) { + Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_4ms[i] ); + Host->TDQHPage->InterruptQHs_8ms[i*2 ].Next = addr | 2; + Host->TDQHPage->InterruptQHs_8ms[i*2+1].Next = addr | 2; + } + Host->TDQHPage->InterruptQHs_4ms[0].Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH ) | 2; + // Set child pointers + for( int i = 0; i < 127; i ++ ) { + Host->TDQHPage->InterruptQHs_256ms[i].Child = 1; + } + + // Set up control and bulk queues + Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2; + Host->TDQHPage->ControlQH.Child = 1; + Host->TDQHPage->BulkQH.Next = 1; + Host->TDQHPage->BulkQH.Child = 1; + + // Set frame length to 1 ms + _OutByte( Host, SOFMOD, 64 ); + + // Set Frame List + _OutDWord( Host, FLBASEADD, Host->PhysFrameList ); + _OutWord( Host, FRNUM, 0 ); + + // Enable Interrupts + _OutWord( Host, USBINTR, 0x000F ); + PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 ); + + // Enable processing + _OutWord( Host, USBCMD, 0x0001 ); + + LEAVE('i', 0); + return 0; +} + +// -------------------------------------------------------------------- +// TDs and QH Allocation/Appending +// -------------------------------------------------------------------- tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont) { static tMutex lock; @@ -143,7 +273,7 @@ tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont) gaUHCI_TDPool[i].Link = 1; gaUHCI_TDPool[i].Control = (1 << 23); gaUHCI_TDPool[i]._info.bActive = 1; - gaUHCI_TDPool[i]._info.bComplete = 0; + gaUHCI_TDPool[i]._info.period_entry = 0; Mutex_Release( &lock ); return &gaUHCI_TDPool[i]; } @@ -152,26 +282,12 @@ tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont) return NULL; } -void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD) +void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD) { static tMutex lock; // TODO: Should I use a shortlock (avoid being preempted) Mutex_Acquire(&lock); - #if 0 - int next_frame; - - next_frame = (_InWord(Cont, FRNUM) + 2) & (1024-1); - - TD->Control |= (1 << 24); // Ensure that there is an interrupt for each used frame - - TD->Link = Cont->FrameList[next_frame]; - Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD ); - #else - - // TODO: Support other QHs - tUHCI_QH *qh = &Cont->BulkQH; - // Ensure that there is an interrupt for each used frame TD->Control |= (1 << 24); @@ -180,18 +296,21 @@ void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD) // Add TD->Link = 1; - if( qh->Child & 1 ) { - qh->Child = MM_GetPhysAddr( (tVAddr)TD ); + if( QH->Child & 1 ) { + QH->Child = MM_GetPhysAddr( (tVAddr)TD ); } else { - qh->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ); + // Depth first + QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4; } - qh->_LastItem = TD; + QH->_LastItem = TD; // Reenable controller _OutWord( Cont, USBCMD, 0x0001 ); + + // DEBUG! + LOG("QH(%p)->Child = %x", QH, QH->Child); LOG("TD(%p)->Control = %x", TD, TD->Control); - #endif Mutex_Release(&lock); } @@ -202,7 +321,7 @@ void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD) * \param Addr Function Address * 16 + Endpoint * \param bTgl Data toggle value */ -void *UHCI_int_SendTransaction( +tUHCI_TD *UHCI_int_CreateTD( tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length) { @@ -215,9 +334,7 @@ void *UHCI_int_SendTransaction( } td = UHCI_int_AllocateTD(Cont); - if( !td ) { - // TODO: Wait for one to free? Log_Error("UHCI", "No avaliable TDs, transaction dropped"); return NULL; } @@ -274,7 +391,6 @@ void *UHCI_int_SendTransaction( if( !info ) info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 ); LOG("IOC Cb=%p CbData=%p", Cb, CbData); - // NOTE: if ERRPTR then the TD is kept allocated until checked info->Callback = Cb; info->CallbackPtr = CbData; } @@ -283,112 +399,181 @@ void *UHCI_int_SendTransaction( LOG("info = %p", info); td->Control |= (1 << 24); td->_info.ExtraInfo = info; - LOG("TD(%p)->Control = 0x%0x", td, td->Control); } - UHCI_int_AppendTD(Cont, td); - return td; } -void *UHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period) { - return UHCI_int_SendTransaction(Ptr, Dest, 0x69, DataTgl, Cb, CbData, Buf, Length); + tUHCI_QH *qh; + const int qh_offsets[] = { 0, 64, 96, 112, 120, 124, 126}; +// const int qh_sizes[] = {64, 32, 16, 8, 4, 2, 1}; + + // Bounds limit + if( Period < 0 ) return ; + if( Period > 256 ) Period = 256; + + // Get the log base2 of the period + int period_slot = 0; + while( Period >>= 1 ) period_slot ++; + + // Adjust for the 4ms minimum period + if( period_slot < 2 ) period_slot = 0; + else period_slot -= 2; + + TD->_info.period_entry = qh_offsets[period_slot] + 1; + qh = Cont->TDQHPage->InterruptQHs_4ms + TD->_info.period_entry - 1; + // TODO: Find queue with lowest load + + LOG("period_slot = %i, period_entry = %i (+1 when encoded)", + period_slot, TD->_info.period_entry); + + UHCI_int_AppendTD(Cont, qh, TD); } -void *UHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) { - return UHCI_int_SendTransaction(Ptr, Dest, 0xE1, DataTgl, Cb, CbData, Buf, Length); + tUHCI_TD *td; + + if( Period < 0 ) return NULL; + + ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength", + Ptr, Dest, Period, Cb, CbData, Buf, Length); + + // TODO: Data toggle? + td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length); + if( !td ) return NULL; + + UHCI_int_SetInterruptPoll(Ptr, td, Period); + + LEAVE('p', td); + return td; } +// TODO: Does interrupt OUT make sense? +void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +{ + tUHCI_TD *td; -void *UHCI_SendSetup(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) + if( Period < 0 ) return NULL; + + ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength", + Ptr, Dest, Period, Cb, CbData, Buf, Length); + + // TODO: Data toggle? + td = UHCI_int_CreateTD(Ptr, Dest, PID_OUT, 0, Cb, CbData, Buf, Length); + if( !td ) return NULL; + + UHCI_int_SetInterruptPoll(Ptr, td, Period); + + LEAVE('p', td); + return td; +} + +void UHCI_StopInterrupt(void *Ptr, void *Handle) { - return UHCI_int_SendTransaction(Ptr, Dest, 0x2D, DataTgl, Cb, CbData, Buf, Length); + // TODO: Stop interrupt transaction } -int UHCI_IsTransferComplete(void *Ptr, void *Handle) +void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length) { - tUHCI_TD *td = Handle; - #if DEBUG - tUHCI_Controller *Cont = &gUHCI_Controllers[0]; - LOG("%p->Control = 0x%08x", td, td->Control); - LOG("USBSTS = 0x%x, USBINTR = 0x%x", _InWord(Cont, USBSTS), _InWord(Cont, USBINTR)); - LOG("Cont->BulkQH.Child = %x", Cont->BulkQH.Child); - #endif - if(td->Control & (1 << 23)) { - return 0; - } - LOG("inactive, waiting for completion"); - if(td->_info.bComplete) - { - td->_info.bActive = 0; - td->_info.bComplete = 0; - td->Link = 0; - return 1; - } - else - { - return 0; - } + tUHCI_Controller *Cont = Ptr; + tUHCI_QH *qh = &Cont->TDQHPage->ControlQH; + tUHCI_TD *td; + + ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length); + + td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length); + UHCI_int_AppendTD(Cont, qh, td); + + LEAVE('p', td); + + return td; } +void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length) +{ + tUHCI_Controller *Cont = Ptr; + tUHCI_QH *qh = &Cont->TDQHPage->ControlQH; + tUHCI_TD *td; -// === INTERNAL FUNCTIONS === -/** - * \fn int UHCI_Int_InitHost(tUCHI_Controller *Host) - * \brief Initialises a UHCI host controller - * \param Host Pointer - Host to initialise - */ -int UHCI_Int_InitHost(tUHCI_Controller *Host) + ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length); + + td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length); + UHCI_int_AppendTD(Cont, qh, td); + + LEAVE('p', td); + return td; +} +void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length) { - ENTER("pHost", Host); + tUHCI_Controller *Cont = Ptr; + tUHCI_QH *qh = &Cont->TDQHPage->ControlQH; + tUHCI_TD *td; - _OutWord( Host, USBCMD, 4 ); // GRESET - Time_Delay(10); - _OutWord( Host, USBCMD, 0 ); // GRESET + ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length); - // Allocate Frame List - // - 1 Page, 32-bit address - // - 1 page = 1024 4 byte entries - Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList); - if( !Host->FrameList ) { - Log_Warning("UHCI", "Unable to allocate frame list, aborting"); - LEAVE('i', -1); - return -1; + td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, !!Tgl, Cb, CbData, Data, Length); + UHCI_int_AppendTD(Cont, qh, td); + + LEAVE('p', td); + return td; +} + +void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +{ + tUHCI_Controller *Cont = Ptr; + tUHCI_QH *qh = &Cont->TDQHPage->BulkQH; + tUHCI_TD *td; + char *src = Buf; + + ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length); + + while( Length > MAX_PACKET_SIZE ) + { + LOG("MaxPacket (rem = %i)", Length); + td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, MAX_PACKET_SIZE); + UHCI_int_AppendTD(Cont, qh, td); + + bToggle = !bToggle; + Length -= MAX_PACKET_SIZE; + src += MAX_PACKET_SIZE; } - // TODO: Handle QHs not being in a 32-bit paddr range - // Need another page, probably get some more TDs from it too + LOG("Final"); + td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length); + UHCI_int_AppendTD(Cont, qh, td); - // Set up interrupt and bulk queue - Host->InterruptQH.Next = MM_GetPhysAddr( (tVAddr)&Host->ControlQH ) | 2; - Host->InterruptQH.Child = 1; - Host->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->BulkQH ) | 2; - Host->ControlQH.Child = 1; - Host->BulkQH.Next = 1; - Host->BulkQH.Child = 1; + LEAVE('p', td); + return td; +} +void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +{ + tUHCI_Controller *Cont = Ptr; + tUHCI_QH *qh = &Cont->TDQHPage->BulkQH; + tUHCI_TD *td; + char *dst = Buf; - LOG("Allocated frame list 0x%x (0x%x)", Host->FrameList, Host->PhysFrameList); - for( int i = 0; i < 1024; i ++ ) - Host->FrameList[i] = MM_GetPhysAddr( (tVAddr)&Host->InterruptQH ) | 2; - - // Set frame length to 1 ms - _OutByte( Host, SOFMOD, 64 ); - - // Set Frame List - _OutDWord( Host, FLBASEADD, Host->PhysFrameList ); - _OutWord( Host, FRNUM, 0 ); - - // Enable Interrupts - _OutWord( Host, USBINTR, 0x000F ); - PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 ); + ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length); + while( Length > MAX_PACKET_SIZE ) + { + LOG("MaxPacket (rem = %i)", Length); + td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, MAX_PACKET_SIZE); + UHCI_int_AppendTD(Cont, qh, td); + + bToggle = !bToggle; + Length -= MAX_PACKET_SIZE; + dst += MAX_PACKET_SIZE; + } - // Enable processing - _OutWord( Host, USBCMD, 0x0001 ); + LOG("Final"); + td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length); + UHCI_int_AppendTD(Cont, qh, td); - LEAVE('i', 0); - return 0; + LEAVE('p', td); + return td; } +// === INTERNAL FUNCTIONS === void UHCI_CheckPortUpdate(void *Ptr) { tUHCI_Controller *Host = Ptr; @@ -428,8 +613,9 @@ void UHCI_CheckPortUpdate(void *Ptr) } } -void UHCI_int_InterruptThread(void *Unused) +void UHCI_int_InterruptThread(void *Pointer) { + tUHCI_Controller *Cont = Pointer; Threads_SetName("UHCI Interrupt Handler"); for( ;; ) { @@ -451,8 +637,6 @@ void UHCI_int_InterruptThread(void *Unused) if( td->_info.bActive == 0 ) continue ; // Skip ones that are still in use if( td->Control & (1 << 23) ) continue ; - // Skip ones that are waiting for ACK - if( td->_info.bComplete == 1 ) continue ; // If no callback/alt buffer, mark as free and move on if( td->_info.ExtraInfo ) @@ -460,7 +644,7 @@ void UHCI_int_InterruptThread(void *Unused) // Get size of transfer byte_count = (td->Control & 0x7FF)+1; - // Handle non page-aligned destination (with a > 32-bit paddr) + // Handle non page-aligned destination (or with a > 32-bit paddr) if(info->FirstPage) { char *src, *dest; @@ -489,18 +673,6 @@ void UHCI_int_InterruptThread(void *Unused) MM_FreeTemp( (tVAddr)dest ); } - // Don't mark as inactive, the check should do that - if( info->Callback == INVLPTR ) - { - LOG("Marking %p as complete", td); - td->_info.bComplete = 1; - free( info ); - td->_info.ExtraInfo = NULL; - if( td->_info.bFreePointer ) - MM_DerefPhys( td->BufferPointer ); - continue ; - } - // Callback if( info->Callback != NULL ) { @@ -515,6 +687,20 @@ void UHCI_int_InterruptThread(void *Unused) td->_info.ExtraInfo = NULL; } + if( td->_info.period_entry > 0 ) + { + LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.period_entry-1); + // TODO: Flip toggle? + td->Control |= (1 << 23); + // Add back into controller's interrupt list + UHCI_int_AppendTD( + Cont, + Cont->TDQHPage->InterruptQHs_256ms + td->_info.period_entry - 1, + td + ); + continue ; + } + if( td->_info.bFreePointer ) MM_DerefPhys( td->BufferPointer ); diff --git a/KernelLand/Modules/USB/UHCI/uhci.h b/KernelLand/Modules/USB/UHCI/uhci.h index 40b589b1..db5078e9 100644 --- a/KernelLand/Modules/USB/UHCI/uhci.h +++ b/KernelLand/Modules/USB/UHCI/uhci.h @@ -85,7 +85,7 @@ struct sUHCI_TD { tUHCI_ExtraTDInfo *ExtraInfo; char bActive; // Allocated - char bComplete; // Job complete + Uint8 period_entry; // index + 1, 0 = non-interrupt, 1 = offset 0 char bFreePointer; // Free \a BufferPointer once done } _info; } __attribute__((aligned(16))); @@ -164,9 +164,29 @@ struct sUHCI_Controller tUSBHub *RootHub; - tUHCI_QH InterruptQH; - tUHCI_QH ControlQH; - tUHCI_QH BulkQH; + /** + */ +// int FrameLoads[1024]; + + tPAddr PhysTDQHPage; + struct + { + // 127 Interrupt Queue Heads + // - 4ms -> 256ms range of periods + tUHCI_QH InterruptQHs_256ms[64]; + tUHCI_QH InterruptQHs_128ms[32]; + tUHCI_QH InterruptQHs_64ms [16]; + tUHCI_QH InterruptQHs_32ms [ 8]; + tUHCI_QH InterruptQHs_16ms [ 4]; + tUHCI_QH InterruptQHs_8ms [ 2]; + tUHCI_QH InterruptQHs_4ms [ 1]; + tUHCI_QH _padding; + + tUHCI_QH ControlQH; + tUHCI_QH BulkQH; + + tUHCI_TD LocalTDPool[ (4096-(128+2)*sizeof(tUHCI_QH)) / sizeof(tUHCI_TD) ]; + } *TDQHPage; }; // === ENUMERATIONS === -- 2.20.1