ArduinoCore-samd/cores/arduino/USB/USBCore.cpp
Henry Gabryjelski 8afd0523b1 Fix unintended fall-through
The compiler found this, not me.  Building with
all warnings enabled is recommended.
2020-01-16 16:15:34 -08:00

1042 lines
24 KiB
C++

/*
Copyright (c) 2016 Arduino LLC. All right reserved.
SAMD51 support added by Adafruit - Copyright (c) 2018 Dean Miller for Adafruit Industries
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef USE_TINYUSB
#if defined(USBCON)
#include <Arduino.h>
// there are ~slight~ CMSIS differences :/
#ifdef __SAMR21G18A__
#include "SAMR21_USBDevice.h"
#else
#include "SAMD21_USBDevice.h"
#endif
#include "PluggableUSB.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#ifdef __SAMR21G18A__
USBDevice_SAMR21G18x usbd;
#else
USBDevice_SAMD21G18x usbd;
#endif
/** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */
#define TX_RX_LED_PULSE_MS 100
#ifdef PIN_LED_TXL
static volatile uint8_t txLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */
#endif
#ifdef PIN_LED_RXL
static volatile uint8_t rxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */
#endif
static char isRemoteWakeUpEnabled = 0;
static char isEndpointHalt = 0;
extern void (*gpf_isr)(void);
// USB_Handler ISR
extern "C" void UDD_Handler(void) {
USBDevice.ISRHandler();
}
const uint16_t STRING_LANGUAGE[2] = {
(3<<8) | (2+2),
0x0409 // English
};
#ifndef USB_PRODUCT
// If no product is provided, use USB IO Board
#define USB_PRODUCT "USB IO Board"
#endif
const uint8_t STRING_PRODUCT[] = USB_PRODUCT;
#if USB_VID == 0x2341
# if defined(USB_MANUFACTURER)
# undef USB_MANUFACTURER
# endif
# define USB_MANUFACTURER "Arduino LLC"
#elif !defined(USB_MANUFACTURER)
// Fall through to unknown if no manufacturer name was provided in a macro
# define USB_MANUFACTURER "Unknown"
#endif
const uint8_t STRING_MANUFACTURER[] = USB_MANUFACTURER;
// DEVICE DESCRIPTOR
const DeviceDescriptor USB_DeviceDescriptorB = D_DEVICE(0xEF, 0x02, 0x01, 64, USB_VID, USB_PID, 0x100, IMANUFACTURER, IPRODUCT, ISERIAL, 1);
const DeviceDescriptor USB_DeviceDescriptor = D_DEVICE(0x00, 0x00, 0x00, 64, USB_VID, USB_PID, 0x100, IMANUFACTURER, IPRODUCT, ISERIAL, 1);
//==================================================================
volatile uint32_t _usbConfiguration = 0;
volatile uint32_t _usbSetInterface = 0;
static __attribute__((__aligned__(4))) //__attribute__((__section__(".bss_hram0")))
uint8_t udd_ep_out_cache_buffer[7][64];
static __attribute__((__aligned__(4))) //__attribute__((__section__(".bss_hram0")))
uint8_t udd_ep_in_cache_buffer[7][64];
// Some EP are handled using EPHanlders.
// Possibly all the sparse EP handling subroutines will be
// converted into reusable EPHandlers in the future.
static EPHandler *epHandlers[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
//==================================================================
// Send a USB descriptor string. The string is stored as a
// plain ASCII string but is sent out as UTF-16 with the
// correct 2-byte prefix
bool USBDeviceClass::sendStringDescriptor(const uint8_t *string, uint8_t maxlen)
{
if (maxlen < 2)
return false;
uint8_t buffer[maxlen];
buffer[0] = strlen((const char*)string) * 2 + 2;
buffer[1] = 0x03;
uint8_t i;
for (i = 2; i < maxlen && *string; i++) {
buffer[i++] = *string++;
if (i == maxlen) break;
buffer[i] = 0;
}
return USBDevice.sendControl(buffer, i);
}
bool _dry_run = false;
bool _pack_message = false;
uint16_t _pack_size = 0;
uint8_t _pack_buffer[256];
void USBDeviceClass::packMessages(bool val)
{
if (val) {
_pack_message = true;
_pack_size = 0;
} else {
_pack_message = false;
sendControl(_pack_buffer, _pack_size);
}
}
uint8_t USBDeviceClass::SendInterfaces(uint32_t* total)
{
uint8_t interfaces = 0;
#if defined(CDC_ENABLED)
total[0] += CDC_GetInterface(&interfaces);
#endif
#ifdef PLUGGABLE_USB_ENABLED
total[0] += PluggableUSB().getInterface(&interfaces);
#endif
return interfaces;
}
// Construct a dynamic configuration descriptor
// This really needs dynamic endpoint allocation etc
uint32_t USBDeviceClass::sendConfiguration(uint32_t maxlen)
{
uint32_t total = 0;
// Count and measure interfaces
_dry_run = true;
uint8_t interfaces = SendInterfaces(&total);
_Pragma("pack(1)")
ConfigDescriptor config = D_CONFIG((uint16_t)(total + sizeof(ConfigDescriptor)), interfaces);
_Pragma("pack()")
// Now send them
_dry_run = false;
if (maxlen == sizeof(ConfigDescriptor)) {
sendControl(&config, sizeof(ConfigDescriptor));
return true;
}
total = 0;
packMessages(true);
sendControl(&config, sizeof(ConfigDescriptor));
SendInterfaces(&total);
packMessages(false);
return true;
}
static void utox8(uint32_t val, char* s) {
for (int i = 0; i < 8; i++) {
int d = val & 0XF;
val = (val >> 4);
s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d;
}
}
bool USBDeviceClass::sendDescriptor(USBSetup &setup)
{
uint8_t t = setup.wValueH;
uint8_t desc_length = 0;
bool _cdcComposite;
int ret;
const uint8_t *desc_addr = 0;
if (t == USB_CONFIGURATION_DESCRIPTOR_TYPE)
{
return USBDevice.sendConfiguration(setup.wLength);
}
#ifdef PLUGGABLE_USB_ENABLED
ret = PluggableUSB().getDescriptor(setup);
if (ret != 0) {
return (ret > 0 ? true : false);
}
#endif
if (t == USB_DEVICE_DESCRIPTOR_TYPE)
{
if (setup.wLength == 8)
_cdcComposite = 1;
desc_addr = _cdcComposite ? (const uint8_t*)&USB_DeviceDescriptorB : (const uint8_t*)&USB_DeviceDescriptor;
if (*desc_addr > setup.wLength) {
desc_length = setup.wLength;
}
}
else if (USB_STRING_DESCRIPTOR_TYPE == t)
{
if (setup.wValueL == 0) {
desc_addr = (const uint8_t*)&STRING_LANGUAGE;
}
else if (setup.wValueL == IPRODUCT) {
return sendStringDescriptor(STRING_PRODUCT, setup.wLength);
}
else if (setup.wValueL == IMANUFACTURER) {
return sendStringDescriptor(STRING_MANUFACTURER, setup.wLength);
}
else if (setup.wValueL == ISERIAL) {
#ifdef PLUGGABLE_USB_ENABLED
#ifdef __SAMD51__
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x008061FC)
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x00806010)
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x00806014)
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x00806018)
#else // samd21
// from section 9.3.3 of the datasheet
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C)
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040)
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044)
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048)
#endif
char name[ISERIAL_MAX_LEN];
utox8(SERIAL_NUMBER_WORD_0, &name[0]);
utox8(SERIAL_NUMBER_WORD_1, &name[8]);
utox8(SERIAL_NUMBER_WORD_2, &name[16]);
utox8(SERIAL_NUMBER_WORD_3, &name[24]);
name[32] = '\0';
return sendStringDescriptor((uint8_t*)name, setup.wLength);
#endif
}
else {
return false;
}
if (*desc_addr > setup.wLength) {
desc_length = setup.wLength;
}
}
else
{
}
if (desc_addr == 0) {
return false;
}
if (desc_length == 0) {
desc_length = *desc_addr;
}
sendControl(desc_addr, desc_length);
return true;
}
void USBDeviceClass::standby() {
usbd.noRunInStandby();
}
void USBDeviceClass::handleEndpoint(uint8_t ep)
{
#if defined(CDC_ENABLED)
if (ep == CDC_ENDPOINT_IN)
{
// NAK on endpoint IN, the bank is not yet filled in.
usbd.epBank1ResetReady(CDC_ENDPOINT_IN);
usbd.epBank1AckTransferComplete(CDC_ENDPOINT_IN);
}
if (ep == CDC_ENDPOINT_ACM)
{
// NAK on endpoint IN, the bank is not yet filled in.
usbd.epBank1ResetReady(CDC_ENDPOINT_ACM);
usbd.epBank1AckTransferComplete(CDC_ENDPOINT_ACM);
}
#endif
#if defined(PLUGGABLE_USB_ENABLED)
// Empty
#endif
}
void USBDeviceClass::init()
{
#ifdef PIN_LED_TXL
txLEDPulse = 0;
pinMode(PIN_LED_TXL, OUTPUT);
digitalWrite(PIN_LED_TXL, HIGH);
#endif
#ifdef PIN_LED_RXL
rxLEDPulse = 0;
pinMode(PIN_LED_RXL, OUTPUT);
digitalWrite(PIN_LED_RXL, HIGH);
#endif
/* Enable USB clock */
#if defined(__SAMD51__)
MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
// Set up the USB DP/DN pins
PORT->Group[0].PINCFG[PIN_PA24H_USB_DM].bit.PMUXEN = 1;
PORT->Group[0].PMUX[PIN_PA24H_USB_DM/2].reg &= ~(0xF << (4 * (PIN_PA24H_USB_DM & 0x01u)));
PORT->Group[0].PMUX[PIN_PA24H_USB_DM/2].reg |= MUX_PA24H_USB_DM << (4 * (PIN_PA24H_USB_DM & 0x01u));
PORT->Group[0].PINCFG[PIN_PA25H_USB_DP].bit.PMUXEN = 1;
PORT->Group[0].PMUX[PIN_PA25H_USB_DP/2].reg &= ~(0xF << (4 * (PIN_PA25H_USB_DP & 0x01u)));
PORT->Group[0].PMUX[PIN_PA25H_USB_DP/2].reg |= MUX_PA25H_USB_DP << (4 * (PIN_PA25H_USB_DP & 0x01u));
GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
#else
PM->APBBMASK.reg |= PM_APBBMASK_USB;
// Set up the USB DP/DN pins
PORT->Group[0].PINCFG[PIN_PA24G_USB_DM].bit.PMUXEN = 1;
PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg &= ~(0xF << (4 * (PIN_PA24G_USB_DM & 0x01u)));
PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg |= MUX_PA24G_USB_DM << (4 * (PIN_PA24G_USB_DM & 0x01u));
PORT->Group[0].PINCFG[PIN_PA25G_USB_DP].bit.PMUXEN = 1;
PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg &= ~(0xF << (4 * (PIN_PA25G_USB_DP & 0x01u)));
PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg |= MUX_PA25G_USB_DP << (4 * (PIN_PA25G_USB_DP & 0x01u));
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference)
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(6) | // Generic Clock Multiplexer 6
GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
GCLK_CLKCTRL_CLKEN;
while (GCLK->STATUS.bit.SYNCBUSY)
;
#endif
USB_SetHandler(&UDD_Handler);
// Reset USB Device
usbd.reset();
usbd.calibrate();
usbd.setUSBDeviceMode();
usbd.runInStandby();
usbd.setFullSpeed();
// Configure interrupts
#if defined(__SAMD51__)
/* Attach to the USB host */
NVIC_SetPriority(USB_0_IRQn, 0UL);
NVIC_SetPriority(USB_1_IRQn, 0UL);
NVIC_SetPriority(USB_2_IRQn, 0UL);
NVIC_SetPriority(USB_3_IRQn, 0UL);
NVIC_EnableIRQ(USB_0_IRQn);
NVIC_EnableIRQ(USB_1_IRQn);
NVIC_EnableIRQ(USB_2_IRQn);
NVIC_EnableIRQ(USB_3_IRQn);
#else
NVIC_SetPriority((IRQn_Type) USB_IRQn, 0UL);
NVIC_EnableIRQ((IRQn_Type) USB_IRQn);
#endif
usbd.enable();
initialized = true;
}
bool USBDeviceClass::attach()
{
if (!initialized)
return false;
usbd.attach();
usbd.enableEndOfResetInterrupt();
usbd.enableStartOfFrameInterrupt();
_usbConfiguration = 0;
return true;
}
void USBDeviceClass::setAddress(uint32_t addr)
{
usbd.epBank1SetByteCount(0, 0);
usbd.epBank1AckTransferComplete(0);
// RAM buffer is full, we can send data (IN)
usbd.epBank1SetReady(0);
// Wait for transfer to complete
while (!usbd.epBank1IsTransferComplete(0)) {}
// Set USB address to addr
USB->DEVICE.DADD.bit.DADD = addr; // Address
USB->DEVICE.DADD.bit.ADDEN = 1; // Enable
}
bool USBDeviceClass::detach()
{
if (!initialized)
return false;
usbd.detach();
return true;
}
bool USBDeviceClass::end() {
if (!initialized)
return false;
usbd.disable();
return true;
}
bool USBDeviceClass::configured()
{
return _usbConfiguration != 0;
}
bool USBDeviceClass::handleClassInterfaceSetup(USBSetup& setup)
{
uint8_t i = setup.wIndex;
#if defined(CDC_ENABLED)
if (CDC_ACM_INTERFACE == i)
{
if (CDC_Setup(setup) == false) {
sendZlp(0);
}
return true;
}
#endif
#if defined(PLUGGABLE_USB_ENABLED)
bool ret = PluggableUSB().setup(setup);
if ( ret == false) {
sendZlp(0);
}
return ret;
#endif
return false;
}
uint32_t EndPoints[] =
{
USB_ENDPOINT_TYPE_CONTROL,
#ifdef CDC_ENABLED
USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_IN(0), // CDC_ENDPOINT_ACM
USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_OUT(0), // CDC_ENDPOINT_OUT
USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0), // CDC_ENDPOINT_IN
#endif
#ifdef PLUGGABLE_USB_ENABLED
//allocate 6 endpoints and remove const so they can be changed by the user
0,
0,
0,
0,
0,
0,
#endif
};
void USBDeviceClass::initEndpoints() {
for (uint8_t i = 1; i < sizeof(EndPoints) && EndPoints[i] != 0; i++) {
initEP(i, EndPoints[i]);
}
}
void USBDeviceClass::initEP(uint32_t ep, uint32_t config)
{
if (config == (USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_IN(0)))
{
usbd.epBank1SetSize(ep, 64);
usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]);
usbd.epBank1SetType(ep, 4); // INTERRUPT IN
}
else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_OUT(0)))
{
if (epHandlers[ep] != NULL) {
delete (DoubleBufferedEPOutHandler*)epHandlers[ep];
}
epHandlers[ep] = new DoubleBufferedEPOutHandler(usbd, ep, 256);
}
else if (config == (USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_OUT(0)))
{
if(epHandlers[ep]){
epHandlers[ep]->init();
}
}
else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0)))
{
usbd.epBank1SetSize(ep, 64);
usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]);
// NAK on endpoint IN, the bank is not yet filled in.
usbd.epBank1ResetReady(ep);
usbd.epBank1SetType(ep, 3); // BULK IN
}
else if (config == USB_ENDPOINT_TYPE_CONTROL)
{
// Setup Control OUT
usbd.epBank0SetSize(ep, 64);
usbd.epBank0SetAddress(ep, &udd_ep_out_cache_buffer[ep]);
usbd.epBank0SetType(ep, 1); // CONTROL OUT / SETUP
// Setup Control IN
usbd.epBank1SetSize(ep, 64);
usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[0]);
usbd.epBank1SetType(ep, 1); // CONTROL IN
// Release OUT EP
usbd.epBank0SetMultiPacketSize(ep, 64);
usbd.epBank0SetByteCount(ep, 0);
// NAK on endpoint OUT, the bank is full.
usbd.epBank0SetReady(ep);
}
}
void USBDeviceClass::setHandler(uint32_t ep, EPHandler *handler) {
epHandlers[ep] = handler;
}
void USBDeviceClass::flush(uint32_t ep)
{
if (available(ep)) {
// RAM buffer is full, we can send data (IN)
usbd.epBank1SetReady(ep);
// Clear the transfer complete flag
usbd.epBank1AckTransferComplete(ep);
}
}
void USBDeviceClass::stall(uint32_t ep)
{
// TODO: test
// TODO: use .bit. notation
// Stall endpoint
USB->DEVICE.DeviceEndpoint[ep].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ(2);
}
bool USBDeviceClass::connected()
{
// Count frame numbers
uint8_t f = USB->DEVICE.FNUM.bit.FNUM;
//delay(3);
return f != USB->DEVICE.FNUM.bit.FNUM;
}
uint32_t USBDeviceClass::recvControl(void *_data, uint32_t len)
{
uint8_t *data = reinterpret_cast<uint8_t *>(_data);
// The RAM Buffer is empty: we can receive data
usbd.epBank0ResetReady(0);
//usbd.epBank0AckSetupReceived(0);
uint32_t read = armRecvCtrlOUT(0);
if (read > len)
read = len;
//while (!usbd.epBank0AckTransferComplete(0)) {}
uint8_t *buffer = udd_ep_out_cache_buffer[0];
for (uint32_t i=0; i<len; i++) {
data[i] = buffer[i];
}
return read;
}
// Number of bytes, assumes a rx endpoint
uint32_t USBDeviceClass::available(uint32_t ep)
{
if (epHandlers[ep]) {
return epHandlers[ep]->available();
} else {
return usbd.epBank0ByteCount(ep);
}
}
// Non Blocking receive
// Return number of bytes read
uint32_t USBDeviceClass::recv(uint32_t ep, void *_data, uint32_t len)
{
if (!_usbConfiguration)
return -1;
#ifdef PIN_LED_RXL
if (rxLEDPulse == 0)
digitalWrite(PIN_LED_RXL, LOW);
rxLEDPulse = TX_RX_LED_PULSE_MS;
#endif
if (epHandlers[ep]) {
return epHandlers[ep]->recv(_data, len);
}
if (available(ep) < len)
len = available(ep);
armRecv(ep);
usbd.epBank0DisableTransferComplete(ep);
memcpy(_data, udd_ep_out_cache_buffer[ep], len);
// release empty buffer
if (len && !available(ep)) {
// The RAM Buffer is empty: we can receive data
usbd.epBank0ResetReady(ep);
// Clear Transfer complete 0 flag
usbd.epBank0AckTransferComplete(ep);
// Enable Transfer complete 0 interrupt
usbd.epBank0EnableTransferComplete(ep);
}
return len;
}
// Recv 1 byte if ready
int USBDeviceClass::recv(uint32_t ep)
{
uint8_t c;
if (recv(ep, &c, 1) != 1) {
return -1;
} else {
return c;
}
}
uint8_t USBDeviceClass::armRecvCtrlOUT(uint32_t ep)
{
// Get endpoint configuration from setting register
usbd.epBank0SetAddress(ep, &udd_ep_out_cache_buffer[ep]);
usbd.epBank0SetMultiPacketSize(ep, 8);
usbd.epBank0SetByteCount(ep, 0);
usbd.epBank0ResetReady(ep);
// Wait OUT
while (!usbd.epBank0IsReady(ep)) {}
while (!usbd.epBank0IsTransferComplete(ep)) {}
return usbd.epBank0ByteCount(ep);
}
uint8_t USBDeviceClass::armRecv(uint32_t ep)
{
uint16_t count = usbd.epBank0ByteCount(ep);
if (count >= 64) {
usbd.epBank0SetByteCount(ep, count - 64);
} else {
usbd.epBank0SetByteCount(ep, 0);
}
return usbd.epBank0ByteCount(ep);
}
// Timeout for sends
#define TX_TIMEOUT_MS 70
static char LastTransmitTimedOut[7] = {
0,
0,
0,
0,
0,
0,
0
};
// Blocking Send of data to an endpoint
uint32_t USBDeviceClass::send(uint32_t ep, const void *data, uint32_t len)
{
uint32_t written = 0;
uint32_t length = 0;
if (!_usbConfiguration)
return -1;
if (len > 16384)
return -1;
#ifdef PIN_LED_TXL
if (txLEDPulse == 0)
digitalWrite(PIN_LED_TXL, LOW);
txLEDPulse = TX_RX_LED_PULSE_MS;
#endif
// Flash area
while (len != 0)
{
if (usbd.epBank1IsReady(ep)) {
// previous transfer is still not complete
// convert the timeout from microseconds to a number of times through
// the wait loop; it takes (roughly) 23 clock cycles per iteration.
uint32_t timeout = microsecondsToClockCycles(TX_TIMEOUT_MS * 1000) / 23;
// Wait for (previous) transfer to complete
// inspired by Paul Stoffregen's work on Teensy
while (!usbd.epBank1IsTransferComplete(ep)) {
if (LastTransmitTimedOut[ep] || timeout-- == 0) {
LastTransmitTimedOut[ep] = 1;
// set byte count to zero, so that ZLP is sent
// instead of stale data
usbd.epBank1SetByteCount(ep, 0);
return -1;
}
}
}
LastTransmitTimedOut[ep] = 0;
if (len > EPX_SIZE) {
length = EPX_SIZE;
} else {
length = len;
}
/* memcopy could be safer in multi threaded environment */
memcpy(&udd_ep_in_cache_buffer[ep], data, length);
usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]);
usbd.epBank1SetByteCount(ep, length);
// Clear the transfer complete flag
usbd.epBank1AckTransferComplete(ep);
// RAM buffer is full, we can send data (IN)
usbd.epBank1SetReady(ep);
written += length;
len -= length;
data = (char *)data + length;
}
return written;
}
uint32_t USBDeviceClass::armSend(uint32_t ep, const void* data, uint32_t len)
{
memcpy(&udd_ep_in_cache_buffer[ep], data, len);
// Get endpoint configuration from setting register
usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]);
usbd.epBank1SetMultiPacketSize(ep, 0);
usbd.epBank1SetByteCount(ep, len);
return len;
}
uint32_t USBDeviceClass::sendControl(const void* _data, uint32_t len)
{
const uint8_t *data = reinterpret_cast<const uint8_t *>(_data);
uint32_t length = len;
uint32_t sent = 0;
uint32_t pos = 0;
if (_dry_run == true)
return length;
if (_pack_message == true) {
memcpy(&_pack_buffer[_pack_size], data, len);
_pack_size += len;
return length;
}
while (len > 0)
{
sent = armSend(EP0, data + pos, len);
pos += sent;
len -= sent;
}
return length;
}
void USBDeviceClass::sendZlp(uint32_t ep)
{
// Set the byte count as zero
usbd.epBank1SetByteCount(ep, 0);
}
bool USBDeviceClass::handleStandardSetup(USBSetup &setup)
{
switch (setup.bRequest) {
case GET_STATUS:
if (setup.bmRequestType == 0) // device
{
// Send the device status
// TODO: Check current configuration for power mode (if device is configured)
// TODO: Check if remote wake-up is enabled
uint8_t buff[] = { 0, 0 };
armSend(0, buff, 2);
return true;
}
// if( setup.bmRequestType == 2 ) // Endpoint:
else
{
// Send the endpoint status
// Check if the endpoint if currently halted
uint8_t buff[] = { 0, 0 };
if (isEndpointHalt == 1)
buff[0] = 1;
armSend(0, buff, 2);
return true;
}
case CLEAR_FEATURE:
// Check which is the selected feature
if (setup.wValueL == 1) // DEVICEREMOTEWAKEUP
{
// Enable remote wake-up and send a ZLP
uint8_t buff[] = { 0, 0 };
if (isRemoteWakeUpEnabled == 1)
buff[0] = 1;
armSend(0, buff, 2);
return true;
}
else // if( setup.wValueL == 0) // ENDPOINTHALT
{
isEndpointHalt = 0;
sendZlp(0);
return true;
}
case SET_FEATURE:
// Check which is the selected feature
if (setup.wValueL == 1) // DEVICEREMOTEWAKEUP
{
// Enable remote wake-up and send a ZLP
isRemoteWakeUpEnabled = 1;
uint8_t buff[] = { 0 };
armSend(0, buff, 1);
return true;
}
if (setup.wValueL == 0) // ENDPOINTHALT
{
// Halt endpoint
isEndpointHalt = 1;
sendZlp(0);
return true;
}
return false;
case SET_ADDRESS:
setAddress(setup.wValueL);
return true;
case GET_DESCRIPTOR:
return sendDescriptor(setup);
case SET_DESCRIPTOR:
return false;
case GET_CONFIGURATION:
armSend(0, (void*)&_usbConfiguration, 1);
return true;
case SET_CONFIGURATION:
if (REQUEST_DEVICE == (setup.bmRequestType & REQUEST_RECIPIENT)) {
initEndpoints();
_usbConfiguration = setup.wValueL;
#if defined(CDC_ENABLED)
// Enable interrupt for CDC reception from host (OUT packet)
usbd.epBank1EnableTransferComplete(CDC_ENDPOINT_ACM);
usbd.epBank0EnableTransferComplete(CDC_ENDPOINT_OUT);
#endif
sendZlp(0);
return true;
} else {
return false;
}
case GET_INTERFACE:
armSend(0, (void*)&_usbSetInterface, 1);
return true;
case SET_INTERFACE:
_usbSetInterface = setup.wValueL;
sendZlp(0);
return true;
default:
return true;
}
}
void USBDeviceClass::ISRHandler()
{
if (_pack_message == true) {
return;
}
// End-Of-Reset
if (usbd.isEndOfResetInterrupt())
{
// Configure EP 0
initEP(0, USB_ENDPOINT_TYPE_CONTROL);
// Enable Setup-Received interrupt
usbd.epBank0EnableSetupReceived(0);
_usbConfiguration = 0;
usbd.ackEndOfResetInterrupt();
}
// Start-Of-Frame
if (usbd.isStartOfFrameInterrupt())
{
usbd.ackStartOfFrameInterrupt();
// check whether the one-shot period has elapsed. if so, turn off the LED
#ifdef PIN_LED_TXL
if (txLEDPulse > 0) {
txLEDPulse--;
if (txLEDPulse == 0)
digitalWrite(PIN_LED_TXL, HIGH);
}
#endif
#ifdef PIN_LED_RXL
if (rxLEDPulse > 0) {
rxLEDPulse--;
if (rxLEDPulse == 0)
digitalWrite(PIN_LED_RXL, HIGH);
}
#endif
}
// Endpoint 0 Received Setup interrupt
if (usbd.epBank0IsSetupReceived(0))
{
usbd.epBank0AckSetupReceived(0);
USBSetup *setup = reinterpret_cast<USBSetup *>(udd_ep_out_cache_buffer[0]);
/* Clear the Bank 0 ready flag on Control OUT */
// The RAM Buffer is empty: we can receive data
usbd.epBank0ResetReady(0);
bool ok;
if (REQUEST_STANDARD == (setup->bmRequestType & REQUEST_TYPE)) {
// Standard Requests
ok = handleStandardSetup(*setup);
} else {
// Class Interface Requests
ok = handleClassInterfaceSetup(*setup);
}
if (ok) {
usbd.epBank1SetReady(0);
} else {
stall(0);
}
if (usbd.epBank1IsStalled(0))
{
usbd.epBank1AckStalled(0);
// Remove stall request
usbd.epBank1DisableStalled(0);
}
} // end Received Setup handler
uint8_t i=0;
uint8_t ept_int = usbd.epInterruptSummary() & 0xFE; // Remove endpoint number 0 (setup)
while (ept_int != 0)
{
// Check if endpoint has a pending interrupt
if ((ept_int & (1 << i)) != 0)
{
// Endpoint Transfer Complete (0/1) Interrupt
if (usbd.epBank0IsTransferComplete(i) ||
usbd.epBank1IsTransferComplete(i))
{
if (epHandlers[i]) {
epHandlers[i]->handleEndpoint();
} else {
handleEndpoint(i);
}
}
ept_int &= ~(1 << i);
}
i++;
if (i > USB_EPT_NUM)
break; // fire exit
}
}
/*
* USB Device instance
* -------------------
*/
// USBDevice class instance
USBDeviceClass USBDevice;
#endif
#endif // USE_TINYUSB