Compare commits
No commits in common. "master" and "usb_rework" have entirely different histories.
master
...
usb_rework
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
||||
docs/html/
|
@ -10,8 +10,8 @@ endif()
|
||||
set(FLATUSB_CLASSES_SRC "")
|
||||
if ("CDC_ACM" IN_LIST FLATUSB_CLASSES)
|
||||
list(APPEND FLATUSB_CLASSES_SRC
|
||||
class/acm.c
|
||||
class/acm.h
|
||||
class/cdc.c
|
||||
class/cdc.h
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -73,9 +73,3 @@ if (FLATUSB_DESC_JSON AND FLATUSB_DESC_DIR)
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
flatUSB-docs
|
||||
COMMAND doxygen
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
198
class/acm.c
198
class/acm.c
@ -1,198 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @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
128
class/acm.h
@ -1,128 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @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 */
|
180
class/cdc.c
Normal file
180
class/cdc.c
Normal file
@ -0,0 +1,180 @@
|
||||
#include "cdc.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#include "../usb_device_types.h"
|
||||
|
||||
#include "../usb.h"
|
||||
|
||||
#include <blocking_io/blocking_fifo.h>
|
||||
|
||||
#include <cmsis_gcc.h>
|
||||
#include <string.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));
|
||||
|
||||
// set Control Line State to something invalid
|
||||
cdcs.control_line_state.D = USB_CDC_INVALID_CONTROL_LINE_STATE;
|
||||
|
||||
// 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);
|
||||
|
||||
// initialize an all-0 interrupt
|
||||
cdcs.interrupt_data = 0;
|
||||
cdcs.interrupt_pending = true;
|
||||
|
||||
// communication parameters have not been set
|
||||
cdcs.commInit = false;
|
||||
|
||||
// from now on CDC module is considered initialized
|
||||
cdcs.moduleInit = true;
|
||||
}
|
||||
|
||||
// static uint8_t replyBuf[sizeof(USB_Cdc_LineCodingStruct)];
|
||||
|
||||
// static void usbcore_setup_cplt_cb(uint8_t in) {
|
||||
// usbcore_wake_up_endpoint(cdcs.ep_assignments.data_ep, USB_IN);
|
||||
// }
|
||||
|
||||
static void usb_cdc_review_comm_init() {
|
||||
// check if line coding was initialized
|
||||
USB_Cdc_LineCodingStruct *lc = &cdcs.line_coding;
|
||||
bool lcOk = (lc->dwDTERate != 0) && (lc->bDataBits != 0);
|
||||
|
||||
// check if control line state was initialized
|
||||
bool clsOk = cdcs.control_line_state.D != USB_CDC_INVALID_CONTROL_LINE_STATE;
|
||||
|
||||
// combine the above criteria
|
||||
cdcs.commInit = lcOk && clsOk;
|
||||
|
||||
// // wake up endpoint if initialized
|
||||
// if (cdcs.commInit) {
|
||||
// usbcore_register_IN_callback(0, usbcore_setup_cplt_cb);
|
||||
// }
|
||||
}
|
||||
|
||||
int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) {
|
||||
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_CDC_SET_LINE_CODING: // set line coding
|
||||
memcpy(&cdcs.line_coding, cbevt->data, sizeof(USB_Cdc_LineCodingStruct));
|
||||
usb_cdc_review_comm_init(); // review the communcation initialization state
|
||||
// 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);
|
||||
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
|
||||
cdcs.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
|
||||
// MSG("%u\n", cdcs.control_line_state.D);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
cbevt->reply_valid = false; // this event is not processed by or not related to the CDC ACM module
|
||||
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
|
||||
if (cdcs.interrupt_pending) {
|
||||
usbcore_schedule_transmission(cdcs.ep_assignments.control_ep, (const uint8_t *)&(cdcs.interrupt_data), sizeof(uint16_t)); // send ZLP
|
||||
cdcs.interrupt_pending = false;
|
||||
}
|
||||
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
|
||||
if (cdcs.commInit) {
|
||||
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.moduleInit) {
|
||||
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;
|
||||
}
|
78
class/cdc.h
Normal file
78
class/cdc.h
Normal file
@ -0,0 +1,78 @@
|
||||
#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
|
||||
uint16_t interrupt_data; // data sent though the next transfer on the notification element
|
||||
bool interrupt_pending; // interrupt data is valid and should be send in the next cycle
|
||||
bool moduleInit; // CDC module is initialized
|
||||
bool commInit; // communication is initialized
|
||||
} USB_CdcState;
|
||||
|
||||
// ----------------
|
||||
|
||||
#define USB_CDC_INVALID_CONTROL_LINE_STATE (0xFFFF)
|
||||
|
||||
#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 */
|
@ -73,7 +73,7 @@ USB_EemEvent usb_eem_pop_event() {
|
||||
return evt;
|
||||
}
|
||||
|
||||
int usb_eem_process_and_return(Usb_CallbackEvent *cbevt) {
|
||||
int usb_eem_process_and_return(USB_CallbackEvent *cbevt) {
|
||||
switch (cbevt->type) {
|
||||
case USB_CBEVT_UNKNOWN_REQ:
|
||||
break;
|
||||
|
@ -24,6 +24,7 @@ usb_target_dir = argv[2]
|
||||
print("Input JSON: ", usb_config_file_name)
|
||||
print("Target directory: ", usb_target_dir)
|
||||
|
||||
#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)
|
||||
@ -41,5 +42,29 @@ if "misc" in usb_config:
|
||||
cfggen = ConfigGenerator(usb_config)
|
||||
cfggen.generate(usb_target_dir)
|
||||
|
||||
# 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())
|
||||
|
||||
# print complete message
|
||||
print("Descriptor generation complete!")
|
21
docs/LICENSE
21
docs/LICENSE
@ -1,21 +0,0 @@
|
||||
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.
|
@ -1,157 +0,0 @@
|
||||
/**
|
||||
|
||||
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);
|
@ -1,85 +0,0 @@
|
||||
/**
|
||||
|
||||
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)
|
@ -1,91 +0,0 @@
|
||||
/**
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
|
||||
/**
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/**
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/**
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
<!-- 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"> $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 -->
|
@ -1,3 +0,0 @@
|
||||
body {
|
||||
--top-height: 160px
|
||||
}
|
@ -1,655 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,251 +0,0 @@
|
||||
#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 */
|
16
driver/stm32/usb_core_types.h
Normal file
16
driver/stm32/usb_core_types.h
Normal file
@ -0,0 +1,16 @@
|
||||
#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 */
|
@ -3,6 +3,8 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "stm32f4xx_hal.h"
|
||||
|
||||
#include "flatUSB_config.h"
|
||||
#include "usb_driver_common.h"
|
||||
|
||||
@ -13,16 +15,14 @@
|
||||
|
||||
// ------------------------
|
||||
|
||||
#define USB_RX_BUF_SIZE (512) ///< Receive buffer size
|
||||
#define USB_RX_BUF_SIZE (512)
|
||||
|
||||
static USBDRV_GlobalState gs; ///< Blobal USB state
|
||||
static uint8_t rx_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; ///< Receive buffer
|
||||
static UsbDrv_IN_cb cbs[USB_NUM_OF_ENDPOINTS]; ///< Callbacks for IN completion
|
||||
static USBDRV_GlobalState gs; // global USB state
|
||||
static uint8_t rx_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; // receive buffer
|
||||
static USBDRV_IN_cb cbs[USB_NUM_OF_ENDPOINTS]; // callbacks for IN completion
|
||||
|
||||
/** \cond false */
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
/** \endcond */
|
||||
|
||||
// ------------------------
|
||||
|
||||
@ -46,13 +46,13 @@ __weak void usbdrv_ulpi_init() {
|
||||
|
||||
// ------------------------
|
||||
|
||||
void usbdrv_register_IN_complete_cb(uint8_t ep, UsbDrv_IN_cb cb) {
|
||||
void usbdrv_register_IN_complete_cb(uint8_t ep, USBDRV_IN_cb cb) {
|
||||
cbs[ep] = cb;
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
|
||||
static UsbDrv_DrvIntf drvIntf;
|
||||
static USB_DrvIntf drvIntf;
|
||||
|
||||
void usbdrv_init_intf() {
|
||||
drvIntf.init = usbdrv_init;
|
||||
@ -67,7 +67,7 @@ void usbdrv_init_intf() {
|
||||
drvIntf.reg_IN_cb = usbdrv_register_IN_complete_cb;
|
||||
}
|
||||
|
||||
UsbDrv_DrvIntf *usbdrv_get_intf() {
|
||||
USB_DrvIntf *usbdrv_get_intf() {
|
||||
return &drvIntf;
|
||||
}
|
||||
|
||||
@ -185,21 +185,13 @@ void usbdrv_init_global_state() {
|
||||
memset(&gs, 0, sizeof(USBDRV_GlobalState));
|
||||
|
||||
// clear IN complete callbacks
|
||||
memset(&cbs, 0, sizeof(UsbDrv_IN_cb) * USB_NUM_OF_ENDPOINTS);
|
||||
memset(&cbs, 0, sizeof(USBDRV_IN_cb) * USB_NUM_OF_ENDPOINTS);
|
||||
|
||||
// initialize receive buffer
|
||||
gs.rx_buf = rx_buf;
|
||||
gs.rx_buf_level = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -209,7 +201,6 @@ void usbdrv_init() {
|
||||
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);
|
||||
@ -743,26 +734,12 @@ void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data) {
|
||||
USBMSG("%s [%u] %u\n", FIFO_STATUS_STR[pckt_status - 1], ep_num, byte_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
// always pass ALIGNED data!
|
||||
__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) {
|
||||
__weak void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -838,7 +815,7 @@ void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) {
|
||||
// SET_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // unmask interrupt
|
||||
} else { // not EP0
|
||||
if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV) {
|
||||
UsbDrv_CallbackCompound cbcpd;
|
||||
USBDRV_CallbackCompound cbcpd;
|
||||
cbcpd.ep = evt_data.rx.ep_num;
|
||||
cbcpd.dir = USB_OUT;
|
||||
cbcpd.code = USB_CBC_OUT;
|
||||
@ -875,7 +852,7 @@ void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) {
|
||||
case USB_EVT_IN_DONE: { // some IN operations have finished
|
||||
|
||||
// callback compound
|
||||
UsbDrv_CallbackCompound cbcpd;
|
||||
USBDRV_CallbackCompound cbcpd;
|
||||
cbcpd.dir = USB_IN;
|
||||
cbcpd.data = NULL;
|
||||
cbcpd.size = 0;
|
||||
@ -910,7 +887,7 @@ void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) {
|
||||
|
||||
// invoke callback if registered
|
||||
if (cbs[ep] != NULL) {
|
||||
UsbDrv_IN_cb cb = cbs[ep]; // fetch function pointer
|
||||
USBDRV_IN_cb cb = cbs[ep]; // fetch function pointer
|
||||
cbs[ep] = NULL; // clear callback
|
||||
cb(ep); // invoke the callback
|
||||
}
|
||||
@ -953,7 +930,7 @@ void USB_IRQ_HANDLER() {
|
||||
// USB reset
|
||||
if (ints & USB_OTG_GINTSTS_USBRST) {
|
||||
SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_USBRST); // clear interrupt
|
||||
//PROCESS_EVENT(USB_EVT_USB_RESET, NULL); // process event
|
||||
PROCESS_EVENT(USB_EVT_USB_RESET, NULL); // process event
|
||||
}
|
||||
|
||||
// End of enumeration (meaning NOT the USB ENUMERATION PROCESS,
|
||||
|
@ -118,11 +118,6 @@ void usbdrv_init();
|
||||
*/
|
||||
void usbdrv_reset();
|
||||
|
||||
/**
|
||||
* Init driver's global state.
|
||||
*/
|
||||
void usbdrv_init_global_state();
|
||||
|
||||
/**
|
||||
* Initialize driver interface.
|
||||
*/
|
||||
@ -130,33 +125,33 @@ 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.
|
||||
* Flush specific RX FIFO.
|
||||
*/
|
||||
void usbdrv_flush_rx_fifo();
|
||||
|
||||
/**
|
||||
* Power down or up the USB peripheral. Also control built-in transciever.
|
||||
*
|
||||
*
|
||||
* @param en power on/off
|
||||
*/
|
||||
void usbdrv_power_and_connect(bool en);
|
||||
|
||||
/**
|
||||
* Configure USB Endpoint.
|
||||
*
|
||||
*
|
||||
* @param ep index of the endpoint
|
||||
* @param dir direction of the endpoint to be configured
|
||||
* @param cfg pointer to the configuration details
|
||||
@ -165,7 +160,7 @@ void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *c
|
||||
|
||||
/**
|
||||
* Deconfigure a specific USB Endpoint.
|
||||
*
|
||||
*
|
||||
* @param ep index of the endpoint
|
||||
* @param dir direction of the endpoint to be deconfigured
|
||||
*/
|
||||
@ -173,7 +168,7 @@ 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
|
||||
@ -182,7 +177,7 @@ 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
|
||||
*/
|
||||
@ -190,7 +185,7 @@ 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
|
||||
*/
|
||||
@ -200,7 +195,7 @@ 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
|
||||
@ -210,7 +205,7 @@ 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
|
||||
@ -222,26 +217,28 @@ uint32_t usbdrv_arm_OUT_endpoint(uint8_t ep, uint16_t size);
|
||||
* 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);
|
||||
@ -249,7 +246,7 @@ 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
|
||||
@ -262,6 +259,7 @@ void usbdrv_preload_endpoint_config(uint8_t ep, uint8_t dir, const USBDRV_EpConf
|
||||
*/
|
||||
void usbdrv_clear_endpoint_config();
|
||||
|
||||
|
||||
/**
|
||||
* Apply preloaded Endpoint configuration, write
|
||||
* Endpoint config to the hardware.
|
||||
@ -271,7 +269,7 @@ 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);
|
||||
@ -288,47 +286,24 @@ 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();
|
||||
USB_DrvIntf * usbdrv_get_intf();
|
||||
|
||||
/**
|
||||
* Register callback for IN transaction complete.
|
||||
*
|
||||
*
|
||||
* @param ep index of the Endpoint
|
||||
* @param cb pointer to the callback function
|
||||
*/
|
||||
void usbdrv_register_IN_complete_cb(uint8_t ep, UsbDrv_IN_cb cb);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
void usbdrv_register_IN_complete_cb(uint8_t ep, USBDRV_IN_cb cb);
|
||||
|
||||
#endif /* STM32_USB_DRV */
|
||||
|
@ -1,38 +0,0 @@
|
||||
#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 */
|
@ -1,46 +0,0 @@
|
||||
#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 */
|
@ -1,58 +0,0 @@
|
||||
#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 */
|
@ -1,82 +0,0 @@
|
||||
#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 */
|
@ -1,38 +0,0 @@
|
||||
#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 */
|
@ -3,12 +3,12 @@
|
||||
"subclass": "0x00",
|
||||
"protocol_code": "0x00",
|
||||
"max_ep0_packet_size": 64,
|
||||
"vendor_id": "0x0000",
|
||||
"product_id": "0x0001",
|
||||
"vendor_id": "0x0925",
|
||||
"product_id": "0x9050",
|
||||
"device_release_bcd": "0x0100",
|
||||
"vendor_string": "<Vendor>",
|
||||
"product_string": "<Product name>",
|
||||
"serial_number": "0000000000000",
|
||||
"vendor_string": "Epagris",
|
||||
"product_string": "Testdevice",
|
||||
"serial_number": "1552",
|
||||
"configurations": [
|
||||
{
|
||||
"id": 1,
|
@ -3,12 +3,12 @@
|
||||
"subclass": "0x00",
|
||||
"protocol_code": "0x00",
|
||||
"max_ep0_packet_size": 64,
|
||||
"vendor_id": "0x0000",
|
||||
"product_id": "0x0001",
|
||||
"vendor_id": "0x0925",
|
||||
"product_id": "0x9050",
|
||||
"device_release_bcd": "0x0100",
|
||||
"vendor_string": "<Vendor>",
|
||||
"product_string": "<Product name>",
|
||||
"serial_number": "0000000000000",
|
||||
"vendor_string": "Epagris",
|
||||
"product_string": "Testdevice",
|
||||
"serial_number": "1552",
|
||||
"configurations": [
|
||||
{
|
||||
"id": 1,
|
@ -3,12 +3,12 @@
|
||||
"subclass": "0x00",
|
||||
"protocol_code": "0x00",
|
||||
"max_ep0_packet_size": 64,
|
||||
"vendor_id": "0x0000",
|
||||
"product_id": "0x0001",
|
||||
"vendor_id": "0x0925",
|
||||
"product_id": "0x9050",
|
||||
"device_release_bcd": "0x0100",
|
||||
"vendor_string": "<Vendor>",
|
||||
"product_string": "<Product name>",
|
||||
"serial_number": "0000000000000",
|
||||
"vendor_string": "Epagris",
|
||||
"product_string": "Testdevice",
|
||||
"serial_number": "1552",
|
||||
"configurations": [
|
||||
{
|
||||
"id": 1,
|
120
usb.c
120
usb.c
@ -6,52 +6,72 @@
|
||||
#include "usb_common.h"
|
||||
#include "usb_driver_common.h"
|
||||
|
||||
// #include "utils/gen_queue.h"
|
||||
|
||||
#include FLATUSB_DESCRIPTOR_HEADER
|
||||
|
||||
// ---------------
|
||||
|
||||
/** \cond false */
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
/** \endcond */
|
||||
|
||||
#define USBDRV_ARM_IN_ZLP(ep) drv->arm_IN((ep), NULL, 0) ///< Arm a ZLP IN message
|
||||
|
||||
// ---------------
|
||||
|
||||
static uint8_t tx_assembly_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; ///< Buffer for assembling packets
|
||||
static Usb_SetupTransferState stups; ///< Setup transfer state.
|
||||
static UsbDrv_DrvIntf *drv; ///< Mutable pointer to the driver interface
|
||||
#define USBDRV_ARM_IN_ZLP(ep) drv->arm_IN((ep), NULL, 0)
|
||||
|
||||
#define USB_RX_BUF_SIZE (512) // FIXME
|
||||
static uint8_t tx_assembly_buf[USB_RX_BUF_SIZE] DWORD_ALIGN; // buffer for assembling packets
|
||||
|
||||
// ---------------
|
||||
|
||||
// // state of changeing address
|
||||
// typedef enum {
|
||||
// USB_ADDRCHG_IDLE = 0, // idle state
|
||||
// USB_ADDRCHG_NEW_ADDR_RECVD, // new address received
|
||||
// } USB_AddressChangeState;
|
||||
|
||||
// ----------------
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
__attribute__((weak)) void usb_event_callback(USB_CallbackEvent *cbevt) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
||||
/**
|
||||
* Callback function for bus issued Reset is done notification.
|
||||
*/
|
||||
// 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) {
|
||||
// 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
|
||||
// USB_DescHdr *hdr = (USB_DescHdr *)gs.tx_assembly_buf; // obtain header
|
||||
// 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;
|
||||
static USB_DrvIntf *drv;
|
||||
|
||||
void usbcore_reset() {
|
||||
// init state
|
||||
memset(&stups, 0, sizeof(USB_SetupTransferState));
|
||||
}
|
||||
|
||||
static void usbcore_rst_notify() {
|
||||
usbcore_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Enumeration Done notification.
|
||||
*/
|
||||
static void usbcore_enum_notify(uint8_t spd) {
|
||||
(void)spd;
|
||||
}
|
||||
|
||||
void usbcore_init(UsbDrv_DrvIntf *drvIntf) {
|
||||
void usbcore_init(USB_DrvIntf *drvIntf) {
|
||||
usbcore_reset(); // reset USB Core
|
||||
|
||||
drv = drvIntf; // store USB driver interface assignments
|
||||
@ -59,34 +79,6 @@ void usbcore_init(UsbDrv_DrvIntf *drvIntf) {
|
||||
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) {
|
||||
|
||||
// MSG("STUP: %u, %s\n", size, stage == UST_SETUP ? "SETUP" : "DATA");
|
||||
@ -174,12 +166,7 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
|
||||
}
|
||||
|
||||
// arm data transmission
|
||||
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
|
||||
}
|
||||
drv->arm_IN(0, data, sz);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -199,12 +186,12 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
|
||||
}
|
||||
case UREQ_SetInterface: // SET INTERFACE
|
||||
{
|
||||
// TODO: set interface
|
||||
// TODO: set configuration
|
||||
USBDRV_ARM_IN_ZLP(0);
|
||||
break;
|
||||
}
|
||||
default: { // UNKNOWN REQUEST, pass processing to user application
|
||||
Usb_CallbackEvent cbevt = {0};
|
||||
USB_CallbackEvent cbevt = {0};
|
||||
cbevt.type = USB_CBEVT_UNKNOWN_REQ;
|
||||
cbevt.setup_request = &stups.setup_req;
|
||||
cbevt.data = (const uint8_t *)data;
|
||||
@ -224,16 +211,11 @@ void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stag
|
||||
stups.out_complete = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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...
|
||||
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
|
||||
|
||||
bool discard_event = false; // only discard if event does not fit any category below
|
||||
bool discard_event = false; // only discard if event does not fit any category above
|
||||
switch (cbcpd->code) {
|
||||
case USB_CBC_OUT: {
|
||||
cbevt.type = USB_CBEVT_OUT;
|
||||
@ -271,9 +253,11 @@ void usbcore_process_nonsetup_event(UsbDrv_CallbackCompound *cbcpd) {
|
||||
}
|
||||
}
|
||||
|
||||
void usbcore_register_IN_callback(uint8_t ep, UsbDrv_IN_cb cb) {
|
||||
bool en = cb != NULL;
|
||||
drv->en_ep_irq(ep, USB_IN, en);
|
||||
void usbcore_wake_up_endpoint(uint8_t ep, uint8_t dir) {
|
||||
drv->en_ep_irq(ep, dir, true);
|
||||
}
|
||||
|
||||
void usbcore_register_IN_callback(uint8_t ep, USBDRV_IN_cb cb) {
|
||||
drv->reg_IN_cb(ep, cb);
|
||||
}
|
||||
|
||||
|
67
usb.h
67
usb.h
@ -3,69 +3,12 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usb_device_types.h"
|
||||
#include "usb_driver_common.h"
|
||||
|
||||
// --------------
|
||||
|
||||
/**
|
||||
* 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);
|
||||
void usbcore_init(USB_DrvIntf *drvIntf); // initialize USB core
|
||||
uint32_t usbcore_schedule_transmission(uint8_t ep, const uint8_t *data, uint16_t size); // write data to endpoint, return with number of bytes actually written
|
||||
uint32_t usbcore_schedule_reception(uint8_t ep, uint16_t size); // expect data coming from the endpoint
|
||||
void usbcore_wake_up_endpoint(uint8_t ep, uint8_t dir); // wake up endpoint
|
||||
void usbcore_register_IN_callback(uint8_t ep, USBDRV_IN_cb cb); // register IN complete callback
|
||||
|
||||
#endif /* CORE_USB_USB */
|
||||
|
@ -5,39 +5,30 @@
|
||||
|
||||
#include "usb_device_types.h"
|
||||
|
||||
/**
|
||||
* Callback event type.
|
||||
*/
|
||||
typedef enum {
|
||||
USB_CBEVT_OUT, ///< OUT event
|
||||
USB_CBEVT_IN, ///< IN event
|
||||
USB_CBEVT_UNKNOWN_REQ ///< unknown request on a control endpoint
|
||||
} Usb_CallbackEventType;
|
||||
USB_CBEVT_OUT, // OUT event
|
||||
USB_CBEVT_IN, // IN event
|
||||
USB_CBEVT_UNKNOWN_REQ // unknown request on a control endpoint
|
||||
} USB_CallbackEventType;
|
||||
|
||||
/**
|
||||
* Callback event subtype.
|
||||
*/
|
||||
typedef enum {
|
||||
USB_CBEVST_IN_NONE = 0, ///< No subtype
|
||||
USB_CBEVST_IN_REQ ///< IN request was received but could not be responded
|
||||
} Usb_CallbackEventSubType;
|
||||
USB_CBEVST_IN_NONE = 0, // no subtype
|
||||
USB_CBEVST_IN_REQ // IN request was received but could not be responded
|
||||
} USB_CallbackEventSubType;
|
||||
|
||||
/**
|
||||
* USB callback event.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t type : 4; ///< Event type
|
||||
uint8_t subtype : 4; ///< Event subtype
|
||||
uint8_t ep; ///< Endpoint number
|
||||
uint8_t dir; ///< Endpoint direction
|
||||
uint8_t size; ///< Size of accompaining data
|
||||
const uint8_t *data; ///< Event data
|
||||
const USB_SetupRequest *setup_request; ///< Corresponding setup request (if exists)
|
||||
uint8_t type : 4; // event type
|
||||
uint8_t subtype : 4; // event subtype
|
||||
uint8_t ep; // endpoint number
|
||||
uint8_t dir; // endpoint direction
|
||||
uint8_t size; // size of accompaining data
|
||||
const uint8_t * data; // event data
|
||||
const USB_SetupRequest * setup_request; // corresponding setup request (if exists)
|
||||
|
||||
bool reply_valid; ///< Reply message is valid
|
||||
const uint8_t *reply_data; ///< Reply data
|
||||
uint8_t reply_size; ///< Reply size
|
||||
bool arm_out_endpoint; ///< Automatically arm OUT endpoint at the end of the current transmission
|
||||
} Usb_CallbackEvent;
|
||||
bool reply_valid; // reply message is valid
|
||||
const uint8_t * reply_data; // reply data
|
||||
uint8_t reply_size; // reply size
|
||||
bool arm_out_endpoint; // automatically arm OUT endpoint at the end of the current transmission
|
||||
} USB_CallbackEvent;
|
||||
|
||||
#endif /* CORE_USB_USB_CALLBACK_EVENT */
|
||||
|
20
usb_common.h
20
usb_common.h
@ -4,18 +4,18 @@
|
||||
#include "usb_common_types.h"
|
||||
#include "usb_device_types.h"
|
||||
|
||||
#define USB_DELAY(t) for (uint64_t i = 0; i < (t) * 10000; i++) { asm("nop"); } ///< Create a delay that does not relay on the processor ticks.
|
||||
#define USB_DELAY(t) for (uint64_t i = 0; i < (t) * 10000; i++) { asm("nop"); }
|
||||
|
||||
#define READ_FIELD(r,f) (((r) & (f##_Msk)) >> (f##_Pos)) ///< 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 READ_FIELD(r,f) (((r) & (f##_Msk)) >> (f##_Pos))
|
||||
#define WRITE_FIELD(r,f,v) ((r) = ((r) & ~(f##_Msk)) | (v << (f##_Pos)))
|
||||
#define WAIT_FOR_BIT(r,b) while ((r) & (b)) {}
|
||||
#define WAIT_FOR_BIT_DELAY(r,b,d) while ((r) & (b)) { USB_DELAY((d)); }
|
||||
#define WAIT_FOR_nBIT(r,b) while (!((r) & (b))) {}
|
||||
#define WAIT_FOR_nBIT_DELAY(r,b,d) while (!((r) & (b))) { USB_DELAY((d)); }
|
||||
|
||||
#define DWORD_ALIGN __attribute__((aligned(4))) ///< Declare a 32-bit aligned memory area
|
||||
#define DWORD_ALIGN __attribute__((aligned(4)))
|
||||
|
||||
#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
|
||||
#define CEILDIV4(x) (((x) + 3) >> 2)
|
||||
#define CEIL4(x) (((x) + 3) & (~0b11))
|
||||
|
||||
#endif /* CORE_USB_USB_COMMON */
|
||||
|
@ -4,9 +4,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* A byte-sized boolean.
|
||||
*/
|
||||
typedef uint8_t bool8_t;
|
||||
|
||||
#endif /* CORE_USB_USB_COMMON_TYPES */
|
||||
|
@ -9,120 +9,109 @@
|
||||
|
||||
/* ------ USB DESCRIPTORS ------- */
|
||||
|
||||
/**
|
||||
* USB Descriptor type codes.
|
||||
*/
|
||||
// USB Descriptor type codes
|
||||
|
||||
typedef enum {
|
||||
UD_Device = 1, ///< Device Descriptor
|
||||
UD_Configuration = 2, ///< Configuration Descriptor
|
||||
UD_String = 3, ///< String Descriptor
|
||||
UD_Interface = 4, ///< Interface Descriptor
|
||||
UD_Endpoint = 5, ///< Endpoint Descriptor
|
||||
UD_DeviceQualifier = 6, ///< DeviceQualifier Descriptor
|
||||
UD_OtherSpeedConfiguration = 7 ///< OtherSpeedConfiguration Descriptor
|
||||
UD_Device = 1,
|
||||
UD_Configuration = 2,
|
||||
UD_String = 3,
|
||||
UD_Interface = 4,
|
||||
UD_Endpoint = 5,
|
||||
UD_DeviceQualifier = 6,
|
||||
UD_OtherSpeedConfiguration = 7
|
||||
// NOT FULL!
|
||||
} USB_DescType;
|
||||
|
||||
/**
|
||||
* USB descriptor header
|
||||
*/
|
||||
// USB descriptor header
|
||||
#define USB_DESC_HEADER \
|
||||
uint8_t bLength; /* length in bytes */ \
|
||||
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 {
|
||||
USB_DESC_HEADER
|
||||
} USB_DescHdr;
|
||||
|
||||
/**
|
||||
* USB Device descriptor
|
||||
*/
|
||||
// USB Device descriptor
|
||||
|
||||
typedef struct {
|
||||
USB_DESC_HEADER
|
||||
uint16_t bcdUSB; ///< USB specification release number (BCD)
|
||||
uint8_t bDeviceClass; ///< Class code
|
||||
uint8_t bDeviceSubclass; ///< Subclass code
|
||||
uint8_t bDeviceProtocol; ///< Protocol code
|
||||
uint8_t bMaxPacketSize0; ///< Maximum packet size for endpoint 0
|
||||
uint16_t idVendor; ///< Vendor ID
|
||||
uint16_t idProduct; ///< Product ID
|
||||
uint16_t bcdDevice; ///< Device release number (BCD)
|
||||
uint8_t iManufacturer; ///< Index of string descriptor for manufacturer
|
||||
uint8_t iProduct; ///< Index of string descriptor for the product
|
||||
uint8_t iSerialNumber; ///< Index of string descriptor for the serial number
|
||||
uint8_t bNumConfiguration; ///< Number of possible configurations
|
||||
uint16_t bcdUSB; // USB specification release number (BCD)
|
||||
uint8_t bDeviceClass; // Class code
|
||||
uint8_t bDeviceSubclass; // Subclass code
|
||||
uint8_t bDeviceProtocol; // Protocol code
|
||||
uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0
|
||||
uint16_t idVendor; // Vendor ID
|
||||
uint16_t idProduct; // Product ID
|
||||
uint16_t bcdDevice; // Device release number (BCD)
|
||||
uint8_t iManufacturer; // Index of string descriptor for manufacturer
|
||||
uint8_t iProduct; // Index of string descriptor for the product
|
||||
uint8_t iSerialNumber; // Index of string descriptor for the serial number
|
||||
uint8_t bNumConfiguration; // Number of possible configurations
|
||||
} USB_DeviceDesc;
|
||||
|
||||
/**
|
||||
* USB Device Qualifier descriptor
|
||||
*/
|
||||
// USB Device Qualifier descriptor
|
||||
|
||||
typedef struct {
|
||||
USB_DESC_HEADER
|
||||
uint16_t bcdUSB; ///< USB specification release number (BCD)
|
||||
uint8_t bDeviceClass; ///< Class code
|
||||
uint8_t bDeviceSubclass; ///< Subclass code
|
||||
uint8_t bDeviceProtocol; ///< Protocol code
|
||||
uint8_t bMaxPacketSize0; ///< Maximum packet size for endpoint 0
|
||||
uint8_t bNumConfiguration; ///< Number of possible configurations
|
||||
uint16_t bcdUSB; // USB specification release number (BCD)
|
||||
uint8_t bDeviceClass; // Class code
|
||||
uint8_t bDeviceSubclass; // Subclass code
|
||||
uint8_t bDeviceProtocol; // Protocol code
|
||||
uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0
|
||||
uint8_t bNumConfiguration; // Number of possible configurations
|
||||
} __attribute__((packed)) USB_DeviceQualifierDesc;
|
||||
|
||||
#define USB_CONFIG_ATTR_SELF_POWERED (1 << 6) ///< Device is self-powered
|
||||
#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) ///< It's a USB 1.1 or newer device
|
||||
#define USB_CONFIG_ATTR_SELF_POWERED (1 << 6)
|
||||
#define USB_CONFIG_ATTR_REMOTE_WKUP (1 << 5)
|
||||
#define USB_CONFIG_ATTR_USB1_1_FLAG (1 << 7)
|
||||
|
||||
// USB Configuration descriptor
|
||||
|
||||
/**
|
||||
* USB Configuration descriptor
|
||||
*/
|
||||
typedef struct {
|
||||
USB_DESC_HEADER
|
||||
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 bConfigrationValue; ///< Identifier for Set Configuration and Get Configuration Requests
|
||||
uint8_t iConfiguration; ///< Index of string descriptor for the configuration
|
||||
uint8_t bmAttributes; ///< Self/bus power and remote wakeup settings
|
||||
uint8_t bMaxPower; ///< Bus power required in units of 2 mA
|
||||
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 bConfigrationValue; // Identifier for Set Configuration and Get Configuration Requests
|
||||
uint8_t iConfiguration; // Index of string descriptor for the configuration
|
||||
uint8_t bmAttributes; // Self/bus power and remote wakeup settings
|
||||
uint8_t bMaxPower; // Bus power required in units of 2 mA
|
||||
} __attribute__((packed)) USB_ConfigurationDesc;
|
||||
|
||||
/**
|
||||
* USB Interface descriptor
|
||||
*/
|
||||
// USB Interface descriptor
|
||||
|
||||
typedef struct {
|
||||
USB_DESC_HEADER
|
||||
uint8_t bInterfaceNumber; ///< Number identifying this interface
|
||||
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 bInterfaceClass; ///< Class code
|
||||
uint8_t bInterfaceSubclass; ///< Subclass code
|
||||
uint8_t bInterfaceProtocol; ///< Protocol code
|
||||
uint8_t iInterface; ///< Index of string descriptor for the 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 bNumEndpoints; // Number of endpoints supported not counting endpoint zero
|
||||
uint8_t bInterfaceClass; // Class code
|
||||
uint8_t bInterfaceSubclass; // Subclass code
|
||||
uint8_t bInterfaceProtocol; // Protocol code
|
||||
uint8_t iInterface; // Index of string descriptor for the interface
|
||||
} __attribute__((packed)) USB_InterfaceDesc;
|
||||
|
||||
/**
|
||||
* USB Transfer type
|
||||
*/
|
||||
// USB Transfer type
|
||||
|
||||
typedef enum {
|
||||
UT_Control = 0b00, ///< Control
|
||||
UT_Isochronous = 0b01, ///< Isochronous
|
||||
UT_Bulk = 0b10, ///< Bulk
|
||||
UT_Interrupt = 0b11 ///< Interrupt
|
||||
UT_Control = 0b00,
|
||||
UT_Isochronous = 0b01,
|
||||
UT_Bulk = 0b10,
|
||||
UT_Interrupt = 0b11
|
||||
} USB_TransferType;
|
||||
|
||||
// USB Endpoint descriptor
|
||||
|
||||
/**
|
||||
* USB Endpoint descriptor
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
USB_DESC_HEADER
|
||||
uint8_t bEndpointAddress; ///< Endpoint number and direction
|
||||
uint8_t bmAttributes; ///< Transfer type and supplementary information
|
||||
uint16_t wMaxPacketSize; ///< Maximum packet size supported
|
||||
uint8_t bInterval; ///< Service interval or NAK rate
|
||||
uint8_t bEndpointAddress; // Endpoint number and direction
|
||||
uint8_t bmAttributes; // Transfer type and supplementary information
|
||||
uint16_t wMaxPacketSize; // Maximum packet size supported
|
||||
uint8_t bInterval; // Service interval or NAK rate
|
||||
} __attribute__((packed)) USB_EndpointDesc;
|
||||
|
||||
/* struct {
|
||||
@ -130,25 +119,21 @@ typedef struct {
|
||||
uint8_t dir; // direction
|
||||
} bEndpointAddress; // Endpoint number and direction*/
|
||||
|
||||
// USB String descriptor
|
||||
|
||||
/**
|
||||
* USB String descriptor
|
||||
*/
|
||||
typedef struct {
|
||||
USB_DESC_HEADER
|
||||
} USB_StringDesc;
|
||||
|
||||
/**
|
||||
* USB endpoint direction
|
||||
*/
|
||||
// USB endpoint direction
|
||||
|
||||
typedef enum {
|
||||
USB_OUT = 0, ///< OUT
|
||||
USB_IN = 1 ///< IN
|
||||
USB_OUT = 0,
|
||||
USB_IN = 1
|
||||
} USB_EndpointDir;
|
||||
|
||||
/**
|
||||
* USB PIDs
|
||||
*/
|
||||
// USB PIDs
|
||||
|
||||
typedef enum {
|
||||
PID_EXT = 0,
|
||||
PID_OUT = 1,
|
||||
@ -168,18 +153,16 @@ typedef enum {
|
||||
PID_MDATA = 15,
|
||||
} USB_PID;
|
||||
|
||||
/**
|
||||
* USB Request types
|
||||
*/
|
||||
// USB Request types
|
||||
|
||||
typedef enum {
|
||||
UR_Standard = 0,
|
||||
UR_VendorSpec = 1,
|
||||
UR_ReqDefVendorSpec = 2
|
||||
} USB_RequestType;
|
||||
|
||||
/**
|
||||
* USB Recipients
|
||||
*/
|
||||
// USB Recipients
|
||||
|
||||
typedef enum {
|
||||
UREC_Device = 0,
|
||||
UREC_SpecificInterface = 1,
|
||||
@ -187,9 +170,8 @@ typedef enum {
|
||||
UREC_OtherElement = 3
|
||||
} USB_Recipient;
|
||||
|
||||
/**
|
||||
* USB Request types
|
||||
*/
|
||||
// USB Request types
|
||||
|
||||
typedef enum {
|
||||
UREQ_GetStatus = 0x00,
|
||||
UREQ_ClearFeature = 0x01,
|
||||
@ -206,24 +188,23 @@ typedef enum {
|
||||
UREQ_SetIsochronousDelay = 0x31
|
||||
} USB_RequestCode;
|
||||
|
||||
/**
|
||||
* Setup request structure
|
||||
*/
|
||||
// setup request structure
|
||||
|
||||
typedef struct {
|
||||
|
||||
union {
|
||||
uint8_t bmRequestType; ///< Request type
|
||||
uint8_t bmRequestType;
|
||||
|
||||
struct {
|
||||
uint8_t recipient : 5; ///< Recipient
|
||||
uint8_t type : 2; ///< Type
|
||||
uint8_t dir : 1; ///< Direction
|
||||
uint8_t recipient : 5;
|
||||
uint8_t type : 2;
|
||||
uint8_t dir : 1;
|
||||
} fields;
|
||||
} bmRequestType; ///< Request type
|
||||
uint8_t bRequest; ///< Request
|
||||
uint16_t wValue; ///< Value
|
||||
uint16_t wIndex; ///< Index
|
||||
uint16_t wLength; ///< Number of bytes in the data stage
|
||||
} bmRequestType; // request type
|
||||
uint8_t bRequest; // request
|
||||
uint16_t wValue; // ...
|
||||
uint16_t wIndex; // ...
|
||||
uint16_t wLength; // number of bytes in the data stage
|
||||
} USB_SetupRequest;
|
||||
|
||||
#endif /* CORE_USB_USB_DEVICE_TYPES */
|
||||
|
1113
usb_driver.c
Normal file
1113
usb_driver.c
Normal file
File diff suppressed because it is too large
Load Diff
170
usb_driver.h
Normal file
170
usb_driver.h
Normal file
@ -0,0 +1,170 @@
|
||||
#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 */
|
@ -9,60 +9,49 @@ typedef enum {
|
||||
USB_SPD_LOW = 1,
|
||||
USB_SPD_FULL = 2,
|
||||
USB_SPD_HIGH = 3
|
||||
} UsbDrv_LineSpeed;
|
||||
} USBDRV_LineSpeed;
|
||||
|
||||
typedef void (*UsbDrv_IN_cb)(uint8_t ep);
|
||||
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
|
||||
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;
|
||||
void (*rst_notify)(); // notify USB core of a bus issued reset
|
||||
void (*enum_notify)(uint8_t spd); // notify the USB core that speed negotiation has concluded
|
||||
} USB_DrvIntf;
|
||||
|
||||
#ifndef USBDBGMSG
|
||||
#define USBMSG(...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Callback codes.
|
||||
*/
|
||||
// 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;
|
||||
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.
|
||||
*/
|
||||
// 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;
|
||||
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;
|
||||
typedef enum { UST_SETUP,
|
||||
UST_DATA } USBDRV_ControlTfStage;
|
||||
|
||||
#endif /* FLATUSB_USB_DRIVER_INTERFACE */
|
||||
|
Loading…
x
Reference in New Issue
Block a user