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, size_t MaxPacketSize);
40 void *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize);
41 void *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize);
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 if( Arguments && *Arguments && strcmp(*Arguments, "0") == 0 )
99 LOG("Disabled by argument");
100 LEAVE('i', MODULE_ERR_NOTNEEDED);
101 return MODULE_ERR_NOTNEEDED;
104 // Initialise with no maximum value
105 Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
107 if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 )
109 LEAVE('i', MODULE_ERR_NOTNEEDED);
110 return MODULE_ERR_NOTNEEDED;
115 gaUHCI_TDPool = (void *) MM_AllocDMA(1, 32, &tmp);
116 memset(gaUHCI_TDPool, 0, PAGE_SIZE);
119 // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
120 while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
122 tUHCI_Controller *cinfo = &gUHCI_Controllers[i];
124 // NOTE: Check "protocol" from PCI?
127 base_addr = PCI_GetBAR(id, 4);
131 cinfo->IOBase = base_addr & ~1;
132 cinfo->MemIOMap = NULL;
136 cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
138 cinfo->IRQNum = PCI_GetIRQ(id);
140 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
141 id, base_addr, cinfo->IRQNum);
143 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
146 ret = UHCI_int_InitHost(cinfo);
153 // Spin off interrupt handling thread
154 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
157 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
158 LOG("cinfo->RootHub = %p", cinfo->RootHub);
163 if(i == MAX_CONTROLLERS) {
164 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
166 LEAVE('i', MODULE_ERR_OK);
167 return MODULE_ERR_OK;
171 * \fn void UHCI_Cleanup()
172 * \brief Called just before module is unloaded
179 * \brief Initialises a UHCI host controller
180 * \param Host Pointer - Host to initialise
182 int UHCI_int_InitHost(tUHCI_Controller *Host)
184 ENTER("pHost", Host);
186 // - 1 Page, 32-bit address
187 // - 1 page = 1024 4 byte entries
188 Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
189 if( !Host->FrameList ) {
190 Log_Warning("UHCI", "Unable to allocate frame list, aborting");
195 Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
196 if( !Host->TDQHPage ) {
198 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
204 // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
205 const int dest_offsets[] = {
206 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,
207 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31
209 // Fill all slots (but every 4th will be changed below
210 for( int i = 0; i < 1024; i ++ ) {
211 Uint32 addr = MM_GetPhysAddr( &Host->TDQHPage->ControlQH );
212 Host->FrameList[i] = addr | 2;
214 for( int i = 0; i < 64; i ++ ) {
215 int ofs = dest_offsets[ i & (32-1) ] * 2 + (i >= 32);
216 Uint32 addr = Host->PhysTDQHPage + ofs * sizeof(tUHCI_QH);
217 LOG("Slot %i to (%i,%i,%i,%i) ms slots",
218 ofs, 0 + i*4, 256 + i*4, 512 + i*4, 768 + i*4);
219 Host->FrameList[ 0 + i*4] = addr | 2;
220 Host->FrameList[256 + i*4] = addr | 2;
221 Host->FrameList[512 + i*4] = addr | 2;
222 Host->FrameList[768 + i*4] = addr | 2;
225 // Build up interrupt binary tree
227 tUHCI_QH *dest = Host->TDQHPage->InterruptQHs;
228 Uint32 destphys = Host->PhysTDQHPage;
230 // Set up next pointer to index to i/2 in the next step
231 for( int _count = 64; _count > 1; _count /= 2 )
233 for( int i = 0; i < _count; i ++ ) {
234 dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
237 dest += _count; destphys += _count * sizeof(tUHCI_QH);
239 // Skip padding, and move to control QH
240 dest->Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
244 // Set up control and bulk queues
245 Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
246 Host->TDQHPage->ControlQH.Child = 1;
247 Host->TDQHPage->BulkQH.Next = 1;
248 Host->TDQHPage->BulkQH.Child = 1;
251 _OutWord( Host, USBCMD, 4 );
253 _OutWord( Host, USBCMD, 0 );
255 // Allocate Frame List
256 // Set frame length to 1 ms
257 _OutByte( Host, SOFMOD, 64 );
260 _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
261 _OutWord( Host, FRNUM, 0 );
264 _OutWord( Host, USBINTR, 0x000F );
265 PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
268 _OutWord( Host, USBCMD, 0x0001 );
274 // --------------------------------------------------------------------
275 // TDs and QH Allocation/Appending
276 // --------------------------------------------------------------------
277 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
280 Mutex_Acquire( &lock );
281 for( int i = 0; i < NUM_TDs; i ++ )
283 if(gaUHCI_TDPool[i]._info.bActive == 0)
285 gaUHCI_TDPool[i].Link = 1;
286 gaUHCI_TDPool[i].Control = TD_CTL_ACTIVE;
287 gaUHCI_TDPool[i]._info.bActive = 1;
288 gaUHCI_TDPool[i]._info.QueueIndex = 128;
289 Mutex_Release( &lock );
290 return &gaUHCI_TDPool[i];
293 Mutex_Release( &lock );
297 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
299 static tMutex lock; // TODO: Should I use a shortlock (avoid being preempted)
301 Mutex_Acquire(&lock);
303 // Ensure that there is an interrupt for each used frame
304 TD->Control |= TD_CTL_IOC;
305 TD->_info.QueueIndex = ((tVAddr)QH - (tVAddr)Cont->TDQHPage->InterruptQHs) / sizeof(tUHCI_QH);
306 LOG("TD(%p)->QueueIndex = %i", TD, TD->_info.QueueIndex);
308 TD->Control &= ~0x7FF;
309 TD->Control |= (TD->Token >> 21) & 0x7FF;
312 _OutWord( Cont, USBCMD, 0x0000 );
316 if( QH->Child & 1 ) {
317 QH->Child = MM_GetPhysAddr( TD );
321 QH->_LastItem->Link = MM_GetPhysAddr( TD ) | 4;
325 // Reenable controller
326 _OutWord( Cont, USBCMD, 0x0001 );
329 LOG("QH(%p)->Child = %x", QH, QH->Child);
330 LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
332 Mutex_Release(&lock);
336 * \brief Send a transaction to the USB bus
337 * \param Cont Controller pointer
338 * \param Addr Function Address * 16 + Endpoint
339 * \param bTgl Data toggle value
341 tUHCI_TD *UHCI_int_CreateTD(
342 tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
343 tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
346 tUHCI_ExtraTDInfo *info = NULL;
348 if( Length > 0x400 ) {
349 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
350 return NULL; // Controller allows up to 0x500, but USB doesn't
353 td = UHCI_int_AllocateTD(Cont);
355 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
359 LOG("TD %p %i bytes, Type %x to 0x%x",
360 td, Length, Type, Addr);
362 td->Control = (Length - 1) & 0x7FF;
363 td->Control |= TD_CTL_ACTIVE; // Active set
364 td->Control |= (3 << 27); // 3 retries
365 // High speed device (must be explicitly enabled
369 td->Control |= 1 << 26;
371 td->Token = ((Length - 1) & 0x7FF) << 21;
372 td->Token |= (bTgl & 1) << 19;
373 td->Token |= (Addr & 0xF) << 15;
374 td->Token |= ((Addr/16) & 0xFF) << 8;
378 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
380 || MM_GetPhysAddr( Data ) >> 32
384 td->BufferPointer = MM_AllocPhysRange(1, 32);
386 LOG("Allocated page %x", td->BufferPointer);
388 if( Type == 0x69 ) // IN token
391 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
392 info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
393 info->FirstPage = MM_GetPhysAddr( Data );
394 info->SecondPage = MM_GetPhysAddr( (const char *)Data + Length - 1 );
398 LOG("Relocated OUT/SETUP");
399 void *ptr = MM_MapTemp(td->BufferPointer);
400 memcpy( ptr, Data, Length );
402 td->Control |= TD_CTL_IOC;
404 td->_info.bFreePointer = 1;
408 td->BufferPointer = MM_GetPhysAddr( Data );
409 td->_info.bFreePointer = 0;
412 // Interrupt on completion
416 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
417 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
419 info->CallbackPtr = CbData;
423 LOG("info = %p", info);
424 td->Control |= TD_CTL_IOC;
425 td->_info.ExtraInfo = info;
431 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
434 const int qh_offsets[] = {126, 124, 120, 112, 96, 64, 0};
435 const int qh_sizes[] = { 1, 2, 4, 8, 16, 32, 64};
438 if( Period < 0 ) return ;
439 if( Period > 256 ) Period = 256;
440 if( Period == 255 ) Period = 256;
442 // Get the log base2 of the period
444 while( Period >>= 1 ) period_slot ++;
446 // Adjust for the 4ms minimum period
447 if( period_slot < 2 ) period_slot = 0;
448 else period_slot -= 2;
450 // _AppendTD calculates this from qh, but we use it to determine qh
451 TD->_info.QueueIndex = qh_offsets[period_slot];
452 // TODO: Find queue with lowest load
455 int min_load_slot = 0;
456 for( int i = 0; i < qh_sizes[period_slot]; i ++ )
459 index = qh_offsets[period_slot] + i;
461 while( index >= 0 && index < 127 )
463 qh = Cont->TDQHPage->InterruptQHs + index;
464 load += Cont->InterruptLoad[index];
465 index = ((qh->Next & ~3) - Cont->PhysTDQHPage)/sizeof(tUHCI_QH);
468 LOG("Slot %i (and below) load %i", qh_offsets[period_slot] + i, load);
470 // i = 0 will initialise the values, otherwise update if lower
471 if( i == 0 || load < min_load )
476 // - Fast return if no load
477 if( load == 0 ) break;
479 min_load_slot += qh_offsets[period_slot];
480 TD->_info.QueueIndex = min_load_slot;
481 if( min_load + (TD->Control & 0x7FF) > MAX_INTERRUPT_LOAD )
483 Log_Warning("UHCI", "Interrupt load on %i ms is too high (slot %i load %i bytes)",
484 1 << (period_slot+2), min_load_slot, min_load
487 Cont->InterruptLoad[min_load_slot] += (TD->Control & 0x7FF);
489 qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
491 LOG("period_slot = %i, QueueIndex = %i",
492 period_slot, TD->_info.QueueIndex);
494 // Stop any errors causing the TD to stop (NAK will error)
495 // - If the device is unplugged, the removal code should remove the interrupt
496 TD->Control &= ~(3 << 27);
498 UHCI_int_AppendTD(Cont, qh, TD);
501 // --------------------------------------------------------------------
503 // --------------------------------------------------------------------
504 void *UHCI_InitInterrupt(void *Ptr, int Endpt, int bOutbound,
505 int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len)
508 if( Period <= 0 ) return NULL;
510 ENTER("pPtr xEndpt bbOutbound iPeriod pCb pCbData pBuf iLen",
511 Ptr, Endpt, bOutbound, Period, Cb, CbData, Buf, Len);
513 // TODO: Data toggle?
514 td = UHCI_int_CreateTD(Ptr, Endpt, (bOutbound ? PID_OUT : PID_IN), 0, Cb, CbData, Buf, Len);
515 if( !td ) return NULL;
517 UHCI_int_SetInterruptPoll(Ptr, td, Period);
523 void *UHCI_int_InitEndpt(tUHCI_Controller *Cont, int Type, int Endpt, size_t MaxPacketSize)
525 if( Endpt >= 256*16 )
528 if( MaxPacketSize > MAX_PACKET_SIZE) {
529 Log_Warning("UHCI", "MaxPacketSize for %x greater than controller max (%i > %i)",
530 Endpt, MaxPacketSize, MAX_PACKET_SIZE);
534 if( Cont->DevInfo[Endpt / 16] == NULL ) {
535 Cont->DevInfo[Endpt / 16] = calloc( 1, sizeof(*Cont->DevInfo[0]) );
537 tUHCI_EndpointInfo *epi = &Cont->DevInfo[Endpt/16]->EndpointInfo[Endpt%16];
540 Log_Warning("UHCI", "Endpoint %x reused?", Endpt);
544 epi->MaxPacketSize = MaxPacketSize;
548 return (void*)(tVAddr)(Endpt+1);
552 void *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize)
554 return UHCI_int_InitEndpt(Ptr, 1, Endpt, MaxPacketSize);
557 void *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize)
559 return UHCI_int_InitEndpt(Ptr, 2, Endpt, MaxPacketSize);
562 void UHCI_RemoveEndpoint(void *Ptr, void *Handle)
564 tUHCI_Controller *Cont = Ptr;
568 if( (tVAddr)Handle <= 256*16 ) {
569 int addr = (tVAddr)Handle;
570 Cont->DevInfo[addr/16]->EndpointInfo[addr%16].Type = 0;
573 // TODO: Stop interrupt transaction
574 Log_Error("UHCI", "TODO: Implement stopping interrupt polling");
578 void *UHCI_SendControl(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData,
579 int bOutbound, // (1) SETUP, OUT, IN vs (0) SETUP, IN, OUT
580 const void *SetupData, size_t SetupLength,
581 const void *OutData, size_t OutLength,
582 void *InData, size_t InLength
585 ENTER("pPtr pEndpt bOutbound", Ptr, Endpt, bOutbound);
587 tUHCI_Controller *Cont = Ptr;
588 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
590 tUHCI_EndpointInfo *epi;
594 if( Endpt == NULL ) {
595 Log_Error("UHCI", "Passed a NULL Endpoint handle");
600 // Sanity check Endpt
601 if( (tVAddr)Endpt > 0x800 ) {
605 dest = (tVAddr)Endpt - 1;
606 if( Cont->DevInfo[dest/16] == NULL ) LEAVE_RET('n', NULL);
607 epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
608 if( epi->Type != 1 ) LEAVE_RET('n', NULL);
609 mps = epi->MaxPacketSize;
612 // TODO: Build up list and then append to QH in one operation
614 char *data_ptr, *status_ptr;
615 size_t data_len, status_len;
616 Uint8 data_pid, status_pid;
619 data_pid = PID_OUT; data_ptr = (void*)OutData; data_len = OutLength;
620 status_pid = PID_IN; status_ptr = InData; status_len = InLength;
623 data_pid = PID_IN; data_ptr = InData; data_len = InLength;
624 status_pid = PID_OUT; status_ptr = (void*)OutData; status_len = OutLength;
627 // Sanity check data lengths
628 if( SetupLength > mps ) LEAVE_RET('n', NULL);
629 if( status_len > mps ) LEAVE_RET('n', NULL);
631 // Create and append SETUP packet
632 td = UHCI_int_CreateTD(Cont, dest, PID_SETUP, tgl, NULL, NULL, (void*)SetupData, SetupLength);
633 UHCI_int_AppendTD(Cont, qh, td);
637 while( data_len > 0 )
639 size_t len = MIN(data_len, mps);
640 td = UHCI_int_CreateTD(Cont, dest, data_pid, tgl, NULL, NULL, data_ptr, len);
641 UHCI_int_AppendTD(Cont, qh, td);
649 td = UHCI_int_CreateTD(Cont, dest, status_pid, tgl, Cb, CbData, status_ptr, status_len);
650 UHCI_int_AppendTD(Cont, qh, td);
653 // Update toggle value
657 // for( int i = 0; i < 1024; i ++ )
659 // LOG("- FrameList[%i] = %x", i, Cont->FrameList[i]);
667 void *UHCI_SendBulk(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData, int bOutbound, void *Data, size_t Length)
669 tUHCI_Controller *Cont = Ptr;
670 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
672 tUHCI_EndpointInfo *epi;
676 ENTER("pPtr pEndpt pCb pCbData bOutbound pData iLength", Ptr, Endpt, Cb, CbData, bOutbound, Data, Length);
678 if( Endpt == NULL ) {
679 Log_Error("UHCI", "_SendBulk passed a NULL endpoint handle");
684 // Sanity check Endpt
685 if( (tVAddr)Endpt > 256*16 ) {
686 Log_Error("UHCI", "_SendBulk passed an interrupt endpoint handle");
690 dest = (tVAddr)Endpt - 1;
691 if( Cont->DevInfo[dest/16] == NULL ) {
692 Log_Error("UHCI", "_SendBulk passed an uninitialised handle");
696 epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
697 if( epi->Type != 2 ) {
698 Log_Error("UHCI", "_SendBulk passed an invalid endpoint type (%i!=2)", epi->Type);
703 mps = epi->MaxPacketSize;
705 Uint8 pid = (bOutbound ? PID_OUT : PID_IN);
710 size_t len = MIN(mps, Length);
712 td = UHCI_int_CreateTD(Cont, dest, pid, tgl, Cb, (len == Length ? CbData : NULL), pos, len);
713 UHCI_int_AppendTD(Cont, qh, td);
726 // ==========================
727 // === INTERNAL FUNCTIONS ===
728 // ==========================
729 void UHCI_CheckPortUpdate(void *Ptr)
731 tUHCI_Controller *Host = Ptr;
733 for( int i = 0; i < 2; i ++ )
735 int port = PORTSC1 + i*2;
738 status = _InWord(Host, port);
739 // Check for port change
740 if( !(status & 0x0002) ) continue;
741 _OutWord(Host, port, 0x0002);
743 // Check if the port is connected
746 // Tell the USB code it's gone.
747 USB_DeviceDisconnected(Host->RootHub, i);
752 LOG("Port %i has something", i);
753 // Reset port (set bit 9)
755 _OutWord(Host, port, 0x0200);
756 Time_Delay(50); // 50ms delay
757 _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
760 Time_Delay(50); // 50ms delay
761 _OutWord(Host, port, _InWord(Host, port) | 0x0004);
762 // Tell USB there's a new device
763 USB_DeviceConnected(Host->RootHub, i);
768 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
770 if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
772 PAddr -= Controller->PhysTDQHPage;
773 PAddr -= (128+2)*sizeof(tUHCI_QH);
774 if( PAddr > PAGE_SIZE ) return NULL; // Wrapping will bring above page size
775 PAddr /= sizeof(tUHCI_TD);
776 return &Controller->TDQHPage->LocalTDPool[PAddr];
780 tPAddr global_pool = MM_GetPhysAddr( gaUHCI_TDPool );
782 if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE ) return NULL;
784 PAddr -= global_pool;
785 PAddr /= sizeof(tUHCI_TD);
786 return &gaUHCI_TDPool[PAddr];
789 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
791 tUHCI_TD *td, *prev = NULL;
795 // Disable controller
796 _OutWord( Cont, USBCMD, 0x0000 );
800 LOG("cur_td = 0x%08x", cur_td);
801 while( !(cur_td & 1) )
803 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
805 Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
811 if( td->Control & TD_CTL_ACTIVE ) {
812 LOG("%p still active", td);
818 LOG("Removed %p from QH %p", td, QH);
821 QH->Child = td->Link;
823 prev->Link = td->Link;
828 if( nCleaned == 0 ) {
829 LOG("Nothing cleaned... what the?");
832 // re-enable controller
833 _OutWord( Cont, USBCMD, 0x0001 );
836 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
838 int byte_count = (TD->Control & 0x7FF)+1;
839 tUHCI_ExtraTDInfo *info = TD->_info.ExtraInfo;
841 // Handle non page-aligned destination (or with a > 32-bit paddr)
842 // TODO: Needs fixing for alignment issues
846 int src_ofs = TD->BufferPointer & (PAGE_SIZE-1);
847 src = MM_MapTemp(TD->BufferPointer);
848 dest = MM_MapTemp(info->FirstPage);
849 // Check for a single page transfer
850 if( byte_count + info->Offset <= PAGE_SIZE )
852 LOG("Single page copy %P to %P of %p",
853 TD->BufferPointer, info->FirstPage, TD);
854 memcpy(dest + info->Offset, src + src_ofs, byte_count);
859 LOG("Multi page copy %P to (%P,%P) of %p",
860 TD->BufferPointer, info->FirstPage, info->SecondPage, TD);
861 int part_len = PAGE_SIZE - info->Offset;
862 memcpy(dest + info->Offset, src + src_ofs, part_len);
864 dest = MM_MapTemp(info->SecondPage);
865 memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
872 if( info->Callback != NULL )
874 LOG("Calling cb %p (%i bytes)", info->Callback, byte_count);
875 void *ptr = MM_MapTemp( TD->BufferPointer );
876 info->Callback( info->CallbackPtr, ptr, byte_count );
881 if( TD->_info.QueueIndex > 127 )
884 TD->_info.ExtraInfo = NULL;
888 void UHCI_int_InterruptThread(void *Pointer)
890 tUHCI_Controller *Cont = Pointer;
891 Threads_SetName("UHCI Interrupt Handler");
898 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
901 for( int i = 0; i < NUM_TDs; i ++ )
905 td = &gaUHCI_TDPool[i];
907 // Skip completely inactive TDs
908 if( td->_info.bActive == 0 ) continue ;
909 // Skip ones that are still in use
910 if( td->Control & TD_CTL_ACTIVE ) continue ;
914 // If no callback/alt buffer, mark as free and move on
915 if( td->_info.ExtraInfo )
917 UHCI_int_HandleTDComplete(Cont, td);
921 if( td->Control & 0x00FF0000 ) {
922 LOG("td->control(Status) = %s%s%s%s%s%s%s%s",
923 td->Control & TD_CTL_ACTIVE ? "Active, " : "",
924 td->Control & TD_CTL_STALLED ? "Stalled, " : "",
925 td->Control & TD_CTL_DATABUFERR ? "Data Buffer Error, " : "",
926 td->Control & TD_CTL_BABBLE ? "Babble, " : "",
927 td->Control & TD_CTL_NAK ? "NAK, " : "",
928 td->Control & TD_CTL_CRCERR ? "CRC Error, " : "",
929 td->Control & TD_CTL_BITSTUFF ? "Bitstuff Error, " : "",
930 td->Control & TD_CTL_RESERVED ? "Reserved " : ""
932 LOG("From queue %i", td->_info.QueueIndex);
933 // Clean up QH (removing all inactive entries)
934 UHCI_int_CleanQH(Cont, Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex);
938 // Handle rescheduling of interrupt TDs
939 if( td->_info.QueueIndex <= 127 )
941 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.QueueIndex);
942 // TODO: Flip toggle?
943 td->Control |= TD_CTL_ACTIVE;
944 // Add back into controller's interrupt list
947 Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
954 if( td->_info.bFreePointer )
955 MM_DerefPhys( td->BufferPointer );
958 LOG("Cleaned %p (->Control = %x)", td, td->Control);
959 td->_info.bActive = 0;
963 LOG("Why did you wake me?");
968 void UHCI_InterruptHandler(int IRQ, void *Ptr)
970 tUHCI_Controller *Host = Ptr;
971 // int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
972 Uint16 status = _InWord(Host, USBSTS);
974 LOG("%p: status = 0x%04x", Ptr, status);
976 // Interrupt-on-completion
979 // TODO: Support isochronous transfers (will need updating the frame pointer)
980 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
983 // USB Error Interrupt
990 // - Fired if in suspend state and a USB device sends the RESUME signal
1002 // Host Controller Process Error
1005 Log_Error("UHCI", "Host controller process error on controller %p", Ptr);
1008 _OutWord(Host, USBSTS, status);
1011 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
1013 if( Host->MemIOMap )
1014 ((Uint8*)Host->MemIOMap)[Reg] = Value;
1016 outb(Host->IOBase + Reg, Value);
1019 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
1021 if( Host->MemIOMap )
1022 Host->MemIOMap[Reg/2] = Value;
1024 outw(Host->IOBase + Reg, Value);
1027 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
1029 if( Host->MemIOMap )
1030 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
1032 outd(Host->IOBase + Reg, Value);
1035 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
1037 if( Host->MemIOMap )
1038 return Host->MemIOMap[Reg/2];
1040 return inw(Host->IOBase + Reg);