brians | 0ab60bb | 2013-01-31 02:21:51 +0000 | [diff] [blame^] | 1 | /* |
| 2 | LPCUSB, an USB device driver for LPC microcontrollers |
| 3 | Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) |
| 4 | |
| 5 | Redistribution and use in source and binary forms, with or without |
| 6 | modification, are permitted provided that the following conditions are met: |
| 7 | |
| 8 | 1. Redistributions of source code must retain the above copyright |
| 9 | notice, this list of conditions and the following disclaimer. |
| 10 | 2. Redistributions in binary form must reproduce the above copyright |
| 11 | notice, this list of conditions and the following disclaimer in the |
| 12 | documentation and/or other materials provided with the distribution. |
| 13 | 3. The name of the author may not be used to endorse or promote products |
| 14 | derived from this software without specific prior written permission. |
| 15 | |
| 16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 17 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 18 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 19 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 20 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 21 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 25 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | |
| 28 | |
| 29 | /** @file |
| 30 | Standard request handler. |
| 31 | |
| 32 | This modules handles the 'chapter 9' processing, specifically the |
| 33 | standard device requests in table 9-3 from the universal serial bus |
| 34 | specification revision 2.0 |
| 35 | |
| 36 | Specific types of devices may specify additional requests (for example |
| 37 | HID devices add a GET_DESCRIPTOR request for interfaces), but they |
| 38 | will not be part of this module. |
| 39 | |
| 40 | @todo some requests have to return a request error if device not configured: |
| 41 | @todo GET_INTERFACE, GET_STATUS, SET_INTERFACE, SYNCH_FRAME |
| 42 | @todo this applies to the following if endpoint != 0: |
| 43 | @todo SET_FEATURE, GET_FEATURE |
| 44 | */ |
| 45 | |
| 46 | #include "usbdebug.h" |
| 47 | #include "usbstruct.h" |
| 48 | #include "usbapi.h" |
| 49 | |
| 50 | #define MAX_DESC_HANDLERS 4 /**< device, interface, endpoint, other */ |
| 51 | |
| 52 | |
| 53 | /* general descriptor field offsets */ |
| 54 | #define DESC_bLength 0 /**< length offset */ |
| 55 | #define DESC_bDescriptorType 1 /**< descriptor type offset */ |
| 56 | |
| 57 | /* config descriptor field offsets */ |
| 58 | #define CONF_DESC_wTotalLength 2 /**< total length offset */ |
| 59 | #define CONF_DESC_bConfigurationValue 5 /**< configuration value offset */ |
| 60 | #define CONF_DESC_bmAttributes 7 /**< configuration characteristics */ |
| 61 | |
| 62 | /* interface descriptor field offsets */ |
| 63 | #define INTF_DESC_bAlternateSetting 3 /**< alternate setting offset */ |
| 64 | |
| 65 | /* endpoint descriptor field offsets */ |
| 66 | #define ENDP_DESC_bEndpointAddress 2 /**< endpoint address offset */ |
| 67 | #define ENDP_DESC_wMaxPacketSize 4 /**< maximum packet size offset */ |
| 68 | |
| 69 | |
| 70 | /** Currently selected configuration */ |
| 71 | static unsigned char bConfiguration = 0; |
| 72 | /** Installed custom request handler */ |
| 73 | static TFnHandleRequest *pfnHandleCustomReq = NULL; |
| 74 | /** Pointer to registered descriptors */ |
| 75 | static const unsigned char *pabDescrip = NULL; |
| 76 | |
| 77 | |
| 78 | /** |
| 79 | Registers a pointer to a descriptor block containing all descriptors |
| 80 | for the device. |
| 81 | |
| 82 | @param [in] pabDescriptors The descriptor byte array |
| 83 | */ |
| 84 | void USBRegisterDescriptors(const unsigned char *pabDescriptors) |
| 85 | { |
| 86 | pabDescrip = pabDescriptors; |
| 87 | } |
| 88 | |
| 89 | |
| 90 | /** |
| 91 | Parses the list of installed USB descriptors and attempts to find |
| 92 | the specified USB descriptor. |
| 93 | |
| 94 | @param [in] wTypeIndex Type and index of the descriptor |
| 95 | @param [in] wLangID Language ID of the descriptor (currently unused) |
| 96 | @param [out] *piLen Descriptor length |
| 97 | @param [out] *ppbData Descriptor data |
| 98 | |
| 99 | @return TRUE if the descriptor was found, FALSE otherwise |
| 100 | */ |
| 101 | BOOL USBGetDescriptor(unsigned short wTypeIndex, unsigned short wLangID, int *piLen, unsigned char **ppbData) |
| 102 | { |
| 103 | unsigned char bType, bIndex; |
| 104 | unsigned char *pab; |
| 105 | int iCurIndex; |
| 106 | |
| 107 | ASSERT(pabDescrip != NULL); |
| 108 | |
| 109 | bType = GET_DESC_TYPE(wTypeIndex); |
| 110 | bIndex = GET_DESC_INDEX(wTypeIndex); |
| 111 | |
| 112 | pab = (unsigned char *)pabDescrip; |
| 113 | iCurIndex = 0; |
| 114 | |
| 115 | while (pab[DESC_bLength] != 0) { |
| 116 | if (pab[DESC_bDescriptorType] == bType) { |
| 117 | if (iCurIndex == bIndex) { |
| 118 | // set data pointer |
| 119 | *ppbData = pab; |
| 120 | // get length from structure |
| 121 | if (bType == DESC_CONFIGURATION) { |
| 122 | // configuration descriptor is an exception, length is at offset 2 and 3 |
| 123 | *piLen = (pab[CONF_DESC_wTotalLength]) | |
| 124 | (pab[CONF_DESC_wTotalLength + 1] << 8); |
| 125 | } else { |
| 126 | // normally length is at offset 0 |
| 127 | *piLen = pab[DESC_bLength]; |
| 128 | } |
| 129 | return TRUE; |
| 130 | } |
| 131 | iCurIndex++; |
| 132 | } |
| 133 | // skip to next descriptor |
| 134 | pab += pab[DESC_bLength]; |
| 135 | } |
| 136 | // nothing found |
| 137 | DBG("Desc %x not found!\n", wTypeIndex); |
| 138 | return FALSE; |
| 139 | } |
| 140 | |
| 141 | |
| 142 | /** |
| 143 | Configures the device according to the specified configuration index and |
| 144 | alternate setting by parsing the installed USB descriptor list. |
| 145 | A configuration index of 0 unconfigures the device. |
| 146 | |
| 147 | @param [in] bConfigIndex Configuration index |
| 148 | @param [in] bAltSetting Alternate setting number |
| 149 | |
| 150 | @todo function always returns TRUE, add stricter checking? |
| 151 | |
| 152 | @return TRUE if successfully configured, FALSE otherwise |
| 153 | */ |
| 154 | static BOOL USBSetConfiguration(unsigned char bConfigIndex, unsigned char bAltSetting) |
| 155 | { |
| 156 | unsigned char *pab; |
| 157 | unsigned char bCurConfig, bCurAltSetting; |
| 158 | unsigned char bEP; |
| 159 | unsigned short wMaxPktSize; |
| 160 | |
| 161 | ASSERT(pabDescrip != NULL); |
| 162 | |
| 163 | if (bConfigIndex == 0) { |
| 164 | // unconfigure device |
| 165 | USBHwConfigDevice(FALSE); |
| 166 | } else { |
| 167 | // configure endpoints for this configuration/altsetting |
| 168 | pab = (unsigned char *)pabDescrip; |
| 169 | bCurConfig = 0xFF; |
| 170 | bCurAltSetting = 0xFF; |
| 171 | |
| 172 | while (pab[DESC_bLength] != 0) { |
| 173 | |
| 174 | switch (pab[DESC_bDescriptorType]) { |
| 175 | |
| 176 | case DESC_CONFIGURATION: |
| 177 | // remember current configuration index |
| 178 | bCurConfig = pab[CONF_DESC_bConfigurationValue]; |
| 179 | break; |
| 180 | |
| 181 | case DESC_INTERFACE: |
| 182 | // remember current alternate setting |
| 183 | bCurAltSetting = pab[INTF_DESC_bAlternateSetting]; |
| 184 | break; |
| 185 | |
| 186 | case DESC_ENDPOINT: |
| 187 | if ((bCurConfig == bConfigIndex) && |
| 188 | (bCurAltSetting == bAltSetting)) { |
| 189 | // endpoint found for desired config and alternate setting |
| 190 | bEP = pab[ENDP_DESC_bEndpointAddress]; |
| 191 | wMaxPktSize = (pab[ENDP_DESC_wMaxPacketSize]) | |
| 192 | (pab[ENDP_DESC_wMaxPacketSize + 1] << 8); |
| 193 | // configure endpoint |
| 194 | USBHwEPConfig(bEP, wMaxPktSize); |
| 195 | } |
| 196 | break; |
| 197 | |
| 198 | default: |
| 199 | break; |
| 200 | } |
| 201 | // skip to next descriptor |
| 202 | pab += pab[DESC_bLength]; |
| 203 | } |
| 204 | |
| 205 | // configure device |
| 206 | USBHwConfigDevice(TRUE); |
| 207 | } |
| 208 | |
| 209 | return TRUE; |
| 210 | } |
| 211 | SetConfigHandler *SetConfig_CallBack = NULL; |
| 212 | void USBHwRegisterSetConfigHandler(SetConfigHandler *handler){ |
| 213 | SetConfig_CallBack = handler; |
| 214 | } |
| 215 | |
| 216 | |
| 217 | /** |
| 218 | Local function to handle a standard device request |
| 219 | |
| 220 | @param [in] pSetup The setup packet |
| 221 | @param [in,out] *piLen Pointer to data length |
| 222 | @param [in,out] ppbData Data buffer. |
| 223 | |
| 224 | @return TRUE if the request was handled successfully |
| 225 | */ |
| 226 | static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData) |
| 227 | { |
| 228 | unsigned char *pbData = *ppbData; |
| 229 | |
| 230 | switch (pSetup->bRequest) { |
| 231 | |
| 232 | case REQ_GET_STATUS: |
| 233 | // bit 0: self-powered |
| 234 | // bit 1: remote wakeup = not supported |
| 235 | pbData[0] = 0; |
| 236 | pbData[1] = 0; |
| 237 | *piLen = 2; |
| 238 | break; |
| 239 | |
| 240 | case REQ_SET_ADDRESS: |
| 241 | USBHwSetAddress(pSetup->wValue); |
| 242 | break; |
| 243 | |
| 244 | case REQ_GET_DESCRIPTOR: |
| 245 | DBG("D%x", pSetup->wValue); |
| 246 | return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData); |
| 247 | |
| 248 | case REQ_GET_CONFIGURATION: |
| 249 | // indicate if we are configured |
| 250 | pbData[0] = bConfiguration; |
| 251 | *piLen = 1; |
| 252 | break; |
| 253 | |
| 254 | case REQ_SET_CONFIGURATION: |
| 255 | if (!USBSetConfiguration(pSetup->wValue & 0xFF, 0)) { |
| 256 | DBG("USBSetConfiguration failed!\n"); |
| 257 | return FALSE; |
| 258 | } |
| 259 | // configuration successful, update current configuration |
| 260 | bConfiguration = pSetup->wValue & 0xFF; |
| 261 | if(SetConfig_CallBack != NULL) |
| 262 | SetConfig_CallBack(); |
| 263 | break; |
| 264 | |
| 265 | case REQ_CLEAR_FEATURE: |
| 266 | case REQ_SET_FEATURE: |
| 267 | if (pSetup->wValue == FEA_REMOTE_WAKEUP) { |
| 268 | // put DEVICE_REMOTE_WAKEUP code here |
| 269 | } |
| 270 | if (pSetup->wValue == FEA_TEST_MODE) { |
| 271 | // put TEST_MODE code here |
| 272 | } |
| 273 | return FALSE; |
| 274 | |
| 275 | case REQ_SET_DESCRIPTOR: |
| 276 | DBG("Device req %d not implemented\n", pSetup->bRequest); |
| 277 | return FALSE; |
| 278 | |
| 279 | default: |
| 280 | DBG("Illegal device req %d\n", pSetup->bRequest); |
| 281 | return FALSE; |
| 282 | } |
| 283 | |
| 284 | return TRUE; |
| 285 | } |
| 286 | |
| 287 | |
| 288 | /** |
| 289 | Local function to handle a standard interface request |
| 290 | |
| 291 | @param [in] pSetup The setup packet |
| 292 | @param [in,out] *piLen Pointer to data length |
| 293 | @param [in] ppbData Data buffer. |
| 294 | |
| 295 | @return TRUE if the request was handled successfully |
| 296 | */ |
| 297 | static BOOL HandleStdInterfaceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData) |
| 298 | { |
| 299 | unsigned char *pbData = *ppbData; |
| 300 | |
| 301 | switch (pSetup->bRequest) { |
| 302 | |
| 303 | case REQ_GET_STATUS: |
| 304 | // no bits specified |
| 305 | pbData[0] = 0; |
| 306 | pbData[1] = 0; |
| 307 | *piLen = 2; |
| 308 | break; |
| 309 | |
| 310 | case REQ_CLEAR_FEATURE: |
| 311 | case REQ_SET_FEATURE: |
| 312 | // not defined for interface |
| 313 | return FALSE; |
| 314 | |
| 315 | case REQ_GET_INTERFACE: // TODO use bNumInterfaces |
| 316 | // there is only one interface, return n-1 (= 0) |
| 317 | pbData[0] = 0; |
| 318 | *piLen = 1; |
| 319 | break; |
| 320 | |
| 321 | case REQ_SET_INTERFACE: // TODO use bNumInterfaces |
| 322 | // there is only one interface (= 0) |
| 323 | if (pSetup->wValue != 0) { |
| 324 | return FALSE; |
| 325 | } |
| 326 | *piLen = 0; |
| 327 | break; |
| 328 | |
| 329 | default: |
| 330 | DBG("Illegal interface req %d\n", pSetup->bRequest); |
| 331 | return FALSE; |
| 332 | } |
| 333 | |
| 334 | return TRUE; |
| 335 | } |
| 336 | |
| 337 | |
| 338 | /** |
| 339 | Local function to handle a standard endpoint request |
| 340 | |
| 341 | @param [in] pSetup The setup packet |
| 342 | @param [in,out] *piLen Pointer to data length |
| 343 | @param [in] ppbData Data buffer. |
| 344 | |
| 345 | @return TRUE if the request was handled successfully |
| 346 | */ |
| 347 | static BOOL HandleStdEndPointReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData) |
| 348 | { |
| 349 | unsigned char *pbData = *ppbData; |
| 350 | |
| 351 | switch (pSetup->bRequest) { |
| 352 | case REQ_GET_STATUS: |
| 353 | // bit 0 = endpointed halted or not |
| 354 | pbData[0] = (USBHwEPGetStatus(pSetup->wIndex) & EP_STATUS_STALLED) ? 1 : 0; |
| 355 | pbData[1] = 0; |
| 356 | *piLen = 2; |
| 357 | break; |
| 358 | |
| 359 | case REQ_CLEAR_FEATURE: |
| 360 | if (pSetup->wValue == FEA_ENDPOINT_HALT) { |
| 361 | // clear HALT by unstalling |
| 362 | USBHwEPStall(pSetup->wIndex, FALSE); |
| 363 | break; |
| 364 | } |
| 365 | // only ENDPOINT_HALT defined for endpoints |
| 366 | return FALSE; |
| 367 | |
| 368 | case REQ_SET_FEATURE: |
| 369 | if (pSetup->wValue == FEA_ENDPOINT_HALT) { |
| 370 | // set HALT by stalling |
| 371 | USBHwEPStall(pSetup->wIndex, TRUE); |
| 372 | break; |
| 373 | } |
| 374 | // only ENDPOINT_HALT defined for endpoints |
| 375 | return FALSE; |
| 376 | |
| 377 | case REQ_SYNCH_FRAME: |
| 378 | DBG("EP req %d not implemented\n", pSetup->bRequest); |
| 379 | return FALSE; |
| 380 | |
| 381 | default: |
| 382 | DBG("Illegal EP req %d\n", pSetup->bRequest); |
| 383 | return FALSE; |
| 384 | } |
| 385 | |
| 386 | return TRUE; |
| 387 | } |
| 388 | |
| 389 | |
| 390 | /** |
| 391 | Default handler for standard ('chapter 9') requests |
| 392 | |
| 393 | If a custom request handler was installed, this handler is called first. |
| 394 | |
| 395 | @param [in] pSetup The setup packet |
| 396 | @param [in,out] *piLen Pointer to data length |
| 397 | @param [in] ppbData Data buffer. |
| 398 | |
| 399 | @return TRUE if the request was handled successfully |
| 400 | */ |
| 401 | BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData) |
| 402 | { |
| 403 | // try the custom request handler first |
| 404 | if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq(pSetup, piLen, ppbData)) { |
| 405 | return TRUE; |
| 406 | } |
| 407 | |
| 408 | switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) { |
| 409 | case REQTYPE_RECIP_DEVICE: |
| 410 | return HandleStdDeviceReq(pSetup, piLen, ppbData); |
| 411 | case REQTYPE_RECIP_INTERFACE: |
| 412 | return HandleStdInterfaceReq(pSetup, piLen, ppbData); |
| 413 | case REQTYPE_RECIP_ENDPOINT: |
| 414 | return HandleStdEndPointReq(pSetup, piLen, ppbData); |
| 415 | default: |
| 416 | return FALSE; |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | |
| 421 | /** |
| 422 | Registers a callback for custom device requests |
| 423 | |
| 424 | In USBHandleStandardRequest, the custom request handler gets a first |
| 425 | chance at handling the request before it is handed over to the 'chapter 9' |
| 426 | request handler. |
| 427 | |
| 428 | This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR |
| 429 | request is sent to an interface, which is not covered by the 'chapter 9' |
| 430 | specification. |
| 431 | |
| 432 | @param [in] pfnHandler Callback function pointer |
| 433 | */ |
| 434 | void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler) |
| 435 | { |
| 436 | pfnHandleCustomReq = pfnHandler; |
| 437 | } |
| 438 | |