USB device stack, with KL25Z fixes for USB 3.0 hosts and sleep/resume interrupt handling

Dependents:   frdm_Slider_Keyboard idd_hw2_figlax_PanType idd_hw2_appachu_finger_chording idd_hw3_AngieWangAntonioDeLimaFernandesDanielLim_BladeSymphony ... more

Fork of USBDevice by mbed official

This is an overhauled version of the standard mbed USB device-side driver library, with bug fixes for KL25Z devices. It greatly improves reliability and stability of USB on the KL25Z, especially with devices using multiple endpoints concurrently.

I've had some nagging problems with the base mbed implementation for a long time, manifesting as occasional random disconnects that required rebooting the device. Recently (late 2015), I started implementing a USB device on the KL25Z that used multiple endpoints, and suddenly the nagging, occasional problems turned into frequent and predictable crashes. This forced me to delve into the USB stack and figure out what was really going on. Happily, the frequent crashes made it possible to track down and fix the problems. This new version is working very reliably in my testing - the random disconnects seem completely eradicated, even under very stressful conditions for the device.

Summary

  • Overall stability improvements
  • USB 3.0 host support
  • Stalled endpoint fixes
  • Sleep/resume notifications
  • Smaller memory footprint
  • General code cleanup

Update - 2/15/2016

My recent fixes introduced a new problem that made the initial connection fail most of the time on certain hosts. It's not clear if the common thread was a particular type of motherboard or USB chip set, or a specific version of Windows, or what, but several people ran into it. We tracked the problem down to the "stall" fixes in the earlier updates, which we now know weren't quite the right fixes after all. The latest update (2/15/2016) fixes this. It has new and improved "unstall" handling that so far works well with diverse hosts.

Race conditions and overall stability

The base mbed KL25Z implementation has a lot of problems with "race conditions" - timing problems that can happen when hardware interrupts occur at inopportune moments. The library shares a bunch of static variable data between interrupt handler context and regular application context. This isn't automatically a bad thing, but it does require careful coordination to make sure that the interrupt handler doesn't corrupt data that the other code was in the middle of updating when an interrupt occurs. The base mbed code, though, doesn't do any of the necessary coordination. This makes it kind of amazing that the base code worked at all for anyone, but I guess the interrupt rate is low enough in most applications that the glitch rate was below anyone's threshold to seriously investigate.

This overhaul adds the necessary coordination for the interrupt handlers to protect against these data corruptions. I think it's very solid now, and hopefully entirely free of the numerous race conditions in the old code. It's always hard to be certain that you've fixed every possible bug like this because they strike (effectively) at random, but I'm pretty confident: my test application was reliably able to trigger glitches in the base code in a matter of minutes, but the same application (with the overhauled library) now runs for days on end without dropping the connection.

Stalled endpoint fixes

USB has a standard way of handling communications errors called a "stall", which basically puts the connection into an error mode to let both sides know that they need to reset their internal states and sync up again. The original mbed version of the USB device library doesn't seem to have the necessary code to recover from this condition properly. The KL25Z hardware does some of the work, but it also seems to require the software to take some steps to "un-stall" the connection. (I keep saying "seems to" because the hardware reference material is very sketchy about all of this. Most of what I've figured out is from observing the device in action with a Windows host.) This new version adds code to do the necessary re-syncing and get the connection going again, automatically, and transparently to the user.

USB 3.0 Hosts

The original mbed code sometimes didn't work when connecting to hosts with USB 3.0 ports. This didn't affect every host, but it affected many of them. The common element seemed to be the Intel Haswell chip set on the host, but there may be other chip sets affected as well. In any case, the problem affected many PCs from the Windows 7 and 8 generation, as well as many Macs. It was possible to work around the problem by avoiding USB 3.0 ports - you could use a USB 2 port on the host, or plug a USB 2 hub between the host and device. But I wanted to just fix the problem and eliminate the need for such workarounds. This modified version of the library has such a fix, which so far has worked for everyone who's tried.

Sleep/resume notifications

This modified version also contains an innocuous change to the KL25Z USB HAL code to handle sleep and resume interrupts with calls to suspendStateChanged(). The original KL25Z code omitted these calls (and in fact didn't even enable the interrupts), but I think this was an unintentional oversight - the notifier function is part of the generic API, and other supported boards all implement it. I use this feature in my own application so that I can distinguish sleep mode from actual disconnects and handle the two conditions correctly.

Smaller memory footprint

The base mbed version of the code allocates twice as much memory for USB buffers as it really needed to. It looks like the original developers intended to implement the KL25Z USB hardware's built-in double-buffering mechanism, but they ultimately abandoned that effort. But they left in the double memory allocation. This version removes that and allocates only what's actually needed. The USB buffers aren't that big (128 bytes per endpoint), so this doesn't save a ton of memory, but even a little memory is pretty precious on this machine given that it only has 16K.

(I did look into adding the double-buffering support that the original developers abandoned, but after some experimentation I decided they were right to skip it. It just doesn't seem to mesh well with the design of the rest of the mbed USB code. I think it would take a major rewrite to make it work, and it doesn't seem worth the effort given that most applications don't need it - it would only benefit applications that are moving so much data through USB that they're pushing the limits of the CPU. And even for those, I think it would be a lot simpler to build a purely software-based buffer rotation mechanism.)

General code cleanup

The KL25Z HAL code in this version has greatly expanded commentary and a lot of general cleanup. Some of the hardware constants were given the wrong symbolic names (e.g., EVEN and ODD were reversed), and many were just missing (written as hard-coded numbers without explanation). I fixed the misnomers and added symbolic names for formerly anonymous numbers. Hopefully the next person who has to overhaul this code will at least have an easier time understanding what I thought I was doing!

Committer:
mjr
Date:
Wed Apr 27 01:50:32 2016 +0000
Revision:
50:946bc763c068
Parent:
49:03527ce6840e
Child:
51:666cc4fedd3f
Error handling in the main code with the long explanation

Who changed what in which revision?

UserRevisionLine numberNew contents of line
samux 1:80ab0d068708 1 /* Copyright (c) 2010-2011 mbed.org, MIT License
samux 1:80ab0d068708 2 *
samux 1:80ab0d068708 3 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
samux 1:80ab0d068708 4 * and associated documentation files (the "Software"), to deal in the Software without
samux 1:80ab0d068708 5 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
samux 1:80ab0d068708 6 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
samux 1:80ab0d068708 7 * Software is furnished to do so, subject to the following conditions:
samux 1:80ab0d068708 8 *
samux 1:80ab0d068708 9 * The above copyright notice and this permission notice shall be included in all copies or
samux 1:80ab0d068708 10 * substantial portions of the Software.
samux 1:80ab0d068708 11 *
samux 1:80ab0d068708 12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
samux 1:80ab0d068708 13 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
samux 1:80ab0d068708 14 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
samux 1:80ab0d068708 15 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
samux 1:80ab0d068708 16 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
samux 1:80ab0d068708 17 */
samux 1:80ab0d068708 18
samux 1:80ab0d068708 19 #include "stdint.h"
samux 1:80ab0d068708 20
samux 1:80ab0d068708 21 #include "USBEndpoints.h"
samux 1:80ab0d068708 22 #include "USBDevice.h"
samux 1:80ab0d068708 23 #include "USBDescriptor.h"
samux 1:80ab0d068708 24
mjr 50:946bc763c068 25 //#define DEBUG_WITH_PRINTF
mjr 50:946bc763c068 26 #ifdef DEBUG_WITH_PRINTF
mjr 50:946bc763c068 27 // debug printf; does a regular printf() in debug mode, nothing in
mjr 50:946bc763c068 28 // normal mode. Note that many of our routines are called in ISR
mjr 50:946bc763c068 29 // context, so printf should really never be used here. But in
mjr 50:946bc763c068 30 // practice we can get away with it enough that it can be helpful
mjr 50:946bc763c068 31 // as a limited debugging tool.
mjr 37:c5ac4ccf6597 32 #define printd(fmt, ...) printf(fmt, __VA_ARGS__)
mjr 37:c5ac4ccf6597 33 #else
mjr 37:c5ac4ccf6597 34 #define printd(fmt, ...)
mjr 37:c5ac4ccf6597 35 #endif
samux 1:80ab0d068708 36
mjr 50:946bc763c068 37 // Makeshift HAL debug instrumentation. This is a safer and better
mjr 50:946bc763c068 38 // alternative to printf() that gathers event information in a
mjr 50:946bc763c068 39 // circular buffer for later useoutside of interrupt context, such
mjr 50:946bc763c068 40 // as printf() display at intervals in the main program loop.
mjr 50:946bc763c068 41 //
mjr 50:946bc763c068 42 // Timing is critical to USB, so debug instrumentation is inherently
mjr 50:946bc763c068 43 // problematic in that it can affect the timing and thereby change
mjr 50:946bc763c068 44 // the behavior of what we're trying to debug. Small timing changes
mjr 50:946bc763c068 45 // can create new errors that wouldn't be there otherwise, or even
mjr 50:946bc763c068 46 // accidentally fix the bug were trying to find (e.g., by changing
mjr 50:946bc763c068 47 // the timing enough to avoid a race condition). To minimize these
mjr 50:946bc763c068 48 // effects, we use a small buffer and very terse event codes -
mjr 50:946bc763c068 49 // generally one character per event. That makes for a cryptic
mjr 50:946bc763c068 50 // debug log, but it results in almost zero timing effects, allowing
mjr 50:946bc763c068 51 // us to see a more faithful version of the subject program.
mjr 50:946bc763c068 52 //
mjr 50:946bc763c068 53 // NB: Implemented only for KL25Z.
mjr 50:946bc763c068 54 //#define DEBUG_WITH_EVENTS
mjr 50:946bc763c068 55 #ifdef DEBUG_WITH_EVENTS
mjr 50:946bc763c068 56 extern void HAL_DEBUG_EVENT(char c);
mjr 50:946bc763c068 57 extern void HAL_DEBUG_EVENT(char a, char b);
mjr 50:946bc763c068 58 extern void HAL_DEBUG_EVENT(char a, char b, char c);
mjr 50:946bc763c068 59 extern void HAL_DEBUG_EVENT(const char *s);
mjr 50:946bc763c068 60 extern void HAL_DEBUG_EVENTF(const char *f, ...);
mjr 50:946bc763c068 61 #else
mjr 50:946bc763c068 62 #define HAL_DEBUG_EVENT(...)
mjr 50:946bc763c068 63 #define HAL_DEBUG_EVENTF(...)
mjr 50:946bc763c068 64 #endif
mjr 50:946bc763c068 65
samux 1:80ab0d068708 66 /* Device status */
samux 1:80ab0d068708 67 #define DEVICE_STATUS_SELF_POWERED (1U<<0)
samux 1:80ab0d068708 68 #define DEVICE_STATUS_REMOTE_WAKEUP (1U<<1)
samux 1:80ab0d068708 69
samux 1:80ab0d068708 70 /* Endpoint status */
samux 1:80ab0d068708 71 #define ENDPOINT_STATUS_HALT (1U<<0)
samux 1:80ab0d068708 72
samux 1:80ab0d068708 73 /* Standard feature selectors */
samux 1:80ab0d068708 74 #define DEVICE_REMOTE_WAKEUP (1)
samux 1:80ab0d068708 75 #define ENDPOINT_HALT (0)
samux 1:80ab0d068708 76
samux 1:80ab0d068708 77 /* Macro to convert wIndex endpoint number to physical endpoint number */
samux 1:80ab0d068708 78 #define WINDEX_TO_PHYSICAL(endpoint) (((endpoint & 0x0f) << 1) + \
samux 1:80ab0d068708 79 ((endpoint & 0x80) ? 1 : 0))
samux 1:80ab0d068708 80
samux 1:80ab0d068708 81 bool USBDevice::requestGetDescriptor(void)
samux 1:80ab0d068708 82 {
samux 1:80ab0d068708 83 bool success = false;
mjr 37:c5ac4ccf6597 84 printd("get descr: type: %d\r\n", DESCRIPTOR_TYPE(transfer.setup.wValue));
samux 1:80ab0d068708 85 switch (DESCRIPTOR_TYPE(transfer.setup.wValue))
samux 1:80ab0d068708 86 {
mjr 50:946bc763c068 87 case DEVICE_DESCRIPTOR:
mjr 50:946bc763c068 88 if (deviceDesc() != NULL)
mjr 50:946bc763c068 89 {
mjr 50:946bc763c068 90 if ((deviceDesc()[0] == DEVICE_DESCRIPTOR_LENGTH) \
mjr 50:946bc763c068 91 && (deviceDesc()[1] == DEVICE_DESCRIPTOR))
samux 1:80ab0d068708 92 {
mjr 50:946bc763c068 93 printd("device descr\r\n");
mjr 50:946bc763c068 94 transfer.remaining = DEVICE_DESCRIPTOR_LENGTH;
mjr 50:946bc763c068 95 transfer.ptr = deviceDesc();
mjr 50:946bc763c068 96 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 97 success = true;
mjr 50:946bc763c068 98 }
mjr 50:946bc763c068 99 }
mjr 50:946bc763c068 100 break;
samux 1:80ab0d068708 101
mjr 50:946bc763c068 102 case CONFIGURATION_DESCRIPTOR:
mjr 50:946bc763c068 103 if (configurationDesc() != NULL)
mjr 50:946bc763c068 104 {
mjr 50:946bc763c068 105 if ((configurationDesc()[0] == CONFIGURATION_DESCRIPTOR_LENGTH)
mjr 50:946bc763c068 106 && (configurationDesc()[1] == CONFIGURATION_DESCRIPTOR))
samux 1:80ab0d068708 107 {
mjr 50:946bc763c068 108 printd("conf descr request\r\n");
mjr 50:946bc763c068 109
mjr 50:946bc763c068 110 /* Get wTotalLength */
mjr 50:946bc763c068 111 transfer.remaining = configurationDesc()[2] | (configurationDesc()[3] << 8);
mjr 50:946bc763c068 112 transfer.ptr = configurationDesc();
mjr 50:946bc763c068 113 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 114 success = true;
samux 1:80ab0d068708 115 }
mjr 50:946bc763c068 116 }
mjr 50:946bc763c068 117 break;
mjr 50:946bc763c068 118
mjr 50:946bc763c068 119 case STRING_DESCRIPTOR:
mjr 50:946bc763c068 120 printd("str descriptor\r\n");
mjr 50:946bc763c068 121 switch (DESCRIPTOR_INDEX(transfer.setup.wValue))
mjr 50:946bc763c068 122 {
mjr 50:946bc763c068 123 case STRING_OFFSET_LANGID:
mjr 50:946bc763c068 124 printd("1\r\n");
mjr 50:946bc763c068 125 transfer.remaining = stringLangidDesc()[0];
mjr 50:946bc763c068 126 transfer.ptr = stringLangidDesc();
mjr 50:946bc763c068 127 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 128 success = true;
mjr 37:c5ac4ccf6597 129 break;
mjr 37:c5ac4ccf6597 130
mjr 50:946bc763c068 131 case STRING_OFFSET_IMANUFACTURER:
mjr 50:946bc763c068 132 printd("2\r\n");
mjr 50:946bc763c068 133 transfer.remaining = stringImanufacturerDesc()[0];
mjr 50:946bc763c068 134 transfer.ptr = stringImanufacturerDesc();
mjr 50:946bc763c068 135 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 136 success = true;
mjr 50:946bc763c068 137 break;
mjr 50:946bc763c068 138
mjr 50:946bc763c068 139 case STRING_OFFSET_IPRODUCT:
mjr 50:946bc763c068 140 printd("3\r\n");
mjr 50:946bc763c068 141 transfer.remaining = stringIproductDesc()[0];
mjr 50:946bc763c068 142 transfer.ptr = stringIproductDesc();
mjr 50:946bc763c068 143 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 144 success = true;
mjr 50:946bc763c068 145 break;
mjr 50:946bc763c068 146
mjr 50:946bc763c068 147 case STRING_OFFSET_ISERIAL:
mjr 50:946bc763c068 148 printd("4\r\n");
mjr 50:946bc763c068 149 transfer.remaining = stringIserialDesc()[0];
mjr 50:946bc763c068 150 transfer.ptr = stringIserialDesc();
mjr 50:946bc763c068 151 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 152 success = true;
samux 1:80ab0d068708 153 break;
mjr 37:c5ac4ccf6597 154
mjr 50:946bc763c068 155 case STRING_OFFSET_ICONFIGURATION:
mjr 50:946bc763c068 156 printd("5\r\n");
mjr 50:946bc763c068 157 transfer.remaining = stringIConfigurationDesc()[0];
mjr 50:946bc763c068 158 transfer.ptr = stringIConfigurationDesc();
mjr 50:946bc763c068 159 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 160 success = true;
mjr 50:946bc763c068 161 break;
mjr 50:946bc763c068 162
mjr 50:946bc763c068 163 case STRING_OFFSET_IINTERFACE:
mjr 50:946bc763c068 164 printd("6\r\n");
mjr 50:946bc763c068 165 transfer.remaining = stringIinterfaceDesc()[0];
mjr 50:946bc763c068 166 transfer.ptr = stringIinterfaceDesc();
mjr 50:946bc763c068 167 transfer.direction = DEVICE_TO_HOST;
mjr 50:946bc763c068 168 success = true;
samux 1:80ab0d068708 169 break;
mjr 50:946bc763c068 170 }
mjr 50:946bc763c068 171 break;
mjr 50:946bc763c068 172
mjr 50:946bc763c068 173 case INTERFACE_DESCRIPTOR:
mjr 50:946bc763c068 174 printd("interface descr\r\n");
mjr 50:946bc763c068 175 break;
mjr 50:946bc763c068 176
mjr 50:946bc763c068 177 case ENDPOINT_DESCRIPTOR:
mjr 50:946bc763c068 178 /* TODO: Support is optional, not implemented here */
mjr 50:946bc763c068 179 printd("endpoint descr\r\n");
mjr 50:946bc763c068 180 break;
mjr 50:946bc763c068 181
mjr 50:946bc763c068 182 default:
mjr 50:946bc763c068 183 printd("ERROR - unknown descriptor type in GET DESCRIPTOR\r\n");
mjr 50:946bc763c068 184 break;
samux 1:80ab0d068708 185 }
samux 1:80ab0d068708 186
samux 1:80ab0d068708 187 return success;
samux 1:80ab0d068708 188 }
samux 1:80ab0d068708 189
samux 1:80ab0d068708 190 void USBDevice::decodeSetupPacket(uint8_t *data, SETUP_PACKET *packet)
samux 1:80ab0d068708 191 {
samux 1:80ab0d068708 192 /* Fill in the elements of a SETUP_PACKET structure from raw data */
samux 1:80ab0d068708 193 packet->bmRequestType.dataTransferDirection = (data[0] & 0x80) >> 7;
samux 1:80ab0d068708 194 packet->bmRequestType.Type = (data[0] & 0x60) >> 5;
samux 1:80ab0d068708 195 packet->bmRequestType.Recipient = data[0] & 0x1f;
samux 1:80ab0d068708 196 packet->bRequest = data[1];
samux 1:80ab0d068708 197 packet->wValue = (data[2] | (uint16_t)data[3] << 8);
samux 1:80ab0d068708 198 packet->wIndex = (data[4] | (uint16_t)data[5] << 8);
samux 1:80ab0d068708 199 packet->wLength = (data[6] | (uint16_t)data[7] << 8);
samux 1:80ab0d068708 200 }
samux 1:80ab0d068708 201
samux 1:80ab0d068708 202
samux 1:80ab0d068708 203 bool USBDevice::controlOut(void)
samux 1:80ab0d068708 204 {
samux 1:80ab0d068708 205 /* Control transfer data OUT stage */
samux 1:80ab0d068708 206 uint8_t buffer[MAX_PACKET_SIZE_EP0];
samux 1:80ab0d068708 207 uint32_t packetSize;
samux 1:80ab0d068708 208
samux 1:80ab0d068708 209 /* Check we should be transferring data OUT */
samux 1:80ab0d068708 210 if (transfer.direction != HOST_TO_DEVICE)
samux 1:80ab0d068708 211 {
mjr 30:2f8f4c3822a1 212 #if defined(TARGET_KL25Z) | defined(TARGET_KL46Z) | defined(TARGET_K20D5M) | defined(TARGET_K64F)
mjr 29:b502c11a3dff 213 /*
mjr 29:b502c11a3dff 214 * We seem to have a pending device-to-host transfer. The host must have
mjr 29:b502c11a3dff 215 * sent a new control request without waiting for us to finish processing
mjr 29:b502c11a3dff 216 * the previous one. This appears to happen when we're connected to certain
mjr 50:946bc763c068 217 * USB 3.0 host chip sets. Do a zero-length send and return failure to tell
mjr 50:946bc763c068 218 * the host we're not ready for the new request. That'll make it resend.
mjr 29:b502c11a3dff 219 */
mjr 50:946bc763c068 220 EP0write(NULL, 0);
mjr 31:81f57ea86f8f 221
mjr 36:20bb47609697 222 /* execute our pending transfer */
mjr 31:81f57ea86f8f 223 controlIn();
mjr 31:81f57ea86f8f 224
mjr 48:b225d025ca1d 225 /* indicate failure */
mjr 48:b225d025ca1d 226 return false;
mjr 29:b502c11a3dff 227 #else
mjr 31:81f57ea86f8f 228 /* for other platforms, count on the HAL to handle this case */
mjr 31:81f57ea86f8f 229 return false;
mjr 29:b502c11a3dff 230 #endif
samux 1:80ab0d068708 231 }
samux 1:80ab0d068708 232
samux 1:80ab0d068708 233 /* Read from endpoint */
samux 1:80ab0d068708 234 packetSize = EP0getReadResult(buffer);
samux 1:80ab0d068708 235
samux 1:80ab0d068708 236 /* Check if transfer size is valid */
samux 1:80ab0d068708 237 if (packetSize > transfer.remaining)
samux 1:80ab0d068708 238 {
samux 1:80ab0d068708 239 /* Too big */
samux 1:80ab0d068708 240 return false;
samux 1:80ab0d068708 241 }
samux 1:80ab0d068708 242
samux 1:80ab0d068708 243 /* Update transfer */
samux 1:80ab0d068708 244 transfer.ptr += packetSize;
samux 1:80ab0d068708 245 transfer.remaining -= packetSize;
samux 1:80ab0d068708 246
samux 1:80ab0d068708 247 /* Check if transfer has completed */
samux 1:80ab0d068708 248 if (transfer.remaining == 0)
samux 1:80ab0d068708 249 {
samux 1:80ab0d068708 250 /* Transfer completed */
samux 1:80ab0d068708 251 if (transfer.notify)
samux 1:80ab0d068708 252 {
samux 1:80ab0d068708 253 /* Notify class layer. */
samux 1:80ab0d068708 254 USBCallback_requestCompleted(buffer, packetSize);
samux 1:80ab0d068708 255 transfer.notify = false;
samux 1:80ab0d068708 256 }
mjr 50:946bc763c068 257
samux 1:80ab0d068708 258 /* Status stage */
samux 1:80ab0d068708 259 EP0write(NULL, 0);
samux 1:80ab0d068708 260 }
samux 1:80ab0d068708 261 else
samux 1:80ab0d068708 262 {
samux 1:80ab0d068708 263 EP0read();
samux 1:80ab0d068708 264 }
samux 1:80ab0d068708 265
samux 1:80ab0d068708 266 return true;
samux 1:80ab0d068708 267 }
samux 1:80ab0d068708 268
samux 1:80ab0d068708 269 bool USBDevice::controlIn(void)
samux 1:80ab0d068708 270 {
samux 1:80ab0d068708 271 /* Control transfer data IN stage */
samux 1:80ab0d068708 272 uint32_t packetSize;
samux 1:80ab0d068708 273
samux 1:80ab0d068708 274 /* Check if transfer has completed (status stage transactions */
samux 1:80ab0d068708 275 /* also have transfer.remaining == 0) */
samux 1:80ab0d068708 276 if (transfer.remaining == 0)
samux 1:80ab0d068708 277 {
samux 1:80ab0d068708 278 if (transfer.zlp)
samux 1:80ab0d068708 279 {
samux 1:80ab0d068708 280 /* Send zero length packet */
samux 1:80ab0d068708 281 EP0write(NULL, 0);
samux 1:80ab0d068708 282 transfer.zlp = false;
samux 1:80ab0d068708 283 }
samux 1:80ab0d068708 284
samux 1:80ab0d068708 285 /* Transfer completed */
samux 1:80ab0d068708 286 if (transfer.notify)
samux 1:80ab0d068708 287 {
samux 1:80ab0d068708 288 /* Notify class layer. */
samux 1:80ab0d068708 289 USBCallback_requestCompleted(NULL, 0);
samux 1:80ab0d068708 290 transfer.notify = false;
samux 1:80ab0d068708 291 }
samux 1:80ab0d068708 292
mjr 50:946bc763c068 293 //$$$ EP0read();
samux 8:335f2506f422 294 EP0readStage();
samux 1:80ab0d068708 295
samux 1:80ab0d068708 296 /* Completed */
mjr 50:946bc763c068 297 //$$$transfer.direction = HOST_TO_DEVICE;
samux 1:80ab0d068708 298 return true;
samux 1:80ab0d068708 299 }
samux 1:80ab0d068708 300
samux 1:80ab0d068708 301 /* Check we should be transferring data IN */
samux 1:80ab0d068708 302 if (transfer.direction != DEVICE_TO_HOST)
samux 1:80ab0d068708 303 {
samux 1:80ab0d068708 304 return false;
samux 1:80ab0d068708 305 }
samux 1:80ab0d068708 306
samux 1:80ab0d068708 307 packetSize = transfer.remaining;
samux 1:80ab0d068708 308
samux 1:80ab0d068708 309 if (packetSize > MAX_PACKET_SIZE_EP0)
samux 1:80ab0d068708 310 {
samux 1:80ab0d068708 311 packetSize = MAX_PACKET_SIZE_EP0;
samux 1:80ab0d068708 312 }
samux 1:80ab0d068708 313
samux 1:80ab0d068708 314 /* Write to endpoint */
samux 1:80ab0d068708 315 EP0write(transfer.ptr, packetSize);
samux 1:80ab0d068708 316
samux 1:80ab0d068708 317 /* Update transfer */
samux 1:80ab0d068708 318 transfer.ptr += packetSize;
samux 1:80ab0d068708 319 transfer.remaining -= packetSize;
mjr 36:20bb47609697 320
mjr 36:20bb47609697 321 /* are we done? */
mjr 50:946bc763c068 322 //$$$ if (transfer.remaining == 0)
mjr 50:946bc763c068 323 //$$$ transfer.direction = HOST_TO_DEVICE;
samux 1:80ab0d068708 324
mjr 36:20bb47609697 325 /* success */
samux 1:80ab0d068708 326 return true;
samux 1:80ab0d068708 327 }
samux 1:80ab0d068708 328
samux 1:80ab0d068708 329 bool USBDevice::requestSetAddress(void)
samux 1:80ab0d068708 330 {
samux 1:80ab0d068708 331 /* Set the device address */
samux 1:80ab0d068708 332 setAddress(transfer.setup.wValue);
samux 1:80ab0d068708 333
samux 1:80ab0d068708 334 if (transfer.setup.wValue == 0)
samux 1:80ab0d068708 335 {
mjr 50:946bc763c068 336 setDeviceState(DEFAULT);
samux 1:80ab0d068708 337 }
samux 1:80ab0d068708 338 else
samux 1:80ab0d068708 339 {
mjr 50:946bc763c068 340 setDeviceState(ADDRESS);
samux 1:80ab0d068708 341 }
samux 1:80ab0d068708 342
samux 1:80ab0d068708 343 return true;
samux 1:80ab0d068708 344 }
samux 1:80ab0d068708 345
samux 1:80ab0d068708 346 bool USBDevice::requestSetConfiguration(void)
samux 1:80ab0d068708 347 {
mjr 50:946bc763c068 348 /* Set the device configuration */
samux 1:80ab0d068708 349 device.configuration = transfer.setup.wValue;
samux 1:80ab0d068708 350 if (device.configuration == 0)
samux 1:80ab0d068708 351 {
samux 1:80ab0d068708 352 /* Not configured */
samux 1:80ab0d068708 353 unconfigureDevice();
mjr 50:946bc763c068 354 setDeviceState(ADDRESS);
samux 1:80ab0d068708 355 }
samux 1:80ab0d068708 356 else
samux 1:80ab0d068708 357 {
samux 1:80ab0d068708 358 if (USBCallback_setConfiguration(device.configuration))
samux 1:80ab0d068708 359 {
samux 1:80ab0d068708 360 /* Valid configuration */
samux 1:80ab0d068708 361 configureDevice();
mjr 50:946bc763c068 362 setDeviceState(CONFIGURED);
samux 1:80ab0d068708 363 }
samux 1:80ab0d068708 364 else
samux 1:80ab0d068708 365 {
samux 1:80ab0d068708 366 return false;
samux 1:80ab0d068708 367 }
samux 1:80ab0d068708 368 }
samux 1:80ab0d068708 369
samux 1:80ab0d068708 370 return true;
samux 1:80ab0d068708 371 }
samux 1:80ab0d068708 372
samux 1:80ab0d068708 373 bool USBDevice::requestGetConfiguration(void)
samux 1:80ab0d068708 374 {
samux 1:80ab0d068708 375 /* Send the device configuration */
samux 1:80ab0d068708 376 transfer.ptr = &device.configuration;
samux 1:80ab0d068708 377 transfer.remaining = sizeof(device.configuration);
samux 1:80ab0d068708 378 transfer.direction = DEVICE_TO_HOST;
samux 1:80ab0d068708 379 return true;
samux 1:80ab0d068708 380 }
samux 1:80ab0d068708 381
samux 1:80ab0d068708 382 bool USBDevice::requestGetInterface(void)
samux 1:80ab0d068708 383 {
samux 1:80ab0d068708 384 /* Return the selected alternate setting for an interface */
samux 1:80ab0d068708 385
samux 1:80ab0d068708 386 if (device.state != CONFIGURED)
samux 1:80ab0d068708 387 {
samux 1:80ab0d068708 388 return false;
samux 1:80ab0d068708 389 }
samux 1:80ab0d068708 390
samux 1:80ab0d068708 391 /* Send the alternate setting */
samux 1:80ab0d068708 392 transfer.setup.wIndex = currentInterface;
samux 1:80ab0d068708 393 transfer.ptr = &currentAlternate;
samux 1:80ab0d068708 394 transfer.remaining = sizeof(currentAlternate);
samux 1:80ab0d068708 395 transfer.direction = DEVICE_TO_HOST;
samux 1:80ab0d068708 396 return true;
samux 1:80ab0d068708 397 }
samux 1:80ab0d068708 398
samux 1:80ab0d068708 399 bool USBDevice::requestSetInterface(void)
samux 1:80ab0d068708 400 {
samux 1:80ab0d068708 401 bool success = false;
samux 1:80ab0d068708 402 if(USBCallback_setInterface(transfer.setup.wIndex, transfer.setup.wValue))
samux 1:80ab0d068708 403 {
samux 1:80ab0d068708 404 success = true;
samux 1:80ab0d068708 405 currentInterface = transfer.setup.wIndex;
mbed_official 25:7c72828865f3 406 currentAlternate = transfer.setup.wValue;
samux 1:80ab0d068708 407 }
samux 1:80ab0d068708 408 return success;
samux 1:80ab0d068708 409 }
samux 1:80ab0d068708 410
samux 1:80ab0d068708 411 bool USBDevice::requestSetFeature()
samux 1:80ab0d068708 412 {
samux 1:80ab0d068708 413 bool success = false;
samux 1:80ab0d068708 414
samux 1:80ab0d068708 415 if (device.state != CONFIGURED)
samux 1:80ab0d068708 416 {
samux 1:80ab0d068708 417 /* Endpoint or interface must be zero */
samux 1:80ab0d068708 418 if (transfer.setup.wIndex != 0)
samux 1:80ab0d068708 419 {
samux 1:80ab0d068708 420 return false;
samux 1:80ab0d068708 421 }
samux 1:80ab0d068708 422 }
samux 1:80ab0d068708 423
samux 1:80ab0d068708 424 switch (transfer.setup.bmRequestType.Recipient)
samux 1:80ab0d068708 425 {
samux 1:80ab0d068708 426 case DEVICE_RECIPIENT:
samux 1:80ab0d068708 427 /* TODO: Remote wakeup feature not supported */
samux 1:80ab0d068708 428 break;
samux 1:80ab0d068708 429 case ENDPOINT_RECIPIENT:
samux 1:80ab0d068708 430 if (transfer.setup.wValue == ENDPOINT_HALT)
samux 1:80ab0d068708 431 {
samux 1:80ab0d068708 432 /* TODO: We should check that the endpoint number is valid */
samux 1:80ab0d068708 433 stallEndpoint(
samux 1:80ab0d068708 434 WINDEX_TO_PHYSICAL(transfer.setup.wIndex));
samux 1:80ab0d068708 435 success = true;
samux 1:80ab0d068708 436 }
samux 1:80ab0d068708 437 break;
samux 1:80ab0d068708 438 default:
samux 1:80ab0d068708 439 break;
samux 1:80ab0d068708 440 }
samux 1:80ab0d068708 441
samux 1:80ab0d068708 442 return success;
samux 1:80ab0d068708 443 }
samux 1:80ab0d068708 444
samux 1:80ab0d068708 445 bool USBDevice::requestClearFeature()
samux 1:80ab0d068708 446 {
samux 1:80ab0d068708 447 bool success = false;
samux 1:80ab0d068708 448
samux 1:80ab0d068708 449 if (device.state != CONFIGURED)
samux 1:80ab0d068708 450 {
samux 1:80ab0d068708 451 /* Endpoint or interface must be zero */
samux 1:80ab0d068708 452 if (transfer.setup.wIndex != 0)
samux 1:80ab0d068708 453 {
samux 1:80ab0d068708 454 return false;
samux 1:80ab0d068708 455 }
samux 1:80ab0d068708 456 }
samux 1:80ab0d068708 457
samux 1:80ab0d068708 458 switch (transfer.setup.bmRequestType.Recipient)
samux 1:80ab0d068708 459 {
samux 1:80ab0d068708 460 case DEVICE_RECIPIENT:
samux 1:80ab0d068708 461 /* TODO: Remote wakeup feature not supported */
samux 1:80ab0d068708 462 break;
samux 1:80ab0d068708 463 case ENDPOINT_RECIPIENT:
samux 1:80ab0d068708 464 /* TODO: We should check that the endpoint number is valid */
samux 1:80ab0d068708 465 if (transfer.setup.wValue == ENDPOINT_HALT)
samux 1:80ab0d068708 466 {
samux 1:80ab0d068708 467 unstallEndpoint( WINDEX_TO_PHYSICAL(transfer.setup.wIndex));
samux 1:80ab0d068708 468 success = true;
samux 1:80ab0d068708 469 }
samux 1:80ab0d068708 470 break;
samux 1:80ab0d068708 471 default:
samux 1:80ab0d068708 472 break;
samux 1:80ab0d068708 473 }
samux 1:80ab0d068708 474
samux 1:80ab0d068708 475 return success;
samux 1:80ab0d068708 476 }
samux 1:80ab0d068708 477
samux 1:80ab0d068708 478 bool USBDevice::requestGetStatus(void)
samux 1:80ab0d068708 479 {
samux 1:80ab0d068708 480 static uint16_t status;
samux 1:80ab0d068708 481 bool success = false;
samux 1:80ab0d068708 482
samux 1:80ab0d068708 483 if (device.state != CONFIGURED)
samux 1:80ab0d068708 484 {
samux 1:80ab0d068708 485 /* Endpoint or interface must be zero */
samux 1:80ab0d068708 486 if (transfer.setup.wIndex != 0)
samux 1:80ab0d068708 487 return false;
samux 1:80ab0d068708 488 }
samux 1:80ab0d068708 489
samux 1:80ab0d068708 490 switch (transfer.setup.bmRequestType.Recipient)
samux 1:80ab0d068708 491 {
mjr 50:946bc763c068 492 case DEVICE_RECIPIENT:
mjr 50:946bc763c068 493 /* TODO: Currently only supports self powered devices */
mjr 50:946bc763c068 494 status = DEVICE_STATUS_SELF_POWERED;
mjr 50:946bc763c068 495 success = true;
mjr 50:946bc763c068 496 break;
mjr 50:946bc763c068 497
mjr 50:946bc763c068 498 case INTERFACE_RECIPIENT:
mjr 50:946bc763c068 499 status = 0;
mjr 50:946bc763c068 500 success = true;
mjr 50:946bc763c068 501 break;
mjr 50:946bc763c068 502
mjr 50:946bc763c068 503 case ENDPOINT_RECIPIENT:
mjr 50:946bc763c068 504 /* TODO: We should check that the endpoint number is valid */
mjr 50:946bc763c068 505 if (getEndpointStallState(WINDEX_TO_PHYSICAL(transfer.setup.wIndex)))
mjr 50:946bc763c068 506 {
mjr 50:946bc763c068 507 status = ENDPOINT_STATUS_HALT;
mjr 50:946bc763c068 508 }
mjr 50:946bc763c068 509 else
mjr 50:946bc763c068 510 {
samux 1:80ab0d068708 511 status = 0;
mjr 50:946bc763c068 512 }
mjr 50:946bc763c068 513 success = true;
mjr 50:946bc763c068 514 break;
mjr 50:946bc763c068 515
mjr 50:946bc763c068 516 default:
mjr 50:946bc763c068 517 break;
samux 1:80ab0d068708 518 }
samux 1:80ab0d068708 519
samux 1:80ab0d068708 520 if (success)
samux 1:80ab0d068708 521 {
mbed_official 25:7c72828865f3 522 /* Send the status */
samux 1:80ab0d068708 523 transfer.ptr = (uint8_t *)&status; /* Assumes little endian */
samux 1:80ab0d068708 524 transfer.remaining = sizeof(status);
samux 1:80ab0d068708 525 transfer.direction = DEVICE_TO_HOST;
samux 1:80ab0d068708 526 }
mbed_official 25:7c72828865f3 527
samux 1:80ab0d068708 528 return success;
samux 1:80ab0d068708 529 }
samux 1:80ab0d068708 530
samux 1:80ab0d068708 531 bool USBDevice::requestSetup(void)
samux 1:80ab0d068708 532 {
samux 1:80ab0d068708 533 bool success = false;
samux 1:80ab0d068708 534
samux 1:80ab0d068708 535 /* Process standard requests */
samux 1:80ab0d068708 536 if ((transfer.setup.bmRequestType.Type == STANDARD_TYPE))
samux 1:80ab0d068708 537 {
samux 1:80ab0d068708 538 switch (transfer.setup.bRequest)
samux 1:80ab0d068708 539 {
mjr 50:946bc763c068 540 case GET_STATUS:
mjr 50:946bc763c068 541 success = requestGetStatus();
mjr 50:946bc763c068 542 break;
mjr 50:946bc763c068 543
mjr 50:946bc763c068 544 case CLEAR_FEATURE:
mjr 50:946bc763c068 545 success = requestClearFeature();
mjr 50:946bc763c068 546 break;
mjr 50:946bc763c068 547
mjr 50:946bc763c068 548 case SET_FEATURE:
mjr 50:946bc763c068 549 success = requestSetFeature();
mjr 50:946bc763c068 550 break;
mjr 50:946bc763c068 551
mjr 50:946bc763c068 552 case SET_ADDRESS:
mjr 50:946bc763c068 553 success = requestSetAddress();
mjr 50:946bc763c068 554 break;
mjr 50:946bc763c068 555
mjr 50:946bc763c068 556 case GET_DESCRIPTOR:
mjr 50:946bc763c068 557 success = requestGetDescriptor();
mjr 50:946bc763c068 558 break;
mjr 50:946bc763c068 559
mjr 50:946bc763c068 560 case SET_DESCRIPTOR:
mjr 50:946bc763c068 561 /* TODO: Support is optional, not implemented here */
mjr 50:946bc763c068 562 success = false;
mjr 50:946bc763c068 563 break;
mjr 50:946bc763c068 564
mjr 50:946bc763c068 565 case GET_CONFIGURATION:
mjr 50:946bc763c068 566 success = requestGetConfiguration();
mjr 50:946bc763c068 567 break;
mjr 50:946bc763c068 568
mjr 50:946bc763c068 569 case SET_CONFIGURATION:
mjr 50:946bc763c068 570 success = requestSetConfiguration();
mjr 50:946bc763c068 571 break;
mjr 50:946bc763c068 572
mjr 50:946bc763c068 573 case GET_INTERFACE:
mjr 50:946bc763c068 574 success = requestGetInterface();
mjr 50:946bc763c068 575 break;
mjr 50:946bc763c068 576
mjr 50:946bc763c068 577 case SET_INTERFACE:
mjr 50:946bc763c068 578 success = requestSetInterface();
mjr 50:946bc763c068 579 break;
mjr 50:946bc763c068 580
mjr 50:946bc763c068 581 default:
mjr 50:946bc763c068 582 break;
samux 1:80ab0d068708 583 }
samux 1:80ab0d068708 584 }
samux 1:80ab0d068708 585
samux 1:80ab0d068708 586 return success;
samux 1:80ab0d068708 587 }
samux 1:80ab0d068708 588
samux 1:80ab0d068708 589 bool USBDevice::controlSetup(void)
samux 1:80ab0d068708 590 {
samux 1:80ab0d068708 591 bool success = false;
samux 1:80ab0d068708 592
samux 1:80ab0d068708 593 /* Control transfer setup stage */
samux 1:80ab0d068708 594 uint8_t buffer[MAX_PACKET_SIZE_EP0];
samux 1:80ab0d068708 595
samux 1:80ab0d068708 596 EP0setup(buffer);
samux 1:80ab0d068708 597
samux 1:80ab0d068708 598 /* Initialise control transfer state */
samux 1:80ab0d068708 599 decodeSetupPacket(buffer, &transfer.setup);
samux 1:80ab0d068708 600 transfer.ptr = NULL;
samux 1:80ab0d068708 601 transfer.remaining = 0;
mjr 36:20bb47609697 602 transfer.direction = HOST_TO_DEVICE;
samux 1:80ab0d068708 603 transfer.zlp = false;
samux 1:80ab0d068708 604 transfer.notify = false;
mbed_official 25:7c72828865f3 605
mjr 37:c5ac4ccf6597 606 printd("dataTransferDirection: %d\r\nType: %d\r\nRecipient: %d\r\nbRequest: %d\r\nwValue: %d\r\nwIndex: %d\r\nwLength: %d\r\n",
mjr 37:c5ac4ccf6597 607 transfer.setup.bmRequestType.dataTransferDirection,
mjr 37:c5ac4ccf6597 608 transfer.setup.bmRequestType.Type,
mjr 37:c5ac4ccf6597 609 transfer.setup.bmRequestType.Recipient,
mjr 37:c5ac4ccf6597 610 transfer.setup.bRequest,
mjr 37:c5ac4ccf6597 611 transfer.setup.wValue,
mjr 37:c5ac4ccf6597 612 transfer.setup.wIndex,
mjr 37:c5ac4ccf6597 613 transfer.setup.wLength);
samux 1:80ab0d068708 614
samux 1:80ab0d068708 615 /* Class / vendor specific */
samux 1:80ab0d068708 616 success = USBCallback_request();
samux 1:80ab0d068708 617
samux 1:80ab0d068708 618 if (!success)
samux 1:80ab0d068708 619 {
samux 1:80ab0d068708 620 /* Standard requests */
samux 1:80ab0d068708 621 if (!requestSetup())
samux 1:80ab0d068708 622 {
mjr 37:c5ac4ccf6597 623 printd("requestSetup() failed: type=%d, req=%d\r\n", (int)transfer.setup.bmRequestType.Type, (int)transfer.setup.bRequest);
samux 1:80ab0d068708 624 return false;
samux 1:80ab0d068708 625 }
samux 1:80ab0d068708 626 }
samux 1:80ab0d068708 627
samux 1:80ab0d068708 628 /* Check transfer size and direction */
mjr 36:20bb47609697 629 if (transfer.setup.wLength > 0)
samux 1:80ab0d068708 630 {
mjr 48:b225d025ca1d 631 if (transfer.setup.bmRequestType.dataTransferDirection == DEVICE_TO_HOST)
samux 1:80ab0d068708 632 {
samux 1:80ab0d068708 633 /* IN data stage is required */
samux 1:80ab0d068708 634 if (transfer.direction != DEVICE_TO_HOST)
samux 1:80ab0d068708 635 {
mjr 37:c5ac4ccf6597 636 printd("controlSetup transfer direction wrong 1\r\n");
samux 1:80ab0d068708 637 return false;
samux 1:80ab0d068708 638 }
samux 1:80ab0d068708 639
samux 1:80ab0d068708 640 /* Transfer must be less than or equal to the size */
samux 1:80ab0d068708 641 /* requested by the host */
samux 1:80ab0d068708 642 if (transfer.remaining > transfer.setup.wLength)
samux 1:80ab0d068708 643 transfer.remaining = transfer.setup.wLength;
samux 1:80ab0d068708 644 }
samux 1:80ab0d068708 645 else
samux 1:80ab0d068708 646 {
mbed_official 25:7c72828865f3 647
samux 1:80ab0d068708 648 /* OUT data stage is required */
samux 1:80ab0d068708 649 if (transfer.direction != HOST_TO_DEVICE)
samux 1:80ab0d068708 650 {
mjr 37:c5ac4ccf6597 651 printd("controlSetup transfer direction wrong 2: type=%d, req=%d\r\n", (int)transfer.setup.bmRequestType.Type, (int)transfer.setup.bRequest);
samux 1:80ab0d068708 652 return false;
samux 1:80ab0d068708 653 }
samux 1:80ab0d068708 654
samux 1:80ab0d068708 655 /* Transfer must be equal to the size requested by the host */
samux 1:80ab0d068708 656 if (transfer.remaining != transfer.setup.wLength)
samux 1:80ab0d068708 657 {
mjr 37:c5ac4ccf6597 658 printd("controlSetup remaining length wrong: return len=%d, type=%d, req=%d, wvalue=%d, windex=%x, wlength=%d\r\n",
mjr 37:c5ac4ccf6597 659 transfer.remaining, transfer.setup.bmRequestType.Type, transfer.setup.bRequest, transfer.setup.wValue, transfer.setup.wIndex, transfer.setup.wLength);
samux 1:80ab0d068708 660 return false;
samux 1:80ab0d068708 661 }
samux 1:80ab0d068708 662 }
samux 1:80ab0d068708 663 }
samux 1:80ab0d068708 664 else
samux 1:80ab0d068708 665 {
samux 1:80ab0d068708 666 /* No data stage; transfer size must be zero */
samux 1:80ab0d068708 667 if (transfer.remaining != 0)
samux 1:80ab0d068708 668 {
mjr 37:c5ac4ccf6597 669 printd("controlSetup remaining length must be zero: return len=%d, type=%d, req=%d, wvalue=%d, windex=%x, wlength=%d\r\n",
mjr 37:c5ac4ccf6597 670 (int)transfer.remaining, (int)transfer.setup.bmRequestType.Type, (int)transfer.setup.bRequest, (int)transfer.setup.wValue, (int)transfer.setup.wIndex, (int)transfer.setup.wLength);
samux 1:80ab0d068708 671 return false;
samux 1:80ab0d068708 672 }
samux 1:80ab0d068708 673 }
samux 1:80ab0d068708 674
samux 1:80ab0d068708 675 /* Data or status stage if applicable */
mjr 36:20bb47609697 676 if (transfer.setup.wLength > 0)
samux 1:80ab0d068708 677 {
mjr 50:946bc763c068 678 if (transfer.setup.bmRequestType.dataTransferDirection == DEVICE_TO_HOST)
samux 1:80ab0d068708 679 {
samux 1:80ab0d068708 680 /* Check if we'll need to send a zero length packet at */
samux 1:80ab0d068708 681 /* the end of this transfer */
mjr 50:946bc763c068 682 if (transfer.setup.wLength >= transfer.remaining)
samux 1:80ab0d068708 683 {
samux 1:80ab0d068708 684 /* Device wishes to transfer less than host requested */
samux 1:80ab0d068708 685 if ((transfer.remaining % MAX_PACKET_SIZE_EP0) == 0)
samux 1:80ab0d068708 686 {
samux 1:80ab0d068708 687 /* Transfer is a multiple of EP0 max packet size */
samux 1:80ab0d068708 688 transfer.zlp = true;
samux 1:80ab0d068708 689 }
samux 1:80ab0d068708 690 }
samux 1:80ab0d068708 691
samux 1:80ab0d068708 692 /* IN stage */
samux 1:80ab0d068708 693 controlIn();
samux 1:80ab0d068708 694 }
samux 1:80ab0d068708 695 else
samux 1:80ab0d068708 696 {
samux 1:80ab0d068708 697 /* OUT stage */
samux 1:80ab0d068708 698 EP0read();
samux 1:80ab0d068708 699 }
samux 1:80ab0d068708 700 }
samux 1:80ab0d068708 701 else
samux 1:80ab0d068708 702 {
samux 1:80ab0d068708 703 /* Status stage */
samux 1:80ab0d068708 704 EP0write(NULL, 0);
samux 1:80ab0d068708 705 }
samux 1:80ab0d068708 706
samux 1:80ab0d068708 707 return true;
samux 1:80ab0d068708 708 }
samux 1:80ab0d068708 709
samux 1:80ab0d068708 710 void USBDevice::busReset(void)
samux 1:80ab0d068708 711 {
mjr 50:946bc763c068 712 // reset the device state
mjr 50:946bc763c068 713 memset(&device, 0, sizeof(device));
mjr 50:946bc763c068 714 setDeviceState(DEFAULT);
samux 1:80ab0d068708 715 device.configuration = 0;
samux 1:80ab0d068708 716 device.suspended = false;
samux 1:80ab0d068708 717
mjr 50:946bc763c068 718 // reset the transfer state
mjr 50:946bc763c068 719 memset(&transfer, 0, sizeof(transfer));
mjr 50:946bc763c068 720
mjr 50:946bc763c068 721 // reset interface state
mjr 50:946bc763c068 722 currentInterface = 0;
mjr 50:946bc763c068 723 currentAlternate = 0;
mjr 50:946bc763c068 724
samux 1:80ab0d068708 725 /* Call class / vendor specific busReset function */
samux 1:80ab0d068708 726 USBCallback_busReset();
samux 1:80ab0d068708 727 }
samux 1:80ab0d068708 728
samux 1:80ab0d068708 729 void USBDevice::EP0setupCallback(void)
samux 1:80ab0d068708 730 {
samux 1:80ab0d068708 731 /* Endpoint 0 setup event */
samux 1:80ab0d068708 732 if (!controlSetup())
samux 1:80ab0d068708 733 {
samux 1:80ab0d068708 734 /* Protocol stall */
samux 1:80ab0d068708 735 EP0stall();
samux 1:80ab0d068708 736 }
samux 1:80ab0d068708 737 }
samux 1:80ab0d068708 738
samux 1:80ab0d068708 739 void USBDevice::EP0out(void)
samux 1:80ab0d068708 740 {
samux 1:80ab0d068708 741 /* Endpoint 0 OUT data event */
samux 1:80ab0d068708 742 if (!controlOut())
samux 1:80ab0d068708 743 {
mjr 29:b502c11a3dff 744 /* Protocol stall; this will stall both endpoints */
samux 1:80ab0d068708 745 EP0stall();
samux 1:80ab0d068708 746 }
samux 1:80ab0d068708 747 }
samux 1:80ab0d068708 748
samux 1:80ab0d068708 749 void USBDevice::EP0in(void)
samux 1:80ab0d068708 750 {
samux 1:80ab0d068708 751 /* Endpoint 0 IN data event */
mjr 48:b225d025ca1d 752 printd("EP0IN\r\n");
samux 1:80ab0d068708 753 if (!controlIn())
samux 1:80ab0d068708 754 {
samux 1:80ab0d068708 755 /* Protocol stall; this will stall both endpoints */
samux 1:80ab0d068708 756 EP0stall();
samux 1:80ab0d068708 757 }
samux 1:80ab0d068708 758 }
samux 1:80ab0d068708 759
samux 1:80ab0d068708 760 bool USBDevice::configured(void)
samux 1:80ab0d068708 761 {
samux 1:80ab0d068708 762 /* Returns true if device is in the CONFIGURED state */
samux 1:80ab0d068708 763 return (device.state == CONFIGURED);
samux 1:80ab0d068708 764 }
samux 1:80ab0d068708 765
mbed_official 18:78bdbce94509 766 void USBDevice::connect(bool blocking)
samux 1:80ab0d068708 767 {
samux 1:80ab0d068708 768 /* Connect device */
samux 1:80ab0d068708 769 USBHAL::connect();
mbed_official 25:7c72828865f3 770
mbed_official 18:78bdbce94509 771 if (blocking) {
mjr 37:c5ac4ccf6597 772 /* Block until configured */
mjr 37:c5ac4ccf6597 773 while (!configured()) { }
mbed_official 18:78bdbce94509 774 }
samux 1:80ab0d068708 775 }
samux 1:80ab0d068708 776
samux 1:80ab0d068708 777 void USBDevice::disconnect(void)
samux 1:80ab0d068708 778 {
samux 1:80ab0d068708 779 /* Disconnect device */
samux 1:80ab0d068708 780 USBHAL::disconnect();
mbed_official 26:8ef73dd868a0 781
mbed_official 26:8ef73dd868a0 782 /* Set initial device state */
mjr 50:946bc763c068 783 setDeviceState(POWERED);
mbed_official 26:8ef73dd868a0 784 device.configuration = 0;
mbed_official 26:8ef73dd868a0 785 device.suspended = false;
samux 1:80ab0d068708 786 }
samux 1:80ab0d068708 787
samux 1:80ab0d068708 788 CONTROL_TRANSFER * USBDevice::getTransferPtr(void)
samux 1:80ab0d068708 789 {
samux 1:80ab0d068708 790 return &transfer;
samux 1:80ab0d068708 791 }
samux 1:80ab0d068708 792
samux 1:80ab0d068708 793 bool USBDevice::addEndpoint(uint8_t endpoint, uint32_t maxPacket)
samux 1:80ab0d068708 794 {
samux 1:80ab0d068708 795 return realiseEndpoint(endpoint, maxPacket, 0);
samux 1:80ab0d068708 796 }
samux 1:80ab0d068708 797
samux 1:80ab0d068708 798 bool USBDevice::addRateFeedbackEndpoint(uint8_t endpoint, uint32_t maxPacket)
samux 1:80ab0d068708 799 {
samux 1:80ab0d068708 800 /* For interrupt endpoints only */
samux 1:80ab0d068708 801 return realiseEndpoint(endpoint, maxPacket, RATE_FEEDBACK_MODE);
samux 1:80ab0d068708 802 }
samux 1:80ab0d068708 803
mjr 49:03527ce6840e 804 const uint8_t *USBDevice::findDescriptor(uint8_t descriptorType, int idx)
samux 1:80ab0d068708 805 {
samux 1:80ab0d068708 806 /* Find a descriptor within the list of descriptors */
samux 1:80ab0d068708 807 /* following a configuration descriptor. */
samux 1:80ab0d068708 808 uint16_t wTotalLength;
mjr 49:03527ce6840e 809 const uint8_t *ptr;
samux 1:80ab0d068708 810
samux 1:80ab0d068708 811 if (configurationDesc() == NULL)
samux 1:80ab0d068708 812 {
samux 1:80ab0d068708 813 return NULL;
samux 1:80ab0d068708 814 }
samux 1:80ab0d068708 815
samux 1:80ab0d068708 816 /* Check this is a configuration descriptor */
samux 1:80ab0d068708 817 if ((configurationDesc()[0] != CONFIGURATION_DESCRIPTOR_LENGTH) \
samux 1:80ab0d068708 818 || (configurationDesc()[1] != CONFIGURATION_DESCRIPTOR))
samux 1:80ab0d068708 819 {
samux 1:80ab0d068708 820 return NULL;
samux 1:80ab0d068708 821 }
samux 1:80ab0d068708 822
samux 1:80ab0d068708 823 wTotalLength = configurationDesc()[2] | (configurationDesc()[3] << 8);
samux 1:80ab0d068708 824
samux 1:80ab0d068708 825 /* Check there are some more descriptors to follow */
samux 1:80ab0d068708 826 if (wTotalLength <= (CONFIGURATION_DESCRIPTOR_LENGTH+2))
samux 1:80ab0d068708 827 /* +2 is for bLength and bDescriptorType of next descriptor */
samux 1:80ab0d068708 828 {
bogdanm 11:eeb3cbbaa996 829 return NULL;
samux 1:80ab0d068708 830 }
samux 1:80ab0d068708 831
samux 1:80ab0d068708 832 /* Start at first descriptor after the configuration descriptor */
samux 1:80ab0d068708 833 ptr = &(configurationDesc()[CONFIGURATION_DESCRIPTOR_LENGTH]);
samux 1:80ab0d068708 834
samux 1:80ab0d068708 835 do {
samux 1:80ab0d068708 836 if (ptr[1] /* bDescriptorType */ == descriptorType)
samux 1:80ab0d068708 837 {
mjr 33:b0a3f6b27b07 838 // Found - if the index has reached zero, it's the one we're
mjr 33:b0a3f6b27b07 839 // looking for; if not, just decrement the index and keep looking.
mjr 33:b0a3f6b27b07 840 if (idx == 0)
mjr 33:b0a3f6b27b07 841 return ptr;
mjr 33:b0a3f6b27b07 842 else
mjr 33:b0a3f6b27b07 843 --idx;
samux 1:80ab0d068708 844 }
samux 1:80ab0d068708 845
samux 1:80ab0d068708 846 /* Skip to next descriptor */
samux 1:80ab0d068708 847 ptr += ptr[0]; /* bLength */
samux 1:80ab0d068708 848 } while (ptr < (configurationDesc() + wTotalLength));
samux 1:80ab0d068708 849
samux 1:80ab0d068708 850 /* Reached end of the descriptors - not found */
samux 1:80ab0d068708 851 return NULL;
samux 1:80ab0d068708 852 }
samux 1:80ab0d068708 853
samux 1:80ab0d068708 854
samux 1:80ab0d068708 855 void USBDevice::connectStateChanged(unsigned int connected)
samux 1:80ab0d068708 856 {
samux 1:80ab0d068708 857 }
samux 1:80ab0d068708 858
samux 1:80ab0d068708 859 void USBDevice::suspendStateChanged(unsigned int suspended)
samux 1:80ab0d068708 860 {
samux 1:80ab0d068708 861 }
samux 1:80ab0d068708 862
mjr 50:946bc763c068 863 void USBDevice::sleepStateChanged(unsigned int sleep)
mjr 50:946bc763c068 864 {
mjr 50:946bc763c068 865 // If we got a Sleep signal while in ADDRESS mode, it means that the
mjr 50:946bc763c068 866 // initial connection setup failed.
mjr 50:946bc763c068 867 if (sleep && device.state == ADDRESS)
mjr 50:946bc763c068 868 connectFailed();
mjr 50:946bc763c068 869 }
mjr 50:946bc763c068 870
samux 1:80ab0d068708 871
samux 1:80ab0d068708 872 USBDevice::USBDevice(uint16_t vendor_id, uint16_t product_id, uint16_t product_release){
mbed_official 25:7c72828865f3 873 VENDOR_ID = vendor_id;
mbed_official 25:7c72828865f3 874 PRODUCT_ID = product_id;
samux 1:80ab0d068708 875 PRODUCT_RELEASE = product_release;
samux 1:80ab0d068708 876
samux 1:80ab0d068708 877 /* Set initial device state */
mjr 50:946bc763c068 878 setDeviceState(POWERED);
samux 1:80ab0d068708 879 device.configuration = 0;
samux 1:80ab0d068708 880 device.suspended = false;
samux 1:80ab0d068708 881 };
samux 1:80ab0d068708 882
samux 1:80ab0d068708 883
samux 1:80ab0d068708 884 bool USBDevice::readStart(uint8_t endpoint, uint32_t maxSize)
samux 1:80ab0d068708 885 {
samux 1:80ab0d068708 886 return endpointRead(endpoint, maxSize) == EP_PENDING;
samux 1:80ab0d068708 887 }
samux 1:80ab0d068708 888
samux 1:80ab0d068708 889
samux 1:80ab0d068708 890 bool USBDevice::write(uint8_t endpoint, uint8_t * buffer, uint32_t size, uint32_t maxSize)
samux 1:80ab0d068708 891 {
samux 1:80ab0d068708 892 EP_STATUS result;
samux 1:80ab0d068708 893
samux 1:80ab0d068708 894 if (size > maxSize)
samux 1:80ab0d068708 895 {
samux 1:80ab0d068708 896 return false;
samux 1:80ab0d068708 897 }
mbed_official 25:7c72828865f3 898
mbed_official 25:7c72828865f3 899
samux 1:80ab0d068708 900 if(!configured()) {
samux 1:80ab0d068708 901 return false;
samux 1:80ab0d068708 902 }
mbed_official 25:7c72828865f3 903
samux 1:80ab0d068708 904 /* Send report */
mjr 48:b225d025ca1d 905 result = endpointWrite(endpoint, buffer, size);
samux 1:80ab0d068708 906
samux 1:80ab0d068708 907 if (result != EP_PENDING)
samux 1:80ab0d068708 908 {
samux 1:80ab0d068708 909 return false;
samux 1:80ab0d068708 910 }
samux 1:80ab0d068708 911
samux 1:80ab0d068708 912 /* Wait for completion */
samux 1:80ab0d068708 913 do {
samux 1:80ab0d068708 914 result = endpointWriteResult(endpoint);
samux 1:80ab0d068708 915 } while ((result == EP_PENDING) && configured());
samux 1:80ab0d068708 916
samux 1:80ab0d068708 917 return (result == EP_COMPLETED);
samux 1:80ab0d068708 918 }
samux 1:80ab0d068708 919
samux 1:80ab0d068708 920
samux 1:80ab0d068708 921 bool USBDevice::writeNB(uint8_t endpoint, uint8_t * buffer, uint32_t size, uint32_t maxSize)
samux 1:80ab0d068708 922 {
samux 1:80ab0d068708 923 EP_STATUS result;
samux 1:80ab0d068708 924
samux 1:80ab0d068708 925 if (size > maxSize)
samux 1:80ab0d068708 926 {
samux 1:80ab0d068708 927 return false;
samux 1:80ab0d068708 928 }
mbed_official 25:7c72828865f3 929
samux 1:80ab0d068708 930 if(!configured()) {
samux 1:80ab0d068708 931 return false;
samux 1:80ab0d068708 932 }
samux 1:80ab0d068708 933
samux 1:80ab0d068708 934 /* Send report */
samux 1:80ab0d068708 935 result = endpointWrite(endpoint, buffer, size);
samux 1:80ab0d068708 936
samux 1:80ab0d068708 937 if (result != EP_PENDING)
samux 1:80ab0d068708 938 {
samux 1:80ab0d068708 939 return false;
samux 1:80ab0d068708 940 }
samux 1:80ab0d068708 941
samux 1:80ab0d068708 942 result = endpointWriteResult(endpoint);
samux 1:80ab0d068708 943
samux 1:80ab0d068708 944 return (result == EP_COMPLETED);
samux 1:80ab0d068708 945 }
samux 1:80ab0d068708 946
mjr 32:a8eb758f4074 947 bool USBDevice::writeTO(uint8_t endpoint, uint8_t * buffer, uint32_t size, uint32_t maxSize, int timeout_ms)
mjr 32:a8eb758f4074 948 {
mjr 32:a8eb758f4074 949 EP_STATUS result;
mjr 32:a8eb758f4074 950
mjr 32:a8eb758f4074 951 if (size > maxSize)
mjr 32:a8eb758f4074 952 {
mjr 32:a8eb758f4074 953 return false;
mjr 32:a8eb758f4074 954 }
mjr 32:a8eb758f4074 955
mjr 48:b225d025ca1d 956 if(!configured()) {
mjr 48:b225d025ca1d 957 return false;
mjr 48:b225d025ca1d 958 }
mjr 32:a8eb758f4074 959
mjr 48:b225d025ca1d 960 /* Send report */
mjr 48:b225d025ca1d 961 result = endpointWrite(endpoint, buffer, size);
mjr 48:b225d025ca1d 962
mjr 48:b225d025ca1d 963 if (result != EP_PENDING)
mjr 48:b225d025ca1d 964 {
mjr 32:a8eb758f4074 965 return false;
mjr 32:a8eb758f4074 966 }
mjr 34:884405d998bb 967
mjr 34:884405d998bb 968 /* set up a timer for monitoring the timeout period */
mjr 34:884405d998bb 969 Timer t;
mjr 34:884405d998bb 970 t.start();
mjr 32:a8eb758f4074 971
mjr 32:a8eb758f4074 972 /* Wait for completion or timeout */
mjr 32:a8eb758f4074 973 do {
mjr 32:a8eb758f4074 974 result = endpointWriteResult(endpoint);
mjr 32:a8eb758f4074 975 } while ((result == EP_PENDING) && configured() && t.read_ms() < timeout_ms);
mjr 32:a8eb758f4074 976
mjr 32:a8eb758f4074 977 return (result == EP_COMPLETED);
mjr 32:a8eb758f4074 978 }
mjr 32:a8eb758f4074 979
samux 1:80ab0d068708 980 bool USBDevice::readEP(uint8_t endpoint, uint8_t * buffer, uint32_t * size, uint32_t maxSize)
samux 1:80ab0d068708 981 {
samux 1:80ab0d068708 982 EP_STATUS result;
mbed_official 25:7c72828865f3 983
samux 1:80ab0d068708 984 if(!configured()) {
samux 1:80ab0d068708 985 return false;
samux 1:80ab0d068708 986 }
samux 1:80ab0d068708 987
samux 1:80ab0d068708 988 /* Wait for completion */
samux 1:80ab0d068708 989 do {
samux 1:80ab0d068708 990 result = endpointReadResult(endpoint, buffer, size);
samux 1:80ab0d068708 991 } while ((result == EP_PENDING) && configured());
samux 1:80ab0d068708 992
samux 1:80ab0d068708 993 return (result == EP_COMPLETED);
samux 1:80ab0d068708 994 }
samux 1:80ab0d068708 995
samux 1:80ab0d068708 996
samux 1:80ab0d068708 997 bool USBDevice::readEP_NB(uint8_t endpoint, uint8_t * buffer, uint32_t * size, uint32_t maxSize)
samux 1:80ab0d068708 998 {
samux 1:80ab0d068708 999 EP_STATUS result;
mbed_official 25:7c72828865f3 1000
samux 1:80ab0d068708 1001 if(!configured()) {
samux 1:80ab0d068708 1002 return false;
samux 1:80ab0d068708 1003 }
samux 1:80ab0d068708 1004
samux 1:80ab0d068708 1005 result = endpointReadResult(endpoint, buffer, size);
mbed_official 25:7c72828865f3 1006
samux 1:80ab0d068708 1007 return (result == EP_COMPLETED);
samux 1:80ab0d068708 1008 }
samux 1:80ab0d068708 1009
samux 1:80ab0d068708 1010
samux 1:80ab0d068708 1011
mjr 49:03527ce6840e 1012 const uint8_t *USBDevice::deviceDesc() {
mjr 49:03527ce6840e 1013 static const uint8_t deviceDescriptor[] = {
samux 1:80ab0d068708 1014 DEVICE_DESCRIPTOR_LENGTH, /* bLength */
samux 1:80ab0d068708 1015 DEVICE_DESCRIPTOR, /* bDescriptorType */
samux 1:80ab0d068708 1016 LSB(USB_VERSION_2_0), /* bcdUSB (LSB) */
samux 1:80ab0d068708 1017 MSB(USB_VERSION_2_0), /* bcdUSB (MSB) */
samux 1:80ab0d068708 1018 0x00, /* bDeviceClass */
samux 1:80ab0d068708 1019 0x00, /* bDeviceSubClass */
samux 1:80ab0d068708 1020 0x00, /* bDeviceprotocol */
samux 1:80ab0d068708 1021 MAX_PACKET_SIZE_EP0, /* bMaxPacketSize0 */
bogdanm 11:eeb3cbbaa996 1022 (uint8_t)(LSB(VENDOR_ID)), /* idVendor (LSB) */
bogdanm 11:eeb3cbbaa996 1023 (uint8_t)(MSB(VENDOR_ID)), /* idVendor (MSB) */
bogdanm 11:eeb3cbbaa996 1024 (uint8_t)(LSB(PRODUCT_ID)), /* idProduct (LSB) */
bogdanm 11:eeb3cbbaa996 1025 (uint8_t)(MSB(PRODUCT_ID)), /* idProduct (MSB) */
bogdanm 11:eeb3cbbaa996 1026 (uint8_t)(LSB(PRODUCT_RELEASE)), /* bcdDevice (LSB) */
bogdanm 11:eeb3cbbaa996 1027 (uint8_t)(MSB(PRODUCT_RELEASE)), /* bcdDevice (MSB) */
samux 1:80ab0d068708 1028 STRING_OFFSET_IMANUFACTURER, /* iManufacturer */
samux 1:80ab0d068708 1029 STRING_OFFSET_IPRODUCT, /* iProduct */
samux 1:80ab0d068708 1030 STRING_OFFSET_ISERIAL, /* iSerialNumber */
samux 1:80ab0d068708 1031 0x01 /* bNumConfigurations */
samux 1:80ab0d068708 1032 };
samux 1:80ab0d068708 1033 return deviceDescriptor;
samux 1:80ab0d068708 1034 }
samux 1:80ab0d068708 1035
mjr 49:03527ce6840e 1036 const uint8_t *USBDevice::stringLangidDesc() {
mjr 49:03527ce6840e 1037 static const uint8_t stringLangidDescriptor[] = {
samux 1:80ab0d068708 1038 0x04, /*bLength*/
samux 1:80ab0d068708 1039 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
mbed_official 17:bbd6dac92961 1040 0x09,0x04, /*bString Lang ID - 0x0409 - English*/
samux 1:80ab0d068708 1041 };
samux 1:80ab0d068708 1042 return stringLangidDescriptor;
samux 1:80ab0d068708 1043 }
samux 1:80ab0d068708 1044
mjr 49:03527ce6840e 1045 const uint8_t *USBDevice::stringImanufacturerDesc() {
mjr 49:03527ce6840e 1046 static const uint8_t stringImanufacturerDescriptor[] = {
samux 1:80ab0d068708 1047 0x12, /*bLength*/
samux 1:80ab0d068708 1048 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
samux 1:80ab0d068708 1049 'm',0,'b',0,'e',0,'d',0,'.',0,'o',0,'r',0,'g',0, /*bString iManufacturer - mbed.org*/
samux 1:80ab0d068708 1050 };
samux 1:80ab0d068708 1051 return stringImanufacturerDescriptor;
samux 1:80ab0d068708 1052 }
samux 1:80ab0d068708 1053
mjr 49:03527ce6840e 1054 const uint8_t *USBDevice::stringIserialDesc() {
mjr 49:03527ce6840e 1055 static const uint8_t stringIserialDescriptor[] = {
samux 1:80ab0d068708 1056 0x16, /*bLength*/
samux 1:80ab0d068708 1057 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
samux 1:80ab0d068708 1058 '0',0,'1',0,'2',0,'3',0,'4',0,'5',0,'6',0,'7',0,'8',0,'9',0, /*bString iSerial - 0123456789*/
samux 1:80ab0d068708 1059 };
samux 1:80ab0d068708 1060 return stringIserialDescriptor;
samux 1:80ab0d068708 1061 }
samux 1:80ab0d068708 1062
mjr 49:03527ce6840e 1063 const uint8_t *USBDevice::stringIConfigurationDesc() {
mjr 49:03527ce6840e 1064 static const uint8_t stringIconfigurationDescriptor[] = {
samux 1:80ab0d068708 1065 0x06, /*bLength*/
samux 1:80ab0d068708 1066 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
samux 1:80ab0d068708 1067 '0',0,'1',0, /*bString iConfiguration - 01*/
samux 1:80ab0d068708 1068 };
samux 1:80ab0d068708 1069 return stringIconfigurationDescriptor;
samux 1:80ab0d068708 1070 }
samux 1:80ab0d068708 1071
mjr 49:03527ce6840e 1072 const uint8_t *USBDevice::stringIinterfaceDesc() {
mjr 49:03527ce6840e 1073 static const uint8_t stringIinterfaceDescriptor[] = {
samux 1:80ab0d068708 1074 0x08, /*bLength*/
samux 1:80ab0d068708 1075 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
samux 1:80ab0d068708 1076 'U',0,'S',0,'B',0, /*bString iInterface - USB*/
samux 1:80ab0d068708 1077 };
samux 1:80ab0d068708 1078 return stringIinterfaceDescriptor;
samux 1:80ab0d068708 1079 }
samux 1:80ab0d068708 1080
mjr 49:03527ce6840e 1081 const uint8_t *USBDevice::stringIproductDesc() {
mjr 49:03527ce6840e 1082 static const uint8_t stringIproductDescriptor[] = {
samux 1:80ab0d068708 1083 0x16, /*bLength*/
samux 1:80ab0d068708 1084 STRING_DESCRIPTOR, /*bDescriptorType 0x03*/
samux 1:80ab0d068708 1085 'U',0,'S',0,'B',0,' ',0,'D',0,'E',0,'V',0,'I',0,'C',0,'E',0 /*bString iProduct - USB DEVICE*/
samux 1:80ab0d068708 1086 };
samux 1:80ab0d068708 1087 return stringIproductDescriptor;
samux 1:80ab0d068708 1088 }