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
23 #define MAX_INTERRUPT_LOAD 1024 // Maximum bytes per frame for interrupts
27 #define PID_SETUP 0x2D
30 int UHCI_Initialise(char **Arguments);
32 int UHCI_int_InitHost(tUHCI_Controller *Host);
34 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
35 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD);
36 tUHCI_TD *UHCI_int_CreateTD(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
38 void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
39 void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
40 void UHCI_StopInterrupt(void *Ptr, void *Handle);
41 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length);
42 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
43 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
44 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
45 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
47 void UHCI_CheckPortUpdate(void *Ptr);
48 void UHCI_int_InterruptThread(void *Unused);
49 void UHCI_InterruptHandler(int IRQ, void *Ptr);
51 static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value);
52 static void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value);
53 static void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value);
54 static Uint16 _InWord(tUHCI_Controller *Host, int Reg);
57 MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
58 tUHCI_TD *gaUHCI_TDPool;
59 tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS];
60 tUSBHostDef gUHCI_HostDef = {
61 .InterruptIN = UHCI_InterruptIN,
62 .InterruptOUT = UHCI_InterruptOUT,
63 .StopInterrupt = UHCI_StopInterrupt,
65 .ControlSETUP = UHCI_ControlSETUP,
66 .ControlIN = UHCI_ControlIN,
67 .ControlOUT = UHCI_ControlOUT,
69 .BulkOUT = UHCI_BulkOUT,
70 .BulkIN = UHCI_BulkIN,
72 .CheckPorts = UHCI_CheckPortUpdate
74 tSemaphore gUHCI_InterruptSempahore;
78 * \fn int UHCI_Initialise()
79 * \brief Called to initialise the UHCI Driver
81 int UHCI_Initialise(char **Arguments)
88 // Initialise with no maximum value
89 Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
91 if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 )
93 LEAVE('i', MODULE_ERR_NOTNEEDED);
94 return MODULE_ERR_NOTNEEDED;
99 gaUHCI_TDPool = (void *) MM_AllocDMA(1, 32, &tmp);
102 // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
103 while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
105 tUHCI_Controller *cinfo = &gUHCI_Controllers[i];
107 // NOTE: Check "protocol" from PCI?
110 base_addr = PCI_GetBAR(id, 4);
114 cinfo->IOBase = base_addr & ~1;
115 cinfo->MemIOMap = NULL;
119 cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
121 cinfo->IRQNum = PCI_GetIRQ(id);
123 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
124 id, base_addr, cinfo->IRQNum);
126 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
129 ret = UHCI_int_InitHost(cinfo);
136 // Spin off interrupt handling thread
137 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
140 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
141 LOG("cinfo->RootHub = %p", cinfo->RootHub);
146 if(i == MAX_CONTROLLERS) {
147 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
149 LEAVE('i', MODULE_ERR_OK);
150 return MODULE_ERR_OK;
154 * \fn void UHCI_Cleanup()
155 * \brief Called just before module is unloaded
162 * \brief Initialises a UHCI host controller
163 * \param Host Pointer - Host to initialise
165 int UHCI_int_InitHost(tUHCI_Controller *Host)
167 ENTER("pHost", Host);
169 // - 1 Page, 32-bit address
170 // - 1 page = 1024 4 byte entries
171 Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
172 if( !Host->FrameList ) {
173 Log_Warning("UHCI", "Unable to allocate frame list, aborting");
178 Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
179 if( !Host->TDQHPage ) {
181 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
187 // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
188 const int dest_offsets[] = {
189 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,
190 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31
192 for( int i = 0; i < 1024; i ++ ) {
193 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH );
194 Host->FrameList[i] = addr | 2;
196 for( int i = 0; i < 64; i ++ ) {
197 int ofs = dest_offsets[ i & (32-1) ] * 2 + (i >= 32);
198 Uint32 addr = Host->PhysTDQHPage + ofs * sizeof(tUHCI_QH);
199 LOG("Slot %i to (%i,%i,%i,%i) ms slots",
200 ofs, 0 + i*4, 256 + i*4, 512 + i*4, 768 + i*4);
201 Host->FrameList[ 0 + i*4] = addr | 2;
202 Host->FrameList[256 + i*4] = addr | 2;
203 Host->FrameList[512 + i*4] = addr | 2;
204 Host->FrameList[768 + i*4] = addr | 2;
207 // Build up interrupt binary tree
209 tUHCI_QH *dest = Host->TDQHPage->InterruptQHs;
210 Uint32 destphys = Host->PhysTDQHPage;
212 // Set up next pointer to index to i/2 in the next step
213 for( int _count = 64; _count > 1; _count /= 2 )
215 for( int i = 0; i < _count; i ++ ) {
216 dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
219 dest += _count; destphys += _count * sizeof(tUHCI_QH);
221 // Skip padding, and move to control QH
222 dest->Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
226 // Set up control and bulk queues
227 Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
228 Host->TDQHPage->ControlQH.Child = 1;
229 Host->TDQHPage->BulkQH.Next = 1;
230 Host->TDQHPage->BulkQH.Child = 1;
233 _OutWord( Host, USBCMD, 4 );
235 _OutWord( Host, USBCMD, 0 );
237 // Allocate Frame List
238 // Set frame length to 1 ms
239 _OutByte( Host, SOFMOD, 64 );
242 _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
243 _OutWord( Host, FRNUM, 0 );
246 _OutWord( Host, USBINTR, 0x000F );
247 PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
250 _OutWord( Host, USBCMD, 0x0001 );
256 // --------------------------------------------------------------------
257 // TDs and QH Allocation/Appending
258 // --------------------------------------------------------------------
259 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
262 Mutex_Acquire( &lock );
263 for( int i = 0; i < NUM_TDs; i ++ )
265 if(gaUHCI_TDPool[i]._info.bActive == 0)
267 gaUHCI_TDPool[i].Link = 1;
268 gaUHCI_TDPool[i].Control = (1 << 23);
269 gaUHCI_TDPool[i]._info.bActive = 1;
270 gaUHCI_TDPool[i]._info.QueueIndex = 128;
271 Mutex_Release( &lock );
272 return &gaUHCI_TDPool[i];
275 Mutex_Release( &lock );
279 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
281 static tMutex lock; // TODO: Should I use a shortlock (avoid being preempted)
283 Mutex_Acquire(&lock);
285 // Ensure that there is an interrupt for each used frame
286 TD->Control |= (1 << 24);
287 TD->_info.QueueIndex = ((tVAddr)QH - (tVAddr)Cont->TDQHPage->InterruptQHs) / sizeof(tUHCI_QH);
289 TD->Control &= ~0x7FF;
290 TD->Control |= (TD->Token >> 21) & 0x7FF;
293 _OutWord( Cont, USBCMD, 0x0000 );
297 if( QH->Child & 1 ) {
298 QH->Child = MM_GetPhysAddr( (tVAddr)TD );
302 QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
306 // Reenable controller
307 _OutWord( Cont, USBCMD, 0x0001 );
310 LOG("QH(%p)->Child = %x", QH, QH->Child);
311 LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
313 Mutex_Release(&lock);
317 * \brief Send a transaction to the USB bus
318 * \param Cont Controller pointer
319 * \param Addr Function Address * 16 + Endpoint
320 * \param bTgl Data toggle value
322 tUHCI_TD *UHCI_int_CreateTD(
323 tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
324 tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
327 tUHCI_ExtraTDInfo *info = NULL;
329 if( Length > 0x400 ) {
330 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
331 return NULL; // Controller allows up to 0x500, but USB doesn't
334 td = UHCI_int_AllocateTD(Cont);
336 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
340 LOG("TD %p %i bytes, Type %x to 0x%x",
341 td, Length, Type, Addr);
343 td->Control = (Length - 1) & 0x7FF;
344 td->Control |= (1 << 23); // Active set
345 td->Control |= (3 << 27); // 3 retries
346 td->Token = ((Length - 1) & 0x7FF) << 21;
347 td->Token |= (bTgl & 1) << 19;
348 td->Token |= (Addr & 0xF) << 15;
349 td->Token |= ((Addr/16) & 0xFF) << 8;
353 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
355 || MM_GetPhysAddr( (tVAddr)Data ) >> 32
359 td->BufferPointer = MM_AllocPhysRange(1, 32);
361 LOG("Allocated page %x", td->BufferPointer);
363 if( Type == 0x69 ) // IN token
366 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
367 info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
368 info->FirstPage = MM_GetPhysAddr( (tVAddr)Data );
369 info->SecondPage = MM_GetPhysAddr( (tVAddr)Data + Length - 1 );
373 LOG("Relocated OUT/SETUP");
374 tVAddr ptr = MM_MapTemp(td->BufferPointer);
375 memcpy( (void*)ptr, Data, Length );
377 td->Control |= (1 << 24);
379 td->_info.bFreePointer = 1;
383 td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
384 td->_info.bFreePointer = 0;
387 // Interrupt on completion
391 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
392 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
394 info->CallbackPtr = CbData;
398 LOG("info = %p", info);
399 td->Control |= (1 << 24);
400 td->_info.ExtraInfo = info;
406 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
409 const int qh_offsets[] = {126, 124, 120, 112, 96, 64, 0};
410 const int qh_sizes[] = { 1, 2, 4, 8, 16, 32, 64};
413 if( Period < 0 ) return ;
414 if( Period > 256 ) Period = 256;
415 if( Period == 255 ) Period = 256;
417 // Get the log base2 of the period
419 while( Period >>= 1 ) period_slot ++;
421 // Adjust for the 4ms minimum period
422 if( period_slot < 2 ) period_slot = 0;
423 else period_slot -= 2;
425 // _AppendTD calculates this from qh, but we use it to determine qh
426 TD->_info.QueueIndex = qh_offsets[period_slot];
427 // TODO: Find queue with lowest load
430 int min_load_slot = 0;
431 for( int i = 0; i < qh_sizes[period_slot]; i ++ )
434 index = qh_offsets[period_slot] + i;
436 while( index >= 0 && index < 127 )
438 qh = Cont->TDQHPage->InterruptQHs + index;
439 load += Cont->InterruptLoad[index];
440 index = ((qh->Next & ~3) - Cont->PhysTDQHPage)/sizeof(tUHCI_QH);
443 LOG("Slot %i (and below) load %i", qh_offsets[period_slot] + i, load);
445 // i = 0 will initialise the values, otherwise update if lower
446 if( i == 0 || load < min_load )
451 // - Fast return if no load
452 if( load == 0 ) break;
454 min_load_slot += qh_offsets[period_slot];
455 TD->_info.QueueIndex = min_load_slot;
456 if( min_load + (TD->Control & 0x7FF) > MAX_INTERRUPT_LOAD )
458 Log_Warning("UHCI", "Interrupt load on %i ms is too high (slot %i load %i bytes)",
459 1 << (period_slot+2), min_load_slot, min_load
462 Cont->InterruptLoad[min_load_slot] += (TD->Control & 0x7FF);
464 qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
466 LOG("period_slot = %i, QueueIndex = %i",
467 period_slot, TD->_info.QueueIndex);
469 // Stop any errors causing the TD to stop (NAK will error)
470 // - If the device is unplugged, the removal code should remove the interrupt
471 TD->Control &= ~(3 << 27);
473 UHCI_int_AppendTD(Cont, qh, TD);
476 void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
480 if( Period < 0 ) return NULL;
482 ENTER("pPtr xDest iPeriod pCb pCbData pBuf iLength",
483 Ptr, Dest, Period, Cb, CbData, Buf, Length);
485 // TODO: Data toggle?
486 td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length);
487 if( !td ) return NULL;
489 UHCI_int_SetInterruptPoll(Ptr, td, Period);
494 // TODO: Does interrupt OUT make sense?
495 void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
499 if( Period < 0 ) return NULL;
501 ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
502 Ptr, Dest, Period, Cb, CbData, Buf, Length);
504 // TODO: Data toggle?
505 td = UHCI_int_CreateTD(Ptr, Dest, PID_OUT, 0, Cb, CbData, Buf, Length);
506 if( !td ) return NULL;
508 UHCI_int_SetInterruptPoll(Ptr, td, Period);
514 void UHCI_StopInterrupt(void *Ptr, void *Handle)
516 // TODO: Stop interrupt transaction
517 Log_Error("UHCI", "TODO: Implement UHCI_StopInterrupt");
520 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
522 tUHCI_Controller *Cont = Ptr;
523 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
526 ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
528 td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
529 UHCI_int_AppendTD(Cont, qh, td);
535 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
537 tUHCI_Controller *Cont = Ptr;
538 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
541 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
543 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
544 UHCI_int_AppendTD(Cont, qh, td);
549 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
551 tUHCI_Controller *Cont = Ptr;
552 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
555 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
557 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, !!Tgl, Cb, CbData, Data, Length);
558 UHCI_int_AppendTD(Cont, qh, td);
564 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
566 tUHCI_Controller *Cont = Ptr;
567 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
571 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
573 while( Length > MAX_PACKET_SIZE )
575 LOG("MaxPacket (rem = %i)", Length);
576 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, MAX_PACKET_SIZE);
577 UHCI_int_AppendTD(Cont, qh, td);
580 Length -= MAX_PACKET_SIZE;
581 src += MAX_PACKET_SIZE;
585 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length);
586 UHCI_int_AppendTD(Cont, qh, td);
591 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
593 tUHCI_Controller *Cont = Ptr;
594 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
598 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
599 while( Length > MAX_PACKET_SIZE )
601 LOG("MaxPacket (rem = %i)", Length);
602 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, MAX_PACKET_SIZE);
603 UHCI_int_AppendTD(Cont, qh, td);
606 Length -= MAX_PACKET_SIZE;
607 dst += MAX_PACKET_SIZE;
611 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length);
612 UHCI_int_AppendTD(Cont, qh, td);
618 // === INTERNAL FUNCTIONS ===
619 void UHCI_CheckPortUpdate(void *Ptr)
621 tUHCI_Controller *Host = Ptr;
623 for( int i = 0; i < 2; i ++ )
625 int port = PORTSC1 + i*2;
628 status = _InWord(Host, port);
629 // Check for port change
630 if( !(status & 0x0002) ) continue;
631 _OutWord(Host, port, 0x0002);
633 // Check if the port is connected
636 // Tell the USB code it's gone.
637 USB_DeviceDisconnected(Host->RootHub, i);
642 LOG("Port %i has something", i);
643 // Reset port (set bit 9)
645 _OutWord(Host, port, 0x0200);
646 Time_Delay(50); // 50ms delay
647 _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
650 Time_Delay(50); // 50ms delay
651 _OutWord(Host, port, _InWord(Host, port) | 0x0004);
652 // Tell USB there's a new device
653 USB_DeviceConnected(Host->RootHub, i);
658 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
660 if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
662 PAddr -= Controller->PhysTDQHPage;
663 PAddr -= (128+2)*sizeof(tUHCI_QH);
664 if( PAddr > PAGE_SIZE ) return NULL; // Wrapping will bring above page size
665 PAddr /= sizeof(tUHCI_TD);
666 return &Controller->TDQHPage->LocalTDPool[PAddr];
670 tPAddr global_pool = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
672 if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE ) return NULL;
674 PAddr -= global_pool;
675 PAddr /= sizeof(tUHCI_TD);
676 return &gaUHCI_TDPool[PAddr];
679 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
681 tUHCI_TD *td, *prev = NULL;
684 // Disable controller
685 _OutWord( Cont, USBCMD, 0x0000 );
689 while( !(cur_td & 1) )
691 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
693 Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
699 if( td->Control & (1 << 23) ) {
704 LOG("Removed %p from QH %p", td, QH);
707 QH->Child = td->Link;
709 prev->Link = td->Link;
713 // re-enable controller
714 _OutWord( Cont, USBCMD, 0x0001 );
717 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
719 int byte_count = (TD->Control & 0x7FF)+1;
720 tUHCI_ExtraTDInfo *info = TD->_info.ExtraInfo;
722 // Handle non page-aligned destination (or with a > 32-bit paddr)
723 // TODO: Needs fixing for alignment issues
727 int src_ofs = TD->BufferPointer & (PAGE_SIZE-1);
728 src = (void *) MM_MapTemp(TD->BufferPointer);
729 dest = (void *) MM_MapTemp(info->FirstPage);
730 // Check for a single page transfer
731 if( byte_count + info->Offset <= PAGE_SIZE )
733 LOG("Single page copy %P to %P of %p",
734 TD->BufferPointer, info->FirstPage, TD);
735 memcpy(dest + info->Offset, src + src_ofs, byte_count);
740 LOG("Multi page copy %P to (%P,%P) of %p",
741 TD->BufferPointer, info->FirstPage, info->SecondPage, TD);
742 int part_len = PAGE_SIZE - info->Offset;
743 memcpy(dest + info->Offset, src + src_ofs, part_len);
744 MM_FreeTemp( (tVAddr)dest );
745 dest = (void *) MM_MapTemp(info->SecondPage);
746 memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
748 MM_FreeTemp( (tVAddr)src );
749 MM_FreeTemp( (tVAddr)dest );
753 if( info->Callback != NULL )
755 LOG("Calling cb %p (%i bytes)", info->Callback, byte_count);
756 void *ptr = (void *) MM_MapTemp( TD->BufferPointer );
757 info->Callback( info->CallbackPtr, ptr, byte_count );
758 MM_FreeTemp( (tVAddr)ptr );
762 if( TD->_info.QueueIndex > 127 )
765 TD->_info.ExtraInfo = NULL;
769 void UHCI_int_InterruptThread(void *Pointer)
771 tUHCI_Controller *Cont = Pointer;
772 Threads_SetName("UHCI Interrupt Handler");
777 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
780 for( int i = 0; i < NUM_TDs; i ++ )
784 td = &gaUHCI_TDPool[i];
786 // Skip completely inactive TDs
787 if( td->_info.bActive == 0 ) continue ;
788 // Skip ones that are still in use
789 if( td->Control & (1 << 23) ) continue ;
791 // If no callback/alt buffer, mark as free and move on
792 if( td->_info.ExtraInfo )
794 UHCI_int_HandleTDComplete(Cont, td);
798 if( td->Control & 0x00FF0000 ) {
799 LOG("td->control(Status) = %s%s%s%s%s%s%s%s",
800 td->Control & (1 << 23) ? "Active " : "",
801 td->Control & (1 << 22) ? "Stalled " : "",
802 td->Control & (1 << 21) ? "Data Buffer Error " : "",
803 td->Control & (1 << 20) ? "Babble " : "",
804 td->Control & (1 << 19) ? "NAK " : "",
805 td->Control & (1 << 18) ? "CRC Error, " : "",
806 td->Control & (1 << 17) ? "Bitstuff Error, " : "",
807 td->Control & (1 << 16) ? "Reserved " : ""
809 // Clean up QH (removing all inactive entries)
810 UHCI_int_CleanQH(Cont, Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex);
813 // Handle rescheduling of interrupt TDs
814 if( td->_info.QueueIndex <= 127 )
816 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.QueueIndex);
817 // TODO: Flip toggle?
818 td->Control |= (1 << 23);
819 // Add back into controller's interrupt list
822 Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
829 if( td->_info.bFreePointer )
830 MM_DerefPhys( td->BufferPointer );
833 LOG("Cleaned %p (->Control = %x)", td, td->Control);
834 td->_info.bActive = 0;
839 void UHCI_InterruptHandler(int IRQ, void *Ptr)
841 tUHCI_Controller *Host = Ptr;
842 // int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
843 Uint16 status = _InWord(Host, USBSTS);
845 // Interrupt-on-completion
848 // TODO: Support isochronous transfers (will need updating the frame pointer)
849 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
852 LOG("status = 0x%04x", status);
853 _OutWord(Host, USBSTS, status);
856 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
859 ((Uint8*)Host->MemIOMap)[Reg] = Value;
861 outb(Host->IOBase + Reg, Value);
864 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
867 Host->MemIOMap[Reg/2] = Value;
869 outw(Host->IOBase + Reg, Value);
872 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
875 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
877 outd(Host->IOBase + Reg, Value);
880 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
883 return Host->MemIOMap[Reg/2];
885 return inw(Host->IOBase + Reg);