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:
Wed Apr 27 01:50:32 2016 +0000
Revision:
50:946bc763c068
Parent:
49:03527ce6840e
Child:
51:666cc4fedd3f
Error handling in the main code with the long explanation

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 37:c5ac4ccf6597 35 static int 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 //$$$USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);
mjr 50:946bc763c068 307
mjr 50:946bc763c068 308 // set the "reserved" bit in the transceiver control register
mjr 50:946bc763c068 309 // (hw ref: "software must set this bit to 1")
mjr 50:946bc763c068 310 USB0->USBTRC0 |= 0x40;
mjr 50:946bc763c068 311 }
mjr 50:946bc763c068 312
mjr 50:946bc763c068 313 USBHAL::USBHAL(void)
mjr 50:946bc763c068 314 {
samux 8:335f2506f422 315 // Disable IRQ
samux 8:335f2506f422 316 NVIC_DisableIRQ(USB0_IRQn);
mbed_official 20:d38b72fed893 317
mbed_official 22:5b7d31d9d3f3 318 #if defined(TARGET_K64F)
mbed_official 22:5b7d31d9d3f3 319 MPU->CESR=0;
mbed_official 22:5b7d31d9d3f3 320 #endif
samux 8:335f2506f422 321 // fill in callback array
mjr 48:b225d025ca1d 322 epCallback[0] = &USBHAL::EP1_OUT_callback;
mjr 48:b225d025ca1d 323 epCallback[1] = &USBHAL::EP1_IN_callback;
mjr 48:b225d025ca1d 324 epCallback[2] = &USBHAL::EP2_OUT_callback;
mjr 48:b225d025ca1d 325 epCallback[3] = &USBHAL::EP2_IN_callback;
mjr 48:b225d025ca1d 326 epCallback[4] = &USBHAL::EP3_OUT_callback;
mjr 48:b225d025ca1d 327 epCallback[5] = &USBHAL::EP3_IN_callback;
mjr 48:b225d025ca1d 328 epCallback[6] = &USBHAL::EP4_OUT_callback;
mjr 48:b225d025ca1d 329 epCallback[7] = &USBHAL::EP4_IN_callback;
mjr 48:b225d025ca1d 330 epCallback[8] = &USBHAL::EP5_OUT_callback;
mjr 48:b225d025ca1d 331 epCallback[9] = &USBHAL::EP5_IN_callback;
mjr 48:b225d025ca1d 332 epCallback[10] = &USBHAL::EP6_OUT_callback;
mjr 48:b225d025ca1d 333 epCallback[11] = &USBHAL::EP6_IN_callback;
mjr 48:b225d025ca1d 334 epCallback[12] = &USBHAL::EP7_OUT_callback;
mjr 48:b225d025ca1d 335 epCallback[13] = &USBHAL::EP7_IN_callback;
mjr 48:b225d025ca1d 336 epCallback[14] = &USBHAL::EP8_OUT_callback;
mjr 48:b225d025ca1d 337 epCallback[15] = &USBHAL::EP8_IN_callback;
mjr 48:b225d025ca1d 338 epCallback[16] = &USBHAL::EP9_OUT_callback;
mjr 48:b225d025ca1d 339 epCallback[17] = &USBHAL::EP9_IN_callback;
mjr 48:b225d025ca1d 340 epCallback[18] = &USBHAL::EP10_OUT_callback;
mjr 48:b225d025ca1d 341 epCallback[19] = &USBHAL::EP10_IN_callback;
mjr 48:b225d025ca1d 342 epCallback[20] = &USBHAL::EP11_OUT_callback;
mjr 48:b225d025ca1d 343 epCallback[21] = &USBHAL::EP11_IN_callback;
mjr 48:b225d025ca1d 344 epCallback[22] = &USBHAL::EP12_OUT_callback;
mjr 48:b225d025ca1d 345 epCallback[23] = &USBHAL::EP12_IN_callback;
mjr 48:b225d025ca1d 346 epCallback[24] = &USBHAL::EP13_OUT_callback;
mjr 48:b225d025ca1d 347 epCallback[25] = &USBHAL::EP13_IN_callback;
mjr 48:b225d025ca1d 348 epCallback[26] = &USBHAL::EP14_OUT_callback;
mjr 48:b225d025ca1d 349 epCallback[27] = &USBHAL::EP14_IN_callback;
mjr 48:b225d025ca1d 350 epCallback[28] = &USBHAL::EP15_OUT_callback;
mjr 48:b225d025ca1d 351 epCallback[29] = &USBHAL::EP15_IN_callback;
mbed_official 20:d38b72fed893 352
samux 8:335f2506f422 353 // choose usb src as PLL
samux 8:335f2506f422 354 SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);
mbed_official 20:d38b72fed893 355
samux 8:335f2506f422 356 // enable OTG clock
samux 8:335f2506f422 357 SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
samux 8:335f2506f422 358
mjr 37:c5ac4ccf6597 359 // Attach IRQ
mjr 37:c5ac4ccf6597 360 instance = this;
mjr 37:c5ac4ccf6597 361 NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
mjr 37:c5ac4ccf6597 362 NVIC_EnableIRQ(USB0_IRQn);
mjr 37:c5ac4ccf6597 363
samux 8:335f2506f422 364 // USB Module Configuration
samux 8:335f2506f422 365 // Reset USB Module
mjr 50:946bc763c068 366 resetSIE();
samux 8:335f2506f422 367 }
samux 8:335f2506f422 368
mjr 37:c5ac4ccf6597 369 USBHAL::~USBHAL(void)
mjr 48:b225d025ca1d 370 {
mjr 50:946bc763c068 371 // Free buffers
mjr 50:946bc763c068 372 for (int i = 0 ; i < NUMBER_OF_PHYSICAL_ENDPOINTS ; i++)
mjr 50:946bc763c068 373 {
mjr 50:946bc763c068 374 if (endpoint_buffer[i] != NULL)
mjr 50:946bc763c068 375 {
mjr 50:946bc763c068 376 delete [] endpoint_buffer[i];
mjr 50:946bc763c068 377 endpoint_buffer[i] = NULL;
mjr 50:946bc763c068 378 epMaxPacket[i] = 0;
mjr 50:946bc763c068 379 }
mjr 50:946bc763c068 380 }
mjr 37:c5ac4ccf6597 381 }
mjr 35:53e1a208f582 382
mjr 36:20bb47609697 383 void USBHAL::connect(void)
mjr 36:20bb47609697 384 {
mjr 35:53e1a208f582 385 // enable USB
mjr 35:53e1a208f582 386 USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
mjr 48:b225d025ca1d 387
mjr 35:53e1a208f582 388 // Pull up enable
mjr 35:53e1a208f582 389 USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
mjr 34:884405d998bb 390 }
samux 8:335f2506f422 391
mjr 35:53e1a208f582 392 void USBHAL::disconnect(void)
mjr 34:884405d998bb 393 {
samux 8:335f2506f422 394 // disable USB
samux 8:335f2506f422 395 USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
mjr 48:b225d025ca1d 396
samux 8:335f2506f422 397 // Pull up disable
samux 8:335f2506f422 398 USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
mjr 50:946bc763c068 399 }
bogdanm 14:d495202c90f4 400
mjr 50:946bc763c068 401 void USBHAL::hardReset(void)
mjr 50:946bc763c068 402 {
mjr 50:946bc763c068 403 // reset the SIE module
mjr 50:946bc763c068 404 resetSIE();
mjr 50:946bc763c068 405
mjr 50:946bc763c068 406 // do the internal reset work
mjr 50:946bc763c068 407 internalReset();
samux 8:335f2506f422 408 }
samux 8:335f2506f422 409
mjr 37:c5ac4ccf6597 410 void USBHAL::configureDevice(void)
mjr 37:c5ac4ccf6597 411 {
samux 8:335f2506f422 412 // not needed
samux 8:335f2506f422 413 }
samux 8:335f2506f422 414
mjr 37:c5ac4ccf6597 415 void USBHAL::unconfigureDevice(void)
mjr 37:c5ac4ccf6597 416 {
samux 8:335f2506f422 417 // not needed
samux 8:335f2506f422 418 }
samux 8:335f2506f422 419
mjr 37:c5ac4ccf6597 420 void USBHAL::setAddress(uint8_t address)
mjr 37:c5ac4ccf6597 421 {
samux 8:335f2506f422 422 // we don't set the address now otherwise the usb controller does not ack
samux 8:335f2506f422 423 // we set a flag instead
samux 8:335f2506f422 424 // see usbisr when an IN token is received
samux 8:335f2506f422 425 set_addr = 1;
samux 8:335f2506f422 426 addr = address;
mjr 35:53e1a208f582 427 }
samux 8:335f2506f422 428
mjr 35:53e1a208f582 429 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags)
mjr 34:884405d998bb 430 {
mjr 37:c5ac4ccf6597 431 // validate the endpoint number
mjr 34:884405d998bb 432 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS)
mjr 34:884405d998bb 433 return false;
samux 8:335f2506f422 434
mjr 37:c5ac4ccf6597 435 // get the logical endpoint
samux 8:335f2506f422 436 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 34:884405d998bb 437
mjr 48:b225d025ca1d 438 // Assume this is a bulk or interrupt endpoint. For these, the hardware maximum
mjr 48:b225d025ca1d 439 // packet size is 64 bytes, and we use packet handshaking.
mjr 48:b225d025ca1d 440 uint32_t hwMaxPacket = 64;
mjr 48:b225d025ca1d 441 uint32_t handshake_flag = USB_ENDPT_EPHSHK_MASK;
mjr 48:b225d025ca1d 442
mjr 48:b225d025ca1d 443 // If it's to be an isochronous endpoint, the hardware maximum packet size
mjr 48:b225d025ca1d 444 // increases to 1023 bytes, and we don't use handshaking.
mjr 48:b225d025ca1d 445 if (flags & ISOCHRONOUS)
mjr 48:b225d025ca1d 446 {
mjr 50:946bc763c068 447 hwMaxPacket = 1023;
mjr 48:b225d025ca1d 448 handshake_flag = 0;
mjr 48:b225d025ca1d 449 }
mjr 37:c5ac4ccf6597 450
mjr 48:b225d025ca1d 451 // limit the requested max packet size to the hardware limit
mjr 48:b225d025ca1d 452 if (maxPacket > hwMaxPacket)
mjr 48:b225d025ca1d 453 maxPacket = hwMaxPacket;
mjr 37:c5ac4ccf6597 454
mjr 37:c5ac4ccf6597 455 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 456 {
mjr 48:b225d025ca1d 457 // if the endpoint buffer hasn't been allocated yet or was previously
mjr 48:b225d025ca1d 458 // allocated at a smaller size, allocate a new buffer
mjr 37:c5ac4ccf6597 459 uint8_t *buf = endpoint_buffer[endpoint];
mjr 48:b225d025ca1d 460 if (buf == NULL || epMaxPacket[endpoint] < maxPacket)
mjr 37:c5ac4ccf6597 461 {
mjr 48:b225d025ca1d 462 // free any previous buffer
mjr 37:c5ac4ccf6597 463 if (buf != 0)
mjr 49:03527ce6840e 464 delete [] buf;
mjr 48:b225d025ca1d 465
mjr 48:b225d025ca1d 466 // allocate at the new size
mjr 49:03527ce6840e 467 endpoint_buffer[endpoint] = buf = new uint8_t[maxPacket];
mjr 48:b225d025ca1d 468
mjr 48:b225d025ca1d 469 // set the new max packet size
mjr 37:c5ac4ccf6597 470 epMaxPacket[endpoint] = maxPacket;
mjr 37:c5ac4ccf6597 471 }
mjr 37:c5ac4ccf6597 472
mjr 48:b225d025ca1d 473 // set the endpoint register flags and BDT entry
mjr 48:b225d025ca1d 474 if (IN_EP(endpoint))
mjr 48:b225d025ca1d 475 {
mjr 48:b225d025ca1d 476 // IN endpt -> device to host (TX)
mjr 48:b225d025ca1d 477 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | USB_ENDPT_EPTXEN_MASK; // en TX (IN) tran
mjr 48:b225d025ca1d 478 bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = (uint32_t) buf;
mjr 48:b225d025ca1d 479 bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = 0;
mjr 48:b225d025ca1d 480 }
mjr 48:b225d025ca1d 481 else
mjr 37:c5ac4ccf6597 482 {
mjr 48:b225d025ca1d 483 // OUT endpt -> host to device (RX)
mjr 48:b225d025ca1d 484 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | USB_ENDPT_EPRXEN_MASK; // en RX (OUT) tran.
mjr 48:b225d025ca1d 485 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].address = (uint32_t) buf;
mjr 48:b225d025ca1d 486 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address = 0;
mjr 48:b225d025ca1d 487
mjr 48:b225d025ca1d 488 // set up the first read
mjr 48:b225d025ca1d 489 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].byte_count = maxPacket;
mjr 48:b225d025ca1d 490 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 48:b225d025ca1d 491 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info = 0;
mjr 37:c5ac4ccf6597 492 }
mjr 37:c5ac4ccf6597 493
mjr 37:c5ac4ccf6597 494 // Set DATA1 on the endpoint. For RX endpoints, we just queued up our first
mjr 37:c5ac4ccf6597 495 // read, which will always be a DATA0 packet, so the next read will use DATA1.
mjr 37:c5ac4ccf6597 496 // For TX endpoints, we always flip the bit *before* sending the packet, so
mjr 37:c5ac4ccf6597 497 // (counterintuitively) we need to set the DATA1 bit now to send DATA0 in the
mjr 37:c5ac4ccf6597 498 // next packet. So in either case, we want DATA1 initially.
mjr 37:c5ac4ccf6597 499 Data1 |= (1 << endpoint);
mjr 50:946bc763c068 500
mjr 50:946bc763c068 501 // mark the endpoint as realised
mjr 50:946bc763c068 502 epRealised |= (1 << endpoint);
mjr 35:53e1a208f582 503 }
mjr 37:c5ac4ccf6597 504 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 505
mjr 36:20bb47609697 506 // success
samux 8:335f2506f422 507 return true;
samux 8:335f2506f422 508 }
samux 8:335f2506f422 509
mjr 34:884405d998bb 510 // read setup packet
mjr 36:20bb47609697 511 void USBHAL::EP0setup(uint8_t *buffer)
mjr 36:20bb47609697 512 {
mjr 34:884405d998bb 513 uint32_t sz;
mjr 37:c5ac4ccf6597 514 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 515 }
mjr 34:884405d998bb 516
mjr 50:946bc763c068 517 // Start reading the data stage of a SETUP transaction on EP0
mjr 36:20bb47609697 518 void USBHAL::EP0readStage(void)
mjr 36:20bb47609697 519 {
mjr 37:c5ac4ccf6597 520 if (!(bdt[0].info & BD_OWN_MASK))
mjr 37:c5ac4ccf6597 521 {
mjr 37:c5ac4ccf6597 522 Data1 &= ~1UL; // set DATA0
mjr 37:c5ac4ccf6597 523 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 37:c5ac4ccf6597 524 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
mjr 36:20bb47609697 525 }
mjr 34:884405d998bb 526 }
mjr 34:884405d998bb 527
mjr 50:946bc763c068 528 // Read an OUT packet on EP0
mjr 36:20bb47609697 529 void USBHAL::EP0read(void)
mjr 36:20bb47609697 530 {
mjr 38:072e12583e73 531 if (!(bdt[0].info & BD_OWN_MASK))
mjr 50:946bc763c068 532 {
mjr 50:946bc763c068 533 Data1 &= ~1UL;
mjr 37:c5ac4ccf6597 534 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 50:946bc763c068 535 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
mjr 50:946bc763c068 536 }
mjr 34:884405d998bb 537 }
mjr 34:884405d998bb 538
mjr 36:20bb47609697 539 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer)
mjr 36:20bb47609697 540 {
mjr 35:53e1a208f582 541 uint32_t sz;
mjr 50:946bc763c068 542 if (endpointReadResult(EP0OUT, buffer, &sz) == EP_COMPLETED) {
mjr 50:946bc763c068 543 return sz;
mjr 50:946bc763c068 544 }
mjr 50:946bc763c068 545 else {
mjr 50:946bc763c068 546 return 0;
mjr 50:946bc763c068 547 }
mjr 34:884405d998bb 548 }
mjr 34:884405d998bb 549
mjr 50:946bc763c068 550 void USBHAL::EP0write(const volatile uint8_t *buffer, uint32_t size)
mjr 37:c5ac4ccf6597 551 {
mjr 37:c5ac4ccf6597 552 endpointWrite(EP0IN, buffer, size);
mjr 34:884405d998bb 553 }
mjr 34:884405d998bb 554
mjr 37:c5ac4ccf6597 555 void USBHAL::EP0getWriteResult(void)
mjr 37:c5ac4ccf6597 556 {
mjr 34:884405d998bb 557 }
mjr 34:884405d998bb 558
mjr 36:20bb47609697 559 void USBHAL::EP0stall(void)
mjr 36:20bb47609697 560 {
mjr 50:946bc763c068 561 // $$$ stallEndpoint(EP0OUT);
mjr 34:884405d998bb 562 }
mjr 34:884405d998bb 563
mjr 35:53e1a208f582 564 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize)
mjr 34:884405d998bb 565 {
mjr 37:c5ac4ccf6597 566 // We always start a new read when we fetch the result of the
mjr 37:c5ac4ccf6597 567 // previous read, so we don't have to do anything here. Simply
mjr 37:c5ac4ccf6597 568 // indicate that the read is pending so that the caller can proceed
mjr 37:c5ac4ccf6597 569 // to check the results.
mjr 34:884405d998bb 570 return EP_PENDING;
mjr 34:884405d998bb 571 }
mjr 34:884405d998bb 572
mjr 36:20bb47609697 573 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t *buffer, uint32_t *bytesRead)
mjr 34:884405d998bb 574 {
mjr 50:946bc763c068 575 // validate the endpoint number and direction, and make sure it's realised
mjr 37:c5ac4ccf6597 576 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !OUT_EP(endpoint))
mjr 34:884405d998bb 577 return EP_INVALID;
mjr 34:884405d998bb 578
mjr 37:c5ac4ccf6597 579 // get the logical endpoint
mjr 34:884405d998bb 580 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 37:c5ac4ccf6597 581
mjr 37:c5ac4ccf6597 582 // get the mode - it's isochronous if it doesn't have the handshake flag
mjr 48:b225d025ca1d 583 bool iso = (USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK) == 0;
mjr 37:c5ac4ccf6597 584
mjr 35:53e1a208f582 585 // get the BDT index
mjr 35:53e1a208f582 586 int idx = EP_BDT_IDX(log_endpoint, RX, 0);
mjr 37:c5ac4ccf6597 587
mjr 48:b225d025ca1d 588 // Check to see if the endpoint is ready to read
mjr 37:c5ac4ccf6597 589 if (log_endpoint == 0)
mjr 37:c5ac4ccf6597 590 {
mjr 37:c5ac4ccf6597 591 // control endpoint - just make sure we own the BDT
mjr 37:c5ac4ccf6597 592 if (bdt[idx].info & BD_OWN_MASK)
mjr 37:c5ac4ccf6597 593 return EP_PENDING;
mjr 37:c5ac4ccf6597 594 }
mjr 48:b225d025ca1d 595 else
mjr 48:b225d025ca1d 596 {
mjr 48:b225d025ca1d 597 // If it's not isochronous, check to see if we've received data, and
mjr 48:b225d025ca1d 598 // return PENDING if not. Isochronous endpoints don't use the TOKNE
mjr 48:b225d025ca1d 599 // interrupt (they use SOF instead), so the 'complete' flag doesn't
mjr 48:b225d025ca1d 600 // apply if it's an iso endpoint.
mjr 48:b225d025ca1d 601 if (!iso && !(epComplete & EP(endpoint)))
mjr 48:b225d025ca1d 602 return EP_PENDING;
mjr 48:b225d025ca1d 603 }
mjr 50:946bc763c068 604
mjr 50:946bc763c068 605 EP_STATUS result = EP_INVALID;
mjr 37:c5ac4ccf6597 606 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 607 {
mjr 50:946bc763c068 608 // proceed only if the endpoint has been realised
mjr 50:946bc763c068 609 if (epRealised & EP(endpoint))
mjr 50:946bc763c068 610 {
mjr 50:946bc763c068 611 // note if we have a SETUP token
mjr 50:946bc763c068 612 bool setup = (log_endpoint == 0 && TOK_PID(idx) == SETUP_TOKEN);
mjr 50:946bc763c068 613
mjr 50:946bc763c068 614 // get the received data buffer and size
mjr 50:946bc763c068 615 uint8_t *ep_buf = endpoint_buffer[endpoint];
mjr 50:946bc763c068 616 uint32_t sz = bdt[idx].byte_count;
mjr 48:b225d025ca1d 617
mjr 50:946bc763c068 618 // copy the data from the hardware receive buffer to the caller's buffer
mjr 50:946bc763c068 619 *bytesRead = sz;
mjr 50:946bc763c068 620 for (uint32_t n = 0 ; n < sz ; n++)
mjr 50:946bc763c068 621 buffer[n] = ep_buf[n];
mjr 50:946bc763c068 622
mjr 50:946bc763c068 623 // Figure the DATA0/DATA1 bit for the next packet received on this
mjr 50:946bc763c068 624 // endpoint. The bit normally toggles on each packet, but it's
mjr 50:946bc763c068 625 // special for SETUP packets on endpoint 0. The next OUT packet
mjr 50:946bc763c068 626 // after a SETUP packet with no data stage is always DATA0, even
mjr 50:946bc763c068 627 // if the SETUP packet was also DATA0.
mjr 50:946bc763c068 628 if (setup && (sz >= 7 && buffer[6] == 0)) {
mjr 50:946bc763c068 629 // SETUP with no data stage -> next packet is always DATA0
mjr 50:946bc763c068 630 Data1 &= ~1UL;
mjr 50:946bc763c068 631 }
mjr 50:946bc763c068 632 else {
mjr 50:946bc763c068 633 // otherwise just toggle the last bit (assuming it matches our
mjr 50:946bc763c068 634 // internal state - if not, we must be out of sync, so presumably
mjr 50:946bc763c068 635 // *not* toggling our state will get us back in sync)
mjr 50:946bc763c068 636 if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1))
mjr 50:946bc763c068 637 Data1 ^= (1 << endpoint);
mjr 50:946bc763c068 638 }
mjr 50:946bc763c068 639
mjr 50:946bc763c068 640 // set up the BDT entry to receive the next packet, and hand it to the SIE
mjr 50:946bc763c068 641 bdt[idx].byte_count = epMaxPacket[endpoint];
mjr 50:946bc763c068 642 bdt[idx].info = BD_DTS_MASK | BD_OWN_MASK | (((Data1 >> endpoint) & 1) << 6);
mjr 50:946bc763c068 643
mjr 50:946bc763c068 644 // clear the SUSPEND TOKEN BUSY flag to allow token processing to continue
mjr 50:946bc763c068 645 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 646
mjr 50:946bc763c068 647 // clear the 'completed' flag - we're now awaiting the next packet
mjr 50:946bc763c068 648 epComplete &= ~EP(endpoint);
mjr 50:946bc763c068 649
mjr 50:946bc763c068 650 // the read is now complete
mjr 50:946bc763c068 651 result = EP_COMPLETED;
mjr 35:53e1a208f582 652 }
mjr 37:c5ac4ccf6597 653 }
mjr 37:c5ac4ccf6597 654 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 655
mjr 50:946bc763c068 656 return result;
samux 8:335f2506f422 657 }
samux 8:335f2506f422 658
mjr 50:946bc763c068 659 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const volatile uint8_t *data, uint32_t size)
mjr 34:884405d998bb 660 {
mjr 37:c5ac4ccf6597 661 // validate the endpoint number and direction
mjr 37:c5ac4ccf6597 662 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !IN_EP(endpoint))
samux 8:335f2506f422 663 return EP_INVALID;
mjr 35:53e1a208f582 664
mjr 37:c5ac4ccf6597 665 // get the BDT index
mjr 35:53e1a208f582 666 int idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
mjr 37:c5ac4ccf6597 667
mjr 50:946bc763c068 668 EP_STATUS result = EP_INVALID;
mjr 37:c5ac4ccf6597 669 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 670 {
mjr 50:946bc763c068 671 // proceed only if the endpoint has been realised and we own the BDT
mjr 50:946bc763c068 672 if ((epRealised & EP(endpoint)) && !(bdt[idx].info & BD_OWN_MASK))
mjr 50:946bc763c068 673 {
mjr 50:946bc763c068 674 // get the endpoint buffer
mjr 50:946bc763c068 675 uint8_t *ep_buf = endpoint_buffer[endpoint];
mjr 50:946bc763c068 676
mjr 50:946bc763c068 677 // copy the data to the hardware buffer
mjr 50:946bc763c068 678 bdt[idx].byte_count = size;
mjr 50:946bc763c068 679 for (uint32_t n = 0 ; n < size ; n++)
mjr 50:946bc763c068 680 ep_buf[n] = data[n];
mjr 50:946bc763c068 681
mjr 50:946bc763c068 682 // toggle DATA0/DATA1 before sending
mjr 50:946bc763c068 683 Data1 ^= (1 << endpoint);
mjr 48:b225d025ca1d 684
mjr 50:946bc763c068 685 // hand the BDT to the SIE to do the send
mjr 50:946bc763c068 686 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | (((Data1 >> endpoint) & 1) << 6);
mjr 35:53e1a208f582 687
mjr 50:946bc763c068 688 // write is now pending in the hardware
mjr 50:946bc763c068 689 result = EP_PENDING;
mjr 50:946bc763c068 690 }
mjr 37:c5ac4ccf6597 691 }
mjr 37:c5ac4ccf6597 692 EXIT_CRITICAL_SECTION
mjr 48:b225d025ca1d 693
mjr 50:946bc763c068 694 return result;
samux 8:335f2506f422 695 }
samux 8:335f2506f422 696
mjr 35:53e1a208f582 697 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint)
mjr 34:884405d998bb 698 {
mjr 48:b225d025ca1d 699 // assume write is still pending
mjr 37:c5ac4ccf6597 700 EP_STATUS result = EP_PENDING;
mjr 48:b225d025ca1d 701
mjr 37:c5ac4ccf6597 702 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 703 {
mjr 50:946bc763c068 704 // If the endpoint isn't realised, the result is 'invalid'. Otherwise,
mjr 50:946bc763c068 705 // check the 'completed' flag: if set, the write is completed.
mjr 50:946bc763c068 706 if (!(epRealised & EP(endpoint)))
mjr 50:946bc763c068 707 {
mjr 50:946bc763c068 708 // endpoint isn't realised - can't read it
mjr 50:946bc763c068 709 result = EP_INVALID;
mjr 50:946bc763c068 710 }
mjr 50:946bc763c068 711 else if (epComplete & EP(endpoint))
mjr 48:b225d025ca1d 712 {
mjr 48:b225d025ca1d 713 // the result is COMPLETED
mjr 48:b225d025ca1d 714 result = EP_COMPLETED;
mjr 48:b225d025ca1d 715
mjr 48:b225d025ca1d 716 // clear the 'completed' flag - this is consumed by fetching the result
mjr 37:c5ac4ccf6597 717 epComplete &= ~EP(endpoint);
mjr 37:c5ac4ccf6597 718 }
samux 8:335f2506f422 719 }
mjr 37:c5ac4ccf6597 720 EXIT_CRITICAL_SECTION
mjr 36:20bb47609697 721
mjr 48:b225d025ca1d 722 // return the result
mjr 37:c5ac4ccf6597 723 return result;
samux 8:335f2506f422 724 }
samux 8:335f2506f422 725
mjr 36:20bb47609697 726 void USBHAL::stallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 727 {
mjr 50:946bc763c068 728 ENTER_CRITICAL_SECTION
mjr 50:946bc763c068 729 {
mjr 50:946bc763c068 730 if (epRealised & EP(endpoint))
mjr 50:946bc763c068 731 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
mjr 50:946bc763c068 732 }
mjr 50:946bc763c068 733 EXIT_CRITICAL_SECTION
samux 8:335f2506f422 734 }
samux 8:335f2506f422 735
mjr 36:20bb47609697 736 void USBHAL::unstallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 737 {
mjr 37:c5ac4ccf6597 738 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 739 {
mjr 50:946bc763c068 740 if (epRealised & EP(endpoint))
mjr 48:b225d025ca1d 741 {
mjr 50:946bc763c068 742 // clear the stall bit in the endpoint register
mjr 50:946bc763c068 743 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
mjr 50:946bc763c068 744
mjr 50:946bc763c068 745 // take ownership of the BDT entry
mjr 50:946bc763c068 746 int idx = PEP_BDT_IDX(endpoint, 0);
mjr 50:946bc763c068 747 bdt[idx].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 50:946bc763c068 748
mjr 50:946bc763c068 749 // if this is an RX endpoint, start a new read
mjr 50:946bc763c068 750 if (OUT_EP(endpoint))
mjr 50:946bc763c068 751 {
mjr 50:946bc763c068 752 bdt[idx].byte_count = epMaxPacket[endpoint];
mjr 50:946bc763c068 753 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 50:946bc763c068 754 }
mjr 50:946bc763c068 755
mjr 50:946bc763c068 756 // Reset Data1 for the endpoint - we need to set the bit to 1 for
mjr 50:946bc763c068 757 // either TX or RX, by the same logic as in realiseEndpoint()
mjr 50:946bc763c068 758 Data1 |= (1 << endpoint);
mjr 50:946bc763c068 759
mjr 50:946bc763c068 760 // clear the 'completed' bit for the endpoint
mjr 50:946bc763c068 761 epComplete &= ~(1 << endpoint);
mjr 48:b225d025ca1d 762 }
mjr 36:20bb47609697 763 }
mjr 37:c5ac4ccf6597 764 EXIT_CRITICAL_SECTION
samux 8:335f2506f422 765 }
samux 8:335f2506f422 766
mjr 50:946bc763c068 767 void USBHAL_KL25Z_unstall_EP0(bool force)
mjr 37:c5ac4ccf6597 768 {
mjr 50:946bc763c068 769 ENTER_CRITICAL_SECTION
mjr 39:d684a8ce5d88 770 {
mjr 50:946bc763c068 771 if (force || (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK))
mjr 37:c5ac4ccf6597 772 {
mjr 48:b225d025ca1d 773 // clear the stall bit in the endpoint register
mjr 48:b225d025ca1d 774 USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
mjr 48:b225d025ca1d 775
mjr 48:b225d025ca1d 776 // take ownership of the RX and TX BDTs
mjr 48:b225d025ca1d 777 bdt[EP_BDT_IDX(0, RX, EVEN)].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 48:b225d025ca1d 778 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 48:b225d025ca1d 779 bdt[EP_BDT_IDX(0, RX, EVEN)].byte_count = MAX_PACKET_SIZE_EP0;
mjr 48:b225d025ca1d 780 bdt[EP_BDT_IDX(0, TX, EVEN)].byte_count = MAX_PACKET_SIZE_EP0;
mjr 48:b225d025ca1d 781
mjr 48:b225d025ca1d 782 // start a new read on EP0OUT
mjr 48:b225d025ca1d 783 bdt[EP_BDT_IDX(0, RX, EVEN)].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 48:b225d025ca1d 784
mjr 48:b225d025ca1d 785 // reset the DATA0/1 bit to 1 on EP0IN and EP0OUT, by the same
mjr 48:b225d025ca1d 786 // logic as in realiseEndpoint()
mjr 48:b225d025ca1d 787 Data1 |= 0x03;
mjr 36:20bb47609697 788 }
mjr 50:946bc763c068 789 }
mjr 50:946bc763c068 790 EXIT_CRITICAL_SECTION
mjr 50:946bc763c068 791 }
mjr 50:946bc763c068 792
mjr 50:946bc763c068 793 bool USBHAL::getEndpointStallState(uint8_t endpoint)
mjr 50:946bc763c068 794 {
mjr 50:946bc763c068 795 uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
mjr 50:946bc763c068 796 return (stall) ? true : false;
mjr 50:946bc763c068 797 }
mjr 50:946bc763c068 798
mjr 50:946bc763c068 799 void USBHAL::remoteWakeup(void)
mjr 50:946bc763c068 800 {
mjr 50:946bc763c068 801 // [TODO]
mjr 50:946bc763c068 802 }
mjr 50:946bc763c068 803
mjr 50:946bc763c068 804 // Internal reset handler. Called when we get a Bus Reset signal
mjr 50:946bc763c068 805 // from the host, and when we initiate a reset of the SIE hardware
mjr 50:946bc763c068 806 // from the device side.
mjr 50:946bc763c068 807 void USBHAL::internalReset(void)
mjr 50:946bc763c068 808 {
mjr 50:946bc763c068 809 ENTER_CRITICAL_SECTION
mjr 50:946bc763c068 810 {
mjr 50:946bc763c068 811 int i;
mjr 37:c5ac4ccf6597 812
mjr 50:946bc763c068 813 // set the default bus address
mjr 50:946bc763c068 814 USB0->ADDR = 0x00;
mjr 50:946bc763c068 815 addr = 0;
mjr 50:946bc763c068 816 set_addr = 0;
mjr 50:946bc763c068 817
mjr 50:946bc763c068 818 // disable all endpoints
mjr 50:946bc763c068 819 epRealised = 0x00;
mjr 50:946bc763c068 820 for (i = 0 ; i < 16 ; i++)
mjr 50:946bc763c068 821 USB0->ENDPOINT[i].ENDPT = 0x00;
mjr 50:946bc763c068 822
mjr 50:946bc763c068 823 // take control of all BDTs away from the SIE
mjr 50:946bc763c068 824 for (i = 0 ; i < sizeof(bdt)/sizeof(bdt[0]) ; ++i)
mjr 50:946bc763c068 825 {
mjr 50:946bc763c068 826 bdt[i].info = 0;
mjr 50:946bc763c068 827 bdt[i].byte_count = 0;
mjr 50:946bc763c068 828 }
mjr 50:946bc763c068 829
mjr 50:946bc763c068 830 // reset DATA0/1 state
mjr 50:946bc763c068 831 Data1 = 0x55555555;
mjr 50:946bc763c068 832
mjr 50:946bc763c068 833 // reset endpoint completion status
mjr 50:946bc763c068 834 epComplete = 0;
mjr 50:946bc763c068 835
mjr 50:946bc763c068 836 // reset EVEN/ODD state (and keep it permanently on EVEN -
mjr 50:946bc763c068 837 // this disables the hardware double-buffering system)
mjr 50:946bc763c068 838 USB0->CTL |= USB_CTL_ODDRST_MASK;
mjr 50:946bc763c068 839
mjr 50:946bc763c068 840 // reset error status and enable all error interrupts
mjr 50:946bc763c068 841 USB0->ERRSTAT = 0xFF;
mjr 50:946bc763c068 842 USB0->ERREN = 0xFF;
mjr 50:946bc763c068 843
mjr 50:946bc763c068 844 // enable our standard complement of interrupts
mjr 50:946bc763c068 845 USB0->INTEN = BUS_RESET_INTERRUPTS;
mjr 50:946bc763c068 846
mjr 50:946bc763c068 847 // we're not suspended
mjr 50:946bc763c068 848 suspendStateChanged(0);
mjr 50:946bc763c068 849
mjr 50:946bc763c068 850 // we're not sleeping
mjr 50:946bc763c068 851 sleepStateChanged(0);
mjr 50:946bc763c068 852
mjr 50:946bc763c068 853 // notify upper layers of the bus reset, to reset the protocol state
mjr 50:946bc763c068 854 busReset();
mjr 50:946bc763c068 855
mjr 50:946bc763c068 856 // realise the control endpoint (EP0) in both directions
mjr 50:946bc763c068 857 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
mjr 50:946bc763c068 858 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
mjr 50:946bc763c068 859 }
mjr 50:946bc763c068 860 EXIT_CRITICAL_SECTION
mjr 50:946bc763c068 861 }
mjr 50:946bc763c068 862
mjr 50:946bc763c068 863 void USBHAL::_usbisr(void)
mjr 50:946bc763c068 864 {
mjr 50:946bc763c068 865 inIRQ = true;
mjr 50:946bc763c068 866 instance->usbisr();
mjr 50:946bc763c068 867 inIRQ = false;
mjr 50:946bc763c068 868 }
mjr 50:946bc763c068 869
mjr 50:946bc763c068 870 void USBHAL::usbisr(void)
mjr 50:946bc763c068 871 {
mjr 50:946bc763c068 872 // get the interrupt status - this tells us which event(s)
mjr 50:946bc763c068 873 // triggered this interrupt
mjr 50:946bc763c068 874 uint8_t istat = USB0->ISTAT;
mjr 50:946bc763c068 875
mjr 50:946bc763c068 876 // reset interrupt
mjr 50:946bc763c068 877 if (istat & USB_ISTAT_USBRST_MASK)
mjr 50:946bc763c068 878 {
mjr 50:946bc763c068 879 // do the internal reset work
mjr 50:946bc763c068 880 internalReset();
mjr 50:946bc763c068 881
mjr 50:946bc763c068 882 // resume token processing if it was suspended
mjr 36:20bb47609697 883 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 48:b225d025ca1d 884
mjr 50:946bc763c068 885 // clear the interrupt status
mjr 50:946bc763c068 886 USB0->ISTAT = USB_ISTAT_USBRST_MASK;
mjr 50:946bc763c068 887
mjr 50:946bc763c068 888 // return immediately, ignoring any other status flags
mjr 50:946bc763c068 889 return;
samux 8:335f2506f422 890 }
mjr 50:946bc763c068 891
mjr 37:c5ac4ccf6597 892 // token interrupt
mjr 37:c5ac4ccf6597 893 if (istat & USB_ISTAT_TOKDNE_MASK)
mjr 36:20bb47609697 894 {
mjr 48:b225d025ca1d 895 // get the endpoint information from the status register
mjr 50:946bc763c068 896 uint32_t stat = USB0->STAT;
mjr 50:946bc763c068 897 uint32_t num = (stat >> 4) & 0x0F;
mjr 50:946bc763c068 898 uint32_t dir = (stat >> 3) & 0x01;
mjr 34:884405d998bb 899 int endpoint = (num << 1) | dir;
mjr 50:946bc763c068 900 uint32_t ev_odd = (stat >> 2) & 0x01;
mjr 50:946bc763c068 901
mjr 48:b225d025ca1d 902 // check which endpoint we're working with
mjr 48:b225d025ca1d 903 if (num == 0)
mjr 48:b225d025ca1d 904 {
mjr 48:b225d025ca1d 905 // Endpoint 0 requires special handling
mjr 48:b225d025ca1d 906 uint32_t idx = EP_BDT_IDX(num, dir, ev_odd);
mjr 48:b225d025ca1d 907 int pid = TOK_PID(idx);
mjr 48:b225d025ca1d 908 if (pid == SETUP_TOKEN)
mjr 48:b225d025ca1d 909 {
mjr 48:b225d025ca1d 910 // SETUP packet - next IN (TX) packet must be DATA1 (confusingly,
mjr 48:b225d025ca1d 911 // this means we must clear the Data1 bit, since we flip the bit
mjr 48:b225d025ca1d 912 // before each send)
mjr 48:b225d025ca1d 913 Data1 &= ~0x02;
mjr 48:b225d025ca1d 914
mjr 50:946bc763c068 915 // Forcibly take ownership of the EP0IN BDT in case we have
mjr 50:946bc763c068 916 // unfinished previous transmissions. The protocol state machine
mjr 48:b225d025ca1d 917 // assumes that we don't, so it's probably an error if this code
mjr 50:946bc763c068 918 // actually does anything, but just in case...
mjr 48:b225d025ca1d 919 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 920
mjr 48:b225d025ca1d 921 // handle the EP0 SETUP event in the generic protocol layer
mjr 48:b225d025ca1d 922 EP0setupCallback();
mjr 48:b225d025ca1d 923 }
mjr 48:b225d025ca1d 924 else if (pid == OUT_TOKEN)
mjr 48:b225d025ca1d 925 {
mjr 48:b225d025ca1d 926 // OUT packet on EP0
mjr 48:b225d025ca1d 927 EP0out();
mjr 48:b225d025ca1d 928 }
mjr 48:b225d025ca1d 929 else if (pid == IN_TOKEN)
mjr 48:b225d025ca1d 930 {
mjr 48:b225d025ca1d 931 // IN packet on EP0
mjr 48:b225d025ca1d 932 EP0in();
mjr 48:b225d025ca1d 933
mjr 48:b225d025ca1d 934 // Special case: if the 'set address' flag is set, it means that the
mjr 48:b225d025ca1d 935 // host just sent us our bus address. We must put this into effect
mjr 50:946bc763c068 936 // in the hardware SIE immediately after sending the reply. We just
mjr 50:946bc763c068 937 // did that above, so this is the time.
mjr 50:946bc763c068 938 if (set_addr) {
mjr 48:b225d025ca1d 939 USB0->ADDR = addr & 0x7F;
mjr 48:b225d025ca1d 940 set_addr = 0;
mjr 48:b225d025ca1d 941 }
mjr 48:b225d025ca1d 942 }
mjr 48:b225d025ca1d 943 }
mjr 48:b225d025ca1d 944 else
mjr 48:b225d025ca1d 945 {
mjr 48:b225d025ca1d 946 // For all other endpoints, note the read/write completion in the flags
mjr 48:b225d025ca1d 947 epComplete |= EP(endpoint);
mjr 48:b225d025ca1d 948
mjr 48:b225d025ca1d 949 // call the endpoint token callback; if that handles the token, it consumes
mjr 48:b225d025ca1d 950 // the 'completed' status, so clear that flag again
mjr 48:b225d025ca1d 951 if ((instance->*(epCallback[endpoint - 2]))()) {
mjr 48:b225d025ca1d 952 epComplete &= ~EP(endpoint);
mjr 48:b225d025ca1d 953 }
mjr 48:b225d025ca1d 954 }
mjr 37:c5ac4ccf6597 955
mjr 50:946bc763c068 956 // resume token processing if suspended
mjr 50:946bc763c068 957 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 958
mjr 48:b225d025ca1d 959 // clear the TOKDNE interrupt status bit
samux 8:335f2506f422 960 USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
mjr 50:946bc763c068 961 return;
mjr 50:946bc763c068 962 }
mjr 50:946bc763c068 963
mjr 50:946bc763c068 964 // SOF interrupt
mjr 50:946bc763c068 965 if (istat & USB_ISTAT_SOFTOK_MASK)
mjr 50:946bc763c068 966 {
mjr 50:946bc763c068 967 // Read frame number and signal the SOF event to the callback
mjr 50:946bc763c068 968 SOF(frameNumber());
mjr 50:946bc763c068 969 USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
mjr 50:946bc763c068 970 }
mjr 50:946bc763c068 971
mjr 50:946bc763c068 972 // stall interrupt
mjr 50:946bc763c068 973 if (istat & USB_ISTAT_STALL_MASK)
mjr 50:946bc763c068 974 {
mjr 50:946bc763c068 975 // if the control endpoint (EP 0) is stalled, unstall it
mjr 50:946bc763c068 976 USBHAL_KL25Z_unstall_EP0(false);
mjr 50:946bc763c068 977
mjr 50:946bc763c068 978 // clear the busy-suspend bit to resume token processing
mjr 50:946bc763c068 979 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 980
mjr 50:946bc763c068 981 // clear the interrupt status bit for STALL
mjr 50:946bc763c068 982 USB0->ISTAT = USB_ISTAT_STALL_MASK;
samux 8:335f2506f422 983 }
mbed_official 20:d38b72fed893 984
mjr 50:946bc763c068 985 // Sleep interrupt. This indicates that the USB bus has been
mjr 50:946bc763c068 986 // idle for at least 3ms (no frames transacted). This has
mjr 50:946bc763c068 987 // several possible causes:
mjr 50:946bc763c068 988 //
mjr 50:946bc763c068 989 // - The USB cable was unplugged
mjr 50:946bc763c068 990 // - The host was powered off
mjr 50:946bc763c068 991 // - The host has stopped communicating due to a software fault
mjr 50:946bc763c068 992 // - The host has stopped communicating deliberately (e.g., due
mjr 50:946bc763c068 993 // to user action, or due to a protocol error)
mjr 50:946bc763c068 994 //
mjr 50:946bc763c068 995 // A "sleep" event on the SIE is not to be confused with the
mjr 50:946bc763c068 996 // sleep/suspend power state on the PC. The sleep event here
mjr 50:946bc763c068 997 // simply means that the SIE isn't seeing token traffic on the
mjr 50:946bc763c068 998 // required schedule.
mjr 50:946bc763c068 999 //
mjr 50:946bc763c068 1000 // Note that the sleep event is the closest thing the KL25Z USB
mjr 50:946bc763c068 1001 // module has to a disconnect event. There's no way to detect
mjr 50:946bc763c068 1002 // if we're physically connected to a host, so all we can really
mjr 50:946bc763c068 1003 // know is that we're not transacting tokens. USB requires token
mjr 50:946bc763c068 1004 // exchange every 1ms, so if there's no token exchange for a few
mjr 50:946bc763c068 1005 // milliseconds, the connection must be broken at some level.
mjr 36:20bb47609697 1006 if (istat & USB_ISTAT_SLEEP_MASK)
mjr 36:20bb47609697 1007 {
mjr 50:946bc763c068 1008 // tell the upper layers about the change
mjr 50:946bc763c068 1009 sleepStateChanged(1);
mjr 50:946bc763c068 1010
mjr 50:946bc763c068 1011 // $$$ cycle the connection to trigger a host retry if this
mjr 50:946bc763c068 1012 // is during the initial connection
mjr 50:946bc763c068 1013 if (USB0->ADDR != 0x00)
mjr 50:946bc763c068 1014 {
mjr 50:946bc763c068 1015 static Timeout to;
mjr 50:946bc763c068 1016 disconnect();
mjr 50:946bc763c068 1017 to.attach_us(this, &USBHAL::connect, 5000);
mjr 50:946bc763c068 1018 }
mjr 50:946bc763c068 1019
mjr 50:946bc763c068 1020 // resume token processing
mjr 50:946bc763c068 1021 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 50:946bc763c068 1022
mjr 50:946bc763c068 1023 // reset the interrupt bit
mjr 40:cd877d5c09ea 1024 USB0->ISTAT = USB_ISTAT_SLEEP_MASK;
mbed_official 20:d38b72fed893 1025 }
samux 8:335f2506f422 1026
mjr 50:946bc763c068 1027 // Resume from suspend mode.
mjr 50:946bc763c068 1028 //
mjr 50:946bc763c068 1029 // NB: Don't confuse "suspend" with "sleep". Suspend mode refers
mjr 50:946bc763c068 1030 // to a hardware low-power mode initiated by the device. "Sleep"
mjr 50:946bc763c068 1031 // means only that the USB connection has been idle (no tokens
mjr 50:946bc763c068 1032 // transacted) for more than 3ms. A sleep signal means that the
mjr 50:946bc763c068 1033 // connection with the host was broken, either physically or
mjr 50:946bc763c068 1034 // logically; it doesn't of itself have anything to do with suspend
mjr 50:946bc763c068 1035 // mode, and in particular it doesn't mean that the host has
mjr 50:946bc763c068 1036 // commanded us to enter suspend mode or told us that the host
mjr 50:946bc763c068 1037 // is entering a low-power state. The higher-level device
mjr 50:946bc763c068 1038 // implementation might choose to enter suspend mode on the device
mjr 50:946bc763c068 1039 // in response to a lost connection, but the USB/HAL layers don't
mjr 50:946bc763c068 1040 // take any such action on their own. Note that suspend mode can
mjr 50:946bc763c068 1041 // only end with explicit intervention by the host, in the form of
mjr 50:946bc763c068 1042 // a USB RESUME signal, so the host has to be aware that we're
mjr 50:946bc763c068 1043 // doing this sort of power management.
mjr 50:946bc763c068 1044 if (istat & USB_ISTAT_RESUME_MASK)
mjr 50:946bc763c068 1045 {
mjr 50:946bc763c068 1046 // note the change
mjr 50:946bc763c068 1047 suspendStateChanged(0);
mjr 50:946bc763c068 1048
mjr 50:946bc763c068 1049 // remove suspend mode flags
mjr 50:946bc763c068 1050 USB0->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
mjr 50:946bc763c068 1051 USB0->USBTRC0 &= ~USB_USBTRC0_USBRESMEN_MASK;
mjr 50:946bc763c068 1052 USB0->INTEN &= ~USB_INTEN_RESUMEEN_MASK;
mjr 50:946bc763c068 1053
mjr 50:946bc763c068 1054 // clear the interrupt status
mjr 50:946bc763c068 1055 USB0->ISTAT = USB_ISTAT_RESUME_MASK;
mjr 50:946bc763c068 1056 }
mjr 50:946bc763c068 1057
samux 8:335f2506f422 1058 // error interrupt
mjr 36:20bb47609697 1059 if (istat & USB_ISTAT_ERROR_MASK)
mjr 36:20bb47609697 1060 {
mjr 37:c5ac4ccf6597 1061 // reset all error status bits, and clear the SUSPEND flag to allow
mjr 37:c5ac4ccf6597 1062 // token processing to continue
mjr 37:c5ac4ccf6597 1063 USB0->ERRSTAT = 0xFF;
mjr 36:20bb47609697 1064 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 40:cd877d5c09ea 1065 USB0->ISTAT = USB_ISTAT_ERROR_MASK;
samux 8:335f2506f422 1066 }
samux 8:335f2506f422 1067 }
samux 8:335f2506f422 1068
mjr 36:20bb47609697 1069 #endif