flatUSB/driver/ch32/usb_drv.c
Epagris c0ea9c3ab3 - CH32F207 port fixed
- ACM stack size fixed
2025-03-07 16:34:25 +01:00

655 lines
22 KiB
C

#include "usb_drv.h"
#include <stdint.h>
#include <string.h>
#include "flatUSB_config.h"
#include "usb_driver_common.h"
#include FLATUSB_DESCRIPTOR_HEADER
#include "usb_common.h"
#include "ch32f20x_usb.h"
#ifndef SET_BIT
#define SET_BIT(r, b) (r) |= (b)
#endif
#ifndef GET_BIT
#define GET_BIT(r, b) (((r) & (b)) != 0)
#endif
#ifndef CLEAR_BIT
#define CLEAR_BIT(r, b) (r) &= ~(b)
#endif
#ifndef __weak
#define __weak __attribute__((weak))
#endif
// ------------------------
// combined buffer: TX1 | RX1 | TX2 | RX2 (TODO: ennek még utána kell nézni)
#define USB_EP_TX_BUF_SIZE (64) ///< Transmit buffer size
#define USB_EP_RX_BUF_SIZE (64) ///< Receive buffer size
#define USB_EP_COMBINED_BUF_SIZE ((USB_EP_TX_BUF_SIZE + USB_EP_RX_BUF_SIZE)) ///< Combined buffer size in a single direction
#define USB_EP_SUMMED_BUF_SIZE (USB_EP_COMBINED_BUF_SIZE * USB_NUM_OF_ENDPOINTS) ///< Summed size for each endpoint in each direction
static USBDRV_GlobalState gs; ///< Global USB state
static uint8_t buf[USB_EP_SUMMED_BUF_SIZE] DWORD_ALIGN; ///< Transmit/Receive buffer
static UsbDrv_IN_cb cbs[USB_NUM_OF_ENDPOINTS]; ///< Callbacks for IN completion
// FIXME: ez lehet, hogy pont fordítva van...
#define USB_EP_GET_EP0_BUFFER() (gs.buf)
#define USB_EP_GET_TX_BUFFER(ep) (gs.buf + ((ep) * USB_EP_COMBINED_BUF_SIZE) + USB_EP_RX_BUF_SIZE)
#define USB_EP_GET_RX_BUFFER(ep) (gs.buf + ((ep) * USB_EP_COMBINED_BUF_SIZE))
/** \cond false */
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
/** \endcond */
// ------------------------
void usbdrv_register_IN_complete_cb(uint8_t ep, UsbDrv_IN_cb cb) {
cbs[ep] = cb;
}
// ------------------------
static UsbDrv_DrvIntf drvIntf;
void usbdrv_init_intf() {
drvIntf.init = usbdrv_init;
drvIntf.reset = usbdrv_reset;
drvIntf.stall = usbdrv_stall_endpoint;
drvIntf.arm_IN = usbdrv_arm_IN_endpoint;
drvIntf.arm_OUT = usbdrv_arm_OUT_endpoint;
drvIntf.set_address = usbdrv_set_address;
drvIntf.set_config = usbdrv_fetch_endpoint_configuration;
drvIntf.autoarm = usbdrv_autoarm_OUT_endpoint;
drvIntf.en_ep_irq = usbdrv_enable_endpoint_interrupt;
drvIntf.reg_IN_cb = usbdrv_register_IN_complete_cb;
}
UsbDrv_DrvIntf *usbdrv_get_intf() {
return &drvIntf;
}
// ------------------------
// issue a reset
static void usbdrv_hw_reset() {
SET_BIT(USBG->BASE_CTRL, USBFS_UC_RESET_SIE); // assert reset
USB_DELAY(1); // insert some delay
CLEAR_BIT(USBG->BASE_CTRL, USBFS_UC_RESET_SIE); // release reset
return;
}
// initialize USB peripheral
void usbdrv_periph_init(bool reset) {
if (!reset) {
// enable USB clock
USB_CLOCK_ENABLE();
// trigger a reset (FIXME: biztos jó ez így?)
usbdrv_hw_reset();
}
// turn off the transciever
usbdrv_power_and_connect(false);
// select device mode with internal pullups
CLEAR_BIT(USBG->BASE_CTRL, USBFS_UC_HOST_MODE); // device mode
SET_BIT(USBG->BASE_CTRL, USBFS_UC_SYS_CTRL3); // pullup activation
// set Full-Speed operation (TODO: lehet, hogy a low-speed-et is érdemes megírni)
CLEAR_BIT(USBG->BASE_CTRL, USBFS_UC_LOW_SPEED); // (global)
CLEAR_BIT(USBG->UDEV_CTRL, USBFS_UD_LOW_SPEED); // (device)
// set automatic NAK reporting
SET_BIT(USBG->BASE_CTRL, USBFS_UC_INT_BUSY);
// clear all interrupts
USBG->INT_FG = 0xFF;
// allow specific interrupts
uint32_t intmask =
USBFS_UIE_BUS_RST | // Bus reset
USBFS_UIE_TRANSFER | // Transfer complete interrupt
USBFS_UIE_SUSPEND | // Bues suspend or wakeup interrupt
USBFS_UIE_FIFO_OV; // FIFO overflow interrupt
USBG->INT_EN = intmask;
if (!reset) {
// flush Tx and Rx FIFOs
usbdrv_clear_all_fifos();
}
}
// initialize global state
void usbdrv_init_global_state() {
// clear state
memset(&gs, 0, sizeof(USBDRV_GlobalState));
// clear IN complete callbacks
memset(&cbs, 0, sizeof(UsbDrv_IN_cb) * USB_NUM_OF_ENDPOINTS);
// initialize receive buffer
gs.buf = buf;
}
/**
* Hook for initializing modules after the low-level driver has been initialized
* but has not been connected to the bus yet.
*/
__weak void usbdrv_init_hook() {
return;
}
// initialize USB subsystem
void usbdrv_init() {
USB_IRQ_DISABLE(USB_IRQ_N); // disable USB interrupts
usbdrv_init_global_state();
usbdrv_init_intf();
usbdrv_gpio_init();
usbdrv_periph_init(false);
usbdrv_initial_ep0_setup();
usbdrv_init_hook(); // <---
usbdrv_power_and_connect(true);
USB_IRQ_SET_PRIORITY(USB_IRQ_N, USB_IRQ_PRIORITY);
USB_IRQ_ENABLE(USB_IRQ_N);
}
void usbdrv_reset() {
USB_IRQ_DISABLE(USB_IRQ_N); // disable USB interrupts
usbdrv_hw_reset(); // hardware reset
usbdrv_init_global_state();
usbdrv_periph_init(true);
usbdrv_initial_ep0_setup();
usbdrv_power_and_connect(true);
USB_IRQ_SET_PRIORITY(USB_IRQ_N, USB_IRQ_PRIORITY);
USB_IRQ_ENABLE(USB_IRQ_N);
}
// connect to or disconnect from the bus
void usbdrv_power_and_connect(bool en) {
if (en) { // ON
SET_BIT(USBG->BASE_CTRL, USBFS_UC_DEV_PU_EN); // enable USB device and internal pull-ups (global)
SET_BIT(USBG->UDEV_CTRL, USBFS_UD_PORT_EN); // enable USB device physical port (device)
SET_BIT(USBG->BASE_CTRL, USBFS_UC_DMA_EN); // enable DMA operation and interrupts
} else { // OFF
CLEAR_BIT(USBG->BASE_CTRL, USBFS_UC_DEV_PU_EN); // disable...
CLEAR_BIT(USBG->UDEV_CTRL, USBFS_UD_PORT_EN); // ...
CLEAR_BIT(USBG->BASE_CTRL, USBFS_UC_DMA_EN); // ...
}
}
// -------------------
// clear all FIFOs
void usbdrv_clear_all_fifos() {
SET_BIT(USBG->BASE_CTRL, USBFS_UC_CLR_ALL);
USB_DELAY(1);
CLEAR_BIT(USBG->BASE_CTRL, USBFS_UC_CLR_ALL);
}
// ---------------------
typedef struct {
volatile uint8_t *UEP_MOD; ///< EP mode control register
uint8_t RX_EN; ///< EP receive enable flag
uint8_t TX_EN; ///< EP transmit enable flag
uint8_t BUF_MOD; ///< EP buffer mode flag
volatile uint32_t *BUF_START_ADDR; ///< EP buffer start address
volatile uint16_t *TRANSMIT_LENGTH; ///< EP transmit length register
volatile uint8_t *TX_CTRL; ///< EP transmit control register
volatile uint8_t *RX_CTRL; ///< EP receive control register
} USB_EP_Ctrl;
// clang-format off
static const USB_EP_Ctrl USB_EPCtrl[USB_MAX_NUM_OF_ENDPOINTS] = {
{NULL, 0, 0, 0, &(USBG->UEP0_DMA), &(USBG->UEP0_TX_LEN), &(USBG->UEP0_TX_CTRL), &(USBG->UEP0_RX_CTRL)}, // EP0
{&(USBG->UEP4_1_MOD), USBFS_UEP1_RX_EN, USBFS_UEP1_TX_EN, USBFS_UEP1_BUF_MOD, &(USBG->UEP1_DMA), &(USBG->UEP1_TX_LEN), &(USBG->UEP1_TX_CTRL), &(USBG->UEP1_RX_CTRL) }, // EP1
{&(USBG->UEP2_3_MOD), USBFS_UEP2_RX_EN, USBFS_UEP2_TX_EN, USBFS_UEP2_BUF_MOD, &(USBG->UEP2_DMA), &(USBG->UEP2_TX_LEN), &(USBG->UEP2_TX_CTRL), &(USBG->UEP2_RX_CTRL) }, // EP2
{&(USBG->UEP2_3_MOD), USBFS_UEP3_RX_EN, USBFS_UEP3_TX_EN, USBFS_UEP3_BUF_MOD, &(USBG->UEP3_DMA), &(USBG->UEP3_TX_LEN), &(USBG->UEP3_TX_CTRL), &(USBG->UEP3_RX_CTRL) }, // EP3
{&(USBG->UEP4_1_MOD), USBFS_UEP4_RX_EN, USBFS_UEP4_TX_EN, USBFS_UEP4_BUF_MOD, &(USBG->UEP4_DMA), &(USBG->UEP4_TX_LEN), &(USBG->UEP4_TX_CTRL), &(USBG->UEP4_RX_CTRL) }, // EP4
{&(USBG->UEP5_6_MOD), USBFS_UEP5_RX_EN, USBFS_UEP5_TX_EN, USBFS_UEP5_BUF_MOD, &(USBG->UEP5_DMA), &(USBG->UEP5_TX_LEN), &(USBG->UEP5_TX_CTRL), &(USBG->UEP5_RX_CTRL) }, // EP5
{&(USBG->UEP5_6_MOD), USBFS_UEP6_RX_EN, USBFS_UEP6_TX_EN, USBFS_UEP6_BUF_MOD, &(USBG->UEP6_DMA), &(USBG->UEP6_TX_LEN), &(USBG->UEP6_TX_CTRL), &(USBG->UEP6_RX_CTRL) }, // EP6
{&(USBG->UEP7_MOD), USBFS_UEP7_RX_EN, USBFS_UEP7_TX_EN, USBFS_UEP7_BUF_MOD, &(USBG->UEP7_DMA), &(USBG->UEP7_TX_LEN), &(USBG->UEP7_TX_CTRL), &(USBG->UEP7_RX_CTRL) }, // EP7
};
// clang-format on
// configure USB endpoint
void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg) {
const USB_EP_Ctrl *epc = USB_EPCtrl + ep; // get Endpoint control
if (dir == USB_OUT) { // ---- OUT ----
if (ep != 0) { // SPECIAL treatment to EP0, that one cannot be turned on or off
CLEAR_BIT(*epc->UEP_MOD, epc->BUF_MOD); // clear BUFMOD bit
SET_BIT(*epc->UEP_MOD, epc->RX_EN); // set RX enable bit
}
SET_BIT(*epc->RX_CTRL, USBFS_UEP_R_AUTO_TOG); // turn on automatic sync bit toggling
// ---- common for all endpoints ----
// NAK processing
CLEAR_BIT(*epc->RX_CTRL, USBFS_UEP_R_RES_MASK);
if (cfg->responding_NAK) {
SET_BIT(*epc->RX_CTRL, USBFS_UEP_R_RES_NAK); // send NAK
} else {
usbdrv_arm_OUT_endpoint(ep, cfg->max_packet_size);
}
} else { // ---- IN ----
if (ep != 0) { // SPECIAL treatment to EP0, that one cannot be turned on or off
CLEAR_BIT(*epc->UEP_MOD, epc->BUF_MOD); // clear BUFMOD bit
SET_BIT(*epc->UEP_MOD, epc->TX_EN); // set TX enable bit
SET_BIT(*epc->TX_CTRL, USBFS_UEP_T_AUTO_TOG); // turn on automatic sync bit toggling
}
// ---- common for all endpoints ----
// NAK processing
CLEAR_BIT(*epc->TX_CTRL, USBFS_UEP_T_RES_MASK);
// if (cfg->responding_NAK) {
SET_BIT(*epc->TX_CTRL, USBFS_UEP_T_RES_NAK); // send NAK by default
//}
}
}
// deconfigure USB endpoint
void usbdrv_deconfigure_endpoint(uint8_t ep, uint8_t dir) {
if (ep == 0) { // EP0 cannot be deconfigured
return;
}
const USB_EP_Ctrl *epc = USB_EPCtrl + ep; // get Endpoint control
if (dir == USB_OUT) { // ---- OUT ----
CLEAR_BIT(*epc->UEP_MOD, epc->BUF_MOD); // clear BUFMOD bit
CLEAR_BIT(*epc->UEP_MOD, epc->RX_EN); // clear RX enable bit
} else { // ---- IN ----
CLEAR_BIT(*epc->UEP_MOD, epc->BUF_MOD); // clear BUFMOD bit
CLEAR_BIT(*epc->UEP_MOD, epc->TX_EN); // clear TX enable bit
}
}
// ---------------------
// stall endpoint
void usbdrv_stall_endpoint(uint8_t ep, uint8_t dir, bool stall) {
const USB_EP_Ctrl *epc = USB_EPCtrl + ep;
if (dir == USB_IN) {
CLEAR_BIT(*epc->RX_CTRL, USBFS_UEP_R_RES_MASK);
if (stall) {
SET_BIT(*epc->RX_CTRL, USBFS_UEP_R_RES_STALL); // stall endpoint
}
} else {
CLEAR_BIT(*epc->RX_CTRL, USBFS_UEP_T_RES_MASK);
if (stall) {
SET_BIT(*epc->TX_CTRL, USBFS_UEP_T_RES_STALL); // stall endpoint
}
}
}
// ----------------
// write data to specific endpoint FIFO
uint32_t usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) {
// proceed only if it was not armed before
if (gs.ep_IN[ep].txp) {
return 0;
}
// get Endpoint control data
const USB_EP_Ctrl *epc = USB_EPCtrl + ep;
// constrain copy length
uint16_t txLen = MIN(len, USB_EP_TX_BUF_SIZE);
// copy data to the output buffer
if (txLen > 0) {
uint8_t *txBuf = (ep == 0) ? USB_EP_GET_EP0_BUFFER() : USB_EP_GET_TX_BUFFER(ep);
memcpy(txBuf, data, txLen);
}
// set transmission length
*epc->TRANSMIT_LENGTH = txLen;
// append ZLP only, if packet size is MAX_PCKT_SIZE (this way to ZLP is injected in a longer packet whose first part is limited to 64 bytes)
gs.ep_IN[ep].zlp_next = (len == USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS);
// non-EP0 must begin responding with DATA0
// if (ep != 0) {
// CLEAR_BIT(*epc->TX_CTRL, USBFS_UEP_T_TOG);
// }
// signal that transmission is in progress
gs.ep_IN[ep].txp = true;
// enable transmission
CLEAR_BIT(*epc->TX_CTRL, USBFS_UEP_T_RES_MASK);
// return with written size
return txLen;
}
// arm OUT endpoint
uint32_t usbdrv_arm_OUT_endpoint(uint8_t ep, uint16_t size) {
// proceed only if it was not armed before
if (gs.ep_OUT[ep].txp) {
return 0;
}
// cap size at max packet size defined for this endpoint
size = MIN(gs.ep_OUT[ep].max_packet_size, size);
// get Endpoint control data
const USB_EP_Ctrl *epc = USB_EPCtrl + ep;
// copy data to the output buffer
// uint32_t *rxBuf = (uint32_t *)(((uint32_t)(*epc->BUF_START_ADDR)) + USB_EP_TX_BUF_SIZE);
// enable transmission
CLEAR_BIT(*epc->RX_CTRL, USBFS_UEP_R_RES_MASK);
// signal that transmission is in progress
gs.ep_OUT[ep].txp = true;
// return with armed size
return size;
}
void usbdrv_autoarm_OUT_endpoint(uint8_t ep) {
gs.ep_OUT[ep].autoarm = true;
}
// ----------------
void usbdrv_enable_endpoint_interrupt(uint8_t ep, uint8_t dir, bool en) {
return; // FIXME
}
// 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_allocate_buffers();
usbdrv_apply_endpoint_config();
}
// #define USBDRV_ADDR_TABLE_STR_LEN (255)
// static char usbdrv_addr_table[USBDRV_ADDR_TABLE_STR_LEN + 1];
// build buffer structure, allocate buffers (compute addresses)
void usbdrv_allocate_buffers() {
for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) {
*USB_EPCtrl[i].BUF_START_ADDR = (uint32_t)USB_EP_GET_RX_BUFFER(i);
USBDRV_EpConfig *cfg = &gs.ep_IN[i];
if (cfg->is_configured) {
cfg->buffer_address = (uint32_t)(*USB_EPCtrl[i].BUF_START_ADDR); // store DMA start address
cfg->zlp_next = false;
cfg->txp = false; // no transfer is in progress
}
}
}
// ---------------------
// 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_allocate_buffers();
// configure endpoints
usbdrv_apply_endpoint_config();
}
// ---------------------
void usbdrv_set_address(uint8_t addr) {
gs.address = addr;
gs.address_pending = true; // cannot set address immediately, have to wait for next IN transaction
}
// ---------------------
/**
* Function prototype for processing SETUP packets. This function is expected to be
* overridden by the application.
*
* @param data pointer to the SETUP packet
* @param size size of the packet
* @param stage stage of the SETUP transaction
*/
__weak void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stage) {
// always pass ALIGNED data!
return;
}
/**
* Function prototype for processing non-SETUP packets. This function is expected to be
* overridden by the application.
*
* @param cbcpd pointer to callback compound
*/
__weak void usbcore_process_nonsetup_event(UsbDrv_CallbackCompound *cbcpd) {
return;
}
#define USB_INT_STATUS_GET_EP(s) ((s) & USBFS_UIS_ENDP_MASK) ///< Get EP number from interrupt status
#define USB_INT_STATUS_GET_TOKEN(s) ((s) & USBFS_UIS_TOKEN_MASK) ///< Get token of the interrupt (no shifting!)
#define USB_INT_STATUS_IS_NAK(s) (((s) & USBFS_UIS_IS_NAK) != 0) ///< Was it a NAK transmission?
// TODO: egy input buffer kellene ide, mert különben egy lassú feldolgozás felborít mindent!
// 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
usbdrv_reset();
// invoke Reset Done notification
if (drvIntf.rst_notify != NULL) {
drvIntf.rst_notify();
}
USBMSG("RESET\n");
return;
} else if (evt_code == USB_EVT_TRANSFER_COMPLETION) { // transfer complete
uint8_t status = USBG->INT_ST; // read interrupt status register
uint8_t token = USB_INT_STATUS_GET_TOKEN(status);
uint8_t ep = USB_INT_STATUS_GET_EP(status);
bool nak = USB_INT_STATUS_IS_NAK(status);
uint16_t len = USBG->RX_LEN;
UsbDrv_CallbackCompound cbcpd;
cbcpd.ep = ep;
switch (token) {
case USBFS_UIS_TOKEN_OUT: {
if (ep == 0) {
uint8_t *rxBuf = USB_EP_GET_EP0_BUFFER();
usbcore_process_setup_pckt(rxBuf, len, UST_DATA);
} else {
cbcpd.dir = USB_OUT;
cbcpd.code = USB_CBC_OUT;
cbcpd.data = USB_EP_GET_RX_BUFFER(ep);
cbcpd.size = len;
usbcore_process_nonsetup_event(&cbcpd);
}
if (ep != 0) {
SET_BIT(*USB_EPCtrl[ep].RX_CTRL, USBFS_UEP_R_RES_NAK); // send NAK
gs.ep_OUT[ep].txp = false; // transfer concluded
}
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 USBFS_UIS_TOKEN_IN: {
if (gs.ep_IN[ep].zlp_next) {
usbdrv_arm_IN_endpoint(ep, NULL, 0); // send ZLP
} else { // no ZLP
cbcpd.dir = USB_IN;
cbcpd.code = USB_CBC_IN_DONE;
cbcpd.data = NULL;
cbcpd.size = 0;
SET_BIT(*USB_EPCtrl[ep].TX_CTRL, USBFS_UEP_T_RES_NAK); // send NAK
*USB_EPCtrl[ep].TRANSMIT_LENGTH = 0; // zero transmit length
// usbcore_process_nonsetup_event(&cbcpd);
// change address
if (gs.address_pending) {
USBG->DEV_ADDR = gs.address;
gs.address_pending = false;
}
gs.ep_IN[ep].txp = false; // transfer concluded
// toggle EP0 synchronization bit
if (ep == 0) {
bool tog = !GET_BIT(*USB_EPCtrl[0].TX_CTRL, USBFS_UEP_T_TOG);
if (tog) { // DATA1 is the next transfer
SET_BIT(*USB_EPCtrl[0].TX_CTRL, USBFS_UEP_T_TOG);
} else { // DATA0 is the next transfer
CLEAR_BIT(*USB_EPCtrl[0].TX_CTRL, USBFS_UEP_T_TOG);
}
}
if (nak) {
__NOP();
}
// invoke callback if registered
if (cbs[ep] != NULL) {
UsbDrv_IN_cb cb = cbs[ep]; // fetch function pointer
cbs[ep] = NULL; // clear callback
cb(ep); // invoke the callback
}
}
} break;
case USBFS_UIS_TOKEN_SETUP: {
SET_BIT(*USB_EPCtrl[0].TX_CTRL, USBFS_UEP_T_TOG); // secure starting with DATA1 if an IN follows
uint8_t *rxBuf = USB_EP_GET_EP0_BUFFER(); // the receive buffer
usbcore_process_setup_pckt(rxBuf, 8, UST_SETUP); // process the setup packet
} break;
}
}
}
// ---------------------
#define PROCESS_EVENT(evt, data) usbdrv_process_event((evt), (data))
void USB_IRQ_HANDLER() {
uint32_t ints = USBG->INT_FG;
// USB reset
if (ints & USBFS_UIF_BUS_RST) {
SET_BIT(USBG->INT_FG, USBFS_UIF_BUS_RST); // clear interrupt
// PROCESS_EVENT(USB_EVT_USB_RESET, NULL); // process event
}
// USB Suspend
if (ints & USBFS_UIF_SUSPEND) {
SET_BIT(USBG->INT_FG, USBFS_UIF_SUSPEND); // clear interrupt
USBMSG("SUSPEND\n");
}
// FIFO overflow
if (ints & USBFS_UIF_FIFO_OV) {
SET_BIT(USBG->INT_FG, USBFS_UIF_FIFO_OV); // clear interrupt
}
// Transfer complete
if (ints & USBFS_UIF_TRANSFER) {
PROCESS_EVENT(USB_EVT_TRANSFER_COMPLETION, NULL); // process event
SET_BIT(USBG->INT_FG, USBFS_UIF_TRANSFER); // clear interrupt
}
return;
}