- function separation done
- a load of CMake modification
This commit is contained in:
		
							parent
							
								
									48a7ae54a3
								
							
						
					
					
						commit
						685fa77bda
					
				@ -34,19 +34,24 @@ elseif(NOT FLATUSB_DESC_SRC)
 | 
			
		||||
    message("flatUSB: No source of descriptors passed! Please either set FLATUSB_DESC_DIR to the directory containing the descriptor files OR pass descriptor files directly by defining FLATUSB_DESC_SRC and FLATUSB_DESC_HEADER!")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (NOT FLATUSB_DRIVER_SRC)
 | 
			
		||||
    message("flatUSB: No USB driver passed! It's likely an error and not intentional. Please populate FLATUSB_DRIVER_SRC with the list of the USB driver files!")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
message("flatUSB classes selected: ${FLATUSB_CLASSES}") 
 | 
			
		||||
 | 
			
		||||
set(FLATUSB_SRC
 | 
			
		||||
    ${FLATUSB_CLASSES_SRC}
 | 
			
		||||
    ${FLATUSB_DESC_SRC}
 | 
			
		||||
    ${FLATUSB_DRIVER_SRC}
 | 
			
		||||
 | 
			
		||||
    usb.c
 | 
			
		||||
    usb_callback_event.h
 | 
			
		||||
    usb_common.h
 | 
			
		||||
    usb_common_types.h
 | 
			
		||||
    usb_device_types.h
 | 
			
		||||
    usb_driver.c
 | 
			
		||||
    usb_driver.h
 | 
			
		||||
    #usb_driver.c
 | 
			
		||||
    #usb_driver.h
 | 
			
		||||
    usb.h
 | 
			
		||||
 | 
			
		||||
    # utils/gen_queue.c
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								class/cdc.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								class/cdc.c
									
									
									
									
									
								
							@ -5,7 +5,6 @@
 | 
			
		||||
#include "../usb_device_types.h"
 | 
			
		||||
 | 
			
		||||
#include "../usb.h"
 | 
			
		||||
#include "flatUSB/usb_driver.h"
 | 
			
		||||
 | 
			
		||||
#include <blocking_io/blocking_fifo.h>
 | 
			
		||||
 | 
			
		||||
@ -56,6 +55,10 @@ void usb_cdc_init(const USB_CdcAssignments *as) {
 | 
			
		||||
 | 
			
		||||
// static uint8_t replyBuf[sizeof(USB_Cdc_LineCodingStruct)];
 | 
			
		||||
 | 
			
		||||
// static void usbcore_setup_cplt_cb(uint8_t in) {
 | 
			
		||||
//     usbcore_wake_up_endpoint(cdcs.ep_assignments.data_ep, USB_IN);
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
static void usb_cdc_review_comm_init() {
 | 
			
		||||
    // check if line coding was initialized
 | 
			
		||||
    USB_Cdc_LineCodingStruct *lc = &cdcs.line_coding;
 | 
			
		||||
@ -67,10 +70,10 @@ static void usb_cdc_review_comm_init() {
 | 
			
		||||
    // combine the above criteria
 | 
			
		||||
    cdcs.commInit = lcOk && clsOk;
 | 
			
		||||
 | 
			
		||||
    // wake up endpoint if initialized
 | 
			
		||||
    if (cdcs.commInit) {
 | 
			
		||||
        usbcore_wake_up_endpoint(cdcs.ep_assignments.data_ep, USB_IN);
 | 
			
		||||
    }
 | 
			
		||||
    // // wake up endpoint if initialized
 | 
			
		||||
    // if (cdcs.commInit) {
 | 
			
		||||
    //     usbcore_register_IN_callback(0, usbcore_setup_cplt_cb);   
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) {
 | 
			
		||||
 | 
			
		||||
@ -29,4 +29,19 @@
 | 
			
		||||
#define USBFIFO(ep) ((uint32_t *)(((uint32_t)(USBG)) + USB_OTG_FIFO_BASE + (USB_OTG_FIFO_SIZE) * (ep)))
 | 
			
		||||
#define USBPCGCCTL ((uint32_t *)(((uint32_t)(USBG)) + USB_OTG_PCGCCTL_BASE))
 | 
			
		||||
 | 
			
		||||
// Set TOCAL and TRDT values (see Ref. Man.)
 | 
			
		||||
#if defined(USB_STM32H7) /*|| defined(USB_HIGH_SPEED)*/
 | 
			
		||||
#define TOCAL_VALUE (0x00)
 | 
			
		||||
#define TRDT_VALUE (0x05)
 | 
			
		||||
#elif defined(USB_STM32F4)
 | 
			
		||||
#if !defined(USB_HIGH_SPEED)
 | 
			
		||||
#define TOCAL_VALUE (0x07)
 | 
			
		||||
#define TRDT_VALUE (0x06)
 | 
			
		||||
#else
 | 
			
		||||
#define TOCAL_VALUE (0x07)
 | 
			
		||||
#define TRDT_VALUE (0x09)
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* CORE_USB_USB_COMMON_DEFS */
 | 
			
		||||
@ -1,16 +1,16 @@
 | 
			
		||||
#ifndef CORE_USB_USB_CORE_TYPES
 | 
			
		||||
#define CORE_USB_USB_CORE_TYPES
 | 
			
		||||
 | 
			
		||||
#include "usb_common_types.h"
 | 
			
		||||
// #include "usb_common_types.h"
 | 
			
		||||
 | 
			
		||||
/* USB linespeed */
 | 
			
		||||
typedef enum { USB_SPD_UNKNOWN = 0b00, USB_SPD_LOW = 0b01, USB_SPD_FULL = 0b11 } USB_Speed;
 | 
			
		||||
// /* USB linespeed */
 | 
			
		||||
// typedef enum { USB_SPD_UNKNOWN = 0b00, USB_SPD_LOW = 0b01, USB_SPD_FULL = 0b11 } USB_Speed;
 | 
			
		||||
 | 
			
		||||
/* USB core state */
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
    uint8_t linespeed; // USB linespeed
 | 
			
		||||
} USB_CoreState;
 | 
			
		||||
// /* USB core state */
 | 
			
		||||
// typedef struct
 | 
			
		||||
// {
 | 
			
		||||
//     uint8_t linespeed; // USB linespeed
 | 
			
		||||
// } USB_CoreState;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* CORE_USB_USB_CORE_TYPES */
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,40 @@
 | 
			
		||||
#include "usb_drv.h"
 | 
			
		||||
 | 
			
		||||
#include "flatUSB_config.h"
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "stm32f4xx_hal.h"
 | 
			
		||||
 | 
			
		||||
#include "flatUSB_config.h"
 | 
			
		||||
#include "usb_driver_common.h"
 | 
			
		||||
 | 
			
		||||
#include FLATUSB_DESCRIPTOR_HEADER
 | 
			
		||||
 | 
			
		||||
#include "common_defs.h"
 | 
			
		||||
#include "usb_common.h"
 | 
			
		||||
#include "usb_common_defs.h"
 | 
			
		||||
 | 
			
		||||
// ------------------------
 | 
			
		||||
 | 
			
		||||
#define USB_RX_BUF_SIZE (512)
 | 
			
		||||
 | 
			
		||||
static USBDRV_GlobalState gs;                       // global USB state
 | 
			
		||||
static uint8_t rx_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; // receive buffer
 | 
			
		||||
static USBDRV_IN_cb cbs[USB_NUM_OF_ENDPOINTS];      // callbacks for IN completion
 | 
			
		||||
 | 
			
		||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
 | 
			
		||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
 | 
			
		||||
 | 
			
		||||
// ------------------------
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
// ------------------------
 | 
			
		||||
 | 
			
		||||
@ -17,12 +46,48 @@ __weak void usbdrv_ulpi_init() {
 | 
			
		||||
 | 
			
		||||
// ------------------------
 | 
			
		||||
 | 
			
		||||
void usbdrv_register_IN_complete_cb(uint8_t ep, USBDRV_IN_cb cb) {
 | 
			
		||||
    cbs[ep] = cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------------------------
 | 
			
		||||
 | 
			
		||||
static USB_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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
USB_DrvIntf *usbdrv_get_intf() {
 | 
			
		||||
    return &drvIntf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------------------------
 | 
			
		||||
 | 
			
		||||
#if USB_INTERNAL && USB_UPLI
 | 
			
		||||
#error "Select just one from USB_INTERNAL and USB_ULPI"
 | 
			
		||||
#error "Select just one from USB_INTERNAL and USB_ULPI!"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// issue a hardware reset
 | 
			
		||||
static void usbdrv_hw_reset() {
 | 
			
		||||
    WAIT_FOR_nBIT_DELAY(USBG->GRSTCTL, USB_OTG_GRSTCTL_AHBIDL, 1); // wait for AHB transactions to cease
 | 
			
		||||
    SET_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST);                 // reset the USB core
 | 
			
		||||
    WAIT_FOR_BIT_DELAY(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST, 1);
 | 
			
		||||
    // SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_USBRST);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize USB peripheral
 | 
			
		||||
void usbdrv_periph_init() {
 | 
			
		||||
void usbdrv_periph_init(bool reset) {
 | 
			
		||||
    if (!reset) {
 | 
			
		||||
        // enable USB clock
 | 
			
		||||
        USB_CLOCK_ENABLE();
 | 
			
		||||
 | 
			
		||||
@ -40,24 +105,24 @@ void usbdrv_periph_init() {
 | 
			
		||||
        CLEAR_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_ULPIEVBUSI);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    WAIT_FOR_nBIT_DELAY(USBG->GRSTCTL, USB_OTG_GRSTCTL_AHBIDL, 1); // wait for AHB transactions to cease
 | 
			
		||||
    SET_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST); // reset the USB core
 | 
			
		||||
    WAIT_FOR_BIT_DELAY(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST, 1);
 | 
			
		||||
        usbdrv_hw_reset(); // commence a hardware reset
 | 
			
		||||
 | 
			
		||||
#if USB_ULPI
 | 
			
		||||
        usbdrv_ulpi_init(); // initialize PHY
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN); // power the internal transceiver 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, TRDT_VALUE);              // set TRDT according to the RM
 | 
			
		||||
    WRITE_FIELD(USBG->GUSBCFG, USB_OTG_GUSBCFG_TOCAL, TOCAL_VALUE);            // set TOCAL
 | 
			
		||||
    CLEAR_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_FHMOD);                           // clear Host mode forcing
 | 
			
		||||
    SET_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_FDMOD);                             // force Device mode
 | 
			
		||||
 | 
			
		||||
    HAL_Delay(25); // wait for Device mode forcing propagation
 | 
			
		||||
    USB_DELAY(25); // wait for Device mode forcing propagation
 | 
			
		||||
    WAIT_FOR_BIT(USBG->GINTSTS, 0b1);
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
@ -74,15 +139,17 @@ usbdrv_ulpi_init(); // initialize PHY
 | 
			
		||||
    CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_VBUSASEN);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    HAL_Delay(25); // it takes time to forcing Device mode takes effect
 | 
			
		||||
    USB_DELAY(25); // it takes time to forcing Device mode takes effect
 | 
			
		||||
 | 
			
		||||
    if (!reset) {
 | 
			
		||||
#ifdef USB_HIGH_SPEED
 | 
			
		||||
        // WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_FULL_SPEED);
 | 
			
		||||
    // WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_HIGH_SPEED_ULPI);
 | 
			
		||||
    WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_FULL_SPEED_ULPI);
 | 
			
		||||
        WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_HIGH_SPEED_ULPI);
 | 
			
		||||
        // WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_FULL_SPEED_ULPI);
 | 
			
		||||
#else
 | 
			
		||||
        WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_FULL_SPEED); // there's no other possible option
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // allow specific interrupts
 | 
			
		||||
    uint32_t intmask =             /*USB_OTG_GINTMSK_WUIM |     // Wake up */
 | 
			
		||||
@ -94,16 +161,11 @@ usbdrv_ulpi_init(); // initialize PHY
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    if (!reset) {
 | 
			
		||||
        // 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
 | 
			
		||||
@ -117,16 +179,806 @@ usbdrv_ulpi_init(); // initialize PHY
 | 
			
		||||
    SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.rx_buf = rx_buf;
 | 
			
		||||
    gs.rx_buf_level = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize USB subsystem
 | 
			
		||||
void usbdrv_init() {
 | 
			
		||||
    HAL_NVIC_DisableIRQ(USB_IRQn);
 | 
			
		||||
    USB_IRQ_DISABLE(USB_IRQ_N); // disable USB interrupts
 | 
			
		||||
 | 
			
		||||
    //usbdrv_init_global_state();
 | 
			
		||||
    usbdrv_init_global_state();
 | 
			
		||||
    usbdrv_init_intf();
 | 
			
		||||
    usbdrv_gpio_init();
 | 
			
		||||
    usbdrv_periph_init();
 | 
			
		||||
    usbdrv_periph_init(false);
 | 
			
		||||
    usbdrv_initial_ep0_setup();
 | 
			
		||||
    usbdrv_power_and_connect(true);
 | 
			
		||||
 | 
			
		||||
    HAL_NVIC_SetPriority(USB_IRQn, 8, 0);
 | 
			
		||||
    HAL_NVIC_EnableIRQ(USB_IRQn);
 | 
			
		||||
    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
 | 
			
		||||
        CLEAR_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS);
 | 
			
		||||
#ifdef USB_INTERNAL
 | 
			
		||||
        SET_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN); // actually, this is power UP and affects only the built-in FS transciever
 | 
			
		||||
#endif
 | 
			
		||||
    } else { // OFF
 | 
			
		||||
        SET_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS);
 | 
			
		||||
#ifdef USB_INTERNAL
 | 
			
		||||
        CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// -------------------
 | 
			
		||||
 | 
			
		||||
// 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---------------------
 | 
			
		||||
 | 
			
		||||
// addresses of specific DIEPTXF registers, addresses from the RM
 | 
			
		||||
static uint32_t *USB_pDIEPTXF[USB_NUM_OF_ENDPOINTS] = {
 | 
			
		||||
    // FS
 | 
			
		||||
    (uint32_t *)(((uint32_t)USBG) + 0x028), // DIEPTXF0
 | 
			
		||||
    (uint32_t *)(((uint32_t)USBG) + 0x104), // DIEPTXF1
 | 
			
		||||
    (uint32_t *)(((uint32_t)USBG) + 0x108), // DIEPTXF2
 | 
			
		||||
    (uint32_t *)(((uint32_t)USBG) + 0x10C), // DIEPTXF3
 | 
			
		||||
    // HS
 | 
			
		||||
    (uint32_t *)(((uint32_t)USBG) + 0x120), // DIEPTXF4
 | 
			
		||||
    (uint32_t *)(((uint32_t)USBG) + 0x124), // DIEPTXF5
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 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 ----
 | 
			
		||||
 | 
			
		||||
        // 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, 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
 | 
			
		||||
 | 
			
		||||
        // 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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---------------------
 | 
			
		||||
 | 
			
		||||
// 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);
 | 
			
		||||
 | 
			
		||||
        // enable endpoint interrupts
 | 
			
		||||
        SET_BIT(USBD->DAINTMSK, 1 << ep);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // turn on interrupt generation 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);
 | 
			
		||||
 | 
			
		||||
    // WAIT_FOR_nBIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TXFE);
 | 
			
		||||
 | 
			
		||||
    // 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, uint16_t size) {
 | 
			
		||||
    // arm endpoint only if it was not armed before
 | 
			
		||||
    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_enable_endpoint_interrupt(uint8_t ep, uint8_t dir, bool en) {
 | 
			
		||||
    uint32_t mask = 1 << (ep + ((dir == USB_OUT) ? USB_OTG_DAINTMSK_OEPM_Pos : 0));
 | 
			
		||||
    if (en) {
 | 
			
		||||
        SET_BIT(USBD->DAINTMSK, mask);
 | 
			
		||||
    } else {
 | 
			
		||||
        CLEAR_BIT(USBD->DAINTMSK, mask);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---------------------
 | 
			
		||||
 | 
			
		||||
// set Rx FIFO size
 | 
			
		||||
void usbdrv_set_rx_fifo_size(uint16_t size) {
 | 
			
		||||
    USBG->GRXFSIZ = CEILDIV4(size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
 | 
			
		||||
#if defined(USB_STM32F4)
 | 
			
		||||
#if USB_INTERNAL
 | 
			
		||||
#define USB_MIN_GROSS_RX_FIFO_SIZE (2 * USB_MIN_EP_FIFO_SIZE + USB_RX_FIFO_SETUP_RESERVATION_DWORDS * 4)
 | 
			
		||||
#else
 | 
			
		||||
#define USB_MIN_GROSS_RX_FIFO_SIZE (1024)
 | 
			
		||||
#endif
 | 
			
		||||
#elif defined(USB_STM32H7)
 | 
			
		||||
#define USB_MIN_GROSS_RX_FIFO_SIZE (256)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define USBDRV_ADDR_TABLE_STR_LEN (255)
 | 
			
		||||
static char usbdrv_addr_table[USBDRV_ADDR_TABLE_STR_LEN + 1];
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 | 
			
		||||
    uint32_t str_offset = SNPRINTF(usbdrv_addr_table, USBDRV_ADDR_TABLE_STR_LEN, "RX:  000-%03x (%u)\r\n", fifo_size - 1, fifo_size);
 | 
			
		||||
 | 
			
		||||
    // ---- 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, cfg->max_packet_size + 64)); // correct FIFO size if necessary
 | 
			
		||||
            cfg->fifo_address = next_fifo_addr;                                                 // store FIFO address
 | 
			
		||||
            cfg->zlp_next = false;
 | 
			
		||||
 | 
			
		||||
            str_offset += SNPRINTF(usbdrv_addr_table + str_offset, USBDRV_ADDR_TABLE_STR_LEN - str_offset, "TX%u: %03x-%03x (%u)\r\n", i, next_fifo_addr, next_fifo_addr + cfg->fifo_size - 1, cfg->fifo_size);
 | 
			
		||||
 | 
			
		||||
            // cfg->txp = false;                                                                                    // no transfer is in progress
 | 
			
		||||
            next_fifo_addr += cfg->fifo_size; // advance next address
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *usbdrv_get_fifo_addr_table() {
 | 
			
		||||
    return usbdrv_addr_table;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---------------------
 | 
			
		||||
 | 
			
		||||
// 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---------------------
 | 
			
		||||
 | 
			
		||||
void usbdrv_set_address(uint8_t addr) {
 | 
			
		||||
    gs.address = addr;
 | 
			
		||||
    WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DAD, gs.address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---------------------
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
        usbdrv_reset();
 | 
			
		||||
 | 
			
		||||
        // invoke Reset Done notification
 | 
			
		||||
        if (drvIntf.rst_notify != NULL) {
 | 
			
		||||
            drvIntf.rst_notify();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // invoke Enumeration Done notification
 | 
			
		||||
        if (drvIntf.enum_notify != NULL) {
 | 
			
		||||
            uint8_t spd;
 | 
			
		||||
            uint32_t dspd = READ_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD);
 | 
			
		||||
            switch (dspd) {
 | 
			
		||||
            case USB_LINESPEED_FULL_SPEED_ULPI:
 | 
			
		||||
            case USB_LINESPEED_FULL_SPEED:
 | 
			
		||||
                spd = USB_SPD_FULL;
 | 
			
		||||
                break;
 | 
			
		||||
            case USB_LINESPEED_HIGH_SPEED_ULPI:
 | 
			
		||||
                spd = USB_SPD_HIGH;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                spd = USB_SPD_UKWN;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            drvIntf.enum_notify(spd);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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) {
 | 
			
		||||
                    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;
 | 
			
		||||
                bool ep_on = USBD->DAINTMSK & (1 << ep);                      // decide if this endpoint is currently enable for transmission based on its endpoint mask
 | 
			
		||||
                if (gs.ep_IN[ep].is_configured && ep_on) {                    // 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);
 | 
			
		||||
 | 
			
		||||
                            // 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
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            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);
 | 
			
		||||
 | 
			
		||||
                        // disable endpoint interrupt
 | 
			
		||||
                        usbdrv_enable_endpoint_interrupt(ep, USB_IN, false);
 | 
			
		||||
 | 
			
		||||
                        cbcpd.code = USB_CBC_IN_FIFOEMPTY;
 | 
			
		||||
                        usbcore_process_nonsetup_event(&cbcpd);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---------------------
 | 
			
		||||
 | 
			
		||||
#define PROCESS_EVENT(evt, data) usbdrv_process_event((evt), (data))
 | 
			
		||||
 | 
			
		||||
void USB_IRQ_HANDLER() {
 | 
			
		||||
 | 
			
		||||
    uint32_t ints = USBG->GINTSTS;
 | 
			
		||||
 | 
			
		||||
    // USB reset
 | 
			
		||||
    if (ints & USB_OTG_GINTSTS_USBRST) {
 | 
			
		||||
        SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_USBRST); // clear interrupt
 | 
			
		||||
        PROCESS_EVENT(USB_EVT_USB_RESET, NULL);         // process event
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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;
 | 
			
		||||
}
 | 
			
		||||
@ -1,35 +1,309 @@
 | 
			
		||||
#ifndef STM32_USB_DRV
 | 
			
		||||
#define STM32_USB_DRV
 | 
			
		||||
 | 
			
		||||
#include "usb_common_defs.h"
 | 
			
		||||
#include "common_defs.h"
 | 
			
		||||
 | 
			
		||||
// Select IO crossbar advanced function
 | 
			
		||||
#if defined(USB_STM32H7)
 | 
			
		||||
#ifndef STM32H723xx
 | 
			
		||||
#define USB_GPIO_AF (GPIO_AF10_OTG2_FS)
 | 
			
		||||
#else
 | 
			
		||||
// #define USB_GPIO_AF (GPIO_AF10_OTG1_HS)
 | 
			
		||||
#endif
 | 
			
		||||
#elif defined(USB_STM32F4)
 | 
			
		||||
#ifdef USB_HIGH_SPEED
 | 
			
		||||
#define USB_GPIO_AF (GPIO_AF10_OTG_HS)
 | 
			
		||||
#else
 | 
			
		||||
#define USB_GPIO_AF (GPIO_AF10_OTG_FS)
 | 
			
		||||
#endif
 | 
			
		||||
#include "../../usb_common_types.h"
 | 
			
		||||
 | 
			
		||||
#include "../../usb_driver_common.h"
 | 
			
		||||
 | 
			
		||||
// number of supported endpoints
 | 
			
		||||
#define USB_NUM_OF_ENDPOINTS (6) // set it to the maximum that this type of module can support
 | 
			
		||||
 | 
			
		||||
// non isochronous transfers
 | 
			
		||||
#define USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS (64)
 | 
			
		||||
#define USB_MAX_HS_PCKT_SIZE_NON_ISOCHRONOUS (512)
 | 
			
		||||
 | 
			
		||||
// isochronous transfers
 | 
			
		||||
#define USB_MAX_FS_PCKT_SIZE_ISOCHRONOUS (1023)
 | 
			
		||||
 | 
			
		||||
#define USB_MIN_EP_FIFO_SIZE (64)
 | 
			
		||||
 | 
			
		||||
// Unfortunately this cannot be enabled, since the USB controller
 | 
			
		||||
// generates such enormous amounts of interrupts, so that no other
 | 
			
		||||
// task can run along the USB processing.
 | 
			
		||||
#define USB_EVENT_PROCESSING_IN_OS_THREAD (0)
 | 
			
		||||
 | 
			
		||||
#if USB_EVENT_PROCESSING_IN_OS_THREAD
 | 
			
		||||
#include <cmsis_os2.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Set TOCAL and TRDT values (see Ref. Man.)
 | 
			
		||||
#if defined(USB_STM32H7) /*|| defined(USB_HIGH_SPEED)*/
 | 
			
		||||
#define TOCAL_VALUE (0x00)
 | 
			
		||||
#define TRDT_VALUE (0x05)
 | 
			
		||||
#elif defined(USB_STM32F4)
 | 
			
		||||
#if !defined(USB_HIGH_SPEED)
 | 
			
		||||
#define TOCAL_VALUE (0x07)
 | 
			
		||||
#define TRDT_VALUE (0x06)
 | 
			
		||||
#else
 | 
			
		||||
#define TOCAL_VALUE (0x07)
 | 
			
		||||
#define TRDT_VALUE (0x09)
 | 
			
		||||
#endif
 | 
			
		||||
// 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 fifo_address; // address in the FIFO
 | 
			
		||||
    uint16_t fifo_size;    // FIFO size
 | 
			
		||||
    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)
 | 
			
		||||
} USBDRV_EpConfig;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    USB_FSM_INITIAL_WAIT_SPEEDNEG = 0, // initial state, right after reset, wait for speed negotiation, must be ZERO!
 | 
			
		||||
    USB_FSM_SETUP_OPERATE,             // ready to perform setup operations
 | 
			
		||||
} USBDRV_FsmState;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    USB_EVT_USB_RESET,      // bus reset received
 | 
			
		||||
    USB_EVT_SPEEDNEG_DONE,  // linespeed negotiation (ST calls "enumeration") done
 | 
			
		||||
    USB_EVT_RECEPTION_DONE, // received packet is waiting to be fetched from the Rx FIFO
 | 
			
		||||
    USB_EVT_OUT_DONE,       // something has happened on an OUT endpoint
 | 
			
		||||
    USB_EVT_IN_DONE,        // previously armed IN endpoint has finished packet transmission
 | 
			
		||||
} USBDRV_EventCode;
 | 
			
		||||
 | 
			
		||||
// endpoint event
 | 
			
		||||
typedef enum {
 | 
			
		||||
    USB_EPEVT_IDLE = 0x00, // endpoint is IDLE
 | 
			
		||||
    USB_EPEVT_ARMED,       // endpoint is armed for transmission
 | 
			
		||||
    USB_EPEVT_STALLED,     // endpoint is stalled
 | 
			
		||||
    USB_EPEVT_NAK          // endpoint responds NAK regardless of FIFO level
 | 
			
		||||
} USBDRV_EndpointEvent;
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
    uint16_t rx_fifo_size;                        // Rx FIFO size in bytes
 | 
			
		||||
    uint16_t state;                               // FSM state
 | 
			
		||||
    uint8_t *rx_buf;                              // pointer to the receive buffer (this way declaration can be separated)
 | 
			
		||||
    uint16_t rx_buf_level;                        // fill level of the rx buffer
 | 
			
		||||
    uint8_t address;                              // device address
 | 
			
		||||
#if USB_EVENT_PROCESSING_IN_OS_THREAD
 | 
			
		||||
    osMessageQueueId_t event_queue; // event queue
 | 
			
		||||
    osThreadId_t th;                // event processing thread
 | 
			
		||||
#endif
 | 
			
		||||
} USBDRV_GlobalState;
 | 
			
		||||
 | 
			
		||||
// USB received packet status
 | 
			
		||||
typedef enum { USB_PCKT_STATUS_GLOBAL_OUT_NAK = 0b0001,
 | 
			
		||||
               USB_PCKT_STATUS_OUT_DATA_RECV = 0b0010,
 | 
			
		||||
               USB_PCKT_STATUS_OUT_TRANSFER_CPLT = 0b0011,
 | 
			
		||||
               USB_PCKT_STATUS_SETUP_CPLT = 0b0100,
 | 
			
		||||
               USB_PCKT_STATUS_SETUP_DATA_RECV = 0b0110,
 | 
			
		||||
} USBDRV_PcktStatus;
 | 
			
		||||
 | 
			
		||||
#define USB_FLUSH_TX_FIFO_ALL (0b10000)
 | 
			
		||||
 | 
			
		||||
// 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();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Initialize driver interface.
 | 
			
		||||
 */
 | 
			
		||||
void usbdrv_init_intf();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * USB peripheral initialization.
 | 
			
		||||
 * 
 | 
			
		||||
 * @param reset only perform an after-reset reinitialization
 | 
			
		||||
 */
 | 
			
		||||
void usbdrv_periph_init(bool reset);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Flush specific TX FIFO.
 | 
			
		||||
 * 
 | 
			
		||||
 * @param n index of the TX FIFO
 | 
			
		||||
 */
 | 
			
		||||
void usbdrv_flush_tx_fifo(uint8_t n);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Flush specific RX FIFO.
 | 
			
		||||
 */
 | 
			
		||||
void usbdrv_flush_rx_fifo();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implement or rescind responding NAK globally in the chosen direction.
 | 
			
		||||
 * 
 | 
			
		||||
 * @param dir communication direction
 | 
			
		||||
 * @param en enable/disable NAK responses
 | 
			
		||||
 */
 | 
			
		||||
void usbdrv_set_global_NAK(uint8_t dir, bool en);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Fetch data corresponding to a single Endpoint.
 | 
			
		||||
 * 
 | 
			
		||||
 * @param ep index of endpoint
 | 
			
		||||
 * @param len maximum length of data to be fetched
 | 
			
		||||
 */
 | 
			
		||||
void usbdrv_fetch_received_data(uint8_t ep, uint16_t len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the shared FIFO size for all receptions.
 | 
			
		||||
 * (Shared among all OUT endpoints.)
 | 
			
		||||
 * Size of multiple of 4 are recommended. Values
 | 
			
		||||
 * not obeying this will be rounded up.
 | 
			
		||||
 * 
 | 
			
		||||
 * @param size FIFO size in BYTES
 | 
			
		||||
 */
 | 
			
		||||
void usbdrv_set_rx_fifo_size(uint16_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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_build_fifo();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
 */
 | 
			
		||||
USB_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);
 | 
			
		||||
 | 
			
		||||
#endif /* STM32_USB_DRV */
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								usb.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								usb.c
									
									
									
									
									
								
							@ -2,35 +2,24 @@
 | 
			
		||||
 | 
			
		||||
#include <memory.h>
 | 
			
		||||
 | 
			
		||||
#include "usb_callback_event.h"
 | 
			
		||||
#include "usb_common.h"
 | 
			
		||||
#include "usb_driver_common.h"
 | 
			
		||||
 | 
			
		||||
// #include "utils/gen_queue.h"
 | 
			
		||||
 | 
			
		||||
#include FLATUSB_DESCRIPTOR_HEADER
 | 
			
		||||
 | 
			
		||||
#include "usb_driver.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
 | 
			
		||||
 | 
			
		||||
// ---------------
 | 
			
		||||
 | 
			
		||||
#ifndef USB_HIGH_SPEED
 | 
			
		||||
#define USB_RX_BUF_SIZE (USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS)
 | 
			
		||||
#else
 | 
			
		||||
#define USB_RX_BUF_SIZE (USB_MAX_HS_PCKT_SIZE_NON_ISOCHRONOUS)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define USBDRV_ARM_IN_ZLP(ep) drv->arm_IN((ep), NULL, 0)
 | 
			
		||||
 | 
			
		||||
#define USB_RX_BUF_SIZE (512)                                // FIXME
 | 
			
		||||
static uint8_t tx_assembly_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; // buffer for assembling packets
 | 
			
		||||
 | 
			
		||||
// ---------------
 | 
			
		||||
@ -67,12 +56,29 @@ typedef struct {
 | 
			
		||||
} USB_SetupTransferState;
 | 
			
		||||
 | 
			
		||||
static USB_SetupTransferState stups;
 | 
			
		||||
static USB_DrvIntf *drv;
 | 
			
		||||
 | 
			
		||||
void usbcore_init() {
 | 
			
		||||
void usbcore_reset() {
 | 
			
		||||
    // init state
 | 
			
		||||
    memset(&stups, 0, sizeof(USB_SetupTransferState));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void usbcore_rst_notify() {
 | 
			
		||||
    usbcore_reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void usbcore_enum_notify(uint8_t spd) {
 | 
			
		||||
    (void)spd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usbcore_init(USB_DrvIntf *drvIntf) {
 | 
			
		||||
    usbcore_reset(); // reset USB Core
 | 
			
		||||
 | 
			
		||||
    drv = drvIntf;                          // store USB driver interface assignments
 | 
			
		||||
    drv->rst_notify = usbcore_rst_notify;   // assign Reset Done callback
 | 
			
		||||
    drv->enum_notify = usbcore_enum_notify; // assign Enumeration Done callback
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stage) {
 | 
			
		||||
 | 
			
		||||
    // MSG("STUP: %u, %s\n", size, stage == UST_SETUP ? "SETUP" : "DATA");
 | 
			
		||||
@ -112,7 +118,7 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // expect status transaction to follow the data phase
 | 
			
		||||
    usbdrv_arm_OUT_endpoint(0, 64);
 | 
			
		||||
    drv->arm_OUT(0, 64);
 | 
			
		||||
 | 
			
		||||
    // only continue processing if the transaction has concluded
 | 
			
		||||
    if (!stups.out_complete) {
 | 
			
		||||
@ -155,26 +161,26 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
 | 
			
		||||
            break;
 | 
			
		||||
        // Descriptors not implemented
 | 
			
		||||
        default:
 | 
			
		||||
            usbdrv_stall_endpoint(0, USB_IN, true); // stall IN, since request in unsupported
 | 
			
		||||
            drv->stall(0, USB_IN, true); // stall IN, since request in unsupported
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // arm data transmission
 | 
			
		||||
        usbdrv_arm_IN_endpoint(0, data, sz);
 | 
			
		||||
        drv->arm_IN(0, data, sz);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case UREQ_SetAddress: { // SET ADDRESS
 | 
			
		||||
        uint8_t addr = stups.setup_req.wValue & 0x7F;
 | 
			
		||||
        usbdrv_set_address(addr);
 | 
			
		||||
        usbdrv_arm_OUT_endpoint(0, 64); // prepare for data OUT stage
 | 
			
		||||
        drv->set_address(addr);
 | 
			
		||||
        drv->arm_OUT(0, 64);  // prepare for data OUT stage
 | 
			
		||||
        USBDRV_ARM_IN_ZLP(0); // ZLP IN
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    case UREQ_SetConfiguration: // SET CONFIGURATION
 | 
			
		||||
    {
 | 
			
		||||
        uint8_t config_id = stups.setup_req.wValue & 0xFF;
 | 
			
		||||
        usbdrv_fetch_endpoint_configuration(config_id - 1);
 | 
			
		||||
        drv->set_config(config_id - 1);
 | 
			
		||||
        USBDRV_ARM_IN_ZLP(0);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
@ -196,7 +202,7 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
 | 
			
		||||
        usb_event_callback(&cbevt);
 | 
			
		||||
 | 
			
		||||
        if (cbevt.reply_valid) {
 | 
			
		||||
            usbdrv_arm_IN_endpoint(0, cbevt.reply_data, cbevt.reply_size);
 | 
			
		||||
            drv->arm_IN(0, cbevt.reply_data, cbevt.reply_size);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
@ -241,21 +247,25 @@ void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) {
 | 
			
		||||
        // for OUT events, check the autoarm flag
 | 
			
		||||
        if (cbevt.dir == USB_OUT) {
 | 
			
		||||
            if (cbevt.arm_out_endpoint) {
 | 
			
		||||
                usbdrv_autoarm_OUT_endpoint(cbcpd->ep);
 | 
			
		||||
                drv->autoarm(cbcpd->ep);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usbcore_wake_up_endpoint(uint8_t ep, uint8_t dir) {
 | 
			
		||||
    usbdrv_enable_endpoint_interrupt(ep, dir, true);
 | 
			
		||||
    drv->en_ep_irq(ep, dir, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usbcore_register_IN_callback(uint8_t ep, USBDRV_IN_cb cb) {
 | 
			
		||||
    drv->reg_IN_cb(ep, cb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size) {
 | 
			
		||||
    usbdrv_enable_endpoint_interrupt(ep, USB_IN, true);
 | 
			
		||||
    return usbdrv_arm_IN_endpoint(ep, data, size);
 | 
			
		||||
    drv->en_ep_irq(ep, USB_IN, true);
 | 
			
		||||
    return drv->arm_IN(ep, data, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size) {
 | 
			
		||||
    return usbdrv_arm_OUT_endpoint(ep, size);
 | 
			
		||||
    return drv->arm_OUT(ep, size);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								usb.h
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								usb.h
									
									
									
									
									
								
							@ -1,11 +1,14 @@
 | 
			
		||||
#ifndef CORE_USB_USB
 | 
			
		||||
#define CORE_USB_USB
 | 
			
		||||
 | 
			
		||||
#include "usb_callback_event.h"
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
void usbcore_init();                                                                    // initialize USB core
 | 
			
		||||
#include "usb_driver_common.h"
 | 
			
		||||
 | 
			
		||||
void usbcore_init(USB_DrvIntf *drvIntf);                                                // initialize USB core
 | 
			
		||||
uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size); // write data to endpoint, return with number of bytes actually written
 | 
			
		||||
uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size);                         // expect data coming from the endpoint
 | 
			
		||||
void usbcore_wake_up_endpoint(uint8_t ep, uint8_t dir);                                 // wake up endpoint
 | 
			
		||||
void usbcore_register_IN_callback(uint8_t ep, USBDRV_IN_cb cb);                         // register IN complete callback
 | 
			
		||||
 | 
			
		||||
#endif /* CORE_USB_USB */
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,14 @@
 | 
			
		||||
#include "usb_common_types.h"
 | 
			
		||||
#include "usb_device_types.h"
 | 
			
		||||
 | 
			
		||||
#define USB_DELAY(t) for (uint64_t i = 0; i < (t) * 10000; i++) { asm("nop"); }
 | 
			
		||||
 | 
			
		||||
#define READ_FIELD(r,f) (((r) & (f##_Msk)) >> (f##_Pos))
 | 
			
		||||
#define WRITE_FIELD(r,f,v) ((r) = ((r) & ~(f##_Msk)) | (v << (f##_Pos)))
 | 
			
		||||
#define WAIT_FOR_BIT(r,b) while ((r) & (b)) {}
 | 
			
		||||
#define WAIT_FOR_BIT_DELAY(r,b,d) while ((r) & (b)) { HAL_Delay((d)); }
 | 
			
		||||
#define WAIT_FOR_BIT_DELAY(r,b,d) while ((r) & (b)) { USB_DELAY((d)); }
 | 
			
		||||
#define WAIT_FOR_nBIT(r,b) while (!((r) & (b))) {}
 | 
			
		||||
#define WAIT_FOR_nBIT_DELAY(r,b,d) while (!((r) & (b))) { HAL_Delay((d)); }
 | 
			
		||||
#define WAIT_FOR_nBIT_DELAY(r,b,d) while (!((r) & (b))) { USB_DELAY((d)); }
 | 
			
		||||
 | 
			
		||||
#define DWORD_ALIGN __attribute__((aligned(4)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								usb_driver_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								usb_driver_common.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
#ifndef FLATUSB_USB_DRIVER_INTERFACE
 | 
			
		||||
#define FLATUSB_USB_DRIVER_INTERFACE
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    USB_SPD_UKWN = 0,
 | 
			
		||||
    USB_SPD_LOW = 1,
 | 
			
		||||
    USB_SPD_FULL = 2,
 | 
			
		||||
    USB_SPD_HIGH = 3
 | 
			
		||||
} USBDRV_LineSpeed;
 | 
			
		||||
 | 
			
		||||
typedef void (*USBDRV_IN_cb)(uint8_t ep);
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    // USB CORE -> USB DRIVER
 | 
			
		||||
    void (*init)();                                                    // initialize USB driver
 | 
			
		||||
    void (*reset)();                                                   // reset USB driver
 | 
			
		||||
    void (*stall)(uint8_t ep, uint8_t dir, bool stall);                // stall the endpoint
 | 
			
		||||
    uint32_t (*arm_IN)(uint8_t ep, const uint8_t *data, uint16_t len); // arm IN endpoint
 | 
			
		||||
    uint32_t (*arm_OUT)(uint8_t ep, uint16_t len);                     // arm OUT endpoint
 | 
			
		||||
    void (*set_address)(uint8_t addr);                                 // set device address
 | 
			
		||||
    void (*set_config)(uint8_t ci);                                    // make the chosen setting operational
 | 
			
		||||
    void (*autoarm)(uint8_t ep);                                       // enable OUT autoarm
 | 
			
		||||
    void (*en_ep_irq)(uint8_t ep, uint8_t dir, bool en);               // enable endpoint interrupt
 | 
			
		||||
    void (*reg_IN_cb)(uint8_t ep, USBDRV_IN_cb cb);                    // register a callback for IN transaction complete
 | 
			
		||||
 | 
			
		||||
    // USB DRIVER -> USB CORE
 | 
			
		||||
    void (*rst_notify)();             // notify USB core of a bus issued reset
 | 
			
		||||
    void (*enum_notify)(uint8_t spd); // notify the USB core that speed negotiation has concluded
 | 
			
		||||
} USB_DrvIntf;
 | 
			
		||||
 | 
			
		||||
#ifndef USBDBGMSG
 | 
			
		||||
#define USBMSG(...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// callback codes
 | 
			
		||||
typedef enum {
 | 
			
		||||
    USB_CBC_OUT,          // OUT done!
 | 
			
		||||
    USB_CBC_IN_DONE,      // IN done!
 | 
			
		||||
    USB_CBC_IN_FIFOEMPTY, // could not serve IN request, since Tx FIFO was empty
 | 
			
		||||
} USBDRV_CallbackCode;
 | 
			
		||||
 | 
			
		||||
// callback compound
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint8_t ep : 4;      // endpoint number
 | 
			
		||||
    uint8_t dir : 1;     // direction
 | 
			
		||||
    uint8_t code : 3;    // event code
 | 
			
		||||
    uint16_t size;       // data size
 | 
			
		||||
    const uint8_t *data; // data
 | 
			
		||||
} USBDRV_CallbackCompound;
 | 
			
		||||
 | 
			
		||||
typedef enum { UST_SETUP,
 | 
			
		||||
               UST_DATA } USBDRV_ControlTfStage;
 | 
			
		||||
 | 
			
		||||
#endif /* FLATUSB_USB_DRIVER_INTERFACE */
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user