Revision:
1:4461071ed964
Parent:
0:63d45df56584
--- a/UsbHostController.cpp	Sun Jul 08 20:18:58 2012 +0000
+++ b/UsbHostController.cpp	Wed Sep 19 16:39:23 2012 +0000
@@ -39,10 +39,10 @@
  */
 #define MAX_DEVICES 8
 
-//Do #if 0 to enable debug log on the host controller driver.
 #if 0
-#undef DEBUG
-#define  DEBUG(...)       do {} while(0)
+#define DEBUGV DEBUG
+#else
+#define  DEBUGV(...)       do {} while(0)
 #endif
 
 namespace USB
@@ -56,7 +56,8 @@
 /**
  * Flash a light so I get too see stuff changing.
  */
-static DigitalOut irqTick(LED2);
+static DigitalOut irqTick(LED3);
+static DigitalOut errorLight(LED4);
 
 
 /*
@@ -77,19 +78,8 @@
  */
 extern "C" void USB_IRQHandler(void) __irq;
 void USB_IRQHandler (void) __irq
-{
-    // It is our interrupt, prevent HC from doing it to us again until we are finished
-    LPC_USB->HcInterruptDisable = MasterInterruptEnable;
-
-    //Writing back the interupts we have handled tells the system we did them.
-    //So for now say I done them all. ;)
-    //If we don't do this the system locks up.
-    //I guess because once we renable the interupts they refire so the main thread never gets a chance to runs again.
-    //It is done via the return value to stop the compiler from optimising it out, which some may do if you are not carful.   
-    LPC_USB->HcInterruptStatus = _controller.USBInterupt(LPC_USB->HcInterruptStatus); //Process the interupt.
-    
-    // It is our interrupt, prevent HC from doing it to us again until we are finished
-    LPC_USB->HcInterruptEnable = MasterInterruptEnable;
+{    
+    _controller.USBInterupt(); //Process the interupt.
 }
 
 /*
@@ -97,7 +87,7 @@
  */
 static void QueueRootHubStatusCheckCallback()
 {
-    DEBUG("Resetting hub port after connect");
+    DEBUGV("Resetting hub port after connect");
     //Reset the port ready for using the connected device.
     //ROOTHUB_PORT_RESET_STATUS_CHANGE bit will be set when done.
     LPC_USB->HcRhPortStatus1 = ROOTHUB_PORT_RESET_STATUS;
@@ -115,27 +105,57 @@
 /*
  * Process the interupt that just came in.
  */
-uint32_t HostController::USBInterupt(const uint32_t interruptStatus)
+uint32_t HostController::USBInterupt()
 {
+    // It is our interrupt, prevent HC from doing it to us again until we are finished
+    LPC_USB->HcInterruptDisable = MasterInterruptEnable;
+    
+    //Other systems seem to grab the head and status fields, re enable interupts and then process whilst still in the handler, not sure why.
+    TransferDescriptor *doneHead = _controller.commsArea->doneHead;
+    _controller.commsArea->doneHead = 0;
+
+
+    //Writing back the interupts we have handled tells the system we did them.
+    //So for now say I done them all. ;)
+    //If we don't do this the system locks up.
+    //I guess because once we renable the interupts they refire so the main thread never gets a chance to runs again.
+    //It is done via the return value to stop the compiler from optimising it out, which some may do if you are not carful.
+    const uint32_t interruptStatus = LPC_USB->HcInterruptStatus;
+    LPC_USB->HcInterruptStatus = interruptStatus;
+    
+    // It is our interrupt, prevent HC from doing it to us again until we are finished
+    LPC_USB->HcInterruptEnable = MasterInterruptEnable;
+    
     uint32_t servicedInterrupt = 0;
     if( interruptStatus & INTERRUPT_UNRECOVERABLE_ERROR )
     {
-        DEBUG("USB Controller has failed, please reset.");
+        flags.unrecoverableError = 1;
+        errorLight = 1;
         servicedInterrupt |= INTERRUPT_UNRECOVERABLE_ERROR;
     }
     
     //Have we recived and data?
     if( interruptStatus & INTERRUPT_WRITEBACK_HEAD_DONE )
     {//Process all the recived packets.
-        TransferDescriptor *transfer = commsArea->doneHead;
-        commsArea->doneHead = 0;
-        while( transfer != NULL )
+        while( doneHead != NULL )
         {
-            messageCallback->TransferDone(transfer);
-            TransferDescriptor *next = (TransferDescriptor *)transfer->nextTD;
-            FreeMemoryPoolItem(transfer);
-            transfer = next;
-            DEBUG("TransferDescriptor freed");
+//            DEBUG("TD 0x%08x done\n",(int)doneHead);
+            
+            //Get next before we start processing so the code can reuse the nextTD pointer.
+            TransferDescriptor *next = doneHead->nextTD;
+            
+            //Tell driver transfer is done.
+            messageCallback->TransferDone(doneHead);
+            
+            //If not zero then the call to messageCallback->TransferDone will queue the transfer for processing in the update phase.
+            //The update call will then free the pool item.
+            if( doneHead->transferCallback == NULL )
+            {
+                FreeMemoryPoolItem(doneHead);
+                DEBUGV("TransferDescriptor freed");
+            }
+            //Move to next.
+            doneHead = next;        
         }
     
         //All done re enable the interupt.
@@ -149,14 +169,14 @@
         {
             if(hubPortStatus & ROOTHUB_CURRENT_CONNECT_STATUS)
             {
-                DEBUG("Resetting hub port after connect");
+                DEBUGV("Resetting hub port after connect");
                 //Reset the port ready for using the connected device.
                 //ROOTHUB_PORT_RESET_STATUS_CHANGE bit will be set when done.
                 delayedRootHubStatusChangeTimeout.attach(QueueRootHubStatusCheckCallback,0.5f);
             }
             else
             {
-                DEBUG("Disconnected");
+                DEBUGV("Disconnected");
             }
         }
         else if( (hubPortStatus&(ROOTHUB_PORT_RESET_STATUS_CHANGE|ROOTHUB_PORT_RESET_STATUS)) == ROOTHUB_PORT_RESET_STATUS_CHANGE )
@@ -170,7 +190,7 @@
     
     if( interruptStatus & INTERRUPT_FRAME_NUMBER_OVERFLOW )
     {
-        DEBUG("FRAME_NUMBER_OVERFLOW");
+        DEBUGV("FRAME_NUMBER_OVERFLOW");
         servicedInterrupt |= INTERRUPT_FRAME_NUMBER_OVERFLOW;
     }
 
@@ -186,6 +206,7 @@
 {
     this->messageCallback = messageCallback;
 
+    errorLight = 0;
     commsArea = (HostControllerCommunicationsArea*)HOST_CONTROLLER_COMMUNICATIONS_AREA_ADDRESS;
     
     DEBUG("Starting USB Host Controller Driver.");
@@ -270,10 +291,16 @@
     irqTick = 0;//Clear this, may have been left on after an interupt.
     if( flags.newDeviceConnected )
     {
-        DEBUG("AddDevice START");
+        flags.newDeviceConnected = 0;
+        DEBUGV("AddDevice START");
         messageCallback->AddDevice(0,1,hubPortStatus);//Root hub is always hub zero, port 1. So add the device at this location.
-        DEBUG("AddDevice DONE");
-        flags.newDeviceConnected = 0;
+        DEBUGV("AddDevice DONE");
+    }
+    
+    if( flags.unrecoverableError )
+    {
+        DEBUG("USB Controller has failed, please reset.");
+        flags.unrecoverableError = 0;
     }
 }
 
@@ -286,13 +313,7 @@
     LinkedListItem* f = memoryPoolHead;
     memoryPoolHead = memoryPoolHead->next;
     numAllocatedPoolItems++;
-    DEBUG("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
-    
-    //Clear it.
-    ((uint32_t*)f)[0] = 0;
-    ((uint32_t*)f)[1] = 0;
-    ((uint32_t*)f)[2] = 0;
-    ((uint32_t*)f)[3] = 0;
+    DEBUGV("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
     
     return (void*)f;
 }
@@ -304,7 +325,7 @@
     f->next = memoryPoolHead;
     memoryPoolHead = f;
     numAllocatedPoolItems--;
-    DEBUG("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
+    DEBUGV("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
 }
 
 /**
@@ -313,19 +334,19 @@
  * If the endpoint has need been used before it will not be linked into the list on the device. In that case we allocate it and add it.
  * When the device is removed all the enpoints are removed from the system.
  */
-EndpointDescriptor *HostController::GetEndpointDescriptor(EndpointDescriptor** headED,uint32_t functionAddress,int endpointNumber,int maximumPacketSize,int lowspeed)
+EndpointDescriptor *HostController::GetEndpointDescriptor(EndpointDescriptor** headEP,uint32_t functionAddress,int endpointNumber,int maximumPacketSize,int lowspeed)
 {
     uint32_t control = (maximumPacketSize<<16) | (lowspeed<<13) | (endpointNumber<<7) | functionAddress;
     
     //have we already allocated and linked in the end point?
-    EndpointDescriptor* ep = *headED;
+    EndpointDescriptor* ep = *headEP;    
     while( ep != NULL )
     {
         if( ep->control == control )
         {
             return ep;
         }
-        ep = ep->NextED;
+        ep = ep->NextEP;
     }
 
     //Get here and we have not see the endpoint before.
@@ -334,69 +355,105 @@
     
     if( ep == NULL )
     {
-        DEBUG("AllocateEndpoint failed");
+        DEBUGV("AllocateEndpoint failed");
         return NULL;
     }
     
     //Link the endpoint onto the list of things to do the next frame when ControlListFilled of HcCommandStatus is set.    
     //When the host controller reaches the end of the list pointed to by HcControlCurrentED
     //It checks the ControlListEnable flag, if set then HcControlHeadED is copied to HcControlCurrentED and starts the new transfers.
-    ep->NextED = *headED;
-    *headED = ep;
+    ep->NextEP = *headEP;
+    *headEP = ep;
 
     ep->control = control;//Set the address etc...
-
-    DEBUG("endpoint allocated ep(%d) deviceID(%d)",endpointNumber,functionAddress);
+    ep->TailTD = 0;
+    ep->HeadTD = 0;
+    ep->C = 0;
+    ep->H = 0;
+    ep->ZERO = 0;
+    
+    DEBUGV("endpoint allocated ep(%d) deviceID(%d)",endpointNumber,functionAddress);
 
     return ep;
 }
 
 
-TransferDescriptor *HostController::AllocateTransferDescriptor(int deviceID,int direction,int dataToggle,const uint8_t* data,int dataLength)
+TransferDescriptor *HostController::AllocateTransferDescriptor(int deviceID,int direction,const uint8_t* data,int dataLength)
 {
     TransferDescriptor* td = (TransferDescriptor*)AllocateMemoryPoolItem();
     
     td->deviceID = (uint8_t)deviceID;//So we know who sent this transfer.
     td->bufferRounding = 1;//No rounding needed
     td->direction = direction;
-    td->dataToggle = dataToggle;//DATA0 and use this field for the toggle value. MSb states use this filed, LSb is zero as it's DATA0
+    td->dataToggle = 0;
     td->conditionCode = 15;
     if( data != NULL )
     {
         td->CurrentBufferPointer = data;
         td->bufferEnd = (data + dataLength - 1);//Points to the last byte.
     }
-    DEBUG("transfer desc allocated %08x",*((uint32_t*)td));
+    DEBUGV("transfer desc allocated %08x",*((uint32_t*)td));
     return td;
 }
 
-
-
-
-
-void HostController::QueueControlTransferStage(int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,int dataToggle,const uint8_t* data,int dataLength)
+void HostController::QueueControlTransfer(int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,int dataToggle,const uint8_t* data,int dataLength)
 {
-    //If endpoint zero then toggle is always DATA0 for setup packet and DATA1 for the rest.
+    TransferDescriptor* td = QueueTransfer((EndpointDescriptor**)&LPC_USB->HcControlHeadED,deviceID,direction,endpointNumber,maximumPacketSize,lowspeed,data,dataLength);
+    
+   //If endpoint zero then toggle is always DATA0 for setup packet and DATA1 for the rest.
     if( endpointNumber == 0 )
     {
         dataToggle = direction == TDD_SETUP ? TOGGLE_DATA0 : TOGGLE_DATA1;
     }
+    td->dataToggle = dataToggle;//DATA0 and use this field for the toggle value. MSb states use this filed, LSb is zero as it's DATA0
+    td->transferType = TT_CONTROL;//For the driver.
+    td->transferCallback = NULL;
+    LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | ControlListFilled;//Tell the host of new things to do.
+    LPC_USB->HcControl = LPC_USB->HcControl | ControlListEnable;//Make sure list processing is on.
+} 
 
-    TransferDescriptor* td = AllocateTransferDescriptor(deviceID,direction,dataToggle,data,dataLength);
+void HostController::QueueBulkTransfer(int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,TransferCallback* callback,const uint8_t* data,int dataLength)
+{
+    TransferDescriptor* td = QueueTransfer((EndpointDescriptor**)&LPC_USB->HcBulkHeadED,deviceID,direction,endpointNumber,maximumPacketSize,lowspeed,data,dataLength);
+    DEBUGV("QueueBulkTransfer (0x%08x)",td);
+    
+    //For transfers that need a callback.
+    //If callbackID is non zero then when the transfer is done a callback is generated.
+    td->transferCallback = callback;
+    td->data = data;
+    td->length = dataLength;
+    td->transferType = TT_BULK;//For the driver.
+
+    LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | BulkListFilled;//Tell the host of new things to do.
+    LPC_USB->HcControl = LPC_USB->HcControl | BulkListEnable;//Make sure list processing is on.
+}
+
+TransferDescriptor* HostController::QueueTransfer(EndpointDescriptor** headED,int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,const uint8_t* data,int dataLength)
+{
+    //Additions into transfer queues are always done by copying the new entry information to the entry
+    //at the tail of the queue and then appending a new tail entry to the queue. This is accomplished
+    //by:
+    //  1. Copying the new information to the entry pointed to by TailP
+    //  2. Setting the NextTD pointer in the current tail entry to a new place holder
+    //  3. Advancing the TailP pointer to the new place holder
+    //  4. Writing to the ControlListFilled or BulkListFilled bit in HcCommandStatus if the
+    //      insert was to a queue on the Control list or Bulk list.
+
+    TransferDescriptor* td = AllocateTransferDescriptor(deviceID,direction,data,dataLength);
+    td->ep = endpointNumber;
     
     //Get the endpoint needed for this transfer.
-    EndpointDescriptor* ep = GetEndpointDescriptor(((EndpointDescriptor**)&LPC_USB->HcControlHeadED),deviceID,endpointNumber,maximumPacketSize,lowspeed);
+    EndpointDescriptor* ep = GetEndpointDescriptor(headED,deviceID,endpointNumber,maximumPacketSize,lowspeed);
     
-    td->nextTD = &commsArea->commonTail;
-    ep->TailTD = &commsArea->commonTail;
-    ep->HeadTD = td;
-    ep->NextED = NULL;
+    //Can only queue one at a time, I need to fix.    
+    ep->HeadTD = ((int)td)>>4;
+    ep->TailTD = 0;
+    td->nextTD = 0;
+    
+    return td;
+}
 
 
-    LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | ControlListFilled;//Tell the host of new things to do.
-    LPC_USB->HcControl = LPC_USB->HcControl | ControlListEnable;//Make sure list processing is on.
-}
-
 uint8_t* HostController::getScratchRam()
 {
     return commsArea->scratchRam;
@@ -419,18 +476,18 @@
         if( ( (*(list))->control&0x7f) == deviceID )
         {
             EndpointDescriptor* old = *list;
-            *list = old->NextED;//Unlink old.
-            //Should not be any transfers to do this this EP if we are removing it...... TODO Check this!
-            if( old->HeadTD != &commsArea->commonTail )
+            *list = old->NextEP;//Unlink old.
+            //Should not be any transfers to do this EP if we are removing it...... TODO Check this!
+            if( old->HeadTD != 0 )
             {
-                DEBUG("Removing endpoint with unfinished transfers, please fix bug richard!");
+                DEBUGV("Removing endpoint with unfinished transfers, please fix bug richard!");
             }
-            DEBUG("Freed EP for device(%d)",deviceID);
+            DEBUGV("Freed EP for device(%d)",deviceID);
             FreeMemoryPoolItem(old);
         }
         else
         {
-            list = &(*(list))->NextED;//Move to next.
+            list = &(*(list))->NextEP;//Move to next.
         }
     }
 }