Compare commits

...

26 Commits

Author SHA1 Message Date
c0ea9c3ab3 - CH32F207 port fixed
- ACM stack size fixed
2025-03-07 16:34:25 +01:00
88bb420b26 - ACM fixes, CH32 driver fixes, example CH32 config added 2025-02-25 12:17:11 +01:00
1737696a8b Merge branch 'ch32_drv' 2025-02-25 11:13:04 +01:00
f5ccd09612 Merge branch 'core_fix_2' 2025-02-25 11:11:41 +01:00
0e0fcbb121 - initial working driver 2025-02-25 11:06:35 +01:00
7960b2957e - some fixes 2025-02-25 11:05:41 +01:00
9753e7ded8 - wakeup() removed 2025-02-25 10:55:00 +01:00
fb894c4703 - usbdrv_init_hook() added 2025-02-25 10:53:53 +01:00
983c8a8840 - ACM fixed, thread added, wakeup() removed 2025-02-25 10:51:57 +01:00
a977b32471 Merge branch 'master' of epagris.com:epagris/flatUSB 2024-12-05 10:00:52 +01:00
0bb047ddf6 - reset processing temporarily bypassed 2024-12-05 09:59:46 +01:00
27942ade8e - stm32 driver fixes
- example JSON vendor and product ID's placeholdered
2024-11-19 07:39:33 +01:00
2a69c57225 - basic docs added
- doxygen styling added
- CDC -> ACM refactor
- type refactoring
- examples categorized
- flatUSB_config.h examples added
2024-11-16 21:53:01 +01:00
685fa77bda - function separation done
- a load of CMake modification
2024-11-16 18:15:13 +01:00
48a7ae54a3 - minor modifications 2024-11-16 09:37:11 +01:00
dcf2c2b808 - CMake target on generating descriptors from JSON
added
- basic flatUSB_config.h concept added
- example JSONs added
- CDC request replies fixed
2024-11-14 13:19:09 +01:00
6edf06c2ca - polling with delay added
- H7xx initialization code corrected
2024-10-29 16:43:18 +01:00
e1ba91e952 Merge branch 'master' of epagris.com:epagris/flatUSB 2024-10-29 13:25:41 +01:00
4ddb02fb17 - H723 added
- some H7-related defines reworked
2024-10-29 13:22:45 +01:00
32a964f13c Merge branch 'master' of epagris.com:epagris/flatUSB 2024-10-06 23:38:39 +02:00
9394977012 flatUSB descriptors changed 2024-10-06 23:38:36 +02:00
d671bac4af - merge: usb_driver.c conflicts fixed 2024-07-11 08:17:47 +02:00
a9d0e99d7b - CDC: the way responding on the notification element changed 2024-07-11 08:05:29 +02:00
4df8912593 - InaboF407 working code 2024-06-25 08:09:54 +02:00
8ca9852915 Merge branch 'master' of epagris.com:epagris/flatUSB 2024-06-25 07:18:08 +02:00
d140d7c183 - EEM class indented 2024-06-25 07:17:29 +02:00
49 changed files with 8887 additions and 1465 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
docs/html/

View File

@ -10,8 +10,8 @@ endif()
set(FLATUSB_CLASSES_SRC "") set(FLATUSB_CLASSES_SRC "")
if ("CDC_ACM" IN_LIST FLATUSB_CLASSES) if ("CDC_ACM" IN_LIST FLATUSB_CLASSES)
list(APPEND FLATUSB_CLASSES_SRC list(APPEND FLATUSB_CLASSES_SRC
class/cdc.c class/acm.c
class/cdc.h class/acm.h
) )
endif() endif()
@ -22,22 +22,36 @@ if ("CDC_EEM" IN_LIST FLATUSB_CLASSES)
) )
endif() endif()
if (FLATUSB_DESC_DIR)
AUX_SOURCE_DIRECTORY(${FLATUSB_DESC_DIR} FLATUSB_DESC_SRC)
set(FLATUSB_DESC_HEADER ${FLATUSB_DESC_DIR}/usb_desc.h)
if (FLATUSB_DESC_SRC)
message("flatUSB descriptors: " ${FLATUSB_DESC_SRC})
else()
message("flatUSB descriptors: (empty)")
endif()
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}") message("flatUSB classes selected: ${FLATUSB_CLASSES}")
set(FLATUSB_SRC set(FLATUSB_SRC
${FLATUSB_CLASSES_SRC} ${FLATUSB_CLASSES_SRC}
${FLATUSB_DESC_SRC}
${FLATUSB_DRIVER_SRC}
desc/usb_desc.c
desc/usb_desc.h
usb.c usb.c
usb_callback_event.h usb_callback_event.h
usb_common_defs.h
usb_common.h usb_common.h
usb_common_types.h usb_common_types.h
usb_core_types.h
usb_device_types.h usb_device_types.h
usb_driver.c #usb_driver.c
usb_driver.h #usb_driver.h
usb.h usb.h
# utils/gen_queue.c # utils/gen_queue.c
@ -45,6 +59,23 @@ set(FLATUSB_SRC
) )
add_library(${FLATUSB_TARGET} STATIC ${FLATUSB_SRC}) add_library(${FLATUSB_TARGET} STATIC ${FLATUSB_SRC})
target_include_directories(${FLATUSB_TARGET} PRIVATE ${FLATUSB_INCLUDES}) target_include_directories(${FLATUSB_TARGET} PRIVATE ${FLATUSB_INCLUDES} ${CMAKE_CURRENT_LIST_DIR})
target_compile_options(${FLATUSB_TARGET} PRIVATE ${FLATUSB_CPU_PARAMS}) target_compile_options(${FLATUSB_TARGET} PRIVATE ${FLATUSB_CPU_PARAMS})
target_compile_definitions(${FLATUSB_TARGET} PRIVATE ${FLATUSB_COMPILE_DEFS}) target_compile_definitions(${FLATUSB_TARGET} PRIVATE ${FLATUSB_COMPILE_DEFS})
target_compile_definitions(${FLATUSB_TARGET} PRIVATE FLATUSB_DESCRIPTOR_HEADER="${FLATUSB_DESC_HEADER}")
if (FLATUSB_DESC_JSON AND FLATUSB_DESC_DIR)
message("flatUSB: descriptor JSON passed: " ${FLATUSB_DESC_JSON})
message("flatUSB: also defining flatUSB-gen-desc target")
add_custom_target(
flatUSB-gen-desc
COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/desc-gen/main.py ${FLATUSB_DESC_JSON} ${FLATUSB_DESC_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)
endif()
add_custom_target(
flatUSB-docs
COMMAND doxygen
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)

2676
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

198
class/acm.c Normal file
View File

@ -0,0 +1,198 @@
/**
******************************************************************************
* @file cdc.c
* @copyright András Wiesner, 2024-\showdate "%Y"
* @brief CDC ACM implementation module for the flatUSB project.
******************************************************************************
*/
#include "acm.h"
#include <memory.h>
#include <stdint.h>
#include <string.h>
#include "../usb.h"
#include "../usb_device_types.h"
#include "cmsis_os2.h"
#include <blocking_io/blocking_fifo.h>
#include <strings.h>
// -------------------------
static Usb_AcmState acms = {0}; ///< ACM module state
static uint8_t tx_buffer[USB_ACM_PCKT_BUFSIZE]; ///< Transmit buffer
static uint8_t fifo_mem[USB_ACM_FIFO_MEM_SIZE]; ///< Memory assigned to the TX BFifo
static BFifo fifo; ///< TX Blocking FIFO
static osThreadId_t th; ///< ACM thread
static osEventFlagsId_t flags; ///< Event flags
#define USB_ACM_DATA_IN_DONE (0x01) ///< IN transfer done flag
#define USB_ACM_COMM_INIT (0x02) ///< Communication has been initialized
#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_INITIAL_DELAY_TICKS (100) ///< Delay before sending data
// -------------------------
static void thread_usb_acm(void *arg) {
osEventFlagsWait(flags, USB_ACM_COMM_INIT, 0, osWaitForever); // wait for communication to become initialized
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) {
uint32_t signals = osEventFlagsWait(flags, USB_ACM_HOSTBOUND_DATA_AVAIL, osFlagsWaitAny, USB_ACM_LOOP_TIMEOUT_TICKS);
if (signals != osErrorTimeout) { // check timeout
if (signals & USB_ACM_HOSTBOUND_DATA_AVAIL) { // data hostbound available
do {
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
if (readSize > 0) {
uint32_t writeSize = usbcore_schedule_transmission(acms.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer
bfifo_pop(&fifo, writeSize, 0); // pop with no blocking
}
} while (bfifo_get_used(&fifo) > 0);
}
} else { // timeout
// send an all-zero interrupt
usbcore_schedule_transmission(acms.ep_assignments.control_ep, (const uint8_t *)&(acms.interrupt_data), sizeof(uint16_t));
}
}
}
// -------------------------
void usb_acm_read_callback(const uint8_t *data, uint32_t size);
void usb_acm_init(const Usb_Acm_EpAssignments *as) {
// clear the structure
memset(&acms, 0, sizeof(Usb_AcmState));
// set Control Line State to something invalid
acms.control_line_state.D = USB_ACM_INVALID_CONTROL_LINE_STATE;
// fill-in assigments
acms.ep_assignments = *as;
// initialize buffer
bfifo_create(&fifo, fifo_mem, USB_ACM_FIFO_MEM_SIZE);
// initialize an all-0 interrupt
acms.interrupt_data = 0;
// communication parameters have not been set
acms.commInit = false;
// from now on CDC module is considered initialized
acms.moduleInit = true;
// create flags
flags = osEventFlagsNew(NULL);
// create thread
osThreadAttr_t attr;
memset(&attr, 0, sizeof(osThreadAttr_t));
attr.stack_size = 512;
attr.name = "acm";
th = osThreadNew(thread_usb_acm, NULL, &attr);
}
static void usb_cdc_review_comm_init() {
// check if line coding was initialized
Usb_Acm_LineCodingStruct *lc = &acms.line_coding;
bool lcOk = (lc->dwDTERate != 0) && (lc->bDataBits != 0);
// check if control line state was initialized
bool clsOk = acms.control_line_state.D != USB_ACM_INVALID_CONTROL_LINE_STATE;
// combine the above criteria
acms.commInit = lcOk && clsOk;
// signal the processing thread
osEventFlagsSet(flags, USB_ACM_COMM_INIT);
}
int usb_acm_process_and_return(Usb_CallbackEvent *cbevt) {
int ret = -1;
switch (cbevt->type) {
case USB_CBEVT_UNKNOWN_REQ: {
// initialize reply
cbevt->reply_data = NULL;
cbevt->reply_size = 0;
cbevt->reply_valid = true;
switch (cbevt->setup_request->bRequest) {
case USB_ACM_SET_LINE_CODING: // set line coding
memcpy(&acms.line_coding, cbevt->data, sizeof(Usb_Acm_LineCodingStruct));
usb_cdc_review_comm_init(); // review the communcation initialization state
ret = 0;
break;
case USB_ACM_GET_LINE_CODING: // get line coding
cbevt->reply_data = (const uint8_t *)&acms.line_coding; // expert move: pass the pointer, no copying
cbevt->reply_size = sizeof(Usb_Acm_LineCodingStruct);
ret = 0;
break;
case USB_ACM_SEND_BREAK: // send break
// do nothing
ret = 0;
break;
case USB_ACM_SET_CONTROL_LINE_STATE: // set control line state
acms.control_line_state.D = cbevt->setup_request->wValue; // control line state is carried in wValue of the SETUP request
usb_cdc_review_comm_init(); // review the communcation initialization state
ret = 0;
break;
default:
cbevt->reply_valid = false; // this event is not processed by or not related to the CDC ACM module
break;
}
break;
}
case USB_CBEVT_OUT: {
if (cbevt->ep == acms.ep_assignments.data_ep) {
ret = 0;
usb_acm_read_callback(cbevt->data, cbevt->size);
}
break;
}
case USB_CBEVT_IN: {
if (cbevt->ep == acms.ep_assignments.data_ep) {
osEventFlagsSet(flags, USB_ACM_DATA_IN_DONE);
}
ret = 0;
break;
}
default:
break;
}
return ret;
}
void usb_acm_write(const uint8_t *data, uint32_t size) {
if (acms.moduleInit) {
bfifo_push_all(&fifo, data, size);
osEventFlagsSet(flags, USB_ACM_HOSTBOUND_DATA_AVAIL);
}
}
/**
* @fn void usb_acm_read_callback(const uint8_t *data, uint32_t size)
* Callback function prototype for data reception. This function is
* expected to be overridden by the application.
*
* @param data ingress data
* @param size length of the data
*/
__attribute__((weak)) void usb_acm_read_callback(const uint8_t *data, uint32_t size) {
(void)data;
(void)size;
return;
}

128
class/acm.h Normal file
View File

@ -0,0 +1,128 @@
/**
******************************************************************************
* @file cdc.h
* @brief This is the USB Communication Device Class, Abstract Control Model
* implementation module for the flatUSB project. The following standard
* requestes are implemented: `SetLineCoding`, `GetLineCoding`,
* `SetControlLineState` and `SendBreak`. Data transmission is performed by
* calling `usb_acm_write()`. Ingress data is passed to the application through
* the `usb_acm_read_callback()` function. A weak prototype is provided which
* is expected to be overridden by the application. Supplying a BlockingFifo
* is mandatory!
*
* If necessary, `USB_CDC_FIFO_MEM_SIZE` and `USB_CDC_PCKT_BUFSIZE` can be freely
* overridden in the flatUSB configuration file.
*
* For more information about the USB CDC ACM class refer to the
* <a href="https://www.usb.org/document-library/class-definitions-communication-devices-12" target="_blank">
* USB CDC PSTN subclass</a>.
*
* @copyright András Wiesner, 2024-\showdate "%Y"
******************************************************************************
*/
#ifndef CORE_USB_CLASS_CDC
#define CORE_USB_CLASS_CDC
#include "../usb_callback_event.h"
/**
* CDC request codes
*/
typedef enum {
USB_ACM_SET_COMM_FEATURE = 0x2, ///< SetCommFeature
USB_ACM_GET_COMM_FEATURE = 0x3, ///< GetCommFeature
USB_ACM_CLEAR_COMM_FEATURE = 0x4, ///< ClearCommFeature
USB_ACM_SET_AUX_LINE_STATE = 0x10, ///< SetAuxLineState
USB_ACM_SET_HOOK_STATE = 0x11, ///< SetHookState
USB_ACM_PULSE_SETUP = 0x12, ///< PulseSetup
USB_ACM_SEND_PULSE = 0x13, ///< SendPulse
USB_ACM_SET_PULSE_TIME = 0x14, ///< SetPulseTime
USB_ACM_RING_AUX_JACK = 0x15, ///< RingAuxJack
USB_ACM_SET_LINE_CODING = 0x20, ///< SetLineCoding
USB_ACM_GET_LINE_CODING = 0x21, ///< GetLineCoding
USB_ACM_SET_CONTROL_LINE_STATE = 0x22, ///< SetControlLineState
USB_ACM_SEND_BREAK = 0x23, ///< SendBreak
USB_ACM_SET_RINGER_PARMS = 0x30, ///< SetRingerParms
USB_ACM_GET_RINGER_PARMS = 0x31, ///< GetRingerParms
USB_ACM_SET_OPERATION_PARMS = 0x32, ///< SetOperationParms
USB_ACM_GET_OPERATION_PARMS = 0x33, ///< GetOperationParms
USB_ACM_SET_LINE_PARMS = 0x34, ///< GetLineParms
} Usb_Acm_RequestCodes;
/**
* ACM line coding structure
*/
typedef struct {
uint32_t dwDTERate; ///< data terminal rate, bits per second
uint8_t bCharFormat; ///< character format
uint8_t bParityType; ///< parity type
uint8_t bDataBits; ///< data bits
} Usb_Acm_LineCodingStruct;
/**
* CDC control line state struct
*/
typedef struct {
uint16_t D; ///< settings word
} Usb_Acm_ControlLineStateStruct;
// ----------------
/**
* Endpoint assignments
*/
typedef struct {
uint8_t control_ep : 4; ///< Control endpoint
uint8_t data_ep : 4; ///< Data endpoint
} Usb_Acm_EpAssignments;
typedef struct {
Usb_Acm_EpAssignments ep_assignments; ///< Endpoint assignments
Usb_Acm_LineCodingStruct line_coding; ///< Line Coding
Usb_Acm_ControlLineStateStruct control_line_state; ///< Control Line State
uint16_t interrupt_data; ///< Data sent though the next transfer on the notification element
bool moduleInit; ///< CDC module is initialized
bool commInit; ///< Communication protocol is initialized
} Usb_AcmState;
// ----------------
#define USB_ACM_INVALID_CONTROL_LINE_STATE (0xFFFF) ///< Invalid Control Line State
// ----------------
#ifndef USB_ACM_FIFO_MEM_SIZE
#define USB_ACM_FIFO_MEM_SIZE (3072) ///< CDC ACM FIFO memory size
#endif
#ifndef USB_ACM_PCKT_BUFSIZE
#define USB_ACM_PCKT_BUFSIZE (128) ///< Buffer size assigned to the data transfer endpoint
#endif
// ----------------
/**
* Initialize CDC ACM module.
*
* @param as pointer to filled endpoint assignments object
*/
void usb_acm_init(const Usb_Acm_EpAssignments *as);
/**
* Regular USB class process and return function.
*
* @param cbevt pointer to callback event emitted by the USB core
* @return request is unprocessed if < 0
*/
int usb_acm_process_and_return(Usb_CallbackEvent *cbevt);
/**
* Write to CDC ACM interface.
*
* @param data data to be sent through the interface
* @param size length of the data
*/
void usb_acm_write(const uint8_t *data, uint32_t size);
#endif /* CORE_USB_CLASS_CDC */

View File

@ -1,136 +0,0 @@
#include "cdc.h"
#include <memory.h>
#include "../usb_device_types.h"
#include "../usb.h"
#include <blocking_io/blocking_fifo.h>
#include <cmsis_gcc.h>
// state
static USB_CdcState cdcs = { 0 };
static uint8_t tx_buffer[USB_CDC_PCKT_BUFSIZE];
#define USB_CDC_FIFO_MEM_SIZE (3072) // FIXME: ez vagy a blocking FIFO bugos
static uint8_t fifo_mem[USB_CDC_FIFO_MEM_SIZE];
static BFifo fifo;
void usb_cdc_read_callback(const uint8_t * data, uint32_t size);
void usb_cdc_init(const USB_CdcAssignments *as) {
// clear the structure
memset(&cdcs, 0, sizeof(USB_CdcState));
// fill-in default values
USB_Cdc_LineCodingStruct *lc = &cdcs.line_coding;
lc->dwDTERate = USB_CDC_DEFAULT_BITRATE;
lc->bCharFormat = USB_CDC_DEFAULT_CHAR_FORMAT;
lc->bParityType = USB_CDC_DEFAULT_PARITY_TYPE;
lc->bDataBits = USB_CDC_DEFAULT_DATA_BITS;
// fill-in assigments
cdcs.ep_assignments = *as;
// initialize buffer
bfifo_create(&fifo, fifo_mem, USB_CDC_FIFO_MEM_SIZE);
// from now on CDC is considered initialized
cdcs.initialized = true;
}
//static uint8_t replyBuf[sizeof(USB_Cdc_LineCodingStruct)];
int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) {
int ret = -1;
switch (cbevt->type) {
case USB_CBEVT_UNKNOWN_REQ: {
switch (cbevt->setup_request->bRequest) {
case USB_CDC_SET_LINE_CODING: // set line coding
memcpy(&cdcs.line_coding, cbevt->data, sizeof(USB_Cdc_LineCodingStruct));
//MSG("%u\n", cdcs.line_coding.dwDTERate);
ret = 0;
break;
case USB_CDC_GET_LINE_CODING: // get line coding
cbevt->reply_data = (const uint8_t *) &cdcs.line_coding; // expert move: pass the pointer, no copying
cbevt->reply_size = sizeof(USB_Cdc_LineCodingStruct);
cbevt->reply_valid = true; // the reply has been set to something valid
ret = 0;
break;
case USB_CDC_SEND_BREAK: // send break
// do nothing
ret = 0;
break;
case USB_CDC_SET_CONTROL_LINE_STATE: // set control line state
memcpy(&cdcs.control_line_state, cbevt->data, sizeof(USB_Cdc_ControlLineStateStruct));
//MSG("%u\n", cdcs.control_line_state.D);
ret = 0;
break;
default:
break;
}
// send a ZLP reply
if (ret != -1) {
cbevt->reply_data = NULL;
cbevt->reply_size = 0;
cbevt->reply_valid = true;
}
break;
}
case USB_CBEVT_OUT: {
if (cbevt->ep == cdcs.ep_assignments.data_ep) {
//MSG("%c\n", cbevt->data[0]);
ret = 0;
usb_cdc_read_callback(cbevt->data, cbevt->size);
//usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, cbevt->data, cbevt->size); // echo
}
break;
}
case USB_CBEVT_IN: {
if (cbevt->ep == cdcs.ep_assignments.control_ep) { // if notification feeding is requested
usbcore_schedule_transmission(cdcs.ep_assignments.control_ep, NULL, 0); // send ZLP
ret = 0;
} else if (cbevt->ep == cdcs.ep_assignments.data_ep) { // if data are requested
//usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, NULL, 0); // send ZLP
ret = 0;
// read from the fifo
uint32_t readSize = bfifo_read(&fifo, tx_buffer, USB_CDC_PCKT_BUFSIZE);
if (readSize > 0) {
uint32_t writeSize = usbcore_schedule_transmission(cdcs.ep_assignments.data_ep, tx_buffer, readSize); // write data acquired from the buffer
bfifo_pop(&fifo, writeSize, 0); // pop with no blocking
}
}
}
default:
break;
}
return ret;
}
// void usb_event_callback(USB_CallbackEvent *cbevt) {
// usb_cdc_process_and_return(cbevt);
// }
void usb_cdc_write(const uint8_t * data, uint32_t size) {
if (cdcs.initialized) {
bfifo_push_all(&fifo, data, size);
usbcore_wake_up_endpoint(cdcs.ep_assignments.data_ep, USB_IN);
}
}
__attribute__((weak)) void usb_cdc_read_callback(const uint8_t * data, uint32_t size) {
(void) data;
(void) size;
return;
}

View File

@ -1,73 +0,0 @@
#ifndef CORE_USB_CLASS_CDC
#define CORE_USB_CLASS_CDC
#include "../usb_callback_event.h"
// CDC request codes
typedef enum {
USB_CDC_SET_COMM_FEATURE = 0x2,
USB_CDC_GET_COMM_FEATURE = 0x3,
USB_CDC_CLEAR_COMM_FEATURE = 0x4,
USB_CDC_SET_AUX_LINE_STATE = 0x10,
USB_CDC_SET_HOOK_STATE = 0x11,
USB_CDC_PULSE_SETUP = 0x12,
USB_CDC_SEND_PULSE = 0x13,
USB_CDC_SET_PULSE_TIME = 0x14,
USB_CDC_RING_AUX_JACK = 0x15,
USB_CDC_SET_LINE_CODING = 0x20,
USB_CDC_GET_LINE_CODING = 0x21,
USB_CDC_SET_CONTROL_LINE_STATE = 0x22,
USB_CDC_SEND_BREAK = 0x23,
USB_CDC_SET_RINGER_PARMS = 0x30,
USB_CDC_GET_RINGER_PARMS = 0x31,
USB_CDC_SET_OPERATION_PARMS = 0x32,
USB_CDC_GET_OPERATION_PARMS = 0x33,
USB_CDC_SET_LINE_PARMS = 0x34,
} USB_Cdc_RequestCodes;
// CDC line coding structure
typedef struct {
uint32_t dwDTERate; // data terminal rate, bits per second
uint8_t bCharFormat; // character format
uint8_t bParityType; // parity type
uint8_t bDataBits; // data bits
} USB_Cdc_LineCodingStruct;
// CDC control line state struct
typedef struct {
uint16_t D; // settings word
} USB_Cdc_ControlLineStateStruct;
// ----------------
// endpoint assignments
typedef struct {
uint8_t control_ep : 4; // control endpoint
uint8_t data_ep : 4; // data endpoint
} USB_CdcAssignments;
typedef struct {
USB_CdcAssignments ep_assignments; // endpoint assignments
USB_Cdc_LineCodingStruct line_coding; // line coding
USB_Cdc_ControlLineStateStruct control_line_state; // control line state
bool initialized; // CDC is initialized
} USB_CdcState;
// ----------------
#define USB_CDC_DEFAULT_BITRATE (115200)
#define USB_CDC_DEFAULT_DATA_BITS (8)
#define USB_CDC_DEFAULT_PARITY_TYPE (0)
#define USB_CDC_DEFAULT_CHAR_FORMAT (0)
// ----------------
#define USB_CDC_PCKT_BUFSIZE (300)
// ----------------
void usb_cdc_init(const USB_CdcAssignments * as);
int usb_cdc_process_and_return(USB_CallbackEvent * cbevt);
void usb_cdc_write(const uint8_t * data, uint32_t size);
#endif /* CORE_USB_CLASS_CDC */

View File

@ -73,7 +73,7 @@ USB_EemEvent usb_eem_pop_event() {
return evt; return evt;
} }
int usb_eem_process_and_return(USB_CallbackEvent *cbevt) { int usb_eem_process_and_return(Usb_CallbackEvent *cbevt) {
switch (cbevt->type) { switch (cbevt->type) {
case USB_CBEVT_UNKNOWN_REQ: case USB_CBEVT_UNKNOWN_REQ:
break; break;
@ -300,6 +300,10 @@ void usb_eem_ethernet_intercept_cb(EthInterface *intf, const RawPckt *rawPckt) {
} }
void usb_eem_set_intf(EthInterface *intf) { void usb_eem_set_intf(EthInterface *intf) {
if (!eems.initialized) {
return;
}
eems.intf = intf; // store interface eems.intf = intf; // store interface
ethinf_set_intercept_callback(intf, usb_eem_ethernet_intercept_cb); // set intercept callback ethinf_set_intercept_callback(intf, usb_eem_ethernet_intercept_cb); // set intercept callback
} }

View File

@ -1,4 +1,5 @@
import subprocess import subprocess
import datetime
import Descriptor import Descriptor
import Descriptor as desc import Descriptor as desc
@ -78,8 +79,12 @@ class ConfigGenerator:
self.LANG_ID_US_EN = "0x0409" self.LANG_ID_US_EN = "0x0409"
self.max_config_str_desc_size = 0 # maximal size (wLength) of config descriptors self.max_config_str_desc_size = (
self.max_endpoint_index = 0 # maximum endpoint index used in at least one of the configurations 0 # maximal size (wLength) of config descriptors
)
self.max_endpoint_index = (
0 # maximum endpoint index used in at least one of the configurations
)
self.max_endpoint_count = 0 # maximum endpoint count across all configurations self.max_endpoint_count = 0 # maximum endpoint count across all configurations
# add macro to header file # add macro to header file
@ -94,23 +99,36 @@ class ConfigGenerator:
self.h += "\n" self.h += "\n"
# save generated config # save generated config
def save(self): def save(self, target_dir):
DECLARATIONS = "usb_desc.h" DECLARATIONS = "usb_desc.h"
DEFINITIONS = "usb_desc.c" DEFINITIONS = "usb_desc.c"
with open(DEFINITIONS, 'w') as f_c: DECLARATIONS_FULLPATH = target_dir + "/" + DECLARATIONS
f_c.write("#include \"" + DECLARATIONS + "\"\n\n") DEFINITIONS_FULLPATH = target_dir + "/" + DEFINITIONS
INFO_HEADER = "//------------\n// Descriptors generated by flatUSB Descriptor Generator by Epagris on " + (str)(datetime.date.today()) + ".\n//------------\n\n"
with open(DEFINITIONS_FULLPATH, "w") as f_c:
f_c.write(INFO_HEADER)
f_c.write('#include "' + DECLARATIONS + '"\n\n')
f_c.write(self.c) f_c.write(self.c)
include_guard_tag = "_" + DECLARATIONS.replace(".", "_").upper() include_guard_tag = "_" + DECLARATIONS.replace(".", "_").upper()
with open(DECLARATIONS, 'w') as f_h: with open(DECLARATIONS_FULLPATH, "w") as f_h:
f_h.writelines(("#ifndef " + include_guard_tag + "\n", "#define " + include_guard_tag + "\n\n", "#include \"../usb_device_types.h\"\n\n")) f_h.writelines(
(
INFO_HEADER,
"#ifndef " + include_guard_tag + "\n",
"#define " + include_guard_tag + "\n\n",
'#include "usb_descriptor_common.h"\n\n',
)
)
f_h.write(self.h) f_h.write(self.h)
f_h.writelines(("\n\n#endif // " + include_guard_tag)) f_h.writelines(("\n\n#endif // " + include_guard_tag))
subprocess.run(["indent", "-linux", "-l120", "-i4", "-nut", DECLARATIONS]) subprocess.run(["indent", "-linux", "-l120", "-i4", "-nut", DECLARATIONS_FULLPATH])
subprocess.run(["indent", "-linux", "-l120", "-i4", "-nut", DEFINITIONS]) subprocess.run(["indent", "-linux", "-l120", "-i4", "-nut", DEFINITIONS_FULLPATH])
# generate Device descriptor # generate Device descriptor
def generate_device_descriptor(self): def generate_device_descriptor(self):
@ -152,35 +170,49 @@ class ConfigGenerator:
# generate interface descriptor # generate interface descriptor
intf_varname = "intf{:d}".format(intf["id"]) intf_varname = "intf{:d}".format(intf["id"])
ep_count = get_distinct_endpoint_count(intf["endpoints"]) ep_count = get_distinct_endpoint_count(intf["endpoints"])
intf_struct = desc.InterfaceDescriptor(intf_varname, intf) # endpoint count is unknown intf_struct = desc.InterfaceDescriptor(
intf_varname, intf
) # endpoint count is unknown
full_conf_struct.add_record(intf_struct) full_conf_struct.add_record(intf_struct)
# generate additional interface descriptors # generate additional interface descriptors
for addintf in intf["additional_interfaces"]: for addintf in intf["additional_interfaces"]:
type = addintf["type"] type = addintf["type"]
addintf_struct = desc.DescriptorFactory.generate(type, intf_varname + type, addintf, self.str_mgr) addintf_struct = desc.DescriptorFactory.generate(
type, intf_varname + type, addintf, self.str_mgr
)
full_conf_struct.add_record(addintf_struct) full_conf_struct.add_record(addintf_struct)
# generate typedef only, if not done before # generate typedef only, if not done before
if addintf_tdefs.count(type) == 0: if addintf_tdefs.count(type) == 0:
self.h += addintf_struct.print_typedef() # print typedef self.h += addintf_struct.print_typedef() # print typedef
addintf_tdefs.append(type) # add to already generated typedefs to avoid duplication addintf_tdefs.append(
type
) # add to already generated typedefs to avoid duplication
# generate endpoint descriptors # generate endpoint descriptors
self.generate_endpoint_descriptors(intf, full_conf_struct) # generate endpoints self.generate_endpoint_descriptors(
intf, full_conf_struct
) # generate endpoints
# generate configuration descriptors and subordinate descriptor # generate configuration descriptors and subordinate descriptor
def generate_configuration_descriptors(self): def generate_configuration_descriptors(self):
# array for referencing configuration descriptors # array for referencing configuration descriptors
conf_desc_array_struct = desc.Descriptor("confDescs", "USB_ConfigurationDesc *", []) conf_desc_array_struct = desc.Descriptor(
"confDescs", "USB_ConfigurationDesc *", []
)
conf_desc_array_struct.typedef = "ConfDescs" conf_desc_array_struct.typedef = "ConfDescs"
# generate full configurations # generate full configurations
index = 0 index = 0
for conf in self.config["configurations"]: for conf in self.config["configurations"]:
full_conf_typename = "struct _FullConfigurations{:d}".format(index) full_conf_typename = "struct _FullConfigurations{:d}".format(index)
full_conf_struct = Descriptor.Descriptor("fullConfDescs{:d}".format(index), full_conf_typename, []) full_conf_struct = Descriptor.Descriptor(
conf_struct = desc.ConfigurationDescriptor("config", conf, "sizeof({:s})".format(full_conf_typename)) # TODO: total length "fullConfDescs{:d}".format(index), full_conf_typename, []
)
conf_struct = desc.ConfigurationDescriptor(
"config", conf, "sizeof({:s})".format(full_conf_typename)
) # TODO: total length
full_conf_struct.add_record(conf_struct) full_conf_struct.add_record(conf_struct)
self.generate_interface_descriptors(conf, full_conf_struct) self.generate_interface_descriptors(conf, full_conf_struct)
@ -188,7 +220,12 @@ class ConfigGenerator:
self.h += full_conf_struct.gen_h(print_typedef=True) self.h += full_conf_struct.gen_h(print_typedef=True)
# add descriptor's pointer to the list # add descriptor's pointer to the list
str_desc_ptr = StructGenerator.DummyRecord(full_conf_struct.name, "USB_ConfigurationDesc *", "(USB_ConfigurationDesc *) &" + full_conf_struct.name, StructGenerator.RecordFactory.PLAIN_PRINTER) str_desc_ptr = StructGenerator.DummyRecord(
full_conf_struct.name,
"USB_ConfigurationDesc *",
"(USB_ConfigurationDesc *) &" + full_conf_struct.name,
StructGenerator.RecordFactory.PLAIN_PRINTER,
)
conf_desc_array_struct.add_record(str_desc_ptr) conf_desc_array_struct.add_record(str_desc_ptr)
self.h += self.DIVIDER self.h += self.DIVIDER
@ -212,7 +249,12 @@ class ConfigGenerator:
self.c += lang_id_struct.gen_c() self.c += lang_id_struct.gen_c()
self.h += lang_id_struct.gen_h(print_typedef=True) self.h += lang_id_struct.gen_h(print_typedef=True)
str_desc_ptr = StructGenerator.DummyRecord(lang_id_struct.name, "USB_StringDesc *", "(USB_StringDesc *) &" + lang_id_struct.name, StructGenerator.RecordFactory.PLAIN_PRINTER) str_desc_ptr = StructGenerator.DummyRecord(
lang_id_struct.name,
"USB_StringDesc *",
"(USB_StringDesc *) &" + lang_id_struct.name,
StructGenerator.RecordFactory.PLAIN_PRINTER,
)
str_desc_array_struct.add_record(str_desc_ptr) str_desc_array_struct.add_record(str_desc_ptr)
# generate individual string descriptors # generate individual string descriptors
@ -226,7 +268,12 @@ class ConfigGenerator:
self.h += str_struct.gen_h(print_typedef=True) self.h += str_struct.gen_h(print_typedef=True)
# add descriptor's pointer to the list # add descriptor's pointer to the list
str_desc_ptr = StructGenerator.DummyRecord(text_id, "USB_StringDesc *", "(USB_StringDesc *) &" + str_struct.name, StructGenerator.RecordFactory.PLAIN_PRINTER) str_desc_ptr = StructGenerator.DummyRecord(
text_id,
"USB_StringDesc *",
"(USB_StringDesc *) &" + str_struct.name,
StructGenerator.RecordFactory.PLAIN_PRINTER,
)
str_desc_array_struct.add_record(str_desc_ptr) str_desc_array_struct.add_record(str_desc_ptr)
self.h += self.DIVIDER self.h += self.DIVIDER
@ -237,10 +284,14 @@ class ConfigGenerator:
self.c += str_desc_array_struct.gen_c() self.c += str_desc_array_struct.gen_c()
def generate_macros(self): def generate_macros(self):
self.add_def_macro("USBF_NOF_CONFIGURATIONS", str(len(self.config["configurations"])), "Number of configurations") self.add_def_macro(
"USBF_NOF_CONFIGURATIONS",
str(len(self.config["configurations"])),
"Number of configurations",
)
self.h += self.DIVIDER self.h += self.DIVIDER
def generate(self): def generate(self, target_dir: str):
self.generate_macros() # generate macro definitions self.generate_macros() # generate macro definitions
self.generate_device_descriptor() # generate device descriptor self.generate_device_descriptor() # generate device descriptor
self.generate_device_qualifier_descriptor() # generate device qualifier descriptor self.generate_device_qualifier_descriptor() # generate device qualifier descriptor
@ -248,4 +299,4 @@ class ConfigGenerator:
self.generate_string_descriptors() # generate string descriptors (should be the last one, since additional interface descriptors may add new string descriptors) self.generate_string_descriptors() # generate string descriptors (should be the last one, since additional interface descriptors may add new string descriptors)
# save generated files # save generated files
self.save() self.save(target_dir)

45
desc-gen/main.py Normal file
View File

@ -0,0 +1,45 @@
import json
from sys import argv
import Descriptor as desc
import StructGenerator as sg
from itertools import chain
from ConfigGenerator import ConfigGenerator
# print welcome message
print("----\nflatUSB descriptor generator by Epagris\n----\n")
# check number of parameters
if len(argv) < 3:
print("Insufficient parameters!\nCorrect usage: `main.py <input json> <target directory>`\nExiting.")
exit(0)
# fetch USB settings
usb_config_file_name = argv[1]
usb_target_dir = argv[2]
# print input and output parameters
print("Input JSON: ", usb_config_file_name)
print("Target directory: ", usb_target_dir)
with open(usb_config_file_name, 'r') as usb_config_file:
usb_config_data = usb_config_file.read()
usb_config = json.loads(usb_config_data)
# process "misc"
if "misc" in usb_config:
misc = usb_config["misc"]
# mutable descriptors
if "mutable_descriptors" in misc:
if misc["mutable_descriptors"]:
desc.Descriptor.QUALIFIERS = ""
# generate config
cfggen = ConfigGenerator(usb_config)
cfggen.generate(usb_target_dir)
# print complete message
print("Descriptor generation complete!")

View File

@ -1,53 +0,0 @@
import json
from sys import argv
import Descriptor as desc
import StructGenerator as sg
from itertools import chain
from ConfigGenerator import ConfigGenerator
# fetch USB settings
usb_config_file_name = argv[1]
#usb_config_file_name = "/home/epagris/VCSDEV/usbt1/stws/USB-T1/Modules/flatUSB/desc/usb_config_cdc.json"
with open(usb_config_file_name, 'r') as usb_config_file:
usb_config_data = usb_config_file.read()
usb_config = json.loads(usb_config_data)
# process "misc"
if "misc" in usb_config:
misc = usb_config["misc"]
# mutable descriptors
if "mutable_descriptors" in misc:
if misc["mutable_descriptors"]:
desc.Descriptor.QUALIFIERS = ""
# generate config
cfggen = ConfigGenerator(usb_config)
cfggen.generate()
# devDesc = desc.DeviceDescriptor(usb_config)
# print(devDesc.print_assigment())
#
# strDesc = desc.StringDescriptor("Testdevice", "StrDevice")
# print(strDesc.print_assigment())
#intrec = sg.RecordFactory.create("num", "u32", 127)
#print(intrec.print_typedef("INT"))
# intrec = sg.RecordFactory.create([ "num", "u32", 127, ])
# print(intrec.print_typedef("INTINT"))
# #
# strrec = sg.RecordFactory.create("some_string", "str", "Some string")
# print(strrec.print_typedef("STR"))
#print(strrec.print_content())
#
# recs = [ intrec, strrec ]
#
# struct1 = sg.StructRecord("struct1", "struct struct 1", recs)
# struct2 = sg.StructRecord("struct2", "struct struct2", recs)
# print(struct2.print_typedef("struct2"))
#
# structrec = sg.StructRecord("object", "struct compound", [ struct1, struct2, *recs ])
# print(structrec.print_content())

View File

@ -1,176 +0,0 @@
#include "usb_desc.h"
const USB_DeviceDesc devDesc = { /* Device Descriptor */
0x12, //bLength
UD_Device, //bDescriptorType
0x200, //bcdUSB
0x2, //bDeviceClass
0x0, //bDeviceSubclass
0x0, //bDeviceProtocol
0x40, //bMaxPacketSize0
0x925, //idVendor
0x9050, //idProduct
0x100, //bcdDevice
0x1, //iManufacturer
0x2, //iProduct
0x3, //iSerialNumber
0x1, //bNumConfigurations
};
const USB_DeviceQualifierDesc devQualDesc = { /* Device Qualifier descriptor */
0x9, //bLength
UD_DeviceQualifier, //bDescriptorType
0x200, //bcdUSB
0x2, //bDeviceClass
0x0, //bDeviceSubclass
0x0, //bDeviceProtocol
0x40, //bMaxPacketSize
0x1, //bNumConfigurations
};
const FullConfigurations0 fullConfDescs0 = {
{ /* Configuration descriptor */
0x9, //bLength
UD_Configuration, //bDescriptorType
sizeof(struct _FullConfigurations0), //wTotalLength
0x3, //bNumInterfaces
0x1, //bConfigurationValue
0x0, //iConfiguration
USB_CONFIG_ATTR_USB1_1_FLAG, //bmAttributes
0x32, //bMaxPower
}, //config
{ /* Interface descriptor : 0 */
0x9, //bLength
UD_Interface, //bDescriptorType
0x0, //bInterfaceNumber
0x0, //bAlternateSetting
0x1, //bNumEndpoints
0x2, //bInterfaceClass
0x2, //bInterfaceSubclass
0x1, //bInterfaceProtocol
0x0, //iInterface
}, //intf0
{ /* Header Functional descriptor */
0x5, //bLength
0x24, //bDescriptorType
0x0, //bDescriptorSubType
0x110, //bcdCDC
}, //intf0hfd
{ /* Abstract Control Management Functional descriptor */
0x4, //bLength
0x24, //bDescriptorType
0x2, //bDescriptorSubType
0x2, //bmCapabilities
}, //intf0acmfd
{ /* Union Functional descriptor */
0x5, //bLength
0x24, //bDescriptorType
0x6, //bDescriptorSubType
0x0, //bMasterInterface
0x1, //bSlaveInterface0
}, //intf0ufd
{ /* Call Management Functional descriptor */
0x5, //bLength
0x24, //bDescriptorType
0x1, //bDescriptorSubType
0x0, //bmCapabilities
0x1, //dDataInterface
}, //intf0cmfd
{ /* Endpoint descriptor : 2 in */
0x7, //bLength
UD_Endpoint, //bDescriptorType
(USB_IN << 7) | (2), //bEndpointAddress
UT_Interrupt, //bmAttributes
0x40, //wMaxPacketSize
0x2, //bInterval
}, //ep2in
{ /* Interface descriptor : 1 */
0x9, //bLength
UD_Interface, //bDescriptorType
0x1, //bInterfaceNumber
0x0, //bAlternateSetting
0x2, //bNumEndpoints
0xa, //bInterfaceClass
0x0, //bInterfaceSubclass
0x0, //bInterfaceProtocol
0x0, //iInterface
}, //intf1
{ /* Endpoint descriptor : 1 in */
0x7, //bLength
UD_Endpoint, //bDescriptorType
(USB_IN << 7) | (1), //bEndpointAddress
UT_Bulk, //bmAttributes
0x40, //wMaxPacketSize
0x14, //bInterval
}, //ep1in
{ /* Endpoint descriptor : 1 out */
0x7, //bLength
UD_Endpoint, //bDescriptorType
(USB_OUT << 7) | (1), //bEndpointAddress
UT_Bulk, //bmAttributes
0x40, //wMaxPacketSize
0x14, //bInterval
}, //ep1out
{ /* Interface descriptor : 2 */
0x9, //bLength
UD_Interface, //bDescriptorType
0x2, //bInterfaceNumber
0x0, //bAlternateSetting
0x2, //bNumEndpoints
0x2, //bInterfaceClass
0xc, //bInterfaceSubclass
0x7, //bInterfaceProtocol
0x0, //iInterface
}, //intf2
{ /* Endpoint descriptor : 3 in */
0x7, //bLength
UD_Endpoint, //bDescriptorType
(USB_IN << 7) | (3), //bEndpointAddress
UT_Bulk, //bmAttributes
0x40, //wMaxPacketSize
0xa, //bInterval
}, //ep3in
{ /* Endpoint descriptor : 3 out */
0x7, //bLength
UD_Endpoint, //bDescriptorType
(USB_OUT << 7) | (3), //bEndpointAddress
UT_Bulk, //bmAttributes
0x40, //wMaxPacketSize
0xa, //bInterval
}, //ep3out
};
const ConfDescs confDescs = {
(USB_ConfigurationDesc *) & fullConfDescs0, //fullConfDescs0
};
const USB_StrDesc_lang_id lang_id = { /* String descriptor */
0x4, //bLength
UD_String, //bDescriptorType
0x409, //bString
};
const USB_StrDesc_vendor_string vendor_string = { /* String descriptor */
0x10, //bLength
UD_String, //bDescriptorType
{'E', 'p', 'a', 'g', 'r', 'i', 's',}, //bString
};
const USB_StrDesc_product_string product_string = { /* String descriptor */
0x16, //bLength
UD_String, //bDescriptorType
{'T', 'e', 's', 't', 'd', 'e', 'v', 'i', 'c', 'e',}, //bString
};
const USB_StrDesc_serial_number serial_number = { /* String descriptor */
0xa, //bLength
UD_String, //bDescriptorType
{'1', '5', '5', '2',}, //bString
};
const StringDescs strDescs = {
(USB_StringDesc *) & lang_id, //lang_id
(USB_StringDesc *) & vendor_string, //vendor_string
(USB_StringDesc *) & product_string, //product_string
(USB_StringDesc *) & serial_number, //serial_number
};

View File

@ -1,94 +0,0 @@
#ifndef _USB_DESC_H
#define _USB_DESC_H
#include "../usb_device_types.h"
#define USBF_NOF_CONFIGURATIONS (1) // Number of configurations
// ---------------------------
extern const USB_DeviceDesc devDesc; /* Device Descriptor */
extern const USB_DeviceQualifierDesc devQualDesc; /* Device Qualifier descriptor */
typedef struct _USB_HeaderFunctionalDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t bcdCDC;
} __attribute__((packed)) USB_HeaderFunctionalDescriptor;
typedef struct _USB_AbstractControlManagementFunctionalDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bmCapabilities;
} __attribute__((packed)) USB_AbstractControlManagementFunctionalDescriptor;
typedef struct _USB_UnionFunctionalDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bMasterInterface;
uint8_t bSlaveInterface0;
} __attribute__((packed)) USB_UnionFunctionalDescriptor;
typedef struct _USB_CallManagementFunctionalDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bmCapabilities;
uint8_t dDataInterface;
} __attribute__((packed)) USB_CallManagementFunctionalDescriptor;
typedef struct _FullConfigurations0 {
USB_ConfigurationDesc config; /* Configuration descriptor */
USB_InterfaceDesc intf0; /* Interface descriptor : 0 */
USB_HeaderFunctionalDescriptor intf0hfd; /* Header Functional descriptor */
USB_AbstractControlManagementFunctionalDescriptor intf0acmfd; /* Abstract Control Management Functional descriptor */
USB_UnionFunctionalDescriptor intf0ufd; /* Union Functional descriptor */
USB_CallManagementFunctionalDescriptor intf0cmfd; /* Call Management Functional descriptor */
USB_EndpointDesc ep2in; /* Endpoint descriptor : 2 in */
USB_InterfaceDesc intf1; /* Interface descriptor : 1 */
USB_EndpointDesc ep1in; /* Endpoint descriptor : 1 in */
USB_EndpointDesc ep1out; /* Endpoint descriptor : 1 out */
USB_InterfaceDesc intf2; /* Interface descriptor : 2 */
USB_EndpointDesc ep3in; /* Endpoint descriptor : 3 in */
USB_EndpointDesc ep3out; /* Endpoint descriptor : 3 out */
} __attribute__((packed)) FullConfigurations0;
extern const FullConfigurations0 fullConfDescs0;
// ---------------------------
typedef USB_ConfigurationDesc *ConfDescs[1];
extern const ConfDescs confDescs;
typedef struct _USB_StrDesc_lang_id {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bString;
} __attribute__((packed)) USB_StrDesc_lang_id;
extern const USB_StrDesc_lang_id lang_id; /* String descriptor */
typedef struct _USB_StrDesc_vendor_string {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bString[7];
} __attribute__((packed)) USB_StrDesc_vendor_string;
extern const USB_StrDesc_vendor_string vendor_string; /* String descriptor */
typedef struct _USB_StrDesc_product_string {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bString[10];
} __attribute__((packed)) USB_StrDesc_product_string;
extern const USB_StrDesc_product_string product_string; /* String descriptor */
typedef struct _USB_StrDesc_serial_number {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bString[4];
} __attribute__((packed)) USB_StrDesc_serial_number;
extern const USB_StrDesc_serial_number serial_number; /* String descriptor */
// ---------------------------
typedef USB_StringDesc *StringDescs[4];
extern const StringDescs strDescs;
#endif // _USB_DESC_H

21
docs/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,157 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
// SVG icons from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
static title = "Toggle Light/Dark Mode"
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
static _staticConstructor = function() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
// Update the color scheme when the browsers preference changes
// without user interaction on the website.
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
})
// Update the color scheme when the tab is made visible again.
// It is possible that the appearance was changed in another tab
// while this tab was in the background.
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
}
});
}()
static init() {
$(function() {
$(document).ready(function() {
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
toggleButton.updateIcon()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
toggleButton.updateIcon()
})
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
toggleButton.updateIcon()
}
});
$(document).ready(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
$(window).resize(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
})
})
}
constructor() {
super();
this.onclick=this.toggleDarkMode
}
/**
* @returns `true` for dark-mode, `false` for light-mode system preference
*/
static get systemPreference() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
/**
* @returns `true` for dark-mode, `false` for light-mode user preference
*/
static get userPreference() {
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
}
static set userPreference(userPreference) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
if(!userPreference) {
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
}
} else {
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
}
}
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
}
static enableDarkMode(enable) {
if(enable) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
document.documentElement.classList.add("dark-mode")
document.documentElement.classList.remove("light-mode")
} else {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
document.documentElement.classList.remove("dark-mode")
document.documentElement.classList.add("light-mode")
}
}
static onSystemPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
static onUserPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
toggleDarkMode() {
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
this.updateIcon()
}
updateIcon() {
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
} else {
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
}
}
}
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);

View File

@ -0,0 +1,85 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
constructor() {
super();
this.onclick=this.copyContent
}
static title = "Copy to clipboard"
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`
static successDuration = 980
static init() {
$(function() {
$(document).ready(function() {
if(navigator.clipboard) {
const fragments = document.getElementsByClassName("fragment")
for(const fragment of fragments) {
const fragmentWrapper = document.createElement("div")
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
fragmentWrapper.appendChild(fragment)
fragmentWrapper.appendChild(fragmentCopyButton)
}
}
})
})
}
copyContent() {
const content = this.previousSibling.cloneNode(true)
// filter out line number from file listings
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
node.remove()
})
let textContent = content.textContent
// remove trailing newlines that appear in file listings
let numberOfTrailingNewlines = 0
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
numberOfTrailingNewlines++;
}
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
navigator.clipboard.writeText(textContent);
this.classList.add("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
window.setTimeout(() => {
this.classList.remove("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
}, DoxygenAwesomeFragmentCopyButton.successDuration);
}
}
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)

View File

@ -0,0 +1,91 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeInteractiveToc {
static topOffset = 38
static hideMobileMenu = true
static headers = []
static init() {
window.addEventListener("load", () => {
let toc = document.querySelector(".contents > .toc")
if(toc) {
toc.classList.add("interactive")
if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) {
toc.classList.add("open")
}
document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => {
if(toc.classList.contains("open")) {
toc.classList.remove("open")
} else {
toc.classList.add("open")
}
})
document.querySelectorAll(".contents > .toc > ul a").forEach((node) => {
let id = node.getAttribute("href").substring(1)
DoxygenAwesomeInteractiveToc.headers.push({
node: node,
headerNode: document.getElementById(id)
})
document.getElementById("doc-content")?.addEventListener("scroll",this.throttle(DoxygenAwesomeInteractiveToc.update, 100))
})
DoxygenAwesomeInteractiveToc.update()
}
})
}
static update() {
let active = DoxygenAwesomeInteractiveToc.headers[0]?.node
DoxygenAwesomeInteractiveToc.headers.forEach((header) => {
let position = header.headerNode.getBoundingClientRect().top
header.node.classList.remove("active")
header.node.classList.remove("aboveActive")
if(position < DoxygenAwesomeInteractiveToc.topOffset) {
active = header.node
active?.classList.add("aboveActive")
}
})
active?.classList.add("active")
active?.classList.remove("aboveActive")
}
static throttle(func, delay) {
let lastCall = 0;
return function (...args) {
const now = new Date().getTime();
if (now - lastCall < delay) {
return;
}
lastCall = now;
return setTimeout(() => {func(...args)}, delay);
};
}
}

View File

@ -0,0 +1,40 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@media screen and (min-width: 768px) {
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
}
}

View File

@ -0,0 +1,116 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
html {
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
* Make sure it is wide enough to contain the page title (logo + title + version)
*/
--side-nav-fixed-width: 335px;
--menu-display: none;
--top-height: 120px;
--toc-sticky-top: -25px;
--toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);
}
#projectname {
white-space: nowrap;
}
@media screen and (min-width: 768px) {
html {
--searchbar-background: var(--page-background-color);
}
#side-nav {
min-width: var(--side-nav-fixed-width);
max-width: var(--side-nav-fixed-width);
top: var(--top-height);
overflow: visible;
}
#nav-tree, #side-nav {
height: calc(100vh - var(--top-height)) !important;
}
#nav-tree {
padding: 0;
}
#top {
display: block;
border-bottom: none;
height: var(--top-height);
margin-bottom: calc(0px - var(--top-height));
max-width: var(--side-nav-fixed-width);
overflow: hidden;
background: var(--side-nav-background);
}
#main-nav {
float: left;
padding-right: 0;
}
.ui-resizable-handle {
cursor: default;
width: 1px !important;
background: var(--separator-color);
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
}
#nav-path {
position: fixed;
right: 0;
left: var(--side-nav-fixed-width);
bottom: 0;
width: auto;
}
#doc-content {
height: calc(100vh - 31px) !important;
padding-bottom: calc(3 * var(--spacing-large));
padding-top: calc(var(--top-height) - 80px);
box-sizing: border-box;
margin-left: var(--side-nav-fixed-width) !important;
}
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
}
#MSearchResultsWindow {
left: var(--spacing-medium) !important;
right: auto;
}
}

View File

@ -0,0 +1,90 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeTabs {
static init() {
window.addEventListener("load", () => {
document.querySelectorAll(".tabbed:not(:empty)").forEach((tabbed, tabbedIndex) => {
let tabLinkList = []
tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => {
tab.id = "tab_" + tabbedIndex + "_" + tabIndex
let header = tab.querySelector(".tab-title")
let tabLink = document.createElement("button")
tabLink.classList.add("tab-button")
tabLink.appendChild(header)
header.title = header.textContent
tabLink.addEventListener("click", () => {
tabbed.querySelectorAll(":scope > ul > li").forEach((tab) => {
tab.classList.remove("selected")
})
tabLinkList.forEach((tabLink) => {
tabLink.classList.remove("active")
})
tab.classList.add("selected")
tabLink.classList.add("active")
})
tabLinkList.push(tabLink)
if(tabIndex == 0) {
tab.classList.add("selected")
tabLink.classList.add("active")
}
})
let tabsOverview = document.createElement("div")
tabsOverview.classList.add("tabs-overview")
let tabsOverviewContainer = document.createElement("div")
tabsOverviewContainer.classList.add("tabs-overview-container")
tabLinkList.forEach((tabLink) => {
tabsOverview.appendChild(tabLink)
})
tabsOverviewContainer.appendChild(tabsOverview)
tabbed.before(tabsOverviewContainer)
function resize() {
let maxTabHeight = 0
tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => {
let visibility = tab.style.display
tab.style.display = "block"
maxTabHeight = Math.max(tab.offsetHeight, maxTabHeight)
tab.style.display = visibility
})
tabbed.style.height = `${maxTabHeight + 10}px`
}
resize()
new ResizeObserver(resize).observe(tabbed)
})
})
}
static resize(tabbed) {
}
}

2681
docs/doxygen-awesome.css Normal file

File diff suppressed because it is too large Load Diff

86
docs/header.html Normal file
View File

@ -0,0 +1,86 @@
<!-- HTML header for doxygen 1.9.8-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<script type="text/javascript">var page_layout=1;</script>
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
$darkmode
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
</script>
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript">
DoxygenAwesomeInteractiveToc.init()
</script>
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript">
DoxygenAwesomeDarkModeToggle.init()
</script>
</head>
<body>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr id="projectrow">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign">
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber">&#160;$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td>
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<!--BEGIN !FULL_SIDEBAR-->
<td>$searchbox</td>
<!--END !FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
<!--BEGIN SEARCHENGINE-->
<!--BEGIN FULL_SIDEBAR-->
<tr><td colspan="2">$searchbox</td></tr>
<!--END FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

@ -0,0 +1,3 @@
body {
--top-height: 160px
}

655
driver/ch32/usb_drv.c Normal file
View 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
View 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 */

View File

@ -1,22 +1,27 @@
#ifndef CORE_USB_USB_COMMON_DEFS #ifndef CORE_USB_USB_COMMON_DEFS
#define CORE_USB_USB_COMMON_DEFS #define CORE_USB_USB_COMMON_DEFS
#if defined(STM32H745xx) || defined(STM32H743xx) #include <flatUSB_config.h>
// Select appropriate headers
#if defined(STM32H745xx) || defined(STM32H743xx) || defined(STM32H723xx)
#include <stm32h7xx.h> #include <stm32h7xx.h>
#include <stm32h7xx_hal.h> #include <stm32h7xx_hal.h>
#define USB_STM32H7 #define USB_STM32H7
// ------------
#elif defined(STM32F407xx) || defined(STM32F401xC) #elif defined(STM32F407xx) || defined(STM32F401xC)
#include <stm32f4xx.h> #include <stm32f4xx.h>
#include <stm32f4xx_hal.h> #include <stm32f4xx_hal.h>
#define USB_STM32F4 #define USB_STM32F4
#ifdef USB_HIGH_SPEED
#define USBG (USB_OTG_HS)
#else
#define USBG (USB_OTG_FS)
#endif #endif
#endif #define USB_LINESPEED_FULL_SPEED (0b11)
#define USB_LINESPEED_FULL_SPEED_ULPI (0b01)
#define USB_LINESPEED_HIGH_SPEED_ULPI (0b00)
#define USBD ((USB_OTG_DeviceTypeDef *) (((uint32_t)(USBG)) + ((uint32_t)(USB_OTG_DEVICE_BASE)))) #define USBD ((USB_OTG_DeviceTypeDef *) (((uint32_t)(USBG)) + ((uint32_t)(USB_OTG_DEVICE_BASE))))
#define USBINEP ((USB_OTG_INEndpointTypeDef *) (((uint32_t)(USBG)) + ((uint32_t)(USB_OTG_IN_ENDPOINT_BASE)))) #define USBINEP ((USB_OTG_INEndpointTypeDef *) (((uint32_t)(USBG)) + ((uint32_t)(USB_OTG_IN_ENDPOINT_BASE))))
@ -24,4 +29,19 @@
#define USBFIFO(ep) ((uint32_t *)(((uint32_t)(USBG)) + USB_OTG_FIFO_BASE + (USB_OTG_FIFO_SIZE) * (ep))) #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)) #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 */ #endif /* CORE_USB_USB_COMMON_DEFS */

File diff suppressed because it is too large Load Diff

334
driver/stm32/usb_drv.h Normal file
View File

@ -0,0 +1,334 @@
#ifndef STM32_USB_DRV
#define STM32_USB_DRV
#include "common_defs.h"
#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
// 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();
/**
* 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);
/**
* Flush specific TX FIFO.
*
* @param n index of the TX FIFO
*/
void usbdrv_flush_tx_fifo(uint8_t n);
/**
* Flush all RX FIFOs.
*/
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
*/
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);
/**
* Process data on the RX FIFO top.
*
* @param evt_data pointer to event data target area
*/
void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data);
/**
* Get the address table.
*
* @return immutable pointer to the address table
*/
const char *usbdrv_get_fifo_addr_table();
#endif /* STM32_USB_DRV */

View 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 */

View File

@ -0,0 +1,46 @@
#ifndef SRC_FLATUSB_CONFIG
#define SRC_FLATUSB_CONFIG
#include <stm32f4xx_hal.h>
#define USB_VBUSSENSE (0)
static inline void usbdrv_gpio_init() {
__HAL_RCC_GPIOA_CLK_ENABLE(); // turn ON GPIOA clocks
GPIO_InitTypeDef gpio_init;
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Alternate = GPIO_AF10_OTG_FS;
gpio_init.Pin = GPIO_PIN_11 | GPIO_PIN_12;
HAL_GPIO_Init(GPIOA, &gpio_init); // USB D-, D+
#if USB_VBUSSENSE
// FIXME
#endif
}
#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) HAL_NVIC_SetPriority((irq),(priority),0)
#define USB_IRQ_ENABLE(irq) HAL_NVIC_EnableIRQ((irq))
#define USB_IRQ_DISABLE(irq) HAL_NVIC_DisableIRQ((irq))
// define USBG
#define USBG (USB_OTG_FS)
#define USB_CLOCK_ENABLE() __HAL_RCC_USB_OTG_FS_CLK_ENABLE()
#define USB_INTERNAL (1)
#define USB_UPLI (0)
#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 */

View File

@ -0,0 +1,58 @@
#ifndef SRC_FLATUSB_CONFIG
#define SRC_FLATUSB_CONFIG
#include <stm32f4xx_hal.h>
#define USB_VBUSSENSE (0)
static inline void usbdrv_gpio_init() {
// turn on GPIO clocks
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
// initialize pins
GPIO_InitTypeDef gpio_init;
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Alternate = GPIO_AF10_OTG_HS;
gpio_init.Pin = GPIO_PIN_3 | GPIO_PIN_5; // D0, CK
HAL_GPIO_Init(GPIOA, &gpio_init);
// D1, D2, D7, D3, D4, D5, D6
gpio_init.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_5 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
HAL_GPIO_Init(GPIOB, &gpio_init);
// STP, DIR, NXT
gpio_init.Pin = GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3;
HAL_GPIO_Init(GPIOC, &gpio_init);
#if USB_VBUSSENSE
// FIXME
#endif
}
#define USB_IRQ_N OTG_HS_IRQn
#define USB_IRQ_HANDLER OTG_HS_IRQHandler
#define USB_IRQ_PRIORITY (8)
#define USB_IRQ_SET_PRIORITY(irq, priority) HAL_NVIC_SetPriority((irq),(priority),0)
#define USB_IRQ_ENABLE(irq) HAL_NVIC_EnableIRQ((irq))
#define USB_IRQ_DISABLE(irq) HAL_NVIC_DisableIRQ((irq))
#define USBG (USB_OTG_HS)
#define USB_CLOCK_ENABLE() __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); __HAL_RCC_USB_OTG_HS_ULPI_CLK_ENABLE()
#define USB_INTERNAL (0)
#define USB_UPLI (1)
#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 */

View File

@ -0,0 +1,82 @@
#ifndef SRC_FLATUSB_CONFIG
#define SRC_FLATUSB_CONFIG
#include <stm32h7xx_hal.h>
#define USB_VBUSSENSE (0)
// H723
#if 1
static inline void usbdrv_gpio_init() {
__HAL_RCC_GPIOA_CLK_ENABLE();
#if USB_VBUSSENSE
// FIXME
#endif
}
// H743
#elif 1
static inline void usbdrv_gpio_init() {
__HAL_RCC_GPIOA_CLK_ENABLE(); // turn ON GPIOA clocks
GPIO_InitTypeDef gpio_init;
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Alternate = GPIO_AF10_OTG_FS;
gpio_init.Pin = GPIO_PIN_11 | GPIO_PIN_12;
HAL_GPIO_Init(GPIOA, &gpio_init); // USB D-, D+
#if USB_VBUSSENSE
// FIXME
#endif
}
#endif
// H723
#if 1
#define USB_IRQ_N OTG_HS_IRQn
#define USB_IRQ_HANDLER OTG_HS_IRQHandler
// H743
#elif 1
#define USB_IRQ_N OTG_FS_IRQn
#define USB_IRQ_HANDLER OTG_FS_IRQHandler
#endif
#define USB_IRQ_PRIORITY (8)
#define USB_IRQ_SET_PRIORITY(irq, priority) HAL_NVIC_SetPriority((irq),(priority),0)
#define USB_IRQ_ENABLE(irq) HAL_NVIC_EnableIRQ((irq))
#define USB_IRQ_DISABLE(irq) HAL_NVIC_DisableIRQ((irq))
// H723
#if 1
#define USBG (USB_OTG_HS)
// H743
#elif 1
#define USBG (USB_OTG_FS)
#endif
#define USB_STM32H7_ENABLE_USB_VOLTAGE_DETECTOR() HAL_PWREx_EnableUSBVoltageDetector(); WAIT_FOR_nBIT_DELAY(PWR->CR3, PWR_CR3_USB33RDY, 1)
// STM32H723
#if 1
#define USB_CLOCK_ENABLE() USB_STM32H7_ENABLE_USB_VOLTAGE_DETECTOR(); __HAL_RCC_USB1_OTG_HS_CLK_ENABLE()
// STM32H743
#elif 1
#define USB_CLOCK_ENABLE() USB_STM32H7_ENABLE_USB_VOLTAGE_DETECTOR(); __HAL_RCC_USB2_OTG_FS_CLK_ENABLE()
#endif
#define USB_INTERNAL (1)
#define USB_UPLI (0)
#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 */

View File

@ -0,0 +1,38 @@
#ifndef SRC_FLATUSB_CONFIG
#define SRC_FLATUSB_CONFIG
#include <stm32h7xx_hal.h>
#define USB_VBUSSENSE (0)
static inline void usbdrv_gpio_init() {
// FIXME
#if USB_VBUSSENSE
// FIXME
#endif
}
#define USB_IRQ_N OTG_HS_IRQn
#define USB_IRQ_HANDLER OTG_HS_IRQHandler
#define USB_IRQ_PRIORITY (8)
#define USB_IRQ_SET_PRIORITY(irq, priority) HAL_NVIC_SetPriority((irq),(priority),0)
#define USB_IRQ_ENABLE(irq) HAL_NVIC_EnableIRQ((irq))
#define USB_IRQ_DISABLE(irq) HAL_NVIC_DisableIRQ((irq))
#define USBG (USB_OTG_HS)
#define USB_STM32H7_ENABLE_USB_VOLTAGE_DETECTOR() // FIXME
#define USB_CLOCK_ENABLE() // FIXME
#define USB_INTERNAL (0)
#define USB_UPLI (1)
#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 */

View File

@ -3,12 +3,12 @@
"subclass": "0x00", "subclass": "0x00",
"protocol_code": "0x00", "protocol_code": "0x00",
"max_ep0_packet_size": 64, "max_ep0_packet_size": 64,
"vendor_id": "0x0925", "vendor_id": "0x0000",
"product_id": "0x9050", "product_id": "0x0001",
"device_release_bcd": "0x0100", "device_release_bcd": "0x0100",
"vendor_string": "Epagris", "vendor_string": "<Vendor>",
"product_string": "Testdevice", "product_string": "<Product name>",
"serial_number": "1552", "serial_number": "0000000000000",
"configurations": [ "configurations": [
{ {
"id": 1, "id": 1,

View File

@ -3,12 +3,12 @@
"subclass": "0x00", "subclass": "0x00",
"protocol_code": "0x00", "protocol_code": "0x00",
"max_ep0_packet_size": 64, "max_ep0_packet_size": 64,
"vendor_id": "0x0925", "vendor_id": "0x0000",
"product_id": "0x9050", "product_id": "0x0001",
"device_release_bcd": "0x0100", "device_release_bcd": "0x0100",
"vendor_string": "Epagris", "vendor_string": "<Vendor>",
"product_string": "Testdevice", "product_string": "<Product name>",
"serial_number": "1552", "serial_number": "0000000000000",
"configurations": [ "configurations": [
{ {
"id": 1, "id": 1,

View File

@ -3,12 +3,12 @@
"subclass": "0x00", "subclass": "0x00",
"protocol_code": "0x00", "protocol_code": "0x00",
"max_ep0_packet_size": 64, "max_ep0_packet_size": 64,
"vendor_id": "0x0925", "vendor_id": "0x0000",
"product_id": "0x9050", "product_id": "0x0001",
"device_release_bcd": "0x0100", "device_release_bcd": "0x0100",
"vendor_string": "Epagris", "vendor_string": "<Vendor>",
"product_string": "Testdevice", "product_string": "<Product name>",
"serial_number": "1552", "serial_number": "0000000000000",
"configurations": [ "configurations": [
{ {
"id": 1, "id": 1,

163
usb.c
View File

@ -2,78 +2,91 @@
#include <memory.h> #include <memory.h>
#include "usb_callback_event.h"
#include "usb_common.h" #include "usb_common.h"
#include "usb_core_types.h" #include "usb_driver_common.h"
// #include "utils/gen_queue.h" #include FLATUSB_DESCRIPTOR_HEADER
#include "desc/usb_desc.h"
#include "usb_driver.h"
// --------------- // ---------------
/** \cond false */
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))
/** \endcond */
#ifdef USBDBGMSG #define USBDRV_ARM_IN_ZLP(ep) drv->arm_IN((ep), NULL, 0) ///< Arm a ZLP IN message
#include "../cli/stdio_uart.h"
#define USBMSG(...) MSG(__VA_ARGS__)
#else
#define USBMSG(...)
#endif
// --------------- // ---------------
#ifndef USB_HIGH_SPEED static uint8_t tx_assembly_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; ///< Buffer for assembling packets
#define USB_RX_BUF_SIZE (USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS) static Usb_SetupTransferState stups; ///< Setup transfer state.
#else static UsbDrv_DrvIntf *drv; ///< Mutable pointer to the driver interface
#define USB_RX_BUF_SIZE (USB_MAX_HS_PCKT_SIZE_NON_ISOCHRONOUS)
#endif
static uint8_t tx_assembly_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; // buffer for assembling packets
// ---------------
// // state of changeing address
// typedef enum {
// USB_ADDRCHG_IDLE = 0, // idle state
// USB_ADDRCHG_NEW_ADDR_RECVD, // new address received
// } USB_AddressChangeState;
// ---------------- // ----------------
__weak void usb_event_callback(USB_CallbackEvent *cbevt) { /**
* @fn void usb_event_callback(USB_CallbackEvent *cbevt)
* Prototype for the USB event callback.
*
* @param cbevt pointer to the event data
*/
__attribute__((weak)) void usb_event_callback(Usb_CallbackEvent *cbevt) {
return; return;
} }
// ---------------- // ----------------
// prepare descriptor for transmission, return with final transmission size /**
// static uint8_t usb_prepare_descriptor(const uint8_t *desc, uint8_t desc_size, uint8_t req_size) { * Callback function for bus issued Reset is done notification.
// uint8_t sz = (uint8_t)(MIN(req_size, desc_size)); // determine transmission size */
// memcpy(gs.tx_assembly_buf, desc, sz); // copy that portion to the assembly buffer static void usbcore_rst_notify() {
// USB_DescHdr *hdr = (USB_DescHdr *)gs.tx_assembly_buf; // obtain header usbcore_reset();
// hdr->bLength = MIN(sz, hdr->bLength); // write transmit size
// return sz;
// }
// #define USB_SETUP_STREAM_BUFFER (72)
typedef struct {
USB_SetupRequest setup_req; // setup request
uint8_t next_stage; // next expected stage
bool out_complete; // signals if transfer's OUT direction is complete, no later data reception is expected
} USB_SetupTransferState;
static USB_SetupTransferState stups;
void usbcore_init() {
// init state
memset(&stups, 0, sizeof(USB_SetupTransferState));
} }
/**
* Callback for Enumeration Done notification.
*/
static void usbcore_enum_notify(uint8_t spd) {
(void)spd;
}
void usbcore_init(UsbDrv_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_reset() {
// init state
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.
*
* @param data pointer to the SETUP packet
* @param size size of the packet
* @param stage stage of the SETUP transaction
*/
void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stage) { 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"); // MSG("STUP: %u, %s\n", size, stage == UST_SETUP ? "SETUP" : "DATA");
@ -113,7 +126,7 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
} }
// expect status transaction to follow the data phase // 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 // only continue processing if the transaction has concluded
if (!stups.out_complete) { if (!stups.out_complete) {
@ -156,37 +169,42 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
break; break;
// Descriptors not implemented // Descriptors not implemented
default: 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; break;
} }
// arm data transmission // arm data transmission
usbdrv_arm_IN_endpoint(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;
} }
case UREQ_SetAddress: { // SET ADDRESS case UREQ_SetAddress: { // SET ADDRESS
uint8_t addr = stups.setup_req.wValue & 0x7F; uint8_t addr = stups.setup_req.wValue & 0x7F;
usbdrv_set_address(addr); drv->set_address(addr);
usbdrv_arm_OUT_endpoint(0, 64); // prepare for data OUT stage drv->arm_OUT(0, 64); // prepare for data OUT stage
USBDRV_ARM_IN_ZLP(0); // ZLP IN USBDRV_ARM_IN_ZLP(0); // ZLP IN
break; break;
} }
case UREQ_SetConfiguration: // SET CONFIGURATION case UREQ_SetConfiguration: // SET CONFIGURATION
{ {
uint8_t config_id = stups.setup_req.wValue & 0xFF; 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); USBDRV_ARM_IN_ZLP(0);
break; break;
} }
case UREQ_SetInterface: // SET INTERFACE case UREQ_SetInterface: // SET INTERFACE
{ {
// TODO: set configuration // TODO: set interface
USBDRV_ARM_IN_ZLP(0); USBDRV_ARM_IN_ZLP(0);
break; break;
} }
default: { // UNKNOWN REQUEST, pass processing to user application default: { // UNKNOWN REQUEST, pass processing to user application
USB_CallbackEvent cbevt = {0}; Usb_CallbackEvent cbevt = {0};
cbevt.type = USB_CBEVT_UNKNOWN_REQ; cbevt.type = USB_CBEVT_UNKNOWN_REQ;
cbevt.setup_request = &stups.setup_req; cbevt.setup_request = &stups.setup_req;
cbevt.data = (const uint8_t *)data; cbevt.data = (const uint8_t *)data;
@ -197,7 +215,7 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
usb_event_callback(&cbevt); usb_event_callback(&cbevt);
if (cbevt.reply_valid) { 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; break;
} }
@ -206,11 +224,16 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
stups.out_complete = false; stups.out_complete = false;
} }
void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) { /**
USB_CallbackEvent cbevt = {0}; // allocate and clear structure... * Process the non-SETUP packets.
*
* @param cbcpd pointer to callback compound
*/
void usbcore_process_nonsetup_event(UsbDrv_CallbackCompound *cbcpd) {
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;
@ -242,21 +265,23 @@ void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) {
// for OUT events, check the autoarm flag // for OUT events, check the autoarm flag
if (cbevt.dir == USB_OUT) { if (cbevt.dir == USB_OUT) {
if (cbevt.arm_out_endpoint) { 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) { void usbcore_register_IN_callback(uint8_t ep, UsbDrv_IN_cb cb) {
usbdrv_enable_endpoint_interrupt(ep, dir, true); bool en = cb != NULL;
drv->en_ep_irq(ep, USB_IN, en);
drv->reg_IN_cb(ep, cb);
} }
uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size) { uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size) {
usbdrv_enable_endpoint_interrupt(ep, USB_IN, true); drv->en_ep_irq(ep, USB_IN, true);
return usbdrv_arm_IN_endpoint(ep, data, size); return drv->arm_IN(ep, data, size);
} }
uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t 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);
} }

70
usb.h
View File

@ -1,11 +1,71 @@
#ifndef CORE_USB_USB #ifndef CORE_USB_USB
#define CORE_USB_USB #define CORE_USB_USB
#include "usb_callback_event.h" #include <stdint.h>
void usbcore_init(); // initialize USB core #include "usb_device_types.h"
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 #include "usb_driver_common.h"
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 // --------------
/**
* Setup transfer state.
*/
typedef struct {
USB_SetupRequest setup_req; ///< Setup request
uint8_t next_stage; ///< Next expected stage
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;
// --------------
/** \cond 0 */
#ifndef USB_RX_BUF_SIZE
#define USB_RX_BUF_SIZE (512) ///< Receive buffer size FIXME
#endif
/** \endcond */
// --------------
/**
* Initialize USB core.
*
* @param drvIntf pointer to a mutable driver interface object
*/
void usbcore_init(UsbDrv_DrvIntf *drvIntf);
/**
* Reset USB core.
*/
void usbcore_reset();
/**
* Write data to an IN Endpoint.
*
* @param ep index of the Endpoint
* @param data pointer to data to be written
* @param size data length
* @return number of bytes written
*/
uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size);
/**
* Expect ingress data on an Endpoint.
*
* @param ep index of the Endpoint
* @param size expected reception length
* @return message length that the endpoint can support during next reception
*/
uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size);
/**
* Register a callback for IN transmission completion.
*
* @param ep index of the Endpoint
* @param cb callback function
*/
void usbcore_register_IN_callback(uint8_t ep, UsbDrv_IN_cb cb);
#endif /* CORE_USB_USB */ #endif /* CORE_USB_USB */

View File

@ -5,30 +5,39 @@
#include "usb_device_types.h" #include "usb_device_types.h"
/**
* Callback event type.
*/
typedef enum { typedef enum {
USB_CBEVT_OUT, // OUT event USB_CBEVT_OUT, ///< OUT event
USB_CBEVT_IN, // IN event USB_CBEVT_IN, ///< IN event
USB_CBEVT_UNKNOWN_REQ // unknown request on a control endpoint USB_CBEVT_UNKNOWN_REQ ///< unknown request on a control endpoint
} USB_CallbackEventType; } Usb_CallbackEventType;
/**
* Callback event subtype.
*/
typedef enum { typedef enum {
USB_CBEVST_IN_NONE = 0, // no subtype USB_CBEVST_IN_NONE = 0, ///< No subtype
USB_CBEVST_IN_REQ // IN request was received but could not be responded USB_CBEVST_IN_REQ ///< IN request was received but could not be responded
} USB_CallbackEventSubType; } Usb_CallbackEventSubType;
/**
* USB callback event.
*/
typedef struct { typedef struct {
uint8_t type : 4; // event type uint8_t type : 4; ///< Event type
uint8_t subtype : 4; // event subtype uint8_t subtype : 4; ///< Event subtype
uint8_t ep; // endpoint number uint8_t ep; ///< Endpoint number
uint8_t dir; // endpoint direction uint8_t dir; ///< Endpoint direction
uint8_t size; // size of accompaining data uint8_t size; ///< Size of accompaining data
const uint8_t * data; // event data const uint8_t *data; ///< Event data
const USB_SetupRequest * setup_request; // corresponding setup request (if exists) const USB_SetupRequest *setup_request; ///< Corresponding setup request (if exists)
bool reply_valid; // reply message is valid bool reply_valid; ///< Reply message is valid
const uint8_t * reply_data; // reply data const uint8_t *reply_data; ///< Reply data
uint8_t reply_size; // reply size uint8_t reply_size; ///< Reply size
bool arm_out_endpoint; // automatically arm OUT endpoint at the end of the current transmission bool arm_out_endpoint; ///< Automatically arm OUT endpoint at the end of the current transmission
} USB_CallbackEvent; } Usb_CallbackEvent;
#endif /* CORE_USB_USB_CALLBACK_EVENT */ #endif /* CORE_USB_USB_CALLBACK_EVENT */

View File

@ -2,17 +2,20 @@
#define CORE_USB_USB_COMMON #define CORE_USB_USB_COMMON
#include "usb_common_types.h" #include "usb_common_types.h"
#include "usb_common_defs.h"
#include "usb_device_types.h" #include "usb_device_types.h"
#define READ_FIELD(r,f) (((r) & (f##_Msk)) >> (f##_Pos)) #define USB_DELAY(t) for (uint64_t i = 0; i < (t) * 10000; i++) { asm("nop"); } ///< Create a delay that does not relay on the processor ticks.
#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_nBIT(r,b) while (!((r) & (b))) {}
#define DWORD_ALIGN __attribute__((aligned(4))) #define READ_FIELD(r,f) (((r) & (f##_Msk)) >> (f##_Pos)) ///< Read a named field of a register
#define WRITE_FIELD(r,f,v) ((r) = ((r) & ~(f##_Msk)) | (v << (f##_Pos))) ///< Write a name field of a register
#define WAIT_FOR_BIT(r,b) while ((r) & (b)) {} ///< Wait for a bit to clear
#define WAIT_FOR_BIT_DELAY(r,b,d) while ((r) & (b)) { USB_DELAY((d)); } ///< Wait for a bit to clear with injected delay
#define WAIT_FOR_nBIT(r,b) while (!((r) & (b))) {} ///< Wait for a bit to set
#define WAIT_FOR_nBIT_DELAY(r,b,d) while (!((r) & (b))) { USB_DELAY((d)); } ///< Wait for a bit to set with injected delay
#define CEILDIV4(x) (((x) + 3) >> 2) #define DWORD_ALIGN __attribute__((aligned(4))) ///< Declare a 32-bit aligned memory area
#define CEIL4(x) (((x) + 3) & (~0b11))
#define CEILDIV4(x) (((x) + 3) >> 2) ///< Integer division by four after rounding up to the nearest multiple of 4
#define CEIL4(x) (((x) + 3) & (~0b11)) ///< Round up to the closest multiple of 4
#endif /* CORE_USB_USB_COMMON */ #endif /* CORE_USB_USB_COMMON */

View File

@ -4,6 +4,9 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
/**
* A byte-sized boolean.
*/
typedef uint8_t bool8_t; typedef uint8_t bool8_t;
#endif /* CORE_USB_USB_COMMON_TYPES */ #endif /* CORE_USB_USB_COMMON_TYPES */

View File

@ -1,16 +0,0 @@
#ifndef CORE_USB_USB_CORE_TYPES
#define CORE_USB_USB_CORE_TYPES
#include "usb_common_types.h"
/* 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;
#endif /* CORE_USB_USB_CORE_TYPES */

8
usb_descriptor_common.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef FLATUSB_USB_DESCRIPTOR_COMMON
#define FLATUSB_USB_DESCRIPTOR_COMMON
#include <stdint.h>
#include "usb_device_types.h"
#endif /* FLATUSB_USB_DESCRIPTOR_COMMON */

View File

@ -9,109 +9,120 @@
/* ------ USB DESCRIPTORS ------- */ /* ------ USB DESCRIPTORS ------- */
// USB Descriptor type codes /**
* USB Descriptor type codes.
*/
typedef enum { typedef enum {
UD_Device = 1, UD_Device = 1, ///< Device Descriptor
UD_Configuration = 2, UD_Configuration = 2, ///< Configuration Descriptor
UD_String = 3, UD_String = 3, ///< String Descriptor
UD_Interface = 4, UD_Interface = 4, ///< Interface Descriptor
UD_Endpoint = 5, UD_Endpoint = 5, ///< Endpoint Descriptor
UD_DeviceQualifier = 6, UD_DeviceQualifier = 6, ///< DeviceQualifier Descriptor
UD_OtherSpeedConfiguration = 7 UD_OtherSpeedConfiguration = 7 ///< OtherSpeedConfiguration Descriptor
// NOT FULL! // NOT FULL!
} USB_DescType; } USB_DescType;
// USB descriptor header /**
* USB descriptor header
*/
#define USB_DESC_HEADER \ #define USB_DESC_HEADER \
uint8_t bLength; /* length in bytes */ \ uint8_t bLength; /* length in bytes */ \
uint8_t bDescriptorType; /* descriptor type */ uint8_t bDescriptorType; /* descriptor type */
#define USB_DESC_HEADER_SIZE (2) // USB descriptor header size #define USB_DESC_HEADER_SIZE (2) ///< USB descriptor header size
// USB Descriptor header
/**
* USB Descriptor header
*/
typedef struct { typedef struct {
USB_DESC_HEADER USB_DESC_HEADER
} USB_DescHdr; } USB_DescHdr;
// USB Device descriptor /**
* USB Device descriptor
*/
typedef struct { typedef struct {
USB_DESC_HEADER USB_DESC_HEADER
uint16_t bcdUSB; // USB specification release number (BCD) uint16_t bcdUSB; ///< USB specification release number (BCD)
uint8_t bDeviceClass; // Class code uint8_t bDeviceClass; ///< Class code
uint8_t bDeviceSubclass; // Subclass code uint8_t bDeviceSubclass; ///< Subclass code
uint8_t bDeviceProtocol; // Protocol code uint8_t bDeviceProtocol; ///< Protocol code
uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0 uint8_t bMaxPacketSize0; ///< Maximum packet size for endpoint 0
uint16_t idVendor; // Vendor ID uint16_t idVendor; ///< Vendor ID
uint16_t idProduct; // Product ID uint16_t idProduct; ///< Product ID
uint16_t bcdDevice; // Device release number (BCD) uint16_t bcdDevice; ///< Device release number (BCD)
uint8_t iManufacturer; // Index of string descriptor for manufacturer uint8_t iManufacturer; ///< Index of string descriptor for manufacturer
uint8_t iProduct; // Index of string descriptor for the product uint8_t iProduct; ///< Index of string descriptor for the product
uint8_t iSerialNumber; // Index of string descriptor for the serial number uint8_t iSerialNumber; ///< Index of string descriptor for the serial number
uint8_t bNumConfiguration; // Number of possible configurations uint8_t bNumConfiguration; ///< Number of possible configurations
} USB_DeviceDesc; } USB_DeviceDesc;
// USB Device Qualifier descriptor /**
* USB Device Qualifier descriptor
*/
typedef struct { typedef struct {
USB_DESC_HEADER USB_DESC_HEADER
uint16_t bcdUSB; // USB specification release number (BCD) uint16_t bcdUSB; ///< USB specification release number (BCD)
uint8_t bDeviceClass; // Class code uint8_t bDeviceClass; ///< Class code
uint8_t bDeviceSubclass; // Subclass code uint8_t bDeviceSubclass; ///< Subclass code
uint8_t bDeviceProtocol; // Protocol code uint8_t bDeviceProtocol; ///< Protocol code
uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0 uint8_t bMaxPacketSize0; ///< Maximum packet size for endpoint 0
uint8_t bNumConfiguration; // Number of possible configurations uint8_t bNumConfiguration; ///< Number of possible configurations
} __attribute__((packed)) USB_DeviceQualifierDesc; } __attribute__((packed)) USB_DeviceQualifierDesc;
#define USB_CONFIG_ATTR_SELF_POWERED (1 << 6) #define USB_CONFIG_ATTR_SELF_POWERED (1 << 6) ///< Device is self-powered
#define USB_CONFIG_ATTR_REMOTE_WKUP (1 << 5) #define USB_CONFIG_ATTR_REMOTE_WKUP (1 << 5) ///< Device is intended to wake up the host
#define USB_CONFIG_ATTR_USB1_1_FLAG (1 << 7) #define USB_CONFIG_ATTR_USB1_1_FLAG (1 << 7) ///< It's a USB 1.1 or newer device
// USB Configuration descriptor
/**
* USB Configuration descriptor
*/
typedef struct { typedef struct {
USB_DESC_HEADER USB_DESC_HEADER
uint16_t wTotalLength; // The number of bytes in the configuration descriptor and all of its subordinate descriptors uint16_t wTotalLength; ///< The number of bytes in the configuration descriptor and all of its subordinate descriptors
uint8_t bNumInterfaces; // Number of interfaces in the configuration uint8_t bNumInterfaces; ///< Number of interfaces in the configuration
uint8_t bConfigrationValue; // Identifier for Set Configuration and Get Configuration Requests uint8_t bConfigrationValue; ///< Identifier for Set Configuration and Get Configuration Requests
uint8_t iConfiguration; // Index of string descriptor for the configuration uint8_t iConfiguration; ///< Index of string descriptor for the configuration
uint8_t bmAttributes; // Self/bus power and remote wakeup settings uint8_t bmAttributes; ///< Self/bus power and remote wakeup settings
uint8_t bMaxPower; // Bus power required in units of 2 mA uint8_t bMaxPower; ///< Bus power required in units of 2 mA
} __attribute__((packed)) USB_ConfigurationDesc; } __attribute__((packed)) USB_ConfigurationDesc;
// USB Interface descriptor /**
* USB Interface descriptor
*/
typedef struct { typedef struct {
USB_DESC_HEADER USB_DESC_HEADER
uint8_t bInterfaceNumber; // Number identifying this interface uint8_t bInterfaceNumber; ///< Number identifying this interface
uint8_t bAlternateSetting; // A number that identifies a descriptor with alternate settings for this bInterfaceNumber uint8_t bAlternateSetting; ///< A number that identifies a descriptor with alternate settings for this bInterfaceNumber
uint8_t bNumEndpoints; // Number of endpoints supported not counting endpoint zero uint8_t bNumEndpoints; ///< Number of endpoints supported not counting endpoint zero
uint8_t bInterfaceClass; // Class code uint8_t bInterfaceClass; ///< Class code
uint8_t bInterfaceSubclass; // Subclass code uint8_t bInterfaceSubclass; ///< Subclass code
uint8_t bInterfaceProtocol; // Protocol code uint8_t bInterfaceProtocol; ///< Protocol code
uint8_t iInterface; // Index of string descriptor for the interface uint8_t iInterface; ///< Index of string descriptor for the interface
} __attribute__((packed)) USB_InterfaceDesc; } __attribute__((packed)) USB_InterfaceDesc;
// USB Transfer type /**
* USB Transfer type
*/
typedef enum { typedef enum {
UT_Control = 0b00, UT_Control = 0b00, ///< Control
UT_Isochronous = 0b01, UT_Isochronous = 0b01, ///< Isochronous
UT_Bulk = 0b10, UT_Bulk = 0b10, ///< Bulk
UT_Interrupt = 0b11 UT_Interrupt = 0b11 ///< Interrupt
} USB_TransferType; } USB_TransferType;
// USB Endpoint descriptor
/**
* USB Endpoint descriptor
*/
typedef struct { typedef struct {
USB_DESC_HEADER USB_DESC_HEADER
uint8_t bEndpointAddress; // Endpoint number and direction uint8_t bEndpointAddress; ///< Endpoint number and direction
uint8_t bmAttributes; // Transfer type and supplementary information uint8_t bmAttributes; ///< Transfer type and supplementary information
uint16_t wMaxPacketSize; // Maximum packet size supported uint16_t wMaxPacketSize; ///< Maximum packet size supported
uint8_t bInterval; // Service interval or NAK rate uint8_t bInterval; ///< Service interval or NAK rate
} __attribute__((packed)) USB_EndpointDesc; } __attribute__((packed)) USB_EndpointDesc;
/* struct { /* struct {
@ -119,21 +130,25 @@ typedef struct {
uint8_t dir; // direction uint8_t dir; // direction
} bEndpointAddress; // Endpoint number and direction*/ } bEndpointAddress; // Endpoint number and direction*/
// USB String descriptor
/**
* USB String descriptor
*/
typedef struct { typedef struct {
USB_DESC_HEADER USB_DESC_HEADER
} USB_StringDesc; } USB_StringDesc;
// USB endpoint direction /**
* USB endpoint direction
*/
typedef enum { typedef enum {
USB_OUT = 0, USB_OUT = 0, ///< OUT
USB_IN = 1 USB_IN = 1 ///< IN
} USB_EndpointDir; } USB_EndpointDir;
// USB PIDs /**
* USB PIDs
*/
typedef enum { typedef enum {
PID_EXT = 0, PID_EXT = 0,
PID_OUT = 1, PID_OUT = 1,
@ -153,16 +168,18 @@ typedef enum {
PID_MDATA = 15, PID_MDATA = 15,
} USB_PID; } USB_PID;
// USB Request types /**
* USB Request types
*/
typedef enum { typedef enum {
UR_Standard = 0, UR_Standard = 0,
UR_VendorSpec = 1, UR_VendorSpec = 1,
UR_ReqDefVendorSpec = 2 UR_ReqDefVendorSpec = 2
} USB_RequestType; } USB_RequestType;
// USB Recipients /**
* USB Recipients
*/
typedef enum { typedef enum {
UREC_Device = 0, UREC_Device = 0,
UREC_SpecificInterface = 1, UREC_SpecificInterface = 1,
@ -170,8 +187,9 @@ typedef enum {
UREC_OtherElement = 3 UREC_OtherElement = 3
} USB_Recipient; } USB_Recipient;
// USB Request types /**
* USB Request types
*/
typedef enum { typedef enum {
UREQ_GetStatus = 0x00, UREQ_GetStatus = 0x00,
UREQ_ClearFeature = 0x01, UREQ_ClearFeature = 0x01,
@ -188,23 +206,24 @@ typedef enum {
UREQ_SetIsochronousDelay = 0x31 UREQ_SetIsochronousDelay = 0x31
} USB_RequestCode; } USB_RequestCode;
// setup request structure /**
* Setup request structure
*/
typedef struct { typedef struct {
union { union {
uint8_t bmRequestType; uint8_t bmRequestType; ///< Request type
struct { struct {
uint8_t recipient : 5; uint8_t recipient : 5; ///< Recipient
uint8_t type : 2; uint8_t type : 2; ///< Type
uint8_t dir : 1; uint8_t dir : 1; ///< Direction
} fields; } fields;
} bmRequestType; // request type } bmRequestType; ///< Request type
uint8_t bRequest; // request uint8_t bRequest; ///< Request
uint16_t wValue; // ... uint16_t wValue; ///< Value
uint16_t wIndex; // ... uint16_t wIndex; ///< Index
uint16_t wLength; // number of bytes in the data stage uint16_t wLength; ///< Number of bytes in the data stage
} USB_SetupRequest; } USB_SetupRequest;
#endif /* CORE_USB_USB_DEVICE_TYPES */ #endif /* CORE_USB_USB_DEVICE_TYPES */

View File

@ -1,170 +0,0 @@
#ifndef CORE_USB_USB_DRIVER
#define CORE_USB_USB_DRIVER
// #include "utils/gen_queue.h"
#include "usb_common_types.h"
// #define USBDBGMSG
#define USB_NUM_OF_ENDPOINTS (4) // FIXME: this is module-dependend
// 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
// 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;
// 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;
// ------------
void usbdrv_gpio_init(); // USB pin low level, early peripheral initialization
void usbdrv_init_global_state(); // initialize global state
void usbdrv_periph_init(); // initialize USB peripheral
void usbdrv_initial_ep0_setup(); // create an initial setup for EP0 in both directions
void usbdrv_power_and_connect(bool en); // connect to or disconnect from the bus
void usbdrv_init(); // initialize USB subsystem
void usbdrv_reset(); // reset USB subsystem
void usbdrv_preload_endpoint_config(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg); // preload usb endpoint config
void usbdrv_clear_endpoint_config(); // clear endpoint config
void usbdrv_apply_endpoint_config(); // apply preloaded endpoint configuration
void usbdrv_build_fifo(); // build FIFO (compute addresses)
const char *usbdrv_get_fifo_addr_table(); // get FIFO address table
void usbdrv_fetch_endpoint_configuration(uint8_t config_index); // fetch endpoint configuration from descriptor dump
void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg); // configure USB endpoint
void usbdrv_deconfigure_endpoint(uint8_t ep, uint8_t dir); // deconfigure USB endpoint
void usbdrv_stall_endpoint(uint8_t ep, uint8_t dir, bool stall); // stall endpoint
void usbdrv_set_global_NAK(uint8_t dir, bool en); // set global NAK
void usbdrv_flush_tx_fifo(uint8_t n); // flush specific or all Tx FIFOs
void usbdrv_flush_rx_fifo(); // flush the Rx FIFO
void usbdrv_set_rx_fifo_size(uint16_t size); // set Rx FIFO size
void usbdrv_fetch_received_data(uint8_t ep, uint16_t len); // fetch received data from RX FIFO to receive buffer
void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data); // see what's on top of Rx FIFO
uint32_t usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len); // write data to specific endpoint FIFO
#define USBDRV_ARM_IN_ZLP(ep) usbdrv_arm_IN_endpoint((ep), NULL, 0)
uint32_t usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size); // arm OUT endpoint, returned with the actual armed size (capped by the max packet size on that endpoint)
void usbdrv_autoarm_OUT_endpoint(uint8_t ep); // automatically re-arm OUT endpoint at the and of the current OUT transfer
void usbdrv_enable_endpoint_interrupt(uint8_t ep, uint8_t dir, bool en); // enable/disable endpoint interrupt signaling
bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir); // get endpoint interrupt flag
void usbdrv_set_address(uint8_t addr); // set device address
void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data); // process USB event
void usbdrv_push_event(uint32_t evt_code, USBDRV_EventData *evt_data); // push event onto the event queue
void usbdrv_periodic_processing(); // call this to process incoming events
#endif /* CORE_USB_USB_DRIVER */

68
usb_driver_common.h Normal file
View File

@ -0,0 +1,68 @@
#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);
/**
* USB Driver interface.
*/
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
} UsbDrv_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;
/**
* Control transfer stages.
*/
typedef enum { UST_SETUP, ///< Setup stage
UST_DATA ///< Data stage
} UsbDrv_ControlTfStage;
#endif /* FLATUSB_USB_DRIVER_INTERFACE */