3 * - By John Hodge (thePowersGang)
9 #define VERSION VER2(0,1)
18 #define EHCI_MAX_CONTROLLERS 4
21 int EHCI_Initialise(char **Arguments);
22 int EHCI_Cleanup(void);
23 int EHCI_InitController(tPAddr BaseAddress, Uint8 InterruptNum);
24 void EHCI_InterruptHandler(int IRQ, void *Ptr);
26 void *EHCI_InitInterrupt(void *Ptr, int Endpoint, int bInput, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
27 void *EHCI_InitIsoch (void *Ptr, int Endpoint, size_t MaxPacketSize);
28 void *EHCI_InitControl(void *Ptr, int Endpoint, size_t MaxPacketSize);
29 void *EHCI_InitBulk (void *Ptr, int Endpoint, size_t MaxPacketSize);
30 void EHCI_RemEndpoint(void *Ptr, void *Handle);
31 void *EHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
33 const void *SetupData, size_t SetupLength,
34 const void *OutData, size_t OutLength,
35 void *InData, size_t InLength
37 void *EHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length);
38 void EHCI_FreeOp(void *Ptr, void *Handle);
40 tEHCI_qTD *EHCI_int_AllocateTD(tEHCI_Controller *Cont, int PID, void *Data, size_t Length, tUSBHostCb Cb, void *CbData);
41 void EHCI_int_DeallocateTD(tEHCI_Controller *Cont, tEHCI_qTD *TD);
42 void EHCI_int_AppendTD(tEHCI_QH *QH, tEHCI_qTD *TD);
43 tEHCI_QH *EHCI_int_AllocateQH(tEHCI_Controller *Cont, int Endpoint, size_t MaxPacketSize);
44 void EHCI_int_DeallocateQH(tEHCI_Controller *Cont, tEHCI_QH *QH);
47 MODULE_DEFINE(0, VERSION, USB_EHCI, EHCI_Initialise, NULL, "USB_Core", NULL);
48 tEHCI_Controller gaEHCI_Controllers[EHCI_MAX_CONTROLLERS];
49 tUSBHostDef gEHCI_HostDef = {
50 .InitInterrupt = EHCI_InitInterrupt,
51 .InitIsoch = EHCI_InitIsoch,
52 .InitBulk = EHCI_InitBulk,
53 .RemEndpoint = EHCI_RemEndpoint,
55 .SendControl = EHCI_SendControl,
56 .SendBulk = EHCI_SendBulk,
61 int EHCI_Initialise(char **Arguments)
63 for( int id = -1; (id = PCI_GetDeviceByClass(0x0C0320, 0xFFFFFF, id)) >= 0; )
65 Uint32 addr = PCI_GetBAR(id, 0);
67 // Oops, PCI BIOS emulation time
69 Uint8 irq = PCI_GetIRQ(id);
74 if( EHCI_InitController(addr, irq) ) {
75 // TODO: Detect other forms of failure than "out of slots"
82 int EHCI_Cleanup(void)
87 // --- Driver Init ---
88 int EHCI_InitController(tPAddr BaseAddress, Uint8 InterruptNum)
90 tEHCI_Controller *cont = NULL;
92 for( int i = 0; i < EHCI_MAX_CONTROLLERS; i ++ )
94 if( gaEHCI_Controllers[i].PhysBase == 0 ) {
95 cont = &gaEHCI_Controllers[i];
96 cont->PhysBase = BaseAddress;
105 // -- Build up structure --
106 cont->CapRegs = (void*)MM_MapHWPages(BaseAddress, 1);
108 cont->OpRegs = (void*)( (Uint32*)cont->CapRegs + cont->CapRegs->CapLength / 4 );
109 // - Allocate periodic queue
110 cont->PeriodicQueue = (void*)MM_AllocDMA(1, 32, NULL);
115 IRQ_AddHandler(InterruptNum, EHCI_InterruptHandler, cont);
117 // -- Initialisation procedure (from ehci-r10) --
118 // - Reset controller
119 cont->OpRegs->USBCmd = USBCMD_HCReset;
120 // - Set CTRLDSSEGMENT (TODO: 64-bit support)
122 cont->OpRegs->USBIntr = USBINTR_IOC|USBINTR_PortChange|USBINTR_FrameRollover;
123 // - Set PERIODICLIST BASE
124 cont->OpRegs->PeridocListBase = MM_GetPhysAddr( cont->PeriodicQueue );
125 // - Enable controller
126 cont->OpRegs->USBCmd = (0x40 << 16) | USBCMD_PeriodicEnable | USBCMD_Run;
128 cont->OpRegs->ConfigFlag = 1;
133 void EHCI_InterruptHandler(int IRQ, void *Ptr)
135 tEHCI_Controller *cont = Ptr;
136 Uint32 sts = cont->OpRegs->USBSts;
139 cont->OpRegs->USBSts = sts;
141 if( sts & USBINTR_IOC ) {
146 if( sts & USBINTR_PortChange ) {
147 // Port change, determine what port and poke helper thread
148 sts &= ~USBINTR_PortChange;
151 if( sts & USBINTR_FrameRollover ) {
152 // Frame rollover, used to aid timing (trigger per-second operations)
153 sts &= ~USBINTR_FrameRollover;
157 // Unhandled interupt bits
162 // --------------------------------------------------------------------
164 // --------------------------------------------------------------------
165 void *EHCI_InitInterrupt(void *Ptr, int Endpoint, int bOutbound, int Period,
166 tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
168 tEHCI_Controller *Cont = Ptr;
169 int pow2period, period_pow;
171 if( Endpoint >= 256*16 )
178 // Round the period to the closest power of two
181 // - Find the first power above the period
182 while( pow2period < Period )
187 // - Check which is closest
188 if( Period - pow2period / 2 > pow2period - Period )
191 Period = pow2period/2;
196 tEHCI_QH *qh = EHCI_int_AllocateQH(Cont, Endpoint, Length);
197 qh->Impl.IntPeriodPow = period_pow;
199 // Choose an interrupt slot to use
200 int minslot = 0, minslot_load = INT_MAX;
201 for( int slot = 0; slot < Period; slot ++ )
204 for( int i = 0; i < PERIODIC_SIZE; i += Period )
205 load += Cont->InterruptLoad[i+slot];
206 if( load == 0 ) break;
207 if( load < minslot_load ) {
212 // Increase loading on the selected slot
213 for( int i = 0; i < PERIODIC_SIZE; i += Period )
214 Cont->InterruptLoad[i+minslot] += Length;
215 qh->Impl.IntOfs = minslot;
217 // Allocate TD for the data
218 tEHCI_qTD *td = EHCI_int_AllocateTD(Cont, (bOutbound ? PID_OUT : PID_IN), Buf, Length, Cb, CbData);
219 EHCI_int_AppendTD(qh, td);
221 // Insert into the periodic list
226 void *EHCI_InitIsoch(void *Ptr, int Endpoint, size_t MaxPacketSize)
228 return (void*)(tVAddr)(Endpoint + 1);
230 void *EHCI_InitControl(void *Ptr, int Endpoint, size_t MaxPacketSize)
232 tEHCI_Controller *Cont = Ptr;
235 tEHCI_QH *qh = EHCI_int_AllocateQH(Cont, Endpoint, MaxPacketSize);
237 // Append to async list
238 if( Cont->LastAsyncHead ) {
239 Cont->LastAsyncHead->HLink = MM_GetPhysAddr(qh)|2;
240 Cont->LastAsyncHead->Impl.Next = qh;
243 Cont->OpRegs->AsyncListAddr = MM_GetPhysAddr(qh)|2;
244 Cont->LastAsyncHead = qh;
248 void *EHCI_InitBulk(void *Ptr, int Endpoint, size_t MaxPacketSize)
250 return EHCI_InitControl(Ptr, Endpoint, MaxPacketSize);
252 void EHCI_RemEndpoint(void *Ptr, void *Handle)
256 else if( (tVAddr)Handle <= 256*16 )
259 // Remove QH from list
260 // - If it's a polling endpoint, need to remove from all periodic lists
263 EHCI_int_DeallocateQH(Ptr, Handle);
267 void *EHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
269 const void *SetupData, size_t SetupLength,
270 const void *OutData, size_t OutLength,
271 void *InData, size_t InLength
274 tEHCI_Controller *Cont = Ptr;
275 tEHCI_qTD *td_setup, *td_data, *td_status;
278 if( (tVAddr)Dest <= 256*16 )
281 // Check size of SETUP and status
284 td_setup = EHCI_int_AllocateTD(Cont, PID_SETUP, (void*)SetupData, SetupLength, NULL, NULL);
287 td_data = EHCI_int_AllocateTD(Cont, PID_OUT, (void*)OutData, OutLength, NULL, NULL);
288 td_status = EHCI_int_AllocateTD(Cont, PID_IN, InData, InLength, Cb, CbData);
292 td_data = EHCI_int_AllocateTD(Cont, PID_IN, InData, InLength, NULL, NULL);
293 td_status = EHCI_int_AllocateTD(Cont, PID_OUT, (void*)OutData, OutLength, Cb, CbData);
297 EHCI_int_AppendTD(Dest, td_setup);
298 EHCI_int_AppendTD(Dest, td_data);
299 EHCI_int_AppendTD(Dest, td_status);
304 void *EHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length)
306 tEHCI_Controller *Cont = Ptr;
308 // Sanity check the pointer
309 // - Can't be NULL or an isoch
310 if( (tVAddr)Dest <= 256*16 )
313 // Allocate single TD
314 tEHCI_qTD *td = EHCI_int_AllocateTD(Cont, (Dir ? PID_OUT : PID_IN), Data, Length, Cb, CbData);
315 EHCI_int_AppendTD(Dest, td);
320 void EHCI_FreeOp(void *Ptr, void *Handle)
322 tEHCI_Controller *Cont = Ptr;
324 EHCI_int_DeallocateTD(Cont, Handle);
327 // --------------------------------------------------------------------
329 // --------------------------------------------------------------------
330 tEHCI_qTD *EHCI_int_AllocateTD(tEHCI_Controller *Cont, int PID, void *Data, size_t Length, tUSBHostCb Cb, void *CbData)
335 void EHCI_int_DeallocateTD(tEHCI_Controller *Cont, tEHCI_qTD *TD)
339 void EHCI_int_AppendTD(tEHCI_QH *QH, tEHCI_qTD *TD)
343 tEHCI_QH *EHCI_int_AllocateQH(tEHCI_Controller *Cont, int Endpoint, size_t MaxPacketSize)
348 void EHCI_int_DeallocateQH(tEHCI_Controller *Cont, tEHCI_QH *QH)