Modules/UHCI - Added option to disable at runtime
[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   1
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 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
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_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,
45         int isOutbound,
46         const void *SetupData, size_t SetupLength,
47         const void *OutData, size_t OutLength,
48         void *InData, size_t InLength
49         );
50 void    *UHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length);
51
52 void    UHCI_CheckPortUpdate(void *Ptr);
53 void    UHCI_int_InterruptThread(void *Unused);
54 void    UHCI_InterruptHandler(int IRQ, void *Ptr);
55 // 
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);
60
61 // === GLOBALS ===
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,
71         
72 //      .SendIsoch   = UHCI_SendIsoch,
73         .SendControl = UHCI_SendControl,
74         .SendBulk    = UHCI_SendBulk,
75         .FreeOp      = NULL,
76         
77         .CheckPorts = UHCI_CheckPortUpdate,
78 //      .ClearPortFeature = NULL,
79 //      .GetBusState      = NULL,
80 //      .GetPortStatus    = NULL,
81 //      .SetPortFeature   = NULL
82         };
83 tSemaphore      gUHCI_InterruptSempahore;
84
85 // === CODE ===
86 /**
87  * \fn int UHCI_Initialise()
88  * \brief Called to initialise the UHCI Driver
89  */
90 int UHCI_Initialise(char **Arguments)
91 {
92          int    i=0, id=-1;
93          int    ret;
94         
95         ENTER("");
96
97         if( Arguments && *Arguments && strcmp(*Arguments, "0") == 0 )
98         {
99                 LOG("Disabled by argument");
100                 LEAVE('i', MODULE_ERR_NOTNEEDED);
101                 return MODULE_ERR_NOTNEEDED;
102         }
103
104         // Initialise with no maximum value
105         Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
106
107         if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 )
108         {
109                 LEAVE('i', MODULE_ERR_NOTNEEDED);
110                 return MODULE_ERR_NOTNEEDED;
111         }
112
113         {
114                 tPAddr  tmp;    
115                 gaUHCI_TDPool = (void *) MM_AllocDMA(1, 32, &tmp);
116                 memset(gaUHCI_TDPool, 0, PAGE_SIZE);
117         }
118
119         // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
120         while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
121         {
122                 tUHCI_Controller        *cinfo = &gUHCI_Controllers[i];
123                 Uint32  base_addr;
124                 // NOTE: Check "protocol" from PCI?
125                 
126                 cinfo->PciId = id;
127                 base_addr = PCI_GetBAR(id, 4);
128                 
129                 if( base_addr & 1 )
130                 {
131                         cinfo->IOBase = base_addr & ~1;
132                         cinfo->MemIOMap = NULL;
133                 }
134                 else
135                 {
136                         cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
137                 }
138                 cinfo->IRQNum = PCI_GetIRQ(id);
139                 
140                 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
141                         id, base_addr, cinfo->IRQNum);
142                 
143                 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
144         
145                 // Initialise Host
146                 ret = UHCI_int_InitHost(cinfo);
147                 // Detect an error
148                 if(ret != 0) {
149                         LEAVE('i', ret);
150                         return ret;
151                 }
152
153                 // Spin off interrupt handling thread
154                 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
155
156                 
157                 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
158                 LOG("cinfo->RootHub = %p", cinfo->RootHub);
159
160                 i ++;
161         }
162
163         if(i == MAX_CONTROLLERS) {
164                 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
165         }
166         LEAVE('i', MODULE_ERR_OK);
167         return MODULE_ERR_OK;
168 }
169
170 /**
171  * \fn void UHCI_Cleanup()
172  * \brief Called just before module is unloaded
173  */
174 void UHCI_Cleanup()
175 {
176 }
177
178 /**
179  * \brief Initialises a UHCI host controller
180  * \param Host  Pointer - Host to initialise
181  */
182 int UHCI_int_InitHost(tUHCI_Controller *Host)
183 {
184         ENTER("pHost", Host);
185
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");
191                 LEAVE('i', -1);
192                 return -1;
193         }
194
195         Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
196         if( !Host->TDQHPage ) {
197                 // TODO: Clean up
198                 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
199                 LEAVE('i', -1);
200                 return -1;
201         }
202
203         // Fill frame list
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
208                 };
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;
213         }
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;
223         }
224
225         // Build up interrupt binary tree       
226         {
227                 tUHCI_QH        *dest = Host->TDQHPage->InterruptQHs;
228                 Uint32  destphys = Host->PhysTDQHPage;
229                 
230                 // Set up next pointer to index to i/2 in the next step
231                 for( int _count = 64; _count > 1; _count /= 2 )
232                 {
233                         for( int i = 0; i < _count; i ++ ) {
234                                 dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
235                                 dest[i].Child = 1;
236                         }
237                         dest += _count; destphys += _count * sizeof(tUHCI_QH);
238                 }
239                 // Skip padding, and move to control QH
240                 dest->Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
241                 dest->Child = 1;
242         }
243
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;
249
250         // Global reset 
251         _OutWord( Host, USBCMD, 4 );
252         Time_Delay(10);
253         _OutWord( Host, USBCMD, 0 );
254         
255         // Allocate Frame List
256         // Set frame length to 1 ms
257         _OutByte( Host, SOFMOD, 64 );
258         
259         // Set Frame List
260         _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
261         _OutWord( Host, FRNUM, 0 );
262         
263         // Enable Interrupts
264         _OutWord( Host, USBINTR, 0x000F );
265         PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
266
267         // Enable processing
268         _OutWord( Host, USBCMD, 0x0001 );
269
270         LEAVE('i', 0);
271         return 0;
272 }
273
274 // --------------------------------------------------------------------
275 // TDs and QH Allocation/Appending
276 // --------------------------------------------------------------------
277 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
278 {
279         static tMutex   lock;
280         Mutex_Acquire( &lock );
281         for( int i = 0; i < NUM_TDs; i ++ )
282         {
283                 if(gaUHCI_TDPool[i]._info.bActive == 0)
284                 {
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];
291                 }
292         }
293         Mutex_Release( &lock );
294         return NULL;
295 }
296
297 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
298 {
299         static tMutex   lock;   // TODO: Should I use a shortlock (avoid being preempted)
300
301         Mutex_Acquire(&lock);
302         
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);
307         // Update length
308         TD->Control &= ~0x7FF;
309         TD->Control |= (TD->Token >> 21) & 0x7FF;
310
311         // Stop controller
312         _OutWord( Cont, USBCMD, 0x0000 );
313         
314         // Add
315         TD->Link = 1;
316         if( QH->Child & 1 ) {
317                 QH->Child = MM_GetPhysAddr( TD );
318         }
319         else {
320                 // Depth first
321                 QH->_LastItem->Link = MM_GetPhysAddr( TD ) | 4;
322         }
323         QH->_LastItem = TD;
324
325         // Reenable controller
326         _OutWord( Cont, USBCMD, 0x0001 );
327         
328         // DEBUG!
329         LOG("QH(%p)->Child = %x", QH, QH->Child);
330         LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
331
332         Mutex_Release(&lock);
333 }
334
335 /**
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
340  */
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)
344 {
345         tUHCI_TD        *td;
346         tUHCI_ExtraTDInfo       *info = NULL;
347
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
351         }
352
353         td = UHCI_int_AllocateTD(Cont);
354         if( !td ) {
355                 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
356                 return NULL;
357         }
358
359         LOG("TD %p %i bytes, Type %x to 0x%x",
360                 td, Length, Type, Addr);
361
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
366         if( Addr & 0x8000 )
367                 ;
368         else
369                 td->Control |= 1 << 26;
370                 
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;
375         td->Token |= Type;
376
377         if(
378                 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
379         #if PHYS_BITS > 32
380                 || MM_GetPhysAddr( Data ) >> 32
381         #endif
382                 )
383         {
384                 td->BufferPointer = MM_AllocPhysRange(1, 32);
385
386                 LOG("Allocated page %x", td->BufferPointer);            
387
388                 if( Type == 0x69 )      // IN token
389                 {
390                         LOG("Relocated IN");
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 );
395                 }
396                 else
397                 {
398                         LOG("Relocated OUT/SETUP");
399                         void *ptr = MM_MapTemp(td->BufferPointer);
400                         memcpy( ptr, Data, Length );
401                         MM_FreeTemp(ptr);
402                         td->Control |= TD_CTL_IOC;
403                 }
404                 td->_info.bFreePointer = 1;
405         }
406         else
407         {
408                 td->BufferPointer = MM_GetPhysAddr( Data );
409                 td->_info.bFreePointer = 0;
410         }
411
412         // Interrupt on completion
413         if( Cb )
414         {
415                 if( !info )
416                         info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
417                 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
418                 info->Callback = Cb;
419                 info->CallbackPtr = CbData;
420         }
421         
422         if( info ) {
423                 LOG("info = %p", info);
424                 td->Control |= TD_CTL_IOC;
425                 td->_info.ExtraInfo = info;
426         }
427
428         return td;
429 }
430
431 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
432 {
433         tUHCI_QH        *qh;
434         const int       qh_offsets[] = {126, 124, 120, 112, 96, 64,  0};
435         const int       qh_sizes[]   = {  1,   2,   4,   8, 16, 32, 64};
436         
437         // Bounds limit
438         if( Period < 0 )        return ;
439         if( Period > 256 )      Period = 256;
440         if( Period == 255 )     Period = 256;
441
442         // Get the log base2 of the period
443          int    period_slot = 0;
444         while( Period >>= 1 )   period_slot ++;
445
446         // Adjust for the 4ms minimum period
447         if( period_slot < 2 )   period_slot = 0;
448         else    period_slot -= 2;
449         
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
453 #if 1
454          int    min_load = 0;
455          int    min_load_slot = 0;
456         for( int i = 0; i < qh_sizes[period_slot]; i ++ )
457         {
458                 int load, index;
459                 index = qh_offsets[period_slot] + i;
460                 load = 0;
461                 while( index >= 0 && index < 127 )
462                 {
463                         qh = Cont->TDQHPage->InterruptQHs + index;
464                         load += Cont->InterruptLoad[index];
465                         index = ((qh->Next & ~3) - Cont->PhysTDQHPage)/sizeof(tUHCI_QH);
466                 }
467
468                 LOG("Slot %i (and below) load %i", qh_offsets[period_slot] + i, load);
469
470                 // i = 0 will initialise the values, otherwise update if lower
471                 if( i == 0 || load < min_load )
472                 {
473                         min_load = load;
474                         min_load_slot = i;
475                 }
476                 // - Fast return if no load
477                 if( load == 0 ) break;
478         }
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 )
482         {
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
485                         );
486         }
487         Cont->InterruptLoad[min_load_slot] += (TD->Control & 0x7FF);
488 #endif
489         qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
490
491         LOG("period_slot = %i, QueueIndex = %i",
492                 period_slot, TD->_info.QueueIndex);
493
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);
497
498         UHCI_int_AppendTD(Cont, qh, TD);
499 }
500
501 // --------------------------------------------------------------------
502 // API
503 // --------------------------------------------------------------------
504 void *UHCI_InitInterrupt(void *Ptr, int Endpt, int bOutbound,
505         int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len)
506 {
507         tUHCI_TD        *td;
508         if( Period <= 0 )       return NULL;
509         
510         ENTER("pPtr xEndpt bbOutbound iPeriod pCb pCbData pBuf iLen",
511                 Ptr, Endpt, bOutbound, Period, Cb, CbData, Buf, Len);
512
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;
516         
517         UHCI_int_SetInterruptPoll(Ptr, td, Period);
518
519         LEAVE('p', td);
520         return td;
521 }
522
523 void *UHCI_int_InitEndpt(tUHCI_Controller *Cont, int Type, int Endpt, size_t MaxPacketSize)
524 {
525         if( Endpt >= 256*16 )
526                 return NULL;    
527
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);
531                 return NULL;
532         }
533
534         if( Cont->DevInfo[Endpt / 16] == NULL ) {
535                 Cont->DevInfo[Endpt / 16] = calloc( 1, sizeof(*Cont->DevInfo[0]) );
536         }
537         tUHCI_EndpointInfo *epi = &Cont->DevInfo[Endpt/16]->EndpointInfo[Endpt%16];
538         if( epi->Type ) {
539                 // oops, in use
540                 Log_Warning("UHCI", "Endpoint %x reused?", Endpt);
541                 return NULL;
542         }
543
544         epi->MaxPacketSize = MaxPacketSize;
545         epi->Type = Type;
546         epi->Tgl = 0;
547
548         return (void*)(tVAddr)(Endpt+1);
549
550 }
551
552 void *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize)
553 {
554         return UHCI_int_InitEndpt(Ptr, 1, Endpt, MaxPacketSize);
555 }
556
557 void *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize)
558 {
559         return UHCI_int_InitEndpt(Ptr, 2, Endpt, MaxPacketSize);
560 }
561
562 void UHCI_RemoveEndpoint(void *Ptr, void *Handle)
563 {
564         tUHCI_Controller *Cont = Ptr;
565         if( Handle == NULL )
566                 return ;
567         
568         if( (tVAddr)Handle <= 256*16 ) {
569                  int    addr = (tVAddr)Handle;
570                 Cont->DevInfo[addr/16]->EndpointInfo[addr%16].Type = 0;
571         }
572         else {
573                 // TODO: Stop interrupt transaction
574                 Log_Error("UHCI", "TODO: Implement stopping interrupt polling");
575         }
576 }
577
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
583         )
584 {
585         ENTER("pPtr pEndpt bOutbound", Ptr, Endpt, bOutbound);
586         
587         tUHCI_Controller        *Cont = Ptr;
588         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
589         tUHCI_TD        *td;
590         tUHCI_EndpointInfo *epi;
591          int    dest, tgl;
592         size_t  mps;
593
594         if( Endpt == NULL ) {
595                 Log_Error("UHCI", "Passed a NULL Endpoint handle");
596                 LEAVE('n');
597                 return NULL;
598         }
599
600         // Sanity check Endpt
601         if( (tVAddr)Endpt > 0x800 ) {
602                 LEAVE('n');
603                 return NULL;
604         }
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;
610         tgl = epi->Tgl;
611
612         // TODO: Build up list and then append to QH in one operation
613
614         char    *data_ptr, *status_ptr;
615         size_t  data_len,   status_len;
616         Uint8   data_pid,   status_pid;
617         
618         if( bOutbound ) {
619                 data_pid   = PID_OUT; data_ptr   = (void*)OutData; data_len   = OutLength;
620                 status_pid = PID_IN;  status_ptr = InData;  status_len = InLength;
621         }
622         else {
623                 data_pid   = PID_IN;  data_ptr   = InData;  data_len   = InLength;
624                 status_pid = PID_OUT; status_ptr = (void*)OutData; status_len = OutLength;
625         }
626
627         // Sanity check data lengths
628         if( SetupLength > mps ) LEAVE_RET('n', NULL);
629         if( status_len > mps )  LEAVE_RET('n', NULL);
630
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);
634         tgl = !tgl;
635
636         // Send data packets
637         while( data_len > 0 )
638         {
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);
642                 tgl = !tgl;
643                 
644                 data_ptr += len;
645                 data_len -= len;
646         }
647         
648         // Send status
649         td = UHCI_int_CreateTD(Cont, dest, status_pid, tgl, Cb, CbData, status_ptr, status_len);
650         UHCI_int_AppendTD(Cont, qh, td);
651         tgl = !tgl;
652         
653         // Update toggle value
654         epi->Tgl = tgl;
655
656         // --- HACK!!!
657 //      for( int i = 0; i < 1024; i ++ )
658 //      {
659 //              LOG("- FrameList[%i] = %x", i, Cont->FrameList[i]);
660 //      }
661         // --- /HACK    
662
663         LEAVE('p', td); 
664         return td;
665 }
666
667 void *UHCI_SendBulk(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData, int bOutbound, void *Data, size_t Length)
668 {
669         tUHCI_Controller        *Cont = Ptr;
670         tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
671         tUHCI_TD        *td = NULL;
672         tUHCI_EndpointInfo *epi;
673          int    dest, tgl;
674         size_t  mps;
675
676         ENTER("pPtr pEndpt pCb pCbData bOutbound pData iLength", Ptr, Endpt, Cb, CbData, bOutbound, Data, Length);
677
678         if( Endpt == NULL ) {
679                 Log_Error("UHCI", "_SendBulk passed a NULL endpoint handle");
680                 LEAVE('n');
681                 return NULL;
682         }
683
684         // Sanity check Endpt
685         if( (tVAddr)Endpt > 256*16 ) {
686                 Log_Error("UHCI", "_SendBulk passed an interrupt endpoint handle");
687                 LEAVE('n');
688                 return NULL;
689         }
690         dest = (tVAddr)Endpt - 1;
691         if( Cont->DevInfo[dest/16] == NULL ) {
692                 Log_Error("UHCI", "_SendBulk passed an uninitialised handle");
693                 LEAVE('n');
694                 return NULL;
695         }
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);
699                 LEAVE('n');
700                 return NULL;
701         }
702         tgl = epi->Tgl;
703         mps = epi->MaxPacketSize;
704
705         Uint8   pid = (bOutbound ? PID_OUT : PID_IN);
706
707         char *pos = Data;
708         while( Length > 0 )
709         {
710                 size_t len = MIN(mps, Length);
711
712                 td = UHCI_int_CreateTD(Cont, dest, pid, tgl, Cb, (len == Length ? CbData : NULL), pos, len);
713                 UHCI_int_AppendTD(Cont, qh, td);
714                 
715                 pos += len;
716                 Length -= len;
717                 tgl = !tgl;
718         }
719         
720         epi->Tgl = tgl;
721
722         LEAVE('p', td);
723         return td;
724 }
725
726 // ==========================
727 // === INTERNAL FUNCTIONS ===
728 // ==========================
729 void UHCI_CheckPortUpdate(void *Ptr)
730 {
731         tUHCI_Controller        *Host = Ptr;
732         // Enable ports
733         for( int i = 0; i < 2; i ++ )
734         {
735                  int    port = PORTSC1 + i*2;
736                 Uint16  status;
737         
738                 status = _InWord(Host, port);
739                 // Check for port change
740                 if( !(status & 0x0002) )        continue;
741                 _OutWord(Host, port, 0x0002);
742                 
743                 // Check if the port is connected
744                 if( !(status & 1) )
745                 {
746                         // Tell the USB code it's gone.
747                         USB_DeviceDisconnected(Host->RootHub, i);
748                         continue;
749                 }
750                 else
751                 {
752                         LOG("Port %i has something", i);
753                         // Reset port (set bit 9)
754                         LOG("Reset");
755                         _OutWord(Host, port, 0x0200);
756                         Time_Delay(50); // 50ms delay
757                         _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
758                         // Enable port
759                         LOG("Enable");
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);
764                 }
765         }
766 }
767
768 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
769 {
770         if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
771         {
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];
777         }
778
779         
780         tPAddr  global_pool = MM_GetPhysAddr( gaUHCI_TDPool );
781         
782         if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE )   return NULL;
783         
784         PAddr -= global_pool;
785         PAddr /= sizeof(tUHCI_TD);
786         return &gaUHCI_TDPool[PAddr];
787 }
788
789 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
790 {
791         tUHCI_TD        *td, *prev = NULL;
792         Uint32  cur_td;
793          int    nCleaned = 0;
794
795         // Disable controller
796         _OutWord( Cont, USBCMD, 0x0000 );
797         
798         // Scan QH list
799         cur_td = QH->Child;
800         LOG("cur_td = 0x%08x", cur_td);
801         while( !(cur_td & 1) )
802         {
803                 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
804                 if(!td) {
805                         Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
806                                 QH, cur_td);
807                         break ;
808                 }
809                 
810                 // Active? Ok.
811                 if( td->Control & TD_CTL_ACTIVE ) {
812                         LOG("%p still active", td);
813                         prev = td;
814                         cur_td = td->Link;
815                         continue ;
816                 }
817
818                 LOG("Removed %p from QH %p", td, QH);           
819
820                 if( !prev )
821                         QH->Child = td->Link;
822                 else
823                         prev->Link = td->Link;
824                 cur_td = td->Link;
825                 nCleaned ++;
826         }
827
828         if( nCleaned == 0 ) {
829                 LOG("Nothing cleaned... what the?");
830         }
831
832         // re-enable controller
833         _OutWord( Cont, USBCMD, 0x0001 );       
834 }
835
836 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
837 {
838          int    byte_count = (TD->Control & 0x7FF)+1;
839         tUHCI_ExtraTDInfo       *info = TD->_info.ExtraInfo;
840
841         // Handle non page-aligned destination (or with a > 32-bit paddr)
842         // TODO: Needs fixing for alignment issues
843         if(info->FirstPage)
844         {
845                 char    *src, *dest;
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 )
851                 {
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);
855                 }
856                 else
857                 {
858                         // Multi-page
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);
863                         MM_FreeTemp( dest );
864                         dest = MM_MapTemp(info->SecondPage);
865                         memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
866                 }
867                 MM_FreeTemp( src );
868                 MM_FreeTemp( dest );
869         }
870
871         // Callback
872         if( info->Callback != NULL )
873         {
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 );
877                 MM_FreeTemp( ptr );
878         }
879         
880         // Clean up info
881         if( TD->_info.QueueIndex > 127 )
882         {
883                 free( info );
884                 TD->_info.ExtraInfo = NULL;
885         }
886 }
887
888 void UHCI_int_InterruptThread(void *Pointer)
889 {
890         tUHCI_Controller        *Cont = Pointer;
891         Threads_SetName("UHCI Interrupt Handler");
892         for( ;; )
893         {
894                  int    nSeen = 0;
895                 
896                 LOG("zzzzz....");
897                 // 0 = Take all
898                 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
899                 LOG("Huh?");
900         
901                 for( int i = 0; i < NUM_TDs; i ++ )
902                 {
903                         tUHCI_TD        *td;
904                         
905                         td = &gaUHCI_TDPool[i];
906
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 ;
911
912                         nSeen ++;
913
914                         // If no callback/alt buffer, mark as free and move on
915                         if( td->_info.ExtraInfo )
916                         {
917                                 UHCI_int_HandleTDComplete(Cont, td);
918                         }
919
920                         // Error check
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 " : ""
931                                         );
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);
935                                 td->Control = 0;
936                         }
937         
938                         // Handle rescheduling of interrupt TDs
939                         if( td->_info.QueueIndex <= 127 )
940                         {
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
945                                 UHCI_int_AppendTD(
946                                         Cont,
947                                         Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
948                                         td
949                                         );
950                                 continue ;
951                         }
952
953                         // Clean up
954                         if( td->_info.bFreePointer )
955                                 MM_DerefPhys( td->BufferPointer );
956
957                         // Clean up
958                         LOG("Cleaned %p (->Control = %x)", td, td->Control);
959                         td->_info.bActive = 0;
960                 }
961
962                 if( nSeen == 0 ) {
963                         LOG("Why did you wake me?");
964                 }
965         }
966 }
967
968 void UHCI_InterruptHandler(int IRQ, void *Ptr)
969 {
970         tUHCI_Controller *Host = Ptr;
971 //       int    frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
972         Uint16  status = _InWord(Host, USBSTS);
973         
974         LOG("%p: status = 0x%04x", Ptr, status);
975         
976         // Interrupt-on-completion
977         if( status & 1 )
978         {
979                 // TODO: Support isochronous transfers (will need updating the frame pointer)
980                 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
981         }
982
983         // USB Error Interrupt
984         if( status & 2 )
985         {
986                 
987         }
988
989         // Resume Detect
990         // - Fired if in suspend state and a USB device sends the RESUME signal
991         if( status & 4 )
992         {
993                 
994         }
995
996         // Host System Error
997         if( status & 8 )
998         {
999                 
1000         }
1001
1002         // Host Controller Process Error
1003         if( status & 0x10 )
1004         {
1005                 Log_Error("UHCI", "Host controller process error on controller %p", Ptr);
1006         }
1007
1008         _OutWord(Host, USBSTS, status);
1009 }
1010
1011 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
1012 {
1013         if( Host->MemIOMap )
1014                 ((Uint8*)Host->MemIOMap)[Reg] = Value;
1015         else
1016                 outb(Host->IOBase + Reg, Value);
1017 }
1018
1019 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
1020 {
1021         if( Host->MemIOMap )
1022                 Host->MemIOMap[Reg/2] = Value;
1023         else
1024                 outw(Host->IOBase + Reg, Value);
1025 }
1026
1027 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
1028 {
1029         if( Host->MemIOMap )
1030                 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
1031         else
1032                 outd(Host->IOBase + Reg, Value);
1033 }
1034
1035 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
1036 {
1037         if( Host->MemIOMap )
1038                 return Host->MemIOMap[Reg/2];
1039         else
1040                 return inw(Host->IOBase + Reg);
1041 }
1042

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