+/**
+ * \brief Initialises a UHCI host controller
+ * \param Host Pointer - Host to initialise
+ */
+int UHCI_int_InitHost(tUHCI_Controller *Host)
+{
+ ENTER("pHost", Host);
+
+ _OutWord( Host, USBCMD, 4 ); // GRESET
+ Time_Delay(10);
+ _OutWord( Host, USBCMD, 0 ); // GRESET
+
+ // Allocate Frame List
+ // - 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) ];
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_256ms[ofs] );
+ 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;
+ }
+ for( int i = 0; i < 32; i ++ ) {
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_128ms[i] );
+ Host->TDQHPage->InterruptQHs_256ms[i*2 ].Next = addr | 2;
+ Host->TDQHPage->InterruptQHs_256ms[i*2+1].Next = addr | 2;
+ }
+ for( int i = 0; i < 16; i ++ ) {
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_64ms[i] );
+ Host->TDQHPage->InterruptQHs_128ms[i*2 ].Next = addr | 2;
+ Host->TDQHPage->InterruptQHs_128ms[i*2+1].Next = addr | 2;
+ }
+ for( int i = 0; i < 8; i ++ ) {
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_32ms[i] );
+ Host->TDQHPage->InterruptQHs_64ms[i*2 ].Next = addr | 2;
+ Host->TDQHPage->InterruptQHs_64ms[i*2+1].Next = addr | 2;
+ }
+ for( int i = 0; i < 4; i ++ ) {
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_16ms[i] );
+ Host->TDQHPage->InterruptQHs_32ms[i*2 ].Next = addr | 2;
+ Host->TDQHPage->InterruptQHs_32ms[i*2+1].Next = addr | 2;
+ }
+ for( int i = 0; i < 2; i ++ ) {
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_8ms[i] );
+ Host->TDQHPage->InterruptQHs_16ms[i*2 ].Next = addr | 2;
+ Host->TDQHPage->InterruptQHs_16ms[i*2+1].Next = addr | 2;
+ }
+ for( int i = 0; i < 1; i ++ ) {
+ Uint32 addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->InterruptQHs_4ms[i] );
+ Host->TDQHPage->InterruptQHs_8ms[i*2 ].Next = addr | 2;
+ Host->TDQHPage->InterruptQHs_8ms[i*2+1].Next = addr | 2;
+ }
+ Host->TDQHPage->InterruptQHs_4ms[0].Next = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH ) | 2;
+ // Set child pointers
+ for( int i = 0; i < 127; i ++ ) {
+ Host->TDQHPage->InterruptQHs_256ms[i].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;
+
+ // 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
+// --------------------------------------------------------------------