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:
samux
Date:
Mon Mar 04 13:37:51 2013 +0000
Revision:
9:354942d2fa38
Parent:
8:335f2506f422
Child:
13:16731886c049
kl25z: use malloc to allocate only endpoints needed

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