3 * - By John Hodge (thePowersGang)
5 * Universal Host Controller Interface
8 #define VERSION VER2(0,5)
16 #include <semaphore.h>
19 #define MAX_CONTROLLERS 4
20 //#define NUM_TDs 1024
21 #define NUM_TDs (PAGE_SIZE/sizeof(tUHCI_TD))
22 #define MAX_PACKET_SIZE 0x400
25 #define PID_SETUP 0x2D
28 int UHCI_Initialise(char **Arguments);
30 int UHCI_int_InitHost(tUHCI_Controller *Host);
32 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
33 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD);
34 tUHCI_TD *UHCI_int_CreateTD(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
36 void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
37 void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
38 void UHCI_StopInterrupt(void *Ptr, void *Handle);
39 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length);
40 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
41 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
42 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
43 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
45 void UHCI_CheckPortUpdate(void *Ptr);
46 void UHCI_int_InterruptThread(void *Unused);
47 void UHCI_InterruptHandler(int IRQ, void *Ptr);
49 static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value);
50 static void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value);
51 static void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value);
52 static Uint16 _InWord(tUHCI_Controller *Host, int Reg);
55 MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
56 tUHCI_TD *gaUHCI_TDPool;
57 tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS];
58 tUSBHostDef gUHCI_HostDef = {
59 .InterruptIN = UHCI_InterruptIN,
60 .InterruptOUT = UHCI_InterruptOUT,
61 .StopInterrupt = UHCI_StopInterrupt,
63 .ControlSETUP = UHCI_ControlSETUP,
64 .ControlIN = UHCI_ControlIN,
65 .ControlOUT = UHCI_ControlOUT,
67 .BulkOUT = UHCI_BulkOUT,
68 .BulkIN = UHCI_BulkIN,
70 .CheckPorts = UHCI_CheckPortUpdate
72 tSemaphore gUHCI_InterruptSempahore;
76 * \fn int UHCI_Initialise()
77 * \brief Called to initialise the UHCI Driver
79 int UHCI_Initialise(char **Arguments)
86 // Initialise with no maximum value
87 Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
89 if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 )
91 LEAVE('i', MODULE_ERR_NOTNEEDED);
92 return MODULE_ERR_NOTNEEDED;
97 gaUHCI_TDPool = (void *) MM_AllocDMA(1, 32, &tmp);
100 // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
101 while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
103 tUHCI_Controller *cinfo = &gUHCI_Controllers[i];
105 // NOTE: Check "protocol" from PCI?
108 base_addr = PCI_GetBAR(id, 4);
112 cinfo->IOBase = base_addr & ~1;
113 cinfo->MemIOMap = NULL;
117 cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
119 cinfo->IRQNum = PCI_GetIRQ(id);
121 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
122 id, base_addr, cinfo->IRQNum);
124 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
127 ret = UHCI_int_InitHost(cinfo);
134 // Spin off interrupt handling thread
135 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
138 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
139 LOG("cinfo->RootHub = %p", cinfo->RootHub);
144 if(i == MAX_CONTROLLERS) {
145 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
147 LEAVE('i', MODULE_ERR_OK);
148 return MODULE_ERR_OK;
152 * \fn void UHCI_Cleanup()
153 * \brief Called just before module is unloaded
160 * \brief Initialises a UHCI host controller
161 * \param Host Pointer - Host to initialise
163 int UHCI_int_InitHost(tUHCI_Controller *Host)
165 ENTER("pHost", Host);
167 // - 1 Page, 32-bit address
168 // - 1 page = 1024 4 byte entries
169 Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
170 if( !Host->FrameList ) {
171 Log_Warning("UHCI", "Unable to allocate frame list, aborting");
176 Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
177 if( !Host->TDQHPage ) {
179 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
185 // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
186 const int dest_offsets[] = {
187 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,
188 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31
190 for( int i = 0; i < 1024; i ++ ) {
191 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH );
192 Host->FrameList[i] = addr | 2;
194 for( int i = 0; i < 64; i ++ ) {
195 int ofs = dest_offsets[ i & (32-1) ];
196 Uint32 addr = Host->PhysTDQHPage + ofs * sizeof(tUHCI_QH);
197 Host->FrameList[ 0 + i*4] = addr | 2;
198 Host->FrameList[256 + i*4] = addr | 2;
199 Host->FrameList[512 + i*4] = addr | 2;
200 Host->FrameList[768 + i*4] = addr | 2;
203 // Build up interrupt binary tree
205 tUHCI_QH *dest = Host->TDQHPage->InterruptQHs;
206 Uint32 destphys = Host->PhysTDQHPage;
208 // Set up next pointer to index to i/2 in the next step
209 for( int _count = 64; _count > 1; _count /= 2 )
211 for( int i = 0; i < _count; i ++ ) {
212 dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
215 dest += _count; destphys += _count * sizeof(tUHCI_QH);
217 // Skip padding, and move to control QH
218 dest->Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
222 // Set up control and bulk queues
223 Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
224 Host->TDQHPage->ControlQH.Child = 1;
225 Host->TDQHPage->BulkQH.Next = 1;
226 Host->TDQHPage->BulkQH.Child = 1;
229 _OutWord( Host, USBCMD, 4 );
231 _OutWord( Host, USBCMD, 0 );
233 // Allocate Frame List
234 // Set frame length to 1 ms
235 _OutByte( Host, SOFMOD, 64 );
238 _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
239 _OutWord( Host, FRNUM, 0 );
242 _OutWord( Host, USBINTR, 0x000F );
243 PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
246 _OutWord( Host, USBCMD, 0x0001 );
252 // --------------------------------------------------------------------
253 // TDs and QH Allocation/Appending
254 // --------------------------------------------------------------------
255 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
258 Mutex_Acquire( &lock );
259 for( int i = 0; i < NUM_TDs; i ++ )
261 if(gaUHCI_TDPool[i]._info.bActive == 0)
263 gaUHCI_TDPool[i].Link = 1;
264 gaUHCI_TDPool[i].Control = (1 << 23);
265 gaUHCI_TDPool[i]._info.bActive = 1;
266 gaUHCI_TDPool[i]._info.QueueIndex = 128;
267 Mutex_Release( &lock );
268 return &gaUHCI_TDPool[i];
271 Mutex_Release( &lock );
275 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
277 static tMutex lock; // TODO: Should I use a shortlock (avoid being preempted)
279 Mutex_Acquire(&lock);
281 // Ensure that there is an interrupt for each used frame
282 TD->Control |= (1 << 24);
283 TD->_info.QueueIndex = ((tVAddr)QH - (tVAddr)Cont->TDQHPage->InterruptQHs) / sizeof(tUHCI_QH);
286 _OutWord( Cont, USBCMD, 0x0000 );
290 if( QH->Child & 1 ) {
291 QH->Child = MM_GetPhysAddr( (tVAddr)TD );
295 QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
299 // Reenable controller
300 _OutWord( Cont, USBCMD, 0x0001 );
303 LOG("QH(%p)->Child = %x", QH, QH->Child);
304 LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
306 Mutex_Release(&lock);
310 * \brief Send a transaction to the USB bus
311 * \param Cont Controller pointer
312 * \param Addr Function Address * 16 + Endpoint
313 * \param bTgl Data toggle value
315 tUHCI_TD *UHCI_int_CreateTD(
316 tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
317 tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
320 tUHCI_ExtraTDInfo *info = NULL;
322 if( Length > 0x400 ) {
323 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
324 return NULL; // Controller allows up to 0x500, but USB doesn't
327 td = UHCI_int_AllocateTD(Cont);
329 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
333 LOG("TD %p %i bytes, Type %x to 0x%x",
334 td, Length, Type, Addr);
336 td->Control = (Length - 1) & 0x7FF;
337 td->Control |= (1 << 23); // Active set
338 td->Control |= (3 << 27); // 3 retries
339 td->Token = ((Length - 1) & 0x7FF) << 21;
340 td->Token |= (bTgl & 1) << 19;
341 td->Token |= (Addr & 0xF) << 15;
342 td->Token |= ((Addr/16) & 0xFF) << 8;
346 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
348 || MM_GetPhysAddr( (tVAddr)Data ) >> 32
352 td->BufferPointer = MM_AllocPhysRange(1, 32);
354 LOG("Allocated page %x", td->BufferPointer);
356 if( Type == 0x69 ) // IN token
359 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
360 info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
361 info->FirstPage = MM_GetPhysAddr( (tVAddr)Data );
362 info->SecondPage = MM_GetPhysAddr( (tVAddr)Data + Length - 1 );
366 LOG("Relocated OUT/SETUP");
367 tVAddr ptr = MM_MapTemp(td->BufferPointer);
368 memcpy( (void*)ptr, Data, Length );
370 td->Control |= (1 << 24);
372 td->_info.bFreePointer = 1;
376 td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
377 td->_info.bFreePointer = 0;
380 // Interrupt on completion
384 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
385 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
387 info->CallbackPtr = CbData;
391 LOG("info = %p", info);
392 td->Control |= (1 << 24);
393 td->_info.ExtraInfo = info;
399 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
402 const int qh_offsets[] = { 0, 64, 96, 112, 120, 124, 126};
403 // const int qh_sizes[] = {64, 32, 16, 8, 4, 2, 1};
406 if( Period < 0 ) return ;
407 if( Period > 256 ) Period = 256;
409 // Get the log base2 of the period
411 while( Period >>= 1 ) period_slot ++;
413 // Adjust for the 4ms minimum period
414 if( period_slot < 2 ) period_slot = 0;
415 else period_slot -= 2;
417 TD->_info.QueueIndex = qh_offsets[period_slot]; // Actually re-filled in _AppendTD, but meh
418 qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
419 // TODO: Find queue with lowest load
421 LOG("period_slot = %i, QueueIndex = %i",
422 period_slot, TD->_info.QueueIndex);
424 // Stop any errors causing the TD to stop (NAK will error)
425 // - If the device goes away, the interrupt should be stopped anyway
426 TD->Control &= ~(3 << 27);
428 UHCI_int_AppendTD(Cont, qh, TD);
431 void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
435 if( Period < 0 ) return NULL;
437 ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
438 Ptr, Dest, Period, Cb, CbData, Buf, Length);
440 // TODO: Data toggle?
441 td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length);
442 if( !td ) return NULL;
444 UHCI_int_SetInterruptPoll(Ptr, td, Period);
449 // TODO: Does interrupt OUT make sense?
450 void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
454 if( Period < 0 ) return NULL;
456 ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
457 Ptr, Dest, Period, Cb, CbData, Buf, Length);
459 // TODO: Data toggle?
460 td = UHCI_int_CreateTD(Ptr, Dest, PID_OUT, 0, Cb, CbData, Buf, Length);
461 if( !td ) return NULL;
463 UHCI_int_SetInterruptPoll(Ptr, td, Period);
469 void UHCI_StopInterrupt(void *Ptr, void *Handle)
471 // TODO: Stop interrupt transaction
472 Log_Error("UHCI", "TODO: Implement UHCI_StopInterrupt");
475 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
477 tUHCI_Controller *Cont = Ptr;
478 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
481 ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
483 td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
484 UHCI_int_AppendTD(Cont, qh, td);
490 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
492 tUHCI_Controller *Cont = Ptr;
493 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
496 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
498 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
499 UHCI_int_AppendTD(Cont, qh, td);
504 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
506 tUHCI_Controller *Cont = Ptr;
507 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
510 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
512 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, !!Tgl, Cb, CbData, Data, Length);
513 UHCI_int_AppendTD(Cont, qh, td);
519 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
521 tUHCI_Controller *Cont = Ptr;
522 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
526 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
528 while( Length > MAX_PACKET_SIZE )
530 LOG("MaxPacket (rem = %i)", Length);
531 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, MAX_PACKET_SIZE);
532 UHCI_int_AppendTD(Cont, qh, td);
535 Length -= MAX_PACKET_SIZE;
536 src += MAX_PACKET_SIZE;
540 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length);
541 UHCI_int_AppendTD(Cont, qh, td);
546 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
548 tUHCI_Controller *Cont = Ptr;
549 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
553 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
554 while( Length > MAX_PACKET_SIZE )
556 LOG("MaxPacket (rem = %i)", Length);
557 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, MAX_PACKET_SIZE);
558 UHCI_int_AppendTD(Cont, qh, td);
561 Length -= MAX_PACKET_SIZE;
562 dst += MAX_PACKET_SIZE;
566 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length);
567 UHCI_int_AppendTD(Cont, qh, td);
573 // === INTERNAL FUNCTIONS ===
574 void UHCI_CheckPortUpdate(void *Ptr)
576 tUHCI_Controller *Host = Ptr;
578 for( int i = 0; i < 2; i ++ )
580 int port = PORTSC1 + i*2;
583 status = _InWord(Host, port);
584 // Check for port change
585 if( !(status & 0x0002) ) continue;
586 _OutWord(Host, port, 0x0002);
588 // Check if the port is connected
591 // Tell the USB code it's gone.
592 USB_DeviceDisconnected(Host->RootHub, i);
597 LOG("Port %i has something", i);
598 // Reset port (set bit 9)
600 _OutWord(Host, port, 0x0200);
601 Time_Delay(50); // 50ms delay
602 _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
605 Time_Delay(50); // 50ms delay
606 _OutWord(Host, port, _InWord(Host, port) | 0x0004);
607 // Tell USB there's a new device
608 USB_DeviceConnected(Host->RootHub, i);
613 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
615 if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
617 PAddr -= Controller->PhysTDQHPage;
618 PAddr -= (128+2)*sizeof(tUHCI_QH);
619 if( PAddr > PAGE_SIZE ) return NULL; // Wrapping will bring above page size
620 PAddr /= sizeof(tUHCI_TD);
621 return &Controller->TDQHPage->LocalTDPool[PAddr];
625 tPAddr global_pool = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
627 if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE ) return NULL;
629 PAddr -= global_pool;
630 PAddr /= sizeof(tUHCI_TD);
631 return &gaUHCI_TDPool[PAddr];
634 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
636 tUHCI_TD *td, *prev = NULL;
639 // Disable controller
640 _OutWord( Cont, USBCMD, 0x0000 );
644 while( !(cur_td & 1) )
646 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
648 Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
654 if( td->Control & (1 << 23) ) {
659 LOG("Removed %p from QH %p", td, QH);
662 QH->Child = td->Link;
664 prev->Link = td->Link;
668 // re-enable controller
669 _OutWord( Cont, USBCMD, 0x0001 );
672 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
674 int byte_count = (TD->Control & 0x7FF)+1;
675 tUHCI_ExtraTDInfo *info = TD->_info.ExtraInfo;
677 // Handle non page-aligned destination (or with a > 32-bit paddr)
678 // TODO: Needs fixing for alignment issues
682 int src_ofs = TD->BufferPointer & (PAGE_SIZE-1);
683 src = (void *) MM_MapTemp(TD->BufferPointer);
684 dest = (void *) MM_MapTemp(info->FirstPage);
685 // Check for a single page transfer
686 if( byte_count + info->Offset <= PAGE_SIZE )
688 LOG("Single page copy %P to %P of %p",
689 td->BufferPointer, info->FirstPage, td);
690 memcpy(dest + info->Offset, src + src_ofs, byte_count);
695 LOG("Multi page copy %P to (%P,%P) of %p",
696 td->BufferPointer, info->FirstPage, info->SecondPage, td);
697 int part_len = PAGE_SIZE - info->Offset;
698 memcpy(dest + info->Offset, src + src_ofs, part_len);
699 MM_FreeTemp( (tVAddr)dest );
700 dest = (void *) MM_MapTemp(info->SecondPage);
701 memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
703 MM_FreeTemp( (tVAddr)src );
704 MM_FreeTemp( (tVAddr)dest );
708 if( info->Callback != NULL )
710 LOG("Calling cb %p", info->Callback);
711 void *ptr = (void *) MM_MapTemp( TD->BufferPointer );
712 info->Callback( info->CallbackPtr, ptr, byte_count );
713 MM_FreeTemp( (tVAddr)ptr );
718 TD->_info.ExtraInfo = NULL;
721 void UHCI_int_InterruptThread(void *Pointer)
723 tUHCI_Controller *Cont = Pointer;
724 Threads_SetName("UHCI Interrupt Handler");
729 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
732 for( int i = 0; i < NUM_TDs; i ++ )
736 td = &gaUHCI_TDPool[i];
738 // Skip completely inactive TDs
739 if( td->_info.bActive == 0 ) continue ;
740 // Skip ones that are still in use
741 if( td->Control & (1 << 23) ) continue ;
743 // If no callback/alt buffer, mark as free and move on
744 if( td->_info.ExtraInfo )
746 UHCI_int_HandleTDComplete(Cont, td);
749 // Handle rescheduling of interrupt TDs
750 if( td->_info.QueueIndex <= 127 )
752 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.QueueIndex);
753 // TODO: Flip toggle?
754 td->Control |= (1 << 23);
755 // Add back into controller's interrupt list
758 Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
765 if( td->_info.bFreePointer )
766 MM_DerefPhys( td->BufferPointer );
769 if( td->Control & 0x00FF0000 ) {
770 LOG("td->control(Status) = %s%s%s%s%s%s%s%s",
771 td->Control & (1 << 23) ? "Active " : "",
772 td->Control & (1 << 22) ? "Stalled " : "",
773 td->Control & (1 << 21) ? "Data Buffer Error " : "",
774 td->Control & (1 << 20) ? "Babble " : "",
775 td->Control & (1 << 19) ? "NAK " : "",
776 td->Control & (1 << 18) ? "CRC Error, " : "",
777 td->Control & (1 << 17) ? "Bitstuff Error, " : "",
778 td->Control & (1 << 16) ? "Reserved " : ""
780 // Clean up QH (removing all inactive entries)
781 UHCI_int_CleanQH(Cont, Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex);
785 LOG("Cleaned %p (->Control = %x)", td, td->Control);
786 td->_info.bActive = 0;
791 void UHCI_InterruptHandler(int IRQ, void *Ptr)
793 tUHCI_Controller *Host = Ptr;
794 // int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
795 Uint16 status = _InWord(Host, USBSTS);
797 // Interrupt-on-completion
800 // TODO: Support isochronous transfers (will need updating the frame pointer)
801 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
804 LOG("status = 0x%04x", status);
805 _OutWord(Host, USBSTS, status);
808 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
811 ((Uint8*)Host->MemIOMap)[Reg] = Value;
813 outb(Host->IOBase + Reg, Value);
816 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
819 Host->MemIOMap[Reg/2] = Value;
821 outw(Host->IOBase + Reg, Value);
824 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
827 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
829 outd(Host->IOBase + Reg, Value);
832 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
835 return Host->MemIOMap[Reg/2];
837 return inw(Host->IOBase + Reg);