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:
Thu Feb 27 09:45:46 2014 +0000
Revision:
20:d38b72fed893
Parent:
14:d495202c90f4
Child:
22:5b7d31d9d3f3
Synchronized with git revision 6b57b5237463a65d25f0ffa5efa3c8505553da4b

Full URL: https://github.com/mbedmicro/mbed/commit/6b57b5237463a65d25f0ffa5efa3c8505553da4b/

add D0-D13 and A0-A5 for Arch V1.1 and Arch Pro

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