- added CMake-managed conditional compilation of desired USB classes - STM32F407xx and STM32H743/5xx support joined in the driver files
932 lines
35 KiB
C
932 lines
35 KiB
C
|
|
#include "usb_driver.h"
|
|
|
|
#include <memory.h>
|
|
|
|
#include "usb_common.h"
|
|
#include "usb_core_types.h"
|
|
|
|
#include "desc/usb_desc.h"
|
|
|
|
// ---------------
|
|
|
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
|
|
#ifdef USBDBGMSG
|
|
#include "../cli/stdio_uart.h"
|
|
#define USBMSG(...) MSG(__VA_ARGS__)
|
|
#else
|
|
#define USBMSG(...)
|
|
#endif
|
|
|
|
// ---------------
|
|
|
|
static USBDRV_GlobalState gs; // global USB state
|
|
|
|
static uint8_t rx_buf[USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS] DWORD_ALIGN; // receive buffer
|
|
|
|
#define USB_EVENT_QUEUE_LENGTH (16)
|
|
|
|
// static uint8_t event_queue_mem[Q_REQ_MEM_SIZE_T(USB_EVENT_QUEUE_LENGTH, USBDRV_EventCompound)] DWORD_ALIGN; // backing memory for the event queue
|
|
|
|
#ifdef USBDBGMSG
|
|
static const char *FIFO_STATUS_STR[6] = {
|
|
"GLOBAL OUT NAK",
|
|
"OUT DATA RECV",
|
|
"OUT TRANSFER CPLT",
|
|
"OUT SETUP CPLT",
|
|
"",
|
|
"OUT SETUP RECV"};
|
|
#endif
|
|
|
|
// ---------------
|
|
|
|
#if defined(STM32H745xx) || defined(STM32H743xx)
|
|
#define USB_GPIO_AF (GPIO_AF10_OTG1_FS)
|
|
#elif defined(STM32F407xx)
|
|
#define USB_GPIO_AF (GPIO_AF10_OTG_FS)
|
|
#endif
|
|
|
|
// USB pin low level, early peripheral initialization
|
|
// PA12: D+, PA11: D-
|
|
void usbdrv_gpio_init() {
|
|
// turn GPIO-s into AF mode
|
|
__HAL_RCC_GPIOA_CLK_ENABLE(); // turn ON GPIOA clocks
|
|
|
|
GPIO_InitTypeDef gpio_init;
|
|
gpio_init.Mode = GPIO_MODE_AF_PP;
|
|
gpio_init.Pin = GPIO_PIN_11 | GPIO_PIN_12;
|
|
gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
|
gpio_init.Pull = GPIO_NOPULL;
|
|
gpio_init.Alternate = USB_GPIO_AF;
|
|
|
|
HAL_GPIO_Init(GPIOA, &gpio_init); // USB D+
|
|
|
|
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
|
|
|
|
// gpio_init.Pin = GPIO_PIN_11;
|
|
// gpio_init.Pull = GPIO_NOPULL;
|
|
// HAL_GPIO_Init(GPIOA, &gpio_init); // USB D-
|
|
|
|
// gpio_init.Mode = GPIO_MODE_INPUT;
|
|
// gpio_init.Pin = GPIO_PIN_9;
|
|
// gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
|
|
// gpio_init.Pull = GPIO_NOPULL;
|
|
// gpio_init.Alternate = 0;
|
|
|
|
// HAL_GPIO_Init(GPIOA, &gpio_init); // USB VBUSSENSE
|
|
}
|
|
|
|
// ---------------
|
|
|
|
// initialize USB subsystem
|
|
void usbdrv_init() {
|
|
NVIC_DisableIRQ(OTG_FS_IRQn);
|
|
|
|
usbdrv_init_global_state();
|
|
usbdrv_gpio_init();
|
|
usbdrv_periph_init();
|
|
usbdrv_initial_ep0_setup();
|
|
usbdrv_power_and_connect(true);
|
|
|
|
NVIC_SetPriority(OTG_FS_IRQn, 6);
|
|
NVIC_EnableIRQ(OTG_FS_IRQn);
|
|
}
|
|
|
|
void usbdrv_reset() {
|
|
usbdrv_init();
|
|
}
|
|
|
|
// ---------------
|
|
|
|
#if USB_EVENT_PROCESSING_IN_OS_THREAD
|
|
static void usbdrv_thread(void *param);
|
|
#endif
|
|
|
|
// initialize global state
|
|
void usbdrv_init_global_state() {
|
|
// clear state
|
|
memset(&gs, 0, sizeof(USBDRV_GlobalState));
|
|
|
|
// initialize receive buffer
|
|
gs.rx_buf = rx_buf;
|
|
gs.rx_buf_level = 0;
|
|
|
|
#if USB_EVENT_PROCESSING_IN_OS_THREAD
|
|
// initialize event queue
|
|
gs.event_queue = osMessageQueueNew(16, sizeof(USBDRV_EventCompound), NULL);
|
|
|
|
// initialize event processing thread
|
|
osThreadAttr_t attr;
|
|
bzero(&attr, sizeof(osThreadAttr_t));
|
|
attr.stack_size = 2048;
|
|
attr.name = "usb";
|
|
attr.priority = osPriorityNormal;
|
|
gs.th = osThreadNew(usbdrv_thread, NULL, &attr);
|
|
#endif
|
|
}
|
|
|
|
// ---------------
|
|
|
|
#define USB_LINESPEED_FULL_SPEED (0b11)
|
|
|
|
// initialize USB peripheral
|
|
void usbdrv_periph_init() {
|
|
__HAL_RCC_USB_OTG_FS_CLK_ENABLE(); // enable clock on USB peripheral
|
|
//__HAL_RCC_USB_OTG_FS_ULPI_CLK_ENABLE();
|
|
//__HAL_RCC_USB_OTG_FS_FORCE_RESET();
|
|
//__HAL_RCC_USB_OTG_FS_RELEASE_RESET();
|
|
|
|
// HAL_PWREx_EnableUSBReg();
|
|
// HAL_PWREx_EnableUSBVoltageDetector();
|
|
|
|
#if defined(STM32H745xx) || defined(STM32H743xx)
|
|
SET_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_PHYSEL); // select the internal FS PHY
|
|
|
|
SET_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST); // reset USB core
|
|
WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
|
|
#endif
|
|
|
|
CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN); // power down the peripheral
|
|
|
|
CLEAR_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); // mask all interrupts for now
|
|
CLEAR_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_HNPCAP | USB_OTG_GUSBCFG_SRPCAP); // disable HNP and SRP
|
|
WRITE_FIELD(USBG->GUSBCFG, USB_OTG_GUSBCFG_TRDT, 0x06); // set TRDT according to the RM
|
|
WRITE_FIELD(USBG->GUSBCFG, USB_OTG_GUSBCFG_TOCAL, 0x07); // set TOCAL
|
|
SET_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_FDMOD); // force Device mode
|
|
|
|
#if defined(STM32H745xx) || defined(STM32H743xx)
|
|
CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_VBDEN); // turn off VBUSSENSE
|
|
SET_BIT(USBG->GOTGCTL, USB_OTG_GOTGCTL_BVALOEN | USB_OTG_GOTGCTL_BVALOVAL); // force B-session
|
|
#elif defined(STM32F407xx)
|
|
SET_BIT(USBG->GCCFG, USB_OTG_GCCFG_NOVBUSSENS); // turn off VBUSSENSE
|
|
#endif
|
|
|
|
// HAL_Delay(50); // it takes time to forcing Device mode takes effect
|
|
|
|
SET_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS); // soft disconnect peripheral (should be upper, but since it's controlled by a Device register, cannot be set before switching to device mode)
|
|
WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_FULL_SPEED); // there's no other possible option
|
|
|
|
// allow specific interrupts
|
|
uint32_t intmask = /*USB_OTG_GINTMSK_WUIM | // Wake up */
|
|
USB_OTG_GINTMSK_OEPINT | // OUT EP events
|
|
USB_OTG_GINTMSK_IEPINT | // IN EP events
|
|
USB_OTG_GINTMSK_ENUMDNEM | // (Linespeed) Enumeration (negotiation) done
|
|
USB_OTG_GINTMSK_USBRST | // USB Reset
|
|
USB_OTG_GINTMSK_USBSUSPM | // USB Suspend
|
|
USB_OTG_GINTMSK_RXFLVLM; // RX FIFO level (signal if non-empty)
|
|
USBG->GINTMSK = intmask;
|
|
|
|
// set global NAK on all Endpoints
|
|
// usbdrv_set_global_NAK(USB_IN, true);
|
|
// usbdrv_set_global_NAK(USB_OUT, true);
|
|
|
|
// flush Tx and Rx FIFOs
|
|
usbdrv_flush_rx_fifo();
|
|
usbdrv_flush_tx_fifo(USB_FLUSH_TX_FIFO_ALL);
|
|
|
|
// make Tx FIFO empty interrupt fire when Tx FIFO is emtpy
|
|
// SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_TXFELVL);
|
|
|
|
// set masks for endpoint interrupts
|
|
SET_BIT(USBD->DIEPMSK, USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_ITTXFEMSK); // transfer complete, timeout and Tx FIFO empty while receiving IN for IN EPs
|
|
SET_BIT(USBD->DOEPMSK, USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_STUPM); // transfer complete and SETUP complete for OUT EPs
|
|
|
|
// mask all endpoint interrupts in both directions and also clear flags
|
|
USBD->DAINTMSK = 0;
|
|
USBD->DAINT = 0;
|
|
|
|
// enbale global interrupts
|
|
SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT);
|
|
}
|
|
|
|
// connect to or disconnect from the bus
|
|
void usbdrv_power_and_connect(bool en) {
|
|
if (en) { // ON
|
|
CLEAR_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS);
|
|
SET_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN); // actually, this is power UP
|
|
} else { // OFF
|
|
SET_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS);
|
|
CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN);
|
|
}
|
|
}
|
|
|
|
// ---------------
|
|
|
|
// preload usb endpoint config
|
|
void usbdrv_preload_endpoint_config(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg) {
|
|
USBMSG("PRELOAD: %u %s\n", ep, dir ? "IN" : "OUT");
|
|
if (dir == USB_OUT) {
|
|
gs.ep_OUT[ep] = *cfg;
|
|
gs.ep_OUT[ep].is_configured = true;
|
|
} else {
|
|
gs.ep_IN[ep] = *cfg;
|
|
gs.ep_IN[ep].is_configured = true;
|
|
}
|
|
}
|
|
|
|
// clear endpoint config
|
|
void usbdrv_clear_endpoint_config() {
|
|
memset(&gs.ep_OUT, 0, USB_NUM_OF_ENDPOINTS * sizeof(USBDRV_EpConfig));
|
|
memset(&gs.ep_IN, 0, USB_NUM_OF_ENDPOINTS * sizeof(USBDRV_EpConfig));
|
|
}
|
|
|
|
// apply preloaded endpoint configuration
|
|
void usbdrv_apply_endpoint_config() {
|
|
for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) {
|
|
// OUT EPs
|
|
if (gs.ep_OUT[i].is_configured) {
|
|
usbdrv_configure_endpoint(i, USB_OUT, gs.ep_OUT + i);
|
|
}
|
|
|
|
// IN EPs
|
|
if (gs.ep_IN[i].is_configured) {
|
|
usbdrv_configure_endpoint(i, USB_IN, gs.ep_IN + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// fetch endpoint configuration from descriptor dump
|
|
void usbdrv_fetch_endpoint_configuration(uint8_t config_index) {
|
|
const uint8_t *fullConfDesc = (const uint8_t *)confDescs[config_index]; // point an array to the beginning of the full configuration
|
|
const USB_ConfigurationDesc *confDesc = (const USB_ConfigurationDesc *)confDescs[config_index]; // fetch the leading configuration descriptor
|
|
|
|
// look up endpoint descriptors
|
|
const uint8_t *iter = fullConfDesc;
|
|
while (iter < (fullConfDesc + confDesc->wTotalLength)) {
|
|
if (iter[1] == UD_Endpoint) { // Endpoint descriptor found
|
|
USB_EndpointDesc epDesc;
|
|
memcpy(&epDesc, iter, iter[0]); // fetch EP descriptor by copy, since desciptor start address is NOT aligned
|
|
USBDRV_EpConfig cfg; // fill-in configuration
|
|
cfg.max_packet_size = epDesc.wMaxPacketSize;
|
|
cfg.responding_NAK = false;
|
|
cfg.type = epDesc.bmAttributes & 0b11;
|
|
cfg.service_interval = epDesc.bInterval;
|
|
|
|
// fetch endpoint address
|
|
uint8_t dir = (epDesc.bEndpointAddress >> 7);
|
|
uint8_t n = epDesc.bEndpointAddress & 0x7F;
|
|
|
|
// apply configuration
|
|
usbdrv_preload_endpoint_config(n, dir, &cfg);
|
|
}
|
|
|
|
// advance iterator using the bLength field (first byte in a descriptor)
|
|
iter += iter[0];
|
|
}
|
|
|
|
usbdrv_build_fifo();
|
|
|
|
usbdrv_apply_endpoint_config();
|
|
}
|
|
|
|
#define USB_MIN_FIFO_SIZE (64)
|
|
#define USB_FIFO_MARGIN (8)
|
|
#define USB_RX_FIFO_SETUP_RESERVATION_DWORDS (10)
|
|
#define USB_MIN_GROSS_TX_FIFO_SIZE (2 * USB_MIN_EP_FIFO_SIZE)
|
|
|
|
#ifdef STM32F407xx
|
|
#define USB_MIN_GROSS_RX_FIFO_SIZE (2 * USB_MIN_EP_FIFO_SIZE + USB_RX_FIFO_SETUP_RESERVATION_DWORDS * 4)
|
|
#elif defined(STM32H745xx) || defined(STM32H743xx)
|
|
#define USB_MIN_GROSS_RX_FIFO_SIZE (256)
|
|
#endif
|
|
|
|
// build FIFO (compute addresses)
|
|
void usbdrv_build_fifo() {
|
|
// ---- OUT ----
|
|
uint16_t next_fifo_addr = 0x00; // Rx FIFO begins at address zero
|
|
uint16_t largest_packet_size = 0; // largest packet size
|
|
uint16_t control_ep_count = 0; // number of control endpoints
|
|
uint16_t out_ep_count = 0; // count of OUT pipes
|
|
for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) { // gather config information
|
|
// look for largest packet size
|
|
if (gs.ep_OUT[i].is_configured) { // examine OUT EPs
|
|
largest_packet_size = MAX(largest_packet_size, gs.ep_OUT[i].max_packet_size);
|
|
out_ep_count++;
|
|
}
|
|
if (gs.ep_IN[i].is_configured) { // examine IN EPs
|
|
largest_packet_size = MAX(largest_packet_size, gs.ep_IN[i].max_packet_size);
|
|
}
|
|
|
|
// count control endpoints
|
|
if (((gs.ep_OUT[i].is_configured) && (gs.ep_OUT[i].type == UT_Control)) ||
|
|
((gs.ep_IN[i].is_configured) && (gs.ep_IN[i].type == UT_Control))) {
|
|
control_ep_count++;
|
|
}
|
|
}
|
|
|
|
// RX FIFO size calculation expression from the RM
|
|
uint16_t fifo_size_dwords = (5 * control_ep_count + 8) + (CEILDIV4(largest_packet_size) + 1) + (2 * out_ep_count) + 1; // calculate RX FIFO size in DWORDS
|
|
uint16_t fifo_size = fifo_size_dwords * 4; // calculate RX FIFO size in bytes
|
|
fifo_size = CEIL4(MAX(fifo_size, USB_MIN_GROSS_RX_FIFO_SIZE)); // RX FIFO should be at least this large
|
|
|
|
//fifo_size *= 2; // TODO:
|
|
gs.rx_fifo_size = fifo_size; // save Rx FIFO size for later
|
|
next_fifo_addr += fifo_size; // advance next FIFO address
|
|
usbdrv_set_rx_fifo_size(fifo_size); // set Rx FIFO size in hardware
|
|
|
|
// ---- IN ----
|
|
for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) {
|
|
USBDRV_EpConfig *cfg = &gs.ep_IN[i];
|
|
if (cfg->is_configured) {
|
|
cfg->fifo_size = CEIL4(MAX(USB_MIN_GROSS_TX_FIFO_SIZE, 2 * cfg->max_packet_size)); // correct FIFO size if necessary
|
|
cfg->fifo_address = next_fifo_addr; // store FIFO address
|
|
cfg->zlp_next = false;
|
|
// cfg->txp = false; // no transfer is in progress
|
|
next_fifo_addr += cfg->fifo_size; // advance next address
|
|
}
|
|
}
|
|
}
|
|
|
|
// create an initial setup for EP0 in both directions
|
|
void usbdrv_initial_ep0_setup() {
|
|
// setup EP0 OUT and IN
|
|
USBDRV_EpConfig ep_cfg;
|
|
ep_cfg.max_packet_size = 64;
|
|
ep_cfg.responding_NAK = false;
|
|
usbdrv_preload_endpoint_config(0, USB_OUT, &ep_cfg);
|
|
usbdrv_preload_endpoint_config(0, USB_IN, &ep_cfg);
|
|
|
|
// build FIFO
|
|
usbdrv_build_fifo();
|
|
|
|
// configure endpoints
|
|
usbdrv_apply_endpoint_config();
|
|
|
|
// turn off global NAK
|
|
usbdrv_set_global_NAK(USB_IN, false);
|
|
usbdrv_set_global_NAK(USB_OUT, false);
|
|
}
|
|
|
|
// ---------------
|
|
|
|
// addresses of specific DIEPTXF registers, addresses from the RM
|
|
static uint32_t *USB_pDIEPTXF[4] = {
|
|
(uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x028), // DIEPTXF0
|
|
(uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x104), // DIEPTXF1
|
|
(uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x108), // DIEPTXF2
|
|
(uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x10C), // DIEPTXF3
|
|
};
|
|
|
|
// configure USB endpoint
|
|
void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg) {
|
|
if (dir == USB_OUT) { // ---- OUT ----
|
|
|
|
if (ep == 0) { // SPECIAL handling on EP0
|
|
WRITE_FIELD(USBOUTEP[0].DOEPCTL, USB_OTG_DOEPCTL_MPSIZ, 0); // fix in 64 bytes
|
|
WRITE_FIELD(USBOUTEP[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT, 3); // SETUP transaction stands of three packets
|
|
} else {
|
|
WRITE_FIELD(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_EPTYP, cfg->type); // program endpoint type
|
|
WRITE_FIELD(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_MPSIZ, cfg->max_packet_size); // program maximum packet size
|
|
SET_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_USBAEP); // the endpoint is active in the current configuration
|
|
}
|
|
|
|
// ---- common for all endpoints ----
|
|
|
|
// program maximum packet size
|
|
// WRITE_FIELD(USBOUTEP[ep].DOEPTSIZ, USB_OTG_DOEPTSIZ_XFRSIZ, cfg->max_packet_size); // TODO:
|
|
|
|
// enable interrupt
|
|
SET_BIT(USBD->DAINTMSK, 1 << (USB_OTG_DAINTMSK_OEPM_Pos + ep));
|
|
|
|
// NAK processing
|
|
if (cfg->responding_NAK) {
|
|
SET_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_SNAK); // send NAK
|
|
} else {
|
|
usbdrv_arm_OUT_endpoint(ep, cfg->max_packet_size);
|
|
}
|
|
} else { // ---- IN ----
|
|
|
|
if (ep == 0) { // SPECIAL handling on EP0
|
|
WRITE_FIELD(USBINEP[0].DIEPCTL, USB_OTG_DIEPCTL_MPSIZ, 0); // fix in 64 bytes
|
|
} else {
|
|
WRITE_FIELD(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPTYP, cfg->type); // program endpoint type
|
|
WRITE_FIELD(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_MPSIZ, cfg->max_packet_size); // program maximum packet size
|
|
SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_USBAEP); // the endpoint is active in the current configuration
|
|
}
|
|
|
|
// ---- common for all endpoints ----
|
|
|
|
// program FIFO corresponding FIFO number
|
|
WRITE_FIELD(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_TXFNUM, ep);
|
|
|
|
// store Tx FIFO size (both fields are WORD units, NOT bytes, this RM is missing this information!)
|
|
uint32_t tx_fifo_config = ((cfg->fifo_size >> 2) << USB_OTG_DIEPTXF_INEPTXFD_Pos) | (cfg->fifo_address >> 2); // combine size in DWORDs and address
|
|
*(USB_pDIEPTXF[ep]) = tx_fifo_config; // save
|
|
|
|
// enable interrupt
|
|
SET_BIT(USBD->DAINTMSK, 1 << ep);
|
|
|
|
// NAK processing
|
|
if (cfg->responding_NAK) {
|
|
SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_SNAK); // send NAK
|
|
} else {
|
|
SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_CNAK); // clear sending NAK
|
|
}
|
|
}
|
|
}
|
|
|
|
// deconfigure USB endpoint
|
|
void usbdrv_deconfigure_endpoint(uint8_t ep, uint8_t dir) {
|
|
if (ep == 0) { // EP0 cannot be deconfigured
|
|
return;
|
|
}
|
|
|
|
if (dir == USB_OUT) { // ---- OUT ----
|
|
CLEAR_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_USBAEP); // deactivate endpoint
|
|
CLEAR_BIT(USBD->DAINTMSK, 1 << (USB_OTG_DAINTMSK_OEPM_Pos + ep)); // disable interrupt
|
|
} else { // ---- IN ----
|
|
CLEAR_BIT(USBD->DAINTMSK, 1 << ep); // disable interrupt
|
|
usbdrv_flush_tx_fifo(ep); // flush Tx FIFO
|
|
SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_USBAEP); // deactivate endpoint
|
|
}
|
|
}
|
|
|
|
// ---------------
|
|
|
|
// flush specific or all Tx FIFOs
|
|
void usbdrv_flush_tx_fifo(uint8_t n) {
|
|
WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_TXFFLSH); // wait for previous request to conclude
|
|
WRITE_FIELD(USBG->GRSTCTL, USB_OTG_GRSTCTL_TXFNUM, n); // issue flush
|
|
WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_TXFFLSH); // wait for our request to conclude
|
|
}
|
|
|
|
// flush the Rx FIFO
|
|
void usbdrv_flush_rx_fifo() {
|
|
WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH);
|
|
SET_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH); // issue flush
|
|
WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH);
|
|
}
|
|
|
|
// set Rx FIFO size
|
|
void usbdrv_set_rx_fifo_size(uint16_t size) {
|
|
USBG->GRXFSIZ = CEILDIV4(size);
|
|
}
|
|
|
|
// ---------------
|
|
|
|
// stall endpoint
|
|
void usbdrv_stall_endpoint(uint8_t ep, uint8_t dir, bool stall) {
|
|
USB_OTG_INEndpointTypeDef *inep = USBINEP + ep;
|
|
// USB_OTG_OUTEndpointTypeDef *outep = USBOUTEP + ep;
|
|
|
|
if (stall) {
|
|
if (dir == USB_IN) {
|
|
if (ep != 0) { // special treatment for EP0
|
|
SET_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_EPDIS); // disable endpoint
|
|
}
|
|
SET_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_STALL); // stall endpoint
|
|
}
|
|
|
|
if (ep != 0) { // EP0 cannot be disabled
|
|
// wait for endpoint disable to get effective
|
|
WAIT_FOR_nBIT(inep->DIEPINT, USB_OTG_DIEPINT_EPDISD);
|
|
CLEAR_BIT(inep->DIEPINT, USB_OTG_DIEPINT_EPDISD);
|
|
}
|
|
|
|
// flush trnasmit FIFO
|
|
usbdrv_flush_tx_fifo(ep);
|
|
} else {
|
|
if (dir == USB_IN) {
|
|
if (ep != 0) { // special treatment for EP0
|
|
SET_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_EPENA); // enable endpoint
|
|
}
|
|
CLEAR_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_STALL); // clear endpoint stall
|
|
}
|
|
}
|
|
}
|
|
|
|
// set global NAK
|
|
void usbdrv_set_global_NAK(uint8_t dir, bool en) {
|
|
if (en) {
|
|
if (dir == USB_IN) {
|
|
SET_BIT(USBD->DCTL, USB_OTG_DCTL_SGINAK);
|
|
} else {
|
|
SET_BIT(USBD->DCTL, USB_OTG_DCTL_SGONAK);
|
|
}
|
|
} else {
|
|
if (dir == USB_IN) {
|
|
SET_BIT(USBD->DCTL, USB_OTG_DCTL_CGINAK);
|
|
} else {
|
|
SET_BIT(USBD->DCTL, USB_OTG_DCTL_CGONAK);
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------
|
|
|
|
// fetch received data from RX FIFO to receive buffer
|
|
void usbdrv_fetch_received_data(uint8_t ep, uint16_t len) {
|
|
if (len > 0) {
|
|
volatile uint32_t *p = USBFIFO(ep);
|
|
uint16_t len_dwords = CEILDIV4(len);
|
|
for (uint16_t i = 0; i < len_dwords; i++) {
|
|
uint32_t dword = p[i];
|
|
uint16_t i0 = i * 4;
|
|
gs.rx_buf[i0] = dword & 0xFF;
|
|
gs.rx_buf[i0 + 1] = (dword >> 8) & 0xFF;
|
|
gs.rx_buf[i0 + 2] = (dword >> 16) & 0xFF;
|
|
gs.rx_buf[i0 + 3] = (dword >> 24) & 0xFF;
|
|
}
|
|
}
|
|
gs.rx_buf_level = len;
|
|
}
|
|
|
|
// write data to specific endpoint FIFO
|
|
uint32_t usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) {
|
|
// determine if a transmission is in progress or not
|
|
bool txp = READ_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA);
|
|
|
|
uint16_t mps = gs.ep_IN[ep].max_packet_size; // fetch maximum packet size
|
|
|
|
// determine final write size
|
|
uint32_t freeSize = USBINEP[ep].DTXFSTS * sizeof(uint32_t); // get free transmit buffer size
|
|
uint16_t writeSize = 0;
|
|
|
|
if (txp) { // transaction is in progress
|
|
uint32_t remainingBytes = USBINEP[ep].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ; // acquire remaining bytes
|
|
if (len >= mps) { // write only full-sized packets, or a short packet
|
|
uint16_t mws = MIN(freeSize, len); // maximum possible write size
|
|
uint16_t fpc = mws / mps; // full packet count
|
|
writeSize = fpc * mps; // form write size
|
|
} else { // length is less then a full packet size
|
|
if (len <= freeSize) { // packet can be written
|
|
writeSize = len;
|
|
} else {
|
|
writeSize = 0;
|
|
}
|
|
}
|
|
// adjust write size to remaining size
|
|
writeSize = MIN(remainingBytes, writeSize);
|
|
} else { // no transaction is in progress
|
|
writeSize = MIN(freeSize, len);
|
|
}
|
|
|
|
// if no transmission is in progress
|
|
if (!txp) {
|
|
// calculate packet count based on max packet size
|
|
uint16_t packet_count = 1; // for ZLPs
|
|
if (len > 0) { // if length is nonzero
|
|
packet_count = len / mps + (((len % mps) > 0) ? 1 : 0); // a transfer may contain multiple packets
|
|
}
|
|
|
|
// set zlp_next if transfer size is integer multiple of max packet size and auto ZLP is on
|
|
gs.ep_IN[ep].zlp_next = (len > 0) && ((len % mps) == 0);
|
|
|
|
// program DIEPTSIZ with transfer length
|
|
USBINEP[ep].DIEPTSIZ = /*(packet_count << USB_OTG_DIEPTSIZ_MULCNT_Pos) |*/ (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | len;
|
|
|
|
// enable endpoint and cancel responding NAK
|
|
SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK);
|
|
}
|
|
|
|
// turn interrupt generation on only, if this is NOT the last FIFO write considering the current transfer
|
|
if (len > writeSize) {
|
|
USBD->DIEPEMPMSK |= ((uint32_t)(1 << ep));
|
|
}
|
|
|
|
// disable ALL USB interrupts to prevent access to specific registers (see errata)
|
|
CLEAR_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT);
|
|
|
|
// https://github.com/iliasam/STM32F4_USB_MICROPHONE/blob/master/Libraries/STM32_USB_OTG_Driver/src/usb_dcd_int.c#L655
|
|
// https://github.com/iliasam/STM32F4_USB_MICROPHONE/blob/master/Libraries/STM32_USB_OTG_Driver/src/usb_core.c#L168
|
|
|
|
// push full dwords
|
|
volatile uint32_t *p = (uint32_t *)USBFIFO(ep);
|
|
uint32_t floorlen_dwords = writeSize >> 2;
|
|
for (uint16_t i = 0; i < floorlen_dwords; i++) {
|
|
uint16_t i0 = 4 * i;
|
|
uint32_t dword = data[i0] | (data[i0 + 1] << 8) | (data[i0 + 2] << 16) | (data[i0 + 3] << 24);
|
|
*p = dword;
|
|
}
|
|
|
|
// push the remaining partial dword
|
|
uint8_t rem_bytes = writeSize & 0b11;
|
|
if (rem_bytes > 0) {
|
|
uint32_t rem_dword = 0;
|
|
uint16_t rem_start = writeSize - rem_bytes;
|
|
for (int16_t i = writeSize - 1; i >= rem_start; i--) {
|
|
rem_dword = (rem_dword << 8) | data[i];
|
|
}
|
|
*p = rem_dword;
|
|
}
|
|
|
|
// unmask USB interrupts
|
|
SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT);
|
|
|
|
// return with written size
|
|
return writeSize;
|
|
}
|
|
|
|
// arm OUT endpoint
|
|
uint32_t usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size) {
|
|
// arm endpoint only if it was not armed before OR if it's the EP0 OUT which is always enabled, but responds NAK after a successful transfer
|
|
if (READ_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_EPENA)) {
|
|
return 0;
|
|
}
|
|
|
|
// cap size at max packet size defined for this endpoint
|
|
size = MIN(gs.ep_OUT[ep].max_packet_size, size);
|
|
|
|
// write registers
|
|
uint32_t doeptsiz = USBOUTEP[ep].DOEPTSIZ;
|
|
doeptsiz &= ~(USB_OTG_DOEPTSIZ_XFRSIZ); // clear XFERSIZ bits
|
|
doeptsiz |= USB_OTG_DOEPTSIZ_PKTCNT | size; // program DIEPTSIZ with maximum (expected) transfer length and set PCKTCNT to make ready for reception
|
|
USBOUTEP[ep].DOEPTSIZ = doeptsiz; // write value to the actual register
|
|
|
|
SET_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK); // enable endpoint and clear NAK
|
|
|
|
// return with armed size
|
|
return size;
|
|
}
|
|
|
|
void usbdrv_autoarm_OUT_endpoint(uint8_t ep) {
|
|
gs.ep_OUT[ep].autoarm = true;
|
|
}
|
|
|
|
// ----------------
|
|
|
|
void usbdrv_set_address(uint8_t addr) {
|
|
gs.address = addr;
|
|
WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DAD, gs.address);
|
|
}
|
|
|
|
// ----------------
|
|
|
|
#if USB_EVENT_PROCESSING_IN_OS_THREAD
|
|
// push event onto the event queue
|
|
void usbdrv_push_event(uint32_t evt_code, USBDRV_EventData *evt_data) {
|
|
USBDRV_EventCompound evt_cpd;
|
|
if (evt_data != NULL) {
|
|
evt_cpd.data = *evt_data;
|
|
}
|
|
evt_cpd.code = evt_code;
|
|
osMessageQueuePut(gs.event_queue, &evt_cpd, 0, 0);
|
|
}
|
|
|
|
static void usbdrv_thread(void *param) {
|
|
for (;;) {
|
|
USBDRV_EventCompound evt_cpd;
|
|
osMessageQueueGet(gs.event_queue, &evt_cpd, 0, osWaitForever);
|
|
usbdrv_process_event(evt_cpd.code, NULL);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ----------------
|
|
|
|
// receive packet
|
|
void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data) {
|
|
uint32_t rxstat = USBG->GRXSTSP; // POP (not just read) latest FIFO status word
|
|
|
|
uint8_t pckt_status = READ_FIELD(rxstat, USB_OTG_GRXSTSP_PKTSTS); // read packet status
|
|
uint8_t data_pid = READ_FIELD(rxstat, USB_OTG_GRXSTSP_DPID); // read data PID
|
|
uint8_t byte_count = READ_FIELD(rxstat, USB_OTG_GRXSTSP_BCNT); // byte count
|
|
uint8_t ep_num = READ_FIELD(rxstat, USB_OTG_GRXSTSP_EPNUM); // read endpoint number
|
|
|
|
// copy to output structure
|
|
evt_data->rx.pckt_status = pckt_status;
|
|
evt_data->rx.data_pid = data_pid;
|
|
evt_data->rx.byte_count = byte_count;
|
|
evt_data->rx.ep_num = ep_num;
|
|
|
|
USBMSG("%s [%u] %u\n", FIFO_STATUS_STR[pckt_status - 1], ep_num, byte_count);
|
|
}
|
|
|
|
// always pass ALIGNED data!
|
|
__weak void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stage) {
|
|
return;
|
|
}
|
|
|
|
__weak void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) {
|
|
return;
|
|
}
|
|
|
|
// process USB event
|
|
void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) {
|
|
if (evt_code == USB_EVT_USB_RESET) { // reset takes precedence over anything else TODO
|
|
SET_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
|
|
WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);
|
|
usbdrv_reset();
|
|
USBMSG("RESET\n");
|
|
return;
|
|
}
|
|
|
|
switch (gs.state) {
|
|
case USB_FSM_INITIAL_WAIT_SPEEDNEG: // wait for speed negotitation to conclude
|
|
if (evt_code == USB_EVT_SPEEDNEG_DONE) {
|
|
gs.state = USB_FSM_SETUP_OPERATE; // wait for speed negotiation
|
|
}
|
|
break;
|
|
case USB_FSM_SETUP_OPERATE: { // expect SETUP transactions first, then everything else as well
|
|
switch (evt_code) {
|
|
case USB_EVT_RECEPTION_DONE: { // reception done
|
|
USBDRV_EventData evt_data = {0};
|
|
usbdrv_process_rx_fifo_top(&evt_data); // process rx fifo top
|
|
|
|
// fetch data if data are available
|
|
if ((evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_DATA_RECV) ||
|
|
(evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV)) {
|
|
usbdrv_fetch_received_data(evt_data.rx.ep_num, evt_data.rx.byte_count); // fetch the data
|
|
}
|
|
|
|
// act according to what we have received
|
|
if (evt_data.rx.ep_num == 0) { // EP0 special treatment
|
|
int stage = -1;
|
|
if (evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_CPLT) {
|
|
stage = UST_SETUP;
|
|
USBMSG("--SETUP\n");
|
|
} else if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_TRANSFER_CPLT) {
|
|
stage = UST_DATA;
|
|
|
|
USBMSG("--DATA\n");
|
|
}
|
|
|
|
// process setup packet
|
|
if (stage != -1) {
|
|
usbcore_process_setup_pckt(gs.rx_buf, gs.rx_buf_level, stage);
|
|
}
|
|
|
|
// SET_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // unmask interrupt
|
|
} else { // not EP0
|
|
if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV) { // TODO maybe the
|
|
USBDRV_CallbackCompound cbcpd;
|
|
cbcpd.ep = evt_data.rx.ep_num;
|
|
cbcpd.dir = USB_OUT;
|
|
cbcpd.code = USB_CBC_OUT;
|
|
cbcpd.data = gs.rx_buf;
|
|
cbcpd.size = gs.rx_buf_level;
|
|
usbcore_process_nonsetup_event(&cbcpd);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case USB_EVT_OUT_DONE: { // some OUT operations have finished
|
|
for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) {
|
|
if (gs.ep_OUT[ep].is_configured) { // if the endpoint is running
|
|
if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP)) { // setup done
|
|
SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP);
|
|
USBMSG("SETUP\n");
|
|
}
|
|
|
|
if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC)) { // OUT transaction done
|
|
SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC);
|
|
USBMSG("OUT\n");
|
|
|
|
if ((ep == 0) || (gs.ep_OUT[ep].autoarm)) { // EP0 must always be armed
|
|
usbdrv_arm_OUT_endpoint(ep, gs.ep_OUT[ep].max_packet_size); // arm endpoint
|
|
gs.ep_OUT[ep].autoarm = false; // clear autoarm flag
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case USB_EVT_IN_DONE: { // some IN operations have finished
|
|
|
|
// callback compound
|
|
USBDRV_CallbackCompound cbcpd;
|
|
cbcpd.dir = USB_IN;
|
|
cbcpd.data = NULL;
|
|
cbcpd.size = 0;
|
|
|
|
for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) {
|
|
cbcpd.ep = ep;
|
|
if (gs.ep_IN[ep].is_configured) { // if the endpoint is running
|
|
if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC)) { // timeout done
|
|
SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC);
|
|
USBMSG("TO\n");
|
|
} else if ((USBD->DIEPEMPMSK & (1 << ep)) && READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TXFE)) {
|
|
// disable FIFO empty interrupt
|
|
USBD->DIEPEMPMSK &= ~((uint32_t)(1 << ep));
|
|
|
|
cbcpd.code = USB_CBC_IN_FIFOEMPTY;
|
|
usbcore_process_nonsetup_event(&cbcpd);
|
|
} else if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC)) { // IN transaction done
|
|
SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC);
|
|
|
|
// disable FIFO empty interrupt
|
|
USBD->DIEPEMPMSK &= ~((uint32_t)(1 << ep));
|
|
|
|
// transfer finished
|
|
// gs.ep_IN[ep].txp = false;
|
|
|
|
// see if a ZLP transmission was queued
|
|
if (gs.ep_IN[ep].zlp_next) {
|
|
usbdrv_arm_IN_endpoint(ep, NULL, 0); // send ZLP
|
|
} else { // no ZLP
|
|
USBMSG("IN [%d]\n", ep);
|
|
|
|
cbcpd.code = USB_CBC_IN_DONE;
|
|
usbcore_process_nonsetup_event(&cbcpd);
|
|
}
|
|
} else if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE)) { // IN endpoint IN token received with Tx FIFO empty interrupt
|
|
SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE);
|
|
|
|
// USBMSG("IN FIFOEMPTY [%d]\n", ep);
|
|
|
|
// transfer finished
|
|
// gs.ep_IN[ep].txp = false;
|
|
|
|
cbcpd.code = USB_CBC_IN_FIFOEMPTY;
|
|
usbcore_process_nonsetup_event(&cbcpd);
|
|
}
|
|
}
|
|
}
|
|
|
|
// // set new address if it's already waiting
|
|
// if (gs.new_address != 0) {
|
|
// WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DAD, gs.new_address);
|
|
// gs.new_address = 0;
|
|
// USBMSG("ADDR SET\n");
|
|
// }
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ----------------
|
|
|
|
// get endpoint interrupt flag
|
|
bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir) {
|
|
return (USBD->DAINT & (1 << (ep + ((dir == USB_OUT) ? USB_OTG_DAINT_OEPINT_Pos : 0)))) != 0;
|
|
}
|
|
|
|
#if USB_EVENT_PROCESSING_IN_OS_THREAD
|
|
#define PROCESS_EVENT(evt, data) usbdrv_push_event((evt), (data))
|
|
#else
|
|
#define PROCESS_EVENT(evt, data) usbdrv_process_event((evt), (data))
|
|
#endif
|
|
|
|
void OTG_FS_IRQHandler() {
|
|
|
|
uint32_t ints = USBG->GINTSTS;
|
|
|
|
// USB reset
|
|
if (ints & USB_OTG_GINTSTS_USBRST) {
|
|
SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_USBRST); // clear interrupt
|
|
// usb_reset(); // reset the USB subsystem
|
|
// return;
|
|
// usbdrv_push_event(USB_EVT_USB_RESET, NULL);
|
|
return;
|
|
}
|
|
|
|
// End of enumeration (meaning NOT the USB ENUMERATION PROCESS,
|
|
// ST calls speed negotiation the enumeration, normally this
|
|
// interrupt fires only before even communication is commenced)
|
|
if (ints & USB_OTG_GINTSTS_ENUMDNE) {
|
|
SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_ENUMDNE); // clear interrupt
|
|
PROCESS_EVENT(USB_EVT_SPEEDNEG_DONE, NULL); // process event
|
|
// usbdrv_process_event(USB_EVT_SPEEDNEG_DONE, NULL); // process event
|
|
// usbdrv_push_event(USB_EVT_SPEEDNEG_DONE, NULL); // push event
|
|
}
|
|
|
|
// Start of Frame received (like Keep-Alive in LS mode)
|
|
if (ints & USB_OTG_GINTSTS_SOF) {
|
|
SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_SOF); // clear interrupt
|
|
}
|
|
|
|
// USB Suspend
|
|
if (ints & USB_OTG_GINTSTS_USBSUSP) {
|
|
SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_USBSUSP); // clear interrupt
|
|
USBMSG("SUSPEND\n");
|
|
}
|
|
|
|
// OUT endpoint interrupt
|
|
if (ints & USB_OTG_GINTSTS_OEPINT) {
|
|
PROCESS_EVENT(USB_EVT_OUT_DONE, NULL);
|
|
// usbdrv_push_event(USB_EVT_OUT_DONE, NULL);
|
|
// if (USBD->DAINT & (1 << 16)) {
|
|
// if (USBOUTEP[0].DOEPINT & USB_OTG_DOEPINT_STUP) {
|
|
// CLEAR_BIT(USBOUTEP[0].DOEPINT, USB_OTG_DOEPINT_STUP);
|
|
// }
|
|
// }
|
|
}
|
|
|
|
// RX FIFO non-empty interrupt
|
|
if (ints & USB_OTG_GINTSTS_RXFLVL) {
|
|
// SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_RXFLVL); // clear interrupt
|
|
USBMSG("RX DONE\n");
|
|
// CLEAR_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // mask interrupt until processing is done
|
|
PROCESS_EVENT(USB_EVT_RECEPTION_DONE, NULL); // process event
|
|
// usbdrv_push_event(USB_EVT_RECEPTION_DONE, NULL); // push event
|
|
}
|
|
|
|
// IN endpoint interrupt
|
|
if (ints & USB_OTG_GINTSTS_IEPINT) {
|
|
PROCESS_EVENT(USB_EVT_IN_DONE, NULL);
|
|
// usbdrv_push_event(USB_EVT_IN_DONE, NULL);
|
|
}
|
|
|
|
return;
|
|
} |