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 Feb 26 18:41:47 2016 +0000
Revision:
49:03527ce6840e
Parent:
48:b225d025ca1d
Child:
50:946bc763c068
const-ify all descriptors to reduce RAM footprint; use new/delete instead of malloc/free to facilitate replacing global allocator

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 37:c5ac4ccf6597 21 //#define DEBUG
mjr 37:c5ac4ccf6597 22 #ifdef DEBUG
mjr 37:c5ac4ccf6597 23 #define printd(fmt, ...) printf(fmt, __VA_ARGS__)
mjr 37:c5ac4ccf6597 24 #else
mjr 37:c5ac4ccf6597 25 #define printd(fmt, ...)
mjr 37:c5ac4ccf6597 26 #endif
mjr 37:c5ac4ccf6597 27
samux 8:335f2506f422 28
samux 8:335f2506f422 29 #include "USBHAL.h"
samux 8:335f2506f422 30
mjr 37:c5ac4ccf6597 31 // Critical section controls. This module uses a bunch of static variables,
mjr 37:c5ac4ccf6597 32 // and much of the code that accesses the statics can be called from either
mjr 37:c5ac4ccf6597 33 // normal application context or IRQ context. Whenever a shared variable is
mjr 37:c5ac4ccf6597 34 // accessed from code that can run in an application context, we have to
mjr 37:c5ac4ccf6597 35 // protect against interrupts by entering a critical section. These macros
mjr 37:c5ac4ccf6597 36 // enable and disable the USB IRQ if we're running in application context.
mjr 37:c5ac4ccf6597 37 // (They do nothing if we're already in interrupt context, because the
mjr 37:c5ac4ccf6597 38 // hardware interrupt controller won't generated another of the same IRQ
mjr 37:c5ac4ccf6597 39 // that we're already handling. We could still be interrupted by a different,
mjr 37:c5ac4ccf6597 40 // higher-priority IRQ, but our shared variables are only shared within this
mjr 37:c5ac4ccf6597 41 // module, so they won't be affected by other interrupt handlers.)
mjr 37:c5ac4ccf6597 42 static int inIRQ;
mjr 37:c5ac4ccf6597 43 #define ENTER_CRITICAL_SECTION \
mjr 37:c5ac4ccf6597 44 if (!inIRQ) \
mjr 37:c5ac4ccf6597 45 NVIC_DisableIRQ(USB0_IRQn);
mjr 37:c5ac4ccf6597 46 #define EXIT_CRITICAL_SECTION \
mjr 37:c5ac4ccf6597 47 if (!inIRQ) \
mjr 37:c5ac4ccf6597 48 NVIC_EnableIRQ(USB0_IRQn);
mjr 37:c5ac4ccf6597 49
mjr 37:c5ac4ccf6597 50 // static singleton instance pointer
samux 8:335f2506f422 51 USBHAL * USBHAL::instance;
samux 8:335f2506f422 52
mjr 48:b225d025ca1d 53
samux 8:335f2506f422 54 // Convert physical endpoint number to register bit
mjr 35:53e1a208f582 55 #define EP(endpoint) (1<<(endpoint))
samux 8:335f2506f422 56
mjr 37:c5ac4ccf6597 57 // Convert physical endpoint number to logical endpoint number.
mjr 37:c5ac4ccf6597 58 // Each logical endpoint has two physical endpoints, one RX and
mjr 37:c5ac4ccf6597 59 // one TX. The physical endpoints are numbered in RX,TX pairs,
mjr 37:c5ac4ccf6597 60 // so the logical endpoint number is simply the physical endpoint
mjr 37:c5ac4ccf6597 61 // number divided by 2 (discarding the remainder).
samux 8:335f2506f422 62 #define PHY_TO_LOG(endpoint) ((endpoint)>>1)
samux 8:335f2506f422 63
mjr 37:c5ac4ccf6597 64 // Get a physical endpoint's direction. IN and OUT are from
mjr 37:c5ac4ccf6597 65 // the host's perspective, so from our perspective on the device,
mjr 37:c5ac4ccf6597 66 // IN == TX and OUT == RX. The physical endpoints are in RX,TX
mjr 37:c5ac4ccf6597 67 // pairs, so the OUT/RX is the even numbered element of a pair
mjr 37:c5ac4ccf6597 68 // and the IN/TX is the odd numbered element.
samux 8:335f2506f422 69 #define IN_EP(endpoint) ((endpoint) & 1U ? true : false)
samux 8:335f2506f422 70 #define OUT_EP(endpoint) ((endpoint) & 1U ? false : true)
samux 8:335f2506f422 71
mjr 37:c5ac4ccf6597 72 // BDT status flags, defined by the SIE hardware. These are
mjr 37:c5ac4ccf6597 73 // bits packed into the 'info' byte of a BDT entry.
mjr 37:c5ac4ccf6597 74 #define BD_OWN_MASK (1<<7) // OWN - hardware SIE owns the BDT (TX/RX in progress)
mjr 37:c5ac4ccf6597 75 #define BD_DATA01_MASK (1<<6) // DATA01 - DATA0/DATA1 bit for current TX/RX on endpoint
mjr 37:c5ac4ccf6597 76 #define BD_KEEP_MASK (1<<5) // KEEP - hardware keeps BDT ownership after token completes
mjr 37:c5ac4ccf6597 77 #define BD_NINC_MASK (1<<4) // NO INCREMENT - buffer location is a FIFO, so use same address for all bytes
mjr 37:c5ac4ccf6597 78 #define BD_DTS_MASK (1<<3) // DATA TOGGLE SENSING - hardware SIE checks for DATA0/DATA1 match during RX/TX
mjr 37:c5ac4ccf6597 79 #define BD_STALL_MASK (1<<2) // STALL - SIE issues STALL handshake in reply to any host access to endpoint
mjr 35:53e1a208f582 80
mjr 48:b225d025ca1d 81 // Endpoint direction (from DEVICE perspective)
samux 8:335f2506f422 82 #define TX 1
samux 8:335f2506f422 83 #define RX 0
mjr 36:20bb47609697 84
mjr 37:c5ac4ccf6597 85 // Buffer parity. The hardware has a double-buffering scheme where each
mjr 37:c5ac4ccf6597 86 // physical endpoint has two associated BDT entries, labeled EVEN and ODD.
mjr 37:c5ac4ccf6597 87 // We disable the double buffering, so only the EVEN buffers are used in
mjr 37:c5ac4ccf6597 88 // this implementation.
mjr 36:20bb47609697 89 #define EVEN 0
mjr 36:20bb47609697 90 #define ODD 1
mjr 36:20bb47609697 91
mjr 37:c5ac4ccf6597 92 // Get the BDT index for a given logical endpoint, direction, and buffer parity
mjr 37:c5ac4ccf6597 93 #define EP_BDT_IDX(logep, dir, odd) (((logep) * 4) + (2 * (dir)) + (1 * (odd)))
mjr 36:20bb47609697 94
mjr 37:c5ac4ccf6597 95 // Get the BDT index for a given physical endpoint and buffer parity
mjr 37:c5ac4ccf6597 96 #define PEP_BDT_IDX(phyep, odd) (((phyep) * 2) + (1 * (odd)))
mjr 36:20bb47609697 97
mjr 37:c5ac4ccf6597 98 // Token types reported in the BDT 'info' flags.
mjr 37:c5ac4ccf6597 99 #define TOK_PID(idx) ((bdt[idx].info >> 2) & 0x0F)
samux 8:335f2506f422 100 #define SETUP_TOKEN 0x0D
samux 8:335f2506f422 101 #define IN_TOKEN 0x09
samux 8:335f2506f422 102 #define OUT_TOKEN 0x01
mjr 34:884405d998bb 103
mjr 37:c5ac4ccf6597 104 // Buffer Descriptor Table (BDT) entry. This is the hardware-defined
mjr 37:c5ac4ccf6597 105 // memory structure for the shared memory block controlling an endpoint.
mjr 35:53e1a208f582 106 typedef struct BDT {
mjr 35:53e1a208f582 107 uint8_t info; // BD[0:7]
mjr 35:53e1a208f582 108 uint8_t dummy; // RSVD: BD[8:15]
mjr 37:c5ac4ccf6597 109 uint16_t byte_count; // BD[16:32]
mjr 35:53e1a208f582 110 uint32_t address; // Addr
mjr 37:c5ac4ccf6597 111 } BDT;
samux 8:335f2506f422 112
mjr 48:b225d025ca1d 113
mjr 36:20bb47609697 114 // There are:
mjr 37:c5ac4ccf6597 115 // * 16 bidirectional logical endpoints -> 32 physical endpoints
mjr 37:c5ac4ccf6597 116 // * 2 BDT entries per endpoint (EVEN/ODD) -> 64 BDT entries
mjr 36:20bb47609697 117 __attribute__((__aligned__(512))) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
mjr 37:c5ac4ccf6597 118
mjr 37:c5ac4ccf6597 119 // Transfer buffers. We allocate the transfer buffers and point the
mjr 37:c5ac4ccf6597 120 // SIE hardware to them via the BDT. We disable hardware SIE's
mjr 37:c5ac4ccf6597 121 // double-buffering (EVEN/ODD) scheme, so we only allocate one buffer
mjr 37:c5ac4ccf6597 122 // per physical endpoint.
mjr 36:20bb47609697 123 uint8_t *endpoint_buffer[NUMBER_OF_PHYSICAL_ENDPOINTS];
mjr 36:20bb47609697 124
mjr 37:c5ac4ccf6597 125 // Allocated size of each endpoint buffer
mjr 37:c5ac4ccf6597 126 size_t epMaxPacket[NUMBER_OF_PHYSICAL_ENDPOINTS];
mjr 37:c5ac4ccf6597 127
mjr 48:b225d025ca1d 128
mjr 37:c5ac4ccf6597 129 // SET ADDRESS mode tracking. The address assignment has to be done in a
mjr 37:c5ac4ccf6597 130 // specific order and with specific timing defined by the USB setup protocol
mjr 37:c5ac4ccf6597 131 // standards. To get the sequencing right, we set a flag when we get the
mjr 37:c5ac4ccf6597 132 // address message, and then set the address in the SIE when we're at the
mjr 37:c5ac4ccf6597 133 // right subsequent packet step in the protocol exchange. These variables
mjr 37:c5ac4ccf6597 134 // are just a place to stash the information between the time we receive the
mjr 37:c5ac4ccf6597 135 // data and the time we're ready to update the SIE register.
mjr 37:c5ac4ccf6597 136 static uint8_t set_addr = 0;
mjr 36:20bb47609697 137 static uint8_t addr = 0;
mjr 36:20bb47609697 138
mjr 37:c5ac4ccf6597 139 // Endpoint DATA0/DATA1 bits, packed as a bit vector. Each endpoint's
mjr 37:c5ac4ccf6597 140 // bit is at (1 << endpoint number). These track the current bit value
mjr 37:c5ac4ccf6597 141 // on the endpoint. For TX endpoints, this is the bit for the LAST
mjr 37:c5ac4ccf6597 142 // packet we sent (so the next packet will be the inverse). For RX
mjr 37:c5ac4ccf6597 143 // endpoints, this is the bit value we expect for the NEXT packet.
mjr 37:c5ac4ccf6597 144 // (Yes, it's inconsistent.)
mjr 37:c5ac4ccf6597 145 static uint32_t Data1 = 0x55555555;
mjr 35:53e1a208f582 146
mjr 37:c5ac4ccf6597 147 // Endpoint read/write completion flags, packed as a bit vector. Each
mjr 37:c5ac4ccf6597 148 // endpoint's bit is at (1 << endpoint number). A 1 bit signifies that
mjr 37:c5ac4ccf6597 149 // the last read or write has completed (and hasn't had its result
mjr 37:c5ac4ccf6597 150 // consumed yet).
mjr 48:b225d025ca1d 151 static volatile uint32_t epComplete = 0;
mjr 35:53e1a208f582 152
mjr 37:c5ac4ccf6597 153 static uint32_t frameNumber()
mjr 37:c5ac4ccf6597 154 {
bogdanm 13:16731886c049 155 return((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
samux 8:335f2506f422 156 }
samux 8:335f2506f422 157
mjr 37:c5ac4ccf6597 158 uint32_t USBHAL::endpointReadcore(uint8_t endpoint, uint8_t *buffer)
mjr 37:c5ac4ccf6597 159 {
samux 8:335f2506f422 160 return 0;
samux 8:335f2506f422 161 }
samux 8:335f2506f422 162
mjr 37:c5ac4ccf6597 163 USBHAL::USBHAL(void) {
samux 8:335f2506f422 164 // Disable IRQ
samux 8:335f2506f422 165 NVIC_DisableIRQ(USB0_IRQn);
mbed_official 20:d38b72fed893 166
mbed_official 22:5b7d31d9d3f3 167 #if defined(TARGET_K64F)
mbed_official 22:5b7d31d9d3f3 168 MPU->CESR=0;
mbed_official 22:5b7d31d9d3f3 169 #endif
samux 8:335f2506f422 170 // fill in callback array
mjr 48:b225d025ca1d 171 epCallback[0] = &USBHAL::EP1_OUT_callback;
mjr 48:b225d025ca1d 172 epCallback[1] = &USBHAL::EP1_IN_callback;
mjr 48:b225d025ca1d 173 epCallback[2] = &USBHAL::EP2_OUT_callback;
mjr 48:b225d025ca1d 174 epCallback[3] = &USBHAL::EP2_IN_callback;
mjr 48:b225d025ca1d 175 epCallback[4] = &USBHAL::EP3_OUT_callback;
mjr 48:b225d025ca1d 176 epCallback[5] = &USBHAL::EP3_IN_callback;
mjr 48:b225d025ca1d 177 epCallback[6] = &USBHAL::EP4_OUT_callback;
mjr 48:b225d025ca1d 178 epCallback[7] = &USBHAL::EP4_IN_callback;
mjr 48:b225d025ca1d 179 epCallback[8] = &USBHAL::EP5_OUT_callback;
mjr 48:b225d025ca1d 180 epCallback[9] = &USBHAL::EP5_IN_callback;
mjr 48:b225d025ca1d 181 epCallback[10] = &USBHAL::EP6_OUT_callback;
mjr 48:b225d025ca1d 182 epCallback[11] = &USBHAL::EP6_IN_callback;
mjr 48:b225d025ca1d 183 epCallback[12] = &USBHAL::EP7_OUT_callback;
mjr 48:b225d025ca1d 184 epCallback[13] = &USBHAL::EP7_IN_callback;
mjr 48:b225d025ca1d 185 epCallback[14] = &USBHAL::EP8_OUT_callback;
mjr 48:b225d025ca1d 186 epCallback[15] = &USBHAL::EP8_IN_callback;
mjr 48:b225d025ca1d 187 epCallback[16] = &USBHAL::EP9_OUT_callback;
mjr 48:b225d025ca1d 188 epCallback[17] = &USBHAL::EP9_IN_callback;
mjr 48:b225d025ca1d 189 epCallback[18] = &USBHAL::EP10_OUT_callback;
mjr 48:b225d025ca1d 190 epCallback[19] = &USBHAL::EP10_IN_callback;
mjr 48:b225d025ca1d 191 epCallback[20] = &USBHAL::EP11_OUT_callback;
mjr 48:b225d025ca1d 192 epCallback[21] = &USBHAL::EP11_IN_callback;
mjr 48:b225d025ca1d 193 epCallback[22] = &USBHAL::EP12_OUT_callback;
mjr 48:b225d025ca1d 194 epCallback[23] = &USBHAL::EP12_IN_callback;
mjr 48:b225d025ca1d 195 epCallback[24] = &USBHAL::EP13_OUT_callback;
mjr 48:b225d025ca1d 196 epCallback[25] = &USBHAL::EP13_IN_callback;
mjr 48:b225d025ca1d 197 epCallback[26] = &USBHAL::EP14_OUT_callback;
mjr 48:b225d025ca1d 198 epCallback[27] = &USBHAL::EP14_IN_callback;
mjr 48:b225d025ca1d 199 epCallback[28] = &USBHAL::EP15_OUT_callback;
mjr 48:b225d025ca1d 200 epCallback[29] = &USBHAL::EP15_IN_callback;
mbed_official 20:d38b72fed893 201
samux 8:335f2506f422 202 // choose usb src as PLL
samux 8:335f2506f422 203 SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);
mbed_official 20:d38b72fed893 204
samux 8:335f2506f422 205 // enable OTG clock
samux 8:335f2506f422 206 SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
samux 8:335f2506f422 207
mjr 37:c5ac4ccf6597 208 // Attach IRQ
mjr 37:c5ac4ccf6597 209 instance = this;
mjr 37:c5ac4ccf6597 210 NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
mjr 37:c5ac4ccf6597 211 NVIC_EnableIRQ(USB0_IRQn);
mjr 37:c5ac4ccf6597 212
samux 8:335f2506f422 213 // USB Module Configuration
samux 8:335f2506f422 214 // Reset USB Module
samux 8:335f2506f422 215 USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
samux 8:335f2506f422 216 while(USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
mbed_official 20:d38b72fed893 217
samux 8:335f2506f422 218 // Set BDT Base Register
mjr 37:c5ac4ccf6597 219 USB0->BDTPAGE1 = (uint8_t)((uint32_t)bdt>>8);
mjr 37:c5ac4ccf6597 220 USB0->BDTPAGE2 = (uint8_t)((uint32_t)bdt>>16);
mjr 37:c5ac4ccf6597 221 USB0->BDTPAGE3 = (uint8_t)((uint32_t)bdt>>24);
samux 8:335f2506f422 222
samux 8:335f2506f422 223 // Clear interrupt flag
samux 8:335f2506f422 224 USB0->ISTAT = 0xff;
samux 8:335f2506f422 225
samux 8:335f2506f422 226 // USB Interrupt Enablers
mjr 37:c5ac4ccf6597 227 USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK |
mjr 37:c5ac4ccf6597 228 USB_INTEN_SOFTOKEN_MASK |
mjr 37:c5ac4ccf6597 229 USB_INTEN_ERROREN_MASK |
mjr 37:c5ac4ccf6597 230 USB_INTEN_SLEEPEN_MASK |
mjr 37:c5ac4ccf6597 231 USB_INTEN_RESUMEEN_MASK |
mjr 37:c5ac4ccf6597 232 USB_INTEN_USBRSTEN_MASK;
mbed_official 20:d38b72fed893 233
mbed_official 20:d38b72fed893 234 // Disable weak pull downs
mbed_official 20:d38b72fed893 235 USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);
mjr 37:c5ac4ccf6597 236
samux 8:335f2506f422 237 USB0->USBTRC0 |= 0x40;
samux 8:335f2506f422 238 }
samux 8:335f2506f422 239
mjr 37:c5ac4ccf6597 240 USBHAL::~USBHAL(void)
mjr 48:b225d025ca1d 241 {
mjr 37:c5ac4ccf6597 242 }
mjr 35:53e1a208f582 243
mjr 36:20bb47609697 244 void USBHAL::connect(void)
mjr 36:20bb47609697 245 {
mjr 35:53e1a208f582 246 // enable USB
mjr 35:53e1a208f582 247 USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
mjr 48:b225d025ca1d 248
mjr 35:53e1a208f582 249 // Pull up enable
mjr 35:53e1a208f582 250 USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
mjr 34:884405d998bb 251 }
samux 8:335f2506f422 252
mjr 35:53e1a208f582 253 void USBHAL::disconnect(void)
mjr 34:884405d998bb 254 {
samux 8:335f2506f422 255 // disable USB
samux 8:335f2506f422 256 USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
mjr 48:b225d025ca1d 257
samux 8:335f2506f422 258 // Pull up disable
samux 8:335f2506f422 259 USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
bogdanm 14:d495202c90f4 260
mjr 48:b225d025ca1d 261 // Free buffers
mjr 37:c5ac4ccf6597 262 for (int i = 0 ; i < NUMBER_OF_PHYSICAL_ENDPOINTS ; i++)
mjr 37:c5ac4ccf6597 263 {
mjr 37:c5ac4ccf6597 264 if (endpoint_buffer[i] != NULL)
mjr 37:c5ac4ccf6597 265 {
mjr 49:03527ce6840e 266 delete [] endpoint_buffer[i];
mjr 37:c5ac4ccf6597 267 endpoint_buffer[i] = NULL;
mjr 48:b225d025ca1d 268 epMaxPacket[i] = 0;
mjr 37:c5ac4ccf6597 269 }
bogdanm 14:d495202c90f4 270 }
samux 8:335f2506f422 271 }
samux 8:335f2506f422 272
mjr 37:c5ac4ccf6597 273 void USBHAL::configureDevice(void)
mjr 37:c5ac4ccf6597 274 {
samux 8:335f2506f422 275 // not needed
samux 8:335f2506f422 276 }
samux 8:335f2506f422 277
mjr 37:c5ac4ccf6597 278 void USBHAL::unconfigureDevice(void)
mjr 37:c5ac4ccf6597 279 {
samux 8:335f2506f422 280 // not needed
samux 8:335f2506f422 281 }
samux 8:335f2506f422 282
mjr 37:c5ac4ccf6597 283 void USBHAL::setAddress(uint8_t address)
mjr 37:c5ac4ccf6597 284 {
samux 8:335f2506f422 285 // we don't set the address now otherwise the usb controller does not ack
samux 8:335f2506f422 286 // we set a flag instead
samux 8:335f2506f422 287 // see usbisr when an IN token is received
samux 8:335f2506f422 288 set_addr = 1;
samux 8:335f2506f422 289 addr = address;
mjr 35:53e1a208f582 290 }
samux 8:335f2506f422 291
mjr 35:53e1a208f582 292 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags)
mjr 34:884405d998bb 293 {
mjr 37:c5ac4ccf6597 294 // validate the endpoint number
mjr 34:884405d998bb 295 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS)
mjr 34:884405d998bb 296 return false;
samux 8:335f2506f422 297
mjr 37:c5ac4ccf6597 298 // get the logical endpoint
samux 8:335f2506f422 299 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 34:884405d998bb 300
mjr 48:b225d025ca1d 301 // Assume this is a bulk or interrupt endpoint. For these, the hardware maximum
mjr 48:b225d025ca1d 302 // packet size is 64 bytes, and we use packet handshaking.
mjr 48:b225d025ca1d 303 uint32_t hwMaxPacket = 64;
mjr 48:b225d025ca1d 304 uint32_t handshake_flag = USB_ENDPT_EPHSHK_MASK;
mjr 48:b225d025ca1d 305
mjr 48:b225d025ca1d 306 // If it's to be an isochronous endpoint, the hardware maximum packet size
mjr 48:b225d025ca1d 307 // increases to 1023 bytes, and we don't use handshaking.
mjr 48:b225d025ca1d 308 if (flags & ISOCHRONOUS)
mjr 48:b225d025ca1d 309 {
mjr 48:b225d025ca1d 310 handshake_flag = 0;
mjr 48:b225d025ca1d 311 hwMaxPacket = 1023;
mjr 48:b225d025ca1d 312 }
mjr 37:c5ac4ccf6597 313
mjr 48:b225d025ca1d 314 // limit the requested max packet size to the hardware limit
mjr 48:b225d025ca1d 315 if (maxPacket > hwMaxPacket)
mjr 48:b225d025ca1d 316 maxPacket = hwMaxPacket;
mjr 37:c5ac4ccf6597 317
mjr 37:c5ac4ccf6597 318 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 319 {
mjr 48:b225d025ca1d 320 // if the endpoint buffer hasn't been allocated yet or was previously
mjr 48:b225d025ca1d 321 // allocated at a smaller size, allocate a new buffer
mjr 37:c5ac4ccf6597 322 uint8_t *buf = endpoint_buffer[endpoint];
mjr 48:b225d025ca1d 323 if (buf == NULL || epMaxPacket[endpoint] < maxPacket)
mjr 37:c5ac4ccf6597 324 {
mjr 48:b225d025ca1d 325 // free any previous buffer
mjr 37:c5ac4ccf6597 326 if (buf != 0)
mjr 49:03527ce6840e 327 delete [] buf;
mjr 48:b225d025ca1d 328
mjr 48:b225d025ca1d 329 // allocate at the new size
mjr 49:03527ce6840e 330 endpoint_buffer[endpoint] = buf = new uint8_t[maxPacket];
mjr 48:b225d025ca1d 331
mjr 48:b225d025ca1d 332 // set the new max packet size
mjr 37:c5ac4ccf6597 333 epMaxPacket[endpoint] = maxPacket;
mjr 37:c5ac4ccf6597 334 }
mjr 37:c5ac4ccf6597 335
mjr 48:b225d025ca1d 336 // set the endpoint register flags and BDT entry
mjr 48:b225d025ca1d 337 if (IN_EP(endpoint))
mjr 48:b225d025ca1d 338 {
mjr 48:b225d025ca1d 339 // IN endpt -> device to host (TX)
mjr 48:b225d025ca1d 340 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | USB_ENDPT_EPTXEN_MASK; // en TX (IN) tran
mjr 48:b225d025ca1d 341 bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = (uint32_t) buf;
mjr 48:b225d025ca1d 342 bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = 0;
mjr 48:b225d025ca1d 343 }
mjr 48:b225d025ca1d 344 else
mjr 37:c5ac4ccf6597 345 {
mjr 48:b225d025ca1d 346 // OUT endpt -> host to device (RX)
mjr 48:b225d025ca1d 347 USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | USB_ENDPT_EPRXEN_MASK; // en RX (OUT) tran.
mjr 48:b225d025ca1d 348 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].address = (uint32_t) buf;
mjr 48:b225d025ca1d 349 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address = 0;
mjr 48:b225d025ca1d 350
mjr 48:b225d025ca1d 351 // set up the first read
mjr 48:b225d025ca1d 352 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].byte_count = maxPacket;
mjr 48:b225d025ca1d 353 bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 48:b225d025ca1d 354 bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info = 0;
mjr 37:c5ac4ccf6597 355 }
mjr 37:c5ac4ccf6597 356
mjr 37:c5ac4ccf6597 357 // Set DATA1 on the endpoint. For RX endpoints, we just queued up our first
mjr 37:c5ac4ccf6597 358 // read, which will always be a DATA0 packet, so the next read will use DATA1.
mjr 37:c5ac4ccf6597 359 // For TX endpoints, we always flip the bit *before* sending the packet, so
mjr 37:c5ac4ccf6597 360 // (counterintuitively) we need to set the DATA1 bit now to send DATA0 in the
mjr 37:c5ac4ccf6597 361 // next packet. So in either case, we want DATA1 initially.
mjr 37:c5ac4ccf6597 362 Data1 |= (1 << endpoint);
mjr 35:53e1a208f582 363 }
mjr 37:c5ac4ccf6597 364 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 365
mjr 36:20bb47609697 366 // success
samux 8:335f2506f422 367 return true;
samux 8:335f2506f422 368 }
samux 8:335f2506f422 369
mjr 34:884405d998bb 370 // read setup packet
mjr 36:20bb47609697 371 void USBHAL::EP0setup(uint8_t *buffer)
mjr 36:20bb47609697 372 {
mjr 34:884405d998bb 373 uint32_t sz;
mjr 37:c5ac4ccf6597 374 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 375 }
mjr 34:884405d998bb 376
mjr 36:20bb47609697 377 void USBHAL::EP0readStage(void)
mjr 36:20bb47609697 378 {
mjr 37:c5ac4ccf6597 379 if (!(bdt[0].info & BD_OWN_MASK))
mjr 37:c5ac4ccf6597 380 {
mjr 37:c5ac4ccf6597 381 Data1 &= ~1UL; // set DATA0
mjr 37:c5ac4ccf6597 382 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 37:c5ac4ccf6597 383 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
mjr 36:20bb47609697 384 }
mjr 34:884405d998bb 385 }
mjr 34:884405d998bb 386
mjr 36:20bb47609697 387 void USBHAL::EP0read(void)
mjr 36:20bb47609697 388 {
mjr 38:072e12583e73 389 if (!(bdt[0].info & BD_OWN_MASK))
mjr 37:c5ac4ccf6597 390 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 34:884405d998bb 391 }
mjr 34:884405d998bb 392
mjr 36:20bb47609697 393 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer)
mjr 36:20bb47609697 394 {
mjr 35:53e1a208f582 395 uint32_t sz;
mjr 37:c5ac4ccf6597 396 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 397 return sz;
mjr 34:884405d998bb 398 }
mjr 34:884405d998bb 399
mjr 49:03527ce6840e 400 void USBHAL::EP0write(const uint8_t *buffer, uint32_t size)
mjr 37:c5ac4ccf6597 401 {
mjr 37:c5ac4ccf6597 402 endpointWrite(EP0IN, buffer, size);
mjr 34:884405d998bb 403 }
mjr 34:884405d998bb 404
mjr 37:c5ac4ccf6597 405 void USBHAL::EP0getWriteResult(void)
mjr 37:c5ac4ccf6597 406 {
mjr 34:884405d998bb 407 }
mjr 34:884405d998bb 408
mjr 36:20bb47609697 409 void USBHAL::EP0stall(void)
mjr 36:20bb47609697 410 {
mjr 34:884405d998bb 411 stallEndpoint(EP0OUT);
mjr 34:884405d998bb 412 }
mjr 34:884405d998bb 413
mjr 35:53e1a208f582 414 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize)
mjr 34:884405d998bb 415 {
mjr 37:c5ac4ccf6597 416 // We always start a new read when we fetch the result of the
mjr 37:c5ac4ccf6597 417 // previous read, so we don't have to do anything here. Simply
mjr 37:c5ac4ccf6597 418 // indicate that the read is pending so that the caller can proceed
mjr 37:c5ac4ccf6597 419 // to check the results.
mjr 34:884405d998bb 420 return EP_PENDING;
mjr 34:884405d998bb 421 }
mjr 34:884405d998bb 422
mjr 36:20bb47609697 423 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t *buffer, uint32_t *bytesRead)
mjr 34:884405d998bb 424 {
mjr 37:c5ac4ccf6597 425 // validate the endpoint number and direction
mjr 37:c5ac4ccf6597 426 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !OUT_EP(endpoint))
mjr 34:884405d998bb 427 return EP_INVALID;
mjr 34:884405d998bb 428
mjr 37:c5ac4ccf6597 429 // get the logical endpoint
mjr 34:884405d998bb 430 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 37:c5ac4ccf6597 431
mjr 37:c5ac4ccf6597 432 // get the mode - it's isochronous if it doesn't have the handshake flag
mjr 48:b225d025ca1d 433 bool iso = (USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK) == 0;
mjr 37:c5ac4ccf6597 434
mjr 35:53e1a208f582 435 // get the BDT index
mjr 35:53e1a208f582 436 int idx = EP_BDT_IDX(log_endpoint, RX, 0);
mjr 37:c5ac4ccf6597 437
mjr 48:b225d025ca1d 438 // Check to see if the endpoint is ready to read
mjr 37:c5ac4ccf6597 439 if (log_endpoint == 0)
mjr 37:c5ac4ccf6597 440 {
mjr 37:c5ac4ccf6597 441 // control endpoint - just make sure we own the BDT
mjr 37:c5ac4ccf6597 442 if (bdt[idx].info & BD_OWN_MASK)
mjr 37:c5ac4ccf6597 443 return EP_PENDING;
mjr 37:c5ac4ccf6597 444 }
mjr 48:b225d025ca1d 445 else
mjr 48:b225d025ca1d 446 {
mjr 48:b225d025ca1d 447 // If it's not isochronous, check to see if we've received data, and
mjr 48:b225d025ca1d 448 // return PENDING if not. Isochronous endpoints don't use the TOKNE
mjr 48:b225d025ca1d 449 // interrupt (they use SOF instead), so the 'complete' flag doesn't
mjr 48:b225d025ca1d 450 // apply if it's an iso endpoint.
mjr 48:b225d025ca1d 451 if (!iso && !(epComplete & EP(endpoint)))
mjr 48:b225d025ca1d 452 return EP_PENDING;
mjr 48:b225d025ca1d 453 }
mjr 37:c5ac4ccf6597 454
mjr 37:c5ac4ccf6597 455 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 456 {
mjr 48:b225d025ca1d 457 // note if we have a SETUP token
mjr 48:b225d025ca1d 458 bool setup = (log_endpoint == 0 && TOK_PID(idx) == SETUP_TOKEN);
mjr 48:b225d025ca1d 459
mjr 48:b225d025ca1d 460 // get the received data buffer and size
mjr 48:b225d025ca1d 461 uint8_t *ep_buf = endpoint_buffer[endpoint];
mjr 37:c5ac4ccf6597 462 uint32_t sz = bdt[idx].byte_count;
mjr 36:20bb47609697 463
mjr 48:b225d025ca1d 464 // copy the data from the hardware receive buffer to the caller's buffer
mjr 37:c5ac4ccf6597 465 *bytesRead = sz;
mjr 48:b225d025ca1d 466 for (uint32_t n = 0 ; n < sz ; n++)
mjr 48:b225d025ca1d 467 buffer[n] = ep_buf[n];
mjr 48:b225d025ca1d 468
mjr 37:c5ac4ccf6597 469 // Figure the DATA0/DATA1 bit for the next packet received on this
mjr 37:c5ac4ccf6597 470 // endpoint. The bit normally toggles on each packet, but it's
mjr 37:c5ac4ccf6597 471 // special for SETUP packets on endpoint 0. The next OUT packet
mjr 37:c5ac4ccf6597 472 // after a SETUP packet with no data stage is always DATA0, even
mjr 37:c5ac4ccf6597 473 // if the SETUP packet was also DATA0.
mjr 37:c5ac4ccf6597 474 if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1))
mjr 37:c5ac4ccf6597 475 {
mjr 48:b225d025ca1d 476 if (setup && (buffer[6] == 0)) // if SETUP with no data stage,
mjr 48:b225d025ca1d 477 Data1 &= ~1UL; // the next packet is always DATA0
mjr 37:c5ac4ccf6597 478 else
mjr 37:c5ac4ccf6597 479 Data1 ^= (1 << endpoint); // otherwise just toggle the last bit
mjr 35:53e1a208f582 480 }
mjr 48:b225d025ca1d 481
mjr 48:b225d025ca1d 482 // set up the BDT entry to receive the next packet, and hand it to the SIE
mjr 37:c5ac4ccf6597 483 bdt[idx].byte_count = epMaxPacket[endpoint];
mjr 48:b225d025ca1d 484 bdt[idx].info = BD_DTS_MASK | BD_OWN_MASK | (((Data1 >> endpoint) & 1) << 6);
mjr 37:c5ac4ccf6597 485
mjr 48:b225d025ca1d 486 // clear the SUSPEND TOKEN BUSY flag to allow token processing to continue
mjr 36:20bb47609697 487 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 34:884405d998bb 488
mjr 48:b225d025ca1d 489 // clear the 'completed' flag - we're now awaiting the next packet
mjr 37:c5ac4ccf6597 490 epComplete &= ~EP(endpoint);
mjr 37:c5ac4ccf6597 491 }
mjr 37:c5ac4ccf6597 492 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 493
mjr 37:c5ac4ccf6597 494 // the read is completed
samux 8:335f2506f422 495 return EP_COMPLETED;
samux 8:335f2506f422 496 }
samux 8:335f2506f422 497
mjr 49:03527ce6840e 498 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size)
mjr 34:884405d998bb 499 {
mjr 37:c5ac4ccf6597 500 // validate the endpoint number and direction
mjr 37:c5ac4ccf6597 501 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !IN_EP(endpoint))
samux 8:335f2506f422 502 return EP_INVALID;
mjr 35:53e1a208f582 503
mjr 37:c5ac4ccf6597 504 // get the BDT index
mjr 35:53e1a208f582 505 int idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
mjr 37:c5ac4ccf6597 506
mjr 37:c5ac4ccf6597 507 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 508 {
mjr 48:b225d025ca1d 509 // get the endpoint buffer
mjr 48:b225d025ca1d 510 uint8_t *ep_buf = endpoint_buffer[endpoint];
mjr 48:b225d025ca1d 511
mjr 37:c5ac4ccf6597 512 // copy the data to the hardware buffer
mjr 37:c5ac4ccf6597 513 bdt[idx].byte_count = size;
mjr 48:b225d025ca1d 514 for (uint32_t n = 0 ; n < size ; n++)
mjr 48:b225d025ca1d 515 ep_buf[n] = data[n];
mjr 48:b225d025ca1d 516
mjr 48:b225d025ca1d 517 // toggle DATA0/DATA1 before sending
mjr 37:c5ac4ccf6597 518 Data1 ^= (1 << endpoint);
mjr 35:53e1a208f582 519
mjr 48:b225d025ca1d 520 // hand the BDT to the SIE to do the send
mjr 48:b225d025ca1d 521 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | (((Data1 >> endpoint) & 1) << 6);
mjr 37:c5ac4ccf6597 522 }
mjr 37:c5ac4ccf6597 523 EXIT_CRITICAL_SECTION
mjr 48:b225d025ca1d 524
mjr 48:b225d025ca1d 525 // write is now pending in the hardware
samux 8:335f2506f422 526 return EP_PENDING;
samux 8:335f2506f422 527 }
samux 8:335f2506f422 528
mjr 35:53e1a208f582 529 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint)
mjr 34:884405d998bb 530 {
mjr 48:b225d025ca1d 531 // assume write is still pending
mjr 37:c5ac4ccf6597 532 EP_STATUS result = EP_PENDING;
mjr 48:b225d025ca1d 533
mjr 37:c5ac4ccf6597 534 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 535 {
mjr 48:b225d025ca1d 536 // check the 'completed' flag - if set, the write is completed
mjr 48:b225d025ca1d 537 if (epComplete & EP(endpoint))
mjr 48:b225d025ca1d 538 {
mjr 48:b225d025ca1d 539 // the result is COMPLETED
mjr 48:b225d025ca1d 540 result = EP_COMPLETED;
mjr 48:b225d025ca1d 541
mjr 48:b225d025ca1d 542 // clear the 'completed' flag - this is consumed by fetching the result
mjr 37:c5ac4ccf6597 543 epComplete &= ~EP(endpoint);
mjr 37:c5ac4ccf6597 544 }
samux 8:335f2506f422 545 }
mjr 37:c5ac4ccf6597 546 EXIT_CRITICAL_SECTION
mjr 36:20bb47609697 547
mjr 48:b225d025ca1d 548 // return the result
mjr 37:c5ac4ccf6597 549 return result;
samux 8:335f2506f422 550 }
samux 8:335f2506f422 551
mjr 36:20bb47609697 552 void USBHAL::stallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 553 {
mjr 37:c5ac4ccf6597 554 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 555 }
samux 8:335f2506f422 556
mjr 36:20bb47609697 557 void USBHAL::unstallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 558 {
mjr 37:c5ac4ccf6597 559 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 560 {
mjr 48:b225d025ca1d 561 // clear the stall bit in the endpoint register
mjr 37:c5ac4ccf6597 562 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
mjr 48:b225d025ca1d 563
mjr 48:b225d025ca1d 564 // take ownership of the BDT entry
mjr 37:c5ac4ccf6597 565 int idx = PEP_BDT_IDX(endpoint, 0);
mjr 37:c5ac4ccf6597 566 bdt[idx].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 48:b225d025ca1d 567
mjr 48:b225d025ca1d 568 // if this is an RX endpoint, start a new read
mjr 48:b225d025ca1d 569 if (OUT_EP(endpoint))
mjr 48:b225d025ca1d 570 {
mjr 48:b225d025ca1d 571 bdt[idx].byte_count = epMaxPacket[endpoint];
mjr 48:b225d025ca1d 572 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 48:b225d025ca1d 573 }
mjr 48:b225d025ca1d 574
mjr 48:b225d025ca1d 575 // Reset Data1 for the endpoint - we need to set the bit to 1 for
mjr 48:b225d025ca1d 576 // either TX or RX, by the same logic as in realiseEndpoint()
mjr 48:b225d025ca1d 577 Data1 |= (1 << endpoint);
mjr 48:b225d025ca1d 578
mjr 48:b225d025ca1d 579 // clear the 'completed' bit for the endpoint
mjr 48:b225d025ca1d 580 epComplete &= ~(1 << endpoint);
mjr 36:20bb47609697 581 }
mjr 37:c5ac4ccf6597 582 EXIT_CRITICAL_SECTION
samux 8:335f2506f422 583 }
samux 8:335f2506f422 584
mjr 36:20bb47609697 585 bool USBHAL::getEndpointStallState(uint8_t endpoint)
mjr 36:20bb47609697 586 {
mjr 37:c5ac4ccf6597 587 uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
mjr 37:c5ac4ccf6597 588 return (stall) ? true : false;
samux 8:335f2506f422 589 }
samux 8:335f2506f422 590
mjr 37:c5ac4ccf6597 591 void USBHAL::remoteWakeup(void)
mjr 37:c5ac4ccf6597 592 {
samux 8:335f2506f422 593 // [TODO]
samux 8:335f2506f422 594 }
samux 8:335f2506f422 595
mjr 37:c5ac4ccf6597 596 void USBHAL::_usbisr(void)
mjr 37:c5ac4ccf6597 597 {
mjr 37:c5ac4ccf6597 598 inIRQ = true;
samux 8:335f2506f422 599 instance->usbisr();
mjr 37:c5ac4ccf6597 600 inIRQ = false;
samux 8:335f2506f422 601 }
samux 8:335f2506f422 602
samux 8:335f2506f422 603
mjr 36:20bb47609697 604 void USBHAL::usbisr(void)
mjr 36:20bb47609697 605 {
samux 8:335f2506f422 606 uint8_t i;
samux 8:335f2506f422 607 uint8_t istat = USB0->ISTAT;
samux 8:335f2506f422 608
samux 8:335f2506f422 609 // reset interrupt
mjr 39:d684a8ce5d88 610 if (istat & USB_ISTAT_USBRST_MASK)
mjr 39:d684a8ce5d88 611 {
mjr 48:b225d025ca1d 612 // disable all endpoints
mjr 48:b225d025ca1d 613 for (i = 0 ; i < 16 ; i++)
samux 8:335f2506f422 614 USB0->ENDPOINT[i].ENDPT = 0x00;
samux 8:335f2506f422 615
samux 8:335f2506f422 616 // enable control endpoint
samux 8:335f2506f422 617 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 618 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 619
mjr 48:b225d025ca1d 620 // reset DATA0/1 state
samux 8:335f2506f422 621 Data1 = 0x55555555;
mjr 48:b225d025ca1d 622
mjr 48:b225d025ca1d 623 // reset endpoint completion status
mjr 39:d684a8ce5d88 624 epComplete = 0;
mjr 48:b225d025ca1d 625
mjr 48:b225d025ca1d 626 // reset EVEN/ODD state (and keep it permanently on EVEN -
mjr 48:b225d025ca1d 627 // this disables the hardware double-buffering system)
samux 8:335f2506f422 628 USB0->CTL |= USB_CTL_ODDRST_MASK;
samux 8:335f2506f422 629
mjr 37:c5ac4ccf6597 630 USB0->ISTAT = 0xFF; // clear all interrupt status flags
mjr 37:c5ac4ccf6597 631 USB0->ERRSTAT = 0xFF; // clear all error flags
mjr 37:c5ac4ccf6597 632 USB0->ERREN = 0xFF; // enable error interrupt sources
samux 8:335f2506f422 633 USB0->ADDR = 0x00; // set default address
mjr 28:0596f144dad5 634
mjr 31:81f57ea86f8f 635 // notify upper layers of the bus reset
mjr 31:81f57ea86f8f 636 busReset();
mjr 31:81f57ea86f8f 637
mjr 31:81f57ea86f8f 638 // we're not suspended
mjr 28:0596f144dad5 639 suspendStateChanged(0);
mjr 48:b225d025ca1d 640
mjr 48:b225d025ca1d 641 // do ONLY the reset processing on a RESET interrupt
samux 8:335f2506f422 642 return;
samux 8:335f2506f422 643 }
samux 8:335f2506f422 644
samux 8:335f2506f422 645 // resume interrupt
mjr 37:c5ac4ccf6597 646 if (istat & USB_ISTAT_RESUME_MASK)
mjr 37:c5ac4ccf6597 647 {
mjr 36:20bb47609697 648 suspendStateChanged(0);
samux 8:335f2506f422 649 USB0->ISTAT = USB_ISTAT_RESUME_MASK;
samux 8:335f2506f422 650 }
samux 8:335f2506f422 651
samux 8:335f2506f422 652 // SOF interrupt
mjr 37:c5ac4ccf6597 653 if (istat & USB_ISTAT_SOFTOK_MASK)
mjr 37:c5ac4ccf6597 654 {
mjr 37:c5ac4ccf6597 655 // Read frame number and signal the SOF event to the callback
samux 8:335f2506f422 656 SOF(frameNumber());
mjr 36:20bb47609697 657 USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
samux 8:335f2506f422 658 }
mbed_official 20:d38b72fed893 659
samux 8:335f2506f422 660 // stall interrupt
mjr 36:20bb47609697 661 if (istat & USB_ISTAT_STALL_MASK)
mjr 36:20bb47609697 662 {
mjr 37:c5ac4ccf6597 663 // if the control endpoint (EP 0) is stalled, unstall it
mjr 37:c5ac4ccf6597 664 if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK)
mjr 37:c5ac4ccf6597 665 {
mjr 48:b225d025ca1d 666 // clear the stall bit in the endpoint register
mjr 48:b225d025ca1d 667 USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
mjr 48:b225d025ca1d 668
mjr 48:b225d025ca1d 669 // take ownership of the RX and TX BDTs
mjr 48:b225d025ca1d 670 bdt[EP_BDT_IDX(0, RX, EVEN)].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 48:b225d025ca1d 671 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 48:b225d025ca1d 672 bdt[EP_BDT_IDX(0, RX, EVEN)].byte_count = MAX_PACKET_SIZE_EP0;
mjr 48:b225d025ca1d 673 bdt[EP_BDT_IDX(0, TX, EVEN)].byte_count = MAX_PACKET_SIZE_EP0;
mjr 48:b225d025ca1d 674
mjr 48:b225d025ca1d 675 // start a new read on EP0OUT
mjr 48:b225d025ca1d 676 bdt[EP_BDT_IDX(0, RX, EVEN)].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 48:b225d025ca1d 677
mjr 48:b225d025ca1d 678 // reset the DATA0/1 bit to 1 on EP0IN and EP0OUT, by the same
mjr 48:b225d025ca1d 679 // logic as in realiseEndpoint()
mjr 48:b225d025ca1d 680 Data1 |= 0x03;
mjr 36:20bb47609697 681 }
mjr 37:c5ac4ccf6597 682
mjr 48:b225d025ca1d 683 // clear the busy-suspend bit to resume token processing
mjr 36:20bb47609697 684 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 48:b225d025ca1d 685
mjr 48:b225d025ca1d 686 // clear the interrupt status bit for STALL
mjr 40:cd877d5c09ea 687 USB0->ISTAT = USB_ISTAT_STALL_MASK;
samux 8:335f2506f422 688 }
samux 8:335f2506f422 689
mjr 37:c5ac4ccf6597 690 // token interrupt
mjr 37:c5ac4ccf6597 691 if (istat & USB_ISTAT_TOKDNE_MASK)
mjr 36:20bb47609697 692 {
mjr 48:b225d025ca1d 693 // get the endpoint information from the status register
mjr 37:c5ac4ccf6597 694 uint32_t num = (USB0->STAT >> 4) & 0x0F;
mjr 37:c5ac4ccf6597 695 uint32_t dir = (USB0->STAT >> 3) & 0x01;
mjr 34:884405d998bb 696 int endpoint = (num << 1) | dir;
mjr 48:b225d025ca1d 697 uint32_t ev_odd = (USB0->STAT >> 2) & 0x01;
mjr 48:b225d025ca1d 698
mjr 48:b225d025ca1d 699 // check which endpoint we're working with
mjr 48:b225d025ca1d 700 if (num == 0)
mjr 48:b225d025ca1d 701 {
mjr 48:b225d025ca1d 702 // Endpoint 0 requires special handling
mjr 48:b225d025ca1d 703 uint32_t idx = EP_BDT_IDX(num, dir, ev_odd);
mjr 48:b225d025ca1d 704 int pid = TOK_PID(idx);
mjr 48:b225d025ca1d 705 if (pid == SETUP_TOKEN)
mjr 48:b225d025ca1d 706 {
mjr 48:b225d025ca1d 707 // SETUP packet - next IN (TX) packet must be DATA1 (confusingly,
mjr 48:b225d025ca1d 708 // this means we must clear the Data1 bit, since we flip the bit
mjr 48:b225d025ca1d 709 // before each send)
mjr 48:b225d025ca1d 710 Data1 &= ~0x02;
mjr 48:b225d025ca1d 711
mjr 48:b225d025ca1d 712 // forcibly take ownership of the EP0IN endpoints in case we have
mjr 48:b225d025ca1d 713 // unfinished previous transmissions (the protocol state machine here
mjr 48:b225d025ca1d 714 // assumes that we don't, so it's probably an error if this code
mjr 48:b225d025ca1d 715 // actually does anything, but we make no provision for handling this)
mjr 48:b225d025ca1d 716 bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
mjr 48:b225d025ca1d 717 bdt[EP_BDT_IDX(0, TX, ODD )].info &= ~BD_OWN_MASK;
samux 8:335f2506f422 718
mjr 48:b225d025ca1d 719 // handle the EP0 SETUP event in the generic protocol layer
mjr 48:b225d025ca1d 720 EP0setupCallback();
mjr 48:b225d025ca1d 721 }
mjr 48:b225d025ca1d 722 else if (pid == OUT_TOKEN)
mjr 48:b225d025ca1d 723 {
mjr 48:b225d025ca1d 724 // OUT packet on EP0
mjr 48:b225d025ca1d 725 EP0out();
mjr 48:b225d025ca1d 726 }
mjr 48:b225d025ca1d 727 else if (pid == IN_TOKEN)
mjr 48:b225d025ca1d 728 {
mjr 48:b225d025ca1d 729 // IN packet on EP0
mjr 48:b225d025ca1d 730 EP0in();
mjr 48:b225d025ca1d 731
mjr 48:b225d025ca1d 732 // Special case: if the 'set address' flag is set, it means that the
mjr 48:b225d025ca1d 733 // host just sent us our bus address. We must put this into effect
mjr 48:b225d025ca1d 734 // in the hardware SIE *after* sending the reply, which we just did
mjr 48:b225d025ca1d 735 // above. So it's now time!
mjr 48:b225d025ca1d 736 if (set_addr == 1) {
mjr 48:b225d025ca1d 737 USB0->ADDR = addr & 0x7F;
mjr 48:b225d025ca1d 738 set_addr = 0;
mjr 48:b225d025ca1d 739 }
mjr 48:b225d025ca1d 740 }
mjr 48:b225d025ca1d 741 }
mjr 48:b225d025ca1d 742 else
mjr 48:b225d025ca1d 743 {
mjr 48:b225d025ca1d 744 // For all other endpoints, note the read/write completion in the flags
mjr 48:b225d025ca1d 745 epComplete |= EP(endpoint);
mjr 48:b225d025ca1d 746
mjr 48:b225d025ca1d 747 // call the endpoint token callback; if that handles the token, it consumes
mjr 48:b225d025ca1d 748 // the 'completed' status, so clear that flag again
mjr 48:b225d025ca1d 749 if ((instance->*(epCallback[endpoint - 2]))()) {
mjr 48:b225d025ca1d 750 epComplete &= ~EP(endpoint);
mjr 48:b225d025ca1d 751 }
mjr 48:b225d025ca1d 752 }
mjr 37:c5ac4ccf6597 753
mjr 48:b225d025ca1d 754 // clear the TOKDNE interrupt status bit
samux 8:335f2506f422 755 USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
samux 8:335f2506f422 756 }
mbed_official 20:d38b72fed893 757
samux 8:335f2506f422 758 // sleep interrupt
mjr 36:20bb47609697 759 if (istat & USB_ISTAT_SLEEP_MASK)
mjr 36:20bb47609697 760 {
mjr 28:0596f144dad5 761 suspendStateChanged(1);
mjr 40:cd877d5c09ea 762 USB0->ISTAT = USB_ISTAT_SLEEP_MASK;
mbed_official 20:d38b72fed893 763 }
samux 8:335f2506f422 764
samux 8:335f2506f422 765 // error interrupt
mjr 36:20bb47609697 766 if (istat & USB_ISTAT_ERROR_MASK)
mjr 36:20bb47609697 767 {
mjr 37:c5ac4ccf6597 768 // reset all error status bits, and clear the SUSPEND flag to allow
mjr 37:c5ac4ccf6597 769 // token processing to continue
mjr 37:c5ac4ccf6597 770 USB0->ERRSTAT = 0xFF;
mjr 36:20bb47609697 771 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 40:cd877d5c09ea 772 USB0->ISTAT = USB_ISTAT_ERROR_MASK;
samux 8:335f2506f422 773 }
samux 8:335f2506f422 774 }
samux 8:335f2506f422 775
mjr 36:20bb47609697 776 #endif