Kernel/USB - Still broken, reworking host controller API to give driver more information
authorJohn Hodge <[email protected]>
Sun, 26 Feb 2012 13:48:20 +0000 (21:48 +0800)
committerJohn Hodge <[email protected]>
Sun, 26 Feb 2012 13:48:20 +0000 (21:48 +0800)
- Moved interrupt polling to host driver
- OHCI not converted yet
- UHCI is buggy, doesn't manage to read descriptors

BuildConf/x86/default.mk
KernelLand/Modules/USB/Core/include/usb_host.h
KernelLand/Modules/USB/Core/usb_io.c
KernelLand/Modules/USB/Core/usb_lowlevel.c
KernelLand/Modules/USB/Core/usb_poll.c
KernelLand/Modules/USB/UHCI/uhci.c
KernelLand/Modules/USB/UHCI/uhci.h

index 11881b0..592d702 100644 (file)
@@ -7,6 +7,7 @@ MODULES += Display/BochsGA
 MODULES += Input/PS2KbMouse
 MODULES += x86/ISADMA x86/VGAText
 
-MODULES += USB/Core USB/UHCI USB/OHCI
+MODULES += USB/Core USB/UHCI
+#USB/OHCI
 MODULES += USB/HID
 #MODULES += Interfaces/UDI
index eda42cf..ab37279 100644 (file)
 
 typedef struct sUSBHostDef     tUSBHostDef;
 
-typedef void   (*tUSBHostCb)(void *DataPtr, void *Data, int Length);
+typedef void   (*tUSBHostCb)(void *DataPtr, void *Data, size_t Length);
 
 typedef void   *(*tUSBHostOp)(void *Ptr, int Dest, int DataTgl, tUSBHostCb CB, void *CbData, void *Data, size_t Length);
+typedef void   *(*tUSBIntOp)(void *Ptr, int Dest, int Period, tUSBHostCb CB, void *CbData, void *Data, size_t Length);
 
 /**
  * \brief Defines a USB Host Controller type
  */
 struct sUSBHostDef
 {
-       tUSBHostOp      SendIN;
-       tUSBHostOp      SendOUT;
-       tUSBHostOp      SendSETUP;
-
-       /**
-        * \brief Check if an operation has completed
-        * \note Only valid to call if CB passed was ERRPTR
-        */
-        int    (*IsOpComplete)(void *Ptr, void *OpPtr);
+       tUSBIntOp       InterruptIN;
+       tUSBIntOp       InterruptOUT;
+       void    (*StopInterrupt)(void *Ptr, void *Handle);
+
+       void    *(*ControlSETUP)(void *Ptr, int Dest, int DataTgl, void *Data, size_t Length);
+       tUSBHostOp      ControlIN;
+       tUSBHostOp      ControlOUT;
+       
+       tUSBHostOp      BulkIN;
+       tUSBHostOp      BulkOUT;
 
        void    (*CheckPorts)(void *Ptr);
 };
index dada0de..745e8a6 100644 (file)
@@ -25,7 +25,7 @@ struct sAsyncOp
 // === PROTOTYPES ===
 void   USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data);
 void   USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data);
-void   USB_AsyncCallback(void *Ptr, void *Buf, int Length);
+void   USB_AsyncCallback(void *Ptr, void *Buf, size_t Length);
 void   USB_AsyncThread(void *unused);
 
 // === GLOBALS ===
@@ -83,7 +83,7 @@ void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf,
 
        host = Dev->Dev->Host;
        LOG("IN from %p %i:%i", host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum);
-       host->HostDef->SendIN(
+       host->HostDef->BulkIN(
                host->Ptr, Dev->Dev->Address*16 + op->Endpt->EndpointNum,
                0, USB_AsyncCallback, op,
                DataBuf, Length
@@ -94,7 +94,7 @@ void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf,
 //     Log_Warning("USB", "TODO: Implement USB_RecvDataA");
 }
 
-void USB_AsyncCallback(void *Ptr, void *Buf, int Length)
+void USB_AsyncCallback(void *Ptr, void *Buf, size_t Length)
 {
        tAsyncOp *op = Ptr;
        op->Length = Length;
index 033be8d..67381c0 100644 (file)
 #include "usb_proto.h"
 #include "usb_lowlevel.h"
 #include <timers.h>
+#include <events.h>
 
 // === PROTOTYPES ===
 void   *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data);
+void   USB_int_WakeThread(void *Thread, void *Data, size_t Length);
  int   USB_int_SendSetupSetAddress(tUSBHost *Host, int Address);
  int   USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest);
 char   *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index);
@@ -26,6 +28,7 @@ void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, in
        // TODO: Sanity check (and check that Type is valid)
        struct sDeviceRequest   req;
         int    dest = Addr * 16 + EndPt;       // TODO: Validate
+       tThread *thisthread = Proc_GetCurThread();
        
        ENTER("pHost xdest iType iReq iVal iIndx iLen pData",
                Host, dest, Type, Req, Val, Indx, Len, Data);
@@ -36,8 +39,10 @@ void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, in
        req.Index = LittleEndian16( Indx );
        req.Length = LittleEndian16( Len );
 
+       Threads_ClearEvent(THREAD_EVENT_SHORTWAIT);
+
        LOG("SETUP");   
-       hdl = Host->HostDef->SendSETUP(Host->Ptr, dest, 0, NULL, NULL, &req, sizeof(req));
+       hdl = Host->HostDef->ControlSETUP(Host->Ptr, dest, 0, &req, sizeof(req));
 
        // TODO: Data toggle?
        // TODO: Multi-packet transfers
@@ -46,34 +51,39 @@ void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, in
                if( Type & 0x80 )
                {
                        LOG("IN");
-                       hdl = Host->HostDef->SendIN(Host->Ptr, dest, 1, NULL, NULL, Data, Len);
+                       hdl = Host->HostDef->ControlIN(Host->Ptr, dest, 1, NULL, NULL, Data, Len);
        
                        LOG("OUT (Status)");
-                       hdl = Host->HostDef->SendOUT(Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0);
+                       hdl = Host->HostDef->ControlOUT(Host->Ptr, dest, 1, USB_int_WakeThread, thisthread, NULL, 0);
                }
                else
                {
                        LOG("OUT");
-                       Host->HostDef->SendOUT(Host->Ptr, dest, 1, NULL, NULL, Data, Len);
+                       Host->HostDef->ControlOUT(Host->Ptr, dest, 1, NULL, NULL, Data, Len);
                        
                        // Status phase (DataToggle=1)
                        LOG("IN (Status)");
-                       hdl = Host->HostDef->SendIN(Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0);
+                       hdl = Host->HostDef->ControlIN(Host->Ptr, dest, 1, USB_int_WakeThread, thisthread, NULL, 0);
                }
        }
        else
        {
                // Zero length, IN status
                LOG("IN (Status)");
-               hdl = Host->HostDef->SendIN(Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0);
+               hdl = Host->HostDef->ControlIN(Host->Ptr, dest, 1, USB_int_WakeThread, thisthread, NULL, 0);
        }
        LOG("Wait...");
-       while( Host->HostDef->IsOpComplete(Host->Ptr, hdl) == 0 )
-               Time_Delay(1);
+       Threads_WaitEvents(THREAD_EVENT_SHORTWAIT);
+
        LEAVE('p', hdl);
        return hdl;
 }
 
+void USB_int_WakeThread(void *Thread, void *Data, size_t Length)
+{
+       Threads_PostEvent(Thread, THREAD_EVENT_SHORTWAIT);
+}
+
 int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address)
 {
        USB_int_Request(Host, 0, 0, 0x00, 5, Address & 0x7F, 0, 0, NULL);
@@ -101,17 +111,13 @@ int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, i
        req.Length = LittleEndian16( Length );
 
        LOG("SETUP");   
-       Dev->Host->HostDef->SendSETUP(
-               Dev->Host->Ptr, dest,
-               0, NULL, NULL,
-               &req, sizeof(req)
-               );
+       Dev->Host->HostDef->ControlSETUP(Dev->Host->Ptr, dest, 0, &req, sizeof(req));
        
        bToggle = 1;
        while( Length > ciMaxPacketSize )
        {
                LOG("IN (%i rem)", Length - ciMaxPacketSize);
-               Dev->Host->HostDef->SendIN(
+               Dev->Host->HostDef->ControlIN(
                        Dev->Host->Ptr, dest,
                        bToggle, NULL, NULL,
                        Dest, ciMaxPacketSize
@@ -121,18 +127,18 @@ int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, i
        }
 
        LOG("IN (final)");
-       Dev->Host->HostDef->SendIN(
-               Dev->Host->Ptr, dest,
-               bToggle, NULL, NULL,
-               Dest, Length
-               );
+       Dev->Host->HostDef->ControlIN( Dev->Host->Ptr, dest, bToggle, NULL, NULL, Dest, Length );
 
+       Threads_ClearEvent(THREAD_EVENT_SHORTWAIT);
        LOG("OUT (Status)");
-       final = Dev->Host->HostDef->SendOUT(Dev->Host->Ptr, dest, 1, INVLPTR, NULL, NULL, 0);
+       final = Dev->Host->HostDef->ControlOUT(
+               Dev->Host->Ptr, dest, 1,
+               USB_int_WakeThread, Proc_GetCurThread(),
+               NULL, 0
+               );
 
        LOG("Waiting");
-       while( Dev->Host->HostDef->IsOpComplete(Dev->Host->Ptr, final) == 0 )
-               Threads_Yield();        // BAD BAD BAD
+       Threads_WaitEvents(THREAD_EVENT_SHORTWAIT);
 
        LEAVE('i', 0);
        return 0;
index 6285a11..d1cbaa9 100644 (file)
@@ -21,10 +21,15 @@ extern tUSBHost     *gUSB_Hosts;
 void   USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint);
 
 // === GLOBALS ===
-tUSBEndpoint   *gUSB_PollQueues[POLL_MAX/POLL_ATOM];
- int   giUSB_PollPosition;     // Index into gUSB_PollQueues
 
 // === CODE ===
+void USB_int_PollCallback(void *Ptr, void *Data, size_t Length)
+{
+       tUSBEndpoint    *ep = Ptr;
+       
+       ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail(ep->Interface, ep->EndpointIdx, Length, Data);
+}
+
 void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint)
 {
        tUSBEndpoint    *endpt;
@@ -44,18 +49,14 @@ void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint)
 
        endpt->InputData = malloc(endpt->MaxPacketSize);
 
-       // Determine polling period in atoms
-       endpt->PollingAtoms = (endpt->PollingPeriod + POLL_ATOM-1) / POLL_ATOM;
-       if(endpt->PollingAtoms > POLL_SLOTS)    endpt->PollingAtoms = POLL_SLOTS;
-       LOG("endpt(%p)->PollingAtoms = %i", endpt, endpt->PollingAtoms);
-       // Add to poll queue
-       // TODO: Locking
-       {
-                int    idx = giUSB_PollPosition + 1;
-               if(idx >= POLL_SLOTS)   idx -= POLL_SLOTS;
-               endpt->Next = gUSB_PollQueues[idx];
-               gUSB_PollQueues[idx] = endpt;
-       }
+       Iface->Dev->Host->HostDef->InterruptIN(
+               Iface->Dev->Host->Ptr,
+               Iface->Dev->Address * 16 + endpt->EndpointNum,
+               endpt->PollingPeriod,
+               USB_int_PollCallback, endpt,
+               endpt->InputData, endpt->MaxPacketSize
+               );
+       LEAVE('-');
 }
 
 /**
@@ -66,74 +67,12 @@ int USB_PollThread(void *unused)
        Threads_SetName("USB Polling Thread");
        for(;;)
        {
-               tUSBEndpoint    *ep, *prev;
-
-               if(giUSB_PollPosition == 0)
+               // Check hosts
+               for( tUSBHost *host = gUSB_Hosts; host; host = host->Next )
                {
-                       // Check hosts
-                       for( tUSBHost *host = gUSB_Hosts; host; host = host->Next )
-                       {
-                               host->HostDef->CheckPorts(host->Ptr);
-                       }
+                       host->HostDef->CheckPorts(host->Ptr);
                }
 
-//             Log_Debug("USBPoll", "giUSB_PollPosition = %i", giUSB_PollPosition);
-
-               // A little evil for neater code
-               prev = (void*)( (tVAddr)&gUSB_PollQueues[giUSB_PollPosition] - offsetof(tUSBEndpoint, Next) );
-
-               // Process queue
-//             LOG("giUSB_PollPosition = %i", giUSB_PollPosition);
-               for( ep = gUSB_PollQueues[giUSB_PollPosition]; ep; prev = ep, ep = ep->Next )
-               {
-                        int    period_in_atoms = ep->PollingAtoms;
-                       LOG("%i: ep = %p", giUSB_PollPosition, ep);
-
-                       // Check for invalid entries
-                       if(period_in_atoms < 0 || period_in_atoms > POLL_ATOM)
-                       {
-                               Log_Warning("USB", "Endpoint on polling queue with invalid period");
-                               continue ;
-                       }
-                       // Check for entries to delete
-                       if(period_in_atoms == 0)
-                       {
-                               // Remove
-                               prev->Next = ep->Next;
-                               ep->PollingAtoms = -1;  // Mark as removed
-                               ep = prev;      // Make sure prev is kept valid
-                               continue ;
-                       }
-
-                       // Read data
-                       // TODO: Check the endpoint
-                       // TODO: Async checking?
-                       // - Send the read request on all of them then wait for the first to complete
-                       USB_RecvDataA(
-                               ep->Interface, ep->EndpointIdx+1,
-                               ep->MaxPacketSize, ep->InputData,
-                               ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail
-                               );
-                               
-                       // Call callback
-
-                       // Reschedule
-                       if( period_in_atoms != POLL_SLOTS )
-                       {
-                                int    newqueue_id = (giUSB_PollPosition + period_in_atoms) % POLL_SLOTS;
-                               tUSBEndpoint    **newqueue = &gUSB_PollQueues[newqueue_id];
-                               
-                               prev->Next = ep->Next;
-                               
-                               ep->Next = *newqueue;
-                               *newqueue = ep;
-                               ep = prev;
-                       }
-               }
-               giUSB_PollPosition ++;
-               if(giUSB_PollPosition == POLL_SLOTS)
-                       giUSB_PollPosition = 0;
-               // TODO: Check for a longer delay
                Time_Delay(POLL_ATOM);
        }
 }
index 48960c2..8e840ab 100644 (file)
 #define        MAX_CONTROLLERS 4
 //#define NUM_TDs      1024
 #define NUM_TDs        64
+#define MAX_PACKET_SIZE        0x400
+#define PID_IN 0x69
+#define PID_OUT        0xE1
+#define PID_SETUP      0x2D
 
 // === PROTOTYPES ===
  int   UHCI_Initialise(char **Arguments);
 void   UHCI_Cleanup();
+ int   UHCI_int_InitHost(tUHCI_Controller *Host);
+// -- List internals
 tUHCI_TD       *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
-void   UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD);
-void   *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void   *UHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
-void   *UHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData,  void *Buf, size_t Length);
-void   *UHCI_SendSetup(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
- int   UHCI_IsTransferComplete(void *Ptr, void *Handle);
- int   UHCI_Int_InitHost(tUHCI_Controller *Host);
+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_CheckPortUpdate(void *Ptr);
 void   UHCI_int_InterruptThread(void *Unused);
 void   UHCI_InterruptHandler(int IRQ, void *Ptr);
@@ -45,10 +56,17 @@ MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL);
 tUHCI_TD       gaUHCI_TDPool[NUM_TDs];
 tUHCI_Controller       gUHCI_Controllers[MAX_CONTROLLERS];
 tUSBHostDef    gUHCI_HostDef = {
-       .SendIN = UHCI_DataIN,
-       .SendOUT = UHCI_DataOUT,
-       .SendSETUP = UHCI_SendSetup,
-       .IsOpComplete = UHCI_IsTransferComplete,
+       .InterruptIN   = UHCI_InterruptIN,
+       .InterruptOUT  = UHCI_InterruptOUT,
+       .StopInterrupt = UHCI_StopInterrupt,
+       
+       .ControlSETUP = UHCI_ControlSETUP,
+       .ControlIN    = UHCI_ControlIN,
+       .ControlOUT   = UHCI_ControlOUT,
+
+       .BulkOUT = UHCI_BulkOUT,
+       .BulkIN = UHCI_BulkIN,
+       
        .CheckPorts = UHCI_CheckPortUpdate
        };
 tSemaphore     gUHCI_InterruptSempahore;
@@ -74,9 +92,6 @@ int UHCI_Initialise(char **Arguments)
                return MODULE_ERR_NOTNEEDED;
        }
        
-       // Spin off interrupt handling thread
-       Proc_SpawnWorker( UHCI_int_InterruptThread, NULL );
-
        // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices
        while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
        {
@@ -104,12 +119,16 @@ int UHCI_Initialise(char **Arguments)
                IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo);
        
                // Initialise Host
-               ret = UHCI_Int_InitHost(&gUHCI_Controllers[i]);
+               ret = UHCI_int_InitHost(cinfo);
                // Detect an error
                if(ret != 0) {
                        LEAVE('i', ret);
                        return ret;
                }
+
+               // Spin off interrupt handling thread
+               Proc_SpawnWorker( UHCI_int_InterruptThread, cinfo );
+
                
                cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
                LOG("cinfo->RootHub = %p", cinfo->RootHub);
@@ -132,6 +151,117 @@ void UHCI_Cleanup()
 {
 }
 
+/**
+ * \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
+// --------------------------------------------------------------------
 tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
 {
        static tMutex   lock;
@@ -143,7 +273,7 @@ tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
                        gaUHCI_TDPool[i].Link = 1;
                        gaUHCI_TDPool[i].Control = (1 << 23);
                        gaUHCI_TDPool[i]._info.bActive = 1;
-                       gaUHCI_TDPool[i]._info.bComplete = 0;
+                       gaUHCI_TDPool[i]._info.period_entry = 0;
                        Mutex_Release( &lock );
                        return &gaUHCI_TDPool[i];
                }
@@ -152,26 +282,12 @@ tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont)
        return NULL;
 }
 
-void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD)
+void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_QH *QH, tUHCI_TD *TD)
 {
        static tMutex   lock;   // TODO: Should I use a shortlock (avoid being preempted)
 
        Mutex_Acquire(&lock);
        
-       #if 0
-        int    next_frame;
-
-       next_frame = (_InWord(Cont, FRNUM) + 2) & (1024-1);
-
-       TD->Control |= (1 << 24);       // Ensure that there is an interrupt for each used frame
-       
-       TD->Link = Cont->FrameList[next_frame];
-       Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD );
-       #else
-
-       // TODO: Support other QHs
-       tUHCI_QH *qh = &Cont->BulkQH;
-       
        // Ensure that there is an interrupt for each used frame
        TD->Control |= (1 << 24);
 
@@ -180,18 +296,21 @@ void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD)
        
        // Add
        TD->Link = 1;
-       if( qh->Child & 1 ) {
-               qh->Child = MM_GetPhysAddr( (tVAddr)TD );
+       if( QH->Child & 1 ) {
+               QH->Child = MM_GetPhysAddr( (tVAddr)TD );
        }
        else {
-               qh->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD );
+               // Depth first
+               QH->_LastItem->Link = MM_GetPhysAddr( (tVAddr)TD ) | 4;
        }
-       qh->_LastItem = TD;
+       QH->_LastItem = TD;
 
        // Reenable controller
        _OutWord( Cont, USBCMD, 0x0001 );
+       
+       // DEBUG!
+       LOG("QH(%p)->Child = %x", QH, QH->Child);
        LOG("TD(%p)->Control = %x", TD, TD->Control);
-       #endif
 
        Mutex_Release(&lock);
 }
@@ -202,7 +321,7 @@ void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD)
  * \param Addr Function Address * 16 + Endpoint
  * \param bTgl Data toggle value
  */
-void *UHCI_int_SendTransaction(
+tUHCI_TD *UHCI_int_CreateTD(
        tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
        tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
 {
@@ -215,9 +334,7 @@ void *UHCI_int_SendTransaction(
        }
 
        td = UHCI_int_AllocateTD(Cont);
-
        if( !td ) {
-               // TODO: Wait for one to free?
                Log_Error("UHCI", "No avaliable TDs, transaction dropped");
                return NULL;
        }
@@ -274,7 +391,6 @@ void *UHCI_int_SendTransaction(
                if( !info )
                        info = calloc( sizeof(tUHCI_ExtraTDInfo), 1 );
                LOG("IOC Cb=%p CbData=%p", Cb, CbData);
-               // NOTE: if ERRPTR then the TD is kept allocated until checked
                info->Callback = Cb;
                info->CallbackPtr = CbData;
        }
@@ -283,112 +399,181 @@ void *UHCI_int_SendTransaction(
                LOG("info = %p", info);
                td->Control |= (1 << 24);
                td->_info.ExtraInfo = info;
-               LOG("TD(%p)->Control = 0x%0x", td, td->Control);
        }
 
-       UHCI_int_AppendTD(Cont, td);
-
        return td;
 }
 
-void *UHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+void UHCI_int_SetInterruptPoll(tUHCI_Controller *Cont, tUHCI_TD *TD, int Period)
 {
-       return UHCI_int_SendTransaction(Ptr, Dest, 0x69, DataTgl, Cb, CbData, Buf, Length);
+       tUHCI_QH        *qh;
+       const int       qh_offsets[] = { 0, 64, 96, 112, 120, 124, 126};
+//     const int       qh_sizes[]   = {64, 32, 16,   8,   4,   2,   1};
+       
+       // Bounds limit
+       if( Period < 0 )        return ;
+       if( Period > 256 )      Period = 256;
+
+       // Get the log base2 of the period
+        int    period_slot = 0;
+       while( Period >>= 1 )   period_slot ++;
+
+       // Adjust for the 4ms minimum period
+       if( period_slot < 2 )   period_slot = 0;
+       else    period_slot -= 2;
+       
+       TD->_info.period_entry = qh_offsets[period_slot] + 1;
+       qh = Cont->TDQHPage->InterruptQHs_4ms + TD->_info.period_entry - 1;
+       // TODO: Find queue with lowest load
+
+       LOG("period_slot = %i, period_entry = %i (+1 when encoded)",
+               period_slot, TD->_info.period_entry);
+
+       UHCI_int_AppendTD(Cont, qh, TD);
 }
 
-void *UHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+void *UHCI_InterruptIN(void *Ptr, int Dest, int Period, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
 {
-       return UHCI_int_SendTransaction(Ptr, Dest, 0xE1, DataTgl, Cb, CbData, Buf, Length);
+       tUHCI_TD        *td;
+
+       if( Period < 0 )        return NULL;
+
+       ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
+               Ptr, Dest, Period, Cb, CbData, Buf, Length);
+
+       // TODO: Data toggle?
+       td = UHCI_int_CreateTD(Ptr, Dest, PID_IN, 0, Cb, CbData, Buf, Length);
+       if( !td )       return NULL;
+       
+       UHCI_int_SetInterruptPoll(Ptr, td, Period);
+       
+       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)
+{
+       tUHCI_TD        *td;
 
-void *UHCI_SendSetup(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
+       if( Period < 0 )        return NULL;
+
+       ENTER("pPtr xDest iPeriod pCb pCbData pBuf, iLength",
+               Ptr, Dest, Period, Cb, CbData, Buf, Length);
+
+       // 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);
+
+       LEAVE('p', td); 
+       return td;
+}
+
+void UHCI_StopInterrupt(void *Ptr, void *Handle)
 {
-       return UHCI_int_SendTransaction(Ptr, Dest, 0x2D, DataTgl, Cb, CbData, Buf, Length);
+       // TODO: Stop interrupt transaction
 }
 
-int UHCI_IsTransferComplete(void *Ptr, void *Handle)
+void *UHCI_ControlSETUP(void *Ptr, int Dest, int Tgl, void *Data, size_t Length)
 {
-       tUHCI_TD        *td = Handle;
-       #if DEBUG
-       tUHCI_Controller        *Cont = &gUHCI_Controllers[0];
-       LOG("%p->Control = 0x%08x", td, td->Control);
-       LOG("USBSTS = 0x%x, USBINTR = 0x%x", _InWord(Cont, USBSTS), _InWord(Cont, USBINTR));
-       LOG("Cont->BulkQH.Child = %x", Cont->BulkQH.Child);
-       #endif
-       if(td->Control & (1 << 23)) {
-               return 0;
-       }
-       LOG("inactive, waiting for completion");
-       if(td->_info.bComplete)
-       {
-               td->_info.bActive = 0;
-               td->_info.bComplete = 0;
-               td->Link = 0;
-               return 1;
-       }
-       else
-       {
-               return 0;
-       }
+       tUHCI_Controller        *Cont = Ptr;
+       tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
+       tUHCI_TD        *td;
+
+       ENTER("pPtr xDest iTgl pData iLength", Ptr, Dest, Tgl, Data, Length);
+       
+       td = UHCI_int_CreateTD(Cont, Dest, PID_SETUP, Tgl, NULL, NULL, Data, Length);
+       UHCI_int_AppendTD(Cont, qh, td);
+
+       LEAVE('p', td); 
+
+       return td;
 }
+void *UHCI_ControlOUT(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;
 
-// === INTERNAL FUNCTIONS ===
-/**
- * \fn int UHCI_Int_InitHost(tUCHI_Controller *Host)
- * \brief Initialises a UHCI host controller
- * \param Host Pointer - Host to initialise
- */
-int UHCI_Int_InitHost(tUHCI_Controller *Host)
+       ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
+
+       td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, Tgl, Cb, CbData, Data, Length);
+       UHCI_int_AppendTD(Cont, qh, td);
+
+       LEAVE('p', td);
+       return td;
+}
+void *UHCI_ControlIN(void *Ptr, int Dest, int Tgl, tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
 {
-       ENTER("pHost", Host);
+       tUHCI_Controller        *Cont = Ptr;
+       tUHCI_QH        *qh = &Cont->TDQHPage->ControlQH;
+       tUHCI_TD        *td;
 
-       _OutWord( Host, USBCMD, 4 );    // GRESET
-       Time_Delay(10);
-       _OutWord( Host, USBCMD, 0 );    // GRESET
+       ENTER("pPtr xDest iTgl pCb pCbData pData iLength", Ptr, Dest, Tgl, Cb, CbData, Data, Length);
        
-       // 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;
+       td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, !!Tgl, Cb, CbData, Data, Length);
+       UHCI_int_AppendTD(Cont, qh, td);
+
+       LEAVE('p', td);
+       return td;
+}
+
+void *UHCI_BulkOUT(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    *src = Buf;
+
+       ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
+
+       while( Length > MAX_PACKET_SIZE )
+       {
+               LOG("MaxPacket (rem = %i)", Length);
+               td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, MAX_PACKET_SIZE);
+               UHCI_int_AppendTD(Cont, qh, td);
+               
+               bToggle = !bToggle;
+               Length -= MAX_PACKET_SIZE;
+               src += MAX_PACKET_SIZE;
        }
 
-       // TODO: Handle QHs not being in a 32-bit paddr range
-       // Need another page, probably get some more TDs from it too
+       LOG("Final");
+       td = UHCI_int_CreateTD(Cont, Dest, PID_OUT, bToggle, NULL, NULL, src, Length);
+       UHCI_int_AppendTD(Cont, qh, td);
 
-       // Set up interrupt and bulk queue
-       Host->InterruptQH.Next = MM_GetPhysAddr( (tVAddr)&Host->ControlQH ) | 2;
-       Host->InterruptQH.Child = 1;
-       Host->ControlQH.Next = MM_GetPhysAddr( (tVAddr)&Host->BulkQH ) | 2;
-       Host->ControlQH.Child = 1;
-       Host->BulkQH.Next = 1;
-       Host->BulkQH.Child = 1;
+       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;
 
-       LOG("Allocated frame list 0x%x (0x%x)", Host->FrameList, Host->PhysFrameList);
-       for( int i = 0; i < 1024; i ++ )
-               Host->FrameList[i] = MM_GetPhysAddr( (tVAddr)&Host->InterruptQH ) | 2;
-       
-       // 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 );
+       ENTER("pPtr xDest ibToggle pCb pCbData pData iLength", Ptr, Dest, bToggle, Cb, CbData, Buf, Length);
+       while( Length > MAX_PACKET_SIZE )
+       {
+               LOG("MaxPacket (rem = %i)", Length);
+               td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, MAX_PACKET_SIZE);
+               UHCI_int_AppendTD(Cont, qh, td);
+               
+               bToggle = !bToggle;
+               Length -= MAX_PACKET_SIZE;
+               dst += MAX_PACKET_SIZE;
+       }
 
-       // Enable processing
-       _OutWord( Host, USBCMD, 0x0001 );
+       LOG("Final");
+       td = UHCI_int_CreateTD(Cont, Dest, PID_IN, bToggle, NULL, NULL, dst, Length);
+       UHCI_int_AppendTD(Cont, qh, td);
 
-       LEAVE('i', 0);
-       return 0;
+       LEAVE('p', td);
+       return td;
 }
 
+// === INTERNAL FUNCTIONS ===
 void UHCI_CheckPortUpdate(void *Ptr)
 {
        tUHCI_Controller        *Host = Ptr;
@@ -428,8 +613,9 @@ void UHCI_CheckPortUpdate(void *Ptr)
        }
 }
 
-void UHCI_int_InterruptThread(void *Unused)
+void UHCI_int_InterruptThread(void *Pointer)
 {
+       tUHCI_Controller        *Cont = Pointer;
        Threads_SetName("UHCI Interrupt Handler");
        for( ;; )
        {
@@ -451,8 +637,6 @@ void UHCI_int_InterruptThread(void *Unused)
                        if( td->_info.bActive == 0 )    continue ;
                        // Skip ones that are still in use
                        if( td->Control & (1 << 23) )   continue ;
-                       // Skip ones that are waiting for ACK
-                       if( td->_info.bComplete == 1 )  continue ;
 
                        // If no callback/alt buffer, mark as free and move on
                        if( td->_info.ExtraInfo )
@@ -460,7 +644,7 @@ void UHCI_int_InterruptThread(void *Unused)
                                // Get size of transfer
                                byte_count = (td->Control & 0x7FF)+1;
                        
-                               // Handle non page-aligned destination (with a > 32-bit paddr)
+                               // Handle non page-aligned destination (or with a > 32-bit paddr)
                                if(info->FirstPage)
                                {
                                        char    *src, *dest;
@@ -489,18 +673,6 @@ void UHCI_int_InterruptThread(void *Unused)
                                        MM_FreeTemp( (tVAddr)dest );
                                }
        
-                               // Don't mark as inactive, the check should do that
-                               if( info->Callback == INVLPTR )
-                               {
-                                       LOG("Marking %p as complete", td);
-                                       td->_info.bComplete = 1;
-                                       free( info );
-                                       td->_info.ExtraInfo = NULL;
-                                       if( td->_info.bFreePointer )
-                                               MM_DerefPhys( td->BufferPointer );                      
-                                       continue ;
-                               }
-
                                // Callback
                                if( info->Callback != NULL )
                                {
@@ -515,6 +687,20 @@ void UHCI_int_InterruptThread(void *Unused)
                                td->_info.ExtraInfo = NULL;
                        }
 
+                       if( td->_info.period_entry > 0 )
+                       {
+                               LOG("Re-schedule interrupt %p (offset %i)", td, td->_info.period_entry-1);
+                               // TODO: Flip toggle?
+                               td->Control |= (1 << 23);
+                               // Add back into controller's interrupt list
+                               UHCI_int_AppendTD(
+                                       Cont,
+                                       Cont->TDQHPage->InterruptQHs_256ms + td->_info.period_entry - 1,
+                                       td
+                                       );
+                               continue ;
+                       }
+
                        if( td->_info.bFreePointer )
                                MM_DerefPhys( td->BufferPointer );                      
        
index 40b589b..db5078e 100644 (file)
@@ -85,7 +85,7 @@ struct sUHCI_TD
        {
                tUHCI_ExtraTDInfo       *ExtraInfo;
                char    bActive;        // Allocated
-               char    bComplete;      // Job complete
+               Uint8   period_entry;   // index + 1, 0 = non-interrupt, 1 = offset 0
                char    bFreePointer;   // Free \a BufferPointer once done
        } _info;
 } __attribute__((aligned(16)));
@@ -164,9 +164,29 @@ struct sUHCI_Controller
 
        tUSBHub *RootHub;
 
-       tUHCI_QH        InterruptQH;
-       tUHCI_QH        ControlQH;
-       tUHCI_QH        BulkQH;
+       /**
+        */
+//      int    FrameLoads[1024];
+
+       tPAddr          PhysTDQHPage;
+       struct
+       {
+               // 127 Interrupt Queue Heads
+               // - 4ms -> 256ms range of periods
+               tUHCI_QH        InterruptQHs_256ms[64];
+               tUHCI_QH        InterruptQHs_128ms[32];
+               tUHCI_QH        InterruptQHs_64ms [16];
+               tUHCI_QH        InterruptQHs_32ms [ 8];
+               tUHCI_QH        InterruptQHs_16ms [ 4];
+               tUHCI_QH        InterruptQHs_8ms  [ 2];
+               tUHCI_QH        InterruptQHs_4ms  [ 1];
+               tUHCI_QH        _padding;
+       
+               tUHCI_QH        ControlQH;
+               tUHCI_QH        BulkQH;
+               
+               tUHCI_TD        LocalTDPool[ (4096-(128+2)*sizeof(tUHCI_QH)) / sizeof(tUHCI_TD) ];
+       }       *TDQHPage;
 };
 
 // === ENUMERATIONS ===

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