Modules/EHCI - Bulk/Control transfers working
[tpg/acess2.git] / KernelLand / Modules / USB / UHCI / uhci.c
index e57a1c3..98a4b09 100644 (file)
@@ -35,14 +35,19 @@ tUHCI_TD    *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
 void   UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD);
 tUHCI_TD       *UHCI_int_CreateTD(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
 // --- API
-void   *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void   *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void   UHCI_StopInterrupt(void *Ptr, void *Handle);
-void   *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length);
-void   *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
-void   *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length);
-void   *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void   *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+void   *UHCI_InitInterrupt(void *Ptr, int Endpt, int bOutbound, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len);
+void   *UHCI_InitIsoch(void *Ptr, int Endpt, size_t MaxPacketSize);
+void   *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize);
+void   *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize);
+void   UHCI_RemoveEndpoint(void *Ptr, void *EndptHandle);
+void   *UHCI_SendIsoch(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length, int When);
+void   *UHCI_SendControl(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData,
+       int isOutbound,
+       const void *SetupData, size_t SetupLength,
+       const void *OutData, size_t OutLength,
+       void *InData, size_t InLength
+       );
+void   *UHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length);
 
 void   UHCI_CheckPortUpdate(void *Ptr);
 void   UHCI_int_InterruptThread(void *Unused);
@@ -58,18 +63,22 @@ MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
 tUHCI_TD       *gaUHCI_TDPool;
 tUHCI_Controller       gUHCI_Controllers[MAX_CONTROLLERS];
 tUSBHostDef    gUHCI_HostDef = {
-       .InterruptIN   = UHCI_InterruptIN,
-       .InterruptOUT  = UHCI_InterruptOUT,
-       .StopInterrupt = UHCI_StopInterrupt,
+       .InitInterrupt = UHCI_InitInterrupt,
+//     .InitIsoch     = UHCI_InitIsoch,
+       .InitControl   = UHCI_InitControl,
+       .InitBulk      = UHCI_InitBulk,
+       .RemEndpoint   = UHCI_RemoveEndpoint,
        
-       .ControlSETUP = UHCI_ControlSETUP,
-       .ControlIN    = UHCI_ControlIN,
-       .ControlOUT   = UHCI_ControlOUT,
-
-       .BulkOUT = UHCI_BulkOUT,
-       .BulkIN = UHCI_BulkIN,
+//     .SendIsoch   = UHCI_SendIsoch,
+       .SendControl = UHCI_SendControl,
+       .SendBulk    = UHCI_SendBulk,
+       .FreeOp      = NULL,
        
-       .CheckPorts = UHCI_CheckPortUpdate
+       .CheckPorts = UHCI_CheckPortUpdate,
+//     .ClearPortFeature = NULL,
+//     .GetBusState      = NULL,
+//     .GetPortStatus    = NULL,
+//     .SetPortFeature   = NULL
        };
 tSemaphore     gUHCI_InterruptSempahore;
 
@@ -84,7 +93,14 @@ int UHCI_Initialise(char **Arguments)
         int    ret;
        
        ENTER("");
-       
+
+       if( Arguments && *Arguments && strcmp(*Arguments, "0") == 0 )
+       {
+               LOG("Disabled by argument");
+               LEAVE('i', MODULE_ERR_NOTNEEDED);
+               return MODULE_ERR_NOTNEEDED;
+       }
+
        // Initialise with no maximum value
        Semaphore_Init( &gUHCI_InterruptSempahore, 0, 0, "UHCI", "Interrupt Queue");
 
@@ -98,14 +114,15 @@ int UHCI_Initialise(char **Arguments)
                tPAddr  tmp;    
                gaUHCI_TDPool = (void *) MM_AllocDMA(1, 32, &tmp);
                memset(gaUHCI_TDPool, 0, PAGE_SIZE);
+               LOG("gaUHCI_TDPool = %p (%P)", gaUHCI_TDPool, tmp);
        }
 
        // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
+       // Class:SubClass:Protocol = 0xC (Serial) : 0x3 (USB) : 0x00 (UHCI)
        while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
        {
                tUHCI_Controller        *cinfo = &gUHCI_Controllers[i];
                Uint32  base_addr;
-               // NOTE: Check "protocol" from PCI?
                
                cinfo->PciId = id;
                base_addr = PCI_GetBAR(id, 4);
@@ -175,6 +192,7 @@ int UHCI_int_InitHost(tUHCI_Controller *Host)
                LEAVE('i', -1);
                return -1;
        }
+       LOG("->FrameList = %p (%P)", Host->FrameList, Host->PhysFrameList);
 
        Host->TDQHPage = (void *) MM_AllocDMA(1, 32, &Host->PhysTDQHPage);
        if( !Host->TDQHPage ) {
@@ -183,6 +201,7 @@ int UHCI_int_InitHost(tUHCI_Controller *Host)
                LEAVE('i', -1);
                return -1;
        }
+       LOG("->TDQHPage = %p (%P)", Host->TDQHPage, Host->PhysTDQHPage);
 
        // Fill frame list
        // - The numbers 0...31, but bit reversed (16 (0b1000) = 1 (0b00001)
@@ -190,8 +209,9 @@ int UHCI_int_InitHost(tUHCI_Controller *Host)
                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
                };
+       // Fill all slots (but every 4th will be changed below
        for( int i = 0; i < 1024; i ++ ) {
-               Uint32  addr = MM_GetPhysAddr( (tVAddr)&Host->TDQHPage->ControlQH );
+               Uint32  addr = MM_GetPhysAddr( &Host->TDQHPage->ControlQH );
                Host->FrameList[i] = addr | 2;
        }
        for( int i = 0; i < 64; i ++ ) {
@@ -205,7 +225,7 @@ int UHCI_int_InitHost(tUHCI_Controller *Host)
                Host->FrameList[768 + i*4] = addr | 2;
        }
 
-       // Build up interrupt binary tree       
+       // Build up interrupt binary tree
        {
                tUHCI_QH        *dest = Host->TDQHPage->InterruptQHs;
                Uint32  destphys = Host->PhysTDQHPage;
@@ -213,19 +233,21 @@ int UHCI_int_InitHost(tUHCI_Controller *Host)
                // Set up next pointer to index to i/2 in the next step
                for( int _count = 64; _count > 1; _count /= 2 )
                {
+                       LOG("count=%i, dest=%p, destphys=%P", _count, dest, destphys);
                        for( int i = 0; i < _count; i ++ ) {
+                               LOG(" %i-%i: %P==%P", _count, i, MM_GetPhysAddr(dest+i), destphys+i*sizeof(tUHCI_QH));
                                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->Next = MM_GetPhysAddr( &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.Next = MM_GetPhysAddr( &Host->TDQHPage->BulkQH ) | 2;
        Host->TDQHPage->ControlQH.Child = 1;
        Host->TDQHPage->BulkQH.Next = 1;
        Host->TDQHPage->BulkQH.Child = 1;
@@ -248,6 +270,7 @@ int UHCI_int_InitHost(tUHCI_Controller *Host)
        PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 );
 
        // Enable processing
+       LOG("Processing enabling");
        _OutWord( Host, USBCMD, 0x0001 );
 
        LEAVE('i', 0);
@@ -292,16 +315,18 @@ void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
        TD->Control |= (TD->Token >> 21) & 0x7FF;
 
        // Stop controller
+       tPAddr  tdaddr = MM_GetPhysAddr( TD );
+       ASSERT(tdaddr);
        _OutWord( Cont, USBCMD, 0x0000 );
        
        // Add
        TD->Link = 1;
        if( QH->Child & 1 ) {
-               QH->Child = MM_GetPhysAddr( (tVAddr)TD );
+               QH->Child = tdaddr;
        }
        else {
                // Depth first
-               QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
+               QH->_LastItem->Link = tdaddr | 4;
        }
        QH->_LastItem = TD;
 
@@ -360,7 +385,7 @@ tUHCI_TD *UHCI_int_CreateTD(
        if(
                ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE
        #if PHYS_BITS > 32
-               || MM_GetPhysAddr( (tVAddr)Data ) >> 32
+               || MM_GetPhysAddr( Data ) >> 32
        #endif
                )
        {
@@ -373,8 +398,8 @@ tUHCI_TD *UHCI_int_CreateTD(
                        LOG("Relocated IN");
                        info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
                        info->Offset = ((tVAddr)Data & (PAGE_SIZE-1));
-                       info->FirstPage = MM_GetPhysAddr( (tVAddr)Data );
-                       info->SecondPage = MM_GetPhysAddr( (tVAddr)Data + Length - 1 );
+                       info->FirstPage = MM_GetPhysAddr( Data );
+                       info->SecondPage = MM_GetPhysAddr( (const char *)Data + Length - 1 );
                }
                else
                {
@@ -388,7 +413,7 @@ tUHCI_TD *UHCI_int_CreateTD(
        }
        else
        {
-               td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
+               td->BufferPointer = MM_GetPhysAddr( Data );
                td->_info.bFreePointer = 0;
        }
 
@@ -481,135 +506,234 @@ void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
        UHCI_int_AppendTD(Cont, qh, TD);
 }
 
-void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+// --------------------------------------------------------------------
+// API
+// --------------------------------------------------------------------
+void *UHCI_InitInterrupt(void *Ptr, int Endpt, int bOutbound,
+       int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Len)
 {
        tUHCI_TD        *td;
-
-       if( Period < 0 )        return NULL;
-
-       ENTER("pPtr xDest iPeriod pCb pCbData pBuf iLength",
-               Ptr, Dest, Period, Cb, CbData, Buf, Length);
+       if( Period <= 0 )       return NULL;
+       
+       ENTER("pPtr xEndpt bbOutbound iPeriod pCb pCbData pBuf iLen",
+               Ptr, Endpt, bOutbound, Period, Cb, CbData, Buf, Len);
 
        // TODO: Data toggle?
-       td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length);
+       td = UHCI_int_CreateTD(Ptr, Endpt, (bOutbound ? PID_OUT : PID_IN), 0, Cb, CbData, Buf, Len);
        if( !td )       return NULL;
        
        UHCI_int_SetInterruptPoll(Ptr, td, Period);
-       
-       LEAVE('p', td); 
+
+       LEAVE('p', td);
        return td;
 }
-// TODO: Does interrupt OUT make sense?
-void *UHCI_InterruptOUT(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+
+void *UHCI_int_InitEndpt(tUHCI_Controller *Cont, int Type, int Endpt, size_t MaxPacketSize)
 {
-       tUHCI_TD        *td;
+       if( Endpt >= 256*16 )
+               return NULL;    
 
-       if( Period < 0 )        return NULL;
+       if( MaxPacketSize > MAX_PACKET_SIZE) {
+               Log_Warning("UHCI", "MaxPacketSize for %x greater than controller max (%i > %i)",
+                       Endpt, MaxPacketSize, MAX_PACKET_SIZE);
+               return NULL;
+       }
 
-       ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
-               Ptr, Dest, Period, Cb, CbData, Buf, Length);
+       if( Cont->DevInfo[Endpt / 16] == NULL ) {
+               Cont->DevInfo[Endpt / 16] = calloc( 1, sizeof(*Cont->DevInfo[0]) );
+       }
+       tUHCI_EndpointInfo *epi = &Cont->DevInfo[Endpt/16]->EndpointInfo[Endpt%16];
+       if( epi->Type ) {
+               // oops, in use
+               Log_Warning("UHCI", "Endpoint %x reused?", Endpt);
+               return NULL;
+       }
 
-       // TODO: Data toggle?
-       td = UHCI_int_CreateTD(Ptr, Dest, PID_OUT, 0, Cb, CbData, Buf, Length);
-       if( !td )       return NULL;
-       
-       UHCI_int_SetInterruptPoll(Ptr, td, Period);
+       epi->MaxPacketSize = MaxPacketSize;
+       epi->Type = Type;
+       epi->Tgl = 0;
+
+       return (void*)(tVAddr)(Endpt+1);
 
-       LEAVE('p', td); 
-       return td;
 }
 
-void UHCI_StopInterrupt(void *Ptr, void *Handle)
+void *UHCI_InitControl(void *Ptr, int Endpt, size_t MaxPacketSize)
 {
-       // TODO: Stop interrupt transaction
-       Log_Error("UHCI", "TODO: Implement UHCI_StopInterrupt");
+       return UHCI_int_InitEndpt(Ptr, 1, Endpt, MaxPacketSize);
 }
 
-void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
+void *UHCI_InitBulk(void *Ptr, int Endpt, size_t MaxPacketSize)
 {
-       tUHCI_Controller        *Cont = Ptr;
-       tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
-       tUHCI_TD        *td;
+       return UHCI_int_InitEndpt(Ptr, 2, Endpt, MaxPacketSize);
+}
 
-       ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
+void UHCI_RemoveEndpoint(void *Ptr, void *Handle)
+{
+       tUHCI_Controller *Cont = Ptr;
+       if( Handle == NULL )
+               return ;
        
-       td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
-       UHCI_int_AppendTD(Cont, qh, td);
-
-       LEAVE('p', td); 
-
-       return td;
+       if( (tVAddr)Handle <= 256*16 ) {
+                int    addr = (tVAddr)Handle;
+               Cont->DevInfo[addr/16]->EndpointInfo[addr%16].Type = 0;
+       }
+       else {
+               // TODO: Stop interrupt transaction
+               Log_Error("UHCI", "TODO: Implement stopping interrupt polling");
+       }
 }
-void *UHCI_ControlOUT(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
+
+void *UHCI_SendControl(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData,
+       int bOutbound,  // (1) SETUP, OUT, IN vs (0) SETUP, IN, OUT
+       const void *SetupData, size_t SetupLength,
+       const void *OutData, size_t OutLength,
+       void *InData, size_t InLength
+       )
 {
+       ENTER("pPtr pEndpt bOutbound", Ptr, Endpt, bOutbound);
+       
        tUHCI_Controller        *Cont = Ptr;
        tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
        tUHCI_TD        *td;
+       tUHCI_EndpointInfo *epi;
+        int    dest, tgl;
+       size_t  mps;
 
-       ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
+       if( Endpt == NULL ) {
+               Log_Error("UHCI", "Passed a NULL Endpoint handle");
+               LEAVE('n');
+               return NULL;
+       }
 
-       td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
-       UHCI_int_AppendTD(Cont, qh, td);
+       // Sanity check Endpt
+       if( (tVAddr)Endpt > 0x800 ) {
+               LEAVE('n');
+               return NULL;
+       }
+       dest = (tVAddr)Endpt - 1;
+       if( Cont->DevInfo[dest/16] == NULL )    LEAVE_RET('n', NULL);
+       epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
+       if( epi->Type != 1 )    LEAVE_RET('n', NULL);
+       mps = epi->MaxPacketSize;
+       tgl = epi->Tgl;
+
+       // TODO: Build up list and then append to QH in one operation
+
+       char    *data_ptr, *status_ptr;
+       size_t  data_len,   status_len;
+       Uint8   data_pid,   status_pid;
+       
+       if( bOutbound ) {
+               data_pid   = PID_OUT; data_ptr   = (void*)OutData; data_len   = OutLength;
+               status_pid = PID_IN;  status_ptr = InData;  status_len = InLength;
+       }
+       else {
+               data_pid   = PID_IN;  data_ptr   = InData;  data_len   = InLength;
+               status_pid = PID_OUT; status_ptr = (void*)OutData; status_len = OutLength;
+       }
 
-       LEAVE('p', td);
-       return td;
-}
-void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
-{
-       tUHCI_Controller        *Cont = Ptr;
-       tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
-       tUHCI_TD        *td;
+       // Sanity check data lengths
+       if( SetupLength > mps ) LEAVE_RET('n', NULL);
+       if( status_len > mps )  LEAVE_RET('n', NULL);
+
+       // Create and append SETUP packet
+       td = UHCI_int_CreateTD(Cont, dest, PID_SETUP, tgl, NULL, NULL, (void*)SetupData, SetupLength);
+       UHCI_int_AppendTD(Cont, qh, td);
+       tgl = !tgl;
 
-       ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
+       // Send data packets
+       while( data_len > 0 )
+       {
+               size_t len = MIN(data_len, mps);
+               td = UHCI_int_CreateTD(Cont, dest, data_pid, tgl, NULL, NULL, data_ptr, len);
+               UHCI_int_AppendTD(Cont, qh, td);
+               tgl = !tgl;
+               
+               data_ptr += len;
+               data_len -= len;
+       }
        
-       td = UHCI_int_CreateTD(Cont, Dest, PID_IN, !!Tgl, Cb, CbData, Data, Length);
+       // Send status
+       td = UHCI_int_CreateTD(Cont, dest, status_pid, tgl, Cb, CbData, status_ptr, status_len);
        UHCI_int_AppendTD(Cont, qh, td);
+       tgl = !tgl;
+       
+       // Update toggle value
+       epi->Tgl = tgl;
 
-       LEAVE('p', td);
+       // --- HACK!!!
+//     for( int i = 0; i < 1024; i ++ )
+//     {
+//             LOG("- FrameList[%i] = %x", i, Cont->FrameList[i]);
+//     }
+       // --- /HACK    
+
+       LEAVE('p', td); 
        return td;
 }
 
-void *UHCI_BulkOUT(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+void *UHCI_SendBulk(void *Ptr, void *Endpt, tUSBHostCb Cb, void *CbData, int bOutbound, void *Data, size_t Length)
 {
        tUHCI_Controller        *Cont = Ptr;
        tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
-       tUHCI_TD        *td;
-       char    *src = Buf;
+       tUHCI_TD        *td = NULL;
+       tUHCI_EndpointInfo *epi;
+        int    dest, tgl;
+       size_t  mps;
 
-       ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
+       ENTER("pPtr pEndpt pCb pCbData bOutbound pData iLength", Ptr, Endpt, Cb, CbData, bOutbound, Data, Length);
 
-       if( Length > MAX_PACKET_SIZE ) {
-               Log_Error("UHCI", "Passed an oversized packet by the USB code (%i > %i)", Length, MAX_PACKET_SIZE);
+       if( Endpt == NULL ) {
+               Log_Error("UHCI", "_SendBulk passed a NULL endpoint handle");
                LEAVE('n');
+               return NULL;
        }
-       
-       td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, Cb, CbData, src, Length);
-       UHCI_int_AppendTD(Cont, qh, td);
-
-       LEAVE('p', td);
-       return td;
-}
-void *UHCI_BulkIN(void *Ptr, int Dest, int bToggle, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
-{
-       tUHCI_Controller        *Cont = Ptr;
-       tUHCI_QH        *qh = &Cont->TDQHPage->BulkQH;
-       tUHCI_TD        *td;
-       char    *dst = Buf;
 
-       ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
-       if( Length > MAX_PACKET_SIZE ) {
-               Log_Error("UHCI", "Passed an oversized packet by the USB code (%i > %i)", Length, MAX_PACKET_SIZE);
+       // Sanity check Endpt
+       if( (tVAddr)Endpt > 256*16 ) {
+               Log_Error("UHCI", "_SendBulk passed an interrupt endpoint handle");
                LEAVE('n');
+               return NULL;
+       }
+       dest = (tVAddr)Endpt - 1;
+       if( Cont->DevInfo[dest/16] == NULL ) {
+               Log_Error("UHCI", "_SendBulk passed an uninitialised handle");
+               LEAVE('n');
+               return NULL;
+       }
+       epi = &Cont->DevInfo[dest/16]->EndpointInfo[dest%16];
+       if( epi->Type != 2 ) {
+               Log_Error("UHCI", "_SendBulk passed an invalid endpoint type (%i!=2)", epi->Type);
+               LEAVE('n');
+               return NULL;
        }
+       tgl = epi->Tgl;
+       mps = epi->MaxPacketSize;
 
-       td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, Cb, CbData, dst, Length);
-       UHCI_int_AppendTD(Cont, qh, td);
+       Uint8   pid = (bOutbound ? PID_OUT : PID_IN);
+
+       char *pos = Data;
+       while( Length > 0 )
+       {
+               size_t len = MIN(mps, Length);
+
+               td = UHCI_int_CreateTD(Cont, dest, pid, tgl, Cb, (len == Length ? CbData : NULL), pos, len);
+               UHCI_int_AppendTD(Cont, qh, td);
+               
+               pos += len;
+               Length -= len;
+               tgl = !tgl;
+       }
+       
+       epi->Tgl = tgl;
 
        LEAVE('p', td);
        return td;
 }
 
+// ==========================
 // === INTERNAL FUNCTIONS ===
+// ==========================
 void UHCI_CheckPortUpdate(void *Ptr)
 {
        tUHCI_Controller        *Host = Ptr;
@@ -661,7 +785,7 @@ tUHCI_TD *UHCI_int_GetTDFromPhys(tUHCI_Controller *Controller, Uint32 PAddr)
        }
 
        
-       tPAddr  global_pool = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
+       tPAddr  global_pool = MM_GetPhysAddr( gaUHCI_TDPool );
        
        if( PAddr < global_pool || PAddr >= global_pool + PAGE_SIZE )   return NULL;
        
@@ -699,7 +823,8 @@ void UHCI_int_CleanQH(tUHCI_Controller *Cont, tUHCI_QH *QH)
                        continue ;
                }
 
-               LOG("Removed %p from QH %p", td, QH);           
+               LOG("Removed %p from QH %p", td, QH);
+               ASSERT(td->Link);
 
                if( !prev )
                        QH->Child = td->Link;
@@ -867,26 +992,38 @@ void UHCI_InterruptHandler(int IRQ, void *Ptr)
        // USB Error Interrupt
        if( status & 2 )
        {
-               
+               Log_Notice("UHCI", "USB Error");
        }
 
        // Resume Detect
        // - Fired if in suspend state and a USB device sends the RESUME signal
        if( status & 4 )
        {
-               
+               Log_Notice("UHCI", "Resume Detect");
        }
 
        // Host System Error
        if( status & 8 )
        {
-               
+               Log_Notice("UHCI", "Host System Error");
        }
 
        // Host Controller Process Error
        if( status & 0x10 )
        {
                Log_Error("UHCI", "Host controller process error on controller %p", Ptr);
+               // Spam Tree
+               //for( int i = 0; i < 1024; i += 4 ) {
+               //      LOG("%4i: %x", i, Host->FrameList[i]);
+               //}
+               
+               tPAddr  phys = Host->TDQHPage->ControlQH.Child;
+               while( !(phys & 1) && MM_GetRefCount(phys & ~15))
+               {
+                       tUHCI_TD *td = UHCI_int_GetTDFromPhys(Host, phys);
+                       LOG("%08P: %08x %08x %08x", phys, td->Control, td->Token, td->BufferPointer);
+                       phys = td->Link;
+               }
        }
 
        _OutWord(Host, USBSTS, status);

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