Usermode/libc - Fix strchr and strrchr behavior
[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 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                 LOG("gaUHCI_TDPool = %p (%P)", gaUHCI_TDPool, tmp);
118         }
119
120         // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
121         // Class:SubClass:Protocol = 0xC (Serial) : 0x3 (USB) : 0x00 (UHCI)
122         while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
123         {
124                 tUHCI_Controller        *cinfo = &gUHCI_Controllers[i];
125                 Uint32  base_addr;
126                 
127                 cinfo->PciId = id;
128                 base_addr = PCI_GetBAR(id, 4);
129                 
130                 if( base_addr & 1 )
131                 {
132                         cinfo->IOBase = base_addr & ~1;
133                         cinfo->MemIOMap = NULL;
134                 }
135                 else
136                 {
137                         cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
138                 }
139                 cinfo->IRQNum = PCI_GetIRQ(id);
140                 
141                 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
142                         id, base_addr, cinfo->IRQNum);
143                 
144                 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
145         
146                 // Initialise Host
147                 ret = UHCI_int_InitHost(cinfo);
148                 // Detect an error
149                 if(ret != 0) {
150                         LEAVE('i', ret);
151                         return ret;
152                 }
153
154                 // Spin off interrupt handling thread
155                 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
156
157                 
158                 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
159                 LOG("cinfo->RootHub = %p", cinfo->RootHub);
160
161                 i ++;
162         }
163
164         if(i == MAX_CONTROLLERS) {
165                 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
166         }
167         LEAVE('i', MODULE_ERR_OK);
168         return MODULE_ERR_OK;
169 }
170
171 /**
172  * \fn void UHCI_Cleanup()
173  * \brief Called just before module is unloaded
174  */
175 void UHCI_Cleanup()
176 {
177 }
178
179 /**
180  * \brief Initialises a UHCI host controller
181  * \param Host  Pointer - Host to initialise
182  */
183 int UHCI_int_InitHost(tUHCI_Controller *Host)
184 {
185         ENTER("pHost", Host);
186
187         // - 1 Page, 32-bit address
188         // - 1 page = 1024  4 byte entries
189         Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
190         if( !Host->FrameList ) {
191                 Log_Warning("UHCI", "Unable to allocate frame list, aborting");
192                 LEAVE('i', -1);
193                 return -1;
194         }
195         LOG("->FrameList = %p (%P)", Host->FrameList, Host->PhysFrameList);
196
197         Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
198         if( !Host->TDQHPage ) {
199                 // TODO: Clean up
200                 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
201                 LEAVE('i', -1);
202                 return -1;
203         }
204         LOG("->TDQHPage = %p (%P)", Host->TDQHPage, Host->PhysTDQHPage);
205
206         // Fill frame list
207         // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
208         const int       dest_offsets[] = {
209                 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,
210                 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31
211                 };
212         // Fill all slots (but every 4th will be changed below
213         for( int i = 0; i < 1024; i ++ ) {
214                 Uint32  addr = MM_GetPhysAddr( &Host->TDQHPage->ControlQH );
215                 Host->FrameList[i] = addr | 2;
216         }
217         for( int i = 0; i < 64; i ++ ) {
218                  int    ofs = dest_offsets[ i & (32-1) ] * 2 + (i >= 32);
219                 Uint32  addr = Host->PhysTDQHPage + ofs * sizeof(tUHCI_QH);
220                 LOG("Slot %i to (%i,%i,%i,%i) ms slots",
221                         ofs, 0 + i*4, 256 + i*4, 512 + i*4, 768 + i*4);
222                 Host->FrameList[  0 + i*4] = addr | 2;
223                 Host->FrameList[256 + i*4] = addr | 2;
224                 Host->FrameList[512 + i*4] = addr | 2;
225                 Host->FrameList[768 + i*4] = addr | 2;
226         }
227
228         // Build up interrupt binary tree
229         {
230                 tUHCI_QH        *dest = Host->TDQHPage->InterruptQHs;
231                 Uint32  destphys = Host->PhysTDQHPage;
232                 
233                 // Set up next pointer to index to i/2 in the next step
234                 for( int _count = 64; _count > 1; _count /= 2 )
235                 {
236                         LOG("count=%i, dest=%p, destphys=%P", _count, dest, destphys);
237                         for( int i = 0; i < _count; i ++ ) {
238                                 LOG(" %i-%i: %P==%P", _count, i, MM_GetPhysAddr(dest+i), destphys+i*sizeof(tUHCI_QH));
239                                 dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
240                                 dest[i].Child = 1;
241                         }
242                         dest += _count; destphys += _count * sizeof(tUHCI_QH);
243                 }
244                 // Skip padding, and move to control QH
245                 dest->Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
246                 dest->Child = 1;
247         }
248
249         // Set up control and bulk queues
250         Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
251         Host->TDQHPage->ControlQH.Child = 1;
252         Host->TDQHPage->BulkQH.Next = 1;
253         Host->TDQHPage->BulkQH.Child = 1;
254
255         // Global reset 
256         _OutWord( Host, USBCMD, 4 );
257         Time_Delay(10);
258         _OutWord( Host, USBCMD, 0 );
259         
260         // Allocate Frame List
261         // Set frame length to 1 ms
262         _OutByte( Host, SOFMOD, 64 );
263         
264         // Set Frame List
265         _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
266         _OutWord( Host, FRNUM, 0 );
267         
268         // Enable Interrupts
269         _OutWord( Host, USBINTR, 0x000F );
270         PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
271
272         // Enable processing
273         LOG("Processing enabling");
274         _OutWord( Host, USBCMD, 0x0001 );
275
276         LEAVE('i', 0);
277         return 0;
278 }
279
280 // --------------------------------------------------------------------
281 // TDs and QH Allocation/Appending
282 // --------------------------------------------------------------------
283 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
284 {
285         static tMutex   lock;
286         Mutex_Acquire( &lock );
287         for( int i = 0; i < NUM_TDs; i ++ )
288         {
289                 if(gaUHCI_TDPool[i]._info.bActive == 0)
290                 {
291                         gaUHCI_TDPool[i].Link = 1;
292                         gaUHCI_TDPool[i].Control = TD_CTL_ACTIVE;
293                         gaUHCI_TDPool[i]._info.bActive = 1;
294                         gaUHCI_TDPool[i]._info.QueueIndex = 128;
295                         Mutex_Release( &lock );
296                         return &gaUHCI_TDPool[i];
297                 }
298         }
299         Mutex_Release( &lock );
300         return NULL;
301 }
302
303 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
304 {
305         static tMutex   lock;   // TODO: Should I use a shortlock (avoid being preempted)
306
307         Mutex_Acquire(&lock);
308         
309         // Ensure that there is an interrupt for each used frame
310         TD->Control |= TD_CTL_IOC;
311         TD->_info.QueueIndex = ((tVAddr)QH - (tVAddr)Cont->TDQHPage->InterruptQHs) / sizeof(tUHCI_QH);
312         LOG("TD(%p)->QueueIndex = %i", TD, TD->_info.QueueIndex);
313         // Update length
314         TD->Control &= ~0x7FF;
315         TD->Control |= (TD->Token >> 21) & 0x7FF;
316
317         // Stop controller
318         tPAddr  tdaddr = MM_GetPhysAddr( TD );
319         ASSERT(tdaddr);
320         _OutWord( Cont, USBCMD, 0x0000 );
321         
322         // Add
323         TD->Link = 1;
324         if( QH->Child & 1 ) {
325                 QH->Child = tdaddr;
326         }
327         else {
328                 // Depth first
329                 QH->_LastItem->Link = tdaddr | 4;
330         }
331         QH->_LastItem = TD;
332
333         // Reenable controller
334         _OutWord( Cont, USBCMD, 0x0001 );
335         
336         // DEBUG!
337         LOG("QH(%p)->Child = %x", QH, QH->Child);
338         LOG("TD(%p)->Control = %x, ->Link = %x", TD, TD->Control, TD->Link);
339
340         Mutex_Release(&lock);
341 }
342
343 /**
344  * \brief Send a transaction to the USB bus
345  * \param Cont  Controller pointer
346  * \param Addr  Function Address * 16 + Endpoint
347  * \param bTgl  Data toggle value
348  */
349 tUHCI_TD *UHCI_int_CreateTD(
350         tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
351         tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
352 {
353         tUHCI_TD        *td;
354         tUHCI_ExtraTDInfo       *info = NULL;
355
356         if( Length > 0x400 ) {
357                 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
358                 return NULL;    // Controller allows up to 0x500, but USB doesn't
359         }
360
361         td = UHCI_int_AllocateTD(Cont);
362         if( !td ) {
363                 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
364                 return NULL;
365         }
366
367         LOG("TD %p %i bytes, Type %x to 0x%x",
368                 td, Length, Type, Addr);
369
370         td->Control = (Length - 1) & 0x7FF;
371         td->Control |= TD_CTL_ACTIVE;   // Active set
372         td->Control |= (3 << 27);       // 3 retries
373         // High speed device (must be explicitly enabled
374         if( Addr & 0x8000 )
375                 ;
376         else
377                 td->Control |= 1 << 26;
378                 
379         td->Token  = ((Length - 1) & 0x7FF) << 21;
380         td->Token |= (bTgl & 1) << 19;
381         td->Token |= (Addr & 0xF) << 15;
382         td->Token |= ((Addr/16) & 0xFF) << 8;
383         td->Token |= Type;
384
385         if(
386                 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
387         #if PHYS_BITS > 32
388                 || MM_GetPhysAddr( Data ) >> 32
389         #endif
390                 )
391         {
392                 td->BufferPointer = MM_AllocPhysRange(1, 32);
393
394                 LOG("Allocated page %x", td->BufferPointer);            
395
396                 if( Type == 0x69 )      // IN token
397                 {
398                         LOG("Relocated IN");
399                         info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
400                         info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
401                         info->FirstPage = MM_GetPhysAddr( Data );
402                         info->SecondPage = MM_GetPhysAddr( (const char *)Data + Length - 1 );
403                 }
404                 else
405                 {
406                         LOG("Relocated OUT/SETUP");
407                         void *ptr = MM_MapTemp(td->BufferPointer);
408                         memcpy( ptr, Data, Length );
409                         MM_FreeTemp(ptr);
410                         td->Control |= TD_CTL_IOC;
411                 }
412                 td->_info.bFreePointer = 1;
413         }
414         else
415         {
416                 td->BufferPointer = MM_GetPhysAddr( Data );
417                 td->_info.bFreePointer = 0;
418         }
419
420         // Interrupt on completion
421         if( Cb )
422         {
423                 if( !info )
424                         info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
425                 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
426                 info->Callback = Cb;
427                 info->CallbackPtr = CbData;
428         }
429         
430         if( info ) {
431                 LOG("info = %p", info);
432                 td->Control |= TD_CTL_IOC;
433                 td->_info.ExtraInfo = info;
434         }
435
436         return td;
437 }
438
439 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
440 {
441         tUHCI_QH        *qh;
442         const int       qh_offsets[] = {126, 124, 120, 112, 96, 64,  0};
443         const int       qh_sizes[]   = {  1,   2,   4,   8, 16, 32, 64};
444         
445         // Bounds limit
446         if( Period < 0 )        return ;
447         if( Period > 256 )      Period = 256;
448         if( Period == 255 )     Period = 256;
449
450         // Get the log base2 of the period
451          int    period_slot = 0;
452         while( Period >>= 1 )   period_slot ++;
453
454         // Adjust for the 4ms minimum period
455         if( period_slot < 2 )   period_slot = 0;
456         else    period_slot -= 2;
457         
458         // _AppendTD calculates this from qh, but we use it to determine qh
459         TD->_info.QueueIndex = qh_offsets[period_slot];
460         // TODO: Find queue with lowest load
461 #if 1
462          int    min_load = 0;
463          int    min_load_slot = 0;
464         for( int i = 0; i < qh_sizes[period_slot]; i ++ )
465         {
466                 int load, index;
467                 index = qh_offsets[period_slot] + i;
468                 load = 0;
469                 while( index >= 0 && index < 127 )
470                 {
471                         qh = Cont->TDQHPage->InterruptQHs + index;
472                         load += Cont->InterruptLoad[index];
473                         index = ((qh->Next & ~3) - Cont->PhysTDQHPage)/sizeof(tUHCI_QH);
474                 }
475
476                 LOG("Slot %i (and below) load %i", qh_offsets[period_slot] + i, load);
477
478                 // i = 0 will initialise the values, otherwise update if lower
479                 if( i == 0 || load < min_load )
480                 {
481                         min_load = load;
482                         min_load_slot = i;
483                 }
484                 // - Fast return if no load
485                 if( load == 0 ) break;
486         }
487         min_load_slot += qh_offsets[period_slot];
488         TD->_info.QueueIndex = min_load_slot;
489         if( min_load + (TD->Control & 0x7FF) > MAX_INTERRUPT_LOAD )
490         {
491                 Log_Warning("UHCI", "Interrupt load on %i ms is too high (slot %i load %i bytes)",
492                         1 << (period_slot+2), min_load_slot, min_load
493                         );
494         }
495         Cont->InterruptLoad[min_load_slot] += (TD->Control & 0x7FF);
496 #endif
497         qh = Cont->TDQHPage->InterruptQHs + TD->_info.QueueIndex;
498
499         LOG("period_slot = %i, QueueIndex = %i",
500                 period_slot, TD->_info.QueueIndex);
501
502         // Stop any errors causing the TD to stop (NAK will error)
503         // - If the device is unplugged, the removal code should remove the interrupt
504         TD->Control &= ~(3 << 27);
505
506         UHCI_int_AppendTD(Cont, qh, TD);
507 }
508
509 // --------------------------------------------------------------------
510 // API
511 // --------------------------------------------------------------------
512 void *UHCI_InitInterrupt(void *Ptr, int Endpt, int bOutbound,
513         int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len)
514 {
515         tUHCI_TD        *td;
516         if( Period <= 0 )       return NULL;
517         
518         ENTER("pPtr xEndpt bbOutbound iPeriod pCb pCbData pBuf iLen",
519                 Ptr, Endpt, bOutbound, Period, Cb, CbData, Buf, Len);
520
521         // TODO: Data toggle?
522         td = UHCI_int_CreateTD(Ptr, Endpt, (bOutbound ? PID_OUT : PID_IN), 0, Cb, CbData, Buf, Len);
523         if( !td )       return NULL;
524         
525         UHCI_int_SetInterruptPoll(Ptr, td, Period);
526
527         LEAVE('p', td);
528         return td;
529 }
530
531 void *UHCI_int_InitEndpt(tUHCI_Controller *Cont, int Type, int Endpt, size_t MaxPacketSize)
532 {
533         if( Endpt >= 256*16 )
534                 return NULL;    
535
536         if( MaxPacketSize > MAX_PACKET_SIZE) {
537                 Log_Warning("UHCI", "MaxPacketSize for %x greater than controller max (%i > %i)",
538                         Endpt, MaxPacketSize, MAX_PACKET_SIZE);
539                 return NULL;
540         }
541
542         if( Cont->DevInfo[Endpt / 16] == NULL ) {
543                 Cont->DevInfo[Endpt / 16] = calloc( 1, sizeof(*Cont->DevInfo[0]) );
544         }
545         tUHCI_EndpointInfo *epi = &Cont->DevInfo[Endpt/16]->EndpointInfo[Endpt%16];
546         if( epi->Type ) {
547                 // oops, in use
548                 Log_Warning("UHCI", "Endpoint %x reused?", Endpt);
549                 return NULL;
550         }
551
552         epi->MaxPacketSize = MaxPacketSize;
553         epi->Type = Type;
554         epi->Tgl = 0;
555
556         return (void*)(tVAddr)(Endpt+1);
557
558 }
559
560 void *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize)
561 {
562         return UHCI_int_InitEndpt(Ptr, 1, Endpt, MaxPacketSize);
563 }
564
565 void *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize)
566 {
567         return UHCI_int_InitEndpt(Ptr, 2, Endpt, MaxPacketSize);
568 }
569
570 void UHCI_RemoveEndpoint(void *Ptr, void *Handle)
571 {
572         tUHCI_Controller *Cont = Ptr;
573         if( Handle == NULL )
574                 return ;
575         
576         if( (tVAddr)Handle <= 256*16 ) {
577                  int    addr = (tVAddr)Handle;
578                 Cont->DevInfo[addr/16]->EndpointInfo[addr%16].Type = 0;
579         }
580         else {
581                 // TODO: Stop interrupt transaction
582                 Log_Error("UHCI", "TODO: Implement stopping interrupt polling");
583         }
584 }
585
586 void *UHCI_SendControl(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData,
587         int bOutbound,  // (1) SETUP, OUT, IN vs (0) SETUP, IN, OUT
588         const void *SetupData, size_t SetupLength,
589         const void *OutData, size_t OutLength,
590         void *InData, size_t InLength
591         )
592 {
593         ENTER("pPtr pEndpt bOutbound", Ptr, Endpt, bOutbound);
594         
595         tUHCI_Controller        *Cont = Ptr;
596         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
597         tUHCI_TD        *td;
598         tUHCI_EndpointInfo *epi;
599          int    dest, tgl;
600         size_t  mps;
601
602         if( Endpt == NULL ) {
603                 Log_Error("UHCI", "Passed a NULL Endpoint handle");
604                 LEAVE('n');
605                 return NULL;
606         }
607
608         // Sanity check Endpt
609         if( (tVAddr)Endpt > 0x800 ) {
610                 LEAVE('n');
611                 return NULL;
612         }
613         dest = (tVAddr)Endpt - 1;
614         if( Cont->DevInfo[dest/16] == NULL )    LEAVE_RET('n', NULL);
615         epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
616         if( epi->Type != 1 )    LEAVE_RET('n', NULL);
617         mps = epi->MaxPacketSize;
618         tgl = epi->Tgl;
619
620         // TODO: Build up list and then append to QH in one operation
621
622         char    *data_ptr, *status_ptr;
623         size_t  data_len,   status_len;
624         Uint8   data_pid,   status_pid;
625         
626         if( bOutbound ) {
627                 data_pid   = PID_OUT; data_ptr   = (void*)OutData; data_len   = OutLength;
628                 status_pid = PID_IN;  status_ptr = InData;  status_len = InLength;
629         }
630         else {
631                 data_pid   = PID_IN;  data_ptr   = InData;  data_len   = InLength;
632                 status_pid = PID_OUT; status_ptr = (void*)OutData; status_len = OutLength;
633         }
634
635         // Sanity check data lengths
636         if( SetupLength > mps ) LEAVE_RET('n', NULL);
637         if( status_len > mps )  LEAVE_RET('n', NULL);
638
639         // Create and append SETUP packet
640         td = UHCI_int_CreateTD(Cont, dest, PID_SETUP, tgl, NULL, NULL, (void*)SetupData, SetupLength);
641         UHCI_int_AppendTD(Cont, qh, td);
642         tgl = !tgl;
643
644         // Send data packets
645         while( data_len > 0 )
646         {
647                 size_t len = MIN(data_len, mps);
648                 td = UHCI_int_CreateTD(Cont, dest, data_pid, tgl, NULL, NULL, data_ptr, len);
649                 UHCI_int_AppendTD(Cont, qh, td);
650                 tgl = !tgl;
651                 
652                 data_ptr += len;
653                 data_len -= len;
654         }
655         
656         // Send status
657         td = UHCI_int_CreateTD(Cont, dest, status_pid, tgl, Cb, CbData, status_ptr, status_len);
658         UHCI_int_AppendTD(Cont, qh, td);
659         tgl = !tgl;
660         
661         // Update toggle value
662         epi->Tgl = tgl;
663
664         // --- HACK!!!
665 //      for( int i = 0; i < 1024; i ++ )
666 //      {
667 //              LOG("- FrameList[%i] = %x", i, Cont->FrameList[i]);
668 //      }
669         // --- /HACK    
670
671         LEAVE('p', td); 
672         return td;
673 }
674
675 void *UHCI_SendBulk(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData, int bOutbound, void *Data, size_t Length)
676 {
677         tUHCI_Controller        *Cont = Ptr;
678         tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
679         tUHCI_TD        *td = NULL;
680         tUHCI_EndpointInfo *epi;
681          int    dest, tgl;
682         size_t  mps;
683
684         ENTER("pPtr pEndpt pCb pCbData bOutbound pData iLength", Ptr, Endpt, Cb, CbData, bOutbound, Data, Length);
685
686         if( Endpt == NULL ) {
687                 Log_Error("UHCI", "_SendBulk passed a NULL endpoint handle");
688                 LEAVE('n');
689                 return NULL;
690         }
691
692         // Sanity check Endpt
693         if( (tVAddr)Endpt > 256*16 ) {
694                 Log_Error("UHCI", "_SendBulk passed an interrupt endpoint handle");
695                 LEAVE('n');
696                 return NULL;
697         }
698         dest = (tVAddr)Endpt - 1;
699         if( Cont->DevInfo[dest/16] == NULL ) {
700                 Log_Error("UHCI", "_SendBulk passed an uninitialised handle");
701                 LEAVE('n');
702                 return NULL;
703         }
704         epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
705         if( epi->Type != 2 ) {
706                 Log_Error("UHCI", "_SendBulk passed an invalid endpoint type (%i!=2)", epi->Type);
707                 LEAVE('n');
708                 return NULL;
709         }
710         tgl = epi->Tgl;
711         mps = epi->MaxPacketSize;
712
713         Uint8   pid = (bOutbound ? PID_OUT : PID_IN);
714
715         char *pos = Data;
716         while( Length > 0 )
717         {
718                 size_t len = MIN(mps, Length);
719
720                 td = UHCI_int_CreateTD(Cont, dest, pid, tgl, Cb, (len == Length ? CbData : NULL), pos, len);
721                 UHCI_int_AppendTD(Cont, qh, td);
722                 
723                 pos += len;
724                 Length -= len;
725                 tgl = !tgl;
726         }
727         
728         epi->Tgl = tgl;
729
730         LEAVE('p', td);
731         return td;
732 }
733
734 // ==========================
735 // === INTERNAL FUNCTIONS ===
736 // ==========================
737 void UHCI_CheckPortUpdate(void *Ptr)
738 {
739         tUHCI_Controller        *Host = Ptr;
740         // Enable ports
741         for( int i = 0; i < 2; i ++ )
742         {
743                  int    port = PORTSC1 + i*2;
744                 Uint16  status;
745         
746                 status = _InWord(Host, port);
747                 // Check for port change
748                 if( !(status & 0x0002) )        continue;
749                 _OutWord(Host, port, 0x0002);
750                 
751                 // Check if the port is connected
752                 if( !(status & 1) )
753                 {
754                         // Tell the USB code it's gone.
755                         USB_DeviceDisconnected(Host->RootHub, i);
756                         continue;
757                 }
758                 else
759                 {
760                         LOG("Port %i has something", i);
761                         // Reset port (set bit 9)
762                         LOG("Reset");
763                         _OutWord(Host, port, 0x0200);
764                         Time_Delay(50); // 50ms delay
765                         _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
766                         // Enable port
767                         LOG("Enable");
768                         Time_Delay(50); // 50ms delay
769                         _OutWord(Host, port, _InWord(Host, port) | 0x0004);
770                         // Tell USB there's a new device
771                         USB_DeviceConnected(Host->RootHub, i);
772                 }
773         }
774 }
775
776 tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
777 {
778         if( PAddr >= Controller->PhysTDQHPage && PAddr < Controller->PhysTDQHPage + PAGE_SIZE )
779         {
780                 PAddr -= Controller->PhysTDQHPage;
781                 PAddr -= (128+2)*sizeof(tUHCI_QH);
782                 if( PAddr > PAGE_SIZE ) return NULL;    // Wrapping will bring above page size
783                 PAddr /= sizeof(tUHCI_TD);
784                 return &Controller->TDQHPage->LocalTDPool[PAddr];
785         }
786
787         
788         tPAddr  global_pool = MM_GetPhysAddr( gaUHCI_TDPool );
789         
790         if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE )   return NULL;
791         
792         PAddr -= global_pool;
793         PAddr /= sizeof(tUHCI_TD);
794         return &gaUHCI_TDPool[PAddr];
795 }
796
797 void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
798 {
799         tUHCI_TD        *td, *prev = NULL;
800         Uint32  cur_td;
801          int    nCleaned = 0;
802
803         // Disable controller
804         _OutWord( Cont, USBCMD, 0x0000 );
805         
806         // Scan QH list
807         cur_td = QH->Child;
808         LOG("cur_td = 0x%08x", cur_td);
809         while( !(cur_td & 1) )
810         {
811                 td = UHCI_int_GetTDFromPhys(Cont, cur_td);
812                 if(!td) {
813                         Log_Warning("UHCI", "_int_CleanQH: QH %p contains TD %x, which was not from a pool",
814                                 QH, cur_td);
815                         break ;
816                 }
817                 
818                 // Active? Ok.
819                 if( td->Control & TD_CTL_ACTIVE ) {
820                         LOG("%p still active", td);
821                         prev = td;
822                         cur_td = td->Link;
823                         continue ;
824                 }
825
826                 LOG("Removed %p from QH %p", td, QH);
827                 ASSERT(td->Link);
828
829                 if( !prev )
830                         QH->Child = td->Link;
831                 else
832                         prev->Link = td->Link;
833                 cur_td = td->Link;
834                 nCleaned ++;
835         }
836
837         if( nCleaned == 0 ) {
838                 LOG("Nothing cleaned... what the?");
839         }
840
841         // re-enable controller
842         _OutWord( Cont, USBCMD, 0x0001 );       
843 }
844
845 void UHCI_int_HandleTDComplete(tUHCI_Controller *Cont, tUHCI_TD *TD)
846 {
847          int    byte_count = (TD->Control & 0x7FF)+1;
848         tUHCI_ExtraTDInfo       *info = TD->_info.ExtraInfo;
849
850         // Handle non page-aligned destination (or with a > 32-bit paddr)
851         // TODO: Needs fixing for alignment issues
852         if(info->FirstPage)
853         {
854                 char    *src, *dest;
855                  int    src_ofs = TD->BufferPointer & (PAGE_SIZE-1);
856                 src = MM_MapTemp(TD->BufferPointer);
857                 dest = MM_MapTemp(info->FirstPage);
858                 // Check for a single page transfer
859                 if( byte_count + info->Offset <= PAGE_SIZE )
860                 {
861                         LOG("Single page copy %P to %P of %p",
862                                 TD->BufferPointer, info->FirstPage, TD);
863                         memcpy(dest + info->Offset, src + src_ofs, byte_count);
864                 }
865                 else
866                 {
867                         // Multi-page
868                         LOG("Multi page copy %P to (%P,%P) of %p",
869                                 TD->BufferPointer, info->FirstPage, info->SecondPage, TD);
870                          int    part_len = PAGE_SIZE - info->Offset;
871                         memcpy(dest + info->Offset, src + src_ofs, part_len);
872                         MM_FreeTemp( dest );
873                         dest = MM_MapTemp(info->SecondPage);
874                         memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
875                 }
876                 MM_FreeTemp( src );
877                 MM_FreeTemp( dest );
878         }
879
880         // Callback
881         if( info->Callback != NULL )
882         {
883                 LOG("Calling cb %p (%i bytes)", info->Callback, byte_count);
884                 void    *ptr = MM_MapTemp( TD->BufferPointer );
885                 info->Callback( info->CallbackPtr, ptr, byte_count );
886                 MM_FreeTemp( ptr );
887         }
888         
889         // Clean up info
890         if( TD->_info.QueueIndex > 127 )
891         {
892                 free( info );
893                 TD->_info.ExtraInfo = NULL;
894         }
895 }
896
897 void UHCI_int_InterruptThread(void *Pointer)
898 {
899         tUHCI_Controller        *Cont = Pointer;
900         Threads_SetName("UHCI Interrupt Handler");
901         for( ;; )
902         {
903                  int    nSeen = 0;
904                 
905                 LOG("zzzzz....");
906                 // 0 = Take all
907                 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
908                 LOG("Huh?");
909         
910                 for( int i = 0; i < NUM_TDs; i ++ )
911                 {
912                         tUHCI_TD        *td;
913                         
914                         td = &gaUHCI_TDPool[i];
915
916                         // Skip completely inactive TDs
917                         if( td->_info.bActive == 0 )    continue ;
918                         // Skip ones that are still in use
919                         if( td->Control & TD_CTL_ACTIVE )       continue ;
920
921                         nSeen ++;
922
923                         // If no callback/alt buffer, mark as free and move on
924                         if( td->_info.ExtraInfo )
925                         {
926                                 UHCI_int_HandleTDComplete(Cont, td);
927                         }
928
929                         // Error check
930                         if( td->Control & 0x00FF0000 ) {
931                                 LOG("td->control(Status) = %s%s%s%s%s%s%s%s",
932                                         td->Control & TD_CTL_ACTIVE     ? "Active, " : "",
933                                         td->Control & TD_CTL_STALLED    ? "Stalled, " : "",
934                                         td->Control & TD_CTL_DATABUFERR ? "Data Buffer Error, " : "",
935                                         td->Control & TD_CTL_BABBLE     ? "Babble, " : "",
936                                         td->Control & TD_CTL_NAK        ? "NAK, " : "",
937                                         td->Control & TD_CTL_CRCERR     ? "CRC Error, " : "",
938                                         td->Control & TD_CTL_BITSTUFF   ? "Bitstuff Error, " : "",
939                                         td->Control & TD_CTL_RESERVED   ? "Reserved " : ""
940                                         );
941                                 LOG("From queue %i", td->_info.QueueIndex);
942                                 // Clean up QH (removing all inactive entries)
943                                 UHCI_int_CleanQH(Cont, Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex);
944                                 td->Control = 0;
945                         }
946         
947                         // Handle rescheduling of interrupt TDs
948                         if( td->_info.QueueIndex <= 127 )
949                         {
950                                 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.QueueIndex);
951                                 // TODO: Flip toggle?
952                                 td->Control |= TD_CTL_ACTIVE;
953                                 // Add back into controller's interrupt list
954                                 UHCI_int_AppendTD(
955                                         Cont,
956                                         Cont->TDQHPage->InterruptQHs + td->_info.QueueIndex,
957                                         td
958                                         );
959                                 continue ;
960                         }
961
962                         // Clean up
963                         if( td->_info.bFreePointer )
964                                 MM_DerefPhys( td->BufferPointer );
965
966                         // Clean up
967                         LOG("Cleaned %p (->Control = %x)", td, td->Control);
968                         td->_info.bActive = 0;
969                 }
970
971                 if( nSeen == 0 ) {
972                         LOG("Why did you wake me?");
973                 }
974         }
975 }
976
977 void UHCI_InterruptHandler(int IRQ, void *Ptr)
978 {
979         tUHCI_Controller *Host = Ptr;
980 //       int    frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
981         Uint16  status = _InWord(Host, USBSTS);
982         
983         LOG("%p: status = 0x%04x", Ptr, status);
984         
985         // Interrupt-on-completion
986         if( status & 1 )
987         {
988                 // TODO: Support isochronous transfers (will need updating the frame pointer)
989                 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
990         }
991
992         // USB Error Interrupt
993         if( status & 2 )
994         {
995                 Log_Notice("UHCI", "USB Error");
996         }
997
998         // Resume Detect
999         // - Fired if in suspend state and a USB device sends the RESUME signal
1000         if( status & 4 )
1001         {
1002                 Log_Notice("UHCI", "Resume Detect");
1003         }
1004
1005         // Host System Error
1006         if( status & 8 )
1007         {
1008                 Log_Notice("UHCI", "Host System Error");
1009         }
1010
1011         // Host Controller Process Error
1012         if( status & 0x10 )
1013         {
1014                 Log_Error("UHCI", "Host controller process error on controller %p", Ptr);
1015                 // Spam Tree
1016                 //for( int i = 0; i < 1024; i += 4 ) {
1017                 //      LOG("%4i: %x", i, Host->FrameList[i]);
1018                 //}
1019                 
1020                 tPAddr  phys = Host->TDQHPage->ControlQH.Child;
1021                 while( !(phys & 1) && MM_GetRefCount(phys & ~15))
1022                 {
1023                         tUHCI_TD *td = UHCI_int_GetTDFromPhys(Host, phys);
1024                         LOG("%08P: %08x %08x %08x", phys, td->Control, td->Token, td->BufferPointer);
1025                         phys = td->Link;
1026                 }
1027         }
1028
1029         _OutWord(Host, USBSTS, status);
1030 }
1031
1032 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
1033 {
1034         if( Host->MemIOMap )
1035                 ((Uint8*)Host->MemIOMap)[Reg] = Value;
1036         else
1037                 outb(Host->IOBase + Reg, Value);
1038 }
1039
1040 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
1041 {
1042         if( Host->MemIOMap )
1043                 Host->MemIOMap[Reg/2] = Value;
1044         else
1045                 outw(Host->IOBase + Reg, Value);
1046 }
1047
1048 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
1049 {
1050         if( Host->MemIOMap )
1051                 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
1052         else
1053                 outd(Host->IOBase + Reg, Value);
1054 }
1055
1056 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
1057 {
1058         if( Host->MemIOMap )
1059                 return Host->MemIOMap[Reg/2];
1060         else
1061                 return inw(Host->IOBase + Reg);
1062 }
1063

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