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:
Thu Feb 11 02:03:14 2016 +0000
Revision:
47:c91e6d7762e4
Parent:
40:cd877d5c09ea
Rollback #6 for finding regression

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