+/**
+ * \brief Initialises a UHCI host controller
+ * \param Host Pointer - Host to initialise
+ */
+int UHCI_int_InitHost(tUHCI_Controller *Host)
+{
+ ENTER("pHost", Host);
+
+ // - 1 Page, 32-bit address
+ // - 1 page = 1024 4 byte entries
+ Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList);
+ if( !Host->FrameList ) {
+ Log_Warning("UHCI", "Unable to allocate frame list, aborting");
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
+ if( !Host->TDQHPage ) {
+ // TODO: Clean up
+ Log_Warning("UHCI", "Unable to allocate QH page, aborting");
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ // Fill frame list
+ // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
+ const int dest_offsets[] = {
+ 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,
+ 1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31
+ };
+ for( int i = 0; i < 1024; i ++ ) {
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH );
+ Host->FrameList[i] = addr | 2;
+ }
+ for( int i = 0; i < 64; i ++ ) {
+ int ofs = dest_offsets[ i & (32-1) ] * 2 + (i >= 32);
+ Uint32 addr = Host->PhysTDQHPage + ofs * sizeof(tUHCI_QH);
+ LOG("Slot %i to (%i,%i,%i,%i) ms slots",
+ ofs, 0 + i*4, 256 + i*4, 512 + i*4, 768 + i*4);
+ Host->FrameList[ 0 + i*4] = addr | 2;
+ Host->FrameList[256 + i*4] = addr | 2;
+ Host->FrameList[512 + i*4] = addr | 2;
+ Host->FrameList[768 + i*4] = addr | 2;
+ }
+
+ // Build up interrupt binary tree
+ {
+ tUHCI_QH *dest = Host->TDQHPage->InterruptQHs;
+ Uint32 destphys = Host->PhysTDQHPage;
+
+ // Set up next pointer to index to i/2 in the next step
+ for( int _count = 64; _count > 1; _count /= 2 )
+ {
+ for( int i = 0; i < _count; i ++ ) {
+ dest[i].Next = destphys + (_count + i/2) * sizeof(tUHCI_QH) + 2;
+ dest[i].Child = 1;
+ }
+ dest += _count; destphys += _count * sizeof(tUHCI_QH);
+ }
+ // Skip padding, and move to control QH
+ dest->Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
+ dest->Child = 1;
+ }
+
+ // Set up control and bulk queues
+ Host->TDQHPage->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->BulkQH ) | 2;
+ Host->TDQHPage->ControlQH.Child = 1;
+ Host->TDQHPage->BulkQH.Next = 1;
+ Host->TDQHPage->BulkQH.Child = 1;
+
+ // Global reset
+ _OutWord( Host, USBCMD, 4 );
+ Time_Delay(10);
+ _OutWord( Host, USBCMD, 0 );
+
+ // Allocate Frame List
+ // Set frame length to 1 ms
+ _OutByte( Host, SOFMOD, 64 );
+
+ // Set Frame List
+ _OutDWord( Host, FLBASEADD, Host->PhysFrameList );
+ _OutWord( Host, FRNUM, 0 );
+
+ // Enable Interrupts
+ _OutWord( Host, USBINTR, 0x000F );
+ PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
+
+ // Enable processing
+ _OutWord( Host, USBCMD, 0x0001 );
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+// --------------------------------------------------------------------
+// TDs and QH Allocation/Appending
+// --------------------------------------------------------------------