Compare commits

..

No commits in common. "master" and "usb_rework" have entirely different histories.

41 changed files with 1845 additions and 7973 deletions

1
.gitignore vendored
View File

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

View File

@ -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}
)

2676
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

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

View File

@ -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;

View File

@ -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!")

View File

@ -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.

View File

@ -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);

View File

@ -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)

View File

@ -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);
};
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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">&#160;$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td>
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<!--BEGIN !FULL_SIDEBAR-->
<td>$searchbox</td>
<!--END !FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
<!--BEGIN SEARCHENGINE-->
<!--BEGIN FULL_SIDEBAR-->
<tr><td colspan="2">$searchbox</td></tr>
<!--END FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

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

View File

@ -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;
}

View File

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

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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
View File

@ -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
View File

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

View File

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

View File

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

View File

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

View File

@ -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

File diff suppressed because it is too large Load Diff

170
usb_driver.h Normal file
View 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 */

View File

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