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 #include <mbed.h>
RichardUK 0:63d45df56584 20 #include "UsbHost.h"
RichardUK 0:63d45df56584 21 #include "UsbHostController.h"
RichardUK 0:63d45df56584 22 #include "HardwareDefines.h"
RichardUK 0:63d45df56584 23 #include "Debug.h"
RichardUK 0:63d45df56584 24 #include "UsbStructures.h"
RichardUK 0:63d45df56584 25 #include "UsbEnums.h"
RichardUK 0:63d45df56584 26
RichardUK 0:63d45df56584 27 //Do #if 0 to enable debug log on the host controller driver.
RichardUK 0:63d45df56584 28 #if 1
RichardUK 0:63d45df56584 29 #undef DEBUG
RichardUK 0:63d45df56584 30 #define DEBUG(...) do {} while(0)
RichardUK 0:63d45df56584 31 #endif
RichardUK 0:63d45df56584 32
RichardUK 0:63d45df56584 33 namespace USB
RichardUK 0:63d45df56584 34 {
RichardUK 0:63d45df56584 35
RichardUK 0:63d45df56584 36 uint32_t controlTransferDataLength = 0;
RichardUK 0:63d45df56584 37
RichardUK 0:63d45df56584 38 Host::Host()
RichardUK 0:63d45df56584 39 {
RichardUK 0:63d45df56584 40 for( int n = 0 ; n < MAX_DEVICES ; n++ )
RichardUK 0:63d45df56584 41 {
RichardUK 0:63d45df56584 42 devices[n] = NULL;
RichardUK 0:63d45df56584 43 }
RichardUK 0:63d45df56584 44
RichardUK 0:63d45df56584 45 driver = HostController::get();
RichardUK 0:63d45df56584 46 driver->Init(this,deviceZero);
RichardUK 0:63d45df56584 47 }
RichardUK 0:63d45df56584 48
RichardUK 0:63d45df56584 49 Host::~Host()
RichardUK 0:63d45df56584 50 {
RichardUK 0:63d45df56584 51 }
RichardUK 0:63d45df56584 52
RichardUK 0:63d45df56584 53 Device* Host::AllocateDevice(int endpointZeroMaxPacketSize,int hubPortStatus)
RichardUK 0:63d45df56584 54 {
RichardUK 0:63d45df56584 55 for( int n = 0 ; n < MAX_DEVICES ; n++ )
RichardUK 0:63d45df56584 56 {
RichardUK 0:63d45df56584 57 if( devices[n] == NULL )
RichardUK 0:63d45df56584 58 {
RichardUK 0:63d45df56584 59 devices[n] = (Device *)driver->AllocateMemoryPoolItem();
RichardUK 0:63d45df56584 60 devices[n]->id = (uint8_t)(n+1);//The device is +1 as device 0 is the host controller.
RichardUK 0:63d45df56584 61 devices[n]->endpointZeroMaxPacketSize = endpointZeroMaxPacketSize;
RichardUK 0:63d45df56584 62 devices[n]->lowspeed = (hubPortStatus&ROOTHUB_LOW_SPEED_DEVICE_ATTACHED) ? 1 : 0;
RichardUK 0:63d45df56584 63
RichardUK 0:63d45df56584 64 return devices[n];
RichardUK 0:63d45df56584 65 }
RichardUK 0:63d45df56584 66 }
RichardUK 0:63d45df56584 67 return NULL;
RichardUK 0:63d45df56584 68 }
RichardUK 0:63d45df56584 69
RichardUK 0:63d45df56584 70 void Host::SendControlTransferData(Device* device,int dataToggle)
RichardUK 0:63d45df56584 71 {
RichardUK 0:63d45df56584 72 driver->QueueControlTransferStage(
RichardUK 0:63d45df56584 73 device->id,
RichardUK 0:63d45df56584 74 device->controlTransferDirToHost == 1 ? TDD_IN : TDD_OUT,
RichardUK 0:63d45df56584 75 0,
RichardUK 0:63d45df56584 76 device->endpointZeroMaxPacketSize,
RichardUK 0:63d45df56584 77 device->lowspeed,
RichardUK 0:63d45df56584 78 dataToggle,
RichardUK 0:63d45df56584 79 device->controlTransferData,
RichardUK 0:63d45df56584 80 device->controlTransferDataLength);
RichardUK 0:63d45df56584 81
RichardUK 0:63d45df56584 82 device->controlTransferState = CTS_DATA;
RichardUK 0:63d45df56584 83 }
RichardUK 0:63d45df56584 84
RichardUK 0:63d45df56584 85 void Host::SendControlTransferAcknowledge(Device* device,int dataToggle)
RichardUK 0:63d45df56584 86 {
RichardUK 0:63d45df56584 87 //Send empty packet to ACK.
RichardUK 0:63d45df56584 88 driver->QueueControlTransferStage(
RichardUK 0:63d45df56584 89 device->id,
RichardUK 0:63d45df56584 90 device->controlTransferDirToHost == 0 ? TDD_IN : TDD_OUT,
RichardUK 0:63d45df56584 91 0,
RichardUK 0:63d45df56584 92 device->endpointZeroMaxPacketSize,
RichardUK 0:63d45df56584 93 device->lowspeed,
RichardUK 0:63d45df56584 94 dataToggle,
RichardUK 0:63d45df56584 95 NULL,0);
RichardUK 0:63d45df56584 96
RichardUK 0:63d45df56584 97 device->controlTransferState = CTS_ACK;
RichardUK 0:63d45df56584 98 }
RichardUK 0:63d45df56584 99
RichardUK 0:63d45df56584 100 Device* Host::getDevice(int deviceID)
RichardUK 0:63d45df56584 101 {
RichardUK 0:63d45df56584 102 if( deviceID == 0 )
RichardUK 0:63d45df56584 103 {
RichardUK 0:63d45df56584 104 return deviceZero;
RichardUK 0:63d45df56584 105 }
RichardUK 0:63d45df56584 106
RichardUK 0:63d45df56584 107 deviceID--;
RichardUK 0:63d45df56584 108 if( deviceID > -1 && deviceID < MAX_DEVICES )
RichardUK 0:63d45df56584 109 {
RichardUK 0:63d45df56584 110 return devices[deviceID];
RichardUK 0:63d45df56584 111 }
RichardUK 0:63d45df56584 112 return NULL;
RichardUK 0:63d45df56584 113 }
RichardUK 0:63d45df56584 114
RichardUK 0:63d45df56584 115 void Host::Update()
RichardUK 0:63d45df56584 116 {
RichardUK 0:63d45df56584 117 driver->Update();
RichardUK 0:63d45df56584 118 }
RichardUK 0:63d45df56584 119
RichardUK 0:63d45df56584 120 int Host::getDeviceDescriptor(int deviceID,DeviceDescription &description)
RichardUK 0:63d45df56584 121 {
RichardUK 0:63d45df56584 122 return ControlTransfer(deviceID,LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR,(LIBUSB_DT_DEVICE << 8), 0, (uint8_t*)&description, sizeof(description),DEFAULT_TIMEOUT);
RichardUK 0:63d45df56584 123 }
RichardUK 0:63d45df56584 124
RichardUK 0:63d45df56584 125 const uint8_t* Host::getConfigurationDescriptor(int deviceID,int index)
RichardUK 0:63d45df56584 126 {
RichardUK 0:63d45df56584 127 uint8_t* buf = driver->getScratchRam();
RichardUK 0:63d45df56584 128 ControlTransfer(deviceID,LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR,(LIBUSB_DT_CONFIG << 8)|index, 0,buf,255,DEFAULT_TIMEOUT);
RichardUK 0:63d45df56584 129 return (const uint8_t*)buf;
RichardUK 0:63d45df56584 130 }
RichardUK 0:63d45df56584 131
RichardUK 0:63d45df56584 132 const char* Host::getStringDescriptor(int deviceID,int index)
RichardUK 0:63d45df56584 133 {
RichardUK 0:63d45df56584 134 Device* dev = getDevice(deviceID);
RichardUK 0:63d45df56584 135 if( dev == NULL )
RichardUK 0:63d45df56584 136 {
RichardUK 0:63d45df56584 137 return "error";
RichardUK 0:63d45df56584 138 }
RichardUK 0:63d45df56584 139
RichardUK 0:63d45df56584 140 uint8_t* buf = driver->getScratchRam();
RichardUK 0:63d45df56584 141 ControlTransfer(deviceID,LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR,(LIBUSB_DT_STRING << 8)|index, dev->languageID,buf,255,DEFAULT_TIMEOUT);
RichardUK 0:63d45df56584 142
RichardUK 0:63d45df56584 143 uint8_t* string = buf;
RichardUK 0:63d45df56584 144 int len = buf[0];
RichardUK 0:63d45df56584 145 for( int n = 2 ; n < len ; n += 2 )
RichardUK 0:63d45df56584 146 {
RichardUK 0:63d45df56584 147 *string++ = buf[n];
RichardUK 0:63d45df56584 148 }
RichardUK 0:63d45df56584 149 *string = 0;
RichardUK 0:63d45df56584 150 return (char*)buf;
RichardUK 0:63d45df56584 151 }
RichardUK 0:63d45df56584 152
RichardUK 0:63d45df56584 153 int Host::setConfiguration(int deviceID,int configuration)
RichardUK 0:63d45df56584 154 {
RichardUK 0:63d45df56584 155 return ControlTransfer(deviceID,LIBUSB_ENDPOINT_OUT, LIBUSB_REQUEST_SET_CONFIGURATION,configuration, 0,NULL,0,DEFAULT_TIMEOUT);
RichardUK 0:63d45df56584 156 }
RichardUK 0:63d45df56584 157
RichardUK 0:63d45df56584 158 /*
RichardUK 0:63d45df56584 159 * Control transfer uses three stages, the setup stage where the request is sent.
RichardUK 0:63d45df56584 160 * The optional Data Stage consists of one or multiple IN or OUT transfers.
RichardUK 0:63d45df56584 161 * The setup request indicates the amount of data to be transmitted in this stage.
RichardUK 0:63d45df56584 162 * If it exceeds the maximum packet size, data will be sent in multiple transfers each being the maximum packet length except for the last packet.
RichardUK 0:63d45df56584 163 * Status Stage reports the status of the overall request and this once again varies due to direction of transfer. Status reporting is always performed by the function.
RichardUK 0:63d45df56584 164 */
RichardUK 0:63d45df56584 165 int Host::ControlTransfer(int deviceID,uint8_t requestType, uint8_t request, uint16_t value, uint16_t index,const uint8_t *data, uint16_t length, uint32_t timeout)
RichardUK 0:63d45df56584 166 {
RichardUK 0:63d45df56584 167 DEBUG("ControlTransfer");
RichardUK 0:63d45df56584 168
RichardUK 0:63d45df56584 169 Device* device = getDevice(deviceID);
RichardUK 0:63d45df56584 170
RichardUK 0:63d45df56584 171 //Remeber the packet we are sending so that when we get the responce from the setup packet we can then ask for or send the data.
RichardUK 0:63d45df56584 172 device->controlTransferData = data;
RichardUK 0:63d45df56584 173 device->controlTransferDataLength = length;
RichardUK 0:63d45df56584 174 device->controlTransferDirToHost = (requestType & LIBUSB_ENDPOINT_IN) ? 1 : 0;
RichardUK 0:63d45df56584 175 device->controlTransferState = CTS_SETUP;
RichardUK 0:63d45df56584 176
RichardUK 0:63d45df56584 177 //First do the setup packet.
RichardUK 0:63d45df56584 178 SetupPacket* sp = (SetupPacket*)driver->AllocateMemoryPoolItem();
RichardUK 0:63d45df56584 179
RichardUK 0:63d45df56584 180 sp->requestType = requestType;
RichardUK 0:63d45df56584 181 sp->request = request;
RichardUK 0:63d45df56584 182 sp->value = value;
RichardUK 0:63d45df56584 183 sp->index = index;
RichardUK 0:63d45df56584 184 sp->length = length;
RichardUK 0:63d45df56584 185
RichardUK 0:63d45df56584 186 DEBUG("Setup %d %d %d %d %d",sp->requestType,sp->request,sp->value,sp->index,sp->length);
RichardUK 0:63d45df56584 187
RichardUK 0:63d45df56584 188 driver->QueueControlTransferStage(deviceID,TDD_SETUP,0,device->endpointZeroMaxPacketSize,device->lowspeed,TOGGLE_DATA0,(const uint8_t*)sp,sizeof(SetupPacket));
RichardUK 0:63d45df56584 189
RichardUK 0:63d45df56584 190 while(device->controlTransferState != CTS_IDLE)
RichardUK 0:63d45df56584 191 {
RichardUK 0:63d45df56584 192 wait_ms(100);
RichardUK 0:63d45df56584 193 };
RichardUK 0:63d45df56584 194
RichardUK 0:63d45df56584 195 //Free this up.
RichardUK 0:63d45df56584 196 driver->FreeMemoryPoolItem(sp);
RichardUK 0:63d45df56584 197 DEBUG("Done, recived %d",controlTransferDataLength);
RichardUK 0:63d45df56584 198
RichardUK 0:63d45df56584 199 return 0;
RichardUK 0:63d45df56584 200 }
RichardUK 0:63d45df56584 201
RichardUK 0:63d45df56584 202 int Host::BulkTransfer(int deviceID,uint8_t endpoint,const uint8_t* data,int length,int callbackID)
RichardUK 0:63d45df56584 203 {
RichardUK 0:63d45df56584 204 DEBUG("ControlTransfer");
RichardUK 0:63d45df56584 205
RichardUK 0:63d45df56584 206 Device* device = getDevice(deviceID);
RichardUK 0:63d45df56584 207 /*
RichardUK 0:63d45df56584 208 driver->QueueControlTransferStage(deviceID,(endpoint&LIBUSB_ENDPOINT_IN) ? TDD_IN : TDD_OUT,endpoint,device->endpointZeroMaxPacketSize,device->lowspeed,TOGGLE_DATA0,data,length);
RichardUK 0:63d45df56584 209
RichardUK 0:63d45df56584 210 while(device->controlTransferState != CTS_IDLE)
RichardUK 0:63d45df56584 211 {
RichardUK 0:63d45df56584 212 wait_ms(100);
RichardUK 0:63d45df56584 213 };
RichardUK 0:63d45df56584 214 */
RichardUK 0:63d45df56584 215
RichardUK 0:63d45df56584 216 return 0;
RichardUK 0:63d45df56584 217 }
RichardUK 0:63d45df56584 218
RichardUK 0:63d45df56584 219
RichardUK 0:63d45df56584 220 void Host::AddDevice(int hub,int port,int hubPortStatus)
RichardUK 0:63d45df56584 221 {
RichardUK 0:63d45df56584 222 DEBUG("AddDevice(%d,%d,%08x)",hub,port,hubPortStatus);
RichardUK 0:63d45df56584 223 DeviceDescription description;
RichardUK 0:63d45df56584 224 description.length = sizeof(DeviceDescription);
RichardUK 0:63d45df56584 225
RichardUK 0:63d45df56584 226 //Setup device zero.
RichardUK 0:63d45df56584 227 deviceZero->endpointZeroMaxPacketSize = 8;
RichardUK 0:63d45df56584 228 deviceZero->lowspeed = (hubPortStatus&ROOTHUB_LOW_SPEED_DEVICE_ATTACHED) ? 1 : 0;
RichardUK 0:63d45df56584 229
RichardUK 0:63d45df56584 230 //For the enumeration we'll use device zero, once done will set the device address and get the rest of the details.
RichardUK 0:63d45df56584 231 //Now we add the first endpoint, endpoint zero by default.
RichardUK 0:63d45df56584 232 //Once added we'll read the device's config and add endpoints for them.
RichardUK 0:63d45df56584 233 //I add my endpoints to the host controller and leave them there.
RichardUK 0:63d45df56584 234 //I set the direction code so that the direction is read from the transfer descriptor.
RichardUK 0:63d45df56584 235 //This means that I need less endpoint objects.
RichardUK 0:63d45df56584 236 //I leave them attached with not transfers to do. That is ok and as per the spec.
RichardUK 0:63d45df56584 237 //Old devices may only have a max packet size of 8 bytes.
RichardUK 0:63d45df56584 238
RichardUK 0:63d45df56584 239 //Using device 0 for the setup and enum phase of the newley connected device.
RichardUK 0:63d45df56584 240 //We do this just to get some info on the device and then send a 'set address' command.
RichardUK 0:63d45df56584 241 //At that point we'll start using it's real ID.
RichardUK 0:63d45df56584 242 DEBUG("Getting endpoint zero maxPacketSize");
RichardUK 0:63d45df56584 243 ControlTransfer(0,LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR,(LIBUSB_DT_DEVICE << 8), 0, (uint8_t*)&description,8,DEFAULT_TIMEOUT);
RichardUK 0:63d45df56584 244
RichardUK 0:63d45df56584 245
RichardUK 0:63d45df56584 246 //Now allocate the device and it's ID.
RichardUK 0:63d45df56584 247 DEBUG("Allocating device");
RichardUK 0:63d45df56584 248 Device* device = AllocateDevice(description.maxPacketSize,hubPortStatus);
RichardUK 0:63d45df56584 249
RichardUK 0:63d45df56584 250 //Now correct the endpoint's max packet size. Needs this for when we setup the endpoint descriptor for a transfer.
RichardUK 0:63d45df56584 251 DEBUG("endpointZeroMaxPacketSize(%d)",device->endpointZeroMaxPacketSize);
RichardUK 0:63d45df56584 252
RichardUK 0:63d45df56584 253 //Set the devices new address.
RichardUK 0:63d45df56584 254 DEBUG("Setting device address to %d",device->id);
RichardUK 0:63d45df56584 255 ControlTransfer(0,LIBUSB_ENDPOINT_OUT | LIBUSB_RECIPIENT_DEVICE, LIBUSB_REQUEST_SET_ADDRESS, device->id,0,NULL,0,DEFAULT_TIMEOUT);
RichardUK 0:63d45df56584 256
RichardUK 0:63d45df56584 257 //New ID is now set, so we can free the object used when it was called device zero.
RichardUK 0:63d45df56584 258 driver->DetachDevice(0);
RichardUK 0:63d45df56584 259
RichardUK 0:63d45df56584 260 //Now fetch the full device description.
RichardUK 0:63d45df56584 261 DEBUG("Getting device description");
RichardUK 0:63d45df56584 262 getDeviceDescriptor(device->id,description);
RichardUK 0:63d45df56584 263
RichardUK 0:63d45df56584 264 DEBUG("idVendor(0x%04x) idProduct(0x%04x)",description.idVendor,description.idProduct);
RichardUK 0:63d45df56584 265
RichardUK 0:63d45df56584 266 //Get the language ID.
RichardUK 0:63d45df56584 267 uint16_t* lang = (uint16_t*)driver->AllocateMemoryPoolItem();
RichardUK 0:63d45df56584 268 ControlTransfer(device->id,LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR,LIBUSB_DT_STRING<<8,0,(uint8_t*)lang,16,DEFAULT_TIMEOUT);
RichardUK 0:63d45df56584 269 device->languageID = lang[1];
RichardUK 0:63d45df56584 270 driver->FreeMemoryPoolItem(lang);
RichardUK 0:63d45df56584 271
RichardUK 0:63d45df56584 272 //Set the first config by default, will get most things up and running.
RichardUK 0:63d45df56584 273 const USB::ConfigurationDescription* config = (const USB::ConfigurationDescription*)getConfigurationDescriptor(device->id,0);
RichardUK 0:63d45df56584 274 wait_ms(100);
RichardUK 0:63d45df56584 275 setConfiguration(device->id,config->configID);
RichardUK 0:63d45df56584 276 wait_ms(100);
RichardUK 0:63d45df56584 277
RichardUK 0:63d45df56584 278 //Tell the app's inherted code about this new device.
RichardUK 0:63d45df56584 279 onConnected(device->id,description);
RichardUK 0:63d45df56584 280 }
RichardUK 0:63d45df56584 281
RichardUK 0:63d45df56584 282 void Host::TransferDone(TransferDescriptor *transfer)
RichardUK 0:63d45df56584 283 {
RichardUK 0:63d45df56584 284 DEBUG("TransferDone, device(%d) errorCount(%d) status(%d) direction(%d) dataToggle(%d)",transfer->deviceID,transfer->errorCount,transfer->conditionCode,transfer->direction,transfer->dataToggle);
RichardUK 0:63d45df56584 285
RichardUK 0:63d45df56584 286 //Get the device this TD that has just been done for.
RichardUK 0:63d45df56584 287 Device* device = getDevice(transfer->deviceID);
RichardUK 0:63d45df56584 288
RichardUK 0:63d45df56584 289 //See if this was a setup packet, if so do we have data to ask for, if not then just ack the transaction.
RichardUK 0:63d45df56584 290 //TODO: Error checking!
RichardUK 0:63d45df56584 291 switch( device->controlTransferState )
RichardUK 0:63d45df56584 292 {
RichardUK 0:63d45df56584 293 case CTS_SETUP:
RichardUK 0:63d45df56584 294 if( device->controlTransferData != NULL )
RichardUK 0:63d45df56584 295 {
RichardUK 0:63d45df56584 296 SendControlTransferData(device,transfer->dataToggle);
RichardUK 0:63d45df56584 297 }
RichardUK 0:63d45df56584 298 else
RichardUK 0:63d45df56584 299 {
RichardUK 0:63d45df56584 300 SendControlTransferAcknowledge(device,transfer->dataToggle);
RichardUK 0:63d45df56584 301 }
RichardUK 0:63d45df56584 302 break;
RichardUK 0:63d45df56584 303
RichardUK 0:63d45df56584 304 case CTS_DATA:
RichardUK 0:63d45df56584 305 controlTransferDataLength = (transfer->bufferEnd - device->controlTransferData) + 1;
RichardUK 0:63d45df56584 306 SendControlTransferAcknowledge(device,transfer->dataToggle);
RichardUK 0:63d45df56584 307 break;
RichardUK 0:63d45df56584 308
RichardUK 0:63d45df56584 309 case CTS_ACK:
RichardUK 0:63d45df56584 310 device->controlTransferState = CTS_IDLE;
RichardUK 0:63d45df56584 311 break;
RichardUK 0:63d45df56584 312 }
RichardUK 0:63d45df56584 313 }
RichardUK 0:63d45df56584 314
RichardUK 0:63d45df56584 315 };//namespace USB