From: John Hodge Date: Thu, 23 Feb 2012 16:04:28 +0000 (+0800) Subject: Modules/UHCI - Big cleanups to code... but there is a bug somewhere X-Git-Tag: rel0.15~755 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=0dd59ddf18248c362e351ae13c7b89bb15831ee4;p=tpg%2Facess2.git Modules/UHCI - Big cleanups to code... but there is a bug somewhere - After a while (i.e. before the mouse is set up) interrupts stop firing or the TDs aren't being processed properly --- diff --git a/KernelLand/Modules/USB/UHCI/uhci.c b/KernelLand/Modules/USB/UHCI/uhci.c index b348a45f..023d28cf 100644 --- a/KernelLand/Modules/USB/UHCI/uhci.c +++ b/KernelLand/Modules/USB/UHCI/uhci.c @@ -4,7 +4,7 @@ * * Universal Host Controller Interface */ -#define DEBUG 0 +#define DEBUG 1 #define VERSION VER2(0,5) #include #include @@ -13,16 +13,17 @@ #include #include "uhci.h" #include +#include // === CONSTANTS === #define MAX_CONTROLLERS 4 -#define NUM_TDs 1024 +//#define NUM_TDs 1024 +#define NUM_TDs 64 // === PROTOTYPES === int UHCI_Initialise(char **Arguments); void UHCI_Cleanup(); tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont); -tUHCI_TD *UHCI_int_GetTDFromPhys(tPAddr PAddr); 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); @@ -31,6 +32,7 @@ void *UHCI_SendSetup(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbDa int UHCI_IsTransferComplete(void *Ptr, void *Handle); int UHCI_Int_InitHost(tUHCI_Controller *Host); void UHCI_CheckPortUpdate(void *Ptr); +void UHCI_int_InterruptThread(void *Unused); void UHCI_InterruptHandler(int IRQ, void *Ptr); // static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value); @@ -49,6 +51,7 @@ tUSBHostDef gUHCI_HostDef = { .IsOpComplete = UHCI_IsTransferComplete, .CheckPorts = UHCI_CheckPortUpdate }; +tSemaphore gUHCI_InterruptSempahore; // === CODE === /** @@ -62,6 +65,18 @@ int UHCI_Initialise(char **Arguments) ENTER(""); + // Initialise with no maximum value + Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue"); + + if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 ) + { + LEAVE('i', MODULE_ERR_NOTNEEDED); + 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 ) { @@ -102,11 +117,6 @@ int UHCI_Initialise(char **Arguments) i ++; } - if(i == 0) { - LEAVE('i', MODULE_ERR_NOTNEEDED); - return MODULE_ERR_NOTNEEDED; - } - if(i == MAX_CONTROLLERS) { Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest"); } @@ -124,87 +134,65 @@ void UHCI_Cleanup() tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont) { - int i; - for(i = 0; i < NUM_TDs; i ++) + static tMutex lock; + Mutex_Acquire( &lock ); + for( int i = 0; i < NUM_TDs; i ++ ) { - if(gaUHCI_TDPool[i].Link == 0) { + if(gaUHCI_TDPool[i]._info.bActive == 0) + { gaUHCI_TDPool[i].Link = 1; - gaUHCI_TDPool[i].Control = 1 << 23; + gaUHCI_TDPool[i].Control = (1 << 23); + gaUHCI_TDPool[i]._info.bActive = 1; + gaUHCI_TDPool[i]._info.bComplete = 0; + Mutex_Release( &lock ); return &gaUHCI_TDPool[i]; } - // Still in use? Skip - if( gaUHCI_TDPool[i].Control & (1 << 23) ) - continue ; - // Is there a callback on it? Skip - if( gaUHCI_TDPool[i]._info.Callback ) - continue ; - // TODO: Garbage collect, but that means removing from the list too - #if 0 - // Ok, this is actually unused - gaUHCI_TDPool[i].Link = 1; - gaUHCI_TDPool[i].Control = 1 << 23; - return &gaUHCI_TDPool[i]; - #endif } + Mutex_Release( &lock ); return NULL; } -tUHCI_TD *UHCI_int_GetTDFromPhys(tPAddr PAddr) -{ - // TODO: Fix this to work with a non-contiguous pool - static tPAddr td_pool_base; - const int pool_size = NUM_TDs; - int offset; - if(!td_pool_base) td_pool_base = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool ); - offset = (PAddr - td_pool_base) / sizeof(gaUHCI_TDPool[0]); - if( offset < 0 || offset >= pool_size ) - { - Log_Error("UHCI", "TD PAddr %P not from pool", PAddr); - return NULL; - } - return gaUHCI_TDPool + offset; -} - void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD) { - int next_frame; - tUHCI_TD *prev_td; - Uint32 link; + static tMutex lock; // TODO: Should I use a shortlock (avoid being preempted) - ENTER("pCont pTD", Cont, TD); + Mutex_Acquire(&lock); + + #if 0 + int next_frame; - // TODO: How to handle FRNUM incrementing while we are in this function? next_frame = (_InWord(Cont, FRNUM) + 2) & (1024-1); + + TD->Control |= (1 << 24); // Ensure that there is an interrupt for each used frame - // Empty list - if( Cont->FrameList[next_frame] & 1 ) - { - // TODO: Ensure 32-bit paddr - Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD ); - TD->Control |= (1 << 24); // Ensure that there is an interrupt for each used frame - LOG("next_frame = %i", next_frame); - LEAVE('-'); - return; - } + TD->Link = Cont->FrameList[next_frame]; + Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD ); + #else - // Find the end of the list - link = Cont->FrameList[next_frame]; - do { - prev_td = UHCI_int_GetTDFromPhys(link); - if(!prev_td) { - Log_Error("UHCI", "ERROR: TD list is bad"); - TD->Link = 1; - LEAVE('-'); - return ; - } - link = prev_td->Link; - } while( !(link & 1) ); + // TODO: Support other QHs + tUHCI_QH *qh = &Cont->BulkQH; - // Append - prev_td->Link = MM_GetPhysAddr( (tVAddr)TD ); + // Ensure that there is an interrupt for each used frame + TD->Control |= (1 << 24); - LOG("next_frame = %i, prev_td = %p", next_frame, prev_td); - LEAVE('-'); + // Stop controller + _OutWord( Cont, USBCMD, 0x0000 ); + + // Add + TD->Link = 1; + if( qh->Child & 1 ) { + qh->Child = MM_GetPhysAddr( (tVAddr)TD ); + } + else { + qh->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ); + } + qh->_LastItem = TD; + + // Reenable controller + _OutWord( Cont, USBCMD, 0x0001 ); + #endif + + Mutex_Release(&lock); } /** @@ -218,8 +206,12 @@ void *UHCI_int_SendTransaction( tUSBHostCb Cb, void *CbData, void *Data, size_t Length) { tUHCI_TD *td; + tUHCI_ExtraTDInfo *info = NULL; - if( Length > 0x400 ) return NULL; // Controller allows up to 0x500, but USB doesn't + if( Length > 0x400 ) { + Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length); + return NULL; // Controller allows up to 0x500, but USB doesn't + } td = UHCI_int_AllocateTD(Cont); @@ -229,7 +221,9 @@ void *UHCI_int_SendTransaction( return NULL; } - td->Link = 1; + LOG("TD %p %i bytes, Type %x to 0x%x", + td, Length, Type, Addr); + td->Control = (Length - 1) & 0x7FF; td->Control |= (1 << 23); td->Token = ((Length - 1) & 0x7FF) << 21; @@ -238,30 +232,57 @@ void *UHCI_int_SendTransaction( td->Token |= ((Addr/16) & 0xFF) << 8; td->Token |= Type; - // TODO: Ensure 32-bit paddr - if( ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE ) { - Log_Warning("UHCI", "TODO: Support non single page transfers (%x + %x > %x)", - (tVAddr)Data & (PAGE_SIZE-1), Length, PAGE_SIZE - ); - // TODO: Need to enable IOC to copy the data back -// td->BufferPointer = - td->_info.bCopyData = 1; - return NULL; + if( + ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE + #if PHYS_BITS > 32 + || MM_GetPhysAddr( (tVAddr)Data ) >> 32 + #endif + ) + { + td->BufferPointer = MM_AllocPhysRange(1, 32); + + LOG("Allocated page %x", td->BufferPointer); + + if( Type == 0x69 ) // IN token + { + LOG("Relocated IN"); + info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 ); + info->Offset = ((tVAddr)Data & (PAGE_SIZE-1)); + info->FirstPage = MM_GetPhysAddr( (tVAddr)Data ); + info->SecondPage = MM_GetPhysAddr( (tVAddr)Data + Length - 1 ); + } + else + { + LOG("Relocated OUT/SETUP"); + tVAddr ptr = MM_MapTemp(td->BufferPointer); + memcpy( (void*)ptr, Data, Length ); + MM_FreeTemp(ptr); + td->Control |= (1 << 24); + } + td->_info.bFreePointer = 1; } - else { + else + { td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data ); - td->_info.bCopyData = 0; + td->_info.bFreePointer = 0; } // Interrupt on completion - if( Cb ) { - td->Control |= (1 << 24); + if( Cb ) + { + if( !info ) + info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 ); LOG("IOC Cb=%p CbData=%p", Cb, CbData); - td->_info.Callback = Cb; // NOTE: if ERRPTR then the TD is kept allocated until checked - td->_info.CallbackPtr = CbData; + // NOTE: if ERRPTR then the TD is kept allocated until checked + info->Callback = Cb; + info->CallbackPtr = CbData; } - td->_info.DataPtr = Data; + if( info ) { + LOG("info = %p", info); + td->Control |= (1 << 24); + td->_info.ExtraInfo = info; + } UHCI_int_AppendTD(Cont, td); @@ -286,13 +307,27 @@ void *UHCI_SendSetup(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbDa int UHCI_IsTransferComplete(void *Ptr, void *Handle) { tUHCI_TD *td = Handle; - int ret; - ret = !(td->Control & (1 << 23)); - if(ret) { - td->_info.Callback = NULL; + #if DEBUG + tUHCI_Controller *Cont = &gUHCI_Controllers[0]; + LOG("%p->Control = %x", 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; } - return ret; } // === INTERNAL FUNCTIONS === @@ -306,7 +341,7 @@ int UHCI_Int_InitHost(tUHCI_Controller *Host) ENTER("pHost", Host); _OutWord( Host, USBCMD, 4 ); // GRESET - // TODO: Wait for at least 10ms + Time_Delay(10); _OutWord( Host, USBCMD, 0 ); // GRESET // Allocate Frame List @@ -318,11 +353,21 @@ int UHCI_Int_InitHost(tUHCI_Controller *Host) LEAVE('i', -1); return -1; } + + // TODO: Handle QHs not being in a 32-bit paddr range + // Need another page, probably get some more TDs from it too + + // 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; + LOG("Allocated frame list 0x%x (0x%x)", Host->FrameList, Host->PhysFrameList); for( int i = 0; i < 1024; i ++ ) - Host->FrameList[i] = 1; // Clear List (Disabling all entries) - - //! \todo Properly fill frame list + Host->FrameList[i] = MM_GetPhysAddr( (tVAddr)&Host->InterruptQH ) | 2; // Set frame length to 1 ms _OutByte( Host, SOFMOD, 64 ); @@ -381,58 +426,115 @@ void UHCI_CheckPortUpdate(void *Ptr) } } -void UHCI_InterruptHandler(int IRQ, void *Ptr) +void UHCI_int_InterruptThread(void *Unused) { - tUHCI_Controller *Host = Ptr; - int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF; - Uint16 status = _InWord(Host, USBSTS); -// Log_Debug("UHCI", "UHIC Interrupt, status = 0x%x, frame = %i", status, frame); - - // Interrupt-on-completion - if( status & 1 ) + Threads_SetName("UHCI Interrupt Handler"); + for( ;; ) { - tPAddr link; - - for( int i = 0; i < 10; i ++ ) + LOG("zzzzz...."); + // 0 = Take all + Semaphore_Wait(&gUHCI_InterruptSempahore, 0); + LOG("Huh?"); + + for( int i = 0; i < NUM_TDs; i ++ ) { - link = Host->FrameList[frame]; - Host->FrameList[frame] = 1; - while( link && !(link & 1) ) + int byte_count; + tUHCI_ExtraTDInfo *info; + tUHCI_TD *td; + + td = &gaUHCI_TDPool[i]; + info = td->_info.ExtraInfo; + + // Skip completely inactive TDs + if( td->_info.bActive == 0 ) continue ; + // Skip ones that are still in use + if( td->Control & (1 << 23) ) continue ; + + // If no callback/alt buffer, mark as free and move on + if( td->_info.ExtraInfo ) { - tUHCI_TD *td = UHCI_int_GetTDFromPhys(link); - int byte_count = (td->Control&0x7FF)+1; - LOG("link = 0x%x, td = %p, byte_count = %i", link, td, byte_count); - // Handle non-page aligned destination - // TODO: This will break if the destination is not in global memory - if(td->_info.bCopyData) + // Get size of transfer + byte_count = (td->Control & 0x7FF)+1; + + // Handle non page-aligned destination (with a > 32-bit paddr) + if(info->FirstPage) + { + char *src, *dest; + int src_ofs = td->BufferPointer & (PAGE_SIZE-1); + src = (void *) MM_MapTemp(td->BufferPointer); + dest = (void *) MM_MapTemp(info->FirstPage); + // Check for a single page transfer + if( byte_count + info->Offset <= PAGE_SIZE ) + { + LOG("Single page copy %P to %P of %p", + td->BufferPointer, info->FirstPage, td); + memcpy(dest + info->Offset, src + src_ofs, byte_count); + } + else + { + // Multi-page + LOG("Multi page copy %P to (%P,%P) of %p", + td->BufferPointer, info->FirstPage, info->SecondPage, td); + int part_len = PAGE_SIZE - info->Offset; + memcpy(dest + info->Offset, src + src_ofs, part_len); + MM_FreeTemp( (tVAddr)dest ); + dest = (void *) MM_MapTemp(info->SecondPage); + memcpy(dest, src + src_ofs + part_len, byte_count - part_len); + } + MM_FreeTemp( (tVAddr)src ); + MM_FreeTemp( (tVAddr)dest ); + } + + // Don't mark as inactive, the check should do that + if( info->Callback == INVLPTR ) { - void *ptr = (void*)MM_MapTemp(td->BufferPointer); - Log_Debug("UHCI", "td->_info.DataPtr = %p", td->_info.DataPtr); - memcpy(td->_info.DataPtr, ptr, byte_count); - MM_FreeTemp((tVAddr)ptr); + 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(td->_info.Callback && td->_info.Callback != INVLPTR) + if( info->Callback != NULL ) { - LOG("Calling cb %p", td->_info.Callback); - td->_info.Callback(td->_info.CallbackPtr, td->_info.DataPtr, byte_count); - td->_info.Callback = NULL; + LOG("Calling cb %p", info->Callback); + void *ptr = (void *) MM_MapTemp( td->BufferPointer ); + info->Callback( info->CallbackPtr, ptr, byte_count ); + MM_FreeTemp( (tVAddr)ptr ); } - link = td->Link; - if( td->_info.Callback != INVLPTR ) - td->Link = 0; + + // Clean up info + free( info ); + td->_info.ExtraInfo = NULL; } - - if(frame == 0) - frame = 0x3ff; - else - frame --; + + if( td->_info.bFreePointer ) + MM_DerefPhys( td->BufferPointer ); + + // Clean up + td->_info.bActive = 0; + LOG("Cleaned %p", td); } - -// Host->LastCleanedFrame = frame; + } +} + +void UHCI_InterruptHandler(int IRQ, void *Ptr) +{ + tUHCI_Controller *Host = Ptr; +// int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF; + Uint16 status = _InWord(Host, USBSTS); + + // Interrupt-on-completion + if( status & 1 ) + { + // TODO: Support isochronous transfers (will need updating the frame pointer) + Semaphore_Signal(&gUHCI_InterruptSempahore, 1); } - LOG("status = 0x%02x", status); + LOG("status = 0x%04x", status); _OutWord(Host, USBSTS, status); } diff --git a/KernelLand/Modules/USB/UHCI/uhci.h b/KernelLand/Modules/USB/UHCI/uhci.h index a93459a9..40b589b1 100644 --- a/KernelLand/Modules/USB/UHCI/uhci.h +++ b/KernelLand/Modules/USB/UHCI/uhci.h @@ -8,53 +8,20 @@ // === TYPES === typedef struct sUHCI_Controller tUHCI_Controller; +typedef struct sUHCI_ExtraTDInfo tUHCI_ExtraTDInfo; + typedef struct sUHCI_TD tUHCI_TD; typedef struct sUHCI_QH tUHCI_QH; // === STRUCTURES === -struct sUHCI_Controller +struct sUHCI_ExtraTDInfo { - /** - * \brief PCI Device ID - */ - Uint16 PciId; + int Offset; + tPAddr FirstPage; + tPAddr SecondPage; - /** - * \brief IO Base Address - */ - Uint16 IOBase; - - /** - * \brief Memory Mapped-IO base address - */ - Uint16 *MemIOMap; - - /** - * \brief IRQ Number assigned to the device - */ - int IRQNum; - - /** - * \brief Number of the last frame to be cleaned - */ - int LastCleanedFrame; - - /** - * \brief Frame list - * - * 31:4 - Frame Pointer - * 3:2 - Reserved - * 1 - QH/TD Selector - * 0 - Terminate (Empty Pointer) - */ - Uint32 *FrameList; - - /** - * \brief Physical Address of the Frame List - */ - tPAddr PhysFrameList; - - tUSBHub *RootHub; + tUSBHostCb Callback; + void *CallbackPtr; }; struct sUHCI_TD @@ -116,10 +83,10 @@ struct sUHCI_TD struct { - tUSBHostCb Callback; - void *CallbackPtr; - void *DataPtr; - int bCopyData; + tUHCI_ExtraTDInfo *ExtraInfo; + char bActive; // Allocated + char bComplete; // Job complete + char bFreePointer; // Free \a BufferPointer once done } _info; } __attribute__((aligned(16))); @@ -145,6 +112,61 @@ struct sUHCI_QH * 0 - Terminate (Last in List) */ Uint32 Child; + + /* + * \note Area for software use + * \brief Last TD in this list, used to add things to the end + */ + tUHCI_TD *_LastItem; +} __attribute__((aligned(16))); + +struct sUHCI_Controller +{ + /** + * \brief PCI Device ID + */ + Uint16 PciId; + + /** + * \brief IO Base Address + */ + Uint16 IOBase; + + /** + * \brief Memory Mapped-IO base address + */ + Uint16 *MemIOMap; + + /** + * \brief IRQ Number assigned to the device + */ + int IRQNum; + + /** + * \brief Number of the last frame to be cleaned + */ + int LastCleanedFrame; + + /** + * \brief Frame list + * + * 31:4 - Frame Pointer + * 3:2 - Reserved + * 1 - QH/TD Selector + * 0 - Terminate (Empty Pointer) + */ + Uint32 *FrameList; + + /** + * \brief Physical Address of the Frame List + */ + tPAddr PhysFrameList; + + tUSBHub *RootHub; + + tUHCI_QH InterruptQH; + tUHCI_QH ControlQH; + tUHCI_QH BulkQH; }; // === ENUMERATIONS ===