Kernel/USB - Still broken, reworking host controller API to give driver more information
[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 4
20 //#define NUM_TDs       1024
21 #define NUM_TDs 64
22 #define MAX_PACKET_SIZE 0x400
23 #define PID_IN  0x69
24 #define PID_OUT 0xE1
25 #define PID_SETUP       0x2D
26
27 // === PROTOTYPES ===
28  int    UHCI_Initialise(char **Arguments);
29 void    UHCI_Cleanup();
30  int    UHCI_int_InitHost(tUHCI_Controller *Host);
31 // -- List internals
32 tUHCI_TD        *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
33 void    UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD);
34 tUHCI_TD        *UHCI_int_CreateTD(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
35 // --- API
36 void    *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
37 void    *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
38 void    UHCI_StopInterrupt(void *Ptr, void *Handle);
39 void    *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length);
40 void    *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
41 void    *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
42 void    *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
43 void    *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
44
45 void    UHCI_CheckPortUpdate(void *Ptr);
46 void    UHCI_int_InterruptThread(void *Unused);
47 void    UHCI_InterruptHandler(int IRQ, void *Ptr);
48 // 
49 static void     _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value);
50 static void     _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value);
51 static void     _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value);
52 static Uint16   _InWord(tUHCI_Controller *Host, int Reg);
53
54 // === GLOBALS ===
55 MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
56 tUHCI_TD        gaUHCI_TDPool[NUM_TDs];
57 tUHCI_Controller        gUHCI_Controllers[MAX_CONTROLLERS];
58 tUSBHostDef     gUHCI_HostDef = {
59         .InterruptIN   = UHCI_InterruptIN,
60         .InterruptOUT  = UHCI_InterruptOUT,
61         .StopInterrupt = UHCI_StopInterrupt,
62         
63         .ControlSETUP = UHCI_ControlSETUP,
64         .ControlIN    = UHCI_ControlIN,
65         .ControlOUT   = UHCI_ControlOUT,
66
67         .BulkOUT = UHCI_BulkOUT,
68         .BulkIN = UHCI_BulkIN,
69         
70         .CheckPorts = UHCI_CheckPortUpdate
71         };
72 tSemaphore      gUHCI_InterruptSempahore;
73
74 // === CODE ===
75 /**
76  * \fn int UHCI_Initialise()
77  * \brief Called to initialise the UHCI Driver
78  */
79 int UHCI_Initialise(char **Arguments)
80 {
81          int    i=0, id=-1;
82          int    ret;
83         
84         ENTER("");
85         
86         // Initialise with no maximum value
87         Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
88
89         if( PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, -1) < 0 )
90         {
91                 LEAVE('i', MODULE_ERR_NOTNEEDED);
92                 return MODULE_ERR_NOTNEEDED;
93         }
94         
95         // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
96         while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
97         {
98                 tUHCI_Controller        *cinfo = &gUHCI_Controllers[i];
99                 Uint32  base_addr;
100                 // NOTE: Check "protocol" from PCI?
101                 
102                 cinfo->PciId = id;
103                 base_addr = PCI_GetBAR(id, 4);
104                 
105                 if( base_addr & 1 )
106                 {
107                         cinfo->IOBase = base_addr & ~1;
108                         cinfo->MemIOMap = NULL;
109                 }
110                 else
111                 {
112                         cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1);
113                 }
114                 cinfo->IRQNum = PCI_GetIRQ(id);
115                 
116                 Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i",
117                         id, base_addr, cinfo->IRQNum);
118                 
119                 IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
120         
121                 // Initialise Host
122                 ret = UHCI_int_InitHost(cinfo);
123                 // Detect an error
124                 if(ret != 0) {
125                         LEAVE('i', ret);
126                         return ret;
127                 }
128
129                 // Spin off interrupt handling thread
130                 Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
131
132                 
133                 cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
134                 LOG("cinfo->RootHub = %p", cinfo->RootHub);
135
136                 i ++;
137         }
138
139         if(i == MAX_CONTROLLERS) {
140                 Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
141         }
142         LEAVE('i', MODULE_ERR_OK);
143         return MODULE_ERR_OK;
144 }
145
146 /**
147  * \fn void UHCI_Cleanup()
148  * \brief Called just before module is unloaded
149  */
150 void UHCI_Cleanup()
151 {
152 }
153
154 /**
155  * \brief Initialises a UHCI host controller
156  * \param Host  Pointer - Host to initialise
157  */
158 int UHCI_int_InitHost(tUHCI_Controller *Host)
159 {
160         ENTER("pHost", Host);
161
162         _OutWord( Host, USBCMD, 4 );    // GRESET
163         Time_Delay(10);
164         _OutWord( Host, USBCMD, 0 );    // GRESET
165         
166         // Allocate Frame List
167         // - 1 Page, 32-bit address
168         // - 1 page = 1024  4 byte entries
169         Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
170         if( !Host->FrameList ) {
171                 Log_Warning("UHCI", "Unable to allocate frame list, aborting");
172                 LEAVE('i', -1);
173                 return -1;
174         }
175
176         Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
177         if( !Host->TDQHPage ) {
178                 // TODO: Clean up
179                 Log_Warning("UHCI", "Unable to allocate QH page, aborting");
180                 LEAVE('i', -1);
181                 return -1;
182         }
183
184         // Fill frame list
185         // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
186         const int       dest_offsets[] = {
187                 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,
188                 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31
189                 };
190         for( int i = 0; i < 1024; i ++ ) {
191                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH );
192                 Host->FrameList[i] = addr | 2;
193         }
194         for( int i = 0; i < 64; i ++ ) {
195                  int    ofs = dest_offsets[ i & (32-1) ];
196                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_256ms[ofs] );
197                 Host->FrameList[  0 + i*4] = addr | 2;
198                 Host->FrameList[256 + i*4] = addr | 2;
199                 Host->FrameList[512 + i*4] = addr | 2;
200                 Host->FrameList[768 + i*4] = addr | 2;
201         }
202         for( int i = 0; i < 32; i ++ ) {
203                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_128ms[i] );
204                 Host->TDQHPage->InterruptQHs_256ms[i*2  ].Next = addr | 2;
205                 Host->TDQHPage->InterruptQHs_256ms[i*2+1].Next = addr | 2;
206         }
207         for( int i = 0; i < 16; i ++ ) {
208                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_64ms[i] );
209                 Host->TDQHPage->InterruptQHs_128ms[i*2  ].Next = addr | 2;
210                 Host->TDQHPage->InterruptQHs_128ms[i*2+1].Next = addr | 2;
211         }
212         for( int i = 0; i < 8; i ++ ) {
213                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_32ms[i] );
214                 Host->TDQHPage->InterruptQHs_64ms[i*2  ].Next = addr | 2;
215                 Host->TDQHPage->InterruptQHs_64ms[i*2+1].Next = addr | 2;
216         }
217         for( int i = 0; i < 4; i ++ ) {
218                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_16ms[i] );
219                 Host->TDQHPage->InterruptQHs_32ms[i*2  ].Next = addr | 2;
220                 Host->TDQHPage->InterruptQHs_32ms[i*2+1].Next = addr | 2;
221         }
222         for( int i = 0; i < 2; i ++ ) {
223                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_8ms[i] );
224                 Host->TDQHPage->InterruptQHs_16ms[i*2  ].Next = addr | 2;
225                 Host->TDQHPage->InterruptQHs_16ms[i*2+1].Next = addr | 2;
226         }
227         for( int i = 0; i < 1; i ++ ) {
228                 Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_4ms[i] );
229                 Host->TDQHPage->InterruptQHs_8ms[i*2  ].Next = addr | 2;
230                 Host->TDQHPage->InterruptQHs_8ms[i*2+1].Next = addr | 2;
231         }
232         Host->TDQHPage->InterruptQHs_4ms[0].Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH ) | 2;
233         // Set child pointers
234         for( int i = 0; i < 127; i ++ ) {
235                 Host->TDQHPage->InterruptQHs_256ms[i].Child = 1;
236         }
237
238         // Set up control and bulk queues
239         Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
240         Host->TDQHPage->ControlQH.Child = 1;
241         Host->TDQHPage->BulkQH.Next = 1;
242         Host->TDQHPage->BulkQH.Child = 1;
243         
244         // Set frame length to 1 ms
245         _OutByte( Host, SOFMOD, 64 );
246         
247         // Set Frame List
248         _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
249         _OutWord( Host, FRNUM, 0 );
250         
251         // Enable Interrupts
252         _OutWord( Host, USBINTR, 0x000F );
253         PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
254
255         // Enable processing
256         _OutWord( Host, USBCMD, 0x0001 );
257
258         LEAVE('i', 0);
259         return 0;
260 }
261
262 // --------------------------------------------------------------------
263 // TDs and QH Allocation/Appending
264 // --------------------------------------------------------------------
265 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
266 {
267         static tMutex   lock;
268         Mutex_Acquire( &lock );
269         for( int i = 0; i < NUM_TDs; i ++ )
270         {
271                 if(gaUHCI_TDPool[i]._info.bActive == 0)
272                 {
273                         gaUHCI_TDPool[i].Link = 1;
274                         gaUHCI_TDPool[i].Control = (1 << 23);
275                         gaUHCI_TDPool[i]._info.bActive = 1;
276                         gaUHCI_TDPool[i]._info.period_entry = 0;
277                         Mutex_Release( &lock );
278                         return &gaUHCI_TDPool[i];
279                 }
280         }
281         Mutex_Release( &lock );
282         return NULL;
283 }
284
285 void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
286 {
287         static tMutex   lock;   // TODO: Should I use a shortlock (avoid being preempted)
288
289         Mutex_Acquire(&lock);
290         
291         // Ensure that there is an interrupt for each used frame
292         TD->Control |= (1 << 24);
293
294         // Stop controller
295         _OutWord( Cont, USBCMD, 0x0000 );
296         
297         // Add
298         TD->Link = 1;
299         if( QH->Child & 1 ) {
300                 QH->Child = MM_GetPhysAddr( (tVAddr)TD );
301         }
302         else {
303                 // Depth first
304                 QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
305         }
306         QH->_LastItem = TD;
307
308         // Reenable controller
309         _OutWord( Cont, USBCMD, 0x0001 );
310         
311         // DEBUG!
312         LOG("QH(%p)->Child = %x", QH, QH->Child);
313         LOG("TD(%p)->Control = %x", TD, TD->Control);
314
315         Mutex_Release(&lock);
316 }
317
318 /**
319  * \brief Send a transaction to the USB bus
320  * \param Cont  Controller pointer
321  * \param Addr  Function Address * 16 + Endpoint
322  * \param bTgl  Data toggle value
323  */
324 tUHCI_TD *UHCI_int_CreateTD(
325         tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
326         tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
327 {
328         tUHCI_TD        *td;
329         tUHCI_ExtraTDInfo       *info = NULL;
330
331         if( Length > 0x400 ) {
332                 Log_Error("UHCI", "Transaction length too large (%i > 0x400)", Length);
333                 return NULL;    // Controller allows up to 0x500, but USB doesn't
334         }
335
336         td = UHCI_int_AllocateTD(Cont);
337         if( !td ) {
338                 Log_Error("UHCI", "No avaliable TDs, transaction dropped");
339                 return NULL;
340         }
341
342         LOG("TD %p %i bytes, Type %x to 0x%x",
343                 td, Length, Type, Addr);
344
345         td->Control = (Length - 1) & 0x7FF;
346         td->Control |= (1 << 23);
347         td->Token  = ((Length - 1) & 0x7FF) << 21;
348         td->Token |= (bTgl & 1) << 19;
349         td->Token |= (Addr & 0xF) << 15;
350         td->Token |= ((Addr/16) & 0xFF) << 8;
351         td->Token |= Type;
352
353         if(
354                 ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
355         #if PHYS_BITS > 32
356                 || MM_GetPhysAddr( (tVAddr)Data ) >> 32
357         #endif
358                 )
359         {
360                 td->BufferPointer = MM_AllocPhysRange(1, 32);
361
362                 LOG("Allocated page %x", td->BufferPointer);            
363
364                 if( Type == 0x69 )      // IN token
365                 {
366                         LOG("Relocated IN");
367                         info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
368                         info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
369                         info->FirstPage = MM_GetPhysAddr( (tVAddr)Data );
370                         info->SecondPage = MM_GetPhysAddr( (tVAddr)Data + Length - 1 );
371                 }
372                 else
373                 {
374                         LOG("Relocated OUT/SETUP");
375                         tVAddr  ptr = MM_MapTemp(td->BufferPointer);
376                         memcpy( (void*)ptr, Data, Length );
377                         MM_FreeTemp(ptr);
378                         td->Control |= (1 << 24);
379                 }
380                 td->_info.bFreePointer = 1;
381         }
382         else
383         {
384                 td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
385                 td->_info.bFreePointer = 0;
386         }
387
388         // Interrupt on completion
389         if( Cb )
390         {
391                 if( !info )
392                         info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
393                 LOG("IOC Cb=%p CbData=%p", Cb, CbData);
394                 info->Callback = Cb;
395                 info->CallbackPtr = CbData;
396         }
397         
398         if( info ) {
399                 LOG("info = %p", info);
400                 td->Control |= (1 << 24);
401                 td->_info.ExtraInfo = info;
402         }
403
404         return td;
405 }
406
407 void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
408 {
409         tUHCI_QH        *qh;
410         const int       qh_offsets[] = { 0, 64, 96, 112, 120, 124, 126};
411 //      const int       qh_sizes[]   = {64, 32, 16,   8,   4,   2,   1};
412         
413         // Bounds limit
414         if( Period < 0 )        return ;
415         if( Period > 256 )      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         TD->_info.period_entry = qh_offsets[period_slot] + 1;
426         qh = Cont->TDQHPage->InterruptQHs_4ms + TD->_info.period_entry - 1;
427         // TODO: Find queue with lowest load
428
429         LOG("period_slot = %i, period_entry = %i (+1 when encoded)",
430                 period_slot, TD->_info.period_entry);
431
432         UHCI_int_AppendTD(Cont, qh, TD);
433 }
434
435 void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
436 {
437         tUHCI_TD        *td;
438
439         if( Period < 0 )        return NULL;
440
441         ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
442                 Ptr, Dest, Period, Cb, CbData, Buf, Length);
443
444         // TODO: Data toggle?
445         td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length);
446         if( !td )       return NULL;
447         
448         UHCI_int_SetInterruptPoll(Ptr, td, Period);
449         
450         LEAVE('p', td); 
451         return td;
452 }
453 // TODO: Does interrupt OUT make sense?
454 void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
455 {
456         tUHCI_TD        *td;
457
458         if( Period < 0 )        return NULL;
459
460         ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
461                 Ptr, Dest, Period, Cb, CbData, Buf, Length);
462
463         // TODO: Data toggle?
464         td = UHCI_int_CreateTD(Ptr, Dest, PID_OUT, 0, Cb, CbData, Buf, Length);
465         if( !td )       return NULL;
466         
467         UHCI_int_SetInterruptPoll(Ptr, td, Period);
468
469         LEAVE('p', td); 
470         return td;
471 }
472
473 void UHCI_StopInterrupt(void *Ptr, void *Handle)
474 {
475         // TODO: Stop interrupt transaction
476 }
477
478 void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
479 {
480         tUHCI_Controller        *Cont = Ptr;
481         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
482         tUHCI_TD        *td;
483
484         ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
485         
486         td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
487         UHCI_int_AppendTD(Cont, qh, td);
488
489         LEAVE('p', td); 
490
491         return td;
492 }
493 void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
494 {
495         tUHCI_Controller        *Cont = Ptr;
496         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
497         tUHCI_TD        *td;
498
499         ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
500
501         td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
502         UHCI_int_AppendTD(Cont, qh, td);
503
504         LEAVE('p', td);
505         return td;
506 }
507 void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
508 {
509         tUHCI_Controller        *Cont = Ptr;
510         tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
511         tUHCI_TD        *td;
512
513         ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
514         
515         td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, !!Tgl, Cb, CbData, Data, Length);
516         UHCI_int_AppendTD(Cont, qh, td);
517
518         LEAVE('p', td);
519         return td;
520 }
521
522 void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
523 {
524         tUHCI_Controller        *Cont = Ptr;
525         tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
526         tUHCI_TD        *td;
527         char    *src = Buf;
528
529         ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
530
531         while( Length > MAX_PACKET_SIZE )
532         {
533                 LOG("MaxPacket (rem = %i)", Length);
534                 td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, MAX_PACKET_SIZE);
535                 UHCI_int_AppendTD(Cont, qh, td);
536                 
537                 bToggle = !bToggle;
538                 Length -= MAX_PACKET_SIZE;
539                 src += MAX_PACKET_SIZE;
540         }
541
542         LOG("Final");
543         td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length);
544         UHCI_int_AppendTD(Cont, qh, td);
545
546         LEAVE('p', td);
547         return td;
548 }
549 void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
550 {
551         tUHCI_Controller        *Cont = Ptr;
552         tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
553         tUHCI_TD        *td;
554         char    *dst = Buf;
555
556         ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
557         while( Length > MAX_PACKET_SIZE )
558         {
559                 LOG("MaxPacket (rem = %i)", Length);
560                 td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, MAX_PACKET_SIZE);
561                 UHCI_int_AppendTD(Cont, qh, td);
562                 
563                 bToggle = !bToggle;
564                 Length -= MAX_PACKET_SIZE;
565                 dst += MAX_PACKET_SIZE;
566         }
567
568         LOG("Final");
569         td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length);
570         UHCI_int_AppendTD(Cont, qh, td);
571
572         LEAVE('p', td);
573         return td;
574 }
575
576 // === INTERNAL FUNCTIONS ===
577 void UHCI_CheckPortUpdate(void *Ptr)
578 {
579         tUHCI_Controller        *Host = Ptr;
580         // Enable ports
581         for( int i = 0; i < 2; i ++ )
582         {
583                  int    port = PORTSC1 + i*2;
584                 Uint16  status;
585         
586                 status = _InWord(Host, port);
587                 // Check for port change
588                 if( !(status & 0x0002) )        continue;
589                 _OutWord(Host, port, 0x0002);
590                 
591                 // Check if the port is connected
592                 if( !(status & 1) )
593                 {
594                         // Tell the USB code it's gone.
595                         USB_DeviceDisconnected(Host->RootHub, i);
596                         continue;
597                 }
598                 else
599                 {
600                         LOG("Port %i has something", i);
601                         // Reset port (set bit 9)
602                         LOG("Reset");
603                         _OutWord(Host, port, 0x0200);
604                         Time_Delay(50); // 50ms delay
605                         _OutWord(Host, port, _InWord(Host, port) & ~0x0200);
606                         // Enable port
607                         LOG("Enable");
608                         Time_Delay(50); // 50ms delay
609                         _OutWord(Host, port, _InWord(Host, port) | 0x0004);
610                         // Tell USB there's a new device
611                         USB_DeviceConnected(Host->RootHub, i);
612                 }
613         }
614 }
615
616 void UHCI_int_InterruptThread(void *Pointer)
617 {
618         tUHCI_Controller        *Cont = Pointer;
619         Threads_SetName("UHCI Interrupt Handler");
620         for( ;; )
621         {
622                 LOG("zzzzz....");
623                 // 0 = Take all
624                 Semaphore_Wait(&gUHCI_InterruptSempahore, 0);
625                 LOG("Huh?");
626         
627                 for( int i = 0; i < NUM_TDs; i ++ )
628                 {
629                          int    byte_count;
630                         tUHCI_ExtraTDInfo       *info;
631                         tUHCI_TD        *td;
632                         
633                         td = &gaUHCI_TDPool[i];
634                         info = td->_info.ExtraInfo;
635
636                         // Skip completely inactive TDs
637                         if( td->_info.bActive == 0 )    continue ;
638                         // Skip ones that are still in use
639                         if( td->Control & (1 << 23) )   continue ;
640
641                         // If no callback/alt buffer, mark as free and move on
642                         if( td->_info.ExtraInfo )
643                         {
644                                 // Get size of transfer
645                                 byte_count = (td->Control & 0x7FF)+1;
646                         
647                                 // Handle non page-aligned destination (or with a > 32-bit paddr)
648                                 if(info->FirstPage)
649                                 {
650                                         char    *src, *dest;
651                                          int    src_ofs = td->BufferPointer & (PAGE_SIZE-1);
652                                         src = (void *) MM_MapTemp(td->BufferPointer);
653                                         dest = (void *) MM_MapTemp(info->FirstPage);
654                                         // Check for a single page transfer
655                                         if( byte_count + info->Offset <= PAGE_SIZE )
656                                         {
657                                                 LOG("Single page copy %P to %P of %p",
658                                                         td->BufferPointer, info->FirstPage, td);
659                                                 memcpy(dest + info->Offset, src + src_ofs, byte_count);
660                                         }
661                                         else
662                                         {
663                                                 // Multi-page
664                                                 LOG("Multi page copy %P to (%P,%P) of %p",
665                                                         td->BufferPointer, info->FirstPage, info->SecondPage, td);
666                                                  int    part_len = PAGE_SIZE - info->Offset;
667                                                 memcpy(dest + info->Offset, src + src_ofs, part_len);
668                                                 MM_FreeTemp( (tVAddr)dest );
669                                                 dest = (void *) MM_MapTemp(info->SecondPage);
670                                                 memcpy(dest, src + src_ofs + part_len, byte_count - part_len);
671                                         }
672                                         MM_FreeTemp( (tVAddr)src );
673                                         MM_FreeTemp( (tVAddr)dest );
674                                 }
675         
676                                 // Callback
677                                 if( info->Callback != NULL )
678                                 {
679                                         LOG("Calling cb %p", info->Callback);
680                                         void    *ptr = (void *) MM_MapTemp( td->BufferPointer );
681                                         info->Callback( info->CallbackPtr, ptr, byte_count );
682                                         MM_FreeTemp( (tVAddr)ptr );
683                                 }
684                                 
685                                 // Clean up info
686                                 free( info );
687                                 td->_info.ExtraInfo = NULL;
688                         }
689
690                         if( td->_info.period_entry > 0 )
691                         {
692                                 LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.period_entry-1);
693                                 // TODO: Flip toggle?
694                                 td->Control |= (1 << 23);
695                                 // Add back into controller's interrupt list
696                                 UHCI_int_AppendTD(
697                                         Cont,
698                                         Cont->TDQHPage->InterruptQHs_256ms + td->_info.period_entry - 1,
699                                         td
700                                         );
701                                 continue ;
702                         }
703
704                         if( td->_info.bFreePointer )
705                                 MM_DerefPhys( td->BufferPointer );                      
706         
707                         // Clean up
708                         td->_info.bActive = 0;
709                         LOG("Cleaned %p", td);
710                 }
711         }
712 }
713
714 void UHCI_InterruptHandler(int IRQ, void *Ptr)
715 {
716         tUHCI_Controller *Host = Ptr;
717 //       int    frame = (_InWord(Host, FRNUM) - 1) & 0x3FF;
718         Uint16  status = _InWord(Host, USBSTS);
719         
720         // Interrupt-on-completion
721         if( status & 1 )
722         {
723                 // TODO: Support isochronous transfers (will need updating the frame pointer)
724                 Semaphore_Signal(&gUHCI_InterruptSempahore, 1);
725         }
726
727         LOG("status = 0x%04x", status);
728         _OutWord(Host, USBSTS, status);
729 }
730
731 void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value)
732 {
733         if( Host->MemIOMap )
734                 ((Uint8*)Host->MemIOMap)[Reg] = Value;
735         else
736                 outb(Host->IOBase + Reg, Value);
737 }
738
739 void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value)
740 {
741         if( Host->MemIOMap )
742                 Host->MemIOMap[Reg/2] = Value;
743         else
744                 outw(Host->IOBase + Reg, Value);
745 }
746
747 void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value)
748 {
749         if( Host->MemIOMap )
750                 ((Uint32*)Host->MemIOMap)[Reg/4] = Value;
751         else
752                 outd(Host->IOBase + Reg, Value);
753 }
754
755 Uint16 _InWord(tUHCI_Controller *Host, int Reg)
756 {
757         if( Host->MemIOMap )
758                 return Host->MemIOMap[Reg/2];
759         else
760                 return inw(Host->IOBase + Reg);
761 }
762

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