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:
Sun Jul 27 03:45:08 2014 +0000
Revision:
31:81f57ea86f8f
Parent:
28:0596f144dad5
Child:
34:884405d998bb
Notify upper layers of a bus reset; return false on ControlOut sync to try to fix reconnect issue (unsuccessful)

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
samux 8:335f2506f422 25 static volatile int epComplete = 0;
samux 8:335f2506f422 26
samux 8:335f2506f422 27 // Convert physical endpoint number to register bit
samux 8:335f2506f422 28 #define EP(endpoint) (1<<(endpoint))
samux 8:335f2506f422 29
samux 8:335f2506f422 30 // Convert physical to logical
samux 8:335f2506f422 31 #define PHY_TO_LOG(endpoint) ((endpoint)>>1)
samux 8:335f2506f422 32
samux 8:335f2506f422 33 // Get endpoint direction
samux 8:335f2506f422 34 #define IN_EP(endpoint) ((endpoint) & 1U ? true : false)
samux 8:335f2506f422 35 #define OUT_EP(endpoint) ((endpoint) & 1U ? false : true)
samux 8:335f2506f422 36
samux 8:335f2506f422 37 #define BD_OWN_MASK (1<<7)
samux 8:335f2506f422 38 #define BD_DATA01_MASK (1<<6)
samux 8:335f2506f422 39 #define BD_KEEP_MASK (1<<5)
samux 8:335f2506f422 40 #define BD_NINC_MASK (1<<4)
samux 8:335f2506f422 41 #define BD_DTS_MASK (1<<3)
samux 8:335f2506f422 42 #define BD_STALL_MASK (1<<2)
samux 8:335f2506f422 43
samux 8:335f2506f422 44 #define TX 1
samux 8:335f2506f422 45 #define RX 0
samux 8:335f2506f422 46 #define ODD 0
samux 8:335f2506f422 47 #define EVEN 1
samux 8:335f2506f422 48 // this macro waits a physical endpoint number
samux 8:335f2506f422 49 #define EP_BDT_IDX(ep, dir, odd) (((ep * 4) + (2 * dir) + (1 * odd)))
samux 8:335f2506f422 50
samux 8:335f2506f422 51 #define SETUP_TOKEN 0x0D
samux 8:335f2506f422 52 #define IN_TOKEN 0x09
samux 8:335f2506f422 53 #define OUT_TOKEN 0x01
samux 8:335f2506f422 54 #define TOK_PID(idx) ((bdt[idx].info >> 2) & 0x0F)
samux 8:335f2506f422 55
samux 8:335f2506f422 56 // for each endpt: 8 bytes
samux 8:335f2506f422 57 typedef struct BDT {
samux 8:335f2506f422 58 uint8_t info; // BD[0:7]
samux 8:335f2506f422 59 uint8_t dummy; // RSVD: BD[8:15]
samux 8:335f2506f422 60 uint16_t byte_count; // BD[16:32]
samux 8:335f2506f422 61 uint32_t address; // Addr
mbed_official 20:d38b72fed893 62 } BDT;
samux 8:335f2506f422 63
samux 8:335f2506f422 64
samux 8:335f2506f422 65 // there are:
samux 8:335f2506f422 66 // * 16 bidirectionnal endpt -> 32 physical endpt
samux 8:335f2506f422 67 // * as there are ODD and EVEN buffer -> 32*2 bdt
samux 8:335f2506f422 68 __attribute__((__aligned__(512))) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
samux 9:354942d2fa38 69 uint8_t * endpoint_buffer[(NUMBER_OF_PHYSICAL_ENDPOINTS - 2) * 2];
samux 9:354942d2fa38 70 uint8_t * endpoint_buffer_iso[2*2];
samux 8:335f2506f422 71
samux 8:335f2506f422 72 static uint8_t set_addr = 0;
samux 8:335f2506f422 73 static uint8_t addr = 0;
samux 8:335f2506f422 74
samux 8:335f2506f422 75 static uint32_t Data1 = 0x55555555;
samux 8:335f2506f422 76
samux 8:335f2506f422 77 static uint32_t frameNumber() {
bogdanm 13:16731886c049 78 return((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
samux 8:335f2506f422 79 }
samux 8:335f2506f422 80
samux 8:335f2506f422 81 uint32_t USBHAL::endpointReadcore(uint8_t endpoint, uint8_t *buffer) {
samux 8:335f2506f422 82 return 0;
samux 8:335f2506f422 83 }
samux 8:335f2506f422 84
mbed_official 20:d38b72fed893 85 USBHAL::USBHAL(void) {
samux 8:335f2506f422 86 // Disable IRQ
samux 8:335f2506f422 87 NVIC_DisableIRQ(USB0_IRQn);
mbed_official 20:d38b72fed893 88
mbed_official 22:5b7d31d9d3f3 89 #if defined(TARGET_K64F)
mbed_official 22:5b7d31d9d3f3 90 MPU->CESR=0;
mbed_official 22:5b7d31d9d3f3 91 #endif
samux 8:335f2506f422 92 // fill in callback array
samux 8:335f2506f422 93 epCallback[0] = &USBHAL::EP1_OUT_callback;
samux 8:335f2506f422 94 epCallback[1] = &USBHAL::EP1_IN_callback;
samux 8:335f2506f422 95 epCallback[2] = &USBHAL::EP2_OUT_callback;
samux 8:335f2506f422 96 epCallback[3] = &USBHAL::EP2_IN_callback;
samux 8:335f2506f422 97 epCallback[4] = &USBHAL::EP3_OUT_callback;
samux 8:335f2506f422 98 epCallback[5] = &USBHAL::EP3_IN_callback;
samux 8:335f2506f422 99 epCallback[6] = &USBHAL::EP4_OUT_callback;
samux 8:335f2506f422 100 epCallback[7] = &USBHAL::EP4_IN_callback;
samux 8:335f2506f422 101 epCallback[8] = &USBHAL::EP5_OUT_callback;
samux 8:335f2506f422 102 epCallback[9] = &USBHAL::EP5_IN_callback;
samux 8:335f2506f422 103 epCallback[10] = &USBHAL::EP6_OUT_callback;
samux 8:335f2506f422 104 epCallback[11] = &USBHAL::EP6_IN_callback;
samux 8:335f2506f422 105 epCallback[12] = &USBHAL::EP7_OUT_callback;
samux 8:335f2506f422 106 epCallback[13] = &USBHAL::EP7_IN_callback;
samux 8:335f2506f422 107 epCallback[14] = &USBHAL::EP8_OUT_callback;
samux 8:335f2506f422 108 epCallback[15] = &USBHAL::EP8_IN_callback;
samux 8:335f2506f422 109 epCallback[16] = &USBHAL::EP9_OUT_callback;
samux 8:335f2506f422 110 epCallback[17] = &USBHAL::EP9_IN_callback;
samux 8:335f2506f422 111 epCallback[18] = &USBHAL::EP10_OUT_callback;
samux 8:335f2506f422 112 epCallback[19] = &USBHAL::EP10_IN_callback;
samux 8:335f2506f422 113 epCallback[20] = &USBHAL::EP11_OUT_callback;
samux 8:335f2506f422 114 epCallback[21] = &USBHAL::EP11_IN_callback;
samux 8:335f2506f422 115 epCallback[22] = &USBHAL::EP12_OUT_callback;
samux 8:335f2506f422 116 epCallback[23] = &USBHAL::EP12_IN_callback;
samux 8:335f2506f422 117 epCallback[24] = &USBHAL::EP13_OUT_callback;
samux 8:335f2506f422 118 epCallback[25] = &USBHAL::EP13_IN_callback;
samux 8:335f2506f422 119 epCallback[26] = &USBHAL::EP14_OUT_callback;
samux 8:335f2506f422 120 epCallback[27] = &USBHAL::EP14_IN_callback;
samux 8:335f2506f422 121 epCallback[28] = &USBHAL::EP15_OUT_callback;
samux 8:335f2506f422 122 epCallback[29] = &USBHAL::EP15_IN_callback;
mbed_official 20:d38b72fed893 123
mbed_official 20:d38b72fed893 124
samux 8:335f2506f422 125 // choose usb src as PLL
samux 8:335f2506f422 126 SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);
mbed_official 20:d38b72fed893 127
samux 8:335f2506f422 128 // enable OTG clock
samux 8:335f2506f422 129 SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
samux 8:335f2506f422 130
samux 8:335f2506f422 131 // Attach IRQ
samux 8:335f2506f422 132 instance = this;
samux 8:335f2506f422 133 NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
samux 8:335f2506f422 134 NVIC_EnableIRQ(USB0_IRQn);
mbed_official 20:d38b72fed893 135
samux 8:335f2506f422 136 // USB Module Configuration
samux 8:335f2506f422 137 // Reset USB Module
samux 8:335f2506f422 138 USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
samux 8:335f2506f422 139 while(USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
mbed_official 20:d38b72fed893 140
samux 8:335f2506f422 141 // Set BDT Base Register
mbed_official 22:5b7d31d9d3f3 142 USB0->BDTPAGE1 = (uint8_t)((uint32_t)bdt>>8);
mbed_official 22:5b7d31d9d3f3 143 USB0->BDTPAGE2 = (uint8_t)((uint32_t)bdt>>16);
mbed_official 22:5b7d31d9d3f3 144 USB0->BDTPAGE3 = (uint8_t)((uint32_t)bdt>>24);
samux 8:335f2506f422 145
samux 8:335f2506f422 146 // Clear interrupt flag
samux 8:335f2506f422 147 USB0->ISTAT = 0xff;
samux 8:335f2506f422 148
samux 8:335f2506f422 149 // USB Interrupt Enablers
mbed_official 20:d38b72fed893 150 USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK |
mbed_official 20:d38b72fed893 151 USB_INTEN_SOFTOKEN_MASK |
samux 8:335f2506f422 152 USB_INTEN_ERROREN_MASK |
mjr 28:0596f144dad5 153 USB_INTEN_SLEEPEN_MASK |
mjr 28:0596f144dad5 154 USB_INTEN_RESUMEEN_MASK |
samux 8:335f2506f422 155 USB_INTEN_USBRSTEN_MASK;
mbed_official 20:d38b72fed893 156
mbed_official 20:d38b72fed893 157 // Disable weak pull downs
mbed_official 20:d38b72fed893 158 USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);
mbed_official 20:d38b72fed893 159
samux 8:335f2506f422 160 USB0->USBTRC0 |= 0x40;
samux 8:335f2506f422 161 }
samux 8:335f2506f422 162
samux 8:335f2506f422 163 USBHAL::~USBHAL(void) { }
samux 8:335f2506f422 164
samux 8:335f2506f422 165 void USBHAL::connect(void) {
samux 8:335f2506f422 166 // enable USB
samux 8:335f2506f422 167 USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
samux 8:335f2506f422 168 // Pull up enable
samux 8:335f2506f422 169 USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
samux 8:335f2506f422 170 }
samux 8:335f2506f422 171
samux 8:335f2506f422 172 void USBHAL::disconnect(void) {
samux 8:335f2506f422 173 // disable USB
samux 8:335f2506f422 174 USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
samux 8:335f2506f422 175 // Pull up disable
samux 8:335f2506f422 176 USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
bogdanm 14:d495202c90f4 177
bogdanm 14:d495202c90f4 178 //Free buffers if required:
bogdanm 14:d495202c90f4 179 for (int i = 0; i<(NUMBER_OF_PHYSICAL_ENDPOINTS - 2) * 2; i++) {
bogdanm 14:d495202c90f4 180 free(endpoint_buffer[i]);
bogdanm 14:d495202c90f4 181 endpoint_buffer[i] = NULL;
bogdanm 14:d495202c90f4 182 }
bogdanm 14:d495202c90f4 183 free(endpoint_buffer_iso[2]);
bogdanm 14:d495202c90f4 184 endpoint_buffer_iso[2] = NULL;
bogdanm 14:d495202c90f4 185 free(endpoint_buffer_iso[0]);
bogdanm 14:d495202c90f4 186 endpoint_buffer_iso[0] = NULL;
samux 8:335f2506f422 187 }
samux 8:335f2506f422 188
samux 8:335f2506f422 189 void USBHAL::configureDevice(void) {
samux 8:335f2506f422 190 // not needed
samux 8:335f2506f422 191 }
samux 8:335f2506f422 192
samux 8:335f2506f422 193 void USBHAL::unconfigureDevice(void) {
samux 8:335f2506f422 194 // not needed
samux 8:335f2506f422 195 }
samux 8:335f2506f422 196
samux 8:335f2506f422 197 void USBHAL::setAddress(uint8_t address) {
samux 8:335f2506f422 198 // we don't set the address now otherwise the usb controller does not ack
samux 8:335f2506f422 199 // we set a flag instead
samux 8:335f2506f422 200 // see usbisr when an IN token is received
samux 8:335f2506f422 201 set_addr = 1;
samux 8:335f2506f422 202 addr = address;
mjr 28:0596f144dad5 203 }
samux 8:335f2506f422 204
samux 8:335f2506f422 205 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags) {
samux 8:335f2506f422 206 uint32_t handshake_flag = 0;
samux 8:335f2506f422 207 uint8_t * buf;
samux 8:335f2506f422 208
samux 8:335f2506f422 209 if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
samux 8:335f2506f422 210 return false;
samux 8:335f2506f422 211 }
samux 8:335f2506f422 212
samux 8:335f2506f422 213 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
samux 8:335f2506f422 214
samux 8:335f2506f422 215 if ((flags & ISOCHRONOUS) == 0) {
samux 8:335f2506f422 216 handshake_flag = USB_ENDPT_EPHSHK_MASK;
samux 9:354942d2fa38 217 if (IN_EP(endpoint)) {
bogdanm 14:d495202c90f4 218 if (endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] == NULL)
bogdanm 14:d495202c90f4 219 endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] = (uint8_t *) malloc (64*2);
bogdanm 14:d495202c90f4 220 buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)][0];
samux 9:354942d2fa38 221 } else {
bogdanm 14:d495202c90f4 222 if (endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] == NULL)
bogdanm 14:d495202c90f4 223 endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] = (uint8_t *) malloc (64*2);
bogdanm 14:d495202c90f4 224 buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)][0];
samux 9:354942d2fa38 225 }
samux 8:335f2506f422 226 } else {
samux 9:354942d2fa38 227 if (IN_EP(endpoint)) {
bogdanm 14:d495202c90f4 228 if (endpoint_buffer_iso[2] == NULL)
bogdanm 14:d495202c90f4 229 endpoint_buffer_iso[2] = (uint8_t *) malloc (1023*2);
samux 8:335f2506f422 230 buf = &endpoint_buffer_iso[2][0];
samux 9:354942d2fa38 231 } else {
bogdanm 14:d495202c90f4 232 if (endpoint_buffer_iso[0] == NULL)
bogdanm 14:d495202c90f4 233 endpoint_buffer_iso[0] = (uint8_t *) malloc (1023*2);
samux 8:335f2506f422 234 buf = &endpoint_buffer_iso[0][0];
samux 9:354942d2fa38 235 }
samux 8:335f2506f422 236 }
samux 8:335f2506f422 237
samux 8:335f2506f422 238 // IN endpt -> device to host (TX)
samux 8:335f2506f422 239 if (IN_EP(endpoint)) {
samux 8:335f2506f422 240 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
samux 8:335f2506f422 241 USB_ENDPT_EPTXEN_MASK; // en TX (IN) tran
samux 8:335f2506f422 242 bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = (uint32_t) buf;
samux 8:335f2506f422 243 bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = 0;
samux 8:335f2506f422 244 }
samux 8:335f2506f422 245 // OUT endpt -> host to device (RX)
samux 8:335f2506f422 246 else {
samux 8:335f2506f422 247 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
samux 8:335f2506f422 248 USB_ENDPT_EPRXEN_MASK; // en RX (OUT) tran.
samux 8:335f2506f422 249 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].byte_count = maxPacket;
samux 8:335f2506f422 250 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address = (uint32_t) buf;
samux 8:335f2506f422 251 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info = BD_OWN_MASK | BD_DTS_MASK;
samux 8:335f2506f422 252 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info = 0;
samux 8:335f2506f422 253 }
samux 8:335f2506f422 254
samux 8:335f2506f422 255 Data1 |= (1 << endpoint);
samux 8:335f2506f422 256
samux 8:335f2506f422 257 return true;
samux 8:335f2506f422 258 }
samux 8:335f2506f422 259
samux 8:335f2506f422 260 // read setup packet
samux 8:335f2506f422 261 void USBHAL::EP0setup(uint8_t *buffer) {
samux 8:335f2506f422 262 uint32_t sz;
samux 8:335f2506f422 263 endpointReadResult(EP0OUT, buffer, &sz);
samux 8:335f2506f422 264 }
samux 8:335f2506f422 265
samux 8:335f2506f422 266 void USBHAL::EP0readStage(void) {
samux 8:335f2506f422 267 Data1 &= ~1UL; // set DATA0
samux 8:335f2506f422 268 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
samux 8:335f2506f422 269 }
samux 8:335f2506f422 270
samux 8:335f2506f422 271 void USBHAL::EP0read(void) {
samux 8:335f2506f422 272 uint32_t idx = EP_BDT_IDX(PHY_TO_LOG(EP0OUT), RX, 0);
samux 8:335f2506f422 273 bdt[idx].byte_count = MAX_PACKET_SIZE_EP0;
samux 8:335f2506f422 274 }
samux 8:335f2506f422 275
samux 8:335f2506f422 276 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer) {
samux 8:335f2506f422 277 uint32_t sz;
samux 8:335f2506f422 278 endpointReadResult(EP0OUT, buffer, &sz);
samux 8:335f2506f422 279 return sz;
samux 8:335f2506f422 280 }
samux 8:335f2506f422 281
samux 8:335f2506f422 282 void USBHAL::EP0write(uint8_t *buffer, uint32_t size) {
samux 8:335f2506f422 283 endpointWrite(EP0IN, buffer, size);
samux 8:335f2506f422 284 }
samux 8:335f2506f422 285
samux 8:335f2506f422 286 void USBHAL::EP0getWriteResult(void) {
samux 8:335f2506f422 287 }
samux 8:335f2506f422 288
samux 8:335f2506f422 289 void USBHAL::EP0stall(void) {
samux 8:335f2506f422 290 stallEndpoint(EP0OUT);
samux 8:335f2506f422 291 }
samux 8:335f2506f422 292
samux 8:335f2506f422 293 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize) {
samux 8:335f2506f422 294 endpoint = PHY_TO_LOG(endpoint);
samux 8:335f2506f422 295 uint32_t idx = EP_BDT_IDX(endpoint, RX, 0);
samux 8:335f2506f422 296 bdt[idx].byte_count = maximumSize;
samux 8:335f2506f422 297 return EP_PENDING;
samux 8:335f2506f422 298 }
samux 8:335f2506f422 299
samux 8:335f2506f422 300 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t * buffer, uint32_t *bytesRead) {
samux 8:335f2506f422 301 uint32_t n, sz, idx, setup = 0;
samux 8:335f2506f422 302 uint8_t not_iso;
samux 8:335f2506f422 303 uint8_t * ep_buf;
mbed_official 20:d38b72fed893 304
samux 8:335f2506f422 305 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mbed_official 20:d38b72fed893 306
samux 8:335f2506f422 307 if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
samux 8:335f2506f422 308 return EP_INVALID;
samux 8:335f2506f422 309 }
samux 8:335f2506f422 310
samux 8:335f2506f422 311 // if read on a IN endpoint -> error
samux 8:335f2506f422 312 if (IN_EP(endpoint)) {
samux 8:335f2506f422 313 return EP_INVALID;
samux 8:335f2506f422 314 }
samux 8:335f2506f422 315
samux 8:335f2506f422 316 idx = EP_BDT_IDX(log_endpoint, RX, 0);
samux 8:335f2506f422 317 sz = bdt[idx].byte_count;
samux 8:335f2506f422 318 not_iso = USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK;
samux 8:335f2506f422 319
samux 8:335f2506f422 320 //for isochronous endpoint, we don't wait an interrupt
samux 8:335f2506f422 321 if ((log_endpoint != 0) && not_iso && !(epComplete & EP(endpoint))) {
samux 8:335f2506f422 322 return EP_PENDING;
samux 8:335f2506f422 323 }
samux 8:335f2506f422 324
samux 8:335f2506f422 325 if ((log_endpoint == 0) && (TOK_PID(idx) == SETUP_TOKEN)) {
samux 8:335f2506f422 326 setup = 1;
samux 8:335f2506f422 327 }
samux 8:335f2506f422 328
samux 8:335f2506f422 329 // non iso endpoint
samux 8:335f2506f422 330 if (not_iso) {
samux 8:335f2506f422 331 ep_buf = endpoint_buffer[idx];
samux 8:335f2506f422 332 } else {
samux 8:335f2506f422 333 ep_buf = endpoint_buffer_iso[0];
samux 8:335f2506f422 334 }
samux 8:335f2506f422 335
samux 8:335f2506f422 336 for (n = 0; n < sz; n++) {
samux 8:335f2506f422 337 buffer[n] = ep_buf[n];
samux 8:335f2506f422 338 }
samux 8:335f2506f422 339
samux 8:335f2506f422 340 if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1)) {
samux 8:335f2506f422 341 if (setup && (buffer[6] == 0)) // if no setup data stage,
samux 8:335f2506f422 342 Data1 &= ~1UL; // set DATA0
mbed_official 20:d38b72fed893 343 else
samux 8:335f2506f422 344 Data1 ^= (1 << endpoint);
samux 8:335f2506f422 345 }
samux 8:335f2506f422 346
samux 8:335f2506f422 347 if (((Data1 >> endpoint) & 1)) {
samux 8:335f2506f422 348 bdt[idx].info = BD_DTS_MASK | BD_DATA01_MASK | BD_OWN_MASK;
samux 8:335f2506f422 349 }
samux 8:335f2506f422 350 else {
samux 8:335f2506f422 351 bdt[idx].info = BD_DTS_MASK | BD_OWN_MASK;
samux 8:335f2506f422 352 }
mbed_official 20:d38b72fed893 353
samux 8:335f2506f422 354 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
samux 8:335f2506f422 355 *bytesRead = sz;
samux 8:335f2506f422 356
samux 8:335f2506f422 357 epComplete &= ~EP(endpoint);
samux 8:335f2506f422 358 return EP_COMPLETED;
samux 8:335f2506f422 359 }
samux 8:335f2506f422 360
samux 8:335f2506f422 361 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
samux 8:335f2506f422 362 uint32_t idx, n;
samux 8:335f2506f422 363 uint8_t * ep_buf;
samux 8:335f2506f422 364
samux 8:335f2506f422 365 if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
samux 8:335f2506f422 366 return EP_INVALID;
samux 8:335f2506f422 367 }
samux 8:335f2506f422 368
samux 8:335f2506f422 369 // if write on a OUT endpoint -> error
samux 8:335f2506f422 370 if (OUT_EP(endpoint)) {
samux 8:335f2506f422 371 return EP_INVALID;
samux 8:335f2506f422 372 }
samux 8:335f2506f422 373
samux 8:335f2506f422 374 idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
samux 8:335f2506f422 375 bdt[idx].byte_count = size;
mbed_official 20:d38b72fed893 376
mbed_official 20:d38b72fed893 377
samux 8:335f2506f422 378 // non iso endpoint
samux 8:335f2506f422 379 if (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPHSHK_MASK) {
samux 8:335f2506f422 380 ep_buf = endpoint_buffer[idx];
samux 8:335f2506f422 381 } else {
samux 8:335f2506f422 382 ep_buf = endpoint_buffer_iso[2];
samux 8:335f2506f422 383 }
mbed_official 20:d38b72fed893 384
samux 8:335f2506f422 385 for (n = 0; n < size; n++) {
samux 8:335f2506f422 386 ep_buf[n] = data[n];
samux 8:335f2506f422 387 }
mbed_official 20:d38b72fed893 388
samux 8:335f2506f422 389 if ((Data1 >> endpoint) & 1) {
samux 8:335f2506f422 390 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
samux 8:335f2506f422 391 } else {
samux 8:335f2506f422 392 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
samux 8:335f2506f422 393 }
mbed_official 20:d38b72fed893 394
samux 8:335f2506f422 395 Data1 ^= (1 << endpoint);
mbed_official 20:d38b72fed893 396
samux 8:335f2506f422 397 return EP_PENDING;
samux 8:335f2506f422 398 }
samux 8:335f2506f422 399
samux 8:335f2506f422 400 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint) {
samux 8:335f2506f422 401 if (epComplete & EP(endpoint)) {
samux 8:335f2506f422 402 epComplete &= ~EP(endpoint);
samux 8:335f2506f422 403 return EP_COMPLETED;
samux 8:335f2506f422 404 }
samux 8:335f2506f422 405
samux 8:335f2506f422 406 return EP_PENDING;
samux 8:335f2506f422 407 }
samux 8:335f2506f422 408
samux 8:335f2506f422 409 void USBHAL::stallEndpoint(uint8_t endpoint) {
samux 8:335f2506f422 410 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 411 }
samux 8:335f2506f422 412
samux 8:335f2506f422 413 void USBHAL::unstallEndpoint(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
samux 8:335f2506f422 417 bool USBHAL::getEndpointStallState(uint8_t endpoint) {
samux 8:335f2506f422 418 uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
samux 8:335f2506f422 419 return (stall) ? true : false;
samux 8:335f2506f422 420 }
samux 8:335f2506f422 421
samux 8:335f2506f422 422 void USBHAL::remoteWakeup(void) {
samux 8:335f2506f422 423 // [TODO]
samux 8:335f2506f422 424 }
samux 8:335f2506f422 425
samux 8:335f2506f422 426
samux 8:335f2506f422 427 void USBHAL::_usbisr(void) {
samux 8:335f2506f422 428 instance->usbisr();
samux 8:335f2506f422 429 }
samux 8:335f2506f422 430
samux 8:335f2506f422 431
samux 8:335f2506f422 432 void USBHAL::usbisr(void) {
samux 8:335f2506f422 433 uint8_t i;
samux 8:335f2506f422 434 uint8_t istat = USB0->ISTAT;
samux 8:335f2506f422 435
samux 8:335f2506f422 436 // reset interrupt
mbed_official 20:d38b72fed893 437 if (istat & USB_ISTAT_USBRST_MASK) {
mjr 31:81f57ea86f8f 438
samux 8:335f2506f422 439 // disable all endpt
samux 8:335f2506f422 440 for(i = 0; i < 16; i++) {
samux 8:335f2506f422 441 USB0->ENDPOINT[i].ENDPT = 0x00;
samux 8:335f2506f422 442 }
samux 8:335f2506f422 443
samux 8:335f2506f422 444 // enable control endpoint
samux 8:335f2506f422 445 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 446 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 447
samux 8:335f2506f422 448 Data1 = 0x55555555;
samux 8:335f2506f422 449 USB0->CTL |= USB_CTL_ODDRST_MASK;
samux 8:335f2506f422 450
samux 8:335f2506f422 451 USB0->ISTAT = 0xFF; // clear all interrupt status flags
samux 8:335f2506f422 452 USB0->ERRSTAT = 0xFF; // clear all error flags
samux 8:335f2506f422 453 USB0->ERREN = 0xFF; // enable error interrupt sources
samux 8:335f2506f422 454 USB0->ADDR = 0x00; // set default address
mjr 28:0596f144dad5 455
mjr 31:81f57ea86f8f 456 // notify upper layers of the bus reset
mjr 31:81f57ea86f8f 457 busReset();
mjr 31:81f57ea86f8f 458
mjr 31:81f57ea86f8f 459 // we're not suspended
mjr 28:0596f144dad5 460 suspendStateChanged(0);
samux 8:335f2506f422 461
samux 8:335f2506f422 462 return;
samux 8:335f2506f422 463 }
samux 8:335f2506f422 464
samux 8:335f2506f422 465 // resume interrupt
samux 8:335f2506f422 466 if (istat & USB_ISTAT_RESUME_MASK) {
samux 8:335f2506f422 467 USB0->ISTAT = USB_ISTAT_RESUME_MASK;
mjr 28:0596f144dad5 468 suspendStateChanged(0);
samux 8:335f2506f422 469 }
samux 8:335f2506f422 470
samux 8:335f2506f422 471 // SOF interrupt
samux 8:335f2506f422 472 if (istat & USB_ISTAT_SOFTOK_MASK) {
mbed_official 20:d38b72fed893 473 USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
samux 8:335f2506f422 474 // SOF event, read frame number
samux 8:335f2506f422 475 SOF(frameNumber());
samux 8:335f2506f422 476 }
mbed_official 20:d38b72fed893 477
samux 8:335f2506f422 478 // stall interrupt
samux 8:335f2506f422 479 if (istat & 1<<7) {
samux 8:335f2506f422 480 if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK)
samux 8:335f2506f422 481 USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 482 USB0->ISTAT |= USB_ISTAT_STALL_MASK;
samux 8:335f2506f422 483 }
samux 8:335f2506f422 484
samux 8:335f2506f422 485 // token interrupt
samux 8:335f2506f422 486 if (istat & 1<<3) {
samux 8:335f2506f422 487 uint32_t num = (USB0->STAT >> 4) & 0x0F;
samux 8:335f2506f422 488 uint32_t dir = (USB0->STAT >> 3) & 0x01;
samux 8:335f2506f422 489 uint32_t ev_odd = (USB0->STAT >> 2) & 0x01;
samux 8:335f2506f422 490
samux 8:335f2506f422 491 // setup packet
samux 8:335f2506f422 492 if ((num == 0) && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == SETUP_TOKEN)) {
samux 8:335f2506f422 493 Data1 &= ~0x02;
samux 8:335f2506f422 494 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 495 bdt[EP_BDT_IDX(0, TX, ODD)].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 496
samux 8:335f2506f422 497 // EP0 SETUP event (SETUP data received)
samux 8:335f2506f422 498 EP0setupCallback();
mbed_official 20:d38b72fed893 499
samux 8:335f2506f422 500 } else {
samux 8:335f2506f422 501 // OUT packet
samux 8:335f2506f422 502 if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == OUT_TOKEN) {
samux 8:335f2506f422 503 if (num == 0)
samux 8:335f2506f422 504 EP0out();
samux 8:335f2506f422 505 else {
samux 8:335f2506f422 506 epComplete |= (1 << EP(num));
samux 8:335f2506f422 507 if ((instance->*(epCallback[EP(num) - 2]))()) {
samux 8:335f2506f422 508 epComplete &= ~(1 << EP(num));
samux 8:335f2506f422 509 }
samux 8:335f2506f422 510 }
samux 8:335f2506f422 511 }
samux 8:335f2506f422 512
samux 8:335f2506f422 513 // IN packet
samux 8:335f2506f422 514 if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == IN_TOKEN) {
samux 8:335f2506f422 515 if (num == 0) {
samux 8:335f2506f422 516 EP0in();
samux 8:335f2506f422 517 if (set_addr == 1) {
samux 8:335f2506f422 518 USB0->ADDR = addr & 0x7F;
samux 8:335f2506f422 519 set_addr = 0;
samux 8:335f2506f422 520 }
samux 8:335f2506f422 521 }
samux 8:335f2506f422 522 else {
samux 8:335f2506f422 523 epComplete |= (1 << (EP(num) + 1));
samux 8:335f2506f422 524 if ((instance->*(epCallback[EP(num) + 1 - 2]))()) {
samux 8:335f2506f422 525 epComplete &= ~(1 << (EP(num) + 1));
samux 8:335f2506f422 526 }
samux 8:335f2506f422 527 }
samux 8:335f2506f422 528 }
samux 8:335f2506f422 529 }
samux 8:335f2506f422 530
samux 8:335f2506f422 531 USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
samux 8:335f2506f422 532 }
mbed_official 20:d38b72fed893 533
samux 8:335f2506f422 534 // sleep interrupt
samux 8:335f2506f422 535 if (istat & 1<<4) {
samux 8:335f2506f422 536 USB0->ISTAT |= USB_ISTAT_SLEEP_MASK;
mjr 28:0596f144dad5 537 suspendStateChanged(1);
mbed_official 20:d38b72fed893 538 }
samux 8:335f2506f422 539
samux 8:335f2506f422 540 // error interrupt
samux 8:335f2506f422 541 if (istat & USB_ISTAT_ERROR_MASK) {
samux 8:335f2506f422 542 USB0->ERRSTAT = 0xFF;
samux 8:335f2506f422 543 USB0->ISTAT |= USB_ISTAT_ERROR_MASK;
samux 8:335f2506f422 544 }
samux 8:335f2506f422 545 }
samux 8:335f2506f422 546
samux 8:335f2506f422 547
samux 8:335f2506f422 548 #endif