Update examples

This commit is contained in:
Valerii Koval
2023-01-05 12:42:19 +02:00
parent d0835e994c
commit 4a461f5221
541 changed files with 145063 additions and 1 deletions
@@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "tusb.h"
#include "cdc.h"
#define CDC_INTF_NUM CFG_TUD_CDC // number of cdc blocks
static esp_tusb_cdc_t *cdc_obj[CDC_INTF_NUM] = {};
static const char *TAG = "tusb_cdc";
esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num)
{
if (itf_num >= CDC_INTF_NUM || itf_num < 0) {
return NULL;
}
return cdc_obj[itf_num];
}
static esp_err_t cdc_obj_check(int itf, bool expected_inited, tusb_class_code_t expected_type)
{
esp_tusb_cdc_t *this_itf = tinyusb_cdc_get_intf(itf);
bool inited = (this_itf != NULL);
ESP_RETURN_ON_FALSE(expected_inited == inited, ESP_ERR_INVALID_STATE, TAG, "Wrong state of the interface. Expected state: %s", expected_inited ? "initialized" : "not initialized");
ESP_RETURN_ON_FALSE(!(inited && (expected_type != -1) && !(this_itf->type == expected_type)), ESP_ERR_INVALID_STATE, TAG, "Wrong type of the interface. Should be : 0x%x (tusb_class_code_t)", expected_type);
return ESP_OK;
}
static esp_err_t tusb_cdc_comm_init(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed");
cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t));
if (cdc_obj[itf] != NULL) {
cdc_obj[itf]->type = TUSB_CLASS_CDC;
ESP_LOGD(TAG, "CDC Comm class initialized");
return ESP_OK;
} else {
ESP_LOGE(TAG, "CDC Comm initialization error");
return ESP_FAIL;
}
}
static esp_err_t tusb_cdc_deinit_comm(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC), TAG, "cdc_obj_check failed");
free(cdc_obj[itf]);
cdc_obj[itf] = NULL;
return ESP_OK;
}
static esp_err_t tusb_cdc_data_init(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed");
cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t));
if (cdc_obj[itf] != NULL) {
cdc_obj[itf]->type = TUSB_CLASS_CDC_DATA;
ESP_LOGD(TAG, "CDC Data class initialized");
return ESP_OK;
} else {
ESP_LOGE(TAG, "CDC Data initialization error");
return ESP_FAIL;
}
}
static esp_err_t tusb_cdc_deinit_data(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed");
free(cdc_obj[itf]);
cdc_obj[itf] = NULL;
return ESP_OK;
}
esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed");
ESP_LOGD(TAG, "Init CDC %d", itf);
if (cfg->cdc_class == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_comm_init(itf), TAG, "tusb_cdc_comm_init failed");
cdc_obj[itf]->cdc_subclass.comm_subclass = cfg->cdc_subclass.comm_subclass;
} else {
ESP_RETURN_ON_ERROR(tusb_cdc_data_init(itf), TAG, "tusb_cdc_data_init failed");
cdc_obj[itf]->cdc_subclass.data_subclass = cfg->cdc_subclass.data_subclass;
}
cdc_obj[itf]->usb_dev = cfg->usb_dev;
return ESP_OK;
}
esp_err_t tinyusb_cdc_deinit(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, -1), TAG, "cdc_obj_check failed");
ESP_LOGD(TAG, "Deinit CDC %d", itf);
if (cdc_obj[itf]->type == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_deinit_comm(itf), TAG, "tusb_cdc_deinit_comm failed");
} else if (cdc_obj[itf]->type == TUSB_CLASS_CDC_DATA) {
ESP_RETURN_ON_ERROR(tusb_cdc_deinit_data(itf), TAG, "tusb_cdc_deinit_data failed");
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
@@ -0,0 +1,150 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "descriptors_control.h"
static const char *TAG = "tusb_desc";
static tusb_desc_device_t s_device_descriptor;
static const uint8_t *s_configuration_descriptor;
static char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
#define MAX_DESC_BUF_SIZE 32
// =============================================================================
// CALLBACKS
// =============================================================================
/**
* @brief Invoked when received GET DEVICE DESCRIPTOR.
* Application returns pointer to descriptor
*
* @return uint8_t const*
*/
uint8_t const *tud_descriptor_device_cb(void)
{
return (uint8_t const *)&s_device_descriptor;
}
/**
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
* Descriptor contents must exist long enough for transfer to complete
*
* @param index
* @return uint8_t const* Application return pointer to descriptor
*/
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // for multiple configurations
return s_configuration_descriptor;
}
static uint16_t _desc_str[MAX_DESC_BUF_SIZE];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
uint8_t chr_count;
if ( index == 0) {
memcpy(&_desc_str[1], s_str_descriptor[0], 2);
chr_count = 1;
} else {
// Convert ASCII string into UTF-16
if ( index >= sizeof(s_str_descriptor) / sizeof(s_str_descriptor[0]) ) {
ESP_LOGE(TAG, "String index (%u) is out of bounds, check your string descriptor", index);
return NULL;
}
if (s_str_descriptor[index] == NULL) {
ESP_LOGE(TAG, "String index (%u) points to NULL, check your string descriptor", index);
return NULL;
}
const char *str = s_str_descriptor[index];
// Cap at max char
chr_count = strlen(str);
if ( chr_count > MAX_DESC_BUF_SIZE - 1 ) {
chr_count = MAX_DESC_BUF_SIZE - 1;
}
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2 * chr_count + 2);
return _desc_str;
}
// =============================================================================
// Driver functions
// =============================================================================
void tusb_set_descriptor(const tusb_desc_device_t *dev_desc, const char **str_desc, const uint8_t *cfg_desc)
{
ESP_LOGI(TAG, "\n"
"┌─────────────────────────────────┐\n"
"│ USB Device Descriptor Summary │\n"
"├───────────────────┬─────────────┤\n"
"│bDeviceClass │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bDeviceSubClass │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bDeviceProtocol │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bMaxPacketSize0 │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│idVendor │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│idProduct │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│bcdDevice │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iManufacturer │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iProduct │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iSerialNumber │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│bNumConfigurations │ %-#10x │\n"
"└───────────────────┴─────────────┘",
dev_desc->bDeviceClass, dev_desc->bDeviceSubClass,
dev_desc->bDeviceProtocol, dev_desc->bMaxPacketSize0,
dev_desc->idVendor, dev_desc->idProduct, dev_desc->bcdDevice,
dev_desc->iManufacturer, dev_desc->iProduct, dev_desc->iSerialNumber,
dev_desc->bNumConfigurations);
s_device_descriptor = *dev_desc;
s_configuration_descriptor = cfg_desc;
if (str_desc != NULL) {
memcpy(s_str_descriptor, str_desc,
sizeof(s_str_descriptor[0])*USB_STRING_DESCRIPTOR_ARRAY_SIZE);
}
}
tusb_desc_device_t *tusb_get_active_desc(void)
{
return &s_device_descriptor;
}
char **tusb_get_active_str_desc(void)
{
return s_str_descriptor;
}
void tusb_clear_descriptor(void)
{
memset(&s_device_descriptor, 0, sizeof(s_device_descriptor));
memset(&s_str_descriptor, 0, sizeof(s_str_descriptor));
}
@@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/usb_phy.h"
#include "soc/usb_pins.h"
#include "tinyusb.h"
#include "descriptors_control.h"
#include "usb_descriptors.h"
#include "tusb.h"
#include "tusb_tasks.h"
const static char *TAG = "TinyUSB";
static usb_phy_handle_t phy_hdl;
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
{
const tusb_desc_device_t *dev_descriptor;
const char **string_descriptor;
const uint8_t *cfg_descriptor;
ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// Configure USB PHY
usb_phy_config_t phy_conf = {
.controller = USB_PHY_CTRL_OTG,
.otg_mode = USB_OTG_MODE_DEVICE,
};
// External PHY IOs config
usb_phy_ext_io_conf_t ext_io_conf = {
.vp_io_num = USBPHY_VP_NUM,
.vm_io_num = USBPHY_VM_NUM,
.rcv_io_num = USBPHY_RCV_NUM,
.oen_io_num = USBPHY_OEN_NUM,
.vpo_io_num = USBPHY_VPO_NUM,
.vmo_io_num = USBPHY_VMO_NUM,
};
if (config->external_phy) {
phy_conf.target = USB_PHY_TARGET_EXT;
phy_conf.ext_io_conf = &ext_io_conf;
} else {
phy_conf.target = USB_PHY_TARGET_INT;
}
// OTG IOs config
const usb_phy_otg_io_conf_t otg_io_conf = USB_PHY_SELF_POWERED_DEVICE(config->vbus_monitor_io);
if (config->self_powered) {
phy_conf.otg_io_conf = &otg_io_conf;
}
ESP_RETURN_ON_ERROR(usb_new_phy(&phy_conf, &phy_hdl), TAG, "Install USB PHY failed");
if (config->configuration_descriptor) {
cfg_descriptor = config->configuration_descriptor;
} else {
#if (CONFIG_TINYUSB_HID_COUNT > 0 || CONFIG_TINYUSB_MIDI_COUNT > 0)
// For HID device, configuration descriptor must be provided
ESP_RETURN_ON_FALSE(config->configuration_descriptor, ESP_ERR_INVALID_ARG, TAG, "Configuration descriptor must be provided for this device");
#else
cfg_descriptor = descriptor_cfg_kconfig;
ESP_LOGW(TAG, "The device's configuration descriptor is not provided by user, using default.");
#endif
}
if (config->string_descriptor) {
string_descriptor = config->string_descriptor;
} else {
string_descriptor = descriptor_str_kconfig;
ESP_LOGW(TAG, "The device's string descriptor is not provided by user, using default.");
}
if (config->device_descriptor) {
dev_descriptor = config->device_descriptor;
} else {
dev_descriptor = &descriptor_dev_kconfig;
ESP_LOGW(TAG, "The device's device descriptor is not provided by user, using default.");
}
tusb_set_descriptor(dev_descriptor, string_descriptor, cfg_descriptor);
ESP_RETURN_ON_FALSE(tusb_init(), ESP_FAIL, TAG, "Init TinyUSB stack failed");
#if !CONFIG_TINYUSB_NO_DEFAULT_TASK
ESP_RETURN_ON_ERROR(tusb_run_task(), TAG, "Run TinyUSB task failed");
#endif
ESP_LOGI(TAG, "TinyUSB Driver installed");
return ESP_OK;
}
@@ -0,0 +1,415 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/ringbuf.h"
#include "tusb.h"
#include "tusb_cdc_acm.h"
#include "cdc.h"
#include "sdkconfig.h"
#define RX_UNREADBUF_SZ_DEFAULT 64 // buffer storing all unread RX data
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
typedef struct {
bool initialized;
size_t rx_unread_buf_sz;
RingbufHandle_t rx_unread_buf;
SemaphoreHandle_t ringbuf_read_mux;
uint8_t *rx_tfbuf;
tusb_cdcacm_callback_t callback_rx;
tusb_cdcacm_callback_t callback_rx_wanted_char;
tusb_cdcacm_callback_t callback_line_state_changed;
tusb_cdcacm_callback_t callback_line_coding_changed;
} esp_tusb_cdcacm_t; /*!< CDC_ACM object */
static const char *TAG = "tusb_cdc_acm";
static inline esp_tusb_cdcacm_t *get_acm(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
if (cdc_inst == NULL) {
return (esp_tusb_cdcacm_t *)NULL;
}
return (esp_tusb_cdcacm_t *)(cdc_inst->subclass_obj);
}
/* TinyUSB callbacks
********************************************************************* */
/* Invoked by cdc interface when line state changed e.g connected/disconnected */
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (dtr && rts) { // connected
if (acm != NULL) {
ESP_LOGV(TAG, "Host connected to CDC no.%d.", itf);
} else {
ESP_LOGW(TAG, "Host is connected to CDC no.%d, but it is not initialized. Initialize it using `tinyusb_cdc_init`.", itf);
return;
}
} else { // disconnected
if (acm != NULL) {
ESP_LOGV(TAG, "Serial device is ready to connect to CDC no.%d", itf);
} else {
return;
}
}
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_line_state_changed;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_LINE_STATE_CHANGED,
.line_state_changed_data = {
.dtr = dtr,
.rts = rts
}
};
cb(itf, &event);
}
}
}
/* Invoked when CDC interface received data from host */
void tud_cdc_rx_cb(uint8_t itf)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
if (!acm->rx_unread_buf) {
ESP_LOGE(TAG, "There is no RX buffer created");
abort();
}
} else {
tud_cdc_n_read_flush(itf); // we have no place to store data, so just drop it
return;
}
while (tud_cdc_n_available(itf)) {
int read_res = tud_cdc_n_read( itf,
acm->rx_tfbuf,
CONFIG_TINYUSB_CDC_RX_BUFSIZE );
int res = xRingbufferSend(acm->rx_unread_buf,
acm->rx_tfbuf,
read_res, 0);
if (res != pdTRUE) {
ESP_LOGW(TAG, "The unread buffer is too small, the data has been lost");
} else {
ESP_LOGV(TAG, "Sent %d bytes to the buffer", read_res);
}
}
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_rx;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_RX
};
cb(itf, &event);
}
}
}
// Invoked when line coding is change via SET_LINE_CODING
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_line_coding_changed;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_LINE_CODING_CHANGED,
.line_coding_changed_data = {
.p_line_coding = p_line_coding,
}
};
cb(itf, &event);
}
} else {
return;
}
}
// Invoked when received `wanted_char`
void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_rx_wanted_char;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_RX_WANTED_CHAR,
.rx_wanted_char_data = {
.wanted_char = wanted_char,
}
};
cb(itf, &event);
}
} else {
return;
}
}
esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type,
tusb_cdcacm_callback_t callback)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
switch (event_type) {
case CDC_EVENT_RX:
acm->callback_rx = callback;
return ESP_OK;
case CDC_EVENT_RX_WANTED_CHAR:
acm->callback_rx_wanted_char = callback;
return ESP_OK;
case CDC_EVENT_LINE_STATE_CHANGED:
acm->callback_line_state_changed = callback;
return ESP_OK;
case CDC_EVENT_LINE_CODING_CHANGED:
acm->callback_line_coding_changed = callback;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
} else {
ESP_LOGE(TAG, "CDC-ACM is not initialized");
return ESP_ERR_INVALID_STATE;
}
}
esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (!acm) {
ESP_LOGE(TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization");
return ESP_ERR_INVALID_STATE;
}
switch (event_type) {
case CDC_EVENT_RX:
acm->callback_rx = NULL;
return ESP_OK;
case CDC_EVENT_RX_WANTED_CHAR:
acm->callback_rx_wanted_char = NULL;
return ESP_OK;
case CDC_EVENT_LINE_STATE_CHANGED:
acm->callback_line_state_changed = NULL;
return ESP_OK;
case CDC_EVENT_LINE_CODING_CHANGED:
acm->callback_line_coding_changed = NULL;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
}
/*********************************************************************** TinyUSB callbacks*/
/* CDC-ACM
********************************************************************* */
static esp_err_t read_from_rx_unread_to_buffer(esp_tusb_cdcacm_t *acm, uint8_t *out_buf, size_t req_bytes, size_t *read_bytes)
{
uint8_t *buf = xRingbufferReceiveUpTo(acm->rx_unread_buf, read_bytes, 0, req_bytes);
if (buf) {
memcpy(out_buf, buf, *read_bytes);
vRingbufferReturnItem(acm->rx_unread_buf, (void *)(buf));
return ESP_OK;
} else {
return ESP_ERR_NO_MEM;
}
}
static esp_err_t ringbuf_mux_take(esp_tusb_cdcacm_t *acm)
{
if (xSemaphoreTake(acm->ringbuf_read_mux, 0) != pdTRUE) {
ESP_LOGW(TAG, "Read error: ACM is busy");
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
static esp_err_t ringbuf_mux_give(esp_tusb_cdcacm_t *acm)
{
BaseType_t ret = xSemaphoreGive(acm->ringbuf_read_mux);
assert(ret == pdTRUE);
return ESP_OK;
}
esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
ESP_RETURN_ON_FALSE(acm, ESP_ERR_INVALID_STATE, TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization");
size_t read_sz;
/* Take a mutex to proceed two uninterrupted read operations */
ESP_RETURN_ON_ERROR(ringbuf_mux_take(acm), TAG, "ringbuf_mux_take failed");
esp_err_t res = read_from_rx_unread_to_buffer(acm, out_buf, out_buf_sz, &read_sz);
if (res != ESP_OK) {
ESP_RETURN_ON_ERROR(ringbuf_mux_give(acm), TAG, "ringbuf_mux_give failed");
return res;
}
*rx_data_size = read_sz;
/* Buffer's data can be wrapped, at that situations we should make another retrievement */
if (read_from_rx_unread_to_buffer(acm, out_buf + read_sz, out_buf_sz - read_sz, &read_sz) == ESP_OK) {
*rx_data_size += read_sz;
}
ESP_RETURN_ON_ERROR(ringbuf_mux_give(acm), TAG, "ringbuf_mux_give failed");
return ESP_OK;
}
size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch)
{
if (!get_acm(itf)) { // non-initialized
return 0;
}
return tud_cdc_n_write_char(itf, ch);
}
size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, const uint8_t *in_buf, size_t in_size)
{
if (!get_acm(itf)) { // non-initialized
return 0;
}
const uint32_t size_available = tud_cdc_n_write_available(itf);
return tud_cdc_n_write(itf, in_buf, MIN(in_size, size_available));
}
static uint32_t tud_cdc_n_write_occupied(tinyusb_cdcacm_itf_t itf)
{
return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(itf);
}
esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks)
{
if (!get_acm(itf)) { // non-initialized
return ESP_FAIL;
}
if (!timeout_ticks) { // if no timeout - nonblocking mode
int res = tud_cdc_n_write_flush(itf);
if (!res) {
ESP_LOGW(TAG, "flush failed (res: %d)", res);
return ESP_FAIL;
} else {
if (tud_cdc_n_write_occupied(itf)) {
ESP_LOGW(TAG, "remained data to flush!");
return ESP_FAIL;
}
}
return ESP_ERR_TIMEOUT;
} else { // trying during the timeout
uint32_t ticks_start = xTaskGetTickCount();
uint32_t ticks_now = ticks_start;
while (1) { // loop until success or until the time runs out
ticks_now = xTaskGetTickCount();
if (!tud_cdc_n_write_occupied(itf)) { // if nothing to write - nothing to flush
break;
}
if (tud_cdc_n_write_flush(itf)) { // Success
break;
}
if ( (ticks_now - ticks_start) > timeout_ticks ) { // Time is up
ESP_LOGW(TAG, "Flush failed");
return ESP_ERR_TIMEOUT;
}
vTaskDelay(1);
}
return ESP_OK;
}
}
static esp_err_t alloc_obj(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
cdc_inst->subclass_obj = calloc(1, sizeof(esp_tusb_cdcacm_t));
if (!cdc_inst->subclass_obj) {
return ESP_FAIL;
} else {
return ESP_OK;
}
}
static void free_obj(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
free(cdc_inst->subclass_obj);
cdc_inst->subclass_obj = NULL;
}
esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg)
{
int itf = (int)cfg->cdc_port;
/* Creating a CDC object */
const tinyusb_config_cdc_t cdc_cfg = {
.usb_dev = cfg->usb_dev,
.cdc_class = TUSB_CLASS_CDC,
.cdc_subclass.comm_subclass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
};
ESP_RETURN_ON_ERROR(tinyusb_cdc_init(itf, &cdc_cfg), TAG, "tinyusb_cdc_init failed");
ESP_RETURN_ON_ERROR(alloc_obj(itf), TAG, "alloc_obj failed");
esp_tusb_cdcacm_t *acm = get_acm(itf);
/* Callbacks setting up*/
if (cfg->callback_rx) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX, cfg->callback_rx);
}
if (cfg->callback_rx_wanted_char) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX_WANTED_CHAR, cfg->callback_rx_wanted_char);
}
if (cfg->callback_line_state_changed) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_LINE_STATE_CHANGED, cfg->callback_line_state_changed);
}
if (cfg->callback_line_coding_changed) {
tinyusb_cdcacm_register_callback( itf, CDC_EVENT_LINE_CODING_CHANGED, cfg->callback_line_coding_changed);
}
/* Buffers */
acm->ringbuf_read_mux = xSemaphoreCreateMutex();
if (acm->ringbuf_read_mux == NULL) {
ESP_LOGE(TAG, "Creation of a ringbuf mutex failed");
free_obj(itf);
return ESP_ERR_NO_MEM;
}
acm->rx_tfbuf = malloc(CONFIG_TINYUSB_CDC_RX_BUFSIZE);
if (!acm->rx_tfbuf) {
ESP_LOGE(TAG, "Creation buffer error");
free_obj(itf);
return ESP_ERR_NO_MEM;
}
acm->rx_unread_buf_sz = cfg->rx_unread_buf_sz == 0 ? RX_UNREADBUF_SZ_DEFAULT : cfg->rx_unread_buf_sz;
acm->rx_unread_buf = xRingbufferCreate(acm->rx_unread_buf_sz, RINGBUF_TYPE_BYTEBUF);
if (acm->rx_unread_buf == NULL) {
ESP_LOGE(TAG, "Creation buffer error");
free_obj(itf);
return ESP_ERR_NO_MEM;
} else {
ESP_LOGD(TAG, "Comm Initialized buff:%d bytes", cfg->rx_unread_buf_sz);
return ESP_OK;
}
}
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
return true;
} else {
return false;
}
}
/*********************************************************************** CDC-ACM*/
@@ -0,0 +1,115 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdio_ext.h>
#include "esp_log.h"
#include "cdc.h"
#include "tusb_console.h"
#include "tinyusb.h"
#include "vfs_tinyusb.h"
#include "esp_check.h"
#define STRINGIFY(s) STRINGIFY2(s)
#define STRINGIFY2(s) #s
static const char *TAG = "tusb_console";
typedef struct {
FILE *in;
FILE *out;
FILE *err;
} console_handle_t;
static console_handle_t con;
/**
* @brief Reopen standard streams using a new path
*
* @param f_in - pointer to a pointer holding a file for in or NULL to don't change stdin
* @param f_out - pointer to a pointer holding a file for out or NULL to don't change stdout
* @param f_err - pointer to a pointer holding a file for err or NULL to don't change stderr
* @param path - mount point
* @return esp_err_t ESP_FAIL or ESP_OK
*/
static esp_err_t redirect_std_streams_to(FILE **f_in, FILE **f_out, FILE **f_err, const char *path)
{
if (f_in) {
*f_in = freopen(path, "r", stdin);
if (*f_in == NULL) {
ESP_LOGE(TAG, "Failed to reopen in!");
return ESP_FAIL;
}
}
if (f_out) {
*f_out = freopen(path, "w", stdout);
if (*f_out == NULL) {
ESP_LOGE(TAG, "Failed to reopen out!");
return ESP_FAIL;
}
}
if (f_err) {
*f_err = freopen(path, "w", stderr);
if (*f_err == NULL) {
ESP_LOGE(TAG, "Failed to reopen err!");
return ESP_FAIL;
}
}
return ESP_OK;
}
/**
* @brief Restore output to default
*
* @param f_in - pointer to a pointer of an in file updated with `redirect_std_streams_to` or NULL to don't change stdin
* @param f_out - pointer to a pointer of an out file updated with `redirect_std_streams_to` or NULL to don't change stdout
* @param f_err - pointer to a pointer of an err file updated with `redirect_std_streams_to` or NULL to don't change stderr
* @return esp_err_t ESP_FAIL or ESP_OK
*/
static esp_err_t restore_std_streams(FILE **f_in, FILE **f_out, FILE **f_err)
{
const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM);
if (f_in) {
stdin = freopen(default_uart_dev, "r", *f_in);
if (stdin == NULL) {
ESP_LOGE(TAG, "Failed to reopen stdin!");
return ESP_FAIL;
}
}
if (f_out) {
stdout = freopen(default_uart_dev, "w", *f_out);
if (stdout == NULL) {
ESP_LOGE(TAG, "Failed to reopen stdout!");
return ESP_FAIL;
}
}
if (f_err) {
stderr = freopen(default_uart_dev, "w", *f_err);
if (stderr == NULL) {
ESP_LOGE(TAG, "Failed to reopen stderr!");
return ESP_FAIL;
}
}
return ESP_OK;
}
esp_err_t esp_tusb_init_console(int cdc_intf)
{
/* Registering TUSB at VFS */
ESP_RETURN_ON_ERROR(esp_vfs_tusb_cdc_register(cdc_intf, NULL), TAG, "");
ESP_RETURN_ON_ERROR(redirect_std_streams_to(&con.in, &con.out, &con.err, "/dev/tusb_cdc"), TAG, "Failed to redirect STD streams");
return ESP_OK;
}
esp_err_t esp_tusb_deinit_console(int cdc_intf)
{
ESP_RETURN_ON_ERROR(restore_std_streams(&con.in, &con.out, &con.err), TAG, "Failed to restore STD streams");
esp_vfs_tusb_cdc_unregister(NULL);
return ESP_OK;
}
@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_check.h"
#include "tinyusb.h"
#include "tusb_tasks.h"
const static char *TAG = "tusb_tsk";
static TaskHandle_t s_tusb_tskh;
/**
* @brief This top level thread processes all usb events and invokes callbacks
*/
static void tusb_device_task(void *arg)
{
ESP_LOGD(TAG, "tinyusb task started");
while (1) { // RTOS forever loop
tud_task();
}
}
esp_err_t tusb_run_task(void)
{
// This function is not garanteed to be thread safe, if invoked multiple times without calling `tusb_stop_task`, will cause memory leak
// doing a sanity check anyway
ESP_RETURN_ON_FALSE(!s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task already started");
// Create a task for tinyusb device stack:
xTaskCreate(tusb_device_task, "TinyUSB", CONFIG_TINYUSB_TASK_STACK_SIZE, NULL, CONFIG_TINYUSB_TASK_PRIORITY, &s_tusb_tskh);
ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_FAIL, TAG, "create TinyUSB main task failed");
return ESP_OK;
}
esp_err_t tusb_stop_task(void)
{
ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task not started yet");
vTaskDelete(s_tusb_tskh);
s_tusb_tskh = NULL;
return ESP_OK;
}
@@ -0,0 +1,186 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usb_descriptors.h"
#include "sdkconfig.h"
/*
* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) ) //| _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
/**** TinyUSB default ****/
tusb_desc_device_t descriptor_tinyusb = {
.bLength = sizeof(descriptor_tinyusb),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_TUSB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
tusb_desc_strarray_device_t descriptor_str_tinyusb = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials, should use chip ID
};
/* End of TinyUSB default */
/**** Kconfig driven Descriptor ****/
const tusb_desc_device_t descriptor_dev_kconfig = {
.bLength = sizeof(descriptor_dev_kconfig),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
#if CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID
.idVendor = USB_ESPRESSIF_VID,
#else
.idVendor = CONFIG_TINYUSB_DESC_CUSTOM_VID,
#endif
#if CONFIG_TINYUSB_DESC_USE_DEFAULT_PID
.idProduct = USB_TUSB_PID,
#else
.idProduct = CONFIG_TINYUSB_DESC_CUSTOM_PID,
#endif
.bcdDevice = CONFIG_TINYUSB_DESC_BCD_DEVICE,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
tusb_desc_strarray_device_t descriptor_str_kconfig = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, // 1: Manufacturer
CONFIG_TINYUSB_DESC_PRODUCT_STRING, // 2: Product
CONFIG_TINYUSB_DESC_SERIAL_STRING, // 3: Serials, should use chip ID
#if CONFIG_TINYUSB_CDC_ENABLED
CONFIG_TINYUSB_DESC_CDC_STRING, // 4: CDC Interface
#else
"",
#endif
#if CONFIG_TINYUSB_MSC_ENABLED
CONFIG_TINYUSB_DESC_MSC_STRING, // 5: MSC Interface
#else
"",
#endif
};
//------------- Configuration Descriptor -------------//
enum {
#if CFG_TUD_CDC
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
#endif
#if CFG_TUD_CDC > 1
ITF_NUM_CDC1,
ITF_NUM_CDC1_DATA,
#endif
#if CFG_TUD_MSC
ITF_NUM_MSC,
#endif
ITF_NUM_TOTAL
};
enum {
TUSB_DESC_TOTAL_LEN = TUD_CONFIG_DESC_LEN +
CFG_TUD_CDC * TUD_CDC_DESC_LEN +
CFG_TUD_MSC * TUD_MSC_DESC_LEN
};
//------------- USB Endpoint numbers -------------//
enum {
// Available USB Endpoints: 5 IN/OUT EPs and 1 IN EP
EP_EMPTY = 0,
#if CFG_TUD_CDC
EPNUM_0_CDC_NOTIF,
EPNUM_0_CDC,
#endif
#if CFG_TUD_CDC > 1
EPNUM_1_CDC_NOTIF,
EPNUM_1_CDC,
#endif
#if CFG_TUD_MSC
EPNUM_MSC,
#endif
};
uint8_t const descriptor_cfg_kconfig[] = {
// Configuration number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
#if CFG_TUD_CDC
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x80 | EPNUM_0_CDC_NOTIF, 8, EPNUM_0_CDC, 0x80 | EPNUM_0_CDC, CFG_TUD_CDC_EP_BUFSIZE),
#endif
#if CFG_TUD_CDC > 1
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC1, 4, 0x80 | EPNUM_1_CDC_NOTIF, 8, EPNUM_1_CDC, 0x80 | EPNUM_1_CDC, CFG_TUD_CDC_EP_BUFSIZE),
#endif
#if CFG_TUD_MSC
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
#endif
};
/* End of Kconfig driven Descriptor */
@@ -0,0 +1,289 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "vfs_tinyusb.h"
#include "sdkconfig.h"
const static char *TAG = "tusb_vfs";
#define VFS_TUSB_MAX_PATH 16
#define VFS_TUSB_PATH_DEFAULT "/dev/tusb_cdc"
// Token signifying that no character is available
#define NONE -1
#define FD_CHECK(fd, ret_val) do { \
if ((fd) != 0) { \
errno = EBADF; \
return (ret_val); \
} \
} while (0)
#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CRLF
#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CR
#else
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_LF
#endif
#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CRLF
#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CR
#else
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF
#endif
typedef struct {
_lock_t write_lock;
_lock_t read_lock;
esp_line_endings_t tx_mode; // Newline conversion mode when transmitting
esp_line_endings_t rx_mode; // Newline conversion mode when receiving
uint32_t flags;
char vfs_path[VFS_TUSB_MAX_PATH];
int cdc_intf;
} vfs_tinyusb_t;
static vfs_tinyusb_t s_vfstusb;
static esp_err_t apply_path(char const *path)
{
if (path != NULL) {
size_t path_len = strlen(path) + 1;
if (path_len > VFS_TUSB_MAX_PATH) {
ESP_LOGE(TAG, "The path is too long; maximum is %d characters", VFS_TUSB_MAX_PATH);
return ESP_ERR_INVALID_ARG;
}
strncpy(s_vfstusb.vfs_path, path, (VFS_TUSB_MAX_PATH - 1));
} else {
strncpy(s_vfstusb.vfs_path,
VFS_TUSB_PATH_DEFAULT,
(VFS_TUSB_MAX_PATH - 1));
}
ESP_LOGV(TAG, "Path is set to `%s`", s_vfstusb.vfs_path);
return ESP_OK;
}
/**
* @brief Fill s_vfstusb
*
* @param cdc_intf - interface of tusb for registration
* @param path - a path where the CDC will be registered
* @return esp_err_t ESP_OK or ESP_ERR_INVALID_ARG
*/
static esp_err_t vfstusb_init(int cdc_intf, char const *path)
{
s_vfstusb.cdc_intf = cdc_intf;
s_vfstusb.tx_mode = DEFAULT_TX_MODE;
s_vfstusb.rx_mode = DEFAULT_RX_MODE;
return apply_path(path);
}
/**
* @brief Clear s_vfstusb to default values
*/
static void vfstusb_deinit(void)
{
memset(&s_vfstusb, 0, sizeof(s_vfstusb));
}
static int tusb_open(const char *path, int flags, int mode)
{
(void) mode;
(void) path;
s_vfstusb.flags = flags | O_NONBLOCK; // for now only non-blocking mode is implemented
return 0;
}
static ssize_t tusb_write(int fd, const void *data, size_t size)
{
FD_CHECK(fd, -1);
size_t written_sz = 0;
const char *data_c = (const char *)data;
_lock_acquire(&(s_vfstusb.write_lock));
for (size_t i = 0; i < size; i++) {
int c = data_c[i];
/* handling the EOL */
if (c == '\n' && s_vfstusb.tx_mode != ESP_LINE_ENDINGS_LF) {
if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, '\r')) {
written_sz++;
} else {
break; // can't write anymore
}
if (s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CR) {
continue;
}
}
/* write a char */
if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, c)) {
written_sz++;
} else {
break; // can't write anymore
}
}
tud_cdc_n_write_flush(s_vfstusb.cdc_intf);
_lock_release(&(s_vfstusb.write_lock));
return written_sz;
}
static int tusb_close(int fd)
{
FD_CHECK(fd, -1);
return 0;
}
static ssize_t tusb_read(int fd, void *data, size_t size)
{
FD_CHECK(fd, -1);
char *data_c = (char *) data;
size_t received = 0;
_lock_acquire(&(s_vfstusb.read_lock));
int cm1 = NONE;
int c = NONE;
while (received < size) {
cm1 = c; // store the old char
int c = tud_cdc_n_read_char(0); // get a new one
if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
if (c == '\r') {
c = '\n';
}
} else if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
if ((c == '\n') & (cm1 == '\r')) {
--received; // step back
c = '\n';
}
}
if ( c == NONE) { // if data ends
break;
}
data_c[received] = (char) c;
++received;
if (c == '\n') {
break;
}
}
_lock_release(&(s_vfstusb.read_lock));
if (received > 0) {
return received;
}
errno = EWOULDBLOCK;
return -1;
}
static int tusb_fstat(int fd, struct stat *st)
{
FD_CHECK(fd, -1);
memset(st, 0, sizeof(*st));
st->st_mode = S_IFCHR;
return 0;
}
static int tusb_fcntl(int fd, int cmd, int arg)
{
FD_CHECK(fd, -1);
int result = 0;
switch (cmd) {
case F_GETFL:
result = s_vfstusb.flags;
break;
case F_SETFL:
s_vfstusb.flags = arg;
break;
default:
result = -1;
errno = ENOSYS;
break;
}
return result;
}
esp_err_t esp_vfs_tusb_cdc_unregister(char const *path)
{
ESP_LOGD(TAG, "Unregistering TinyUSB driver");
int res;
if (path == NULL) { // NULL means using the default path for unregistering: VFS_TUSB_PATH_DEFAULT
res = strcmp(s_vfstusb.vfs_path, VFS_TUSB_PATH_DEFAULT);
} else {
res = strcmp(s_vfstusb.vfs_path, path);
}
if (res) {
res = ESP_ERR_INVALID_ARG;
ESP_LOGE(TAG, "There is no TinyUSB driver registerred to the path '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
return res;
}
res = esp_vfs_unregister(s_vfstusb.vfs_path);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Can't unregister TinyUSB driver from '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
} else {
ESP_LOGD(TAG, "Unregistered TinyUSB driver");
vfstusb_deinit();
}
return res;
}
esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path)
{
ESP_LOGD(TAG, "Registering TinyUSB CDC driver");
int res;
if (!tusb_cdc_acm_initialized(cdc_intf)) {
ESP_LOGE(TAG, "TinyUSB CDC#%d is not initialized", cdc_intf);
return ESP_ERR_INVALID_STATE;
}
res = vfstusb_init(cdc_intf, path);
if (res != ESP_OK) {
return res;
}
esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.close = &tusb_close,
.fcntl = &tusb_fcntl,
.fstat = &tusb_fstat,
.open = &tusb_open,
.read = &tusb_read,
.write = &tusb_write,
};
res = esp_vfs_register(s_vfstusb.vfs_path, &vfs, NULL);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Can't register TinyUSB driver (err: %x)", res);
} else {
ESP_LOGD(TAG, "TinyUSB CDC registered (%s)", s_vfstusb.vfs_path);
}
return res;
}