Compare commits
7 Commits
drv_init_h
...
master
Author | SHA1 | Date | |
---|---|---|---|
c0ea9c3ab3 | |||
88bb420b26 | |||
1737696a8b | |||
f5ccd09612 | |||
0e0fcbb121 | |||
7960b2957e | |||
9753e7ded8 |
13
class/acm.c
13
class/acm.c
@ -33,6 +33,7 @@ static osEventFlagsId_t flags; ///< Event flags
|
|||||||
#define USB_ACM_HOSTBOUND_DATA_AVAIL (0x04) ///< Hostbound data is available
|
#define USB_ACM_HOSTBOUND_DATA_AVAIL (0x04) ///< Hostbound data is available
|
||||||
|
|
||||||
#define USB_ACM_LOOP_TIMEOUT_TICKS (100) ///< Main loop timeout for interrupt status transmission
|
#define USB_ACM_LOOP_TIMEOUT_TICKS (100) ///< Main loop timeout for interrupt status transmission
|
||||||
|
#define USB_ACM_INITIAL_DELAY_TICKS (100) ///< Delay before sending data
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
@ -41,13 +42,15 @@ static void thread_usb_acm(void *arg) {
|
|||||||
|
|
||||||
osEventFlagsSet(flags, USB_ACM_DATA_IN_DONE); // assume we can write to the data endpoint
|
osEventFlagsSet(flags, USB_ACM_DATA_IN_DONE); // assume we can write to the data endpoint
|
||||||
|
|
||||||
|
osDelay(USB_ACM_INITIAL_DELAY_TICKS); // inject some initial delay
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
uint32_t signals = osEventFlagsWait(flags, USB_ACM_HOSTBOUND_DATA_AVAIL, osFlagsWaitAny, USB_ACM_LOOP_TIMEOUT_TICKS);
|
uint32_t signals = osEventFlagsWait(flags, USB_ACM_HOSTBOUND_DATA_AVAIL, osFlagsWaitAny, USB_ACM_LOOP_TIMEOUT_TICKS);
|
||||||
|
|
||||||
if (signals != osErrorTimeout) { // check timeout
|
if (signals != osErrorTimeout) { // check timeout
|
||||||
if (signals & USB_ACM_HOSTBOUND_DATA_AVAIL) { // data hostbound available
|
if (signals & USB_ACM_HOSTBOUND_DATA_AVAIL) { // data hostbound available
|
||||||
do {
|
do {
|
||||||
osEventFlagsWait(flags, USB_ACM_DATA_IN_DONE, osFlagsWaitAny, 1000); // wait for the IN DONE flag
|
osEventFlagsWait(flags, USB_ACM_DATA_IN_DONE, osFlagsWaitAny, 1); // wait for the IN DONE flag
|
||||||
uint32_t readSize = bfifo_read(&fifo, tx_buffer, USB_ACM_PCKT_BUFSIZE); // read from the fifo
|
uint32_t readSize = bfifo_read(&fifo, tx_buffer, USB_ACM_PCKT_BUFSIZE); // read from the fifo
|
||||||
if (readSize > 0) {
|
if (readSize > 0) {
|
||||||
uint32_t writeSize = usbcore_schedule_transmission(acms.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer
|
uint32_t writeSize = usbcore_schedule_transmission(acms.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer
|
||||||
@ -85,18 +88,18 @@ void usb_acm_init(const Usb_Acm_EpAssignments *as) {
|
|||||||
// communication parameters have not been set
|
// communication parameters have not been set
|
||||||
acms.commInit = false;
|
acms.commInit = false;
|
||||||
|
|
||||||
|
// from now on CDC module is considered initialized
|
||||||
|
acms.moduleInit = true;
|
||||||
|
|
||||||
// create flags
|
// create flags
|
||||||
flags = osEventFlagsNew(NULL);
|
flags = osEventFlagsNew(NULL);
|
||||||
|
|
||||||
// create thread
|
// create thread
|
||||||
osThreadAttr_t attr;
|
osThreadAttr_t attr;
|
||||||
memset(&attr, 0, sizeof(osThreadAttr_t));
|
memset(&attr, 0, sizeof(osThreadAttr_t));
|
||||||
attr.stack_size = 1024;
|
attr.stack_size = 512;
|
||||||
attr.name = "acm";
|
attr.name = "acm";
|
||||||
th = osThreadNew(thread_usb_acm, NULL, &attr);
|
th = osThreadNew(thread_usb_acm, NULL, &attr);
|
||||||
|
|
||||||
// from now on CDC module is considered initialized
|
|
||||||
acms.moduleInit = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_cdc_review_comm_init() {
|
static void usb_cdc_review_comm_init() {
|
||||||
|
655
driver/ch32/usb_drv.c
Normal file
655
driver/ch32/usb_drv.c
Normal file
@ -0,0 +1,655 @@
|
|||||||
|
#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;
|
||||||
|
}
|
251
driver/ch32/usb_drv.h
Normal file
251
driver/ch32/usb_drv.h
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#ifndef CH32_USB_DRV
|
||||||
|
#define CH32_USB_DRV
|
||||||
|
|
||||||
|
#include "../../usb_common_types.h"
|
||||||
|
|
||||||
|
#include "../../usb_driver_common.h"
|
||||||
|
|
||||||
|
// maximum number of endpoints that can be supported
|
||||||
|
#define USB_MAX_NUM_OF_ENDPOINTS (8)
|
||||||
|
|
||||||
|
// number of supported endpoints
|
||||||
|
#ifndef USB_NUM_OF_ENDPOINTS // number of endpoints can be overridden to conserve memory
|
||||||
|
#define USB_NUM_OF_ENDPOINTS (USB_MAUSB_MAX_NUM_OF_ENDPOINTS) // set it to the maximum that this type of module can support
|
||||||
|
#else
|
||||||
|
#if USB_MAX_NUM_OF_ENDPOINTS > USB_MAX_NUM_OF_ENDPOINTS // do not allow greater number of endpoints than what the device supports
|
||||||
|
#undef USB_NUM_OF_ENDPOINTS
|
||||||
|
#define USB_NUM_OF_ENDPOINTS (USB_MUSB_MAX_NUM_OF_ENDPOINTS)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// non isochronous transfers
|
||||||
|
#define USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS (64)
|
||||||
|
|
||||||
|
// isochronous transfers
|
||||||
|
#define USB_MAX_FS_PCKT_SIZE_ISOCHRONOUS (1023)
|
||||||
|
|
||||||
|
// endpoint configuration structure
|
||||||
|
typedef struct {
|
||||||
|
uint16_t max_packet_size; // maximum packet size
|
||||||
|
bool8_t responding_NAK; // indicates if endpoint responds with NAK
|
||||||
|
uint8_t type; // endpoint type
|
||||||
|
uint8_t service_interval; // service interval
|
||||||
|
|
||||||
|
// calculated values
|
||||||
|
|
||||||
|
uint8_t is_configured; // the endpoint is in use
|
||||||
|
uint16_t buffer_address; // address in the FIFO
|
||||||
|
bool8_t zlp_next; // indicates, that ZLP should be transmitted at the end of the current transfer (for IN endpoints)
|
||||||
|
bool8_t autoarm; // automatically arm endpoint (for OUT endpoints)
|
||||||
|
bool8_t txp; // transfer in progress
|
||||||
|
} USBDRV_EpConfig;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
USB_EVT_USB_RESET, // bus reset received
|
||||||
|
USB_EVT_TRANSFER_COMPLETION,
|
||||||
|
} USBDRV_EventCode;
|
||||||
|
|
||||||
|
// USB device state
|
||||||
|
typedef struct {
|
||||||
|
USBDRV_EpConfig ep_OUT[USB_NUM_OF_ENDPOINTS]; // OUT endpoint configs
|
||||||
|
USBDRV_EpConfig ep_IN[USB_NUM_OF_ENDPOINTS]; // IN endpoint configs
|
||||||
|
uint8_t *buf; // pointer to the receive buffer (this way declaration can be separated)
|
||||||
|
uint8_t address; // device address
|
||||||
|
bool address_pending; // address change is pending
|
||||||
|
} USBDRV_GlobalState;
|
||||||
|
|
||||||
|
// event data
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint8_t pckt_status; // read packet status
|
||||||
|
uint8_t data_pid; // data PID
|
||||||
|
uint8_t byte_count; // byte count
|
||||||
|
uint8_t ep_num; // read endpoint number
|
||||||
|
} rx; // receive event data
|
||||||
|
} USBDRV_EventData;
|
||||||
|
|
||||||
|
// event compound for queueing
|
||||||
|
typedef struct {
|
||||||
|
uint32_t code; // event code
|
||||||
|
USBDRV_EventData data; // accompaining event data
|
||||||
|
} USBDRV_EventCompound;
|
||||||
|
|
||||||
|
// ------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully initialize USB driver.
|
||||||
|
*/
|
||||||
|
void usbdrv_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset USB driver.
|
||||||
|
*/
|
||||||
|
void usbdrv_reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init driver's global state.
|
||||||
|
*/
|
||||||
|
void usbdrv_init_global_state();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize driver interface.
|
||||||
|
*/
|
||||||
|
void usbdrv_init_intf();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* USB peripheral initialization.
|
||||||
|
*
|
||||||
|
* @param reset only perform an after-reset reinitialization
|
||||||
|
*/
|
||||||
|
void usbdrv_periph_init(bool reset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all TX and RX FIFOs.
|
||||||
|
*/
|
||||||
|
void usbdrv_clear_all_fifos();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Power down or up the USB peripheral. Also control built-in transciever.
|
||||||
|
*
|
||||||
|
* @param en power on/off
|
||||||
|
*/
|
||||||
|
void usbdrv_power_and_connect(bool en);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure USB Endpoint.
|
||||||
|
*
|
||||||
|
* @param ep index of the endpoint
|
||||||
|
* @param dir direction of the endpoint to be configured
|
||||||
|
* @param cfg pointer to the configuration details
|
||||||
|
*/
|
||||||
|
void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deconfigure a specific USB Endpoint.
|
||||||
|
*
|
||||||
|
* @param ep index of the endpoint
|
||||||
|
* @param dir direction of the endpoint to be deconfigured
|
||||||
|
*/
|
||||||
|
void usbdrv_deconfigure_endpoint(uint8_t ep, uint8_t dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stall a particular USB Endpoint.
|
||||||
|
*
|
||||||
|
* @param ep index of the endpoint to be stalled
|
||||||
|
* @param dir direction of the endpoint
|
||||||
|
* @param stall enable/disable stalling
|
||||||
|
*/
|
||||||
|
void usbdrv_stall_endpoint(uint8_t ep, uint8_t dir, bool stall);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup and prepare Endpoint to transfer data towards the host.
|
||||||
|
* May be called multiple times in a single transmission, since
|
||||||
|
* large messages do not have to fit into a single buffer.
|
||||||
|
*
|
||||||
|
* @param ep index of the Endpoint
|
||||||
|
* @param data pointer to the data
|
||||||
|
* @param len length of the data to be read
|
||||||
|
* @return number of bytes read from the data buffer
|
||||||
|
*/
|
||||||
|
uint32_t usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare Endpoint to expect data reception from the host.
|
||||||
|
*
|
||||||
|
* @param ep index of the Endpoint
|
||||||
|
* @param size expected reception length
|
||||||
|
* @return message length that the endpoint can support during next reception
|
||||||
|
*/
|
||||||
|
uint32_t usbdrv_arm_OUT_endpoint(uint8_t ep, uint16_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable OUT Endpoint auto arming.
|
||||||
|
* Right after a transaction completes the USB core
|
||||||
|
* automatically prepares the endpoint for the next
|
||||||
|
* reception if this feature is enabled.
|
||||||
|
*
|
||||||
|
* @param ep index of the Endpoint
|
||||||
|
*/
|
||||||
|
void usbdrv_autoarm_OUT_endpoint(uint8_t ep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mask or unmask Endpoint interrupt generation.
|
||||||
|
*
|
||||||
|
* @param ep index of the Endpoint
|
||||||
|
* @param dir direction of the Endpoint
|
||||||
|
* @param en enable/disable interrupt
|
||||||
|
*/
|
||||||
|
void usbdrv_enable_endpoint_interrupt(uint8_t ep, uint8_t dir, bool en);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preload Endpoint config. Do not write configuration
|
||||||
|
* to the hardware right away, just buffer them in.
|
||||||
|
*
|
||||||
|
* @param ep index of the Endpoint
|
||||||
|
* @param dir direction of the Endpint
|
||||||
|
* @param cfg pointer to Endpoint configuration
|
||||||
|
*/
|
||||||
|
void usbdrv_preload_endpoint_config(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear ALL Endpoints' preload configurations.
|
||||||
|
* Does NOT clear configuration in the hardware!
|
||||||
|
*/
|
||||||
|
void usbdrv_clear_endpoint_config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply preloaded Endpoint configuration, write
|
||||||
|
* Endpoint config to the hardware.
|
||||||
|
*/
|
||||||
|
void usbdrv_apply_endpoint_config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a specific configuration from the descriptor
|
||||||
|
* dump, preload Endpoint settings and finally apply them.
|
||||||
|
*
|
||||||
|
* @param config_index configuration index
|
||||||
|
*/
|
||||||
|
void usbdrv_fetch_endpoint_configuration(uint8_t config_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct FIFO considering configured Endpoints.
|
||||||
|
*/
|
||||||
|
void usbdrv_allocate_buffers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply an initial EP0 setup.
|
||||||
|
*/
|
||||||
|
void usbdrv_initial_ep0_setup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set USB device address.
|
||||||
|
*
|
||||||
|
* @param addr device address
|
||||||
|
*/
|
||||||
|
void usbdrv_set_address(uint8_t addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get driver interface.
|
||||||
|
*
|
||||||
|
* @return pointer to the driver interface
|
||||||
|
*/
|
||||||
|
UsbDrv_DrvIntf *usbdrv_get_intf();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register callback for IN transaction complete.
|
||||||
|
*
|
||||||
|
* @param ep index of the Endpoint
|
||||||
|
* @param cb pointer to the callback function
|
||||||
|
*/
|
||||||
|
void usbdrv_register_IN_complete_cb(uint8_t ep, UsbDrv_IN_cb cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Endpoint interrupt flag.
|
||||||
|
*
|
||||||
|
* @param ep index of the Endpoint
|
||||||
|
* @param dir direction of the Endpoint
|
||||||
|
* @return flag was set
|
||||||
|
*/
|
||||||
|
bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir);
|
||||||
|
|
||||||
|
#endif /* CH32_USB_DRV */
|
38
examples/configs/flatUSB_config_ch32f207_fs.h
Normal file
38
examples/configs/flatUSB_config_ch32f207_fs.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef CONFIGS_FLATUSB_CONFIG
|
||||||
|
#define CONFIGS_FLATUSB_CONFIG
|
||||||
|
#ifndef SRC_FLATUSB_CONFIG
|
||||||
|
#define SRC_FLATUSB_CONFIG
|
||||||
|
|
||||||
|
#include <ch32f20x_usb.h>
|
||||||
|
#include <ch32f20x_rcc.h>
|
||||||
|
#include <ch32f20x.h>
|
||||||
|
|
||||||
|
static inline void usbdrv_gpio_init(void) {
|
||||||
|
// turn ON GPIOA clocks
|
||||||
|
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define USB_IRQ_N OTG_FS_IRQn
|
||||||
|
#define USB_IRQ_HANDLER OTG_FS_IRQHandler
|
||||||
|
#define USB_IRQ_PRIORITY (8)
|
||||||
|
#define USB_IRQ_SET_PRIORITY(irq, priority) NVIC_SetPriority((irq),(priority))
|
||||||
|
#define USB_IRQ_ENABLE(irq) NVIC_EnableIRQ((irq))
|
||||||
|
#define USB_IRQ_DISABLE(irq) NVIC_DisableIRQ((irq))
|
||||||
|
|
||||||
|
// define USBG
|
||||||
|
#define USBG (USBOTG_FS)
|
||||||
|
|
||||||
|
#define USB_CLOCK_ENABLE() RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE);\
|
||||||
|
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE)
|
||||||
|
|
||||||
|
#include "embfmt/embformat.h"
|
||||||
|
#define SNPRINTF(str, n, fmt, ...) embfmt(str, n, fmt, __VA_ARGS__)
|
||||||
|
|
||||||
|
#ifdef USBDBGMSG
|
||||||
|
#define USBMSG(...) MSG(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SRC_FLATUSB_CONFIG */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CONFIGS_FLATUSB_CONFIG */
|
31
usb.c
31
usb.c
@ -64,6 +64,22 @@ void usbcore_reset() {
|
|||||||
memset(&stups, 0, sizeof(Usb_SetupTransferState));
|
memset(&stups, 0, sizeof(Usb_SetupTransferState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response follow-up if the ful response could not fit in a single packet.
|
||||||
|
*
|
||||||
|
* @param ep endpoint number
|
||||||
|
*/
|
||||||
|
static void usbcore_response_follow_up(uint8_t ep) {
|
||||||
|
uint32_t armSize = drv->arm_IN(0, stups.fup_data, stups.fup_sz);
|
||||||
|
stups.fup_sz -= armSize; // calculate remaining follow-up size
|
||||||
|
stups.fup_data += armSize; // advance follow-up data pointer
|
||||||
|
|
||||||
|
if (stups.fup_sz > 0) { // we need further follow-ups
|
||||||
|
} else { // transfer done, remove callback
|
||||||
|
drv->reg_IN_cb(0, NULL); // remove IN callback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process SETUP packets.
|
* Process SETUP packets.
|
||||||
*
|
*
|
||||||
@ -158,7 +174,12 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
|
|||||||
}
|
}
|
||||||
|
|
||||||
// arm data transmission
|
// arm data transmission
|
||||||
drv->arm_IN(0, data, sz);
|
uint32_t armSize = drv->arm_IN(0, data, sz);
|
||||||
|
if (armSize < sz) { // if could not arm the full response at once, then hook up a callback
|
||||||
|
stups.fup_sz = sz - armSize; // follow-up size
|
||||||
|
stups.fup_data = data + armSize; // generate follow-up data pointer
|
||||||
|
drv->reg_IN_cb(0, usbcore_response_follow_up); // register follow-up callback
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +233,7 @@ void usbcore_process_nonsetup_event(UsbDrv_CallbackCompound *cbcpd) {
|
|||||||
Usb_CallbackEvent cbevt = {0}; // allocate and clear structure...
|
Usb_CallbackEvent cbevt = {0}; // allocate and clear structure...
|
||||||
cbevt.ep = cbcpd->ep; // later only fill nonzero fields
|
cbevt.ep = cbcpd->ep; // later only fill nonzero fields
|
||||||
|
|
||||||
bool discard_event = false; // only discard if event does not fit any category above
|
bool discard_event = false; // only discard if event does not fit any category below
|
||||||
switch (cbcpd->code) {
|
switch (cbcpd->code) {
|
||||||
case USB_CBC_OUT: {
|
case USB_CBC_OUT: {
|
||||||
cbevt.type = USB_CBEVT_OUT;
|
cbevt.type = USB_CBEVT_OUT;
|
||||||
@ -250,11 +271,9 @@ void usbcore_process_nonsetup_event(UsbDrv_CallbackCompound *cbcpd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void usbcore_wake_up_endpoint(uint8_t ep, uint8_t dir) {
|
|
||||||
drv->en_ep_irq(ep, dir, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void usbcore_register_IN_callback(uint8_t ep, UsbDrv_IN_cb cb) {
|
void usbcore_register_IN_callback(uint8_t ep, UsbDrv_IN_cb cb) {
|
||||||
|
bool en = cb != NULL;
|
||||||
|
drv->en_ep_irq(ep, USB_IN, en);
|
||||||
drv->reg_IN_cb(ep, cb);
|
drv->reg_IN_cb(ep, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
usb.h
10
usb.h
@ -15,6 +15,8 @@ typedef struct {
|
|||||||
USB_SetupRequest setup_req; ///< Setup request
|
USB_SetupRequest setup_req; ///< Setup request
|
||||||
uint8_t next_stage; ///< Next expected stage
|
uint8_t next_stage; ///< Next expected stage
|
||||||
bool out_complete; ///< Signals if transfer's OUT direction is complete, no later data reception is expected
|
bool out_complete; ///< Signals if transfer's OUT direction is complete, no later data reception is expected
|
||||||
|
uint16_t fup_sz; ///< Some SETUP response IN data follows
|
||||||
|
const uint8_t * fup_data; ///< Pointer on follow-up data
|
||||||
} Usb_SetupTransferState;
|
} Usb_SetupTransferState;
|
||||||
|
|
||||||
// --------------
|
// --------------
|
||||||
@ -58,14 +60,6 @@ uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t
|
|||||||
*/
|
*/
|
||||||
uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size);
|
uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size);
|
||||||
|
|
||||||
/**
|
|
||||||
* Wake up an Endpoint.
|
|
||||||
*
|
|
||||||
* @param ep index of the Endpoint
|
|
||||||
* @param dir direction of the Endpoint
|
|
||||||
*/
|
|
||||||
void usbcore_wake_up_endpoint(uint8_t ep, uint8_t dir);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a callback for IN transmission completion.
|
* Register a callback for IN transmission completion.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user