Diff: UsbHostController.cpp
- Revision:
- 1:4461071ed964
- Parent:
- 0:63d45df56584
diff -r 63d45df56584 -r 4461071ed964 UsbHostController.cpp
--- 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.
}
}
}