Committer:
RichardUK
Date:
Sun Jul 08 20:18:58 2012 +0000
Revision:
0:63d45df56584
Child:
1:4461071ed964
Fixed memory leak where endpoints were being allocated over and over again. Now only allocates on first use.

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 //Do #if 0 to enable debug log on the host controller driver.
RichardUK 0:63d45df56584 43 #if 0
RichardUK 0:63d45df56584 44 #undef DEBUG
RichardUK 0:63d45df56584 45 #define DEBUG(...) 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 0:63d45df56584 59 static DigitalOut irqTick(LED2);
RichardUK 0:63d45df56584 60
RichardUK 0:63d45df56584 61
RichardUK 0:63d45df56584 62 /*
RichardUK 0:63d45df56584 63 * Handle interupts from the USB.
RichardUK 0:63d45df56584 64 * Some parts of this are taken from the official documentation but I was not !00% sure why
RichardUK 0:63d45df56584 65 * they did what they did. Lack of comments in their code or documentation.
RichardUK 0:63d45df56584 66 *
RichardUK 0:63d45df56584 67 * When the HC reaches the end of a frame and
RichardUK 0:63d45df56584 68 * its deferred interrupt register is 0, it writes the
RichardUK 0:63d45df56584 69 * current value of its HcDoneHead to this
RichardUK 0:63d45df56584 70 * location and generates an interrupt if interrupts
RichardUK 0:63d45df56584 71 * are enabled. This location is not written by the
RichardUK 0:63d45df56584 72 * HC again until software clears the WD bit in
RichardUK 0:63d45df56584 73 * the HcInterruptStatus register.
RichardUK 0:63d45df56584 74 * The LSb of this entry is set to 1 to indicate
RichardUK 0:63d45df56584 75 * whether an unmasked HcInterruptStatus was
RichardUK 0:63d45df56584 76 * set when HccaDoneHead was written.
RichardUK 0:63d45df56584 77 */
RichardUK 0:63d45df56584 78 extern "C" void USB_IRQHandler(void) __irq;
RichardUK 0:63d45df56584 79 void USB_IRQHandler (void) __irq
RichardUK 0:63d45df56584 80 {
RichardUK 0:63d45df56584 81 // It is our interrupt, prevent HC from doing it to us again until we are finished
RichardUK 0:63d45df56584 82 LPC_USB->HcInterruptDisable = MasterInterruptEnable;
RichardUK 0:63d45df56584 83
RichardUK 0:63d45df56584 84 //Writing back the interupts we have handled tells the system we did them.
RichardUK 0:63d45df56584 85 //So for now say I done them all. ;)
RichardUK 0:63d45df56584 86 //If we don't do this the system locks up.
RichardUK 0:63d45df56584 87 //I guess because once we renable the interupts they refire so the main thread never gets a chance to runs again.
RichardUK 0:63d45df56584 88 //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 0:63d45df56584 89 LPC_USB->HcInterruptStatus = _controller.USBInterupt(LPC_USB->HcInterruptStatus); //Process the interupt.
RichardUK 0:63d45df56584 90
RichardUK 0:63d45df56584 91 // It is our interrupt, prevent HC from doing it to us again until we are finished
RichardUK 0:63d45df56584 92 LPC_USB->HcInterruptEnable = MasterInterruptEnable;
RichardUK 0:63d45df56584 93 }
RichardUK 0:63d45df56584 94
RichardUK 0:63d45df56584 95 /*
RichardUK 0:63d45df56584 96 * Called after we have recived a status changed intterupt from the USB.
RichardUK 0:63d45df56584 97 */
RichardUK 0:63d45df56584 98 static void QueueRootHubStatusCheckCallback()
RichardUK 0:63d45df56584 99 {
RichardUK 0:63d45df56584 100 DEBUG("Resetting hub port after connect");
RichardUK 0:63d45df56584 101 //Reset the port ready for using the connected device.
RichardUK 0:63d45df56584 102 //ROOTHUB_PORT_RESET_STATUS_CHANGE bit will be set when done.
RichardUK 0:63d45df56584 103 LPC_USB->HcRhPortStatus1 = ROOTHUB_PORT_RESET_STATUS;
RichardUK 0:63d45df56584 104 }
RichardUK 0:63d45df56584 105
RichardUK 0:63d45df56584 106 /**
RichardUK 0:63d45df56584 107 * Once started never stopped, as the memory is in reservers space there is nothing gained in stopping it.
RichardUK 0:63d45df56584 108 */
RichardUK 0:63d45df56584 109 HostController *HostController::get()
RichardUK 0:63d45df56584 110 {
RichardUK 0:63d45df56584 111 return &_controller;
RichardUK 0:63d45df56584 112 }
RichardUK 0:63d45df56584 113
RichardUK 0:63d45df56584 114
RichardUK 0:63d45df56584 115 /*
RichardUK 0:63d45df56584 116 * Process the interupt that just came in.
RichardUK 0:63d45df56584 117 */
RichardUK 0:63d45df56584 118 uint32_t HostController::USBInterupt(const uint32_t interruptStatus)
RichardUK 0:63d45df56584 119 {
RichardUK 0:63d45df56584 120 uint32_t servicedInterrupt = 0;
RichardUK 0:63d45df56584 121 if( interruptStatus & INTERRUPT_UNRECOVERABLE_ERROR )
RichardUK 0:63d45df56584 122 {
RichardUK 0:63d45df56584 123 DEBUG("USB Controller has failed, please reset.");
RichardUK 0:63d45df56584 124 servicedInterrupt |= INTERRUPT_UNRECOVERABLE_ERROR;
RichardUK 0:63d45df56584 125 }
RichardUK 0:63d45df56584 126
RichardUK 0:63d45df56584 127 //Have we recived and data?
RichardUK 0:63d45df56584 128 if( interruptStatus & INTERRUPT_WRITEBACK_HEAD_DONE )
RichardUK 0:63d45df56584 129 {//Process all the recived packets.
RichardUK 0:63d45df56584 130 TransferDescriptor *transfer = commsArea->doneHead;
RichardUK 0:63d45df56584 131 commsArea->doneHead = 0;
RichardUK 0:63d45df56584 132 while( transfer != NULL )
RichardUK 0:63d45df56584 133 {
RichardUK 0:63d45df56584 134 messageCallback->TransferDone(transfer);
RichardUK 0:63d45df56584 135 TransferDescriptor *next = (TransferDescriptor *)transfer->nextTD;
RichardUK 0:63d45df56584 136 FreeMemoryPoolItem(transfer);
RichardUK 0:63d45df56584 137 transfer = next;
RichardUK 0:63d45df56584 138 DEBUG("TransferDescriptor freed");
RichardUK 0:63d45df56584 139 }
RichardUK 0:63d45df56584 140
RichardUK 0:63d45df56584 141 //All done re enable the interupt.
RichardUK 0:63d45df56584 142 servicedInterrupt |= INTERRUPT_WRITEBACK_HEAD_DONE;
RichardUK 0:63d45df56584 143 }
RichardUK 0:63d45df56584 144
RichardUK 0:63d45df56584 145 if( interruptStatus & INTERRUPT_ROOTHUB_STATUS_CHANGE )
RichardUK 0:63d45df56584 146 {
RichardUK 0:63d45df56584 147 hubPortStatus = LPC_USB->HcRhPortStatus1;
RichardUK 0:63d45df56584 148 if( hubPortStatus & ROOTHUB_CONNECT_STATUS_CHANGE )
RichardUK 0:63d45df56584 149 {
RichardUK 0:63d45df56584 150 if(hubPortStatus & ROOTHUB_CURRENT_CONNECT_STATUS)
RichardUK 0:63d45df56584 151 {
RichardUK 0:63d45df56584 152 DEBUG("Resetting hub port after connect");
RichardUK 0:63d45df56584 153 //Reset the port ready for using the connected device.
RichardUK 0:63d45df56584 154 //ROOTHUB_PORT_RESET_STATUS_CHANGE bit will be set when done.
RichardUK 0:63d45df56584 155 delayedRootHubStatusChangeTimeout.attach(QueueRootHubStatusCheckCallback,0.5f);
RichardUK 0:63d45df56584 156 }
RichardUK 0:63d45df56584 157 else
RichardUK 0:63d45df56584 158 {
RichardUK 0:63d45df56584 159 DEBUG("Disconnected");
RichardUK 0:63d45df56584 160 }
RichardUK 0:63d45df56584 161 }
RichardUK 0:63d45df56584 162 else if( (hubPortStatus&(ROOTHUB_PORT_RESET_STATUS_CHANGE|ROOTHUB_PORT_RESET_STATUS)) == ROOTHUB_PORT_RESET_STATUS_CHANGE )
RichardUK 0:63d45df56584 163 {//Deal with port reset status.
RichardUK 0:63d45df56584 164 flags.newDeviceConnected = 1;
RichardUK 0:63d45df56584 165 }
RichardUK 0:63d45df56584 166 LPC_USB->HcRhPortStatus1 = hubPortStatus & 0xffff0000;
RichardUK 0:63d45df56584 167
RichardUK 0:63d45df56584 168 servicedInterrupt |= INTERRUPT_ROOTHUB_STATUS_CHANGE;
RichardUK 0:63d45df56584 169 }
RichardUK 0:63d45df56584 170
RichardUK 0:63d45df56584 171 if( interruptStatus & INTERRUPT_FRAME_NUMBER_OVERFLOW )
RichardUK 0:63d45df56584 172 {
RichardUK 0:63d45df56584 173 DEBUG("FRAME_NUMBER_OVERFLOW");
RichardUK 0:63d45df56584 174 servicedInterrupt |= INTERRUPT_FRAME_NUMBER_OVERFLOW;
RichardUK 0:63d45df56584 175 }
RichardUK 0:63d45df56584 176
RichardUK 0:63d45df56584 177 irqTick = !irqTick;//Show we got an interupt.
RichardUK 0:63d45df56584 178
RichardUK 0:63d45df56584 179 return servicedInterrupt;
RichardUK 0:63d45df56584 180 }
RichardUK 0:63d45df56584 181
RichardUK 0:63d45df56584 182 /*
RichardUK 0:63d45df56584 183 * 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 184 */
RichardUK 0:63d45df56584 185 void HostController::Init(HostControllerMessages *messageCallback,Device*& deviceZero)
RichardUK 0:63d45df56584 186 {
RichardUK 0:63d45df56584 187 this->messageCallback = messageCallback;
RichardUK 0:63d45df56584 188
RichardUK 0:63d45df56584 189 commsArea = (HostControllerCommunicationsArea*)HOST_CONTROLLER_COMMUNICATIONS_AREA_ADDRESS;
RichardUK 0:63d45df56584 190
RichardUK 0:63d45df56584 191 DEBUG("Starting USB Host Controller Driver.");
RichardUK 0:63d45df56584 192 DEBUG("sizeof(UsbHost::Controller) == %d",sizeof(HostController));
RichardUK 0:63d45df56584 193 DEBUG("sizeof(SetupPacket) == %d",sizeof(SetupPacket));
RichardUK 0:63d45df56584 194 DEBUG("%d %d %d %d %d",offsetof(SetupPacket,requestType),offsetof(SetupPacket,request),offsetof(SetupPacket,value),offsetof(SetupPacket,index),offsetof(SetupPacket,length));
RichardUK 0:63d45df56584 195
RichardUK 0:63d45df56584 196 DEBUG("sizeof(HostControllerCommunicationsArea) == %d",sizeof(HostControllerCommunicationsArea));
RichardUK 0:63d45df56584 197 DEBUG("sizeof(EndpointDescriptor) == %d",sizeof(EndpointDescriptor));
RichardUK 0:63d45df56584 198 DEBUG("sizeof(TransferDescriptor) == %d",sizeof(TransferDescriptor));
RichardUK 0:63d45df56584 199 DEBUG("sizeof(LinkedListItem) == %d",sizeof(LinkedListItem));
RichardUK 0:63d45df56584 200 DEBUG("Memory pool size == %d",sizeof(commsArea->memoryPool));
RichardUK 0:63d45df56584 201
RichardUK 0:63d45df56584 202 memset(commsArea,0,sizeof(HostControllerCommunicationsArea));
RichardUK 0:63d45df56584 203
RichardUK 0:63d45df56584 204 deviceZero = &commsArea->deviceZero;
RichardUK 0:63d45df56584 205
RichardUK 0:63d45df56584 206 //Init the memory pool, just creates a linked list of free objects.
RichardUK 0:63d45df56584 207 for( int n = 1 ; n < (sizeof(commsArea->memoryPool) / sizeof(commsArea->memoryPool[0])) ; n++ )
RichardUK 0:63d45df56584 208 {
RichardUK 0:63d45df56584 209 commsArea->memoryPool[n-1].next = commsArea->memoryPool + n;
RichardUK 0:63d45df56584 210 }
RichardUK 0:63d45df56584 211 memoryPoolHead = commsArea->memoryPool;
RichardUK 0:63d45df56584 212 numAllocatedPoolItems = 0;
RichardUK 0:63d45df56584 213
RichardUK 0:63d45df56584 214 DEBUG("Disabling USB_IRQn");
RichardUK 0:63d45df56584 215 NVIC_DisableIRQ(USB_IRQn);
RichardUK 0:63d45df56584 216
RichardUK 0:63d45df56584 217 // Turn on USB interface power/clock control bit in PCONP (Power Control for Peripherals register)
RichardUK 0:63d45df56584 218 LPC_SC->PCONP |= PCUSB;//USB interface power/clock control bit.
RichardUK 0:63d45df56584 219
RichardUK 0:63d45df56584 220 // Enable USB host clock, port selection and AHB clock
RichardUK 0:63d45df56584 221 LPC_USB->USBClkCtrl |= CLOCK_MASK;
RichardUK 0:63d45df56584 222
RichardUK 0:63d45df56584 223 // Wait for clocks to become available
RichardUK 0:63d45df56584 224 while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK);
RichardUK 0:63d45df56584 225
RichardUK 0:63d45df56584 226 // We are a Host
RichardUK 0:63d45df56584 227 LPC_USB->OTGStCtrl |= 1;
RichardUK 0:63d45df56584 228 LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN; // we don't need port selection clock until we do OTG
RichardUK 0:63d45df56584 229
RichardUK 0:63d45df56584 230 // configure USB pins
RichardUK 0:63d45df56584 231 LPC_PINCON->PINSEL1 &= ~((3<<26)|(3<<28));
RichardUK 0:63d45df56584 232 LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // USB D+/D-
RichardUK 0:63d45df56584 233
RichardUK 0:63d45df56584 234 LPC_PINCON->PINSEL3 &= ~((3 << 6) | (3 << 22)); // USB_PPWR, USB_OVRCR
RichardUK 0:63d45df56584 235 LPC_PINCON->PINSEL3 |= ((2 << 6) | (2 << 22));
RichardUK 0:63d45df56584 236
RichardUK 0:63d45df56584 237 LPC_PINCON->PINSEL4 &= ~(3 << 18); // USB_CONNECT
RichardUK 0:63d45df56584 238 LPC_PINCON->PINSEL4 |= (1 << 18);
RichardUK 0:63d45df56584 239
RichardUK 0:63d45df56584 240 // Reset OHCI block
RichardUK 0:63d45df56584 241 LPC_USB->HcControl = 0;
RichardUK 0:63d45df56584 242 LPC_USB->HcControlHeadED = 0;
RichardUK 0:63d45df56584 243 LPC_USB->HcBulkHeadED = 0;
RichardUK 0:63d45df56584 244
RichardUK 0:63d45df56584 245 LPC_USB->HcCommandStatus = HostControllerReset;
RichardUK 0:63d45df56584 246 LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL;
RichardUK 0:63d45df56584 247 LPC_USB->HcPeriodicStart = FRAMEINTERVAL*90/100;
RichardUK 0:63d45df56584 248
RichardUK 0:63d45df56584 249 LPC_USB->HcControl = (LPC_USB->HcControl & (~HostControllerFunctionalState)) | OperationalMask;
RichardUK 0:63d45df56584 250 LPC_USB->HcRhStatus = SetGlobalPower;
RichardUK 0:63d45df56584 251
RichardUK 0:63d45df56584 252 LPC_USB->HcHCCA = (uint32_t)commsArea;
RichardUK 0:63d45df56584 253 LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;
RichardUK 0:63d45df56584 254 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 255
RichardUK 0:63d45df56584 256 DEBUG("Enabling USB_IRQn");
RichardUK 0:63d45df56584 257 NVIC_SetPriority(USB_IRQn, 0);
RichardUK 0:63d45df56584 258 NVIC_EnableIRQ(USB_IRQn);
RichardUK 0:63d45df56584 259
RichardUK 0:63d45df56584 260 wait(1);//Without this things can go a bit wonky because of the above playing with IRQ's. ( I think )
RichardUK 0:63d45df56584 261
RichardUK 0:63d45df56584 262 DEBUG("Host controller communication area (HcHCCA) set to 0x%08x",LPC_USB->HcHCCA);
RichardUK 0:63d45df56584 263 }
RichardUK 0:63d45df56584 264
RichardUK 0:63d45df56584 265 /*
RichardUK 0:63d45df56584 266 * Service the events from the interupts and the results of interupt transfers.
RichardUK 0:63d45df56584 267 */
RichardUK 0:63d45df56584 268 void HostController::Update()
RichardUK 0:63d45df56584 269 {
RichardUK 0:63d45df56584 270 irqTick = 0;//Clear this, may have been left on after an interupt.
RichardUK 0:63d45df56584 271 if( flags.newDeviceConnected )
RichardUK 0:63d45df56584 272 {
RichardUK 0:63d45df56584 273 DEBUG("AddDevice START");
RichardUK 0:63d45df56584 274 messageCallback->AddDevice(0,1,hubPortStatus);//Root hub is always hub zero, port 1. So add the device at this location.
RichardUK 0:63d45df56584 275 DEBUG("AddDevice DONE");
RichardUK 0:63d45df56584 276 flags.newDeviceConnected = 0;
RichardUK 0:63d45df56584 277 }
RichardUK 0:63d45df56584 278 }
RichardUK 0:63d45df56584 279
RichardUK 0:63d45df56584 280 void *HostController::AllocateMemoryPoolItem()
RichardUK 0:63d45df56584 281 {
RichardUK 0:63d45df56584 282 if( memoryPoolHead == NULL )
RichardUK 0:63d45df56584 283 {
RichardUK 0:63d45df56584 284 return NULL;
RichardUK 0:63d45df56584 285 }
RichardUK 0:63d45df56584 286 LinkedListItem* f = memoryPoolHead;
RichardUK 0:63d45df56584 287 memoryPoolHead = memoryPoolHead->next;
RichardUK 0:63d45df56584 288 numAllocatedPoolItems++;
RichardUK 0:63d45df56584 289 DEBUG("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
RichardUK 0:63d45df56584 290
RichardUK 0:63d45df56584 291 //Clear it.
RichardUK 0:63d45df56584 292 ((uint32_t*)f)[0] = 0;
RichardUK 0:63d45df56584 293 ((uint32_t*)f)[1] = 0;
RichardUK 0:63d45df56584 294 ((uint32_t*)f)[2] = 0;
RichardUK 0:63d45df56584 295 ((uint32_t*)f)[3] = 0;
RichardUK 0:63d45df56584 296
RichardUK 0:63d45df56584 297 return (void*)f;
RichardUK 0:63d45df56584 298 }
RichardUK 0:63d45df56584 299
RichardUK 0:63d45df56584 300 void HostController::FreeMemoryPoolItem(void *item)
RichardUK 0:63d45df56584 301 {
RichardUK 0:63d45df56584 302 assert(numAllocatedPoolItems > 0);
RichardUK 0:63d45df56584 303 LinkedListItem* f = (LinkedListItem*)item;
RichardUK 0:63d45df56584 304 f->next = memoryPoolHead;
RichardUK 0:63d45df56584 305 memoryPoolHead = f;
RichardUK 0:63d45df56584 306 numAllocatedPoolItems--;
RichardUK 0:63d45df56584 307 DEBUG("numAllocatedPoolItems (%d)",numAllocatedPoolItems);
RichardUK 0:63d45df56584 308 }
RichardUK 0:63d45df56584 309
RichardUK 0:63d45df56584 310 /**
RichardUK 0:63d45df56584 311 * functionAddress This is our deviceID. We tell the controller the ID we are using when it is inserted.
RichardUK 0:63d45df56584 312 * The docs call it the 'functionAddress'.
RichardUK 0:63d45df56584 313 * 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 314 * When the device is removed all the enpoints are removed from the system.
RichardUK 0:63d45df56584 315 */
RichardUK 0:63d45df56584 316 EndpointDescriptor *HostController::GetEndpointDescriptor(EndpointDescriptor** headED,uint32_t functionAddress,int endpointNumber,int maximumPacketSize,int lowspeed)
RichardUK 0:63d45df56584 317 {
RichardUK 0:63d45df56584 318 uint32_t control = (maximumPacketSize<<16) | (lowspeed<<13) | (endpointNumber<<7) | functionAddress;
RichardUK 0:63d45df56584 319
RichardUK 0:63d45df56584 320 //have we already allocated and linked in the end point?
RichardUK 0:63d45df56584 321 EndpointDescriptor* ep = *headED;
RichardUK 0:63d45df56584 322 while( ep != NULL )
RichardUK 0:63d45df56584 323 {
RichardUK 0:63d45df56584 324 if( ep->control == control )
RichardUK 0:63d45df56584 325 {
RichardUK 0:63d45df56584 326 return ep;
RichardUK 0:63d45df56584 327 }
RichardUK 0:63d45df56584 328 ep = ep->NextED;
RichardUK 0:63d45df56584 329 }
RichardUK 0:63d45df56584 330
RichardUK 0:63d45df56584 331 //Get here and we have not see the endpoint before.
RichardUK 0:63d45df56584 332 //So we need to allocate it and link it in.
RichardUK 0:63d45df56584 333 ep = (EndpointDescriptor*)AllocateMemoryPoolItem();
RichardUK 0:63d45df56584 334
RichardUK 0:63d45df56584 335 if( ep == NULL )
RichardUK 0:63d45df56584 336 {
RichardUK 0:63d45df56584 337 DEBUG("AllocateEndpoint failed");
RichardUK 0:63d45df56584 338 return NULL;
RichardUK 0:63d45df56584 339 }
RichardUK 0:63d45df56584 340
RichardUK 0:63d45df56584 341 //Link the endpoint onto the list of things to do the next frame when ControlListFilled of HcCommandStatus is set.
RichardUK 0:63d45df56584 342 //When the host controller reaches the end of the list pointed to by HcControlCurrentED
RichardUK 0:63d45df56584 343 //It checks the ControlListEnable flag, if set then HcControlHeadED is copied to HcControlCurrentED and starts the new transfers.
RichardUK 0:63d45df56584 344 ep->NextED = *headED;
RichardUK 0:63d45df56584 345 *headED = ep;
RichardUK 0:63d45df56584 346
RichardUK 0:63d45df56584 347 ep->control = control;//Set the address etc...
RichardUK 0:63d45df56584 348
RichardUK 0:63d45df56584 349 DEBUG("endpoint allocated ep(%d) deviceID(%d)",endpointNumber,functionAddress);
RichardUK 0:63d45df56584 350
RichardUK 0:63d45df56584 351 return ep;
RichardUK 0:63d45df56584 352 }
RichardUK 0:63d45df56584 353
RichardUK 0:63d45df56584 354
RichardUK 0:63d45df56584 355 TransferDescriptor *HostController::AllocateTransferDescriptor(int deviceID,int direction,int dataToggle,const uint8_t* data,int dataLength)
RichardUK 0:63d45df56584 356 {
RichardUK 0:63d45df56584 357 TransferDescriptor* td = (TransferDescriptor*)AllocateMemoryPoolItem();
RichardUK 0:63d45df56584 358
RichardUK 0:63d45df56584 359 td->deviceID = (uint8_t)deviceID;//So we know who sent this transfer.
RichardUK 0:63d45df56584 360 td->bufferRounding = 1;//No rounding needed
RichardUK 0:63d45df56584 361 td->direction = direction;
RichardUK 0:63d45df56584 362 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 0:63d45df56584 363 td->conditionCode = 15;
RichardUK 0:63d45df56584 364 if( data != NULL )
RichardUK 0:63d45df56584 365 {
RichardUK 0:63d45df56584 366 td->CurrentBufferPointer = data;
RichardUK 0:63d45df56584 367 td->bufferEnd = (data + dataLength - 1);//Points to the last byte.
RichardUK 0:63d45df56584 368 }
RichardUK 0:63d45df56584 369 DEBUG("transfer desc allocated %08x",*((uint32_t*)td));
RichardUK 0:63d45df56584 370 return td;
RichardUK 0:63d45df56584 371 }
RichardUK 0:63d45df56584 372
RichardUK 0:63d45df56584 373
RichardUK 0:63d45df56584 374
RichardUK 0:63d45df56584 375
RichardUK 0:63d45df56584 376
RichardUK 0:63d45df56584 377 void HostController::QueueControlTransferStage(int deviceID,int direction,int endpointNumber,int maximumPacketSize,int lowspeed,int dataToggle,const uint8_t* data,int dataLength)
RichardUK 0:63d45df56584 378 {
RichardUK 0:63d45df56584 379 //If endpoint zero then toggle is always DATA0 for setup packet and DATA1 for the rest.
RichardUK 0:63d45df56584 380 if( endpointNumber == 0 )
RichardUK 0:63d45df56584 381 {
RichardUK 0:63d45df56584 382 dataToggle = direction == TDD_SETUP ? TOGGLE_DATA0 : TOGGLE_DATA1;
RichardUK 0:63d45df56584 383 }
RichardUK 0:63d45df56584 384
RichardUK 0:63d45df56584 385 TransferDescriptor* td = AllocateTransferDescriptor(deviceID,direction,dataToggle,data,dataLength);
RichardUK 0:63d45df56584 386
RichardUK 0:63d45df56584 387 //Get the endpoint needed for this transfer.
RichardUK 0:63d45df56584 388 EndpointDescriptor* ep = GetEndpointDescriptor(((EndpointDescriptor**)&LPC_USB->HcControlHeadED),deviceID,endpointNumber,maximumPacketSize,lowspeed);
RichardUK 0:63d45df56584 389
RichardUK 0:63d45df56584 390 td->nextTD = &commsArea->commonTail;
RichardUK 0:63d45df56584 391 ep->TailTD = &commsArea->commonTail;
RichardUK 0:63d45df56584 392 ep->HeadTD = td;
RichardUK 0:63d45df56584 393 ep->NextED = NULL;
RichardUK 0:63d45df56584 394
RichardUK 0:63d45df56584 395
RichardUK 0:63d45df56584 396 LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | ControlListFilled;//Tell the host of new things to do.
RichardUK 0:63d45df56584 397 LPC_USB->HcControl = LPC_USB->HcControl | ControlListEnable;//Make sure list processing is on.
RichardUK 0:63d45df56584 398 }
RichardUK 0:63d45df56584 399
RichardUK 0:63d45df56584 400 uint8_t* HostController::getScratchRam()
RichardUK 0:63d45df56584 401 {
RichardUK 0:63d45df56584 402 return commsArea->scratchRam;
RichardUK 0:63d45df56584 403 }
RichardUK 0:63d45df56584 404
RichardUK 0:63d45df56584 405 void HostController::DetachDevice(int deviceID)
RichardUK 0:63d45df56584 406 {
RichardUK 0:63d45df56584 407 RemoveEndpointsForDevice(deviceID,(EndpointDescriptor**)&LPC_USB->HcControlHeadED);
RichardUK 0:63d45df56584 408 RemoveEndpointsForDevice(deviceID,(EndpointDescriptor**)&LPC_USB->HcBulkHeadED);
RichardUK 0:63d45df56584 409 for( int n = 0 ; n < 32 ; n++ )
RichardUK 0:63d45df56584 410 {
RichardUK 0:63d45df56584 411 RemoveEndpointsForDevice(deviceID,(EndpointDescriptor**)&commsArea->interruptTable[n]);
RichardUK 0:63d45df56584 412 }
RichardUK 0:63d45df56584 413 }
RichardUK 0:63d45df56584 414
RichardUK 0:63d45df56584 415 void HostController::RemoveEndpointsForDevice(int deviceID,EndpointDescriptor **list)
RichardUK 0:63d45df56584 416 {
RichardUK 0:63d45df56584 417 while( (*list) != NULL )
RichardUK 0:63d45df56584 418 {
RichardUK 0:63d45df56584 419 if( ( (*(list))->control&0x7f) == deviceID )
RichardUK 0:63d45df56584 420 {
RichardUK 0:63d45df56584 421 EndpointDescriptor* old = *list;
RichardUK 0:63d45df56584 422 *list = old->NextED;//Unlink old.
RichardUK 0:63d45df56584 423 //Should not be any transfers to do this this EP if we are removing it...... TODO Check this!
RichardUK 0:63d45df56584 424 if( old->HeadTD != &commsArea->commonTail )
RichardUK 0:63d45df56584 425 {
RichardUK 0:63d45df56584 426 DEBUG("Removing endpoint with unfinished transfers, please fix bug richard!");
RichardUK 0:63d45df56584 427 }
RichardUK 0:63d45df56584 428 DEBUG("Freed EP for device(%d)",deviceID);
RichardUK 0:63d45df56584 429 FreeMemoryPoolItem(old);
RichardUK 0:63d45df56584 430 }
RichardUK 0:63d45df56584 431 else
RichardUK 0:63d45df56584 432 {
RichardUK 0:63d45df56584 433 list = &(*(list))->NextED;//Move to next.
RichardUK 0:63d45df56584 434 }
RichardUK 0:63d45df56584 435 }
RichardUK 0:63d45df56584 436 }
RichardUK 0:63d45df56584 437
RichardUK 0:63d45df56584 438 };//namespace USB
RichardUK 0:63d45df56584 439