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:
mbed_official
Date:
Wed Apr 02 21:00:41 2014 +0100
Revision:
22:5b7d31d9d3f3
Parent:
20:d38b72fed893
Child:
28:0596f144dad5
Synchronized with git revision d537c51d26da35e031d537f7fc90380fc74cb207

Full URL: https://github.com/mbedmicro/mbed/commit/d537c51d26da35e031d537f7fc90380fc74cb207/

target K64F

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 |
samux 8:335f2506f422 153 USB_INTEN_USBRSTEN_MASK;
mbed_official 20:d38b72fed893 154
mbed_official 20:d38b72fed893 155 // Disable weak pull downs
mbed_official 20:d38b72fed893 156 USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);
mbed_official 20:d38b72fed893 157
samux 8:335f2506f422 158 USB0->USBTRC0 |= 0x40;
samux 8:335f2506f422 159 }
samux 8:335f2506f422 160
samux 8:335f2506f422 161 USBHAL::~USBHAL(void) { }
samux 8:335f2506f422 162
samux 8:335f2506f422 163 void USBHAL::connect(void) {
samux 8:335f2506f422 164 // enable USB
samux 8:335f2506f422 165 USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
samux 8:335f2506f422 166 // Pull up enable
samux 8:335f2506f422 167 USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
samux 8:335f2506f422 168 }
samux 8:335f2506f422 169
samux 8:335f2506f422 170 void USBHAL::disconnect(void) {
samux 8:335f2506f422 171 // disable USB
samux 8:335f2506f422 172 USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
samux 8:335f2506f422 173 // Pull up disable
samux 8:335f2506f422 174 USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
bogdanm 14:d495202c90f4 175
bogdanm 14:d495202c90f4 176 //Free buffers if required:
bogdanm 14:d495202c90f4 177 for (int i = 0; i<(NUMBER_OF_PHYSICAL_ENDPOINTS - 2) * 2; i++) {
bogdanm 14:d495202c90f4 178 free(endpoint_buffer[i]);
bogdanm 14:d495202c90f4 179 endpoint_buffer[i] = NULL;
bogdanm 14:d495202c90f4 180 }
bogdanm 14:d495202c90f4 181 free(endpoint_buffer_iso[2]);
bogdanm 14:d495202c90f4 182 endpoint_buffer_iso[2] = NULL;
bogdanm 14:d495202c90f4 183 free(endpoint_buffer_iso[0]);
bogdanm 14:d495202c90f4 184 endpoint_buffer_iso[0] = NULL;
samux 8:335f2506f422 185 }
samux 8:335f2506f422 186
samux 8:335f2506f422 187 void USBHAL::configureDevice(void) {
samux 8:335f2506f422 188 // not needed
samux 8:335f2506f422 189 }
samux 8:335f2506f422 190
samux 8:335f2506f422 191 void USBHAL::unconfigureDevice(void) {
samux 8:335f2506f422 192 // not needed
samux 8:335f2506f422 193 }
samux 8:335f2506f422 194
samux 8:335f2506f422 195 void USBHAL::setAddress(uint8_t address) {
samux 8:335f2506f422 196 // we don't set the address now otherwise the usb controller does not ack
samux 8:335f2506f422 197 // we set a flag instead
samux 8:335f2506f422 198 // see usbisr when an IN token is received
samux 8:335f2506f422 199 set_addr = 1;
samux 8:335f2506f422 200 addr = address;
samux 8:335f2506f422 201 }
samux 8:335f2506f422 202
samux 8:335f2506f422 203 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags) {
samux 8:335f2506f422 204 uint32_t handshake_flag = 0;
samux 8:335f2506f422 205 uint8_t * buf;
samux 8:335f2506f422 206
samux 8:335f2506f422 207 if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
samux 8:335f2506f422 208 return false;
samux 8:335f2506f422 209 }
samux 8:335f2506f422 210
samux 8:335f2506f422 211 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
samux 8:335f2506f422 212
samux 8:335f2506f422 213 if ((flags & ISOCHRONOUS) == 0) {
samux 8:335f2506f422 214 handshake_flag = USB_ENDPT_EPHSHK_MASK;
samux 9:354942d2fa38 215 if (IN_EP(endpoint)) {
bogdanm 14:d495202c90f4 216 if (endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] == NULL)
bogdanm 14:d495202c90f4 217 endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] = (uint8_t *) malloc (64*2);
bogdanm 14:d495202c90f4 218 buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)][0];
samux 9:354942d2fa38 219 } else {
bogdanm 14:d495202c90f4 220 if (endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] == NULL)
bogdanm 14:d495202c90f4 221 endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] = (uint8_t *) malloc (64*2);
bogdanm 14:d495202c90f4 222 buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)][0];
samux 9:354942d2fa38 223 }
samux 8:335f2506f422 224 } else {
samux 9:354942d2fa38 225 if (IN_EP(endpoint)) {
bogdanm 14:d495202c90f4 226 if (endpoint_buffer_iso[2] == NULL)
bogdanm 14:d495202c90f4 227 endpoint_buffer_iso[2] = (uint8_t *) malloc (1023*2);
samux 8:335f2506f422 228 buf = &endpoint_buffer_iso[2][0];
samux 9:354942d2fa38 229 } else {
bogdanm 14:d495202c90f4 230 if (endpoint_buffer_iso[0] == NULL)
bogdanm 14:d495202c90f4 231 endpoint_buffer_iso[0] = (uint8_t *) malloc (1023*2);
samux 8:335f2506f422 232 buf = &endpoint_buffer_iso[0][0];
samux 9:354942d2fa38 233 }
samux 8:335f2506f422 234 }
samux 8:335f2506f422 235
samux 8:335f2506f422 236 // IN endpt -> device to host (TX)
samux 8:335f2506f422 237 if (IN_EP(endpoint)) {
samux 8:335f2506f422 238 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
samux 8:335f2506f422 239 USB_ENDPT_EPTXEN_MASK; // en TX (IN) tran
samux 8:335f2506f422 240 bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = (uint32_t) buf;
samux 8:335f2506f422 241 bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = 0;
samux 8:335f2506f422 242 }
samux 8:335f2506f422 243 // OUT endpt -> host to device (RX)
samux 8:335f2506f422 244 else {
samux 8:335f2506f422 245 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
samux 8:335f2506f422 246 USB_ENDPT_EPRXEN_MASK; // en RX (OUT) tran.
samux 8:335f2506f422 247 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].byte_count = maxPacket;
samux 8:335f2506f422 248 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address = (uint32_t) buf;
samux 8:335f2506f422 249 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info = BD_OWN_MASK | BD_DTS_MASK;
samux 8:335f2506f422 250 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info = 0;
samux 8:335f2506f422 251 }
samux 8:335f2506f422 252
samux 8:335f2506f422 253 Data1 |= (1 << endpoint);
samux 8:335f2506f422 254
samux 8:335f2506f422 255 return true;
samux 8:335f2506f422 256 }
samux 8:335f2506f422 257
samux 8:335f2506f422 258 // read setup packet
samux 8:335f2506f422 259 void USBHAL::EP0setup(uint8_t *buffer) {
samux 8:335f2506f422 260 uint32_t sz;
samux 8:335f2506f422 261 endpointReadResult(EP0OUT, buffer, &sz);
samux 8:335f2506f422 262 }
samux 8:335f2506f422 263
samux 8:335f2506f422 264 void USBHAL::EP0readStage(void) {
samux 8:335f2506f422 265 Data1 &= ~1UL; // set DATA0
samux 8:335f2506f422 266 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
samux 8:335f2506f422 267 }
samux 8:335f2506f422 268
samux 8:335f2506f422 269 void USBHAL::EP0read(void) {
samux 8:335f2506f422 270 uint32_t idx = EP_BDT_IDX(PHY_TO_LOG(EP0OUT), RX, 0);
samux 8:335f2506f422 271 bdt[idx].byte_count = MAX_PACKET_SIZE_EP0;
samux 8:335f2506f422 272 }
samux 8:335f2506f422 273
samux 8:335f2506f422 274 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer) {
samux 8:335f2506f422 275 uint32_t sz;
samux 8:335f2506f422 276 endpointReadResult(EP0OUT, buffer, &sz);
samux 8:335f2506f422 277 return sz;
samux 8:335f2506f422 278 }
samux 8:335f2506f422 279
samux 8:335f2506f422 280 void USBHAL::EP0write(uint8_t *buffer, uint32_t size) {
samux 8:335f2506f422 281 endpointWrite(EP0IN, buffer, size);
samux 8:335f2506f422 282 }
samux 8:335f2506f422 283
samux 8:335f2506f422 284 void USBHAL::EP0getWriteResult(void) {
samux 8:335f2506f422 285 }
samux 8:335f2506f422 286
samux 8:335f2506f422 287 void USBHAL::EP0stall(void) {
samux 8:335f2506f422 288 stallEndpoint(EP0OUT);
samux 8:335f2506f422 289 }
samux 8:335f2506f422 290
samux 8:335f2506f422 291 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize) {
samux 8:335f2506f422 292 endpoint = PHY_TO_LOG(endpoint);
samux 8:335f2506f422 293 uint32_t idx = EP_BDT_IDX(endpoint, RX, 0);
samux 8:335f2506f422 294 bdt[idx].byte_count = maximumSize;
samux 8:335f2506f422 295 return EP_PENDING;
samux 8:335f2506f422 296 }
samux 8:335f2506f422 297
samux 8:335f2506f422 298 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t * buffer, uint32_t *bytesRead) {
samux 8:335f2506f422 299 uint32_t n, sz, idx, setup = 0;
samux 8:335f2506f422 300 uint8_t not_iso;
samux 8:335f2506f422 301 uint8_t * ep_buf;
mbed_official 20:d38b72fed893 302
samux 8:335f2506f422 303 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mbed_official 20:d38b72fed893 304
samux 8:335f2506f422 305 if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
samux 8:335f2506f422 306 return EP_INVALID;
samux 8:335f2506f422 307 }
samux 8:335f2506f422 308
samux 8:335f2506f422 309 // if read on a IN endpoint -> error
samux 8:335f2506f422 310 if (IN_EP(endpoint)) {
samux 8:335f2506f422 311 return EP_INVALID;
samux 8:335f2506f422 312 }
samux 8:335f2506f422 313
samux 8:335f2506f422 314 idx = EP_BDT_IDX(log_endpoint, RX, 0);
samux 8:335f2506f422 315 sz = bdt[idx].byte_count;
samux 8:335f2506f422 316 not_iso = USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK;
samux 8:335f2506f422 317
samux 8:335f2506f422 318 //for isochronous endpoint, we don't wait an interrupt
samux 8:335f2506f422 319 if ((log_endpoint != 0) && not_iso && !(epComplete & EP(endpoint))) {
samux 8:335f2506f422 320 return EP_PENDING;
samux 8:335f2506f422 321 }
samux 8:335f2506f422 322
samux 8:335f2506f422 323 if ((log_endpoint == 0) && (TOK_PID(idx) == SETUP_TOKEN)) {
samux 8:335f2506f422 324 setup = 1;
samux 8:335f2506f422 325 }
samux 8:335f2506f422 326
samux 8:335f2506f422 327 // non iso endpoint
samux 8:335f2506f422 328 if (not_iso) {
samux 8:335f2506f422 329 ep_buf = endpoint_buffer[idx];
samux 8:335f2506f422 330 } else {
samux 8:335f2506f422 331 ep_buf = endpoint_buffer_iso[0];
samux 8:335f2506f422 332 }
samux 8:335f2506f422 333
samux 8:335f2506f422 334 for (n = 0; n < sz; n++) {
samux 8:335f2506f422 335 buffer[n] = ep_buf[n];
samux 8:335f2506f422 336 }
samux 8:335f2506f422 337
samux 8:335f2506f422 338 if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1)) {
samux 8:335f2506f422 339 if (setup && (buffer[6] == 0)) // if no setup data stage,
samux 8:335f2506f422 340 Data1 &= ~1UL; // set DATA0
mbed_official 20:d38b72fed893 341 else
samux 8:335f2506f422 342 Data1 ^= (1 << endpoint);
samux 8:335f2506f422 343 }
samux 8:335f2506f422 344
samux 8:335f2506f422 345 if (((Data1 >> endpoint) & 1)) {
samux 8:335f2506f422 346 bdt[idx].info = BD_DTS_MASK | BD_DATA01_MASK | BD_OWN_MASK;
samux 8:335f2506f422 347 }
samux 8:335f2506f422 348 else {
samux 8:335f2506f422 349 bdt[idx].info = BD_DTS_MASK | BD_OWN_MASK;
samux 8:335f2506f422 350 }
mbed_official 20:d38b72fed893 351
samux 8:335f2506f422 352 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
samux 8:335f2506f422 353 *bytesRead = sz;
samux 8:335f2506f422 354
samux 8:335f2506f422 355 epComplete &= ~EP(endpoint);
samux 8:335f2506f422 356 return EP_COMPLETED;
samux 8:335f2506f422 357 }
samux 8:335f2506f422 358
samux 8:335f2506f422 359 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
samux 8:335f2506f422 360 uint32_t idx, n;
samux 8:335f2506f422 361 uint8_t * ep_buf;
samux 8:335f2506f422 362
samux 8:335f2506f422 363 if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
samux 8:335f2506f422 364 return EP_INVALID;
samux 8:335f2506f422 365 }
samux 8:335f2506f422 366
samux 8:335f2506f422 367 // if write on a OUT endpoint -> error
samux 8:335f2506f422 368 if (OUT_EP(endpoint)) {
samux 8:335f2506f422 369 return EP_INVALID;
samux 8:335f2506f422 370 }
samux 8:335f2506f422 371
samux 8:335f2506f422 372 idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
samux 8:335f2506f422 373 bdt[idx].byte_count = size;
mbed_official 20:d38b72fed893 374
mbed_official 20:d38b72fed893 375
samux 8:335f2506f422 376 // non iso endpoint
samux 8:335f2506f422 377 if (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPHSHK_MASK) {
samux 8:335f2506f422 378 ep_buf = endpoint_buffer[idx];
samux 8:335f2506f422 379 } else {
samux 8:335f2506f422 380 ep_buf = endpoint_buffer_iso[2];
samux 8:335f2506f422 381 }
mbed_official 20:d38b72fed893 382
samux 8:335f2506f422 383 for (n = 0; n < size; n++) {
samux 8:335f2506f422 384 ep_buf[n] = data[n];
samux 8:335f2506f422 385 }
mbed_official 20:d38b72fed893 386
samux 8:335f2506f422 387 if ((Data1 >> endpoint) & 1) {
samux 8:335f2506f422 388 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
samux 8:335f2506f422 389 } else {
samux 8:335f2506f422 390 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
samux 8:335f2506f422 391 }
mbed_official 20:d38b72fed893 392
samux 8:335f2506f422 393 Data1 ^= (1 << endpoint);
mbed_official 20:d38b72fed893 394
samux 8:335f2506f422 395 return EP_PENDING;
samux 8:335f2506f422 396 }
samux 8:335f2506f422 397
samux 8:335f2506f422 398 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint) {
samux 8:335f2506f422 399 if (epComplete & EP(endpoint)) {
samux 8:335f2506f422 400 epComplete &= ~EP(endpoint);
samux 8:335f2506f422 401 return EP_COMPLETED;
samux 8:335f2506f422 402 }
samux 8:335f2506f422 403
samux 8:335f2506f422 404 return EP_PENDING;
samux 8:335f2506f422 405 }
samux 8:335f2506f422 406
samux 8:335f2506f422 407 void USBHAL::stallEndpoint(uint8_t endpoint) {
samux 8:335f2506f422 408 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 409 }
samux 8:335f2506f422 410
samux 8:335f2506f422 411 void USBHAL::unstallEndpoint(uint8_t endpoint) {
samux 8:335f2506f422 412 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 413 }
samux 8:335f2506f422 414
samux 8:335f2506f422 415 bool USBHAL::getEndpointStallState(uint8_t endpoint) {
samux 8:335f2506f422 416 uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
samux 8:335f2506f422 417 return (stall) ? true : false;
samux 8:335f2506f422 418 }
samux 8:335f2506f422 419
samux 8:335f2506f422 420 void USBHAL::remoteWakeup(void) {
samux 8:335f2506f422 421 // [TODO]
samux 8:335f2506f422 422 }
samux 8:335f2506f422 423
samux 8:335f2506f422 424
samux 8:335f2506f422 425 void USBHAL::_usbisr(void) {
samux 8:335f2506f422 426 instance->usbisr();
samux 8:335f2506f422 427 }
samux 8:335f2506f422 428
samux 8:335f2506f422 429
samux 8:335f2506f422 430 void USBHAL::usbisr(void) {
samux 8:335f2506f422 431 uint8_t i;
samux 8:335f2506f422 432 uint8_t istat = USB0->ISTAT;
samux 8:335f2506f422 433
samux 8:335f2506f422 434 // reset interrupt
mbed_official 20:d38b72fed893 435 if (istat & USB_ISTAT_USBRST_MASK) {
samux 8:335f2506f422 436 // disable all endpt
samux 8:335f2506f422 437 for(i = 0; i < 16; i++) {
samux 8:335f2506f422 438 USB0->ENDPOINT[i].ENDPT = 0x00;
samux 8:335f2506f422 439 }
samux 8:335f2506f422 440
samux 8:335f2506f422 441 // enable control endpoint
samux 8:335f2506f422 442 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 443 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 444
samux 8:335f2506f422 445 Data1 = 0x55555555;
samux 8:335f2506f422 446 USB0->CTL |= USB_CTL_ODDRST_MASK;
samux 8:335f2506f422 447
samux 8:335f2506f422 448 USB0->ISTAT = 0xFF; // clear all interrupt status flags
samux 8:335f2506f422 449 USB0->ERRSTAT = 0xFF; // clear all error flags
samux 8:335f2506f422 450 USB0->ERREN = 0xFF; // enable error interrupt sources
samux 8:335f2506f422 451 USB0->ADDR = 0x00; // set default address
samux 8:335f2506f422 452
samux 8:335f2506f422 453 return;
samux 8:335f2506f422 454 }
samux 8:335f2506f422 455
samux 8:335f2506f422 456 // resume interrupt
samux 8:335f2506f422 457 if (istat & USB_ISTAT_RESUME_MASK) {
samux 8:335f2506f422 458 USB0->ISTAT = USB_ISTAT_RESUME_MASK;
samux 8:335f2506f422 459 }
samux 8:335f2506f422 460
samux 8:335f2506f422 461 // SOF interrupt
samux 8:335f2506f422 462 if (istat & USB_ISTAT_SOFTOK_MASK) {
mbed_official 20:d38b72fed893 463 USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
samux 8:335f2506f422 464 // SOF event, read frame number
samux 8:335f2506f422 465 SOF(frameNumber());
samux 8:335f2506f422 466 }
mbed_official 20:d38b72fed893 467
samux 8:335f2506f422 468 // stall interrupt
samux 8:335f2506f422 469 if (istat & 1<<7) {
samux 8:335f2506f422 470 if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK)
samux 8:335f2506f422 471 USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 472 USB0->ISTAT |= USB_ISTAT_STALL_MASK;
samux 8:335f2506f422 473 }
samux 8:335f2506f422 474
samux 8:335f2506f422 475 // token interrupt
samux 8:335f2506f422 476 if (istat & 1<<3) {
samux 8:335f2506f422 477 uint32_t num = (USB0->STAT >> 4) & 0x0F;
samux 8:335f2506f422 478 uint32_t dir = (USB0->STAT >> 3) & 0x01;
samux 8:335f2506f422 479 uint32_t ev_odd = (USB0->STAT >> 2) & 0x01;
samux 8:335f2506f422 480
samux 8:335f2506f422 481 // setup packet
samux 8:335f2506f422 482 if ((num == 0) && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == SETUP_TOKEN)) {
samux 8:335f2506f422 483 Data1 &= ~0x02;
samux 8:335f2506f422 484 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 485 bdt[EP_BDT_IDX(0, TX, ODD)].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 486
samux 8:335f2506f422 487 // EP0 SETUP event (SETUP data received)
samux 8:335f2506f422 488 EP0setupCallback();
mbed_official 20:d38b72fed893 489
samux 8:335f2506f422 490 } else {
samux 8:335f2506f422 491 // OUT packet
samux 8:335f2506f422 492 if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == OUT_TOKEN) {
samux 8:335f2506f422 493 if (num == 0)
samux 8:335f2506f422 494 EP0out();
samux 8:335f2506f422 495 else {
samux 8:335f2506f422 496 epComplete |= (1 << EP(num));
samux 8:335f2506f422 497 if ((instance->*(epCallback[EP(num) - 2]))()) {
samux 8:335f2506f422 498 epComplete &= ~(1 << EP(num));
samux 8:335f2506f422 499 }
samux 8:335f2506f422 500 }
samux 8:335f2506f422 501 }
samux 8:335f2506f422 502
samux 8:335f2506f422 503 // IN packet
samux 8:335f2506f422 504 if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == IN_TOKEN) {
samux 8:335f2506f422 505 if (num == 0) {
samux 8:335f2506f422 506 EP0in();
samux 8:335f2506f422 507 if (set_addr == 1) {
samux 8:335f2506f422 508 USB0->ADDR = addr & 0x7F;
samux 8:335f2506f422 509 set_addr = 0;
samux 8:335f2506f422 510 }
samux 8:335f2506f422 511 }
samux 8:335f2506f422 512 else {
samux 8:335f2506f422 513 epComplete |= (1 << (EP(num) + 1));
samux 8:335f2506f422 514 if ((instance->*(epCallback[EP(num) + 1 - 2]))()) {
samux 8:335f2506f422 515 epComplete &= ~(1 << (EP(num) + 1));
samux 8:335f2506f422 516 }
samux 8:335f2506f422 517 }
samux 8:335f2506f422 518 }
samux 8:335f2506f422 519 }
samux 8:335f2506f422 520
samux 8:335f2506f422 521 USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
samux 8:335f2506f422 522 }
mbed_official 20:d38b72fed893 523
samux 8:335f2506f422 524 // sleep interrupt
samux 8:335f2506f422 525 if (istat & 1<<4) {
samux 8:335f2506f422 526 USB0->ISTAT |= USB_ISTAT_SLEEP_MASK;
mbed_official 20:d38b72fed893 527 }
samux 8:335f2506f422 528
samux 8:335f2506f422 529 // error interrupt
samux 8:335f2506f422 530 if (istat & USB_ISTAT_ERROR_MASK) {
samux 8:335f2506f422 531 USB0->ERRSTAT = 0xFF;
samux 8:335f2506f422 532 USB0->ISTAT |= USB_ISTAT_ERROR_MASK;
samux 8:335f2506f422 533 }
samux 8:335f2506f422 534 }
samux 8:335f2506f422 535
samux 8:335f2506f422 536
samux 8:335f2506f422 537 #endif