3 * - By John Hodge (thePowersGang)
5 * Universal Host Controller Interface
8 #define VERSION VER2(0,5)
16 #include <semaphore.h>
19 #define MAX_CONTROLLERS 8
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_InitInterrupt(void *Ptr, int Endpt, int bOutbound, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len);
39 void *UHCI_InitIsoch(void *Ptr, int Endpt);
40 void *UHCI_InitControl(void *Ptr, int Endpt);
41 void *UHCI_InitBulk(void *Ptr, int Endpt);
42 void UHCI_RemoveEndpoint(void *Ptr, void *EndptHandle);
43 void *UHCI_SendIsoch(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length, int When);
44 void *UHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
46 const void *SetupData, size_t SetupLength,
47 const void *OutData, size_t OutLength,
48 void *InData, size_t InLength
50 void *UHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length);
52 void UHCI_CheckPortUpdate(void *Ptr);
53 void UHCI_int_InterruptThread(void *Unused);
54 void UHCI_InterruptHandler(int IRQ, void *Ptr);
56 static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value);
57 static void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value);
58 static void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value);
59 static Uint16 _InWord(tUHCI_Controller *Host, int Reg);
62 MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
63 tUHCI_TD *gaUHCI_TDPool;
64 tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS];
65 tUSBHostDef gUHCI_HostDef = {
66 .InitInterrupt = UHCI_InitInterrupt,
67 // .InitIsoch = UHCI_InitIsoch,
68 .InitControl = UHCI_InitControl,
69 .InitBulk = UHCI_InitBulk,
70 .RemEndpoint = UHCI_RemoveEndpoint,
72 // .SendIsoch = UHCI_SendIsoch,
73 .SendControl = UHCI_SendControl,
74 .SendBulk = UHCI_SendBulk,
77 .CheckPorts = UHCI_CheckPortUpdate,
78 // .ClearPortFeature = NULL,
79 // .GetBusState = NULL,
80 // .GetPortStatus = NULL,
81 // .SetPortFeature = NULL
83 tSemaphore gUHCI_InterruptSempahore;
87 * \fn int UHCI_Initialise()
88 * \brief Called to initialise the UHCI Driver
90 int UHCI_Initialise(char **Arguments)
97 // Initialise with no maximum value
98 Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
100 if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 )
102 LEAVE('i', MODULE_ERR_NOTNEEDED);
103 return MODULE_ERR_NOTNEEDED;
108 gaUHCI_TDPool = (void *) MM_AllocDMA(1, 32, &tmp);
109 memset(gaUHCI_TDPool, 0, PAGE_SIZE);
112 // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
113 while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
115 tUHCI_Controller *cinfo = &gUHCI_Controllers[i];
117 // NOTE: Check "protocol" from PCI?
120 base_addr = PCI_GetBAR(id, 4);
124 cinfo->IOBase = base_addr & ~1;
125 cinfo->MemIOMap = NULL;
129 cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
131 cinfo->IRQNum = PCI_GetIRQ(id);
133 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
134 id, base_addr, cinfo->IRQNum);
136 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
139 ret = UHCI_int_InitHost(cinfo);
146 // Spin off interrupt handling thread
147 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
150 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
151 LOG("cinfo->RootHub = %p", cinfo->RootHub);
156 if(i == MAX_CONTROLLERS) {
157 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
159 LEAVE('i', MODULE_ERR_OK);
160 return MODULE_ERR_OK;
164 * \fn void UHCI_Cleanup()
165 * \brief Called just before module is unloaded
172 * \brief Initialises a UHCI host controller
173 * \param Host Pointer - Host to initialise
175 int UHCI_int_InitHost(tUHCI_Controller *Host)
177 ENTER("pHost", Host);
179 // - 1 Page, 32-bit address
180 // - 1 page = 1024 4 byte entries
181 Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
182 if( !Host->FrameList ) {
183 Log_Warning("UHCI", "Unable to allocate frame list, aborting");
188 Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
189 if( !Host->TDQHPage ) {
191 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
197 // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
198 const int dest_offsets[] = {
199 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,
200 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31
202 for( int i = 0; i < 1024; i ++ ) {
203 Uint32 addr = MM_GetPhysAddr( &Host->TDQHPage->ControlQH );
204 Host->FrameList[i] = addr | 2;
206 for( int i = 0; i < 64; i ++ ) {
207 int ofs = dest_offsets[ i & (32-1) ] * 2 + (i >= 32);
208 Uint32 addr = Host->PhysTDQHPage + ofs * sizeof(tUHCI_QH);
209 LOG("Slot %i to (%i,%i,%i,%i) ms slots",
210 ofs, 0 + i*4, 256 + i*4, 512 + i*4, 768 + i*4);
211 Host->FrameList[ 0 + i*4] = addr | 2;
212 Host->FrameList[256 + i*4] = addr | 2;
213 Host->FrameList[512 + i*4] = addr | 2;
214 Host->FrameList[768 + i*4] = addr | 2;
217 // Build up interrupt binary tree
219 tUHCI_QH *dest = Host->TDQHPage->InterruptQHs;
220 Uint32 destphys = Host->PhysTDQHPage;
222 // Set up next pointer to index to i/2 in the next step
223 for( int _count = 64; _count > 1; _count /= 2 )
225 for( int i = 0; i < _count; i ++ ) {
226 dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
229 dest += _count; destphys += _count * sizeof(tUHCI_QH);
231 // Skip padding, and move to control QH
232 dest->Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
236 // Set up control and bulk queues
237 Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
238 Host->TDQHPage->ControlQH.Child = 1;
239 Host->TDQHPage->BulkQH.Next = 1;
240 Host->TDQHPage->BulkQH.Child = 1;
243 _OutWord( Host, USBCMD, 4 );
245 _OutWord( Host, USBCMD, 0 );
247 // Allocate Frame List
248 // Set frame length to 1 ms
249 _OutByte( Host, SOFMOD, 64 );
252 _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
253 _OutWord( Host, FRNUM, 0 );
256 _OutWord( Host, USBINTR, 0x000F );
257 PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
260 _OutWord( Host, USBCMD, 0x0001 );
266 // --------------------------------------------------------------------
267 // TDs and QH Allocation/Appending
268 // --------------------------------------------------------------------
269 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
272 Mutex_Acquire( &lock );
273 for( int i = 0; i < NUM_TDs; i ++ )
275 if(gaUHCI_TDPool[i]._info.bActive == 0)
277 gaUHCI_TDPool[i].Link = 1;
278 gaUHCI_TDPool[i].Control = TD_CTL_ACTIVE;
279 gaUHCI_TDPool[i]._info.bActive = 1;
280 gaUHCI_TDPool[i]._info.QueueIndex = 128;
281 Mutex_Release( &lock );
282 return &gaUHCI_TDPool[i];
285 Mutex_Release( &lock );
289 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
291 static tMutex lock; // TODO: Should I use a shortlock (avoid being preempted)
293 Mutex_Acquire(&lock);
295 // Ensure that there is an interrupt for each used frame
296 TD->Control |= TD_CTL_IOC;
297 TD->_info.QueueIndex = ((tVAddr)QH - (tVAddr)Cont->TDQHPage->InterruptQHs) / sizeof(tUHCI_QH);
298 LOG("TD(%p)->QueueIndex = %i", TD, TD->_info.QueueIndex);
300 TD->Control &= ~0x7FF;
301 TD->Control |= (TD->Token >> 21) & 0x7FF;
304 _OutWord( Cont, USBCMD, 0x0000 );
308 if( QH->Child & 1 ) {
309 QH->Child = MM_GetPhysAddr( TD );
313 QH->_LastItem->Link = MM_GetPhysAddr( TD ) | 4;
317 // Reenable controller
318 _OutWord( Cont, USBCMD, 0x0001 );
321 LOG("QH(%p)->Child = %x", QH, QH->Child);
322 LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
324 Mutex_Release(&lock);
328 * \brief Send a transaction to the USB bus
329 * \param Cont Controller pointer
330 * \param Addr Function Address * 16 + Endpoint
331 * \param bTgl Data toggle value
333 tUHCI_TD *UHCI_int_CreateTD(
334 tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
335 tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
338 tUHCI_ExtraTDInfo *info = NULL;
340 if( Length > 0x400 ) {
341 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
342 return NULL; // Controller allows up to 0x500, but USB doesn't
345 td = UHCI_int_AllocateTD(Cont);
347 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
351 LOG("TD %p %i bytes, Type %x to 0x%x",
352 td, Length, Type, Addr);
354 td->Control = (Length - 1) & 0x7FF;
355 td->Control |= TD_CTL_ACTIVE; // Active set
356 td->Control |= (3 << 27); // 3 retries
357 // High speed device (must be explicitly enabled
361 td->Control |= 1 << 26;
363 td->Token = ((Length - 1) & 0x7FF) << 21;
364 td->Token |= (bTgl & 1) << 19;
365 td->Token |= (Addr & 0xF) << 15;
366 td->Token |= ((Addr/16) & 0xFF) << 8;
370 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
372 || MM_GetPhysAddr( Data ) >> 32
376 td->BufferPointer = MM_AllocPhysRange(1, 32);
378 LOG("Allocated page %x", td->BufferPointer);
380 if( Type == 0x69 ) // IN token
383 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
384 info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
385 info->FirstPage = MM_GetPhysAddr( Data );
386 info->SecondPage = MM_GetPhysAddr( (const char *)Data + Length - 1 );
390 LOG("Relocated OUT/SETUP");
391 void *ptr = MM_MapTemp(td->BufferPointer);
392 memcpy( ptr, Data, Length );
394 td->Control |= TD_CTL_IOC;
396 td->_info.bFreePointer = 1;
400 td->BufferPointer = MM_GetPhysAddr( Data );
401 td->_info.bFreePointer = 0;
404 // Interrupt on completion
408 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
409 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
411 info->CallbackPtr = CbData;
415 LOG("info = %p", info);
416 td->Control |= TD_CTL_IOC;
417 td->_info.ExtraInfo = info;
423 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
426 const int qh_offsets[] = {126, 124, 120, 112, 96, 64, 0};
427 const int qh_sizes[] = { 1, 2, 4, 8, 16, 32, 64};
430 if( Period < 0 ) return ;
431 if( Period > 256 ) Period = 256;
432 if( Period == 255 ) Period = 256;
434 // Get the log base2 of the period
436 while( Period >>= 1 ) period_slot ++;
438 // Adjust for the 4ms minimum period
439 if( period_slot < 2 ) period_slot = 0;
440 else period_slot -= 2;
442 // _AppendTD calculates this from qh, but we use it to determine qh
443 TD->_info.QueueIndex = qh_offsets[period_slot];
444 // TODO: Find queue with lowest load
447 int min_load_slot = 0;
448 for( int i = 0; i < qh_sizes[period_slot]; i ++ )
451 index = qh_offsets[period_slot] + i;
453 while( index >= 0 && index < 127 )
455 qh = Cont->TDQHPage->InterruptQHs + index;
456 load += Cont->InterruptLoad[index];
457 index = ((qh->Next & ~3) - Cont->PhysTDQHPage)/sizeof(tUHCI_QH);
460 LOG("Slot %i (and below) load %i", qh_offsets[period_slot] + i, load);
462 // i = 0 will initialise the values, otherwise update if lower
463 if( i == 0 || load < min_load )
468 // - Fast return if no load
469 if( load == 0 ) break;
471 min_load_slot += qh_offsets[period_slot];
472 TD->_info.QueueIndex = min_load_slot;
473 if( min_load + (TD->Control & 0x7FF) > MAX_INTERRUPT_LOAD )
475 Log_Warning("UHCI", "Interrupt load on %i ms is too high (slot %i load %i bytes)",
476 1 << (period_slot+2), min_load_slot, min_load
479 Cont->InterruptLoad[min_load_slot] += (TD->Control & 0x7FF);
481 qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
483 LOG("period_slot = %i, QueueIndex = %i",
484 period_slot, TD->_info.QueueIndex);
486 // Stop any errors causing the TD to stop (NAK will error)
487 // - If the device is unplugged, the removal code should remove the interrupt
488 TD->Control &= ~(3 << 27);
490 UHCI_int_AppendTD(Cont, qh, TD);
493 // --------------------------------------------------------------------
495 // --------------------------------------------------------------------
496 void *UHCI_InitInterrupt(void *Ptr, int Endpt, int bOutbound,
497 int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len)
500 if( Period <= 0 ) return NULL;
502 ENTER("pPtr xEndpt bbOutbound iPeriod pCb pCbData pBuf iLen",
503 Ptr, Endpt, bOutbound, Period, Cb, CbData, Buf, Len);
505 // TODO: Data toggle?
506 td = UHCI_int_CreateTD(Ptr, Endpt, (bOutbound ? PID_OUT : PID_IN), 0, Cb, CbData, Buf, Len);
507 if( !td ) return NULL;
509 UHCI_int_SetInterruptPoll(Ptr, td, Period);
515 void *UHCI_InitControl(void *Ptr, int Endpt)
517 // TODO: Bitmap of tgl values
518 return (void*)(Endpt+1);
521 void *UHCI_InitBulk(void *Ptr, int Endpt)
523 // TODO: Bitmap of tgl values
524 return (void*)(Endpt+1);
527 void UHCI_RemoveEndpoint(void *Ptr, void *Handle)
529 if( (int)Handle < 0x7FF ) {
533 // TODO: Stop interrupt transaction
534 Log_Error("UHCI", "TODO: Implement stopping interrupt polling");
538 void *UHCI_SendControl(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData,
539 int bOutbound, // (1) SETUP, OUT, IN vs (0) SETUP, IN, OUT
540 const void *SetupData, size_t SetupLength,
541 const void *OutData, size_t OutLength,
542 void *InData, size_t InLength
545 ENTER("pPtr pEndpt ibOutbound", Ptr, Endpt, bOutbound);
547 tUHCI_Controller *Cont = Ptr;
548 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
550 int Dest = (int)Endpt-1;
553 if( Endpt == NULL ) {
554 Log_Error("UHCI", "Passed a NULL Endpoint handle");
559 // Sanity check Endpt
560 if( (tVAddr)Endpt > 0x800 ) {
564 // if( Cont->Devs[Dest/16] == NULL ) LEAVE_RET('n', NULL);
565 // if( Cont->Devs[Dest/16].EndPt[Dest%16].Type != 1 ) LEAVE_RET('n', NULL);
566 // MAX_PACKET_SIZE = Cont->Devs[Dest/16].EndPt[Dest%16].MPS;
567 // Tgl = Cont->Devs[Dest/16].EndPt[Dest%16].Tgl;
569 // TODO: Build up list and then append to QH in one operation
571 char *data_ptr, *status_ptr;
572 size_t data_len, status_len;
573 Uint8 data_pid, status_pid;
576 data_pid = PID_OUT; data_ptr = (void*)OutData; data_len = OutLength;
577 status_pid = PID_IN; status_ptr = InData; status_len = InLength;
580 data_pid = PID_IN; data_ptr = InData; data_len = InLength;
581 status_pid = PID_OUT; status_ptr = (void*)OutData; status_len = OutLength;
584 // Sanity check data lengths
585 if( SetupLength > MAX_PACKET_SIZE ) LEAVE_RET('n', NULL);
586 if( status_len > MAX_PACKET_SIZE ) LEAVE_RET('n', NULL);
588 // Create and append SETUP packet
589 td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, (void*)SetupData, SetupLength);
590 UHCI_int_AppendTD(Cont, qh, td);
594 while( data_len > 0 )
596 size_t len = MIN(data_len, MAX_PACKET_SIZE);
597 td = UHCI_int_CreateTD(Cont, Dest, data_pid, Tgl, NULL, NULL, data_ptr, len);
598 UHCI_int_AppendTD(Cont, qh, td);
603 // TODO: Handle multi-packet
606 td = UHCI_int_CreateTD(Cont, Dest, status_pid, Tgl, Cb, CbData, status_ptr, status_len);
607 UHCI_int_AppendTD(Cont, qh, td);
610 // Cont->Devs[Dest/16].EndPt[Dest%16].Tgl = !Tgl;
616 void *UHCI_SendBulk(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData, int bOutbound, void *Data, size_t Length)
618 tUHCI_Controller *Cont = Ptr;
619 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
621 int Dest = (int)Endpt - 1;
624 ENTER("pPtr pEndpt pCb pCbData bOutbound pData iLength", Ptr, Dest, Cb, CbData, bOutbound, Data, Length);
626 if( Endpt == NULL ) {
627 Log_Error("UHCI", "Passed a NULL Endpoint handle");
632 // Sanity check Endpt
636 Uint8 pid = (bOutbound ? PID_OUT : PID_IN);
641 size_t len = MIN(MAX_PACKET_SIZE, Length);
643 td = UHCI_int_CreateTD(Cont, Dest, pid, Tgl, Cb, (len == Length ? CbData : NULL), pos, len);
644 UHCI_int_AppendTD(Cont, qh, td);
655 // ==========================
656 // === INTERNAL FUNCTIONS ===
657 // ==========================
658 void UHCI_CheckPortUpdate(void *Ptr)
660 tUHCI_Controller *Host = Ptr;
662 for( int i = 0; i < 2; i ++ )
664 int port = PORTSC1 + i*2;
667 status = _InWord(Host, port);
668 // Check for port change
669 if( !(status & 0x0002) ) continue;
670 _OutWord(Host, port, 0x0002);
672 // Check if the port is connected
675 // Tell the USB code it's gone.
676 USB_DeviceDisconnected(Host->RootHub, i);
681 LOG("Port %i has something", i);
682 // Reset port (set bit 9)
684 _OutWord(Host, port, 0x0200);
685 Time_Delay(50); // 50ms delay
686 _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
689 Time_Delay(50); // 50ms delay
690 _OutWord(Host, port, _InWord(Host, port) | 0x0004);
691 // Tell USB there's a new device
692 USB_DeviceConnected(Host->RootHub, i);
697 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
699 if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
701 PAddr -= Controller->PhysTDQHPage;
702 PAddr -= (128+2)*sizeof(tUHCI_QH);
703 if( PAddr > PAGE_SIZE ) return NULL; // Wrapping will bring above page size
704 PAddr /= sizeof(tUHCI_TD);
705 return &Controller->TDQHPage->LocalTDPool[PAddr];
709 tPAddr global_pool = MM_GetPhysAddr( gaUHCI_TDPool );
711 if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE ) return NULL;
713 PAddr -= global_pool;
714 PAddr /= sizeof(tUHCI_TD);
715 return &gaUHCI_TDPool[PAddr];
718 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
720 tUHCI_TD *td, *prev = NULL;
724 // Disable controller
725 _OutWord( Cont, USBCMD, 0x0000 );
729 LOG("cur_td = 0x%08x", cur_td);
730 while( !(cur_td & 1) )
732 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
734 Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
740 if( td->Control & TD_CTL_ACTIVE ) {
741 LOG("%p still active", td);
747 LOG("Removed %p from QH %p", td, QH);
750 QH->Child = td->Link;
752 prev->Link = td->Link;
757 if( nCleaned == 0 ) {
758 LOG("Nothing cleaned... what the?");
761 // re-enable controller
762 _OutWord( Cont, USBCMD, 0x0001 );
765 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
767 int byte_count = (TD->Control & 0x7FF)+1;
768 tUHCI_ExtraTDInfo *info = TD->_info.ExtraInfo;
770 // Handle non page-aligned destination (or with a > 32-bit paddr)
771 // TODO: Needs fixing for alignment issues
775 int src_ofs = TD->BufferPointer & (PAGE_SIZE-1);
776 src = MM_MapTemp(TD->BufferPointer);
777 dest = MM_MapTemp(info->FirstPage);
778 // Check for a single page transfer
779 if( byte_count + info->Offset <= PAGE_SIZE )
781 LOG("Single page copy %P to %P of %p",
782 TD->BufferPointer, info->FirstPage, TD);
783 memcpy(dest + info->Offset, src + src_ofs, byte_count);
788 LOG("Multi page copy %P to (%P,%P) of %p",
789 TD->BufferPointer, info->FirstPage, info->SecondPage, TD);
790 int part_len = PAGE_SIZE - info->Offset;
791 memcpy(dest + info->Offset, src + src_ofs, part_len);
793 dest = MM_MapTemp(info->SecondPage);
794 memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
801 if( info->Callback != NULL )
803 LOG("Calling cb %p (%i bytes)", info->Callback, byte_count);
804 void *ptr = MM_MapTemp( TD->BufferPointer );
805 info->Callback( info->CallbackPtr, ptr, byte_count );
810 if( TD->_info.QueueIndex > 127 )
813 TD->_info.ExtraInfo = NULL;
817 void UHCI_int_InterruptThread(void *Pointer)
819 tUHCI_Controller *Cont = Pointer;
820 Threads_SetName("UHCI Interrupt Handler");
827 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
830 for( int i = 0; i < NUM_TDs; i ++ )
834 td = &gaUHCI_TDPool[i];
836 // Skip completely inactive TDs
837 if( td->_info.bActive == 0 ) continue ;
838 // Skip ones that are still in use
839 if( td->Control & TD_CTL_ACTIVE ) continue ;
843 // If no callback/alt buffer, mark as free and move on
844 if( td->_info.ExtraInfo )
846 UHCI_int_HandleTDComplete(Cont, td);
850 if( td->Control & 0x00FF0000 ) {
851 LOG("td->control(Status) = %s%s%s%s%s%s%s%s",
852 td->Control & TD_CTL_ACTIVE ? "Active, " : "",
853 td->Control & TD_CTL_STALLED ? "Stalled, " : "",
854 td->Control & TD_CTL_DATABUFERR ? "Data Buffer Error, " : "",
855 td->Control & TD_CTL_BABBLE ? "Babble, " : "",
856 td->Control & TD_CTL_NAK ? "NAK, " : "",
857 td->Control & TD_CTL_CRCERR ? "CRC Error, " : "",
858 td->Control & TD_CTL_BITSTUFF ? "Bitstuff Error, " : "",
859 td->Control & TD_CTL_RESERVED ? "Reserved " : ""
861 LOG("From queue %i", td->_info.QueueIndex);
862 // Clean up QH (removing all inactive entries)
863 UHCI_int_CleanQH(Cont, Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex);
867 // Handle rescheduling of interrupt TDs
868 if( td->_info.QueueIndex <= 127 )
870 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.QueueIndex);
871 // TODO: Flip toggle?
872 td->Control |= TD_CTL_ACTIVE;
873 // Add back into controller's interrupt list
876 Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
883 if( td->_info.bFreePointer )
884 MM_DerefPhys( td->BufferPointer );
887 LOG("Cleaned %p (->Control = %x)", td, td->Control);
888 td->_info.bActive = 0;
892 LOG("Why did you wake me?");
897 void UHCI_InterruptHandler(int IRQ, void *Ptr)
899 tUHCI_Controller *Host = Ptr;
900 // int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
901 Uint16 status = _InWord(Host, USBSTS);
903 LOG("%p: status = 0x%04x", Ptr, status);
905 // Interrupt-on-completion
908 // TODO: Support isochronous transfers (will need updating the frame pointer)
909 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
912 // USB Error Interrupt
919 // - Fired if in suspend state and a USB device sends the RESUME signal
931 // Host Controller Process Error
934 Log_Error("UHCI", "Host controller process error on controller %p", Ptr);
937 _OutWord(Host, USBSTS, status);
940 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
943 ((Uint8*)Host->MemIOMap)[Reg] = Value;
945 outb(Host->IOBase + Reg, Value);
948 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
951 Host->MemIOMap[Reg/2] = Value;
953 outw(Host->IOBase + Reg, Value);
956 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
959 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
961 outd(Host->IOBase + Reg, Value);
964 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
967 return Host->MemIOMap[Reg/2];
969 return inw(Host->IOBase + Reg);