Modules/UHCI - Fixed interrupt bug that caused keyboard to not work
[tpg/acess2.git] / KernelLand / Modules / USB / UHCI / uhci.c
1 /*
2  * Acess 2 USB Stack
3  * - By John Hodge (thePowersGang)
4  *
5  * Universal Host Controller Interface
6  */
7 #define DEBUG   0
8 #define VERSION VER2(0,5)
9 #include <acess.h>
10 #include <vfs.h>
11 #include <drv_pci.h>
12 #include <modules.h>
13 #include <usb_host.h>
14 #include "uhci.h"
15 #include <timers.h>
16 #include <semaphore.h>
17
18 // === CONSTANTS ===
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
24
25 #define PID_IN  0x69
26 #define PID_OUT 0xE1
27 #define PID_SETUP       0x2D
28
29 // === PROTOTYPES ===
30  int    UHCI_Initialise(char **Arguments);
31 void    UHCI_Cleanup();
32  int    UHCI_int_InitHost(tUHCI_Controller *Host);
33 // -- List internals
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);
37 // --- API
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);
46
47 void    UHCI_CheckPortUpdate(void *Ptr);
48 void    UHCI_int_InterruptThread(void *Unused);
49 void    UHCI_InterruptHandler(int IRQ, void *Ptr);
50 // 
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);
55
56 // === GLOBALS ===
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,
64         
65         .ControlSETUP = UHCI_ControlSETUP,
66         .ControlIN    = UHCI_ControlIN,
67         .ControlOUT   = UHCI_ControlOUT,
68
69         .BulkOUT = UHCI_BulkOUT,
70         .BulkIN = UHCI_BulkIN,
71         
72         .CheckPorts = UHCI_CheckPortUpdate
73         };
74 tSemaphore      gUHCI_InterruptSempahore;
75
76 // === CODE ===
77 /**
78  * \fn int UHCI_Initialise()
79  * \brief Called to initialise the UHCI Driver
80  */
81 int UHCI_Initialise(char **Arguments)
82 {
83          int    i=0, id=-1;
84          int    ret;
85         
86         ENTER("");
87         
88         // Initialise with no maximum value
89         Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
90
91         if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 )
92         {
93                 LEAVE('i', MODULE_ERR_NOTNEEDED);
94                 return MODULE_ERR_NOTNEEDED;
95         }
96
97         {
98                 tPAddr  tmp;    
99                 gaUHCI_TDPool = (void *) MM_AllocDMA(1, 32, &tmp);
100         }
101
102         // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
103         while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
104         {
105                 tUHCI_Controller        *cinfo = &gUHCI_Controllers[i];
106                 Uint32  base_addr;
107                 // NOTE: Check "protocol" from PCI?
108                 
109                 cinfo->PciId = id;
110                 base_addr = PCI_GetBAR(id, 4);
111                 
112                 if( base_addr & 1 )
113                 {
114                         cinfo->IOBase = base_addr & ~1;
115                         cinfo->MemIOMap = NULL;
116                 }
117                 else
118                 {
119                         cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
120                 }
121                 cinfo->IRQNum = PCI_GetIRQ(id);
122                 
123                 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
124                         id, base_addr, cinfo->IRQNum);
125                 
126                 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
127         
128                 // Initialise Host
129                 ret = UHCI_int_InitHost(cinfo);
130                 // Detect an error
131                 if(ret != 0) {
132                         LEAVE('i', ret);
133                         return ret;
134                 }
135
136                 // Spin off interrupt handling thread
137                 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
138
139                 
140                 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
141                 LOG("cinfo->RootHub = %p", cinfo->RootHub);
142
143                 i ++;
144         }
145
146         if(i == MAX_CONTROLLERS) {
147                 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
148         }
149         LEAVE('i', MODULE_ERR_OK);
150         return MODULE_ERR_OK;
151 }
152
153 /**
154  * \fn void UHCI_Cleanup()
155  * \brief Called just before module is unloaded
156  */
157 void UHCI_Cleanup()
158 {
159 }
160
161 /**
162  * \brief Initialises a UHCI host controller
163  * \param Host  Pointer - Host to initialise
164  */
165 int UHCI_int_InitHost(tUHCI_Controller *Host)
166 {
167         ENTER("pHost", Host);
168
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");
174                 LEAVE('i', -1);
175                 return -1;
176         }
177
178         Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
179         if( !Host->TDQHPage ) {
180                 // TODO: Clean up
181                 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
182                 LEAVE('i', -1);
183                 return -1;
184         }
185
186         // Fill frame list
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
191                 };
192         for( int i = 0; i < 1024; i ++ ) {
193                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH );
194                 Host->FrameList[i] = addr | 2;
195         }
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;
205         }
206
207         // Build up interrupt binary tree       
208         {
209                 tUHCI_QH        *dest = Host->TDQHPage->InterruptQHs;
210                 Uint32  destphys = Host->PhysTDQHPage;
211                 
212                 // Set up next pointer to index to i/2 in the next step
213                 for( int _count = 64; _count > 1; _count /= 2 )
214                 {
215                         for( int i = 0; i < _count; i ++ ) {
216                                 dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
217                                 dest[i].Child = 1;
218                         }
219                         dest += _count; destphys += _count * sizeof(tUHCI_QH);
220                 }
221                 // Skip padding, and move to control QH
222                 dest->Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
223                 dest->Child = 1;
224         }
225
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;
231
232         // Global reset 
233         _OutWord( Host, USBCMD, 4 );
234         Time_Delay(10);
235         _OutWord( Host, USBCMD, 0 );
236         
237         // Allocate Frame List
238         // Set frame length to 1 ms
239         _OutByte( Host, SOFMOD, 64 );
240         
241         // Set Frame List
242         _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
243         _OutWord( Host, FRNUM, 0 );
244         
245         // Enable Interrupts
246         _OutWord( Host, USBINTR, 0x000F );
247         PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
248
249         // Enable processing
250         _OutWord( Host, USBCMD, 0x0001 );
251
252         LEAVE('i', 0);
253         return 0;
254 }
255
256 // --------------------------------------------------------------------
257 // TDs and QH Allocation/Appending
258 // --------------------------------------------------------------------
259 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
260 {
261         static tMutex   lock;
262         Mutex_Acquire( &lock );
263         for( int i = 0; i < NUM_TDs; i ++ )
264         {
265                 if(gaUHCI_TDPool[i]._info.bActive == 0)
266                 {
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];
273                 }
274         }
275         Mutex_Release( &lock );
276         return NULL;
277 }
278
279 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
280 {
281         static tMutex   lock;   // TODO: Should I use a shortlock (avoid being preempted)
282
283         Mutex_Acquire(&lock);
284         
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);
288         // Update length
289         TD->Control &= ~0x7FF;
290         TD->Control |= (TD->Token >> 21) & 0x7FF;
291
292         // Stop controller
293         _OutWord( Cont, USBCMD, 0x0000 );
294         
295         // Add
296         TD->Link = 1;
297         if( QH->Child & 1 ) {
298                 QH->Child = MM_GetPhysAddr( (tVAddr)TD );
299         }
300         else {
301                 // Depth first
302                 QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
303         }
304         QH->_LastItem = TD;
305
306         // Reenable controller
307         _OutWord( Cont, USBCMD, 0x0001 );
308         
309         // DEBUG!
310         LOG("QH(%p)->Child = %x", QH, QH->Child);
311         LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
312
313         Mutex_Release(&lock);
314 }
315
316 /**
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
321  */
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)
325 {
326         tUHCI_TD        *td;
327         tUHCI_ExtraTDInfo       *info = NULL;
328
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
332         }
333
334         td = UHCI_int_AllocateTD(Cont);
335         if( !td ) {
336                 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
337                 return NULL;
338         }
339
340         LOG("TD %p %i bytes, Type %x to 0x%x",
341                 td, Length, Type, Addr);
342
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;
350         td->Token |= Type;
351
352         if(
353                 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
354         #if PHYS_BITS > 32
355                 || MM_GetPhysAddr( (tVAddr)Data ) >> 32
356         #endif
357                 )
358         {
359                 td->BufferPointer = MM_AllocPhysRange(1, 32);
360
361                 LOG("Allocated page %x", td->BufferPointer);            
362
363                 if( Type == 0x69 )      // IN token
364                 {
365                         LOG("Relocated IN");
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 );
370                 }
371                 else
372                 {
373                         LOG("Relocated OUT/SETUP");
374                         tVAddr  ptr = MM_MapTemp(td->BufferPointer);
375                         memcpy( (void*)ptr, Data, Length );
376                         MM_FreeTemp(ptr);
377                         td->Control |= (1 << 24);
378                 }
379                 td->_info.bFreePointer = 1;
380         }
381         else
382         {
383                 td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
384                 td->_info.bFreePointer = 0;
385         }
386
387         // Interrupt on completion
388         if( Cb )
389         {
390                 if( !info )
391                         info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
392                 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
393                 info->Callback = Cb;
394                 info->CallbackPtr = CbData;
395         }
396         
397         if( info ) {
398                 LOG("info = %p", info);
399                 td->Control |= (1 << 24);
400                 td->_info.ExtraInfo = info;
401         }
402
403         return td;
404 }
405
406 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
407 {
408         tUHCI_QH        *qh;
409         const int       qh_offsets[] = {126, 124, 120, 112, 96, 64,  0};
410         const int       qh_sizes[]   = {  1,   2,   4,   8, 16, 32, 64};
411         
412         // Bounds limit
413         if( Period < 0 )        return ;
414         if( Period > 256 )      Period = 256;
415         if( Period == 255 )     Period = 256;
416
417         // Get the log base2 of the period
418          int    period_slot = 0;
419         while( Period >>= 1 )   period_slot ++;
420
421         // Adjust for the 4ms minimum period
422         if( period_slot < 2 )   period_slot = 0;
423         else    period_slot -= 2;
424         
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
428 #if 1
429          int    min_load = 0;
430          int    min_load_slot = 0;
431         for( int i = 0; i < qh_sizes[period_slot]; i ++ )
432         {
433                 int load, index;
434                 index = qh_offsets[period_slot] + i;
435                 load = 0;
436                 while( index >= 0 && index < 127 )
437                 {
438                         qh = Cont->TDQHPage->InterruptQHs + index;
439                         load += Cont->InterruptLoad[index];
440                         index = ((qh->Next & ~3) - Cont->PhysTDQHPage)/sizeof(tUHCI_QH);
441                 }
442
443                 LOG("Slot %i (and below) load %i", qh_offsets[period_slot] + i, load);
444
445                 // i = 0 will initialise the values, otherwise update if lower
446                 if( i == 0 || load < min_load )
447                 {
448                         min_load = load;
449                         min_load_slot = i;
450                 }
451                 // - Fast return if no load
452                 if( load == 0 ) break;
453         }
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 )
457         {
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
460                         );
461         }
462         Cont->InterruptLoad[min_load_slot] += (TD->Control & 0x7FF);
463 #endif
464         qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
465
466         LOG("period_slot = %i, QueueIndex = %i",
467                 period_slot, TD->_info.QueueIndex);
468
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);
472
473         UHCI_int_AppendTD(Cont, qh, TD);
474 }
475
476 void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
477 {
478         tUHCI_TD        *td;
479
480         if( Period < 0 )        return NULL;
481
482         ENTER("pPtr xDest iPeriod pCb pCbData pBuf iLength",
483                 Ptr, Dest, Period, Cb, CbData, Buf, Length);
484
485         // TODO: Data toggle?
486         td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length);
487         if( !td )       return NULL;
488         
489         UHCI_int_SetInterruptPoll(Ptr, td, Period);
490         
491         LEAVE('p', td); 
492         return td;
493 }
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)
496 {
497         tUHCI_TD        *td;
498
499         if( Period < 0 )        return NULL;
500
501         ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
502                 Ptr, Dest, Period, Cb, CbData, Buf, Length);
503
504         // TODO: Data toggle?
505         td = UHCI_int_CreateTD(Ptr, Dest, PID_OUT, 0, Cb, CbData, Buf, Length);
506         if( !td )       return NULL;
507         
508         UHCI_int_SetInterruptPoll(Ptr, td, Period);
509
510         LEAVE('p', td); 
511         return td;
512 }
513
514 void UHCI_StopInterrupt(void *Ptr, void *Handle)
515 {
516         // TODO: Stop interrupt transaction
517         Log_Error("UHCI", "TODO: Implement UHCI_StopInterrupt");
518 }
519
520 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
521 {
522         tUHCI_Controller        *Cont = Ptr;
523         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
524         tUHCI_TD        *td;
525
526         ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
527         
528         td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
529         UHCI_int_AppendTD(Cont, qh, td);
530
531         LEAVE('p', td); 
532
533         return td;
534 }
535 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
536 {
537         tUHCI_Controller        *Cont = Ptr;
538         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
539         tUHCI_TD        *td;
540
541         ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
542
543         td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
544         UHCI_int_AppendTD(Cont, qh, td);
545
546         LEAVE('p', td);
547         return td;
548 }
549 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
550 {
551         tUHCI_Controller        *Cont = Ptr;
552         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
553         tUHCI_TD        *td;
554
555         ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
556         
557         td = UHCI_int_CreateTD(Cont, Dest, PID_IN, !!Tgl, Cb, CbData, Data, Length);
558         UHCI_int_AppendTD(Cont, qh, td);
559
560         LEAVE('p', td);
561         return td;
562 }
563
564 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
565 {
566         tUHCI_Controller        *Cont = Ptr;
567         tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
568         tUHCI_TD        *td;
569         char    *src = Buf;
570
571         ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
572
573         while( Length > MAX_PACKET_SIZE )
574         {
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);
578                 
579                 bToggle = !bToggle;
580                 Length -= MAX_PACKET_SIZE;
581                 src += MAX_PACKET_SIZE;
582         }
583
584         LOG("Final");
585         td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length);
586         UHCI_int_AppendTD(Cont, qh, td);
587
588         LEAVE('p', td);
589         return td;
590 }
591 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
592 {
593         tUHCI_Controller        *Cont = Ptr;
594         tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
595         tUHCI_TD        *td;
596         char    *dst = Buf;
597
598         ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
599         while( Length > MAX_PACKET_SIZE )
600         {
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);
604                 
605                 bToggle = !bToggle;
606                 Length -= MAX_PACKET_SIZE;
607                 dst += MAX_PACKET_SIZE;
608         }
609
610         LOG("Final");
611         td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length);
612         UHCI_int_AppendTD(Cont, qh, td);
613
614         LEAVE('p', td);
615         return td;
616 }
617
618 // === INTERNAL FUNCTIONS ===
619 void UHCI_CheckPortUpdate(void *Ptr)
620 {
621         tUHCI_Controller        *Host = Ptr;
622         // Enable ports
623         for( int i = 0; i < 2; i ++ )
624         {
625                  int    port = PORTSC1 + i*2;
626                 Uint16  status;
627         
628                 status = _InWord(Host, port);
629                 // Check for port change
630                 if( !(status & 0x0002) )        continue;
631                 _OutWord(Host, port, 0x0002);
632                 
633                 // Check if the port is connected
634                 if( !(status & 1) )
635                 {
636                         // Tell the USB code it's gone.
637                         USB_DeviceDisconnected(Host->RootHub, i);
638                         continue;
639                 }
640                 else
641                 {
642                         LOG("Port %i has something", i);
643                         // Reset port (set bit 9)
644                         LOG("Reset");
645                         _OutWord(Host, port, 0x0200);
646                         Time_Delay(50); // 50ms delay
647                         _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
648                         // Enable port
649                         LOG("Enable");
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);
654                 }
655         }
656 }
657
658 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
659 {
660         if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
661         {
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];
667         }
668
669         
670         tPAddr  global_pool = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
671         
672         if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE )   return NULL;
673         
674         PAddr -= global_pool;
675         PAddr /= sizeof(tUHCI_TD);
676         return &gaUHCI_TDPool[PAddr];
677 }
678
679 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
680 {
681         tUHCI_TD        *td, *prev = NULL;
682         Uint32  cur_td;
683
684         // Disable controller
685         _OutWord( Cont, USBCMD, 0x0000 );
686         
687         // Scan QH list
688         cur_td = QH->Child;
689         while( !(cur_td & 1) )
690         {
691                 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
692                 if(!td) {
693                         Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
694                                 QH, cur_td);
695                         break ;
696                 }
697                 
698                 // Active? Ok.
699                 if( td->Control & (1 << 23) ) {
700                         prev = td;
701                         continue ;
702                 }
703
704                 LOG("Removed %p from QH %p", td, QH);           
705
706                 if( !prev )
707                         QH->Child = td->Link;
708                 else
709                         prev->Link = td->Link;
710                 cur_td = td->Link;
711         }
712
713         // re-enable controller
714         _OutWord( Cont, USBCMD, 0x0001 );       
715 }
716
717 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
718 {
719          int    byte_count = (TD->Control & 0x7FF)+1;
720         tUHCI_ExtraTDInfo       *info = TD->_info.ExtraInfo;
721
722         // Handle non page-aligned destination (or with a > 32-bit paddr)
723         // TODO: Needs fixing for alignment issues
724         if(info->FirstPage)
725         {
726                 char    *src, *dest;
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 )
732                 {
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);
736                 }
737                 else
738                 {
739                         // Multi-page
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);
747                 }
748                 MM_FreeTemp( (tVAddr)src );
749                 MM_FreeTemp( (tVAddr)dest );
750         }
751
752         // Callback
753         if( info->Callback != NULL )
754         {
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 );
759         }
760         
761         // Clean up info
762         if( TD->_info.QueueIndex > 127 )
763         {
764                 free( info );
765                 TD->_info.ExtraInfo = NULL;
766         }
767 }
768
769 void UHCI_int_InterruptThread(void *Pointer)
770 {
771         tUHCI_Controller        *Cont = Pointer;
772         Threads_SetName("UHCI Interrupt Handler");
773         for( ;; )
774         {
775                 LOG("zzzzz....");
776                 // 0 = Take all
777                 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
778                 LOG("Huh?");
779         
780                 for( int i = 0; i < NUM_TDs; i ++ )
781                 {
782                         tUHCI_TD        *td;
783                         
784                         td = &gaUHCI_TDPool[i];
785
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 ;
790
791                         // If no callback/alt buffer, mark as free and move on
792                         if( td->_info.ExtraInfo )
793                         {
794                                 UHCI_int_HandleTDComplete(Cont, td);
795                         }
796
797                         // Error check
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 " : ""
808                                         );
809                                 // Clean up QH (removing all inactive entries)
810                                 UHCI_int_CleanQH(Cont, Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex);
811                         }
812         
813                         // Handle rescheduling of interrupt TDs
814                         if( td->_info.QueueIndex <= 127 )
815                         {
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
820                                 UHCI_int_AppendTD(
821                                         Cont,
822                                         Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
823                                         td
824                                         );
825                                 continue ;
826                         }
827
828                         // Clean up
829                         if( td->_info.bFreePointer )
830                                 MM_DerefPhys( td->BufferPointer );
831
832                         // Clean up
833                         LOG("Cleaned %p (->Control = %x)", td, td->Control);
834                         td->_info.bActive = 0;
835                 }
836         }
837 }
838
839 void UHCI_InterruptHandler(int IRQ, void *Ptr)
840 {
841         tUHCI_Controller *Host = Ptr;
842 //       int    frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
843         Uint16  status = _InWord(Host, USBSTS);
844         
845         // Interrupt-on-completion
846         if( status & 1 )
847         {
848                 // TODO: Support isochronous transfers (will need updating the frame pointer)
849                 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
850         }
851
852         LOG("status = 0x%04x", status);
853         _OutWord(Host, USBSTS, status);
854 }
855
856 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
857 {
858         if( Host->MemIOMap )
859                 ((Uint8*)Host->MemIOMap)[Reg] = Value;
860         else
861                 outb(Host->IOBase + Reg, Value);
862 }
863
864 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
865 {
866         if( Host->MemIOMap )
867                 Host->MemIOMap[Reg/2] = Value;
868         else
869                 outw(Host->IOBase + Reg, Value);
870 }
871
872 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
873 {
874         if( Host->MemIOMap )
875                 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
876         else
877                 outd(Host->IOBase + Reg, Value);
878 }
879
880 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
881 {
882         if( Host->MemIOMap )
883                 return Host->MemIOMap[Reg/2];
884         else
885                 return inw(Host->IOBase + Reg);
886 }
887

UCC git Repository :: git.ucc.asn.au