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);
285 TD->Control &= ~0x7FF;
286 TD->Control |= (TD->Token >> 21) & 0x7FF;
289 _OutWord( Cont, USBCMD, 0x0000 );
293 if( QH->Child & 1 ) {
294 QH->Child = MM_GetPhysAddr( (tVAddr)TD );
298 QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
302 // Reenable controller
303 _OutWord( Cont, USBCMD, 0x0001 );
306 LOG("QH(%p)->Child = %x", QH, QH->Child);
307 LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
309 Mutex_Release(&lock);
313 * \brief Send a transaction to the USB bus
314 * \param Cont Controller pointer
315 * \param Addr Function Address * 16 + Endpoint
316 * \param bTgl Data toggle value
318 tUHCI_TD *UHCI_int_CreateTD(
319 tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
320 tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
323 tUHCI_ExtraTDInfo *info = NULL;
325 if( Length > 0x400 ) {
326 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
327 return NULL; // Controller allows up to 0x500, but USB doesn't
330 td = UHCI_int_AllocateTD(Cont);
332 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
336 LOG("TD %p %i bytes, Type %x to 0x%x",
337 td, Length, Type, Addr);
339 td->Control = (Length - 1) & 0x7FF;
340 td->Control |= (1 << 23); // Active set
341 td->Control |= (3 << 27); // 3 retries
342 td->Token = ((Length - 1) & 0x7FF) << 21;
343 td->Token |= (bTgl & 1) << 19;
344 td->Token |= (Addr & 0xF) << 15;
345 td->Token |= ((Addr/16) & 0xFF) << 8;
349 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
351 || MM_GetPhysAddr( (tVAddr)Data ) >> 32
355 td->BufferPointer = MM_AllocPhysRange(1, 32);
357 LOG("Allocated page %x", td->BufferPointer);
359 if( Type == 0x69 ) // IN token
362 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
363 info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
364 info->FirstPage = MM_GetPhysAddr( (tVAddr)Data );
365 info->SecondPage = MM_GetPhysAddr( (tVAddr)Data + Length - 1 );
369 LOG("Relocated OUT/SETUP");
370 tVAddr ptr = MM_MapTemp(td->BufferPointer);
371 memcpy( (void*)ptr, Data, Length );
373 td->Control |= (1 << 24);
375 td->_info.bFreePointer = 1;
379 td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
380 td->_info.bFreePointer = 0;
383 // Interrupt on completion
387 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
388 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
390 info->CallbackPtr = CbData;
394 LOG("info = %p", info);
395 td->Control |= (1 << 24);
396 td->_info.ExtraInfo = info;
402 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
405 const int qh_offsets[] = {126, 124, 120, 112, 96, 64, 0};
406 // const int qh_sizes[] = { 1, 2, 4, 8, 16, 32, 64};
409 if( Period < 0 ) return ;
410 if( Period > 256 ) Period = 256;
411 if( Period == 255 ) Period = 256;
413 // Get the log base2 of the period
415 while( Period >>= 1 ) period_slot ++;
417 // Adjust for the 4ms minimum period
418 if( period_slot < 2 ) period_slot = 0;
419 else period_slot -= 2;
421 TD->_info.QueueIndex = qh_offsets[period_slot]; // Actually re-filled in _AppendTD, but meh
422 qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
423 // TODO: Find queue with lowest load
425 LOG("period_slot = %i, QueueIndex = %i",
426 period_slot, TD->_info.QueueIndex);
428 // Stop any errors causing the TD to stop (NAK will error)
429 // - If the device goes away, the interrupt should be stopped anyway
430 TD->Control &= ~(3 << 27);
432 UHCI_int_AppendTD(Cont, qh, TD);
435 void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
439 if( Period < 0 ) return NULL;
441 ENTER("pPtr xDest iPeriod pCb pCbData pBuf iLength",
442 Ptr, Dest, Period, Cb, CbData, Buf, Length);
444 // TODO: Data toggle?
445 td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length);
446 if( !td ) return NULL;
448 UHCI_int_SetInterruptPoll(Ptr, td, Period);
453 // TODO: Does interrupt OUT make sense?
454 void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
458 if( Period < 0 ) return NULL;
460 ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
461 Ptr, Dest, Period, Cb, CbData, Buf, Length);
463 // TODO: Data toggle?
464 td = UHCI_int_CreateTD(Ptr, Dest, PID_OUT, 0, Cb, CbData, Buf, Length);
465 if( !td ) return NULL;
467 UHCI_int_SetInterruptPoll(Ptr, td, Period);
473 void UHCI_StopInterrupt(void *Ptr, void *Handle)
475 // TODO: Stop interrupt transaction
476 Log_Error("UHCI", "TODO: Implement UHCI_StopInterrupt");
479 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
481 tUHCI_Controller *Cont = Ptr;
482 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
485 ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
487 td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
488 UHCI_int_AppendTD(Cont, qh, td);
494 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
496 tUHCI_Controller *Cont = Ptr;
497 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
500 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
502 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
503 UHCI_int_AppendTD(Cont, qh, td);
508 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
510 tUHCI_Controller *Cont = Ptr;
511 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
514 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
516 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, !!Tgl, Cb, CbData, Data, Length);
517 UHCI_int_AppendTD(Cont, qh, td);
523 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
525 tUHCI_Controller *Cont = Ptr;
526 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
530 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
532 while( Length > MAX_PACKET_SIZE )
534 LOG("MaxPacket (rem = %i)", Length);
535 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, MAX_PACKET_SIZE);
536 UHCI_int_AppendTD(Cont, qh, td);
539 Length -= MAX_PACKET_SIZE;
540 src += MAX_PACKET_SIZE;
544 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length);
545 UHCI_int_AppendTD(Cont, qh, td);
550 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
552 tUHCI_Controller *Cont = Ptr;
553 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
557 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
558 while( Length > MAX_PACKET_SIZE )
560 LOG("MaxPacket (rem = %i)", Length);
561 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, MAX_PACKET_SIZE);
562 UHCI_int_AppendTD(Cont, qh, td);
565 Length -= MAX_PACKET_SIZE;
566 dst += MAX_PACKET_SIZE;
570 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length);
571 UHCI_int_AppendTD(Cont, qh, td);
577 // === INTERNAL FUNCTIONS ===
578 void UHCI_CheckPortUpdate(void *Ptr)
580 tUHCI_Controller *Host = Ptr;
582 for( int i = 0; i < 2; i ++ )
584 int port = PORTSC1 + i*2;
587 status = _InWord(Host, port);
588 // Check for port change
589 if( !(status & 0x0002) ) continue;
590 _OutWord(Host, port, 0x0002);
592 // Check if the port is connected
595 // Tell the USB code it's gone.
596 USB_DeviceDisconnected(Host->RootHub, i);
601 LOG("Port %i has something", i);
602 // Reset port (set bit 9)
604 _OutWord(Host, port, 0x0200);
605 Time_Delay(50); // 50ms delay
606 _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
609 Time_Delay(50); // 50ms delay
610 _OutWord(Host, port, _InWord(Host, port) | 0x0004);
611 // Tell USB there's a new device
612 USB_DeviceConnected(Host->RootHub, i);
617 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
619 if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
621 PAddr -= Controller->PhysTDQHPage;
622 PAddr -= (128+2)*sizeof(tUHCI_QH);
623 if( PAddr > PAGE_SIZE ) return NULL; // Wrapping will bring above page size
624 PAddr /= sizeof(tUHCI_TD);
625 return &Controller->TDQHPage->LocalTDPool[PAddr];
629 tPAddr global_pool = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
631 if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE ) return NULL;
633 PAddr -= global_pool;
634 PAddr /= sizeof(tUHCI_TD);
635 return &gaUHCI_TDPool[PAddr];
638 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
640 tUHCI_TD *td, *prev = NULL;
643 // Disable controller
644 _OutWord( Cont, USBCMD, 0x0000 );
648 while( !(cur_td & 1) )
650 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
652 Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
658 if( td->Control & (1 << 23) ) {
663 LOG("Removed %p from QH %p", td, QH);
666 QH->Child = td->Link;
668 prev->Link = td->Link;
672 // re-enable controller
673 _OutWord( Cont, USBCMD, 0x0001 );
676 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
678 int byte_count = (TD->Control & 0x7FF)+1;
679 tUHCI_ExtraTDInfo *info = TD->_info.ExtraInfo;
681 // Handle non page-aligned destination (or with a > 32-bit paddr)
682 // TODO: Needs fixing for alignment issues
686 int src_ofs = TD->BufferPointer & (PAGE_SIZE-1);
687 src = (void *) MM_MapTemp(TD->BufferPointer);
688 dest = (void *) MM_MapTemp(info->FirstPage);
689 // Check for a single page transfer
690 if( byte_count + info->Offset <= PAGE_SIZE )
692 LOG("Single page copy %P to %P of %p",
693 TD->BufferPointer, info->FirstPage, TD);
694 memcpy(dest + info->Offset, src + src_ofs, byte_count);
699 LOG("Multi page copy %P to (%P,%P) of %p",
700 TD->BufferPointer, info->FirstPage, info->SecondPage, TD);
701 int part_len = PAGE_SIZE - info->Offset;
702 memcpy(dest + info->Offset, src + src_ofs, part_len);
703 MM_FreeTemp( (tVAddr)dest );
704 dest = (void *) MM_MapTemp(info->SecondPage);
705 memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
707 MM_FreeTemp( (tVAddr)src );
708 MM_FreeTemp( (tVAddr)dest );
712 if( info->Callback != NULL )
714 LOG("Calling cb %p (%i bytes)", info->Callback, byte_count);
715 void *ptr = (void *) MM_MapTemp( TD->BufferPointer );
716 info->Callback( info->CallbackPtr, ptr, byte_count );
717 MM_FreeTemp( (tVAddr)ptr );
721 if( TD->_info.QueueIndex > 127 )
724 TD->_info.ExtraInfo = NULL;
728 void UHCI_int_InterruptThread(void *Pointer)
730 tUHCI_Controller *Cont = Pointer;
731 Threads_SetName("UHCI Interrupt Handler");
736 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
739 for( int i = 0; i < NUM_TDs; i ++ )
743 td = &gaUHCI_TDPool[i];
745 // Skip completely inactive TDs
746 if( td->_info.bActive == 0 ) continue ;
747 // Skip ones that are still in use
748 if( td->Control & (1 << 23) ) continue ;
750 // If no callback/alt buffer, mark as free and move on
751 if( td->_info.ExtraInfo )
753 UHCI_int_HandleTDComplete(Cont, td);
757 if( td->Control & 0x00FF0000 ) {
758 LOG("td->control(Status) = %s%s%s%s%s%s%s%s",
759 td->Control & (1 << 23) ? "Active " : "",
760 td->Control & (1 << 22) ? "Stalled " : "",
761 td->Control & (1 << 21) ? "Data Buffer Error " : "",
762 td->Control & (1 << 20) ? "Babble " : "",
763 td->Control & (1 << 19) ? "NAK " : "",
764 td->Control & (1 << 18) ? "CRC Error, " : "",
765 td->Control & (1 << 17) ? "Bitstuff Error, " : "",
766 td->Control & (1 << 16) ? "Reserved " : ""
768 // Clean up QH (removing all inactive entries)
769 UHCI_int_CleanQH(Cont, Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex);
772 // Handle rescheduling of interrupt TDs
773 if( td->_info.QueueIndex <= 127 )
775 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.QueueIndex);
776 // TODO: Flip toggle?
777 td->Control |= (1 << 23);
778 // Add back into controller's interrupt list
781 Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
788 if( td->_info.bFreePointer )
789 MM_DerefPhys( td->BufferPointer );
792 LOG("Cleaned %p (->Control = %x)", td, td->Control);
793 td->_info.bActive = 0;
798 void UHCI_InterruptHandler(int IRQ, void *Ptr)
800 tUHCI_Controller *Host = Ptr;
801 // int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
802 Uint16 status = _InWord(Host, USBSTS);
804 // Interrupt-on-completion
807 // TODO: Support isochronous transfers (will need updating the frame pointer)
808 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
811 LOG("status = 0x%04x", status);
812 _OutWord(Host, USBSTS, status);
815 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
818 ((Uint8*)Host->MemIOMap)[Reg] = Value;
820 outb(Host->IOBase + Reg, Value);
823 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
826 Host->MemIOMap[Reg/2] = Value;
828 outw(Host->IOBase + Reg, Value);
831 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
834 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
836 outd(Host->IOBase + Reg, Value);
839 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
842 return Host->MemIOMap[Reg/2];
844 return inw(Host->IOBase + Reg);