Committer:
RichardUK
Date:
Wed Sep 19 16:44:32 2012 +0000
Revision:
2:33e7ce63dd6d
Parent:
1:4461071ed964
Testing lib update to another project.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
RichardUK 0:63d45df56584 1 /*
RichardUK 0:63d45df56584 2 A host controller driver from the mBed device.
RichardUK 0:63d45df56584 3 Copyright (C) 2012 Richard e Collins - richard.collins@linux.com
RichardUK 0:63d45df56584 4
RichardUK 0:63d45df56584 5 This program is free software: you can redistribute it and/or modify
RichardUK 0:63d45df56584 6 it under the terms of the GNU General Public License as published by
RichardUK 0:63d45df56584 7 the Free Software Foundation, either version 3 of the License, or
RichardUK 0:63d45df56584 8 (at your option) any later version.
RichardUK 0:63d45df56584 9
RichardUK 0:63d45df56584 10 This program is distributed in the hope that it will be useful,
RichardUK 0:63d45df56584 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
RichardUK 0:63d45df56584 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
RichardUK 0:63d45df56584 13 GNU General Public License for more details.
RichardUK 0:63d45df56584 14
RichardUK 0:63d45df56584 15 You should have received a copy of the GNU General Public License
RichardUK 0:63d45df56584 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
RichardUK 0:63d45df56584 17 */
RichardUK 0:63d45df56584 18
RichardUK 0:63d45df56584 19
RichardUK 0:63d45df56584 20 #include <mbed.h>
RichardUK 0:63d45df56584 21 #include <stddef.h>
RichardUK 0:63d45df56584 22 #include <assert.h>
RichardUK 0:63d45df56584 23
RichardUK 0:63d45df56584 24 #include "UsbHostController.h"
RichardUK 0:63d45df56584 25 #include "HardwareDefines.h"
RichardUK 0:63d45df56584 26 #include "Debug.h"
RichardUK 0:63d45df56584 27 #include "UsbStructures.h"
RichardUK 0:63d45df56584 28 #include "UsbEnums.h"
RichardUK 0:63d45df56584 29
RichardUK 0:63d45df56584 30 #define HOST_CLK_EN (1<<0)
RichardUK 0:63d45df56584 31 #define PORTSEL_CLK_EN (1<<3)
RichardUK 0:63d45df56584 32 #define AHB_CLK_EN (1<<4)
RichardUK 0:63d45df56584 33 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
RichardUK 0:63d45df56584 34
RichardUK 0:63d45df56584 35 #define HOST_CONTROLLER_COMMUNICATIONS_AREA_ADDRESS (0x2007C000)
RichardUK 0:63d45df56584 36
RichardUK 0:63d45df56584 37 /*
RichardUK 0:63d45df56584 38 * The max number of connected devices I support.
RichardUK 0:63d45df56584 39 */
RichardUK 0:63d45df56584 40 #define MAX_DEVICES 8
RichardUK 0:63d45df56584 41
RichardUK 0:63d45df56584 42 #if 0
RichardUK 1:4461071ed964 43 #define DEBUGV DEBUG
RichardUK 1:4461071ed964 44 #else
RichardUK 1:4461071ed964 45 #define DEBUGV(...) do {} while(0)
RichardUK 0:63d45df56584 46 #endif
RichardUK 0:63d45df56584 47
RichardUK 0:63d45df56584 48 namespace USB
RichardUK 0:63d45df56584 49 {
RichardUK 0:63d45df56584 50
RichardUK 0:63d45df56584 51 /*
RichardUK 0:63d45df56584 52 * Does not matter where this is allocated, later will make it come after all the important stuff in the first block of extra sram so the lib takes no main ram from the user.
RichardUK 0:63d45df56584 53 */
RichardUK 0:63d45df56584 54 static HostController _controller;
RichardUK 0:63d45df56584 55
RichardUK 0:63d45df56584 56 /**
RichardUK 0:63d45df56584 57 * Flash a light so I get too see stuff changing.
RichardUK 0:63d45df56584 58 */
RichardUK 1:4461071ed964 59 static DigitalOut irqTick(LED3);
RichardUK 1:4461071ed964 60 static DigitalOut errorLight(LED4);
RichardUK 0:63d45df56584 61
RichardUK 0:63d45df56584 62
RichardUK 0:63d45df56584 63 /*
RichardUK 0:63d45df56584 64 * Handle interupts from the USB.
RichardUK 0:63d45df56584 65 * Some parts of this are taken from the official documentation but I was not !00% sure why
RichardUK 0:63d45df56584 66 * they did what they did. Lack of comments in their code or documentation.
RichardUK 0:63d45df56584 67 *
RichardUK 0:63d45df56584 68 * When the HC reaches the end of a frame and
RichardUK 0:63d45df56584 69 * its deferred interrupt register is 0, it writes the
RichardUK 0:63d45df56584 70 * current value of its HcDoneHead to this
RichardUK 0:63d45df56584 71 * location and generates an interrupt if interrupts
RichardUK 0:63d45df56584 72 * are enabled. This location is not written by the
RichardUK 0:63d45df56584 73 * HC again until software clears the WD bit in
RichardUK 0:63d45df56584 74 * the HcInterruptStatus register.
RichardUK 0:63d45df56584 75 * The LSb of this entry is set to 1 to indicate
RichardUK 0:63d45df56584 76 * whether an unmasked HcInterruptStatus was
RichardUK 0:63d45df56584 77 * set when HccaDoneHead was written.
RichardUK 0:63d45df56584 78 */
RichardUK 0:63d45df56584 79 extern "C" void USB_IRQHandler(void) __irq;
RichardUK 0:63d45df56584 80 void USB_IRQHandler (void) __irq
RichardUK 1:4461071ed964 81 {
RichardUK 1:4461071ed964 82 _controller.USBInterupt(); //Process the interupt.
RichardUK 0:63d45df56584 83 }
RichardUK 0:63d45df56584 84
RichardUK 0:63d45df56584 85 /*
RichardUK 0:63d45df56584 86 * Called after we have recived a status changed intterupt from the USB.
RichardUK 0:63d45df56584 87 */
RichardUK 0:63d45df56584 88 static void QueueRootHubStatusCheckCallback()
RichardUK 0:63d45df56584 89 {
RichardUK 1:4461071ed964 90 DEBUGV("Resetting hub port after connect");
RichardUK 0:63d45df56584 91 //Reset the port ready for using the connected device.
RichardUK 0:63d45df56584 92 //ROOTHUB_PORT_RESET_STATUS_CHANGE bit will be set when done.
RichardUK 0:63d45df56584 93 LPC_USB->HcRhPortStatus1 = ROOTHUB_PORT_RESET_STATUS;
RichardUK 0:63d45df56584 94 }
RichardUK 0:63d45df56584 95
RichardUK 0:63d45df56584 96 /**
RichardUK 0:63d45df56584 97 * Once started never stopped, as the memory is in reservers space there is nothing gained in stopping it.
RichardUK 0:63d45df56584 98 */
RichardUK 0:63d45df56584 99 HostController *HostController::get()
RichardUK 0:63d45df56584 100 {
RichardUK 0:63d45df56584 101 return &_controller;
RichardUK 0:63d45df56584 102 }
RichardUK 0:63d45df56584 103
RichardUK 0:63d45df56584 104
RichardUK 0:63d45df56584 105 /*
RichardUK 0:63d45df56584 106 * Process the interupt that just came in.
RichardUK 0:63d45df56584 107 */
RichardUK 1:4461071ed964 108 uint32_t HostController::USBInterupt()
RichardUK 0:63d45df56584 109 {
RichardUK 1:4461071ed964 110 // It is our interrupt, prevent HC from doing it to us again until we are finished
RichardUK 1:4461071ed964 111 LPC_USB->HcInterruptDisable = MasterInterruptEnable;
RichardUK 1:4461071ed964 112
RichardUK 1:4461071ed964 113 //Other systems seem to grab the head and status fields, re enable interupts and then process whilst still in the handler, not sure why.
RichardUK 1:4461071ed964 114 TransferDescriptor *doneHead = _controller.commsArea->doneHead;
RichardUK 1:4461071ed964 115 _controller.commsArea->doneHead = 0;
RichardUK 1:4461071ed964 116
RichardUK 1:4461071ed964 117
RichardUK 1:4461071ed964 118 //Writing back the interupts we have handled tells the system we did them.
RichardUK 1:4461071ed964 119 //So for now say I done them all. ;)
RichardUK 1:4461071ed964 120 //If we don't do this the system locks up.
RichardUK 1:4461071ed964 121 //I guess because once we renable the interupts they refire so the main thread never gets a chance to runs again.
RichardUK 1:4461071ed964 122 //It is done via the return value to stop the compiler from optimising it out, which some may do if you are not carful.
RichardUK 1:4461071ed964 123 const uint32_t interruptStatus = LPC_USB->HcInterruptStatus;
RichardUK 1:4461071ed964 124 LPC_USB->HcInterruptStatus = interruptStatus;
RichardUK 1:4461071ed964 125
RichardUK 1:4461071ed964 126 // It is our interrupt, prevent HC from doing it to us again until we are finished
RichardUK 1:4461071ed964 127 LPC_USB->HcInterruptEnable = MasterInterruptEnable;
RichardUK 1:4461071ed964 128
RichardUK 0:63d45df56584 129 uint32_t servicedInterrupt = 0;
RichardUK 0:63d45df56584 130 if( interruptStatus & INTERRUPT_UNRECOVERABLE_ERROR )
RichardUK 0:63d45df56584 131 {
RichardUK 1:4461071ed964 132 flags.unrecoverableError = 1;
RichardUK 1:4461071ed964 133 errorLight = 1;
RichardUK 0:63d45df56584 134 servicedInterrupt |= INTERRUPT_UNRECOVERABLE_ERROR;
RichardUK 0:63d45df56584 135 }
RichardUK 0:63d45df56584 136
RichardUK 0:63d45df56584 137 //Have we recived and data?
RichardUK 0:63d45df56584 138 if( interruptStatus & INTERRUPT_WRITEBACK_HEAD_DONE )
RichardUK 0:63d45df56584 139 {//Process all the recived packets.
RichardUK 1:4461071ed964 140 while( doneHead != NULL )
RichardUK 0:63d45df56584 141 {
RichardUK 1:4461071ed964 142 // DEBUG("TD 0x%08x done\n",(int)doneHead);
RichardUK 1:4461071ed964 143
RichardUK 1:4461071ed964 144 //Get next before we start processing so the code can reuse the nextTD pointer.
RichardUK 1:4461071ed964 145 TransferDescriptor *next = doneHead->nextTD;
RichardUK 1:4461071ed964 146
RichardUK 1:4461071ed964 147 //Tell driver transfer is done.
RichardUK 1:4461071ed964 148 messageCallback->TransferDone(doneHead);
RichardUK 1:4461071ed964 149
RichardUK 1:4461071ed964 150 //If not zero then the call to messageCallback->TransferDone will queue the transfer for processing in the update phase.
RichardUK 1:4461071ed964 151 //The update call will then free the pool item.
RichardUK 1:4461071ed964 152 if( doneHead->transferCallback == NULL )
RichardUK 1:4461071ed964 153 {
RichardUK 1:4461071ed964 154 FreeMemoryPoolItem(doneHead);
RichardUK 1:4461071ed964 155 DEBUGV("TransferDescriptor freed");
RichardUK 1:4461071ed964 156 }
RichardUK 1:4461071ed964 157 //Move to next.
RichardUK 1:4461071ed964 158 doneHead = next;
RichardUK 0:63d45df56584 159 }
RichardUK 0:63d45df56584 160
RichardUK 0:63d45df56584 161 //All done re enable the interupt.
RichardUK 0:63d45df56584 162 servicedInterrupt |= INTERRUPT_WRITEBACK_HEAD_DONE;
RichardUK 0:63d45df56584 163 }
RichardUK 0:63d45df56584 164
RichardUK 0:63d45df56584 165 if( interruptStatus & INTERRUPT_ROOTHUB_STATUS_CHANGE )
RichardUK 0:63d45df56584 166 {
RichardUK 0:63d45df56584 167 hubPortStatus = LPC_USB->HcRhPortStatus1;
RichardUK 0:63d45df56584 168 if( hubPortStatus & ROOTHUB_CONNECT_STATUS_CHANGE )
RichardUK 0:63d45df56584 169 {
RichardUK 0:63d45df56584 170 if(hubPortStatus & ROOTHUB_CURRENT_CONNECT_STATUS)
RichardUK 0:63d45df56584 171 {
RichardUK 1:4461071ed964 172 DEBUGV("Resetting hub port after connect");
RichardUK 0:63d45df56584 173 //Reset the port ready for using the connected device.
RichardUK 0:63d45df56584 174 //ROOTHUB_PORT_RESET_STATUS_CHANGE bit will be set when done.
RichardUK 0:63d45df56584 175 delayedRootHubStatusChangeTimeout.attach(QueueRootHubStatusCheckCallback,0.5f);
RichardUK 0:63d45df56584 176 }
RichardUK 0:63d45df56584 177 else
RichardUK 0:63d45df56584 178 {
RichardUK 1:4461071ed964 179 DEBUGV("Disconnected");
RichardUK 0:63d45df56584 180 }
RichardUK 0:63d45df56584 181 }
RichardUK 0:63d45df56584 182 else if( (hubPortStatus&(ROOTHUB_PORT_RESET_STATUS_CHANGE|ROOTHUB_PORT_RESET_STATUS)) == ROOTHUB_PORT_RESET_STATUS_CHANGE )
RichardUK 0:63d45df56584 183 {//Deal with port reset status.
RichardUK 0:63d45df56584 184 flags.newDeviceConnected = 1;
RichardUK 0:63d45df56584 185 }
RichardUK 0:63d45df56584 186 LPC_USB->HcRhPortStatus1 = hubPortStatus & 0xffff0000;
RichardUK 0:63d45df56584 187
RichardUK 0:63d45df56584 188 servicedInterrupt |= INTERRUPT_ROOTHUB_STATUS_CHANGE;
RichardUK 0:63d45df56584 189 }
RichardUK 0:63d45df56584 190
RichardUK 0:63d45df56584 191 if( interruptStatus & INTERRUPT_FRAME_NUMBER_OVERFLOW )
RichardUK 0:63d45df56584 192 {
RichardUK 1:4461071ed964 193 DEBUGV("FRAME_NUMBER_OVERFLOW");
RichardUK 0:63d45df56584 194 servicedInterrupt |= INTERRUPT_FRAME_NUMBER_OVERFLOW;
RichardUK 0:63d45df56584 195 }
RichardUK 0:63d45df56584 196
RichardUK 0:63d45df56584 197 irqTick = !irqTick;//Show we got an interupt.
RichardUK 0:63d45df56584 198
RichardUK 0:63d45df56584 199 return servicedInterrupt;
RichardUK 0:63d45df56584 200 }
RichardUK 0:63d45df56584 201
RichardUK 0:63d45df56584 202 /*
RichardUK 0:63d45df56584 203 * Get it all going, I did want this to be done in the constructor but the chip did not like the USB starting before main was called.
RichardUK 0:63d45df56584 204 */
RichardUK 0:63d45df56584 205 void HostController::Init(HostControllerMessages *messageCallback,Device*& deviceZero)
RichardUK 0:63d45df56584 206 {
RichardUK 0:63d45df56584 207 this->messageCallback = messageCallback;
RichardUK 0:63d45df56584 208
RichardUK 1:4461071ed964 209 errorLight = 0;
RichardUK 0:63d45df56584 210 commsArea = (HostControllerCommunicationsArea*)HOST_CONTROLLER_COMMUNICATIONS_AREA_ADDRESS;
RichardUK 0:63d45df56584 211
RichardUK 0:63d45df56584 212 DEBUG("Starting USB Host Controller Driver.");
RichardUK 0:63d45df56584 213 DEBUG("sizeof(UsbHost::Controller) == %d",sizeof(HostController));
RichardUK 0:63d45df56584 214 DEBUG("sizeof(SetupPacket) == %d",sizeof(SetupPacket));
RichardUK 0:63d45df56584 215 DEBUG("%d %d %d %d %d",offsetof(SetupPacket,requestType),offsetof(SetupPacket,request),offsetof(SetupPacket,value),offsetof(SetupPacket,index),offsetof(SetupPacket,length));
RichardUK 0:63d45df56584 216
RichardUK 0:63d45df56584 217 DEBUG("sizeof(HostControllerCommunicationsArea) == %d",sizeof(HostControllerCommunicationsArea));
RichardUK 0:63d45df56584 218 DEBUG("sizeof(EndpointDescriptor) == %d",sizeof(EndpointDescriptor));
RichardUK 0:63d45df56584 219 DEBUG("sizeof(TransferDescriptor) == %d",sizeof(TransferDescriptor));
RichardUK 0:63d45df56584 220 DEBUG("sizeof(LinkedListItem) == %d",sizeof(LinkedListItem));
RichardUK 0:63d45df56584 221 DEBUG("Memory pool size == %d",sizeof(commsArea->memoryPool));
RichardUK 0:63d45df56584 222
RichardUK 0:63d45df56584 223 memset(commsArea,0,sizeof(HostControllerCommunicationsArea));
RichardUK 0:63d45df56584 224
RichardUK 0:63d45df56584 225 deviceZero = &commsArea->deviceZero;
RichardUK 0:63d45df56584 226
RichardUK 0:63d45df56584 227 //Init the memory pool, just creates a linked list of free objects.
RichardUK 0:63d45df56584 228 for( int n = 1 ; n < (sizeof(commsArea->memoryPool) / sizeof(commsArea->memoryPool[0])) ; n++ )
RichardUK 0:63d45df56584 229 {
RichardUK 0:63d45df56584 230 commsArea->memoryPool[n-1].next = commsArea->memoryPool + n;
RichardUK 0:63d45df56584 231 }
RichardUK 0:63d45df56584 232 memoryPoolHead = commsArea->memoryPool;
RichardUK 0:63d45df56584 233 numAllocatedPoolItems = 0;
RichardUK 0:63d45df56584 234
RichardUK 0:63d45df56584 235 DEBUG("Disabling USB_IRQn");
RichardUK 0:63d45df56584 236 NVIC_DisableIRQ(USB_IRQn);
RichardUK 0:63d45df56584 237
RichardUK 0:63d45df56584 238 // Turn on USB interface power/clock control bit in PCONP (Power Control for Peripherals register)
RichardUK 0:63d45df56584 239 LPC_SC->PCONP |= PCUSB;//USB interface power/clock control bit.
RichardUK 0:63d45df56584 240
RichardUK 0:63d45df56584 241 // Enable USB host clock, port selection and AHB clock
RichardUK 0:63d45df56584 242 LPC_USB->USBClkCtrl |= CLOCK_MASK;
RichardUK 0:63d45df56584 243
RichardUK 0:63d45df56584 244 // Wait for clocks to become available
RichardUK 0:63d45df56584 245 while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK);
RichardUK 0:63d45df56584 246
RichardUK 0:63d45df56584 247 // We are a Host
RichardUK 0:63d45df56584 248 LPC_USB->OTGStCtrl |= 1;
RichardUK 0:63d45df56584 249 LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN; // we don't need port selection clock until we do OTG
RichardUK 0:63d45df56584 250
RichardUK 0:63d45df56584 251 // configure USB pins
RichardUK 0:63d45df56584 252 LPC_PINCON->PINSEL1 &= ~((3<<26)|(3<<28));
RichardUK 0:63d45df56584 253 LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // USB D+/D-
RichardUK 0:63d45df56584 254
RichardUK 0:63d45df56584 255 LPC_PINCON->PINSEL3 &= ~((3 << 6) | (3 << 22)); // USB_PPWR, USB_OVRCR
RichardUK 0:63d45df56584 256 LPC_PINCON->PINSEL3 |= ((2 << 6) | (2 << 22));
RichardUK 0:63d45df56584 257
RichardUK 0:63d45df56584 258 LPC_PINCON->PINSEL4 &= ~(3 << 18); // USB_CONNECT
RichardUK 0:63d45df56584 259 LPC_PINCON->PINSEL4 |= (1 << 18);
RichardUK 0:63d45df56584 260
RichardUK 0:63d45df56584 261 // Reset OHCI block
RichardUK 0:63d45df56584 262 LPC_USB->HcControl = 0;
RichardUK 0:63d45df56584 263 LPC_USB->HcControlHeadED = 0;
RichardUK 0:63d45df56584 264 LPC_USB->HcBulkHeadED = 0;
RichardUK 0:63d45df56584 265
RichardUK 0:63d45df56584 266 LPC_USB->HcCommandStatus = HostControllerReset;
RichardUK 0:63d45df56584 267 LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL;
RichardUK 0:63d45df56584 268 LPC_USB->HcPeriodicStart = FRAMEINTERVAL*90/100;
RichardUK 0:63d45df56584 269
RichardUK 0:63d45df56584 270 LPC_USB->HcControl = (LPC_USB->HcControl & (~HostControllerFunctionalState)) | OperationalMask;
RichardUK 0:63d45df56584 271 LPC_USB->HcRhStatus = SetGlobalPower;
RichardUK 0:63d45df56584 272
RichardUK 0:63d45df56584 273 LPC_USB->HcHCCA = (uint32_t)commsArea;
RichardUK 0:63d45df56584 274 LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;
RichardUK 0:63d45df56584 275 LPC_USB->HcInterruptEnable = INTERRUPT_MASTER_INTERRUPT_ENABLE | INTERRUPT_WRITEBACK_HEAD_DONE | INTERRUPT_ROOTHUB_STATUS_CHANGE | INTERRUPT_UNRECOVERABLE_ERROR | INTERRUPT_FRAME_NUMBER_OVERFLOW;
RichardUK 0:63d45df56584 276
RichardUK 0:63d45df56584 277 DEBUG("Enabling USB_IRQn");
RichardUK 0:63d45df56584 278 NVIC_SetPriority(USB_IRQn, 0);
RichardUK 0:63d45df56584 279 NVIC_EnableIRQ(USB_IRQn);
RichardUK 0:63d45df56584 280
RichardUK 0:63d45df56584 281 wait(1);//Without this things can go a bit wonky because of the above playing with IRQ's. ( I think )
RichardUK 0:63d45df56584 282
RichardUK 0:63d45df56584 283 DEBUG("Host controller communication area (HcHCCA) set to 0x%08x",LPC_USB->HcHCCA);
RichardUK 0:63d45df56584 284 }
RichardUK 0:63d45df56584 285
RichardUK 0:63d45df56584 286 /*
RichardUK 0:63d45df56584 287 * Service the events from the interupts and the results of interupt transfers.
RichardUK 0:63d45df56584 288 */
RichardUK 0:63d45df56584 289 void HostController::Update()
RichardUK 0:63d45df56584 290 {
RichardUK 0:63d45df56584 291 irqTick = 0;//Clear this, may have been left on after an interupt.
RichardUK 0:63d45df56584 292 if( flags.newDeviceConnected )
RichardUK 0:63d45df56584 293 {
RichardUK 1:4461071ed964 294 flags.newDeviceConnected = 0;
RichardUK 1:4461071ed964 295 DEBUGV("AddDevice START");
RichardUK 0:63d45df56584 296 messageCallback->AddDevice(0,1,hubPortStatus);//Root hub is always hub zero, port 1. So add the device at this location.
RichardUK 1:4461071ed964 297 DEBUGV("AddDevice DONE");
RichardUK 1:4461071ed964 298 }
RichardUK 1:4461071ed964 299
RichardUK 1:4461071ed964 300 if( flags.unrecoverableError )
RichardUK 1:4461071ed964 301 {
RichardUK 1:4461071ed964 302 DEBUG("USB Controller has failed, please reset.");
RichardUK 1:4461071ed964 303 flags.unrecoverableError = 0;
RichardUK 0:63d45df56584 304 }
RichardUK 0:63d45df56584 305 }
RichardUK 0:63d45df56584 306
RichardUK 0:63d45df56584 307 void *HostController::AllocateMemoryPoolItem()
RichardUK 0:63d45df56584 308 {
RichardUK 0:63d45df56584 309 if( memoryPoolHead == NULL )
RichardUK 0:63d45df56584 310 {
RichardUK 0:63d45df56584 311 return NULL;
RichardUK 0:63d45df56584 312 }
RichardUK 0:63d45df56584 313 LinkedListItem* f = memoryPoolHead;
RichardUK 0:63d45df56584 314 memoryPoolHead = memoryPoolHead->next;
RichardUK 0:63d45df56584 315 numAllocatedPoolItems++;
RichardUK 1:4461071ed964 316 DEBUGV("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
RichardUK 0:63d45df56584 317
RichardUK 0:63d45df56584 318 return (void*)f;
RichardUK 0:63d45df56584 319 }
RichardUK 0:63d45df56584 320
RichardUK 0:63d45df56584 321 void HostController::FreeMemoryPoolItem(void *item)
RichardUK 0:63d45df56584 322 {
RichardUK 0:63d45df56584 323 assert(numAllocatedPoolItems > 0);
RichardUK 0:63d45df56584 324 LinkedListItem* f = (LinkedListItem*)item;
RichardUK 0:63d45df56584 325 f->next = memoryPoolHead;
RichardUK 0:63d45df56584 326 memoryPoolHead = f;
RichardUK 0:63d45df56584 327 numAllocatedPoolItems--;
RichardUK 1:4461071ed964 328 DEBUGV("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
RichardUK 0:63d45df56584 329 }
RichardUK 0:63d45df56584 330
RichardUK 0:63d45df56584 331 /**
RichardUK 0:63d45df56584 332 * functionAddress This is our deviceID. We tell the controller the ID we are using when it is inserted.
RichardUK 0:63d45df56584 333 * The docs call it the 'functionAddress'.
RichardUK 0:63d45df56584 334 * 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.
RichardUK 0:63d45df56584 335 * When the device is removed all the enpoints are removed from the system.
RichardUK 0:63d45df56584 336 */
RichardUK 1:4461071ed964 337 EndpointDescriptor *HostController::GetEndpointDescriptor(EndpointDescriptor** headEP,uint32_t functionAddress,int endpointNumber,int maximumPacketSize,int lowspeed)
RichardUK 0:63d45df56584 338 {
RichardUK 0:63d45df56584 339 uint32_t control = (maximumPacketSize<<16) | (lowspeed<<13) | (endpointNumber<<7) | functionAddress;
RichardUK 0:63d45df56584 340
RichardUK 0:63d45df56584 341 //have we already allocated and linked in the end point?
RichardUK 1:4461071ed964 342 EndpointDescriptor* ep = *headEP;
RichardUK 0:63d45df56584 343 while( ep != NULL )
RichardUK 0:63d45df56584 344 {
RichardUK 0:63d45df56584 345 if( ep->control == control )
RichardUK 0:63d45df56584 346 {
RichardUK 0:63d45df56584 347 return ep;
RichardUK 0:63d45df56584 348 }
RichardUK 1:4461071ed964 349 ep = ep->NextEP;
RichardUK 0:63d45df56584 350 }
RichardUK 0:63d45df56584 351
RichardUK 0:63d45df56584 352 //Get here and we have not see the endpoint before.
RichardUK 0:63d45df56584 353 //So we need to allocate it and link it in.
RichardUK 0:63d45df56584 354 ep = (EndpointDescriptor*)AllocateMemoryPoolItem();
RichardUK 0:63d45df56584 355
RichardUK 0:63d45df56584 356 if( ep == NULL )
RichardUK 0:63d45df56584 357 {
RichardUK 1:4461071ed964 358 DEBUGV("AllocateEndpoint failed");
RichardUK 0:63d45df56584 359 return NULL;
RichardUK 0:63d45df56584 360 }
RichardUK 0:63d45df56584 361
RichardUK 0:63d45df56584 362 //Link the endpoint onto the list of things to do the next frame when ControlListFilled of HcCommandStatus is set.
RichardUK 0:63d45df56584 363 //When the host controller reaches the end of the list pointed to by HcControlCurrentED
RichardUK 0:63d45df56584 364 //It checks the ControlListEnable flag, if set then HcControlHeadED is copied to HcControlCurrentED and starts the new transfers.
RichardUK 1:4461071ed964 365 ep->NextEP = *headEP;
RichardUK 1:4461071ed964 366 *headEP = ep;
RichardUK 0:63d45df56584 367
RichardUK 0:63d45df56584 368 ep->control = control;//Set the address etc...
RichardUK 1:4461071ed964 369 ep->TailTD = 0;
RichardUK 1:4461071ed964 370 ep->HeadTD = 0;
RichardUK 1:4461071ed964 371 ep->C = 0;
RichardUK 1:4461071ed964 372 ep->H = 0;
RichardUK 1:4461071ed964 373 ep->ZERO = 0;
RichardUK 1:4461071ed964 374
RichardUK 1:4461071ed964 375 DEBUGV("endpoint allocated ep(%d) deviceID(%d)",endpointNumber,functionAddress);
RichardUK 0:63d45df56584 376
RichardUK 0:63d45df56584 377 return ep;
RichardUK 0:63d45df56584 378 }
RichardUK 0:63d45df56584 379
RichardUK 0:63d45df56584 380
RichardUK 1:4461071ed964 381 TransferDescriptor *HostController::AllocateTransferDescriptor(int deviceID,int direction,const uint8_t* data,int dataLength)
RichardUK 0:63d45df56584 382 {
RichardUK 0:63d45df56584 383 TransferDescriptor* td = (TransferDescriptor*)AllocateMemoryPoolItem();
RichardUK 0:63d45df56584 384
RichardUK 0:63d45df56584 385 td->deviceID = (uint8_t)deviceID;//So we know who sent this transfer.
RichardUK 0:63d45df56584 386 td->bufferRounding = 1;//No rounding needed
RichardUK 0:63d45df56584 387 td->direction = direction;
RichardUK 1:4461071ed964 388 td->dataToggle = 0;
RichardUK 0:63d45df56584 389 td->conditionCode = 15;
RichardUK 0:63d45df56584 390 if( data != NULL )
RichardUK 0:63d45df56584 391 {
RichardUK 0:63d45df56584 392 td->CurrentBufferPointer = data;
RichardUK 0:63d45df56584 393 td->bufferEnd = (data + dataLength - 1);//Points to the last byte.
RichardUK 0:63d45df56584 394 }
RichardUK 1:4461071ed964 395 DEBUGV("transfer desc allocated %08x",*((uint32_t*)td));
RichardUK 0:63d45df56584 396 return td;
RichardUK 0:63d45df56584 397 }
RichardUK 0:63d45df56584 398
RichardUK 1:4461071ed964 399 void HostController::QueueControlTransfer(int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,int dataToggle,const uint8_t* data,int dataLength)
RichardUK 0:63d45df56584 400 {
RichardUK 1:4461071ed964 401 TransferDescriptor* td = QueueTransfer((EndpointDescriptor**)&LPC_USB->HcControlHeadED,deviceID,direction,endpointNumber,maximumPacketSize,lowspeed,data,dataLength);
RichardUK 1:4461071ed964 402
RichardUK 1:4461071ed964 403 //If endpoint zero then toggle is always DATA0 for setup packet and DATA1 for the rest.
RichardUK 0:63d45df56584 404 if( endpointNumber == 0 )
RichardUK 0:63d45df56584 405 {
RichardUK 0:63d45df56584 406 dataToggle = direction == TDD_SETUP ? TOGGLE_DATA0 : TOGGLE_DATA1;
RichardUK 0:63d45df56584 407 }
RichardUK 1:4461071ed964 408 td->dataToggle = dataToggle;//DATA0 and use this field for the toggle value. MSb states use this filed, LSb is zero as it's DATA0
RichardUK 1:4461071ed964 409 td->transferType = TT_CONTROL;//For the driver.
RichardUK 1:4461071ed964 410 td->transferCallback = NULL;
RichardUK 1:4461071ed964 411 LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | ControlListFilled;//Tell the host of new things to do.
RichardUK 1:4461071ed964 412 LPC_USB->HcControl = LPC_USB->HcControl | ControlListEnable;//Make sure list processing is on.
RichardUK 1:4461071ed964 413 }
RichardUK 0:63d45df56584 414
RichardUK 1:4461071ed964 415 void HostController::QueueBulkTransfer(int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,TransferCallback* callback,const uint8_t* data,int dataLength)
RichardUK 1:4461071ed964 416 {
RichardUK 1:4461071ed964 417 TransferDescriptor* td = QueueTransfer((EndpointDescriptor**)&LPC_USB->HcBulkHeadED,deviceID,direction,endpointNumber,maximumPacketSize,lowspeed,data,dataLength);
RichardUK 1:4461071ed964 418 DEBUGV("QueueBulkTransfer (0x%08x)",td);
RichardUK 1:4461071ed964 419
RichardUK 1:4461071ed964 420 //For transfers that need a callback.
RichardUK 1:4461071ed964 421 //If callbackID is non zero then when the transfer is done a callback is generated.
RichardUK 1:4461071ed964 422 td->transferCallback = callback;
RichardUK 1:4461071ed964 423 td->data = data;
RichardUK 1:4461071ed964 424 td->length = dataLength;
RichardUK 1:4461071ed964 425 td->transferType = TT_BULK;//For the driver.
RichardUK 1:4461071ed964 426
RichardUK 1:4461071ed964 427 LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | BulkListFilled;//Tell the host of new things to do.
RichardUK 1:4461071ed964 428 LPC_USB->HcControl = LPC_USB->HcControl | BulkListEnable;//Make sure list processing is on.
RichardUK 1:4461071ed964 429 }
RichardUK 1:4461071ed964 430
RichardUK 1:4461071ed964 431 TransferDescriptor* HostController::QueueTransfer(EndpointDescriptor** headED,int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,const uint8_t* data,int dataLength)
RichardUK 1:4461071ed964 432 {
RichardUK 1:4461071ed964 433 //Additions into transfer queues are always done by copying the new entry information to the entry
RichardUK 1:4461071ed964 434 //at the tail of the queue and then appending a new tail entry to the queue. This is accomplished
RichardUK 1:4461071ed964 435 //by:
RichardUK 1:4461071ed964 436 // 1. Copying the new information to the entry pointed to by TailP
RichardUK 1:4461071ed964 437 // 2. Setting the NextTD pointer in the current tail entry to a new place holder
RichardUK 1:4461071ed964 438 // 3. Advancing the TailP pointer to the new place holder
RichardUK 1:4461071ed964 439 // 4. Writing to the ControlListFilled or BulkListFilled bit in HcCommandStatus if the
RichardUK 1:4461071ed964 440 // insert was to a queue on the Control list or Bulk list.
RichardUK 1:4461071ed964 441
RichardUK 1:4461071ed964 442 TransferDescriptor* td = AllocateTransferDescriptor(deviceID,direction,data,dataLength);
RichardUK 1:4461071ed964 443 td->ep = endpointNumber;
RichardUK 0:63d45df56584 444
RichardUK 0:63d45df56584 445 //Get the endpoint needed for this transfer.
RichardUK 1:4461071ed964 446 EndpointDescriptor* ep = GetEndpointDescriptor(headED,deviceID,endpointNumber,maximumPacketSize,lowspeed);
RichardUK 0:63d45df56584 447
RichardUK 1:4461071ed964 448 //Can only queue one at a time, I need to fix.
RichardUK 1:4461071ed964 449 ep->HeadTD = ((int)td)>>4;
RichardUK 1:4461071ed964 450 ep->TailTD = 0;
RichardUK 1:4461071ed964 451 td->nextTD = 0;
RichardUK 1:4461071ed964 452
RichardUK 1:4461071ed964 453 return td;
RichardUK 1:4461071ed964 454 }
RichardUK 0:63d45df56584 455
RichardUK 0:63d45df56584 456
RichardUK 0:63d45df56584 457 uint8_t* HostController::getScratchRam()
RichardUK 0:63d45df56584 458 {
RichardUK 0:63d45df56584 459 return commsArea->scratchRam;
RichardUK 0:63d45df56584 460 }
RichardUK 0:63d45df56584 461
RichardUK 0:63d45df56584 462 void HostController::DetachDevice(int deviceID)
RichardUK 0:63d45df56584 463 {
RichardUK 0:63d45df56584 464 RemoveEndpointsForDevice(deviceID,(EndpointDescriptor**)&LPC_USB->HcControlHeadED);
RichardUK 0:63d45df56584 465 RemoveEndpointsForDevice(deviceID,(EndpointDescriptor**)&LPC_USB->HcBulkHeadED);
RichardUK 0:63d45df56584 466 for( int n = 0 ; n < 32 ; n++ )
RichardUK 0:63d45df56584 467 {
RichardUK 0:63d45df56584 468 RemoveEndpointsForDevice(deviceID,(EndpointDescriptor**)&commsArea->interruptTable[n]);
RichardUK 0:63d45df56584 469 }
RichardUK 0:63d45df56584 470 }
RichardUK 0:63d45df56584 471
RichardUK 0:63d45df56584 472 void HostController::RemoveEndpointsForDevice(int deviceID,EndpointDescriptor **list)
RichardUK 0:63d45df56584 473 {
RichardUK 0:63d45df56584 474 while( (*list) != NULL )
RichardUK 0:63d45df56584 475 {
RichardUK 0:63d45df56584 476 if( ( (*(list))->control&0x7f) == deviceID )
RichardUK 0:63d45df56584 477 {
RichardUK 0:63d45df56584 478 EndpointDescriptor* old = *list;
RichardUK 1:4461071ed964 479 *list = old->NextEP;//Unlink old.
RichardUK 1:4461071ed964 480 //Should not be any transfers to do this EP if we are removing it...... TODO Check this!
RichardUK 1:4461071ed964 481 if( old->HeadTD != 0 )
RichardUK 0:63d45df56584 482 {
RichardUK 1:4461071ed964 483 DEBUGV("Removing endpoint with unfinished transfers, please fix bug richard!");
RichardUK 0:63d45df56584 484 }
RichardUK 1:4461071ed964 485 DEBUGV("Freed EP for device(%d)",deviceID);
RichardUK 0:63d45df56584 486 FreeMemoryPoolItem(old);
RichardUK 0:63d45df56584 487 }
RichardUK 0:63d45df56584 488 else
RichardUK 0:63d45df56584 489 {
RichardUK 1:4461071ed964 490 list = &(*(list))->NextEP;//Move to next.
RichardUK 0:63d45df56584 491 }
RichardUK 0:63d45df56584 492 }
RichardUK 0:63d45df56584 493 }
RichardUK 0:63d45df56584 494
RichardUK 0:63d45df56584 495 };//namespace USB
RichardUK 0:63d45df56584 496