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:
Tue Jan 12 00:54:52 2016 +0000
Revision:
38:072e12583e73
Parent:
37:c5ac4ccf6597
Child:
39:d684a8ce5d88
Further KL25Z USB HAL cleanup: regularized the ISR callback handlers and fixed a typo.

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 37:c5ac4ccf6597 267 }
bogdanm 14:d495202c90f4 268 }
samux 8:335f2506f422 269 }
samux 8:335f2506f422 270
mjr 37:c5ac4ccf6597 271 void USBHAL::configureDevice(void)
mjr 37:c5ac4ccf6597 272 {
samux 8:335f2506f422 273 // not needed
samux 8:335f2506f422 274 }
samux 8:335f2506f422 275
mjr 37:c5ac4ccf6597 276 void USBHAL::unconfigureDevice(void)
mjr 37:c5ac4ccf6597 277 {
samux 8:335f2506f422 278 // not needed
samux 8:335f2506f422 279 }
samux 8:335f2506f422 280
mjr 37:c5ac4ccf6597 281 void USBHAL::setAddress(uint8_t address)
mjr 37:c5ac4ccf6597 282 {
samux 8:335f2506f422 283 // we don't set the address now otherwise the usb controller does not ack
samux 8:335f2506f422 284 // we set a flag instead
samux 8:335f2506f422 285 // see usbisr when an IN token is received
samux 8:335f2506f422 286 set_addr = 1;
samux 8:335f2506f422 287 addr = address;
mjr 35:53e1a208f582 288 }
samux 8:335f2506f422 289
mjr 35:53e1a208f582 290 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags)
mjr 34:884405d998bb 291 {
mjr 37:c5ac4ccf6597 292 // validate the endpoint number
mjr 34:884405d998bb 293 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS)
mjr 34:884405d998bb 294 return false;
samux 8:335f2506f422 295
mjr 37:c5ac4ccf6597 296 // get the logical endpoint
samux 8:335f2506f422 297 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 34:884405d998bb 298
mjr 37:c5ac4ccf6597 299 // Constrain the requested packet size to the maximum for the endpoint type.
mjr 37:c5ac4ccf6597 300 // Full Speed USB allows up to 1023 bytes for isochronous endpoints and 64 bytes
mjr 37:c5ac4ccf6597 301 // for bulk and interrupt endpoints.
mjr 37:c5ac4ccf6597 302 uint32_t realMaxPacket = ((flags & ISOCHRONOUS) ? 1023 : 64);
mjr 37:c5ac4ccf6597 303 if (maxPacket > realMaxPacket)
mjr 37:c5ac4ccf6597 304 maxPacket = realMaxPacket;
mjr 37:c5ac4ccf6597 305
mjr 37:c5ac4ccf6597 306 // Use the HANDSHAKE flag for non-isochronous endpoints. Don't use handshaking
mjr 37:c5ac4ccf6597 307 // for an iso endpoint, since this type of endpoint is for applications like
mjr 37:c5ac4ccf6597 308 // audio and video streaming where it's preferable to ignore lost packets and
mjr 37:c5ac4ccf6597 309 // just carry on with the latest data.
mjr 37:c5ac4ccf6597 310 uint32_t ctlFlags = 0;
mjr 37:c5ac4ccf6597 311 if (!(flags & ISOCHRONOUS))
mjr 37:c5ac4ccf6597 312 ctlFlags |= USB_ENDPT_EPHSHK_MASK;
mjr 37:c5ac4ccf6597 313
mjr 37:c5ac4ccf6597 314 // figure the RX/TX based on the endpoint direction
mjr 37:c5ac4ccf6597 315 ctlFlags |= (IN_EP(endpoint) ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK);
mjr 35:53e1a208f582 316
mjr 37:c5ac4ccf6597 317 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 318 {
mjr 37:c5ac4ccf6597 319 // if we don't already have a buffer that's big enough, allocate a new one
mjr 37:c5ac4ccf6597 320 uint8_t *buf = endpoint_buffer[endpoint];
mjr 37:c5ac4ccf6597 321 if (maxPacket > epMaxPacket[endpoint])
mjr 37:c5ac4ccf6597 322 {
mjr 37:c5ac4ccf6597 323 // free any existing buffer
mjr 37:c5ac4ccf6597 324 if (buf != 0)
mjr 37:c5ac4ccf6597 325 free(buf);
mjr 37:c5ac4ccf6597 326
mjr 37:c5ac4ccf6597 327 // allocate a new one
mjr 37:c5ac4ccf6597 328 epMaxPacket[endpoint] = maxPacket;
mjr 37:c5ac4ccf6597 329 endpoint_buffer[endpoint] = buf = (uint8_t *)malloc(maxPacket);
mjr 37:c5ac4ccf6597 330 }
mjr 37:c5ac4ccf6597 331
mjr 37:c5ac4ccf6597 332 // Set up the BDT entry. Note that we disable the hardware double-buffering
mjr 37:c5ac4ccf6597 333 // scheme, so we only use the EVEN buffer for the endpoint.
mjr 37:c5ac4ccf6597 334 int idx = PEP_BDT_IDX(endpoint, EVEN);
mjr 37:c5ac4ccf6597 335 bdt[idx].info = 0;
mjr 37:c5ac4ccf6597 336 bdt[idx].address = (uint32_t)buf;
mjr 37:c5ac4ccf6597 337
mjr 37:c5ac4ccf6597 338 // Set the endpoint flags. Note that these bits are additive, since the
mjr 37:c5ac4ccf6597 339 // endpoint register represents the logical endpoint, which is the combination
mjr 37:c5ac4ccf6597 340 // of the physical IN and OUT endpoints.
mjr 37:c5ac4ccf6597 341 USB0->ENDPOINT[log_endpoint].ENDPT |= ctlFlags;
mjr 37:c5ac4ccf6597 342
mjr 37:c5ac4ccf6597 343 // If this is an OUT endpoint, queue the first read on the endpoint by
mjr 37:c5ac4ccf6597 344 // handing ownership of the BDT to the SIE.
mjr 37:c5ac4ccf6597 345 if (OUT_EP(endpoint))
mjr 37:c5ac4ccf6597 346 {
mjr 37:c5ac4ccf6597 347 bdt[idx].byte_count = maxPacket;
mjr 37:c5ac4ccf6597 348 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
mjr 37:c5ac4ccf6597 349 }
mjr 37:c5ac4ccf6597 350
mjr 37:c5ac4ccf6597 351 // Set DATA1 on the endpoint. For RX endpoints, we just queued up our first
mjr 37:c5ac4ccf6597 352 // read, which will always be a DATA0 packet, so the next read will use DATA1.
mjr 37:c5ac4ccf6597 353 // For TX endpoints, we always flip the bit *before* sending the packet, so
mjr 37:c5ac4ccf6597 354 // (counterintuitively) we need to set the DATA1 bit now to send DATA0 in the
mjr 37:c5ac4ccf6597 355 // next packet. So in either case, we want DATA1 initially.
mjr 37:c5ac4ccf6597 356 Data1 |= (1 << endpoint);
mjr 35:53e1a208f582 357 }
mjr 37:c5ac4ccf6597 358 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 359
mjr 36:20bb47609697 360 // success
samux 8:335f2506f422 361 return true;
samux 8:335f2506f422 362 }
samux 8:335f2506f422 363
mjr 34:884405d998bb 364 // read setup packet
mjr 36:20bb47609697 365 void USBHAL::EP0setup(uint8_t *buffer)
mjr 36:20bb47609697 366 {
mjr 34:884405d998bb 367 uint32_t sz;
mjr 37:c5ac4ccf6597 368 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 369 }
mjr 34:884405d998bb 370
mjr 36:20bb47609697 371 void USBHAL::EP0readStage(void)
mjr 36:20bb47609697 372 {
mjr 37:c5ac4ccf6597 373 if (!(bdt[0].info & BD_OWN_MASK))
mjr 37:c5ac4ccf6597 374 {
mjr 37:c5ac4ccf6597 375 Data1 &= ~1UL; // set DATA0
mjr 37:c5ac4ccf6597 376 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 37:c5ac4ccf6597 377 bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
mjr 36:20bb47609697 378 }
mjr 34:884405d998bb 379 }
mjr 34:884405d998bb 380
mjr 36:20bb47609697 381 void USBHAL::EP0read(void)
mjr 36:20bb47609697 382 {
mjr 38:072e12583e73 383 if (!(bdt[0].info & BD_OWN_MASK))
mjr 37:c5ac4ccf6597 384 bdt[0].byte_count = MAX_PACKET_SIZE_EP0;
mjr 34:884405d998bb 385 }
mjr 34:884405d998bb 386
mjr 36:20bb47609697 387 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer)
mjr 36:20bb47609697 388 {
mjr 35:53e1a208f582 389 uint32_t sz;
mjr 37:c5ac4ccf6597 390 endpointReadResult(EP0OUT, buffer, &sz);
mjr 34:884405d998bb 391 return sz;
mjr 34:884405d998bb 392 }
mjr 34:884405d998bb 393
mjr 37:c5ac4ccf6597 394 void USBHAL::EP0write(uint8_t *buffer, uint32_t size)
mjr 37:c5ac4ccf6597 395 {
mjr 37:c5ac4ccf6597 396 endpointWrite(EP0IN, buffer, size);
mjr 34:884405d998bb 397 }
mjr 34:884405d998bb 398
mjr 37:c5ac4ccf6597 399 void USBHAL::EP0getWriteResult(void)
mjr 37:c5ac4ccf6597 400 {
mjr 34:884405d998bb 401 }
mjr 34:884405d998bb 402
mjr 36:20bb47609697 403 void USBHAL::EP0stall(void)
mjr 36:20bb47609697 404 {
mjr 37:c5ac4ccf6597 405 printd("EP0 stall!\r\n");
mjr 34:884405d998bb 406 stallEndpoint(EP0OUT);
mjr 34:884405d998bb 407 }
mjr 34:884405d998bb 408
mjr 35:53e1a208f582 409 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize)
mjr 34:884405d998bb 410 {
mjr 37:c5ac4ccf6597 411 // We always start a new read when we fetch the result of the
mjr 37:c5ac4ccf6597 412 // previous read, so we don't have to do anything here. Simply
mjr 37:c5ac4ccf6597 413 // indicate that the read is pending so that the caller can proceed
mjr 37:c5ac4ccf6597 414 // to check the results.
mjr 34:884405d998bb 415 return EP_PENDING;
mjr 34:884405d998bb 416 }
mjr 34:884405d998bb 417
mjr 36:20bb47609697 418 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t *buffer, uint32_t *bytesRead)
mjr 34:884405d998bb 419 {
mjr 37:c5ac4ccf6597 420 // validate the endpoint number and direction
mjr 37:c5ac4ccf6597 421 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !OUT_EP(endpoint))
mjr 34:884405d998bb 422 return EP_INVALID;
mjr 34:884405d998bb 423
mjr 37:c5ac4ccf6597 424 // get the logical endpoint
mjr 34:884405d998bb 425 uint32_t log_endpoint = PHY_TO_LOG(endpoint);
mjr 37:c5ac4ccf6597 426
mjr 37:c5ac4ccf6597 427 // get the mode - it's isochronous if it doesn't have the handshake flag
mjr 37:c5ac4ccf6597 428 bool iso = !(USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK);
mjr 37:c5ac4ccf6597 429
mjr 35:53e1a208f582 430 // get the BDT index
mjr 35:53e1a208f582 431 int idx = EP_BDT_IDX(log_endpoint, RX, 0);
mjr 37:c5ac4ccf6597 432
mjr 37:c5ac4ccf6597 433 // If the "complete" flag isn't set, the read is still pending in the SIE.
mjr 37:c5ac4ccf6597 434 // This doesn't apply the isochronous endpoints, since we don't get TOKDNE
mjr 37:c5ac4ccf6597 435 // interrupts on those (we use the SOF signal instead). It also doesn't
mjr 37:c5ac4ccf6597 436 // apply to endpoint 0, since that doesn't use the epComplete mechanism
mjr 37:c5ac4ccf6597 437 // at all (necessary because we handle all transactions on this endpoint
mjr 37:c5ac4ccf6597 438 // in IRQ context). For EP0, just make sure the hardware doesn't still
mjr 37:c5ac4ccf6597 439 // own the BDT - if it does, the last read hasn't completed yet.
mjr 37:c5ac4ccf6597 440 if (log_endpoint == 0)
mjr 37:c5ac4ccf6597 441 {
mjr 37:c5ac4ccf6597 442 // control endpoint - just make sure we own the BDT
mjr 37:c5ac4ccf6597 443 if (bdt[idx].info & BD_OWN_MASK)
mjr 37:c5ac4ccf6597 444 return EP_PENDING;
mjr 37:c5ac4ccf6597 445 }
mjr 37:c5ac4ccf6597 446 else if (!iso && !(epComplete & EP(endpoint)))
mjr 36:20bb47609697 447 return EP_PENDING;
mjr 37:c5ac4ccf6597 448
mjr 37:c5ac4ccf6597 449 // get the buffer
mjr 37:c5ac4ccf6597 450 uint8_t *ep_buf = endpoint_buffer[endpoint];
mjr 34:884405d998bb 451
mjr 37:c5ac4ccf6597 452 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 453 {
mjr 37:c5ac4ccf6597 454 // get the packet size from the BDT
mjr 37:c5ac4ccf6597 455 uint32_t sz = bdt[idx].byte_count;
mjr 36:20bb47609697 456
mjr 37:c5ac4ccf6597 457 // note if it's a SETUP token
mjr 37:c5ac4ccf6597 458 bool setup = (log_endpoint == 0 && TOK_PID(idx) == SETUP_TOKEN);
mjr 34:884405d998bb 459
mjr 37:c5ac4ccf6597 460 // copy the data
mjr 37:c5ac4ccf6597 461 memcpy(buffer, ep_buf, sz);
mjr 37:c5ac4ccf6597 462 *bytesRead = sz;
mjr 37:c5ac4ccf6597 463
mjr 37:c5ac4ccf6597 464 // Figure the DATA0/DATA1 bit for the next packet received on this
mjr 37:c5ac4ccf6597 465 // endpoint. The bit normally toggles on each packet, but it's
mjr 37:c5ac4ccf6597 466 // special for SETUP packets on endpoint 0. The next OUT packet
mjr 37:c5ac4ccf6597 467 // after a SETUP packet with no data stage is always DATA0, even
mjr 37:c5ac4ccf6597 468 // if the SETUP packet was also DATA0.
mjr 37:c5ac4ccf6597 469 if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1))
mjr 37:c5ac4ccf6597 470 {
mjr 37:c5ac4ccf6597 471 if (setup && (buffer[6] == 0)) // if no setup data stage,
mjr 37:c5ac4ccf6597 472 Data1 &= ~1UL; // set DATA0
mjr 37:c5ac4ccf6597 473 else
mjr 37:c5ac4ccf6597 474 Data1 ^= (1 << endpoint); // otherwise just toggle the last bit
mjr 35:53e1a208f582 475 }
mjr 35:53e1a208f582 476
mjr 37:c5ac4ccf6597 477 // set up the BDT entry to receive the next packet, and hand it to the SIE to fill
mjr 37:c5ac4ccf6597 478 bdt[idx].byte_count = epMaxPacket[endpoint];
mjr 37:c5ac4ccf6597 479 bdt[idx].info = BD_DTS_MASK | BD_OWN_MASK | ((Data1 >> endpoint) << 6);
mjr 37:c5ac4ccf6597 480
mjr 37:c5ac4ccf6597 481 // clear the SUSPEND TOKEN BUSY flag to allow the SIE to continue processing tokens
mjr 36:20bb47609697 482 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 34:884405d998bb 483
mjr 37:c5ac4ccf6597 484 // clear the completion flag
mjr 37:c5ac4ccf6597 485 epComplete &= ~EP(endpoint);
mjr 37:c5ac4ccf6597 486 }
mjr 37:c5ac4ccf6597 487 EXIT_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 488
mjr 37:c5ac4ccf6597 489 // the read is completed
samux 8:335f2506f422 490 return EP_COMPLETED;
samux 8:335f2506f422 491 }
samux 8:335f2506f422 492
mjr 35:53e1a208f582 493 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size)
mjr 34:884405d998bb 494 {
mjr 37:c5ac4ccf6597 495 // validate the endpoint number and direction
mjr 37:c5ac4ccf6597 496 if (endpoint >= NUMBER_OF_PHYSICAL_ENDPOINTS || !IN_EP(endpoint))
samux 8:335f2506f422 497 return EP_INVALID;
mjr 35:53e1a208f582 498
mjr 37:c5ac4ccf6597 499 // get the BDT index
mjr 35:53e1a208f582 500 int idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
mjr 37:c5ac4ccf6597 501
mjr 37:c5ac4ccf6597 502 // get the buffer pointer
mjr 37:c5ac4ccf6597 503 uint8_t *buf = endpoint_buffer[endpoint];
mbed_official 20:d38b72fed893 504
mjr 37:c5ac4ccf6597 505 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 506 {
mjr 37:c5ac4ccf6597 507 // copy the data to the hardware buffer
mjr 37:c5ac4ccf6597 508 bdt[idx].byte_count = size;
mjr 37:c5ac4ccf6597 509 memcpy(buf, data, size);
mbed_official 20:d38b72fed893 510
mjr 37:c5ac4ccf6597 511 // flip the DATA1 bit before sending
mjr 37:c5ac4ccf6597 512 Data1 ^= (1 << endpoint);
mjr 35:53e1a208f582 513
mjr 37:c5ac4ccf6597 514 // hand the BDT to the SIE hardware, and set the current DATA1 bit
mjr 37:c5ac4ccf6597 515 bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | ((Data1 >> endpoint) << 6);
mjr 37:c5ac4ccf6597 516 }
mjr 37:c5ac4ccf6597 517 EXIT_CRITICAL_SECTION
mjr 35:53e1a208f582 518
mjr 37:c5ac4ccf6597 519 // the operation is now pending
samux 8:335f2506f422 520 return EP_PENDING;
samux 8:335f2506f422 521 }
samux 8:335f2506f422 522
mjr 35:53e1a208f582 523 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint)
mjr 34:884405d998bb 524 {
mjr 37:c5ac4ccf6597 525 EP_STATUS result = EP_PENDING;
mjr 37:c5ac4ccf6597 526
mjr 37:c5ac4ccf6597 527 ENTER_CRITICAL_SECTION
mjr 37:c5ac4ccf6597 528 {
mjr 37:c5ac4ccf6597 529 if (epComplete & EP(endpoint)) {
mjr 37:c5ac4ccf6597 530 epComplete &= ~EP(endpoint);
mjr 37:c5ac4ccf6597 531 result = EP_COMPLETED;
mjr 37:c5ac4ccf6597 532 }
samux 8:335f2506f422 533 }
mjr 37:c5ac4ccf6597 534 EXIT_CRITICAL_SECTION
mjr 36:20bb47609697 535
mjr 37:c5ac4ccf6597 536 return result;
samux 8:335f2506f422 537 }
samux 8:335f2506f422 538
mjr 36:20bb47609697 539 void USBHAL::stallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 540 {
mjr 37:c5ac4ccf6597 541 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
samux 8:335f2506f422 542 }
samux 8:335f2506f422 543
mjr 36:20bb47609697 544 void USBHAL::unstallEndpoint(uint8_t endpoint)
mjr 36:20bb47609697 545 {
mjr 37:c5ac4ccf6597 546 printd("unstall endpoint %d %s\r\n", endpoint>>1,endpoint&1?"TX":"RX");
mjr 37:c5ac4ccf6597 547 ENTER_CRITICAL_SECTION
mjr 36:20bb47609697 548 {
mjr 37:c5ac4ccf6597 549 USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
mjr 37:c5ac4ccf6597 550 int idx = PEP_BDT_IDX(endpoint, 0);
mjr 37:c5ac4ccf6597 551 bdt[idx].info &= ~(BD_OWN_MASK | BD_STALL_MASK | BD_DATA01_MASK);
mjr 37:c5ac4ccf6597 552 Data1 &= ~(1 << endpoint);
mjr 36:20bb47609697 553 }
mjr 37:c5ac4ccf6597 554 EXIT_CRITICAL_SECTION
samux 8:335f2506f422 555 }
samux 8:335f2506f422 556
mjr 36:20bb47609697 557 bool USBHAL::getEndpointStallState(uint8_t endpoint)
mjr 36:20bb47609697 558 {
mjr 37:c5ac4ccf6597 559 uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
mjr 37:c5ac4ccf6597 560 return (stall) ? true : false;
samux 8:335f2506f422 561 }
samux 8:335f2506f422 562
mjr 37:c5ac4ccf6597 563 void USBHAL::remoteWakeup(void)
mjr 37:c5ac4ccf6597 564 {
samux 8:335f2506f422 565 // [TODO]
samux 8:335f2506f422 566 }
samux 8:335f2506f422 567
mjr 38:072e12583e73 568 // Control endpoint OUT/SETUP callback. Called from ISR context only.
mjr 38:072e12583e73 569 bool USBHAL::EP0_OUT_callback(void)
mjr 38:072e12583e73 570 {
mjr 38:072e12583e73 571 // setup packet
mjr 38:072e12583e73 572 int idx = PEP_BDT_IDX(EP0OUT, EVEN);
mjr 38:072e12583e73 573 if (TOK_PID(idx) == SETUP_TOKEN)
mjr 38:072e12583e73 574 {
mjr 38:072e12583e73 575 // SETUP packet on EP0
mjr 38:072e12583e73 576
mjr 38:072e12583e73 577 // Set DATA1 on Control IN endpoint for next packet (recall that we
mjr 38:072e12583e73 578 // toggle the bit before a send, so clearing the bit sets DATA1 for
mjr 38:072e12583e73 579 // the next send). If there's a data IN stage for the SETUP packet,
mjr 38:072e12583e73 580 // it must always be DATA1, regardless of the prior state of the IN
mjr 38:072e12583e73 581 // endpoint.
mjr 38:072e12583e73 582 Data1 &= ~0x02;
mjr 38:072e12583e73 583
mjr 38:072e12583e73 584 // make sure we own the IN enpdoint now, in preparation for the reply
mjr 38:072e12583e73 585 bdt[PEP_BDT_IDX(EP0IN, EVEN)].info &= ~BD_OWN_MASK;
mjr 38:072e12583e73 586
mjr 38:072e12583e73 587 // process the SETUP packet through the portable protocol code
mjr 38:072e12583e73 588 EP0setupCallback();
mjr 38:072e12583e73 589 }
mjr 38:072e12583e73 590 else
mjr 38:072e12583e73 591 {
mjr 38:072e12583e73 592 // OUT packet on EP0 - process it through the protocol code
mjr 38:072e12583e73 593 EP0out();
mjr 38:072e12583e73 594 }
mjr 38:072e12583e73 595
mjr 38:072e12583e73 596 // success
mjr 38:072e12583e73 597 return true;
mjr 38:072e12583e73 598 }
mjr 38:072e12583e73 599
mjr 38:072e12583e73 600 // Control endpoint IN packet handler. This is only called from ISR context.
mjr 38:072e12583e73 601 bool USBHAL::EP0_IN_callback(void)
mjr 38:072e12583e73 602 {
mjr 38:072e12583e73 603 // process the IN packet through the portable protocol code
mjr 38:072e12583e73 604 EP0in();
mjr 38:072e12583e73 605
mjr 38:072e12583e73 606 // If we have a SET ADDRESS command outstanding, put it into effect now.
mjr 38:072e12583e73 607 // The USB spec requires an address change to be made immediately (within
mjr 38:072e12583e73 608 // 2ms) after the reply to the SET ADDRESS SETUP packet. If the flag is
mjr 38:072e12583e73 609 // set, it means that the EP0in() call above just sent the response, so
mjr 38:072e12583e73 610 // now is the time to make the address change in the SIE hardware register.
mjr 38:072e12583e73 611 if (set_addr == 1)
mjr 38:072e12583e73 612 {
mjr 38:072e12583e73 613 USB0->ADDR = addr & 0x7F;
mjr 38:072e12583e73 614 set_addr = 0;
mjr 38:072e12583e73 615 }
mjr 38:072e12583e73 616
mjr 38:072e12583e73 617 // success
mjr 38:072e12583e73 618 return true;
mjr 38:072e12583e73 619 }
mjr 38:072e12583e73 620
samux 8:335f2506f422 621
mjr 37:c5ac4ccf6597 622 void USBHAL::_usbisr(void)
mjr 37:c5ac4ccf6597 623 {
mjr 37:c5ac4ccf6597 624 inIRQ = true;
samux 8:335f2506f422 625 instance->usbisr();
mjr 37:c5ac4ccf6597 626 inIRQ = false;
samux 8:335f2506f422 627 }
samux 8:335f2506f422 628
samux 8:335f2506f422 629
mjr 36:20bb47609697 630 void USBHAL::usbisr(void)
mjr 36:20bb47609697 631 {
samux 8:335f2506f422 632 uint8_t i;
samux 8:335f2506f422 633 uint8_t istat = USB0->ISTAT;
samux 8:335f2506f422 634
samux 8:335f2506f422 635 // reset interrupt
mjr 37:c5ac4ccf6597 636 if (istat & USB_ISTAT_USBRST_MASK) {
mjr 37:c5ac4ccf6597 637
samux 8:335f2506f422 638 // disable all endpt
mjr 37:c5ac4ccf6597 639 for(i = 0 ; i < 16 ; i++)
samux 8:335f2506f422 640 USB0->ENDPOINT[i].ENDPT = 0x00;
samux 8:335f2506f422 641
samux 8:335f2506f422 642 // enable control endpoint
samux 8:335f2506f422 643 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 644 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
samux 8:335f2506f422 645
samux 8:335f2506f422 646 Data1 = 0x55555555;
samux 8:335f2506f422 647 USB0->CTL |= USB_CTL_ODDRST_MASK;
samux 8:335f2506f422 648
mjr 37:c5ac4ccf6597 649 USB0->ISTAT = 0xFF; // clear all interrupt status flags
mjr 37:c5ac4ccf6597 650 USB0->ERRSTAT = 0xFF; // clear all error flags
mjr 37:c5ac4ccf6597 651 USB0->ERREN = 0xFF; // enable error interrupt sources
samux 8:335f2506f422 652 USB0->ADDR = 0x00; // set default address
mjr 28:0596f144dad5 653
mjr 31:81f57ea86f8f 654 // notify upper layers of the bus reset
mjr 31:81f57ea86f8f 655 busReset();
mjr 31:81f57ea86f8f 656
mjr 31:81f57ea86f8f 657 // we're not suspended
mjr 28:0596f144dad5 658 suspendStateChanged(0);
mjr 37:c5ac4ccf6597 659
mjr 37:c5ac4ccf6597 660 // return now - do no more processing on a RESET interrupt
samux 8:335f2506f422 661 return;
samux 8:335f2506f422 662 }
samux 8:335f2506f422 663
samux 8:335f2506f422 664 // resume interrupt
mjr 37:c5ac4ccf6597 665 if (istat & USB_ISTAT_RESUME_MASK)
mjr 37:c5ac4ccf6597 666 {
mjr 36:20bb47609697 667 suspendStateChanged(0);
samux 8:335f2506f422 668 USB0->ISTAT = USB_ISTAT_RESUME_MASK;
samux 8:335f2506f422 669 }
samux 8:335f2506f422 670
samux 8:335f2506f422 671 // SOF interrupt
mjr 37:c5ac4ccf6597 672 if (istat & USB_ISTAT_SOFTOK_MASK)
mjr 37:c5ac4ccf6597 673 {
mjr 37:c5ac4ccf6597 674 // Read frame number and signal the SOF event to the callback
samux 8:335f2506f422 675 SOF(frameNumber());
mjr 36:20bb47609697 676 USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
samux 8:335f2506f422 677 }
mbed_official 20:d38b72fed893 678
samux 8:335f2506f422 679 // stall interrupt
mjr 36:20bb47609697 680 if (istat & USB_ISTAT_STALL_MASK)
mjr 36:20bb47609697 681 {
mjr 37:c5ac4ccf6597 682 // if the control endpoint (EP 0) is stalled, unstall it
mjr 37:c5ac4ccf6597 683 if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK)
mjr 37:c5ac4ccf6597 684 {
mjr 37:c5ac4ccf6597 685 unstallEndpoint(EP0OUT);
mjr 36:20bb47609697 686 unstallEndpoint(EP0IN);
mjr 36:20bb47609697 687 }
mjr 37:c5ac4ccf6597 688
mjr 37:c5ac4ccf6597 689 // clear the SUSPEND flag to allow token processing to continue
mjr 36:20bb47609697 690 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 37:c5ac4ccf6597 691 USB0->ISTAT |= USB_ISTAT_STALL_MASK;
samux 8:335f2506f422 692 }
samux 8:335f2506f422 693
mjr 37:c5ac4ccf6597 694 // token interrupt
mjr 37:c5ac4ccf6597 695 if (istat & USB_ISTAT_TOKDNE_MASK)
mjr 36:20bb47609697 696 {
mjr 37:c5ac4ccf6597 697 uint32_t num = (USB0->STAT >> 4) & 0x0F;
mjr 37:c5ac4ccf6597 698 uint32_t dir = (USB0->STAT >> 3) & 0x01;
mjr 34:884405d998bb 699 int endpoint = (num << 1) | dir;
mjr 38:072e12583e73 700 // uint32_t ev_odd = (USB0->STAT >> 2) & 0x01; // we only use EVEN buffers, so this is always 0
samux 8:335f2506f422 701
mjr 38:072e12583e73 702 // set the Completed bit for the endpoint to indicate that we've
mjr 38:072e12583e73 703 // finished this send/receive request
mjr 38:072e12583e73 704 epComplete |= EP(endpoint);
mjr 38:072e12583e73 705
mjr 38:072e12583e73 706 // Call the endpoint packet callback. If that returns true, it means
mjr 38:072e12583e73 707 // that the callback handled the packet. That consumes the packet, so
mjr 38:072e12583e73 708 // clear the Completed flag to indicate that we're on to the next
mjr 38:072e12583e73 709 // transaction on the endpoint.
mjr 38:072e12583e73 710 if ((instance->*(epCallback[endpoint]))())
mjr 38:072e12583e73 711 epComplete &= ~EP(endpoint);
mjr 37:c5ac4ccf6597 712
mjr 38:072e12583e73 713 // allow token processing to resume
mjr 37:c5ac4ccf6597 714 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 38:072e12583e73 715
mjr 38:072e12583e73 716 // reset the TOKDNE interrupt status flag
samux 8:335f2506f422 717 USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
samux 8:335f2506f422 718 }
mbed_official 20:d38b72fed893 719
samux 8:335f2506f422 720 // sleep interrupt
mjr 36:20bb47609697 721 if (istat & USB_ISTAT_SLEEP_MASK)
mjr 36:20bb47609697 722 {
mjr 28:0596f144dad5 723 suspendStateChanged(1);
mjr 37:c5ac4ccf6597 724 USB0->ISTAT |= USB_ISTAT_SLEEP_MASK;
mbed_official 20:d38b72fed893 725 }
samux 8:335f2506f422 726
samux 8:335f2506f422 727 // error interrupt
mjr 36:20bb47609697 728 if (istat & USB_ISTAT_ERROR_MASK)
mjr 36:20bb47609697 729 {
mjr 37:c5ac4ccf6597 730 // reset all error status bits, and clear the SUSPEND flag to allow
mjr 37:c5ac4ccf6597 731 // token processing to continue
mjr 37:c5ac4ccf6597 732 USB0->ERRSTAT = 0xFF;
mjr 36:20bb47609697 733 USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
mjr 37:c5ac4ccf6597 734 USB0->ISTAT |= USB_ISTAT_ERROR_MASK;
samux 8:335f2506f422 735 }
samux 8:335f2506f422 736 }
samux 8:335f2506f422 737
mjr 37:c5ac4ccf6597 738 void USBDeviceStatusDump() { }
mjr 36:20bb47609697 739
mjr 36:20bb47609697 740 #endif