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
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[NUM_TDs];
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;
95 // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
96 while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
98 tUHCI_Controller *cinfo = &gUHCI_Controllers[i];
100 // NOTE: Check "protocol" from PCI?
103 base_addr = PCI_GetBAR(id, 4);
107 cinfo->IOBase = base_addr & ~1;
108 cinfo->MemIOMap = NULL;
112 cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
114 cinfo->IRQNum = PCI_GetIRQ(id);
116 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
117 id, base_addr, cinfo->IRQNum);
119 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
122 ret = UHCI_int_InitHost(cinfo);
129 // Spin off interrupt handling thread
130 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
133 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
134 LOG("cinfo->RootHub = %p", cinfo->RootHub);
139 if(i == MAX_CONTROLLERS) {
140 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
142 LEAVE('i', MODULE_ERR_OK);
143 return MODULE_ERR_OK;
147 * \fn void UHCI_Cleanup()
148 * \brief Called just before module is unloaded
155 * \brief Initialises a UHCI host controller
156 * \param Host Pointer - Host to initialise
158 int UHCI_int_InitHost(tUHCI_Controller *Host)
160 ENTER("pHost", Host);
162 _OutWord( Host, USBCMD, 4 ); // GRESET
164 _OutWord( Host, USBCMD, 0 ); // GRESET
166 // Allocate Frame List
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 = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_256ms[ofs] );
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;
202 for( int i = 0; i < 32; i ++ ) {
203 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_128ms[i] );
204 Host->TDQHPage->InterruptQHs_256ms[i*2 ].Next = addr | 2;
205 Host->TDQHPage->InterruptQHs_256ms[i*2+1].Next = addr | 2;
207 for( int i = 0; i < 16; i ++ ) {
208 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_64ms[i] );
209 Host->TDQHPage->InterruptQHs_128ms[i*2 ].Next = addr | 2;
210 Host->TDQHPage->InterruptQHs_128ms[i*2+1].Next = addr | 2;
212 for( int i = 0; i < 8; i ++ ) {
213 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_32ms[i] );
214 Host->TDQHPage->InterruptQHs_64ms[i*2 ].Next = addr | 2;
215 Host->TDQHPage->InterruptQHs_64ms[i*2+1].Next = addr | 2;
217 for( int i = 0; i < 4; i ++ ) {
218 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_16ms[i] );
219 Host->TDQHPage->InterruptQHs_32ms[i*2 ].Next = addr | 2;
220 Host->TDQHPage->InterruptQHs_32ms[i*2+1].Next = addr | 2;
222 for( int i = 0; i < 2; i ++ ) {
223 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_8ms[i] );
224 Host->TDQHPage->InterruptQHs_16ms[i*2 ].Next = addr | 2;
225 Host->TDQHPage->InterruptQHs_16ms[i*2+1].Next = addr | 2;
227 for( int i = 0; i < 1; i ++ ) {
228 Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_4ms[i] );
229 Host->TDQHPage->InterruptQHs_8ms[i*2 ].Next = addr | 2;
230 Host->TDQHPage->InterruptQHs_8ms[i*2+1].Next = addr | 2;
232 Host->TDQHPage->InterruptQHs_4ms[0].Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH ) | 2;
233 // Set child pointers
234 for( int i = 0; i < 127; i ++ ) {
235 Host->TDQHPage->InterruptQHs_256ms[i].Child = 1;
238 // Set up control and bulk queues
239 Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
240 Host->TDQHPage->ControlQH.Child = 1;
241 Host->TDQHPage->BulkQH.Next = 1;
242 Host->TDQHPage->BulkQH.Child = 1;
244 // Set frame length to 1 ms
245 _OutByte( Host, SOFMOD, 64 );
248 _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
249 _OutWord( Host, FRNUM, 0 );
252 _OutWord( Host, USBINTR, 0x000F );
253 PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
256 _OutWord( Host, USBCMD, 0x0001 );
262 // --------------------------------------------------------------------
263 // TDs and QH Allocation/Appending
264 // --------------------------------------------------------------------
265 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
268 Mutex_Acquire( &lock );
269 for( int i = 0; i < NUM_TDs; i ++ )
271 if(gaUHCI_TDPool[i]._info.bActive == 0)
273 gaUHCI_TDPool[i].Link = 1;
274 gaUHCI_TDPool[i].Control = (1 << 23);
275 gaUHCI_TDPool[i]._info.bActive = 1;
276 gaUHCI_TDPool[i]._info.period_entry = 0;
277 Mutex_Release( &lock );
278 return &gaUHCI_TDPool[i];
281 Mutex_Release( &lock );
285 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
287 static tMutex lock; // TODO: Should I use a shortlock (avoid being preempted)
289 Mutex_Acquire(&lock);
291 // Ensure that there is an interrupt for each used frame
292 TD->Control |= (1 << 24);
295 _OutWord( Cont, USBCMD, 0x0000 );
299 if( QH->Child & 1 ) {
300 QH->Child = MM_GetPhysAddr( (tVAddr)TD );
304 QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
308 // Reenable controller
309 _OutWord( Cont, USBCMD, 0x0001 );
312 LOG("QH(%p)->Child = %x", QH, QH->Child);
313 LOG("TD(%p)->Control = %x", TD, TD->Control);
315 Mutex_Release(&lock);
319 * \brief Send a transaction to the USB bus
320 * \param Cont Controller pointer
321 * \param Addr Function Address * 16 + Endpoint
322 * \param bTgl Data toggle value
324 tUHCI_TD *UHCI_int_CreateTD(
325 tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
326 tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
329 tUHCI_ExtraTDInfo *info = NULL;
331 if( Length > 0x400 ) {
332 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
333 return NULL; // Controller allows up to 0x500, but USB doesn't
336 td = UHCI_int_AllocateTD(Cont);
338 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
342 LOG("TD %p %i bytes, Type %x to 0x%x",
343 td, Length, Type, Addr);
345 td->Control = (Length - 1) & 0x7FF;
346 td->Control |= (1 << 23);
347 td->Token = ((Length - 1) & 0x7FF) << 21;
348 td->Token |= (bTgl & 1) << 19;
349 td->Token |= (Addr & 0xF) << 15;
350 td->Token |= ((Addr/16) & 0xFF) << 8;
354 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
356 || MM_GetPhysAddr( (tVAddr)Data ) >> 32
360 td->BufferPointer = MM_AllocPhysRange(1, 32);
362 LOG("Allocated page %x", td->BufferPointer);
364 if( Type == 0x69 ) // IN token
367 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
368 info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
369 info->FirstPage = MM_GetPhysAddr( (tVAddr)Data );
370 info->SecondPage = MM_GetPhysAddr( (tVAddr)Data + Length - 1 );
374 LOG("Relocated OUT/SETUP");
375 tVAddr ptr = MM_MapTemp(td->BufferPointer);
376 memcpy( (void*)ptr, Data, Length );
378 td->Control |= (1 << 24);
380 td->_info.bFreePointer = 1;
384 td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
385 td->_info.bFreePointer = 0;
388 // Interrupt on completion
392 info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
393 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
395 info->CallbackPtr = CbData;
399 LOG("info = %p", info);
400 td->Control |= (1 << 24);
401 td->_info.ExtraInfo = info;
407 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
410 const int qh_offsets[] = { 0, 64, 96, 112, 120, 124, 126};
411 // const int qh_sizes[] = {64, 32, 16, 8, 4, 2, 1};
414 if( Period < 0 ) return ;
415 if( Period > 256 ) 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 TD->_info.period_entry = qh_offsets[period_slot] + 1;
426 qh = Cont->TDQHPage->InterruptQHs_4ms + TD->_info.period_entry - 1;
427 // TODO: Find queue with lowest load
429 LOG("period_slot = %i, period_entry = %i (+1 when encoded)",
430 period_slot, TD->_info.period_entry);
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
478 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
480 tUHCI_Controller *Cont = Ptr;
481 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
484 ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
486 td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
487 UHCI_int_AppendTD(Cont, qh, td);
493 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
495 tUHCI_Controller *Cont = Ptr;
496 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
499 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
501 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
502 UHCI_int_AppendTD(Cont, qh, td);
507 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
509 tUHCI_Controller *Cont = Ptr;
510 tUHCI_QH *qh = &Cont->TDQHPage->ControlQH;
513 ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
515 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, !!Tgl, Cb, CbData, Data, Length);
516 UHCI_int_AppendTD(Cont, qh, td);
522 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
524 tUHCI_Controller *Cont = Ptr;
525 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
529 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
531 while( Length > MAX_PACKET_SIZE )
533 LOG("MaxPacket (rem = %i)", Length);
534 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, MAX_PACKET_SIZE);
535 UHCI_int_AppendTD(Cont, qh, td);
538 Length -= MAX_PACKET_SIZE;
539 src += MAX_PACKET_SIZE;
543 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length);
544 UHCI_int_AppendTD(Cont, qh, td);
549 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
551 tUHCI_Controller *Cont = Ptr;
552 tUHCI_QH *qh = &Cont->TDQHPage->BulkQH;
556 ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
557 while( Length > MAX_PACKET_SIZE )
559 LOG("MaxPacket (rem = %i)", Length);
560 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, MAX_PACKET_SIZE);
561 UHCI_int_AppendTD(Cont, qh, td);
564 Length -= MAX_PACKET_SIZE;
565 dst += MAX_PACKET_SIZE;
569 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length);
570 UHCI_int_AppendTD(Cont, qh, td);
576 // === INTERNAL FUNCTIONS ===
577 void UHCI_CheckPortUpdate(void *Ptr)
579 tUHCI_Controller *Host = Ptr;
581 for( int i = 0; i < 2; i ++ )
583 int port = PORTSC1 + i*2;
586 status = _InWord(Host, port);
587 // Check for port change
588 if( !(status & 0x0002) ) continue;
589 _OutWord(Host, port, 0x0002);
591 // Check if the port is connected
594 // Tell the USB code it's gone.
595 USB_DeviceDisconnected(Host->RootHub, i);
600 LOG("Port %i has something", i);
601 // Reset port (set bit 9)
603 _OutWord(Host, port, 0x0200);
604 Time_Delay(50); // 50ms delay
605 _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
608 Time_Delay(50); // 50ms delay
609 _OutWord(Host, port, _InWord(Host, port) | 0x0004);
610 // Tell USB there's a new device
611 USB_DeviceConnected(Host->RootHub, i);
616 void UHCI_int_InterruptThread(void *Pointer)
618 tUHCI_Controller *Cont = Pointer;
619 Threads_SetName("UHCI Interrupt Handler");
624 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
627 for( int i = 0; i < NUM_TDs; i ++ )
630 tUHCI_ExtraTDInfo *info;
633 td = &gaUHCI_TDPool[i];
634 info = td->_info.ExtraInfo;
636 // Skip completely inactive TDs
637 if( td->_info.bActive == 0 ) continue ;
638 // Skip ones that are still in use
639 if( td->Control & (1 << 23) ) continue ;
641 // If no callback/alt buffer, mark as free and move on
642 if( td->_info.ExtraInfo )
644 // Get size of transfer
645 byte_count = (td->Control & 0x7FF)+1;
647 // Handle non page-aligned destination (or with a > 32-bit paddr)
651 int src_ofs = td->BufferPointer & (PAGE_SIZE-1);
652 src = (void *) MM_MapTemp(td->BufferPointer);
653 dest = (void *) MM_MapTemp(info->FirstPage);
654 // Check for a single page transfer
655 if( byte_count + info->Offset <= PAGE_SIZE )
657 LOG("Single page copy %P to %P of %p",
658 td->BufferPointer, info->FirstPage, td);
659 memcpy(dest + info->Offset, src + src_ofs, byte_count);
664 LOG("Multi page copy %P to (%P,%P) of %p",
665 td->BufferPointer, info->FirstPage, info->SecondPage, td);
666 int part_len = PAGE_SIZE - info->Offset;
667 memcpy(dest + info->Offset, src + src_ofs, part_len);
668 MM_FreeTemp( (tVAddr)dest );
669 dest = (void *) MM_MapTemp(info->SecondPage);
670 memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
672 MM_FreeTemp( (tVAddr)src );
673 MM_FreeTemp( (tVAddr)dest );
677 if( info->Callback != NULL )
679 LOG("Calling cb %p", info->Callback);
680 void *ptr = (void *) MM_MapTemp( td->BufferPointer );
681 info->Callback( info->CallbackPtr, ptr, byte_count );
682 MM_FreeTemp( (tVAddr)ptr );
687 td->_info.ExtraInfo = NULL;
690 if( td->_info.period_entry > 0 )
692 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.period_entry-1);
693 // TODO: Flip toggle?
694 td->Control |= (1 << 23);
695 // Add back into controller's interrupt list
698 Cont->TDQHPage->InterruptQHs_256ms + td->_info.period_entry - 1,
704 if( td->_info.bFreePointer )
705 MM_DerefPhys( td->BufferPointer );
708 td->_info.bActive = 0;
709 LOG("Cleaned %p", td);
714 void UHCI_InterruptHandler(int IRQ, void *Ptr)
716 tUHCI_Controller *Host = Ptr;
717 // int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
718 Uint16 status = _InWord(Host, USBSTS);
720 // Interrupt-on-completion
723 // TODO: Support isochronous transfers (will need updating the frame pointer)
724 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
727 LOG("status = 0x%04x", status);
728 _OutWord(Host, USBSTS, status);
731 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
734 ((Uint8*)Host->MemIOMap)[Reg] = Value;
736 outb(Host->IOBase + Reg, Value);
739 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
742 Host->MemIOMap[Reg/2] = Value;
744 outw(Host->IOBase + Reg, Value);
747 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
750 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
752 outd(Host->IOBase + Reg, Value);
755 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
758 return Host->MemIOMap[Reg/2];
760 return inw(Host->IOBase + Reg);