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:
Fri Dec 25 20:24:34 2015 +0000
Revision:
35:53e1a208f582
Parent:
34:884405d998bb
Child:
36:20bb47609697
Race condition changes;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
samux 8:335f2506f422 1 /* Copyright (c) 2010-2011 mbed.org, MIT License
samux 8:335f2506f422 2 *
samux 8:335f2506f422 3 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
samux 8:335f2506f422 4 * and associated documentation files (the "Software"), to deal in the Software without
samux 8:335f2506f422 5 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
samux 8:335f2506f422 6 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
samux 8:335f2506f422 7 * Software is furnished to do so, subject to the following conditions:
samux 8:335f2506f422 8 *
samux 8:335f2506f422 9 * The above copyright notice and this permission notice shall be included in all copies or
samux 8:335f2506f422 10 * substantial portions of the Software.
samux 8:335f2506f422 11 *
samux 8:335f2506f422 12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
samux 8:335f2506f422 13 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
samux 8:335f2506f422 14 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
samux 8:335f2506f422 15 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
samux 8:335f2506f422 16 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
samux 8:335f2506f422 17 */
samux 8:335f2506f422 18
mbed_official 22:5b7d31d9d3f3 19 #if defined(TARGET_KL25Z) | defined(TARGET_KL46Z) | defined(TARGET_K20D5M) | defined(TARGET_K64F)
samux 8:335f2506f422 20
samux 8:335f2506f422 21 #include "USBHAL.h"
samux 8:335f2506f422 22
samux 8:335f2506f422 23 USBHAL * USBHAL::instance;
samux 8:335f2506f422 24
mjr 35:53e1a208f582 25 // Perform an operation atomically, by disabling interrupts. This must be
mjr 35:53e1a208f582 26 // used for any test-and-write operations in non-IRQ code, including increment,
mjr 35:53e1a208f582 27 // decrement, bit set, and bit clear, on variables that are also written
mjr 35:53e1a208f582 28 // within the IRQ handler. (Operations *within* the IRQ handler don't require
mjr 35:53e1a208f582 29 // this, because regular code can't interrupt an interrupt handler.) This
mjr 35:53e1a208f582 30 // applies in particular to 'epComplete' and 'Data1' updates, which are both
mjr 35:53e1a208f582 31 // bit vectors that are written with bitwise test-and-set operations in
mjr 35:53e1a208f582 32 // regular code and IRQ code.
mjr 35:53e1a208f582 33 #define atomic(_stm_) \
mjr 35:53e1a208f582 34 do { \
mjr 35:53e1a208f582 35 NVIC_DisableIRQ(USB0_IRQn); \
mjr 35:53e1a208f582 36 _stm_; \
mjr 35:53e1a208f582 37 NVIC_EnableIRQ(USB0_IRQn); \
mjr 35:53e1a208f582 38 } while (0)
mjr 35:53e1a208f582 39
mjr 35:53e1a208f582 40
mjr 35:53e1a208f582 41 // endpoint completion flags
mjr 35:53e1a208f582 42 static volatile int epComplete = 0;
mjr 35:53e1a208f582 43
mjr 35:53e1a208f582 44 // clear a completion flag - must be atomic, as the IRQ also manipulates these bits
mjr 35:53e1a208f582 45 #define clear_completion(endpoint) atomic(epComplete &= ~EP(endpoint))
mjr 35:53e1a208f582 46
mjr 35:53e1a208f582 47
samux 8:335f2506f422 48 // Convert physical endpoint number to register bit
mjr 35:53e1a208f582 49 #define EP(endpoint) (1<<(endpoint))
samux 8:335f2506f422 50
samux 8:335f2506f422 51 // Convert physical to logical
samux 8:335f2506f422 52 #define PHY_TO_LOG(endpoint) ((endpoint)>>1)
samux 8:335f2506f422 53
samux 8:335f2506f422 54 // Get endpoint direction
samux 8:335f2506f422 55 #define IN_EP(endpoint) ((endpoint) & 1U ? true : false)
samux 8:335f2506f422 56 #define OUT_EP(endpoint) ((endpoint) & 1U ? false : true)
samux 8:335f2506f422 57
mjr 35:53e1a208f582 58 #define BD_OWN_MASK (1<<7)
mjr 35:53e1a208f582 59 #define BD_DATA01_MASK (1<<6)
mjr 35:53e1a208f582 60 #define BD_KEEP_MASK (1<<5)
mjr 35:53e1a208f582 61 #define BD_NINC_MASK (1<<4)
mjr 35:53e1a208f582 62 #define BD_DTS_MASK (1<<3)
mjr 35:53e1a208f582 63 #define BD_STALL_MASK (1<<2)
mjr 35:53e1a208f582 64
samux 8:335f2506f422 65 #define TX 1
samux 8:335f2506f422 66 #define RX 0
mjr 35:53e1a208f582 67 #define ODD 0
mjr 35:53e1a208f582 68 #define EVEN 1
mjr 35:53e1a208f582 69 // this macro waits a physical endpoint number
mjr 35:53e1a208f582 70 #define EP_BDT_IDX(ep, dir, odd) (((ep * 4) + (2 * dir) + (1 * odd)))
samux 8:335f2506f422 71
samux 8:335f2506f422 72 #define SETUP_TOKEN 0x0D
samux 8:335f2506f422 73 #define IN_TOKEN 0x09
samux 8:335f2506f422 74 #define OUT_TOKEN 0x01
mjr 35:53e1a208f582 75 #define TOK_PID(idx) ((bdt[idx].info >> 2) & 0x0F)
mjr 34:884405d998bb 76
mjr 35:53e1a208f582 77 // for each endpt: 8 bytes
mjr 35:53e1a208f582 78 typedef struct BDT {
mjr 35:53e1a208f582 79 uint8_t info; // BD[0:7]
mjr 35:53e1a208f582 80 uint8_t dummy; // RSVD: BD[8:15]
mjr 35:53e1a208f582 81 uint16_t byte_count; // BD[16:32]
mjr 35:53e1a208f582 82 uint32_t address; // Addr
mjr 35:53e1a208f582 83 } BDT;
samux 8:335f2506f422 84
mjr 35:53e1a208f582 85
mjr 35:53e1a208f582 86 // there are:
mjr 35:53e1a208f582 87 // * 16 bidirectionnal endpt -> 32 physical endpt
mjr 35:53e1a208f582 88 // * as there are ODD and EVEN buffer -> 32*2 bdt
mjr 35:53e1a208f582 89 __attribute__((__aligned__(512))) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
mjr 35:53e1a208f582 90 uint8_t * endpoint_buffer[NUMBER_OF_PHYSICAL_ENDPOINTS];
mjr 35:53e1a208f582 91
mjr 35:53e1a208f582 92 static uint8_t set_addr = 0;
mjr 34:884405d998bb 93 static uint8_t addr = 0;
mjr 34:884405d998bb 94
mjr 34:884405d998bb 95
mjr 35:53e1a208f582 96 // DATA0/DATA1 setting for next packet on each endpoint
mjr 34:884405d998bb 97 static uint32_t Data1 = 0x55555555;
samux 8:335f2506f422 98
mjr 35:53e1a208f582 99 // set DATA0/DATA1 status on an endpoint (must be atomic as the IRQ handler
mjr 35:53e1a208f582 100 // manpulates this variable)
mjr 35:53e1a208f582 101 #define set_DATA0(endpoint) atomic(Data1 &= ~EP(endpoint))
mjr 35:53e1a208f582 102 #define set_DATA1(endpoint) atomic(Data1 |= EP(endpoint))
mjr 35:53e1a208f582 103 #define toggle_DATA01(endpoint) atomic(Data1 ^= EP(endpoint))
samux 8:335f2506f422 104
mjr 35:53e1a208f582 105 static uint32_t frameNumber() {
bogdanm 13:16731886c049 106 return((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
samux 8:335f2506f422 107 }
samux 8:335f2506f422 108
mjr 35:53e1a208f582 109 uint32_t USBHAL::endpointReadcore(uint8_t endpoint, uint8_t *buffer) {
samux 8:335f2506f422 110 return 0;
samux 8:335f2506f422 111 }
samux 8:335f2506f422 112
mjr 35:53e1a208f582 113 USBHAL::USBHAL(void) {
samux 8:335f2506f422 114 // Disable IRQ
samux 8:335f2506f422 115 NVIC_DisableIRQ(USB0_IRQn);
mbed_official 20:d38b72fed893 116
mbed_official 22:5b7d31d9d3f3 117 #if defined(TARGET_K64F)
mbed_official 22:5b7d31d9d3f3 118 MPU->CESR=0;
mbed_official 22:5b7d31d9d3f3 119 #endif
samux 8:335f2506f422 120 // fill in callback array
samux 8:335f2506f422 121 epCallback[0] = &USBHAL::EP1_OUT_callback;
samux 8:335f2506f422 122 epCallback[1] = &USBHAL::EP1_IN_callback;
samux 8:335f2506f422 123 epCallback[2] = &USBHAL::EP2_OUT_callback;
samux 8:335f2506f422 124 epCallback[3] = &USBHAL::EP2_IN_callback;
samux 8:335f2506f422 125 epCallback[4] = &USBHAL::EP3_OUT_callback;
samux 8:335f2506f422 126 epCallback[5] = &USBHAL::EP3_IN_callback;
samux 8:335f2506f422 127 epCallback[6] = &USBHAL::EP4_OUT_callback;
samux 8:335f2506f422 128 epCallback[7] = &USBHAL::EP4_IN_callback;
samux 8:335f2506f422 129 epCallback[8] = &USBHAL::EP5_OUT_callback;
samux 8:335f2506f422 130 epCallback[9] = &USBHAL::EP5_IN_callback;
samux 8:335f2506f422 131 epCallback[10] = &USBHAL::EP6_OUT_callback;
samux 8:335f2506f422 132 epCallback[11] = &USBHAL::EP6_IN_callback;
samux 8:335f2506f422 133 epCallback[12] = &USBHAL::EP7_OUT_callback;
samux 8:335f2506f422 134 epCallback[13] = &USBHAL::EP7_IN_callback;
samux 8:335f2506f422 135 epCallback[14] = &USBHAL::EP8_OUT_callback;
samux 8:335f2506f422 136 epCallback[15] = &USBHAL::EP8_IN_callback;
samux 8:335f2506f422 137 epCallback[16] = &USBHAL::EP9_OUT_callback;
samux 8:335f2506f422 138 epCallback[17] = &USBHAL::EP9_IN_callback;
samux 8:335f2506f422 139 epCallback[18] = &USBHAL::EP10_OUT_callback;
samux 8:335f2506f422 140 epCallback[19] = &USBHAL::EP10_IN_callback;
samux 8:335f2506f422 141 epCallback[20] = &USBHAL::EP11_OUT_callback;
samux 8:335f2506f422 142 epCallback[21] = &USBHAL::EP11_IN_callback;
samux 8:335f2506f422 143 epCallback[22] = &USBHAL::EP12_OUT_callback;
samux 8:335f2506f422 144 epCallback[23] = &USBHAL::EP12_IN_callback;
samux 8:335f2506f422 145 epCallback[24] = &USBHAL::EP13_OUT_callback;
samux 8:335f2506f422 146 epCallback[25] = &USBHAL::EP13_IN_callback;
samux 8:335f2506f422 147 epCallback[26] = &USBHAL::EP14_OUT_callback;
samux 8:335f2506f422 148 epCallback[27] = &USBHAL::EP14_IN_callback;
samux 8:335f2506f422 149 epCallback[28] = &USBHAL::EP15_OUT_callback;
samux 8:335f2506f422 150 epCallback[29] = &USBHAL::EP15_IN_callback;
mbed_official 20:d38b72fed893 151
mbed_official 20:d38b72fed893 152
samux 8:335f2506f422 153 // choose usb src as PLL
samux 8:335f2506f422 154 SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);
mbed_official 20:d38b72fed893 155
samux 8:335f2506f422 156 // enable OTG clock
samux 8:335f2506f422 157 SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
samux 8:335f2506f422 158
samux 8:335f2506f422 159 // Attach IRQ
samux 8:335f2506f422 160 instance = this;
samux 8:335f2506f422 161 NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
samux 8:335f2506f422 162 NVIC_EnableIRQ(USB0_IRQn);
mbed_official 20:d38b72fed893 163
samux 8:335f2506f422 164 // USB Module Configuration
samux 8:335f2506f422 165 // Reset USB Module
samux 8:335f2506f422 166 USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
samux 8:335f2506f422 167 while(USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
mbed_official 20:d38b72fed893 168
samux 8:335f2506f422 169 // Set BDT Base Register
mbed_official 22:5b7d31d9d3f3 170 USB0->BDTPAGE1 = (uint8_t)((uint32_t)bdt>>8);
mbed_official 22:5b7d31d9d3f3 171 USB0->BDTPAGE2 = (uint8_t)((uint32_t)bdt>>16);
mbed_official 22:5b7d31d9d3f3 172 USB0->BDTPAGE3 = (uint8_t)((uint32_t)bdt>>24);
samux 8:335f2506f422 173
samux 8:335f2506f422 174 // Clear interrupt flag
samux 8:335f2506f422 175 USB0->ISTAT = 0xff;
samux 8:335f2506f422 176
samux 8:335f2506f422 177 // USB Interrupt Enablers
mbed_official 20:d38b72fed893 178 USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK |
mbed_official 20:d38b72fed893 179 USB_INTEN_SOFTOKEN_MASK |
samux 8:335f2506f422 180 USB_INTEN_ERROREN_MASK |
mjr 28:0596f144dad5 181 USB_INTEN_SLEEPEN_MASK |
mjr 28:0596f144dad5 182 USB_INTEN_RESUMEEN_MASK |
samux 8:335f2506f422 183 USB_INTEN_USBRSTEN_MASK;
mbed_official 20:d38b72fed893 184
mbed_official 20:d38b72fed893 185 // Disable weak pull downs
mbed_official 20:d38b72fed893 186 USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);
mbed_official 20:d38b72fed893 187
samux 8:335f2506f422 188 USB0->USBTRC0 |= 0x40;
samux 8:335f2506f422 189 }
samux 8:335f2506f422 190
mjr 35:53e1a208f582 191 USBHAL::~USBHAL(void) { }
mjr 35:53e1a208f582 192
mjr 35:53e1a208f582 193 void USBHAL::connect(void) {
mjr 35:53e1a208f582 194 // enable USB
mjr 35:53e1a208f582 195 USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
mjr 35:53e1a208f582 196 // Pull up enable
mjr 35:53e1a208f582 197 USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
mjr 34:884405d998bb 198 }
samux 8:335f2506f422 199
mjr 35:53e1a208f582 200 void USBHAL::disconnect(void)
mjr 34:884405d998bb 201 {
samux 8:335f2506f422 202 // disable USB
samux 8:335f2506f422 203 USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
mjr 34:884405d998bb 204
samux 8:335f2506f422 205 // Pull up disable
samux 8:335f2506f422 206 USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
bogdanm 14:d495202c90f4 207
bogdanm 14:d495202c90f4 208 //Free buffers if required:
mjr 34:884405d998bb 209 for (int i = 0 ; i < NUMBER_OF_PHYSICAL_ENDPOINTS ; i++) {
bogdanm 14:d495202c90f4 210 free(endpoint_buffer[i]);
bogdanm 14:d495202c90f4 211 endpoint_buffer[i] = NULL;
bogdanm 14:d495202c90f4 212 }
samux 8:335f2506f422 213 }
samux 8:335f2506f422 214
mjr 35:53e1a208f582 215 void USBHAL::configureDevice(void) {
samux 8:335f2506f422 216 // not needed
samux 8:335f2506f422 217 }
samux 8:335f2506f422 218
mjr 35:53e1a208f582 219 void USBHAL::unconfigureDevice(void) {
samux 8:335f2506f422 220 // not needed
samux 8:335f2506f422 221 }
samux 8:335f2506f422 222
mjr 35:53e1a208f582 223 void USBHAL::setAddress(uint8_t address) {
samux 8:335f2506f422 224 // we don't set the address now otherwise the usb controller does not ack
samux 8:335f2506f422 225 // we set a flag instead
samux 8:335f2506f422 226 // see usbisr when an IN token is received
samux 8:335f2506f422 227 set_addr = 1;
samux 8:335f2506f422 228 addr = address;
mjr 35:53e1a208f582 229 }
samux 8:335f2506f422 230
mjr 35:53e1a208f582 231 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags)
mjr 34:884405d998bb 232 {
mjr 35:53e1a208f582 233 uint32_t handshake_flag;
mjr 35:53e1a208f582 234 uint32_t alo_size;
mjr 35:53e1a208f582 235
mjr 34:884405d998bb 236 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS)
mjr 34:884405d998bb 237 return false;
samux 8:335f2506f422 238
samux 8:335f2506f422 239 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 34:884405d998bb 240
mjr 35:53e1a208f582 241 if (flags & ISOCHRONOUS) {
mjr 35:53e1a208f582 242 handshake_flag = 0;
mjr 35:53e1a208f582 243 alo_size = 1023;
mjr 35:53e1a208f582 244 }
mjr 35:53e1a208f582 245 else {
mjr 35:53e1a208f582 246 handshake_flag = USB_ENDPT_EPHSHK_MASK;
mjr 35:53e1a208f582 247 alo_size = 64;
mjr 34:884405d998bb 248 }
mjr 34:884405d998bb 249
mjr 35:53e1a208f582 250 if (endpoint_buffer[endpoint] == 0)
mjr 35:53e1a208f582 251 endpoint_buffer[endpoint] = (uint8_t *)malloc(alo_size);
mjr 35:53e1a208f582 252
mjr 35:53e1a208f582 253 // IN endpt -> device to host (TX)
mjr 35:53e1a208f582 254 if (IN_EP(endpoint)) {
mjr 35:53e1a208f582 255 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
mjr 35:53e1a208f582 256 USB_ENDPT_EPTXEN_MASK; // en TX (IN) tran
mjr 35:53e1a208f582 257 bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = (uint32_t) endpoint_buffer[endpoint];
mjr 35:53e1a208f582 258 bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = 0;
mjr 35:53e1a208f582 259 }
mjr 35:53e1a208f582 260 // OUT endpt -> host to device (RX)
mjr 35:53e1a208f582 261 else {
mjr 35:53e1a208f582 262 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
mjr 35:53e1a208f582 263 USB_ENDPT_EPRXEN_MASK; // en RX (OUT) tran.
mjr 35:53e1a208f582 264 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].byte_count = maxPacket;
mjr 35:53e1a208f582 265 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address = (uint32_t) endpoint_buffer[endpoint];
mjr 35:53e1a208f582 266 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].address = 0;
mjr 35:53e1a208f582 267 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 35:53e1a208f582 268 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info = 0;
samux 8:335f2506f422 269 }
samux 8:335f2506f422 270
mjr 35:53e1a208f582 271 set_DATA1(endpoint);
mjr 35:53e1a208f582 272 clear_completion(endpoint);
samux 8:335f2506f422 273
samux 8:335f2506f422 274 return true;
samux 8:335f2506f422 275 }
samux 8:335f2506f422 276
mjr 34:884405d998bb 277 // read setup packet
mjr 35:53e1a208f582 278 void USBHAL::EP0setup(uint8_t *buffer) {
mjr 34:884405d998bb 279 uint32_t sz;
mjr 34:884405d998bb 280 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 281 }
mjr 34:884405d998bb 282
mjr 35:53e1a208f582 283 void USBHAL::EP0readStage(void) {
mjr 35:53e1a208f582 284 set_DATA0(EP0OUT); // set DATA0 for the next packet
mjr 35:53e1a208f582 285 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK); // start the read
mjr 34:884405d998bb 286 }
mjr 34:884405d998bb 287
mjr 35:53e1a208f582 288 void USBHAL::EP0read(void) {
mjr 35:53e1a208f582 289 uint32_t idx = EP_BDT_IDX(PHY_TO_LOG(EP0OUT), RX, 0);
mjr 35:53e1a208f582 290 bdt[idx].byte_count = MAX_PACKET_SIZE_EP0;
mjr 34:884405d998bb 291 }
mjr 34:884405d998bb 292
mjr 35:53e1a208f582 293 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer) {
mjr 35:53e1a208f582 294 uint32_t sz;
mjr 34:884405d998bb 295 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 296 return sz;
mjr 34:884405d998bb 297 }
mjr 34:884405d998bb 298
mjr 35:53e1a208f582 299 void USBHAL::EP0write(uint8_t *buffer, uint32_t size) {
mjr 34:884405d998bb 300 endpointWrite(EP0IN, buffer, size);
mjr 34:884405d998bb 301 }
mjr 34:884405d998bb 302
mjr 35:53e1a208f582 303 void USBHAL::EP0getWriteResult(void) {
mjr 34:884405d998bb 304 }
mjr 34:884405d998bb 305
mjr 35:53e1a208f582 306 void USBHAL::EP0stall(void) {
mjr 34:884405d998bb 307 stallEndpoint(EP0OUT);
mjr 34:884405d998bb 308 }
mjr 34:884405d998bb 309
mjr 35:53e1a208f582 310 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize)
mjr 34:884405d998bb 311 {
mjr 34:884405d998bb 312 return EP_PENDING;
mjr 34:884405d998bb 313 }
mjr 34:884405d998bb 314
mjr 35:53e1a208f582 315 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t * buffer, uint32_t *bytesRead)
mjr 34:884405d998bb 316 {
mjr 34:884405d998bb 317 // validate the endpoint number
mjr 34:884405d998bb 318 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS)
mjr 34:884405d998bb 319 return EP_INVALID;
mjr 34:884405d998bb 320
mjr 35:53e1a208f582 321 // we can only read an OUT endpoint
mjr 34:884405d998bb 322 if (IN_EP(endpoint))
mjr 34:884405d998bb 323 return EP_INVALID;
mjr 35:53e1a208f582 324
mjr 35:53e1a208f582 325 // get the logical endpoint number
mjr 34:884405d998bb 326 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 34:884405d998bb 327
mjr 35:53e1a208f582 328 // get the BDT index
mjr 35:53e1a208f582 329 int idx = EP_BDT_IDX(log_endpoint, RX, 0);
mjr 34:884405d998bb 330
mjr 35:53e1a208f582 331 // make sure we're marked as complete (except for isochronous endpoints)
mjr 35:53e1a208f582 332 bool iso = !(USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK);
mjr 35:53e1a208f582 333 if ((log_endpoint != 0) && !iso && !(epComplete & EP(endpoint)))
mjr 34:884405d998bb 334 return EP_PENDING;
mjr 34:884405d998bb 335
mjr 35:53e1a208f582 336 // this consumes the read status - clear the completion flag to prepare for the next read
mjr 35:53e1a208f582 337 clear_completion(endpoint);
mjr 35:53e1a208f582 338
mjr 35:53e1a208f582 339 // copy the data from the hardware buffer to the caller's buffer
mjr 34:884405d998bb 340 uint8_t *ep_buf = (uint8_t *)bdt[idx].address;
mjr 34:884405d998bb 341 uint32_t sz = *bytesRead = bdt[idx].byte_count;
mjr 35:53e1a208f582 342 for (uint32_t n = 0; n < sz; n++)
mjr 34:884405d998bb 343 buffer[n] = ep_buf[n];
mjr 34:884405d998bb 344
mjr 35:53e1a208f582 345 // Figure the DATA0/DATA1 value for the next packet. If we have a
mjr 35:53e1a208f582 346 // SETUP token with no data stage, it's DATA0. Otherwise we just
mjr 35:53e1a208f582 347 // flip the last value, assuming the new token matches what we expect.
mjr 35:53e1a208f582 348 bool setup = (log_endpoint == 0 && TOK_PID(idx) == SETUP_TOKEN);
mjr 35:53e1a208f582 349 if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1)) {
mjr 35:53e1a208f582 350 if (setup && (buffer[6] == 0)) {
mjr 35:53e1a208f582 351 set_DATA0(EP0OUT); // no setup data stage, so set DATA0
mjr 35:53e1a208f582 352 }
mjr 35:53e1a208f582 353 else {
mjr 35:53e1a208f582 354 toggle_DATA01(endpoint); // in other cases, toggle the DATA0/DATA1 status on each packet
mjr 35:53e1a208f582 355 }
mjr 35:53e1a208f582 356 }
mjr 35:53e1a208f582 357
mjr 35:53e1a208f582 358 // hand off the BDT to start the next read
mjr 35:53e1a208f582 359 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | (((Data1 >> endpoint) & 1) << 6);
mjr 35:53e1a208f582 360
mjr 35:53e1a208f582 361 // turn off the "suspend token busy" flag (the hardware sets this for
mjr 35:53e1a208f582 362 // SETUP tokens to let us process tokens sequentially in this special case)
mjr 35:53e1a208f582 363 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 34:884405d998bb 364
mjr 35:53e1a208f582 365 // operation completed
samux 8:335f2506f422 366 return EP_COMPLETED;
samux 8:335f2506f422 367 }
samux 8:335f2506f422 368
mjr 35:53e1a208f582 369 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size)
mjr 34:884405d998bb 370 {
mjr 34:884405d998bb 371 // validate the endpoint number
mjr 34:884405d998bb 372 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS)
samux 8:335f2506f422 373 return EP_INVALID;
samux 8:335f2506f422 374
mjr 35:53e1a208f582 375 // if write on a OUT endpoint -> error
mjr 34:884405d998bb 376 if (OUT_EP(endpoint))
samux 8:335f2506f422 377 return EP_INVALID;
mjr 35:53e1a208f582 378
mjr 35:53e1a208f582 379 // get the BDT entry for the endpoint
mjr 35:53e1a208f582 380 int idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
mbed_official 20:d38b72fed893 381
mjr 35:53e1a208f582 382 // if the buffer is owned by the SIE, there's a previous write that
mjr 35:53e1a208f582 383 // hasn't finished yet - we can't start a new one yet
mjr 35:53e1a208f582 384 if (bdt[idx].info & BD_OWN_MASK)
mjr 34:884405d998bb 385 return EP_BUSY;
mbed_official 20:d38b72fed893 386
mjr 35:53e1a208f582 387 // copy the bytes to the endpoint hardware buffer
mjr 34:884405d998bb 388 uint8_t *ep_buf = (uint8_t *)bdt[idx].address;
mjr 34:884405d998bb 389 bdt[idx].byte_count = size;
mjr 34:884405d998bb 390 for (uint32_t n = 0 ; n < size ; n++)
samux 8:335f2506f422 391 ep_buf[n] = data[n];
mjr 35:53e1a208f582 392
mjr 35:53e1a208f582 393 // toggle DATA0/DATA1 on each packet
mjr 35:53e1a208f582 394 toggle_DATA01(endpoint);
mjr 35:53e1a208f582 395
mjr 35:53e1a208f582 396 // hand the buffer to the SIE
mjr 34:884405d998bb 397 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | (((Data1 >> endpoint) & 1) << 6);
mbed_official 20:d38b72fed893 398
mjr 35:53e1a208f582 399 // the packet is now pending
samux 8:335f2506f422 400 return EP_PENDING;
samux 8:335f2506f422 401 }
samux 8:335f2506f422 402
mjr 35:53e1a208f582 403 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint)
mjr 34:884405d998bb 404 {
mjr 35:53e1a208f582 405 if (epComplete & EP(endpoint)) {
mjr 35:53e1a208f582 406 clear_completion(endpoint);
samux 8:335f2506f422 407 return EP_COMPLETED;
samux 8:335f2506f422 408 }
mjr 35:53e1a208f582 409
samux 8:335f2506f422 410 return EP_PENDING;
samux 8:335f2506f422 411 }
samux 8:335f2506f422 412
mjr 35:53e1a208f582 413 void USBHAL::stallEndpoint(uint8_t endpoint) {
samux 8:335f2506f422 414 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 415 }
samux 8:335f2506f422 416
mjr 35:53e1a208f582 417 void USBHAL::unstallEndpoint(uint8_t endpoint) {
samux 8:335f2506f422 418 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 419 }
samux 8:335f2506f422 420
mjr 35:53e1a208f582 421 bool USBHAL::getEndpointStallState(uint8_t endpoint) {
samux 8:335f2506f422 422 uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
samux 8:335f2506f422 423 return (stall) ? true : false;
samux 8:335f2506f422 424 }
samux 8:335f2506f422 425
mjr 35:53e1a208f582 426 void USBHAL::remoteWakeup(void) {
samux 8:335f2506f422 427 // [TODO]
samux 8:335f2506f422 428 }
samux 8:335f2506f422 429
samux 8:335f2506f422 430
mjr 35:53e1a208f582 431 void USBHAL::_usbisr(void) {
samux 8:335f2506f422 432 instance->usbisr();
samux 8:335f2506f422 433 }
samux 8:335f2506f422 434
samux 8:335f2506f422 435
mjr 35:53e1a208f582 436 void USBHAL::usbisr(void) {
samux 8:335f2506f422 437 uint8_t i;
samux 8:335f2506f422 438 uint8_t istat = USB0->ISTAT;
samux 8:335f2506f422 439
samux 8:335f2506f422 440 // reset interrupt
mjr 35:53e1a208f582 441 if (istat & USB_ISTAT_USBRST_MASK) {
mjr 35:53e1a208f582 442
samux 8:335f2506f422 443 // disable all endpt
mjr 35:53e1a208f582 444 for(i = 0; i < 16; i++) {
samux 8:335f2506f422 445 USB0->ENDPOINT[i].ENDPT = 0x00;
mjr 35:53e1a208f582 446 }
samux 8:335f2506f422 447
samux 8:335f2506f422 448 // enable control endpoint
samux 8:335f2506f422 449 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 450 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 451
samux 8:335f2506f422 452 Data1 = 0x55555555;
mjr 35:53e1a208f582 453 epComplete = 0;
samux 8:335f2506f422 454 USB0->CTL |= USB_CTL_ODDRST_MASK;
samux 8:335f2506f422 455
samux 8:335f2506f422 456 USB0->ISTAT = 0xFF; // clear all interrupt status flags
samux 8:335f2506f422 457 USB0->ERRSTAT = 0xFF; // clear all error flags
samux 8:335f2506f422 458 USB0->ERREN = 0xFF; // enable error interrupt sources
samux 8:335f2506f422 459 USB0->ADDR = 0x00; // set default address
mjr 28:0596f144dad5 460
mjr 31:81f57ea86f8f 461 // notify upper layers of the bus reset
mjr 31:81f57ea86f8f 462 busReset();
mjr 31:81f57ea86f8f 463
mjr 31:81f57ea86f8f 464 // we're not suspended
mjr 28:0596f144dad5 465 suspendStateChanged(0);
samux 8:335f2506f422 466
samux 8:335f2506f422 467 return;
samux 8:335f2506f422 468 }
samux 8:335f2506f422 469
samux 8:335f2506f422 470 // resume interrupt
mjr 35:53e1a208f582 471 if (istat & USB_ISTAT_RESUME_MASK) {
samux 8:335f2506f422 472 USB0->ISTAT = USB_ISTAT_RESUME_MASK;
mjr 28:0596f144dad5 473 suspendStateChanged(0);
samux 8:335f2506f422 474 }
samux 8:335f2506f422 475
samux 8:335f2506f422 476 // SOF interrupt
mjr 35:53e1a208f582 477 if (istat & USB_ISTAT_SOFTOK_MASK) {
mbed_official 20:d38b72fed893 478 USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
samux 8:335f2506f422 479 // SOF event, read frame number
samux 8:335f2506f422 480 SOF(frameNumber());
samux 8:335f2506f422 481 }
mbed_official 20:d38b72fed893 482
samux 8:335f2506f422 483 // stall interrupt
mjr 35:53e1a208f582 484 if (istat & 1<<7) {
samux 8:335f2506f422 485 if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK)
samux 8:335f2506f422 486 USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 487 USB0->ISTAT |= USB_ISTAT_STALL_MASK;
samux 8:335f2506f422 488 }
samux 8:335f2506f422 489
samux 8:335f2506f422 490 // token interrupt
mjr 35:53e1a208f582 491 if (istat & 1<<3) {
mjr 35:53e1a208f582 492 uint32_t num = (USB0->STAT >> 4) & 0x0F;
mjr 35:53e1a208f582 493 uint32_t dir = (USB0->STAT >> 3) & 0x01;
mjr 35:53e1a208f582 494 uint32_t ev_odd = (USB0->STAT >> 2) & 0x01;
mjr 34:884405d998bb 495 int endpoint = (num << 1) | dir;
samux 8:335f2506f422 496
samux 8:335f2506f422 497 // setup packet
mjr 35:53e1a208f582 498 if ((num == 0) && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == SETUP_TOKEN)) {
samux 8:335f2506f422 499 Data1 &= ~0x02;
mjr 35:53e1a208f582 500 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
mjr 35:53e1a208f582 501 bdt[EP_BDT_IDX(0, TX, ODD)].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 502
mjr 35:53e1a208f582 503 // EP0 SETUP event (SETUP data received)
samux 8:335f2506f422 504 EP0setupCallback();
mjr 35:53e1a208f582 505
mjr 35:53e1a208f582 506 } else {
mjr 35:53e1a208f582 507 // OUT packet
mjr 35:53e1a208f582 508 if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == OUT_TOKEN) {
samux 8:335f2506f422 509 if (num == 0)
samux 8:335f2506f422 510 EP0out();
mjr 35:53e1a208f582 511 else {
mjr 35:53e1a208f582 512 epComplete |= EP(endpoint);
mjr 35:53e1a208f582 513 if ((instance->*(epCallback[endpoint - 2]))()) {
mjr 35:53e1a208f582 514 epComplete &= ~EP(endpoint);
mjr 35:53e1a208f582 515 }
samux 8:335f2506f422 516 }
samux 8:335f2506f422 517 }
samux 8:335f2506f422 518
mjr 35:53e1a208f582 519 // IN packet
mjr 35:53e1a208f582 520 if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == IN_TOKEN) {
mjr 35:53e1a208f582 521 if (num == 0) {
samux 8:335f2506f422 522 EP0in();
mjr 35:53e1a208f582 523 if (set_addr == 1) {
samux 8:335f2506f422 524 USB0->ADDR = addr & 0x7F;
samux 8:335f2506f422 525 set_addr = 0;
samux 8:335f2506f422 526 }
samux 8:335f2506f422 527 }
mjr 35:53e1a208f582 528 else {
mjr 35:53e1a208f582 529 epComplete |= EP(endpoint);
mjr 35:53e1a208f582 530 if ((instance->*(epCallback[endpoint - 2]))()) {
mjr 35:53e1a208f582 531 epComplete &= ~EP(endpoint);
mjr 35:53e1a208f582 532 }
samux 8:335f2506f422 533 }
samux 8:335f2506f422 534 }
samux 8:335f2506f422 535 }
samux 8:335f2506f422 536
samux 8:335f2506f422 537 USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
samux 8:335f2506f422 538 }
mbed_official 20:d38b72fed893 539
samux 8:335f2506f422 540 // sleep interrupt
mjr 35:53e1a208f582 541 if (istat & 1<<4) {
samux 8:335f2506f422 542 USB0->ISTAT |= USB_ISTAT_SLEEP_MASK;
mjr 28:0596f144dad5 543 suspendStateChanged(1);
mbed_official 20:d38b72fed893 544 }
samux 8:335f2506f422 545
samux 8:335f2506f422 546 // error interrupt
mjr 35:53e1a208f582 547 if (istat & USB_ISTAT_ERROR_MASK) {
samux 8:335f2506f422 548 USB0->ERRSTAT = 0xFF;
samux 8:335f2506f422 549 USB0->ISTAT |= USB_ISTAT_ERROR_MASK;
samux 8:335f2506f422 550 }
samux 8:335f2506f422 551 }
samux 8:335f2506f422 552
mjr 35:53e1a208f582 553
samux 8:335f2506f422 554 #endif