USB device stack, with KL25Z fixes for USB 3.0 hosts and sleep/resume interrupt handling

Dependents:   frdm_Slider_Keyboard idd_hw2_figlax_PanType idd_hw2_appachu_finger_chording idd_hw3_AngieWangAntonioDeLimaFernandesDanielLim_BladeSymphony ... more

Fork of USBDevice by mbed official

This is an overhauled version of the standard mbed USB device-side driver library, with bug fixes for KL25Z devices. It greatly improves reliability and stability of USB on the KL25Z, especially with devices using multiple endpoints concurrently.

I've had some nagging problems with the base mbed implementation for a long time, manifesting as occasional random disconnects that required rebooting the device. Recently (late 2015), I started implementing a USB device on the KL25Z that used multiple endpoints, and suddenly the nagging, occasional problems turned into frequent and predictable crashes. This forced me to delve into the USB stack and figure out what was really going on. Happily, the frequent crashes made it possible to track down and fix the problems. This new version is working very reliably in my testing - the random disconnects seem completely eradicated, even under very stressful conditions for the device.

Summary

  • Overall stability improvements
  • USB 3.0 host support
  • Stalled endpoint fixes
  • Sleep/resume notifications
  • Smaller memory footprint
  • General code cleanup

Update - 2/15/2016

My recent fixes introduced a new problem that made the initial connection fail most of the time on certain hosts. It's not clear if the common thread was a particular type of motherboard or USB chip set, or a specific version of Windows, or what, but several people ran into it. We tracked the problem down to the "stall" fixes in the earlier updates, which we now know weren't quite the right fixes after all. The latest update (2/15/2016) fixes this. It has new and improved "unstall" handling that so far works well with diverse hosts.

Race conditions and overall stability

The base mbed KL25Z implementation has a lot of problems with "race conditions" - timing problems that can happen when hardware interrupts occur at inopportune moments. The library shares a bunch of static variable data between interrupt handler context and regular application context. This isn't automatically a bad thing, but it does require careful coordination to make sure that the interrupt handler doesn't corrupt data that the other code was in the middle of updating when an interrupt occurs. The base mbed code, though, doesn't do any of the necessary coordination. This makes it kind of amazing that the base code worked at all for anyone, but I guess the interrupt rate is low enough in most applications that the glitch rate was below anyone's threshold to seriously investigate.

This overhaul adds the necessary coordination for the interrupt handlers to protect against these data corruptions. I think it's very solid now, and hopefully entirely free of the numerous race conditions in the old code. It's always hard to be certain that you've fixed every possible bug like this because they strike (effectively) at random, but I'm pretty confident: my test application was reliably able to trigger glitches in the base code in a matter of minutes, but the same application (with the overhauled library) now runs for days on end without dropping the connection.

Stalled endpoint fixes

USB has a standard way of handling communications errors called a "stall", which basically puts the connection into an error mode to let both sides know that they need to reset their internal states and sync up again. The original mbed version of the USB device library doesn't seem to have the necessary code to recover from this condition properly. The KL25Z hardware does some of the work, but it also seems to require the software to take some steps to "un-stall" the connection. (I keep saying "seems to" because the hardware reference material is very sketchy about all of this. Most of what I've figured out is from observing the device in action with a Windows host.) This new version adds code to do the necessary re-syncing and get the connection going again, automatically, and transparently to the user.

USB 3.0 Hosts

The original mbed code sometimes didn't work when connecting to hosts with USB 3.0 ports. This didn't affect every host, but it affected many of them. The common element seemed to be the Intel Haswell chip set on the host, but there may be other chip sets affected as well. In any case, the problem affected many PCs from the Windows 7 and 8 generation, as well as many Macs. It was possible to work around the problem by avoiding USB 3.0 ports - you could use a USB 2 port on the host, or plug a USB 2 hub between the host and device. But I wanted to just fix the problem and eliminate the need for such workarounds. This modified version of the library has such a fix, which so far has worked for everyone who's tried.

Sleep/resume notifications

This modified version also contains an innocuous change to the KL25Z USB HAL code to handle sleep and resume interrupts with calls to suspendStateChanged(). The original KL25Z code omitted these calls (and in fact didn't even enable the interrupts), but I think this was an unintentional oversight - the notifier function is part of the generic API, and other supported boards all implement it. I use this feature in my own application so that I can distinguish sleep mode from actual disconnects and handle the two conditions correctly.

Smaller memory footprint

The base mbed version of the code allocates twice as much memory for USB buffers as it really needed to. It looks like the original developers intended to implement the KL25Z USB hardware's built-in double-buffering mechanism, but they ultimately abandoned that effort. But they left in the double memory allocation. This version removes that and allocates only what's actually needed. The USB buffers aren't that big (128 bytes per endpoint), so this doesn't save a ton of memory, but even a little memory is pretty precious on this machine given that it only has 16K.

(I did look into adding the double-buffering support that the original developers abandoned, but after some experimentation I decided they were right to skip it. It just doesn't seem to mesh well with the design of the rest of the mbed USB code. I think it would take a major rewrite to make it work, and it doesn't seem worth the effort given that most applications don't need it - it would only benefit applications that are moving so much data through USB that they're pushing the limits of the CPU. And even for those, I think it would be a lot simpler to build a purely software-based buffer rotation mechanism.)

General code cleanup

The KL25Z HAL code in this version has greatly expanded commentary and a lot of general cleanup. Some of the hardware constants were given the wrong symbolic names (e.g., EVEN and ODD were reversed), and many were just missing (written as hard-coded numbers without explanation). I fixed the misnomers and added symbolic names for formerly anonymous numbers. Hopefully the next person who has to overhaul this code will at least have an easier time understanding what I thought I was doing!

Committer:
mjr
Date:
Fri Mar 17 22:01:47 2017 +0000
Revision:
54:2e181d51495a
Parent:
52:d9c520d4704b
Comments

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
mjr 37:c5ac4ccf6597 19 #if defined(TARGET_KL25Z) | defined(TARGET_KL46Z) | defined(TARGET_K20D5M) | defined(TARGET_K64F)
mjr 36:20bb47609697 20
mjr 50:946bc763c068 21 #include <stdarg.h>
samux 8:335f2506f422 22 #include "USBHAL.h"
samux 8:335f2506f422 23
mjr 37:c5ac4ccf6597 24 // Critical section controls. This module uses a bunch of static variables,
mjr 37:c5ac4ccf6597 25 // and much of the code that accesses the statics can be called from either
mjr 37:c5ac4ccf6597 26 // normal application context or IRQ context. Whenever a shared variable is
mjr 37:c5ac4ccf6597 27 // accessed from code that can run in an application context, we have to
mjr 37:c5ac4ccf6597 28 // protect against interrupts by entering a critical section. These macros
mjr 37:c5ac4ccf6597 29 // enable and disable the USB IRQ if we're running in application context.
mjr 37:c5ac4ccf6597 30 // (They do nothing if we're already in interrupt context, because the
mjr 37:c5ac4ccf6597 31 // hardware interrupt controller won't generated another of the same IRQ
mjr 37:c5ac4ccf6597 32 // that we're already handling. We could still be interrupted by a different,
mjr 37:c5ac4ccf6597 33 // higher-priority IRQ, but our shared variables are only shared within this
mjr 37:c5ac4ccf6597 34 // module, so they won't be affected by other interrupt handlers.)
mjr 52:d9c520d4704b 35 static bool inIRQ;
mjr 37:c5ac4ccf6597 36 #define ENTER_CRITICAL_SECTION \
mjr 37:c5ac4ccf6597 37 if (!inIRQ) \
mjr 37:c5ac4ccf6597 38 NVIC_DisableIRQ(USB0_IRQn);
mjr 37:c5ac4ccf6597 39 #define EXIT_CRITICAL_SECTION \
mjr 37:c5ac4ccf6597 40 if (!inIRQ) \
mjr 37:c5ac4ccf6597 41 NVIC_EnableIRQ(USB0_IRQn);
mjr 37:c5ac4ccf6597 42
mjr 50:946bc763c068 43 //#define DEBUG_WITH_PRINTF
mjr 50:946bc763c068 44 // debug printf; does a regular printf() in debug mode, nothing in
mjr 50:946bc763c068 45 // normal mode. Note that many of our routines are called in ISR
mjr 50:946bc763c068 46 // context, so printf should really never be used here. But in
mjr 50:946bc763c068 47 // practice we can get away with it enough that it can be helpful
mjr 50:946bc763c068 48 // as a limited debugging tool.
mjr 50:946bc763c068 49 #ifdef DEBUG_WITH_PRINTF
mjr 50:946bc763c068 50 #define printd(fmt, ...) printf(fmt, __VA_ARGS__)
mjr 50:946bc763c068 51 #else
mjr 50:946bc763c068 52 #define printd(fmt, ...)
mjr 50:946bc763c068 53 #endif
mjr 50:946bc763c068 54
mjr 50:946bc763c068 55 // Makeshift debug instrumentation. This is a safer and better
mjr 50:946bc763c068 56 // alternative to printf() that gathers event information in a
mjr 50:946bc763c068 57 // circular buffer for later useoutside of interrupt context, such
mjr 50:946bc763c068 58 // as printf() display at intervals in the main program loop.
mjr 50:946bc763c068 59 //
mjr 50:946bc763c068 60 // Timing is critical to USB, so debug instrumentation is inherently
mjr 50:946bc763c068 61 // problematic in that it can affect the timing and thereby change
mjr 50:946bc763c068 62 // the behavior of what we're trying to debug. Small timing changes
mjr 50:946bc763c068 63 // can create new errors that wouldn't be there otherwise, or even
mjr 50:946bc763c068 64 // accidentally fix the bug were trying to find (e.g., by changing
mjr 50:946bc763c068 65 // the timing enough to avoid a race condition). To minimize these
mjr 50:946bc763c068 66 // effects, we use a small buffer and very terse event codes -
mjr 50:946bc763c068 67 // generally one character per event. That makes for a cryptic
mjr 50:946bc763c068 68 // debug log, but it results in almost zero timing effects, allowing
mjr 50:946bc763c068 69 // us to see a more faithful version of the subject program.
mjr 50:946bc763c068 70 //
mjr 50:946bc763c068 71 // Note that the buffer size isn't critical to timing, because any
mjr 50:946bc763c068 72 // printf()-type display should always occur in regular (non-ISR)
mjr 50:946bc763c068 73 // context and thus won't have any significant effect on interrupt
mjr 50:946bc763c068 74 // timing or latency. The buffer can be expanded if longer logs
mjr 50:946bc763c068 75 // would be helpful. However, it is important to keep the individual
mjr 50:946bc763c068 76 // event messages short (a character or two in most cases), because
mjr 50:946bc763c068 77 // it takes time to move them into the buffer.
mjr 50:946bc763c068 78 //#define DEBUG_WITH_EVENTS
mjr 50:946bc763c068 79 #ifdef DEBUG_WITH_EVENTS
mjr 50:946bc763c068 80 const int nevents = 64; // MUST BE A POWER OF 2
mjr 50:946bc763c068 81 char events[nevents];
mjr 50:946bc763c068 82 char ewrite = 0, eread = 0;
mjr 50:946bc763c068 83 void HAL_DEBUG_EVENT(char c)
mjr 50:946bc763c068 84 {
mjr 50:946bc763c068 85 events[ewrite] = c;
mjr 50:946bc763c068 86 ewrite = (ewrite+1) & (nevents-1);
mjr 50:946bc763c068 87 if (ewrite == eread)
mjr 50:946bc763c068 88 eread = (eread+1) & (nevents-1);
mjr 50:946bc763c068 89 }
mjr 50:946bc763c068 90 void HAL_DEBUG_EVENT(char a, char b) {
mjr 50:946bc763c068 91 HAL_DEBUG_EVENT(a); HAL_DEBUG_EVENT(b);
mjr 50:946bc763c068 92 }
mjr 50:946bc763c068 93 void HAL_DEBUG_EVENT(char a, char b, char c) {
mjr 50:946bc763c068 94 HAL_DEBUG_EVENT(a); HAL_DEBUG_EVENT(b); HAL_DEBUG_EVENT(c);
mjr 50:946bc763c068 95 }
mjr 50:946bc763c068 96 void HAL_DEBUG_EVENT(const char *s) {
mjr 50:946bc763c068 97 while (*s) HAL_DEBUG_EVENT(*s++);
mjr 50:946bc763c068 98 }
mjr 50:946bc763c068 99 void HAL_DEBUG_EVENTI(char c, int i) {
mjr 50:946bc763c068 100 HAL_DEBUG_EVENT(c);
mjr 50:946bc763c068 101 if (i > 1000) HAL_DEBUG_EVENT(((i / 1000) % 10) + '0');
mjr 50:946bc763c068 102 if (i > 100) HAL_DEBUG_EVENT(((i / 100) % 10) + '0');
mjr 50:946bc763c068 103 if (i > 10) HAL_DEBUG_EVENT(((i / 10) % 10) + '0');
mjr 50:946bc763c068 104 HAL_DEBUG_EVENT((i % 10) + '0');
mjr 50:946bc763c068 105 }
mjr 50:946bc763c068 106 void HAL_DEBUG_EVENTF(const char *fmt, ...) {
mjr 50:946bc763c068 107 va_list va;
mjr 50:946bc763c068 108 va_start(va, fmt);
mjr 50:946bc763c068 109 char buf[64];
mjr 50:946bc763c068 110 vsprintf(buf, fmt, va);
mjr 50:946bc763c068 111 va_end(va);
mjr 50:946bc763c068 112 HAL_DEBUG_EVENT(buf);
mjr 50:946bc763c068 113 }
mjr 50:946bc763c068 114 void HAL_DEBUG_PRINTEVENTS(const char *prefix)
mjr 50:946bc763c068 115 {
mjr 50:946bc763c068 116 if (prefix != 0)
mjr 50:946bc763c068 117 printf("%s ", prefix);
mjr 50:946bc763c068 118 else
mjr 50:946bc763c068 119 printf("ev: ");
mjr 50:946bc763c068 120
mjr 50:946bc763c068 121 char buf[nevents];
mjr 50:946bc763c068 122 int i;
mjr 50:946bc763c068 123 ENTER_CRITICAL_SECTION
mjr 50:946bc763c068 124 {
mjr 50:946bc763c068 125 for (i = 0 ; eread != ewrite ; eread = (eread+1) & (nevents - 1))
mjr 50:946bc763c068 126 buf[i++] = events[eread];
mjr 50:946bc763c068 127 }
mjr 50:946bc763c068 128 EXIT_CRITICAL_SECTION
mjr 50:946bc763c068 129 printf("%.*s\r\n", i, buf);
mjr 50:946bc763c068 130 }
mjr 50:946bc763c068 131 #else
mjr 50:946bc763c068 132 #define HAL_DEBUG_EVENT(...) void(0)
mjr 50:946bc763c068 133 #define HAL_DEBUG_EVENTf(...) void(0)
mjr 50:946bc763c068 134 #define HAL_DEBUG_EVENTI(...) void(0)
mjr 50:946bc763c068 135 void HAL_DEBUG_PRINTEVENTS(const char *) { }
mjr 50:946bc763c068 136 #endif
mjr 50:946bc763c068 137
mjr 50:946bc763c068 138
mjr 37:c5ac4ccf6597 139 // static singleton instance pointer
samux 8:335f2506f422 140 USBHAL * USBHAL::instance;
samux 8:335f2506f422 141
mjr 48:b225d025ca1d 142
samux 8:335f2506f422 143 // Convert physical endpoint number to register bit
mjr 35:53e1a208f582 144 #define EP(endpoint) (1<<(endpoint))
samux 8:335f2506f422 145
mjr 37:c5ac4ccf6597 146 // Convert physical endpoint number to logical endpoint number.
mjr 37:c5ac4ccf6597 147 // Each logical endpoint has two physical endpoints, one RX and
mjr 37:c5ac4ccf6597 148 // one TX. The physical endpoints are numbered in RX,TX pairs,
mjr 37:c5ac4ccf6597 149 // so the logical endpoint number is simply the physical endpoint
mjr 37:c5ac4ccf6597 150 // number divided by 2 (discarding the remainder).
samux 8:335f2506f422 151 #define PHY_TO_LOG(endpoint) ((endpoint)>>1)
samux 8:335f2506f422 152
mjr 37:c5ac4ccf6597 153 // Get a physical endpoint's direction. IN and OUT are from
mjr 37:c5ac4ccf6597 154 // the host's perspective, so from our perspective on the device,
mjr 37:c5ac4ccf6597 155 // IN == TX and OUT == RX. The physical endpoints are in RX,TX
mjr 37:c5ac4ccf6597 156 // pairs, so the OUT/RX is the even numbered element of a pair
mjr 37:c5ac4ccf6597 157 // and the IN/TX is the odd numbered element.
samux 8:335f2506f422 158 #define IN_EP(endpoint) ((endpoint) & 1U ? true : false)
samux 8:335f2506f422 159 #define OUT_EP(endpoint) ((endpoint) & 1U ? false : true)
samux 8:335f2506f422 160
mjr 37:c5ac4ccf6597 161 // BDT status flags, defined by the SIE hardware. These are
mjr 37:c5ac4ccf6597 162 // bits packed into the 'info' byte of a BDT entry.
mjr 37:c5ac4ccf6597 163 #define BD_OWN_MASK (1<<7) // OWN - hardware SIE owns the BDT (TX/RX in progress)
mjr 37:c5ac4ccf6597 164 #define BD_DATA01_MASK (1<<6) // DATA01 - DATA0/DATA1 bit for current TX/RX on endpoint
mjr 37:c5ac4ccf6597 165 #define BD_KEEP_MASK (1<<5) // KEEP - hardware keeps BDT ownership after token completes
mjr 37:c5ac4ccf6597 166 #define BD_NINC_MASK (1<<4) // NO INCREMENT - buffer location is a FIFO, so use same address for all bytes
mjr 37:c5ac4ccf6597 167 #define BD_DTS_MASK (1<<3) // DATA TOGGLE SENSING - hardware SIE checks for DATA0/DATA1 match during RX/TX
mjr 37:c5ac4ccf6597 168 #define BD_STALL_MASK (1<<2) // STALL - SIE issues STALL handshake in reply to any host access to endpoint
mjr 35:53e1a208f582 169
mjr 48:b225d025ca1d 170 // Endpoint direction (from DEVICE perspective)
samux 8:335f2506f422 171 #define TX 1
samux 8:335f2506f422 172 #define RX 0
mjr 36:20bb47609697 173
mjr 37:c5ac4ccf6597 174 // Buffer parity. The hardware has a double-buffering scheme where each
mjr 37:c5ac4ccf6597 175 // physical endpoint has two associated BDT entries, labeled EVEN and ODD.
mjr 37:c5ac4ccf6597 176 // We disable the double buffering, so only the EVEN buffers are used in
mjr 37:c5ac4ccf6597 177 // this implementation.
mjr 36:20bb47609697 178 #define EVEN 0
mjr 36:20bb47609697 179 #define ODD 1
mjr 36:20bb47609697 180
mjr 37:c5ac4ccf6597 181 // Get the BDT index for a given logical endpoint, direction, and buffer parity
mjr 37:c5ac4ccf6597 182 #define EP_BDT_IDX(logep, dir, odd) (((logep) * 4) + (2 * (dir)) + (1 * (odd)))
mjr 36:20bb47609697 183
mjr 37:c5ac4ccf6597 184 // Get the BDT index for a given physical endpoint and buffer parity
mjr 37:c5ac4ccf6597 185 #define PEP_BDT_IDX(phyep, odd) (((phyep) * 2) + (1 * (odd)))
mjr 36:20bb47609697 186
mjr 37:c5ac4ccf6597 187 // Token types reported in the BDT 'info' flags.
mjr 37:c5ac4ccf6597 188 #define TOK_PID(idx) ((bdt[idx].info >> 2) & 0x0F)
samux 8:335f2506f422 189 #define SETUP_TOKEN 0x0D
samux 8:335f2506f422 190 #define IN_TOKEN 0x09
samux 8:335f2506f422 191 #define OUT_TOKEN 0x01
mjr 34:884405d998bb 192
mjr 37:c5ac4ccf6597 193 // Buffer Descriptor Table (BDT) entry. This is the hardware-defined
mjr 37:c5ac4ccf6597 194 // memory structure for the shared memory block controlling an endpoint.
mjr 35:53e1a208f582 195 typedef struct BDT {
mjr 35:53e1a208f582 196 uint8_t info; // BD[0:7]
mjr 35:53e1a208f582 197 uint8_t dummy; // RSVD: BD[8:15]
mjr 37:c5ac4ccf6597 198 uint16_t byte_count; // BD[16:32]
mjr 35:53e1a208f582 199 uint32_t address; // Addr
mjr 37:c5ac4ccf6597 200 } BDT;
samux 8:335f2506f422 201
mjr 48:b225d025ca1d 202
mjr 36:20bb47609697 203 // There are:
mjr 37:c5ac4ccf6597 204 // * 16 bidirectional logical endpoints -> 32 physical endpoints
mjr 37:c5ac4ccf6597 205 // * 2 BDT entries per endpoint (EVEN/ODD) -> 64 BDT entries
mjr 36:20bb47609697 206 __attribute__((__aligned__(512))) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
mjr 37:c5ac4ccf6597 207
mjr 37:c5ac4ccf6597 208 // Transfer buffers. We allocate the transfer buffers and point the
mjr 37:c5ac4ccf6597 209 // SIE hardware to them via the BDT. We disable hardware SIE's
mjr 37:c5ac4ccf6597 210 // double-buffering (EVEN/ODD) scheme, so we only allocate one buffer
mjr 37:c5ac4ccf6597 211 // per physical endpoint.
mjr 36:20bb47609697 212 uint8_t *endpoint_buffer[NUMBER_OF_PHYSICAL_ENDPOINTS];
mjr 36:20bb47609697 213
mjr 37:c5ac4ccf6597 214 // Allocated size of each endpoint buffer
mjr 37:c5ac4ccf6597 215 size_t epMaxPacket[NUMBER_OF_PHYSICAL_ENDPOINTS];
mjr 37:c5ac4ccf6597 216
mjr 48:b225d025ca1d 217
mjr 37:c5ac4ccf6597 218 // SET ADDRESS mode tracking. The address assignment has to be done in a
mjr 37:c5ac4ccf6597 219 // specific order and with specific timing defined by the USB setup protocol
mjr 37:c5ac4ccf6597 220 // standards. To get the sequencing right, we set a flag when we get the
mjr 37:c5ac4ccf6597 221 // address message, and then set the address in the SIE when we're at the
mjr 37:c5ac4ccf6597 222 // right subsequent packet step in the protocol exchange. These variables
mjr 37:c5ac4ccf6597 223 // are just a place to stash the information between the time we receive the
mjr 37:c5ac4ccf6597 224 // data and the time we're ready to update the SIE register.
mjr 37:c5ac4ccf6597 225 static uint8_t set_addr = 0;
mjr 36:20bb47609697 226 static uint8_t addr = 0;
mjr 36:20bb47609697 227
mjr 37:c5ac4ccf6597 228 // Endpoint DATA0/DATA1 bits, packed as a bit vector. Each endpoint's
mjr 37:c5ac4ccf6597 229 // bit is at (1 << endpoint number). These track the current bit value
mjr 37:c5ac4ccf6597 230 // on the endpoint. For TX endpoints, this is the bit for the LAST
mjr 37:c5ac4ccf6597 231 // packet we sent (so the next packet will be the inverse). For RX
mjr 37:c5ac4ccf6597 232 // endpoints, this is the bit value we expect for the NEXT packet.
mjr 37:c5ac4ccf6597 233 // (Yes, it's inconsistent.)
mjr 50:946bc763c068 234 static volatile uint32_t Data1 = 0x55555555;
mjr 35:53e1a208f582 235
mjr 37:c5ac4ccf6597 236 // Endpoint read/write completion flags, packed as a bit vector. Each
mjr 37:c5ac4ccf6597 237 // endpoint's bit is at (1 << endpoint number). A 1 bit signifies that
mjr 37:c5ac4ccf6597 238 // the last read or write has completed (and hasn't had its result
mjr 37:c5ac4ccf6597 239 // consumed yet).
mjr 48:b225d025ca1d 240 static volatile uint32_t epComplete = 0;
mjr 35:53e1a208f582 241
mjr 50:946bc763c068 242 // Endpoint Realised flags. We set these flags (arranged in the usual
mjr 50:946bc763c068 243 // endpoint bit vector format) when endpoints are realised, so that
mjr 50:946bc763c068 244 // read/write operations will know if it's okay to proceed. The
mjr 50:946bc763c068 245 // control endpoint (EP0) is always realised in both directions.
mjr 50:946bc763c068 246 static volatile uint32_t epRealised = 0x03;
mjr 50:946bc763c068 247
mjr 37:c5ac4ccf6597 248 static uint32_t frameNumber()
mjr 37:c5ac4ccf6597 249 {
bogdanm 13:16731886c049 250 return((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
samux 8:335f2506f422 251 }
samux 8:335f2506f422 252
mjr 37:c5ac4ccf6597 253 uint32_t USBHAL::endpointReadcore(uint8_t endpoint, uint8_t *buffer)
mjr 37:c5ac4ccf6597 254 {
samux 8:335f2506f422 255 return 0;
samux 8:335f2506f422 256 }
samux 8:335f2506f422 257
mjr 50:946bc763c068 258 // Enabled interrupts at startup or reset:
mjr 50:946bc763c068 259 // TOKDN - token done
mjr 50:946bc763c068 260 // SOFTOK - start-of-frame token
mjr 50:946bc763c068 261 // ERROR - error
mjr 50:946bc763c068 262 // SLEEP - sleep (inactivity on bus)
mjr 50:946bc763c068 263 // RST - bus reset
mjr 50:946bc763c068 264 //
mjr 50:946bc763c068 265 // Note that don't enable RESUME (resume from suspend mode), per
mjr 50:946bc763c068 266 // the hardware reference manual ("When not in suspend mode this
mjr 50:946bc763c068 267 // interrupt must be disabled"). We also don't enable ATTACH, which
mjr 50:946bc763c068 268 // is only meaningful in host mode.
mjr 50:946bc763c068 269 #define BUS_RESET_INTERRUPTS \
mjr 50:946bc763c068 270 USB_INTEN_TOKDNEEN_MASK \
mjr 50:946bc763c068 271 | USB_INTEN_STALLEN_MASK \
mjr 50:946bc763c068 272 | USB_INTEN_SOFTOKEN_MASK \
mjr 50:946bc763c068 273 | USB_INTEN_ERROREN_MASK \
mjr 50:946bc763c068 274 | USB_INTEN_SLEEPEN_MASK \
mjr 50:946bc763c068 275 | USB_INTEN_USBRSTEN_MASK
mjr 50:946bc763c068 276
mjr 50:946bc763c068 277 // Do a low-level reset on the USB hardware module. This lets the
mjr 50:946bc763c068 278 // device software initiate a hard reset.
mjr 50:946bc763c068 279 static void resetSIE(void)
mjr 50:946bc763c068 280 {
mjr 50:946bc763c068 281 // set the reset bit in the transceiver control register,
mjr 50:946bc763c068 282 // then wait for it to clear
mjr 50:946bc763c068 283 USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
mjr 50:946bc763c068 284 while (USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
mjr 50:946bc763c068 285
mjr 50:946bc763c068 286 // clear BDT entries
mjr 50:946bc763c068 287 for (int i = 0 ; i < sizeof(bdt)/sizeof(bdt[0]) ; ++i)
mjr 50:946bc763c068 288 {
mjr 50:946bc763c068 289 bdt[i].info = 0;
mjr 50:946bc763c068 290 bdt[i].byte_count = 0;
mjr 50:946bc763c068 291 }
mjr 50:946bc763c068 292
mjr 50:946bc763c068 293 // Set BDT Base Register
mjr 50:946bc763c068 294 USB0->BDTPAGE1 = (uint8_t)((uint32_t)bdt>>8);
mjr 50:946bc763c068 295 USB0->BDTPAGE2 = (uint8_t)((uint32_t)bdt>>16);
mjr 50:946bc763c068 296 USB0->BDTPAGE3 = (uint8_t)((uint32_t)bdt>>24);
mjr 50:946bc763c068 297
mjr 50:946bc763c068 298 // Clear interrupt flag
mjr 50:946bc763c068 299 USB0->ISTAT = 0xff;
mjr 50:946bc763c068 300
mjr 50:946bc763c068 301 // Enable the initial set of interrupts
mjr 50:946bc763c068 302 USB0->INTEN = BUS_RESET_INTERRUPTS;
mjr 50:946bc763c068 303
mjr 50:946bc763c068 304 // Disable weak pull downs, and turn off suspend mode
mjr 50:946bc763c068 305 USB0->USBCTRL = 0;
mjr 50:946bc763c068 306
mjr 50:946bc763c068 307 // set the "reserved" bit in the transceiver control register
mjr 50:946bc763c068 308 // (hw ref: "software must set this bit to 1")
mjr 50:946bc763c068 309 USB0->USBTRC0 |= 0x40;
mjr 50:946bc763c068 310 }
mjr 50:946bc763c068 311
mjr 50:946bc763c068 312 USBHAL::USBHAL(void)
mjr 50:946bc763c068 313 {
samux 8:335f2506f422 314 // Disable IRQ
samux 8:335f2506f422 315 NVIC_DisableIRQ(USB0_IRQn);
mbed_official 20:d38b72fed893 316
mbed_official 22:5b7d31d9d3f3 317 #if defined(TARGET_K64F)
mbed_official 22:5b7d31d9d3f3 318 MPU->CESR=0;
mbed_official 22:5b7d31d9d3f3 319 #endif
samux 8:335f2506f422 320 // fill in callback array
mjr 48:b225d025ca1d 321 epCallback[0] = &USBHAL::EP1_OUT_callback;
mjr 48:b225d025ca1d 322 epCallback[1] = &USBHAL::EP1_IN_callback;
mjr 48:b225d025ca1d 323 epCallback[2] = &USBHAL::EP2_OUT_callback;
mjr 48:b225d025ca1d 324 epCallback[3] = &USBHAL::EP2_IN_callback;
mjr 48:b225d025ca1d 325 epCallback[4] = &USBHAL::EP3_OUT_callback;
mjr 48:b225d025ca1d 326 epCallback[5] = &USBHAL::EP3_IN_callback;
mjr 48:b225d025ca1d 327 epCallback[6] = &USBHAL::EP4_OUT_callback;
mjr 48:b225d025ca1d 328 epCallback[7] = &USBHAL::EP4_IN_callback;
mjr 48:b225d025ca1d 329 epCallback[8] = &USBHAL::EP5_OUT_callback;
mjr 48:b225d025ca1d 330 epCallback[9] = &USBHAL::EP5_IN_callback;
mjr 48:b225d025ca1d 331 epCallback[10] = &USBHAL::EP6_OUT_callback;
mjr 48:b225d025ca1d 332 epCallback[11] = &USBHAL::EP6_IN_callback;
mjr 48:b225d025ca1d 333 epCallback[12] = &USBHAL::EP7_OUT_callback;
mjr 48:b225d025ca1d 334 epCallback[13] = &USBHAL::EP7_IN_callback;
mjr 48:b225d025ca1d 335 epCallback[14] = &USBHAL::EP8_OUT_callback;
mjr 48:b225d025ca1d 336 epCallback[15] = &USBHAL::EP8_IN_callback;
mjr 48:b225d025ca1d 337 epCallback[16] = &USBHAL::EP9_OUT_callback;
mjr 48:b225d025ca1d 338 epCallback[17] = &USBHAL::EP9_IN_callback;
mjr 48:b225d025ca1d 339 epCallback[18] = &USBHAL::EP10_OUT_callback;
mjr 48:b225d025ca1d 340 epCallback[19] = &USBHAL::EP10_IN_callback;
mjr 48:b225d025ca1d 341 epCallback[20] = &USBHAL::EP11_OUT_callback;
mjr 48:b225d025ca1d 342 epCallback[21] = &USBHAL::EP11_IN_callback;
mjr 48:b225d025ca1d 343 epCallback[22] = &USBHAL::EP12_OUT_callback;
mjr 48:b225d025ca1d 344 epCallback[23] = &USBHAL::EP12_IN_callback;
mjr 48:b225d025ca1d 345 epCallback[24] = &USBHAL::EP13_OUT_callback;
mjr 48:b225d025ca1d 346 epCallback[25] = &USBHAL::EP13_IN_callback;
mjr 48:b225d025ca1d 347 epCallback[26] = &USBHAL::EP14_OUT_callback;
mjr 48:b225d025ca1d 348 epCallback[27] = &USBHAL::EP14_IN_callback;
mjr 48:b225d025ca1d 349 epCallback[28] = &USBHAL::EP15_OUT_callback;
mjr 48:b225d025ca1d 350 epCallback[29] = &USBHAL::EP15_IN_callback;
mbed_official 20:d38b72fed893 351
samux 8:335f2506f422 352 // choose usb src as PLL
samux 8:335f2506f422 353 SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);
mbed_official 20:d38b72fed893 354
samux 8:335f2506f422 355 // enable OTG clock
samux 8:335f2506f422 356 SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
samux 8:335f2506f422 357
mjr 37:c5ac4ccf6597 358 // Attach IRQ
mjr 37:c5ac4ccf6597 359 instance = this;
mjr 37:c5ac4ccf6597 360 NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
mjr 37:c5ac4ccf6597 361 NVIC_EnableIRQ(USB0_IRQn);
mjr 37:c5ac4ccf6597 362
samux 8:335f2506f422 363 // USB Module Configuration
samux 8:335f2506f422 364 // Reset USB Module
mjr 50:946bc763c068 365 resetSIE();
samux 8:335f2506f422 366 }
samux 8:335f2506f422 367
mjr 37:c5ac4ccf6597 368 USBHAL::~USBHAL(void)
mjr 48:b225d025ca1d 369 {
mjr 50:946bc763c068 370 // Free buffers
mjr 50:946bc763c068 371 for (int i = 0 ; i < NUMBER_OF_PHYSICAL_ENDPOINTS ; i++)
mjr 50:946bc763c068 372 {
mjr 50:946bc763c068 373 if (endpoint_buffer[i] != NULL)
mjr 50:946bc763c068 374 {
mjr 50:946bc763c068 375 delete [] endpoint_buffer[i];
mjr 50:946bc763c068 376 endpoint_buffer[i] = NULL;
mjr 50:946bc763c068 377 epMaxPacket[i] = 0;
mjr 50:946bc763c068 378 }
mjr 50:946bc763c068 379 }
mjr 37:c5ac4ccf6597 380 }
mjr 35:53e1a208f582 381
mjr 36:20bb47609697 382 void USBHAL::connect(void)
mjr 36:20bb47609697 383 {
mjr 35:53e1a208f582 384 // enable USB
mjr 35:53e1a208f582 385 USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
mjr 48:b225d025ca1d 386
mjr 35:53e1a208f582 387 // Pull up enable
mjr 35:53e1a208f582 388 USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
mjr 34:884405d998bb 389 }
samux 8:335f2506f422 390
mjr 35:53e1a208f582 391 void USBHAL::disconnect(void)
mjr 34:884405d998bb 392 {
samux 8:335f2506f422 393 // disable USB
samux 8:335f2506f422 394 USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
mjr 48:b225d025ca1d 395
samux 8:335f2506f422 396 // Pull up disable
samux 8:335f2506f422 397 USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
mjr 50:946bc763c068 398 }
bogdanm 14:d495202c90f4 399
mjr 50:946bc763c068 400 void USBHAL::hardReset(void)
mjr 50:946bc763c068 401 {
mjr 50:946bc763c068 402 // reset the SIE module
mjr 50:946bc763c068 403 resetSIE();
mjr 50:946bc763c068 404
mjr 50:946bc763c068 405 // do the internal reset work
mjr 50:946bc763c068 406 internalReset();
samux 8:335f2506f422 407 }
samux 8:335f2506f422 408
mjr 37:c5ac4ccf6597 409 void USBHAL::configureDevice(void)
mjr 37:c5ac4ccf6597 410 {
samux 8:335f2506f422 411 // not needed
samux 8:335f2506f422 412 }
samux 8:335f2506f422 413
mjr 37:c5ac4ccf6597 414 void USBHAL::unconfigureDevice(void)
mjr 37:c5ac4ccf6597 415 {
samux 8:335f2506f422 416 // not needed
samux 8:335f2506f422 417 }
samux 8:335f2506f422 418
mjr 37:c5ac4ccf6597 419 void USBHAL::setAddress(uint8_t address)
mjr 37:c5ac4ccf6597 420 {
samux 8:335f2506f422 421 // we don't set the address now otherwise the usb controller does not ack
samux 8:335f2506f422 422 // we set a flag instead
samux 8:335f2506f422 423 // see usbisr when an IN token is received
samux 8:335f2506f422 424 set_addr = 1;
samux 8:335f2506f422 425 addr = address;
mjr 35:53e1a208f582 426 }
samux 8:335f2506f422 427
mjr 35:53e1a208f582 428 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags)
mjr 34:884405d998bb 429 {
mjr 37:c5ac4ccf6597 430 // validate the endpoint number
mjr 34:884405d998bb 431 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS)
mjr 34:884405d998bb 432 return false;
samux 8:335f2506f422 433
mjr 37:c5ac4ccf6597 434 // get the logical endpoint
samux 8:335f2506f422 435 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 34:884405d998bb 436
mjr 48:b225d025ca1d 437 // Assume this is a bulk or interrupt endpoint. For these, the hardware maximum
mjr 48:b225d025ca1d 438 // packet size is 64 bytes, and we use packet handshaking.
mjr 48:b225d025ca1d 439 uint32_t hwMaxPacket = 64;
mjr 48:b225d025ca1d 440 uint32_t handshake_flag = USB_ENDPT_EPHSHK_MASK;
mjr 48:b225d025ca1d 441
mjr 48:b225d025ca1d 442 // If it's to be an isochronous endpoint, the hardware maximum packet size
mjr 48:b225d025ca1d 443 // increases to 1023 bytes, and we don't use handshaking.
mjr 48:b225d025ca1d 444 if (flags & ISOCHRONOUS)
mjr 48:b225d025ca1d 445 {
mjr 50:946bc763c068 446 hwMaxPacket = 1023;
mjr 48:b225d025ca1d 447 handshake_flag = 0;
mjr 48:b225d025ca1d 448 }
mjr 37:c5ac4ccf6597 449
mjr 48:b225d025ca1d 450 // limit the requested max packet size to the hardware limit
mjr 48:b225d025ca1d 451 if (maxPacket > hwMaxPacket)
mjr 48:b225d025ca1d 452 maxPacket = hwMaxPacket;
mjr 37:c5ac4ccf6597 453
mjr 37:c5ac4ccf6597 454 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 455 {
mjr 48:b225d025ca1d 456 // if the endpoint buffer hasn't been allocated yet or was previously
mjr 48:b225d025ca1d 457 // allocated at a smaller size, allocate a new buffer
mjr 37:c5ac4ccf6597 458 uint8_t *buf = endpoint_buffer[endpoint];
mjr 48:b225d025ca1d 459 if (buf == NULL || epMaxPacket[endpoint] < maxPacket)
mjr 37:c5ac4ccf6597 460 {
mjr 48:b225d025ca1d 461 // free any previous buffer
mjr 37:c5ac4ccf6597 462 if (buf != 0)
mjr 49:03527ce6840e 463 delete [] buf;
mjr 48:b225d025ca1d 464
mjr 48:b225d025ca1d 465 // allocate at the new size
mjr 49:03527ce6840e 466 endpoint_buffer[endpoint] = buf = new uint8_t[maxPacket];
mjr 48:b225d025ca1d 467
mjr 48:b225d025ca1d 468 // set the new max packet size
mjr 37:c5ac4ccf6597 469 epMaxPacket[endpoint] = maxPacket;
mjr 37:c5ac4ccf6597 470 }
mjr 37:c5ac4ccf6597 471
mjr 48:b225d025ca1d 472 // set the endpoint register flags and BDT entry
mjr 48:b225d025ca1d 473 if (IN_EP(endpoint))
mjr 48:b225d025ca1d 474 {
mjr 48:b225d025ca1d 475 // IN endpt -> device to host (TX)
mjr 48:b225d025ca1d 476 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | USB_ENDPT_EPTXEN_MASK; // en TX (IN) tran
mjr 48:b225d025ca1d 477 bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = (uint32_t) buf;
mjr 48:b225d025ca1d 478 bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = 0;
mjr 48:b225d025ca1d 479 }
mjr 48:b225d025ca1d 480 else
mjr 37:c5ac4ccf6597 481 {
mjr 48:b225d025ca1d 482 // OUT endpt -> host to device (RX)
mjr 48:b225d025ca1d 483 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | USB_ENDPT_EPRXEN_MASK; // en RX (OUT) tran.
mjr 48:b225d025ca1d 484 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].address = (uint32_t) buf;
mjr 48:b225d025ca1d 485 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address = 0;
mjr 48:b225d025ca1d 486
mjr 48:b225d025ca1d 487 // set up the first read
mjr 48:b225d025ca1d 488 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].byte_count = maxPacket;
mjr 48:b225d025ca1d 489 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 48:b225d025ca1d 490 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info = 0;
mjr 37:c5ac4ccf6597 491 }
mjr 37:c5ac4ccf6597 492
mjr 37:c5ac4ccf6597 493 // Set DATA1 on the endpoint. For RX endpoints, we just queued up our first
mjr 37:c5ac4ccf6597 494 // read, which will always be a DATA0 packet, so the next read will use DATA1.
mjr 37:c5ac4ccf6597 495 // For TX endpoints, we always flip the bit *before* sending the packet, so
mjr 37:c5ac4ccf6597 496 // (counterintuitively) we need to set the DATA1 bit now to send DATA0 in the
mjr 37:c5ac4ccf6597 497 // next packet. So in either case, we want DATA1 initially.
mjr 37:c5ac4ccf6597 498 Data1 |= (1 << endpoint);
mjr 50:946bc763c068 499
mjr 50:946bc763c068 500 // mark the endpoint as realised
mjr 50:946bc763c068 501 epRealised |= (1 << endpoint);
mjr 35:53e1a208f582 502 }
mjr 37:c5ac4ccf6597 503 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 504
mjr 36:20bb47609697 505 // success
samux 8:335f2506f422 506 return true;
samux 8:335f2506f422 507 }
samux 8:335f2506f422 508
mjr 34:884405d998bb 509 // read setup packet
mjr 36:20bb47609697 510 void USBHAL::EP0setup(uint8_t *buffer)
mjr 36:20bb47609697 511 {
mjr 34:884405d998bb 512 uint32_t sz;
mjr 37:c5ac4ccf6597 513 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 514 }
mjr 34:884405d998bb 515
mjr 50:946bc763c068 516 // Start reading the data stage of a SETUP transaction on EP0
mjr 36:20bb47609697 517 void USBHAL::EP0readStage(void)
mjr 36:20bb47609697 518 {
mjr 37:c5ac4ccf6597 519 if (!(bdt[0].info & BD_OWN_MASK))
mjr 37:c5ac4ccf6597 520 {
mjr 37:c5ac4ccf6597 521 Data1 &= ~1UL; // set DATA0
mjr 37:c5ac4ccf6597 522 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 37:c5ac4ccf6597 523 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
mjr 36:20bb47609697 524 }
mjr 34:884405d998bb 525 }
mjr 34:884405d998bb 526
mjr 50:946bc763c068 527 // Read an OUT packet on EP0
mjr 36:20bb47609697 528 void USBHAL::EP0read(void)
mjr 36:20bb47609697 529 {
mjr 38:072e12583e73 530 if (!(bdt[0].info & BD_OWN_MASK))
mjr 50:946bc763c068 531 {
mjr 50:946bc763c068 532 Data1 &= ~1UL;
mjr 37:c5ac4ccf6597 533 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 50:946bc763c068 534 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
mjr 50:946bc763c068 535 }
mjr 34:884405d998bb 536 }
mjr 34:884405d998bb 537
mjr 36:20bb47609697 538 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer)
mjr 36:20bb47609697 539 {
mjr 35:53e1a208f582 540 uint32_t sz;
mjr 50:946bc763c068 541 if (endpointReadResult(EP0OUT, buffer, &sz) == EP_COMPLETED) {
mjr 50:946bc763c068 542 return sz;
mjr 50:946bc763c068 543 }
mjr 50:946bc763c068 544 else {
mjr 50:946bc763c068 545 return 0;
mjr 50:946bc763c068 546 }
mjr 34:884405d998bb 547 }
mjr 34:884405d998bb 548
mjr 50:946bc763c068 549 void USBHAL::EP0write(const volatile uint8_t *buffer, uint32_t size)
mjr 37:c5ac4ccf6597 550 {
mjr 37:c5ac4ccf6597 551 endpointWrite(EP0IN, buffer, size);
mjr 34:884405d998bb 552 }
mjr 34:884405d998bb 553
mjr 37:c5ac4ccf6597 554 void USBHAL::EP0getWriteResult(void)
mjr 37:c5ac4ccf6597 555 {
mjr 34:884405d998bb 556 }
mjr 34:884405d998bb 557
mjr 36:20bb47609697 558 void USBHAL::EP0stall(void)
mjr 36:20bb47609697 559 {
mjr 51:666cc4fedd3f 560 stallEndpoint(EP0OUT);
mjr 34:884405d998bb 561 }
mjr 34:884405d998bb 562
mjr 35:53e1a208f582 563 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize)
mjr 34:884405d998bb 564 {
mjr 37:c5ac4ccf6597 565 // We always start a new read when we fetch the result of the
mjr 37:c5ac4ccf6597 566 // previous read, so we don't have to do anything here. Simply
mjr 37:c5ac4ccf6597 567 // indicate that the read is pending so that the caller can proceed
mjr 37:c5ac4ccf6597 568 // to check the results.
mjr 34:884405d998bb 569 return EP_PENDING;
mjr 34:884405d998bb 570 }
mjr 34:884405d998bb 571
mjr 36:20bb47609697 572 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t *buffer, uint32_t *bytesRead)
mjr 34:884405d998bb 573 {
mjr 50:946bc763c068 574 // validate the endpoint number and direction, and make sure it's realised
mjr 37:c5ac4ccf6597 575 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !OUT_EP(endpoint))
mjr 34:884405d998bb 576 return EP_INVALID;
mjr 34:884405d998bb 577
mjr 37:c5ac4ccf6597 578 // get the logical endpoint
mjr 34:884405d998bb 579 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 37:c5ac4ccf6597 580
mjr 37:c5ac4ccf6597 581 // get the mode - it's isochronous if it doesn't have the handshake flag
mjr 48:b225d025ca1d 582 bool iso = (USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK) == 0;
mjr 37:c5ac4ccf6597 583
mjr 35:53e1a208f582 584 // get the BDT index
mjr 35:53e1a208f582 585 int idx = EP_BDT_IDX(log_endpoint, RX, 0);
mjr 37:c5ac4ccf6597 586
mjr 48:b225d025ca1d 587 // Check to see if the endpoint is ready to read
mjr 37:c5ac4ccf6597 588 if (log_endpoint == 0)
mjr 37:c5ac4ccf6597 589 {
mjr 37:c5ac4ccf6597 590 // control endpoint - just make sure we own the BDT
mjr 37:c5ac4ccf6597 591 if (bdt[idx].info & BD_OWN_MASK)
mjr 37:c5ac4ccf6597 592 return EP_PENDING;
mjr 37:c5ac4ccf6597 593 }
mjr 48:b225d025ca1d 594 else
mjr 48:b225d025ca1d 595 {
mjr 48:b225d025ca1d 596 // If it's not isochronous, check to see if we've received data, and
mjr 48:b225d025ca1d 597 // return PENDING if not. Isochronous endpoints don't use the TOKNE
mjr 48:b225d025ca1d 598 // interrupt (they use SOF instead), so the 'complete' flag doesn't
mjr 48:b225d025ca1d 599 // apply if it's an iso endpoint.
mjr 48:b225d025ca1d 600 if (!iso && !(epComplete & EP(endpoint)))
mjr 48:b225d025ca1d 601 return EP_PENDING;
mjr 48:b225d025ca1d 602 }
mjr 50:946bc763c068 603
mjr 50:946bc763c068 604 EP_STATUS result = EP_INVALID;
mjr 37:c5ac4ccf6597 605 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 606 {
mjr 50:946bc763c068 607 // proceed only if the endpoint has been realised
mjr 50:946bc763c068 608 if (epRealised & EP(endpoint))
mjr 50:946bc763c068 609 {
mjr 50:946bc763c068 610 // note if we have a SETUP token
mjr 50:946bc763c068 611 bool setup = (log_endpoint == 0 && TOK_PID(idx) == SETUP_TOKEN);
mjr 50:946bc763c068 612
mjr 50:946bc763c068 613 // get the received data buffer and size
mjr 50:946bc763c068 614 uint8_t *ep_buf = endpoint_buffer[endpoint];
mjr 50:946bc763c068 615 uint32_t sz = bdt[idx].byte_count;
mjr 48:b225d025ca1d 616
mjr 50:946bc763c068 617 // copy the data from the hardware receive buffer to the caller's buffer
mjr 50:946bc763c068 618 *bytesRead = sz;
mjr 50:946bc763c068 619 for (uint32_t n = 0 ; n < sz ; n++)
mjr 50:946bc763c068 620 buffer[n] = ep_buf[n];
mjr 50:946bc763c068 621
mjr 50:946bc763c068 622 // Figure the DATA0/DATA1 bit for the next packet received on this
mjr 50:946bc763c068 623 // endpoint. The bit normally toggles on each packet, but it's
mjr 50:946bc763c068 624 // special for SETUP packets on endpoint 0. The next OUT packet
mjr 50:946bc763c068 625 // after a SETUP packet with no data stage is always DATA0, even
mjr 50:946bc763c068 626 // if the SETUP packet was also DATA0.
mjr 50:946bc763c068 627 if (setup && (sz >= 7 && buffer[6] == 0)) {
mjr 50:946bc763c068 628 // SETUP with no data stage -> next packet is always DATA0
mjr 50:946bc763c068 629 Data1 &= ~1UL;
mjr 50:946bc763c068 630 }
mjr 50:946bc763c068 631 else {
mjr 50:946bc763c068 632 // otherwise just toggle the last bit (assuming it matches our
mjr 50:946bc763c068 633 // internal state - if not, we must be out of sync, so presumably
mjr 50:946bc763c068 634 // *not* toggling our state will get us back in sync)
mjr 50:946bc763c068 635 if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1))
mjr 50:946bc763c068 636 Data1 ^= (1 << endpoint);
mjr 50:946bc763c068 637 }
mjr 50:946bc763c068 638
mjr 50:946bc763c068 639 // set up the BDT entry to receive the next packet, and hand it to the SIE
mjr 50:946bc763c068 640 bdt[idx].byte_count = epMaxPacket[endpoint];
mjr 50:946bc763c068 641 bdt[idx].info = BD_DTS_MASK | BD_OWN_MASK | (((Data1 >> endpoint) & 1) << 6);
mjr 50:946bc763c068 642
mjr 50:946bc763c068 643 // clear the SUSPEND TOKEN BUSY flag to allow token processing to continue
mjr 50:946bc763c068 644 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 645
mjr 50:946bc763c068 646 // clear the 'completed' flag - we're now awaiting the next packet
mjr 50:946bc763c068 647 epComplete &= ~EP(endpoint);
mjr 50:946bc763c068 648
mjr 50:946bc763c068 649 // the read is now complete
mjr 50:946bc763c068 650 result = EP_COMPLETED;
mjr 35:53e1a208f582 651 }
mjr 37:c5ac4ccf6597 652 }
mjr 37:c5ac4ccf6597 653 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 654
mjr 50:946bc763c068 655 return result;
samux 8:335f2506f422 656 }
samux 8:335f2506f422 657
mjr 50:946bc763c068 658 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const volatile uint8_t *data, uint32_t size)
mjr 34:884405d998bb 659 {
mjr 37:c5ac4ccf6597 660 // validate the endpoint number and direction
mjr 37:c5ac4ccf6597 661 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !IN_EP(endpoint))
samux 8:335f2506f422 662 return EP_INVALID;
mjr 35:53e1a208f582 663
mjr 37:c5ac4ccf6597 664 // get the BDT index
mjr 35:53e1a208f582 665 int idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
mjr 37:c5ac4ccf6597 666
mjr 50:946bc763c068 667 EP_STATUS result = EP_INVALID;
mjr 37:c5ac4ccf6597 668 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 669 {
mjr 50:946bc763c068 670 // proceed only if the endpoint has been realised and we own the BDT
mjr 50:946bc763c068 671 if ((epRealised & EP(endpoint)) && !(bdt[idx].info & BD_OWN_MASK))
mjr 50:946bc763c068 672 {
mjr 50:946bc763c068 673 // get the endpoint buffer
mjr 50:946bc763c068 674 uint8_t *ep_buf = endpoint_buffer[endpoint];
mjr 50:946bc763c068 675
mjr 50:946bc763c068 676 // copy the data to the hardware buffer
mjr 50:946bc763c068 677 bdt[idx].byte_count = size;
mjr 50:946bc763c068 678 for (uint32_t n = 0 ; n < size ; n++)
mjr 50:946bc763c068 679 ep_buf[n] = data[n];
mjr 50:946bc763c068 680
mjr 50:946bc763c068 681 // toggle DATA0/DATA1 before sending
mjr 50:946bc763c068 682 Data1 ^= (1 << endpoint);
mjr 48:b225d025ca1d 683
mjr 50:946bc763c068 684 // hand the BDT to the SIE to do the send
mjr 50:946bc763c068 685 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | (((Data1 >> endpoint) & 1) << 6);
mjr 35:53e1a208f582 686
mjr 50:946bc763c068 687 // write is now pending in the hardware
mjr 50:946bc763c068 688 result = EP_PENDING;
mjr 50:946bc763c068 689 }
mjr 37:c5ac4ccf6597 690 }
mjr 37:c5ac4ccf6597 691 EXIT_CRITICAL_SECTION
mjr 48:b225d025ca1d 692
mjr 50:946bc763c068 693 return result;
samux 8:335f2506f422 694 }
samux 8:335f2506f422 695
mjr 35:53e1a208f582 696 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint)
mjr 34:884405d998bb 697 {
mjr 48:b225d025ca1d 698 // assume write is still pending
mjr 37:c5ac4ccf6597 699 EP_STATUS result = EP_PENDING;
mjr 48:b225d025ca1d 700
mjr 37:c5ac4ccf6597 701 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 702 {
mjr 50:946bc763c068 703 // If the endpoint isn't realised, the result is 'invalid'. Otherwise,
mjr 50:946bc763c068 704 // check the 'completed' flag: if set, the write is completed.
mjr 50:946bc763c068 705 if (!(epRealised & EP(endpoint)))
mjr 50:946bc763c068 706 {
mjr 50:946bc763c068 707 // endpoint isn't realised - can't read it
mjr 50:946bc763c068 708 result = EP_INVALID;
mjr 50:946bc763c068 709 }
mjr 50:946bc763c068 710 else if (epComplete & EP(endpoint))
mjr 48:b225d025ca1d 711 {
mjr 48:b225d025ca1d 712 // the result is COMPLETED
mjr 48:b225d025ca1d 713 result = EP_COMPLETED;
mjr 48:b225d025ca1d 714
mjr 48:b225d025ca1d 715 // clear the 'completed' flag - this is consumed by fetching the result
mjr 37:c5ac4ccf6597 716 epComplete &= ~EP(endpoint);
mjr 37:c5ac4ccf6597 717 }
samux 8:335f2506f422 718 }
mjr 37:c5ac4ccf6597 719 EXIT_CRITICAL_SECTION
mjr 36:20bb47609697 720
mjr 48:b225d025ca1d 721 // return the result
mjr 37:c5ac4ccf6597 722 return result;
samux 8:335f2506f422 723 }
samux 8:335f2506f422 724
mjr 36:20bb47609697 725 void USBHAL::stallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 726 {
mjr 50:946bc763c068 727 ENTER_CRITICAL_SECTION
mjr 50:946bc763c068 728 {
mjr 50:946bc763c068 729 if (epRealised & EP(endpoint))
mjr 50:946bc763c068 730 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
mjr 50:946bc763c068 731 }
mjr 50:946bc763c068 732 EXIT_CRITICAL_SECTION
samux 8:335f2506f422 733 }
samux 8:335f2506f422 734
mjr 36:20bb47609697 735 void USBHAL::unstallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 736 {
mjr 37:c5ac4ccf6597 737 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 738 {
mjr 50:946bc763c068 739 if (epRealised & EP(endpoint))
mjr 48:b225d025ca1d 740 {
mjr 50:946bc763c068 741 // clear the stall bit in the endpoint register
mjr 50:946bc763c068 742 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
mjr 50:946bc763c068 743
mjr 50:946bc763c068 744 // take ownership of the BDT entry
mjr 50:946bc763c068 745 int idx = PEP_BDT_IDX(endpoint, 0);
mjr 50:946bc763c068 746 bdt[idx].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 50:946bc763c068 747
mjr 50:946bc763c068 748 // if this is an RX endpoint, start a new read
mjr 50:946bc763c068 749 if (OUT_EP(endpoint))
mjr 50:946bc763c068 750 {
mjr 50:946bc763c068 751 bdt[idx].byte_count = epMaxPacket[endpoint];
mjr 50:946bc763c068 752 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 50:946bc763c068 753 }
mjr 50:946bc763c068 754
mjr 50:946bc763c068 755 // Reset Data1 for the endpoint - we need to set the bit to 1 for
mjr 50:946bc763c068 756 // either TX or RX, by the same logic as in realiseEndpoint()
mjr 50:946bc763c068 757 Data1 |= (1 << endpoint);
mjr 50:946bc763c068 758
mjr 50:946bc763c068 759 // clear the 'completed' bit for the endpoint
mjr 50:946bc763c068 760 epComplete &= ~(1 << endpoint);
mjr 48:b225d025ca1d 761 }
mjr 36:20bb47609697 762 }
mjr 37:c5ac4ccf6597 763 EXIT_CRITICAL_SECTION
samux 8:335f2506f422 764 }
samux 8:335f2506f422 765
mjr 50:946bc763c068 766 void USBHAL_KL25Z_unstall_EP0(bool force)
mjr 37:c5ac4ccf6597 767 {
mjr 50:946bc763c068 768 ENTER_CRITICAL_SECTION
mjr 39:d684a8ce5d88 769 {
mjr 50:946bc763c068 770 if (force || (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK))
mjr 37:c5ac4ccf6597 771 {
mjr 48:b225d025ca1d 772 // clear the stall bit in the endpoint register
mjr 48:b225d025ca1d 773 USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
mjr 48:b225d025ca1d 774
mjr 48:b225d025ca1d 775 // take ownership of the RX and TX BDTs
mjr 48:b225d025ca1d 776 bdt[EP_BDT_IDX(0, RX, EVEN)].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 48:b225d025ca1d 777 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 48:b225d025ca1d 778 bdt[EP_BDT_IDX(0, RX, EVEN)].byte_count = MAX_PACKET_SIZE_EP0;
mjr 48:b225d025ca1d 779 bdt[EP_BDT_IDX(0, TX, EVEN)].byte_count = MAX_PACKET_SIZE_EP0;
mjr 48:b225d025ca1d 780
mjr 48:b225d025ca1d 781 // start a new read on EP0OUT
mjr 48:b225d025ca1d 782 bdt[EP_BDT_IDX(0, RX, EVEN)].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 48:b225d025ca1d 783
mjr 48:b225d025ca1d 784 // reset the DATA0/1 bit to 1 on EP0IN and EP0OUT, by the same
mjr 48:b225d025ca1d 785 // logic as in realiseEndpoint()
mjr 48:b225d025ca1d 786 Data1 |= 0x03;
mjr 36:20bb47609697 787 }
mjr 50:946bc763c068 788 }
mjr 50:946bc763c068 789 EXIT_CRITICAL_SECTION
mjr 50:946bc763c068 790 }
mjr 50:946bc763c068 791
mjr 50:946bc763c068 792 bool USBHAL::getEndpointStallState(uint8_t endpoint)
mjr 50:946bc763c068 793 {
mjr 50:946bc763c068 794 uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
mjr 50:946bc763c068 795 return (stall) ? true : false;
mjr 50:946bc763c068 796 }
mjr 50:946bc763c068 797
mjr 50:946bc763c068 798 void USBHAL::remoteWakeup(void)
mjr 50:946bc763c068 799 {
mjr 50:946bc763c068 800 // [TODO]
mjr 50:946bc763c068 801 }
mjr 50:946bc763c068 802
mjr 50:946bc763c068 803 // Internal reset handler. Called when we get a Bus Reset signal
mjr 50:946bc763c068 804 // from the host, and when we initiate a reset of the SIE hardware
mjr 50:946bc763c068 805 // from the device side.
mjr 50:946bc763c068 806 void USBHAL::internalReset(void)
mjr 50:946bc763c068 807 {
mjr 50:946bc763c068 808 ENTER_CRITICAL_SECTION
mjr 50:946bc763c068 809 {
mjr 50:946bc763c068 810 int i;
mjr 37:c5ac4ccf6597 811
mjr 50:946bc763c068 812 // set the default bus address
mjr 50:946bc763c068 813 USB0->ADDR = 0x00;
mjr 50:946bc763c068 814 addr = 0;
mjr 50:946bc763c068 815 set_addr = 0;
mjr 50:946bc763c068 816
mjr 50:946bc763c068 817 // disable all endpoints
mjr 50:946bc763c068 818 epRealised = 0x00;
mjr 50:946bc763c068 819 for (i = 0 ; i < 16 ; i++)
mjr 50:946bc763c068 820 USB0->ENDPOINT[i].ENDPT = 0x00;
mjr 50:946bc763c068 821
mjr 50:946bc763c068 822 // take control of all BDTs away from the SIE
mjr 50:946bc763c068 823 for (i = 0 ; i < sizeof(bdt)/sizeof(bdt[0]) ; ++i)
mjr 50:946bc763c068 824 {
mjr 50:946bc763c068 825 bdt[i].info = 0;
mjr 50:946bc763c068 826 bdt[i].byte_count = 0;
mjr 50:946bc763c068 827 }
mjr 50:946bc763c068 828
mjr 50:946bc763c068 829 // reset DATA0/1 state
mjr 50:946bc763c068 830 Data1 = 0x55555555;
mjr 50:946bc763c068 831
mjr 50:946bc763c068 832 // reset endpoint completion status
mjr 50:946bc763c068 833 epComplete = 0;
mjr 50:946bc763c068 834
mjr 50:946bc763c068 835 // reset EVEN/ODD state (and keep it permanently on EVEN -
mjr 50:946bc763c068 836 // this disables the hardware double-buffering system)
mjr 50:946bc763c068 837 USB0->CTL |= USB_CTL_ODDRST_MASK;
mjr 50:946bc763c068 838
mjr 50:946bc763c068 839 // reset error status and enable all error interrupts
mjr 50:946bc763c068 840 USB0->ERRSTAT = 0xFF;
mjr 50:946bc763c068 841 USB0->ERREN = 0xFF;
mjr 50:946bc763c068 842
mjr 50:946bc763c068 843 // enable our standard complement of interrupts
mjr 50:946bc763c068 844 USB0->INTEN = BUS_RESET_INTERRUPTS;
mjr 50:946bc763c068 845
mjr 50:946bc763c068 846 // we're not suspended
mjr 50:946bc763c068 847 suspendStateChanged(0);
mjr 50:946bc763c068 848
mjr 50:946bc763c068 849 // we're not sleeping
mjr 50:946bc763c068 850 sleepStateChanged(0);
mjr 50:946bc763c068 851
mjr 50:946bc763c068 852 // notify upper layers of the bus reset, to reset the protocol state
mjr 50:946bc763c068 853 busReset();
mjr 50:946bc763c068 854
mjr 50:946bc763c068 855 // realise the control endpoint (EP0) in both directions
mjr 50:946bc763c068 856 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
mjr 50:946bc763c068 857 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
mjr 50:946bc763c068 858 }
mjr 50:946bc763c068 859 EXIT_CRITICAL_SECTION
mjr 50:946bc763c068 860 }
mjr 50:946bc763c068 861
mjr 50:946bc763c068 862 void USBHAL::_usbisr(void)
mjr 50:946bc763c068 863 {
mjr 50:946bc763c068 864 inIRQ = true;
mjr 50:946bc763c068 865 instance->usbisr();
mjr 50:946bc763c068 866 inIRQ = false;
mjr 50:946bc763c068 867 }
mjr 50:946bc763c068 868
mjr 50:946bc763c068 869 void USBHAL::usbisr(void)
mjr 50:946bc763c068 870 {
mjr 50:946bc763c068 871 // get the interrupt status - this tells us which event(s)
mjr 50:946bc763c068 872 // triggered this interrupt
mjr 50:946bc763c068 873 uint8_t istat = USB0->ISTAT;
mjr 50:946bc763c068 874
mjr 50:946bc763c068 875 // reset interrupt
mjr 50:946bc763c068 876 if (istat & USB_ISTAT_USBRST_MASK)
mjr 50:946bc763c068 877 {
mjr 50:946bc763c068 878 // do the internal reset work
mjr 50:946bc763c068 879 internalReset();
mjr 50:946bc763c068 880
mjr 50:946bc763c068 881 // resume token processing if it was suspended
mjr 36:20bb47609697 882 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 48:b225d025ca1d 883
mjr 50:946bc763c068 884 // clear the interrupt status
mjr 50:946bc763c068 885 USB0->ISTAT = USB_ISTAT_USBRST_MASK;
mjr 50:946bc763c068 886
mjr 50:946bc763c068 887 // return immediately, ignoring any other status flags
mjr 50:946bc763c068 888 return;
samux 8:335f2506f422 889 }
mjr 50:946bc763c068 890
mjr 37:c5ac4ccf6597 891 // token interrupt
mjr 37:c5ac4ccf6597 892 if (istat & USB_ISTAT_TOKDNE_MASK)
mjr 36:20bb47609697 893 {
mjr 48:b225d025ca1d 894 // get the endpoint information from the status register
mjr 50:946bc763c068 895 uint32_t stat = USB0->STAT;
mjr 50:946bc763c068 896 uint32_t num = (stat >> 4) & 0x0F;
mjr 50:946bc763c068 897 uint32_t dir = (stat >> 3) & 0x01;
mjr 34:884405d998bb 898 int endpoint = (num << 1) | dir;
mjr 50:946bc763c068 899 uint32_t ev_odd = (stat >> 2) & 0x01;
mjr 50:946bc763c068 900
mjr 48:b225d025ca1d 901 // check which endpoint we're working with
mjr 48:b225d025ca1d 902 if (num == 0)
mjr 48:b225d025ca1d 903 {
mjr 48:b225d025ca1d 904 // Endpoint 0 requires special handling
mjr 48:b225d025ca1d 905 uint32_t idx = EP_BDT_IDX(num, dir, ev_odd);
mjr 48:b225d025ca1d 906 int pid = TOK_PID(idx);
mjr 48:b225d025ca1d 907 if (pid == SETUP_TOKEN)
mjr 48:b225d025ca1d 908 {
mjr 48:b225d025ca1d 909 // SETUP packet - next IN (TX) packet must be DATA1 (confusingly,
mjr 48:b225d025ca1d 910 // this means we must clear the Data1 bit, since we flip the bit
mjr 48:b225d025ca1d 911 // before each send)
mjr 48:b225d025ca1d 912 Data1 &= ~0x02;
mjr 48:b225d025ca1d 913
mjr 50:946bc763c068 914 // Forcibly take ownership of the EP0IN BDT in case we have
mjr 50:946bc763c068 915 // unfinished previous transmissions. The protocol state machine
mjr 48:b225d025ca1d 916 // assumes that we don't, so it's probably an error if this code
mjr 50:946bc763c068 917 // actually does anything, but just in case...
mjr 48:b225d025ca1d 918 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 919
mjr 48:b225d025ca1d 920 // handle the EP0 SETUP event in the generic protocol layer
mjr 48:b225d025ca1d 921 EP0setupCallback();
mjr 48:b225d025ca1d 922 }
mjr 48:b225d025ca1d 923 else if (pid == OUT_TOKEN)
mjr 48:b225d025ca1d 924 {
mjr 48:b225d025ca1d 925 // OUT packet on EP0
mjr 48:b225d025ca1d 926 EP0out();
mjr 48:b225d025ca1d 927 }
mjr 48:b225d025ca1d 928 else if (pid == IN_TOKEN)
mjr 48:b225d025ca1d 929 {
mjr 48:b225d025ca1d 930 // IN packet on EP0
mjr 48:b225d025ca1d 931 EP0in();
mjr 48:b225d025ca1d 932
mjr 48:b225d025ca1d 933 // Special case: if the 'set address' flag is set, it means that the
mjr 48:b225d025ca1d 934 // host just sent us our bus address. We must put this into effect
mjr 50:946bc763c068 935 // in the hardware SIE immediately after sending the reply. We just
mjr 50:946bc763c068 936 // did that above, so this is the time.
mjr 50:946bc763c068 937 if (set_addr) {
mjr 48:b225d025ca1d 938 USB0->ADDR = addr & 0x7F;
mjr 48:b225d025ca1d 939 set_addr = 0;
mjr 48:b225d025ca1d 940 }
mjr 48:b225d025ca1d 941 }
mjr 48:b225d025ca1d 942 }
mjr 48:b225d025ca1d 943 else
mjr 48:b225d025ca1d 944 {
mjr 48:b225d025ca1d 945 // For all other endpoints, note the read/write completion in the flags
mjr 48:b225d025ca1d 946 epComplete |= EP(endpoint);
mjr 48:b225d025ca1d 947
mjr 48:b225d025ca1d 948 // call the endpoint token callback; if that handles the token, it consumes
mjr 48:b225d025ca1d 949 // the 'completed' status, so clear that flag again
mjr 48:b225d025ca1d 950 if ((instance->*(epCallback[endpoint - 2]))()) {
mjr 48:b225d025ca1d 951 epComplete &= ~EP(endpoint);
mjr 48:b225d025ca1d 952 }
mjr 48:b225d025ca1d 953 }
mjr 37:c5ac4ccf6597 954
mjr 50:946bc763c068 955 // resume token processing if suspended
mjr 50:946bc763c068 956 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 957
mjr 48:b225d025ca1d 958 // clear the TOKDNE interrupt status bit
samux 8:335f2506f422 959 USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
mjr 50:946bc763c068 960 return;
mjr 50:946bc763c068 961 }
mjr 50:946bc763c068 962
mjr 50:946bc763c068 963 // SOF interrupt
mjr 50:946bc763c068 964 if (istat & USB_ISTAT_SOFTOK_MASK)
mjr 50:946bc763c068 965 {
mjr 50:946bc763c068 966 // Read frame number and signal the SOF event to the callback
mjr 50:946bc763c068 967 SOF(frameNumber());
mjr 50:946bc763c068 968 USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
mjr 50:946bc763c068 969 }
mjr 50:946bc763c068 970
mjr 50:946bc763c068 971 // stall interrupt
mjr 50:946bc763c068 972 if (istat & USB_ISTAT_STALL_MASK)
mjr 50:946bc763c068 973 {
mjr 50:946bc763c068 974 // if the control endpoint (EP 0) is stalled, unstall it
mjr 50:946bc763c068 975 USBHAL_KL25Z_unstall_EP0(false);
mjr 50:946bc763c068 976
mjr 50:946bc763c068 977 // clear the busy-suspend bit to resume token processing
mjr 50:946bc763c068 978 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 979
mjr 50:946bc763c068 980 // clear the interrupt status bit for STALL
mjr 50:946bc763c068 981 USB0->ISTAT = USB_ISTAT_STALL_MASK;
samux 8:335f2506f422 982 }
mbed_official 20:d38b72fed893 983
mjr 50:946bc763c068 984 // Sleep interrupt. This indicates that the USB bus has been
mjr 50:946bc763c068 985 // idle for at least 3ms (no frames transacted). This has
mjr 50:946bc763c068 986 // several possible causes:
mjr 50:946bc763c068 987 //
mjr 50:946bc763c068 988 // - The USB cable was unplugged
mjr 50:946bc763c068 989 // - The host was powered off
mjr 50:946bc763c068 990 // - The host has stopped communicating due to a software fault
mjr 50:946bc763c068 991 // - The host has stopped communicating deliberately (e.g., due
mjr 50:946bc763c068 992 // to user action, or due to a protocol error)
mjr 50:946bc763c068 993 //
mjr 50:946bc763c068 994 // A "sleep" event on the SIE is not to be confused with the
mjr 50:946bc763c068 995 // sleep/suspend power state on the PC. The sleep event here
mjr 50:946bc763c068 996 // simply means that the SIE isn't seeing token traffic on the
mjr 50:946bc763c068 997 // required schedule.
mjr 50:946bc763c068 998 //
mjr 50:946bc763c068 999 // Note that the sleep event is the closest thing the KL25Z USB
mjr 50:946bc763c068 1000 // module has to a disconnect event. There's no way to detect
mjr 50:946bc763c068 1001 // if we're physically connected to a host, so all we can really
mjr 50:946bc763c068 1002 // know is that we're not transacting tokens. USB requires token
mjr 50:946bc763c068 1003 // exchange every 1ms, so if there's no token exchange for a few
mjr 50:946bc763c068 1004 // milliseconds, the connection must be broken at some level.
mjr 36:20bb47609697 1005 if (istat & USB_ISTAT_SLEEP_MASK)
mjr 36:20bb47609697 1006 {
mjr 50:946bc763c068 1007 // tell the upper layers about the change
mjr 50:946bc763c068 1008 sleepStateChanged(1);
mjr 51:666cc4fedd3f 1009
mjr 50:946bc763c068 1010 // resume token processing
mjr 50:946bc763c068 1011 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 1012
mjr 50:946bc763c068 1013 // reset the interrupt bit
mjr 40:cd877d5c09ea 1014 USB0->ISTAT = USB_ISTAT_SLEEP_MASK;
mbed_official 20:d38b72fed893 1015 }
samux 8:335f2506f422 1016
mjr 50:946bc763c068 1017 // Resume from suspend mode.
mjr 50:946bc763c068 1018 //
mjr 50:946bc763c068 1019 // NB: Don't confuse "suspend" with "sleep". Suspend mode refers
mjr 50:946bc763c068 1020 // to a hardware low-power mode initiated by the device. "Sleep"
mjr 50:946bc763c068 1021 // means only that the USB connection has been idle (no tokens
mjr 50:946bc763c068 1022 // transacted) for more than 3ms. A sleep signal means that the
mjr 50:946bc763c068 1023 // connection with the host was broken, either physically or
mjr 50:946bc763c068 1024 // logically; it doesn't of itself have anything to do with suspend
mjr 50:946bc763c068 1025 // mode, and in particular it doesn't mean that the host has
mjr 50:946bc763c068 1026 // commanded us to enter suspend mode or told us that the host
mjr 50:946bc763c068 1027 // is entering a low-power state. The higher-level device
mjr 50:946bc763c068 1028 // implementation might choose to enter suspend mode on the device
mjr 50:946bc763c068 1029 // in response to a lost connection, but the USB/HAL layers don't
mjr 50:946bc763c068 1030 // take any such action on their own. Note that suspend mode can
mjr 50:946bc763c068 1031 // only end with explicit intervention by the host, in the form of
mjr 50:946bc763c068 1032 // a USB RESUME signal, so the host has to be aware that we're
mjr 50:946bc763c068 1033 // doing this sort of power management.
mjr 50:946bc763c068 1034 if (istat & USB_ISTAT_RESUME_MASK)
mjr 50:946bc763c068 1035 {
mjr 50:946bc763c068 1036 // note the change
mjr 50:946bc763c068 1037 suspendStateChanged(0);
mjr 50:946bc763c068 1038
mjr 50:946bc763c068 1039 // remove suspend mode flags
mjr 50:946bc763c068 1040 USB0->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
mjr 50:946bc763c068 1041 USB0->USBTRC0 &= ~USB_USBTRC0_USBRESMEN_MASK;
mjr 50:946bc763c068 1042 USB0->INTEN &= ~USB_INTEN_RESUMEEN_MASK;
mjr 50:946bc763c068 1043
mjr 50:946bc763c068 1044 // clear the interrupt status
mjr 50:946bc763c068 1045 USB0->ISTAT = USB_ISTAT_RESUME_MASK;
mjr 50:946bc763c068 1046 }
mjr 50:946bc763c068 1047
samux 8:335f2506f422 1048 // error interrupt
mjr 36:20bb47609697 1049 if (istat & USB_ISTAT_ERROR_MASK)
mjr 36:20bb47609697 1050 {
mjr 37:c5ac4ccf6597 1051 // reset all error status bits, and clear the SUSPEND flag to allow
mjr 37:c5ac4ccf6597 1052 // token processing to continue
mjr 37:c5ac4ccf6597 1053 USB0->ERRSTAT = 0xFF;
mjr 36:20bb47609697 1054 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 40:cd877d5c09ea 1055 USB0->ISTAT = USB_ISTAT_ERROR_MASK;
samux 8:335f2506f422 1056 }
samux 8:335f2506f422 1057 }
samux 8:335f2506f422 1058
mjr 36:20bb47609697 1059 #endif