2 * Acess2 USB Stack - OHCI Driver
3 * - By John Hodge (thePowersGang)
6 * - Open Host Controller Interface driver
9 #define VERSION VER2(0,1)
17 #define MAX_CONTROLLERS 4
18 #define MAX_TD_PAGES 2
19 #define MAX_ENDPT_PAGES 2
20 #define MAX_PACKET_SIZE 1023 // TODO: Check what should be used
23 int OHCI_Initialise(char **Arguments);
24 void OHCI_Cleanup(void);
26 void OHCI_InitialiseController(tOHCI_Controller *Controller);
28 void *OHCI_int_AddTD(tOHCI_Controller *Controller, int Dest, int DataTgl, int Type, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
29 void *OHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
30 void *OHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
31 void *OHCI_SendSETUP(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
32 int OHCI_IsTransferComplete(void *Ptr, void *Handle);
34 void *OHCI_StartPoll(void *Ptr, int Dest, int MaxPeriod, tUSBHostCb Cb, void *CbData, void *DataBuf, size_t Length);
35 void OHCI_StopPoll(void *Ptr, void *Hdl);
36 void OHCI_CheckPortUpdate(void *Ptr);
37 void OHCI_InterruptHandler(int IRQ, void *Ptr);
39 tOHCI_Endpoint *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller, int Dest);
40 tOHCI_Endpoint *OHCI_int_GetEndptFromPhys(tPAddr PhysAddr);
41 tOHCI_GeneralTD *OHCI_int_AllocateGTD(tOHCI_Controller *Controller);
42 tOHCI_GeneralTD *OHCI_int_GetGTDFromPhys(tPAddr PhysAddr);
45 MODULE_DEFINE(0, VERSION, USB_OHCI, OHCI_Initialise, OHCI_Cleanup, "USB_Core", NULL);
46 tUSBHostDef gOHCI_HostDef = {
47 .SendIN = OHCI_DataIN,
48 .SendOUT = OHCI_DataOUT,
49 .SendSETUP = OHCI_SendSETUP,
50 .IsOpComplete = OHCI_IsTransferComplete,
52 // .StartPolling = OHCI_StartPoll,
53 // .StopPolling = OHCI_StopPoll,
55 .CheckPorts = OHCI_CheckPortUpdate
57 tOHCI_Controller gOHCI_Controllers[MAX_CONTROLLERS];
58 tOHCI_GeneralTD *gapOHCI_TDPool[MAX_TD_PAGES]; //!< Virtual pointers to TDs, each has an index in avail bits
59 const int ciTDs_per_page = PAGE_SIZE/sizeof(tOHCI_GeneralTD);
60 tOHCI_Endpoint *gapOHCI_EndpointPool[MAX_ENDPT_PAGES];
61 const int ciEndpoints_per_page = PAGE_SIZE/sizeof(tOHCI_Endpoint);
64 int OHCI_Initialise(char **Arguments)
69 while( (id = PCI_GetDeviceByClass(0x0C0310, 0xFFFFFF, id)) != -1 && card_num < MAX_CONTROLLERS )
71 tOHCI_Controller *ctrlr = &gOHCI_Controllers[card_num];
75 ctrlr->IRQNum = PCI_GetIRQ(id);
76 ctrlr->ControlSpacePhys = PCI_GetBAR(id, 0); // Offset 0x10
77 PCI_ConfigWrite( id, 4, 2, 0x0006 ); // Enable memory and bus master
79 if( ctrlr->ControlSpacePhys == 0 || ctrlr->ControlSpacePhys & 1 ) {
80 ctrlr->ControlSpacePhys = 0;
84 OHCI_InitialiseController( ctrlr );
90 Log_Warning("OHCI", "Too many controllers (> %i) were detected, ignoring the rest",
97 void OHCI_Cleanup(void)
100 // TODO: Cleanup for unload
102 for( i = 0; i < MAX_CONTROLLERS && gOHCI_Controllers[i].ControlSpacePhys; i ++ )
104 // TODO: Clean up resources used
108 void OHCI_InitialiseController(tOHCI_Controller *Controller)
110 tOHCI_Controller *cnt = Controller;
111 volatile struct sRegisters *iospace;
113 Log_Debug("OHCI", "Card #%i at 0x%X, IRQ %i",
114 cnt->ID, cnt->ControlSpacePhys, cnt->IRQNum);
116 // - Prepare mappings
117 cnt->ControlSpace = (void*)MM_MapHWPages(cnt->ControlSpacePhys, 1);
118 IRQ_AddHandler( cnt->IRQNum, OHCI_InterruptHandler, cnt);
119 iospace = cnt->ControlSpace;
121 Log_Debug("OHCI", "Card #%i version is 0x%x", cnt->ID, iospace->HcRevision);
123 // Check who had control
124 if( iospace->HcControl & (1 << 8) ) // InterruptRouting
126 LOG("USB was in the hands of SMM, asking for it back");
127 // SMM has control, ask for it back
128 // - Write '1' to OwnershipChangeRequest
129 // - Wait for InterruptRouting to clear
131 while( iospace->HcControl & (1 << 8) ) ;
134 else if( (iospace->HcControl & 0xC0) != 0x00 ) // UsbReset
136 LOG("USB was in the hands of the BIOS");
137 // BIOS had control, check for Operational
138 if( (iospace->HcControl & 0xC0) != 0x80 ) // UsbOperational
140 // - If not, set to resume
141 iospace->HcControl &= ~0xC0; // UsbResume
142 iospace->HcControl |= 0x40; // UsbResume
148 // Cold boot, wait a bit
149 // TODO: Wait for reset time
152 // Allocate HCCA area
153 cnt->HCCA = (void*)MM_AllocDMA(1, 32, &cnt->HCCAPhys);
155 Log_Error("OHCI", "Unable to allocate HCCA (1 page, 32-bit)");
158 // TODO: Check return value
159 // - HCCA is 256 bytes in length, but I like alignment
160 cnt->IntLists = (void*)( (tVAddr)cnt->HCCA + 512 );
161 LOG("HCCAPhys = %P, HCCA = %p", cnt->HCCAPhys, cnt->HCCA);
163 // --- Restart the controller ---
164 Uint32 fm_interval = iospace->HcFmInterval;
165 iospace->HcCommandStatus |= (1 << 0);
166 // - Wait 10 micro-seconds
168 iospace->HcFmInterval = fm_interval;
169 // (Now in UsbSuspend state)
172 // - Check that things are good?
174 // --- Initialise Virtual Queues ---
175 memset(cnt->IntLists, 0, sizeof(*cnt->IntLists));
176 // Set next pointers into a binary tree
178 tPAddr next_lvl = cnt->HCCAPhys + 512;
179 next_lvl += 16 * sizeof(tOHCI_Endpoint);
180 for( int i = 0; i < 16; i ++ )
181 cnt->IntLists->Period16[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
182 next_lvl += 8 * sizeof(tOHCI_Endpoint);
183 for( int i = 0; i < 8; i ++ )
184 cnt->IntLists->Period8[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
185 next_lvl += 4 * sizeof(tOHCI_Endpoint);
186 for( int i = 0; i < 4; i ++ )
187 cnt->IntLists->Period4[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
188 next_lvl += 2 * sizeof(tOHCI_Endpoint);
189 for( int i = 0; i < 2; i ++ )
190 cnt->IntLists->Period2[i].NextED = next_lvl + sizeof(tOHCI_Endpoint);
191 next_lvl += 1 * sizeof(tOHCI_Endpoint);
192 cnt->IntLists->Period1[0].NextED = next_lvl;
194 // Set all endpoints to be skipped
195 for( int i = 0; i < 32; i ++ )
197 tOHCI_Endpoint *ep = &cnt->IntLists->Period16[i];
198 ep->Flags |= (1 << 14); // Skip
201 // --- Initialise HCCA ---
202 // - Interrupt table is set up to point to each
203 for( int i = 0; i < 32; i ++ )
205 static const int _balance[] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
206 cnt->HCCA->HccaInterruptTable[i] = cnt->HCCAPhys + 512 + _balance[i&15] * sizeof(tOHCI_Endpoint);
208 cnt->HCCA->HccaFrameNumber = 0;
209 cnt->HCCA->HccaDoneHead = 0;
211 // --- Initialise Registers
212 iospace->HcControlHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
213 iospace->HcBulkHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
214 iospace->HcHCCA = cnt->HCCAPhys;
215 iospace->HcInterruptEnable = 0x7B; // 0111 1011 (RHSC, FNO, UE, RD, WDH, SO)
216 iospace->HcInterruptDisable = 0x4; // Disable SOF interrupts
217 iospace->HcControl = 0x3C; // All queues on
218 iospace->HcPeriodicStart = fm_interval / 10 * 9; // 90% of fm_interval
221 iospace->HcControl |= (2 << 6); // UsbOperational
223 LOG("HcRhDescriptorA = 0x%x", iospace->HcRhDescriptorA);
224 LOG("HcRhDescriptorB = 0x%x", iospace->HcRhDescriptorB);
226 // --- Tell USB core that this controller is avaliable
227 cnt->nPorts = iospace->HcRhDescriptorA & 0x7F;
228 if( cnt->nPorts > 15 ) {
230 Log_Warning("OHCI", "Controller reports %i ports, but spec only allows 15, capping", cnt->nPorts);
233 cnt->RootHub = USB_RegisterHost(&gOHCI_HostDef, cnt, cnt->nPorts);
236 // --- USB IO Functions ---
238 tOHCI_Controller *Controller, int Dest,
239 int DataTgl, int Type,
240 tUSBHostCb Cb, void *CbData,
241 void *Buf, size_t Length
249 if( Length > MAX_PACKET_SIZE )
252 ENTER("pController xDest iDataTgl iType pCb pCbData pBuf iLength",
253 Controller, Dest, DataTgl, Type, Cb, CbData, Buf, Length);
255 // ---- Check that the packet resides within memory the controller can access
256 if( MM_GetPhysAddr((tVAddr)Buf) >= (1ULL << 32) || MM_GetPhysAddr((tVAddr)Buf + Length -1) >= (1ULL << 32) )
258 // TODO: Handle destination outside of 32-bit address range
259 Log_Warning("OHCI", "_int_DoTD - Buffer outside of 32-bit range, TODO: Handle this");
264 // --- Find/Allocate Endpoint descriptor
265 ep = OHCI_int_AllocateEndpt(Controller, Dest);
267 Log_Warning("OHCI", "_int_DoTD - Unable to find/allocate Endpoint 0x%x... oops", Dest);
273 td = OHCI_int_AllocateGTD(Controller);
275 Log_Warning("OHCI", "_int_DoTD - Unable to allocate TD... oops");
279 td_phys = MM_GetPhysAddr( (tVAddr)td );
282 td->Flags |= (1 << 18); // Buffer rounding
283 td->Flags |= (Type & 3) << 19; // Type/Direction
284 td->Flags |= (2 | DataTgl) << 24; // Data Toggle
285 td->CBP = MM_GetPhysAddr( (tVAddr) Buf );
286 td->BE = MM_GetPhysAddr( (tVAddr)Buf + Length - 1 );
287 // - Save callback etc
288 td->CbPointer = (tVAddr)Cb;
289 td->CbArg = (tVAddr)CbData;
291 // --- Add to the end of the Endpoint's list
293 OHCI_int_GetGTDFromPhys( ep->TailP )->NextTD = td_phys;
301 void *OHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
303 return OHCI_int_DoTD(Ptr, Dest, DataTgl, 2, Cb, CbData, Buf, Length);
305 void *OHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
307 return OHCI_int_DoTD(Ptr, Dest, DataTgl, 1, Cb, CbData, Buf, Length);
309 void *OHCI_SendSETUP(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
311 return OHCI_int_DoTD(Ptr, Dest, DataTgl, 0, Cb, CbData, Buf, Length);
313 int OHCI_IsTransferComplete(void *Ptr, void *Handle)
315 tOHCI_GeneralTD *td = Handle;
317 ENTER("pPtr pHandle", Ptr, Handle);
326 Log_Warning("OHCI", "TODO: Implement cleanup in OHCI_IsTransferComplete");
332 // --- Interrupt polling ---
333 void *OHCI_StartPoll(void *Ptr, int Dest, int MaxPeriod, tUSBHostCb Cb, void *CbData, void *DataBuf, size_t Length)
337 if( MaxPeriod <= 0 ) return NULL;
339 // Allocate? (or obtain) an ED
342 for( slot = 32; slot > MaxPeriod && slot; slot >>= 1 );
350 // Add to period 1 list
353 // Determine best list by current load
364 Log_Error("OHCI", "OHCI_StartPoll - `slot` is invalid (%i)", slot);
368 Log_Warning("OHCI", "TODO: Implement OHCI_StartPoll");
372 void OHCI_StopPoll(void *Ptr, void *Hdl)
378 void OHCI_CheckPortUpdate(void *Ptr)
380 tOHCI_Controller *cnt = Ptr;
381 for( int i = 0; i < cnt->nPorts; i ++ )
383 volatile Uint32 *status_ptr = &cnt->ControlSpace->HcRhPortStatus[i];
384 Uint32 status = *status_ptr;
386 if( status & (1 << 16) )
388 LOG("Connect status change port %i, *status = 0x%x", i, status);
389 // Connect or disconnect?
390 if( status & (1 << 0) )
392 // Connect, set things up :)
397 *status_ptr = 1 << 4; // PRS
400 *status_ptr = 1 << 1; // SetPortEnable
401 LOG("Device connected on port %i", i);
404 USB_DeviceConnected(cnt->RootHub, i);
409 USB_DeviceDisconnected(cnt->RootHub, i);
413 // TODO: Handle other bits?
417 // --- Interrupt handler
418 void OHCI_InterruptHandler(int IRQ, void *Ptr)
420 tOHCI_Controller *cnt = Ptr;
421 // TODO: Interrupt handler
422 LOG("Interrupt on controller %i - Status = 0x%x",
423 cnt->ID, cnt->ControlSpace->HcInterruptStatus);
425 cnt->ControlSpace->HcInterruptStatus = cnt->ControlSpace->HcInterruptStatus;
428 // --- Internal Functions ---
429 tOHCI_Endpoint *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller, int Dest)
431 tOHCI_Endpoint *first_free_ep = NULL;
432 tOHCI_Endpoint *first_freeable_ep = NULL;
436 // Convert 8.4 dev:endpt destination into 4.7 endpt:dev
437 _dest = (Dest >> 4) | ((Dest & 0xF) << 7);
440 for( pg = 0; pg < MAX_ENDPT_PAGES; pg ++ )
442 if( !gapOHCI_EndpointPool[pg] ) {
443 tPAddr paddr; // Not used
444 gapOHCI_EndpointPool[pg] = (void*)MM_AllocDMA(1, 32, &paddr);
445 memset(gapOHCI_EndpointPool[pg], 0, PAGE_SIZE);
448 for( i = 0; i < ciEndpoints_per_page; i ++ )
450 tOHCI_Endpoint *ep = &gapOHCI_EndpointPool[pg][i];
452 // Check if it's allocated
453 if( ep->Flags & (1 << 31) )
456 first_freeable_ep = ep;
458 if( ((ep->Flags >> 27) & 0xF) != Controller->ID )
460 if( (ep->Flags & 0x7FF) != _dest )
472 if( first_free_ep ) {
473 first_free_ep->Flags = (1 << 31);
476 first_free_ep->Flags |= Controller->ID << 27;
477 first_free_ep->Flags |= _dest;
478 first_free_ep->Flags |= 1023 << 16; // Max Packet Size
480 return first_free_ep;
483 if( first_freeable_ep ) {
485 first_free_ep->Flags = (1 << 31);
488 first_free_ep->Flags |= Controller->ID << 27;
489 first_free_ep->Flags |= _dest;
491 return first_free_ep;
493 Log_Warning("OHCI", "TODO: Implement freeing EPs when no avalable slots");
499 tOHCI_Endpoint *OHCI_int_GetEndptFromPhys(tPAddr PhysAddr)
502 for( i = 0; i < MAX_ENDPT_PAGES; i ++ )
505 addr = MM_GetPhysAddr( (tVAddr)gapOHCI_EndpointPool[i] );
506 if( PhysAddr >= addr && PhysAddr < addr + 0x1000 )
508 return gapOHCI_EndpointPool[i] + (PhysAddr - addr) / sizeof(tOHCI_Endpoint);
514 tOHCI_GeneralTD *OHCI_int_AllocateGTD(tOHCI_Controller *Controller)
518 for( pg = 0; pg < MAX_TD_PAGES; pg ++ )
520 if( !gapOHCI_TDPool[pg] ) {
521 tPAddr paddr; // Not used
522 gapOHCI_TDPool[pg] = (void*)MM_AllocDMA(1, 32, &paddr);
523 memset(gapOHCI_TDPool[pg], 0, PAGE_SIZE);
526 for( i = 0; i < ciTDs_per_page; i ++ )
528 // Check if allocated, and if not take it
529 if( gapOHCI_TDPool[pg][i].Flags & (1 << 31) )
531 gapOHCI_TDPool[pg][i].Flags = (1 << 31);
534 gapOHCI_TDPool[pg][i].Flags |= Controller->ID << 27;
536 return &gapOHCI_TDPool[pg][i];
542 tOHCI_GeneralTD *OHCI_int_GetGTDFromPhys(tPAddr PhysAddr)
545 for( i = 0; i < MAX_TD_PAGES; i ++ )
548 addr = MM_GetPhysAddr( (tVAddr)gapOHCI_TDPool[i] );
549 if( PhysAddr >= addr && PhysAddr < addr + 0x1000 )
551 return gapOHCI_TDPool[i] + (PhysAddr - addr) / sizeof(tOHCI_GeneralTD);