From 84aa1c0cc3f684a16fbcc6f2250da44809095204 Mon Sep 17 00:00:00 2001 From: Andrei Gramakov Date: Thu, 6 Aug 2020 14:41:32 +0200 Subject: [PATCH] usb: cdc support, streams redirection; ci, examples upd tusb: cdc, tasks encapsulation, callbacks api, multiple interfaces examples: added serial interface and usb console ci: reimplemented cmake/make test in python with ignore lists IDF-578 --- components/tinyusb/CMakeLists.txt | 89 +- components/tinyusb/Kconfig | 80 +- .../esp32s2 => additions}/include/tinyusb.h | 9 +- .../include/tinyusb_types.h} | 22 +- .../tinyusb/additions/include/tusb_cdc_acm.h | 197 +++++ .../include/tusb_config.h | 64 +- .../tinyusb/additions/include/tusb_console.h | 33 + .../tinyusb/additions/include/tusb_tasks.h | 44 + .../tinyusb/additions/include/vfs_tinyusb.h | 42 + .../tinyusb/additions/include_private/cdc.h | 99 +++ .../include_private}/descriptors_control.h | 2 +- .../include_private/usb_descriptors.h} | 32 +- components/tinyusb/additions/src/cdc.c | 162 ++++ .../src/descriptors_control.c | 41 +- .../{port/esp32s2 => additions}/src/tinyusb.c | 30 +- .../tinyusb/additions/src/tusb_cdc_acm.c | 412 +++++++++ .../tinyusb/additions/src/tusb_console.c | 142 +++ components/tinyusb/additions/src/tusb_tasks.c | 62 ++ .../src/usb_descriptors.c | 10 +- .../tinyusb/additions/src/vfs_tinyusb.c | 296 +++++++ components/tinyusb/port/common/src/usbd.c | 830 ------------------ .../esp32s2/src/device_controller_driver.c | 725 --------------- components/tinyusb/tinyusb | 2 +- .../usb/tusb_console/CMakeLists.txt | 6 + .../peripherals/usb/tusb_console/README.md | 87 ++ .../usb/tusb_console/main/CMakeLists.txt | 2 + .../usb/tusb_console/main/tusb_console_main.c | 58 ++ .../usb/tusb_console/sdkconfig.defaults | 3 + .../usb/tusb_sample_descriptor/README.md | 34 +- .../main/CMakeLists.txt | 4 +- ...riptor.c => tusb_sample_descriptor_main.c} | 25 +- .../usb/tusb_serial_device/CMakeLists.txt | 6 + .../usb/tusb_serial_device/README.md | 79 ++ .../tusb_serial_device/main/CMakeLists.txt | 2 + .../main/tusb_serial_device_main.c | 74 ++ .../usb/tusb_serial_device/sdkconfig.defaults | 3 + ...check_examples_cmake_make-cmake_ignore.txt | 6 + .../check_examples_cmake_make-make_ignore.txt | 2 + tools/ci/check_examples_cmake_make.py | 120 +++ tools/ci/check_examples_cmake_make.sh | 71 -- tools/ci/config/pre_check.yml | 2 +- tools/ci/executable-list.txt | 2 +- 42 files changed, 2207 insertions(+), 1804 deletions(-) rename components/tinyusb/{port/esp32s2 => additions}/include/tinyusb.h (94%) rename components/tinyusb/{port/common/include/usb_descriptors.h => additions/include/tinyusb_types.h} (70%) create mode 100644 components/tinyusb/additions/include/tusb_cdc_acm.h rename components/tinyusb/{port/esp32s2 => additions}/include/tusb_config.h (82%) create mode 100644 components/tinyusb/additions/include/tusb_console.h create mode 100644 components/tinyusb/additions/include/tusb_tasks.h create mode 100644 components/tinyusb/additions/include/vfs_tinyusb.h create mode 100644 components/tinyusb/additions/include_private/cdc.h rename components/tinyusb/{port/common/include => additions/include_private}/descriptors_control.h (93%) rename components/tinyusb/{port/esp32s2/include/device_controller_driver.h => additions/include_private/usb_descriptors.h} (56%) create mode 100644 components/tinyusb/additions/src/cdc.c rename components/tinyusb/{port/common => additions}/src/descriptors_control.c (85%) rename components/tinyusb/{port/esp32s2 => additions}/src/tinyusb.c (79%) create mode 100644 components/tinyusb/additions/src/tusb_cdc_acm.c create mode 100644 components/tinyusb/additions/src/tusb_console.c create mode 100644 components/tinyusb/additions/src/tusb_tasks.c rename components/tinyusb/{port/common => additions}/src/usb_descriptors.c (97%) create mode 100644 components/tinyusb/additions/src/vfs_tinyusb.c delete mode 100644 components/tinyusb/port/common/src/usbd.c delete mode 100644 components/tinyusb/port/esp32s2/src/device_controller_driver.c create mode 100644 examples/peripherals/usb/tusb_console/CMakeLists.txt create mode 100644 examples/peripherals/usb/tusb_console/README.md create mode 100644 examples/peripherals/usb/tusb_console/main/CMakeLists.txt create mode 100644 examples/peripherals/usb/tusb_console/main/tusb_console_main.c create mode 100644 examples/peripherals/usb/tusb_console/sdkconfig.defaults rename examples/peripherals/usb/tusb_sample_descriptor/main/{tusb_sample_descriptor.c => tusb_sample_descriptor_main.c} (82%) create mode 100644 examples/peripherals/usb/tusb_serial_device/CMakeLists.txt create mode 100644 examples/peripherals/usb/tusb_serial_device/README.md create mode 100644 examples/peripherals/usb/tusb_serial_device/main/CMakeLists.txt create mode 100644 examples/peripherals/usb/tusb_serial_device/main/tusb_serial_device_main.c create mode 100644 examples/peripherals/usb/tusb_serial_device/sdkconfig.defaults create mode 100644 tools/ci/check_examples_cmake_make-cmake_ignore.txt create mode 100644 tools/ci/check_examples_cmake_make-make_ignore.txt create mode 100644 tools/ci/check_examples_cmake_make.py delete mode 100755 tools/ci/check_examples_cmake_make.sh diff --git a/components/tinyusb/CMakeLists.txt b/components/tinyusb/CMakeLists.txt index fc8dd50190..db9369a17f 100644 --- a/components/tinyusb/CMakeLists.txt +++ b/components/tinyusb/CMakeLists.txt @@ -1,37 +1,64 @@ -idf_component_register(REQUIRES esp_rom freertos soc driver) +idf_component_register(REQUIRES esp_rom freertos vfs soc) if(CONFIG_USB_ENABLED) - idf_component_get_property( FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH) - target_compile_options(${COMPONENT_TARGET} INTERFACE - "-DCFG_TUSB_MCU=OPT_MCU_ESP32_S2" - ) + ### variables ### + ################# + set(compile_options + "-DCFG_TUSB_MCU=OPT_MCU_ESP32S2" + "-DCFG_TUSB_DEBUG=${CONFIG_USB_DEBUG_LEVEL}" + "-Wno-type-limits" # needed for the vanila tinyusb with turned off classes + ) + idf_component_get_property(FREERTOS_ORIG_INCLUDE_PATH freertos + ORIG_INCLUDE_PATH) + set(includes_private + # tusb: + "${COMPONENT_DIR}/tinyusb/hw/bsp/" + "${COMPONENT_DIR}/tinyusb/src/" + "${COMPONENT_DIR}/tinyusb/src/device" + # espressif: + "${COMPONENT_DIR}/additions/include_private" + ) - target_include_directories(${COMPONENT_TARGET} INTERFACE - "${FREERTOS_ORIG_INCLUDE_PATH}" - # espressif: - "${COMPONENT_DIR}/port/esp32s2/include/" - "${COMPONENT_DIR}/port/common/include" - # tusb: - "${COMPONENT_DIR}/tinyusb/hw/bsp/" - "${COMPONENT_DIR}/tinyusb/src/" - "${COMPONENT_DIR}/tinyusb/src/device" - ) + set(includes_public + # tusb: + "${FREERTOS_ORIG_INCLUDE_PATH}" + "${COMPONENT_DIR}/tinyusb/src/" + # espressif: + "${COMPONENT_DIR}/additions/include") + set(srcs + # espressif: + "${COMPONENT_DIR}/additions/src/descriptors_control.c" + "${COMPONENT_DIR}/additions/src/tinyusb.c" + "${COMPONENT_DIR}/additions/src/tusb_tasks.c" + "${COMPONENT_DIR}/additions/src/usb_descriptors.c" + # tusb: + "${COMPONENT_DIR}/tinyusb/src/portable/espressif/esp32s2/dcd_esp32s2.c" + "${COMPONENT_DIR}/tinyusb/src/class/cdc/cdc_device.c" + "${COMPONENT_DIR}/tinyusb/src/class/hid/hid_device.c" + "${COMPONENT_DIR}/tinyusb/src/class/midi/midi_device.c" + "${COMPONENT_DIR}/tinyusb/src/class/msc/msc_device.c" + "${COMPONENT_DIR}/tinyusb/src/common/tusb_fifo.c" + "${COMPONENT_DIR}/tinyusb/src/device/usbd_control.c" + "${COMPONENT_DIR}/tinyusb/src/device/usbd.c" + "${COMPONENT_DIR}/tinyusb/src/tusb.c") + # cdc stuff if turned on + if(CONFIG_USB_CDC_ENABLED) + list(APPEND srcs + "${COMPONENT_DIR}/additions/src/cdc.c" + "${COMPONENT_DIR}/additions/src/tusb_cdc_acm.c" + "${COMPONENT_DIR}/additions/src/tusb_console.c" + "${COMPONENT_DIR}/additions/src/vfs_tinyusb.c") + endif() + + ### tinyusb lib ### + ################### + add_library(tinyusb STATIC ${srcs}) + target_include_directories( + tinyusb + PUBLIC ${includes_public} + PRIVATE ${includes_private}) + target_compile_options(tinyusb PRIVATE ${compile_options}) + target_link_libraries(${COMPONENT_TARGET} INTERFACE tinyusb) - target_sources(${COMPONENT_TARGET} INTERFACE - # espressif: - "${COMPONENT_DIR}/port/common/src/descriptors_control.c" - "${COMPONENT_DIR}/port/common/src/usb_descriptors.c" - "${COMPONENT_DIR}/port/common/src/usbd.c" - "${COMPONENT_DIR}/port/esp32s2/src/device_controller_driver.c" - "${COMPONENT_DIR}/port/esp32s2/src/tinyusb.c" - # tusb: - "${COMPONENT_DIR}/tinyusb/src/common/tusb_fifo.c" - "${COMPONENT_DIR}/tinyusb/src/device/usbd_control.c" - "${COMPONENT_DIR}/tinyusb/src/class/msc/msc_device.c" - "${COMPONENT_DIR}/tinyusb/src/class/cdc/cdc_device.c" - "${COMPONENT_DIR}/tinyusb/src/class/hid/hid_device.c" - "${COMPONENT_DIR}/tinyusb/src/class/midi/midi_device.c" - "${COMPONENT_DIR}/tinyusb/src/tusb.c" - ) endif() diff --git a/components/tinyusb/Kconfig b/components/tinyusb/Kconfig index e8184f3a35..ab031a1145 100644 --- a/components/tinyusb/Kconfig +++ b/components/tinyusb/Kconfig @@ -9,19 +9,31 @@ menu "TinyUSB" help Adds support for TinyUSB - config USB_DEBUG - bool "Debug mode" - default n + menu "USB task configuration" depends on USB_ENABLED - help - Debug mode + + config USB_DO_NOT_CREATE_TASK + bool "Do not create a TinyUSB task" + default n + help + This option allows to not create the FreeRTOS task during the driver initialization. User will have + to handle TinyUSB events manually + + config USB_TASK_PRIORITY + int "Set a priority of the TinyUSB task" + default 5 + depends on !USB_DO_NOT_CREATE_TASK + help + User can change the priority of the main task according the application needs + + endmenu menu "Descriptor configuration" + depends on USB_ENABLED config USB_DESC_USE_ESPRESSIF_VID bool "VID: Use an Espressif's default value" default y - depends on USB_ENABLED help Long description @@ -35,7 +47,6 @@ menu "TinyUSB" config USB_DESC_USE_DEFAULT_PID bool "PID: Use a default PID assigning" default y - depends on USB_ENABLED help Default TinyUSB PID assigning uses values 0x4000...0x4007 @@ -49,31 +60,78 @@ menu "TinyUSB" config USB_DESC_BCDDEVICE hex "bcdDevice" default 0x0100 - depends on USB_ENABLED help Version of the firmware of the USB device config USB_DESC_MANUFACTURER_STRING string "Manufacturer" default "Espressif Systems" - depends on USB_ENABLED help Name of the manufacturer of the USB device config USB_DESC_PRODUCT_STRING string "Product" default "Espressif Device" - depends on USB_ENABLED help Name of the USB device config USB_DESC_SERIAL_STRING string "Serial string" default "123456" - depends on USB_ENABLED help Specify serial number of the USB device + config USB_DESC_CDC_STRING + string "CDC Device String" + default "Espressif CDC Device" + depends on USB_CDC_ENABLED + help + Specify name of the CDC device + + config USB_DESC_MSC_STRING + string "MSC Device String" + default "Espressif MSC Device" + depends on USB_MSC_ENABLED + help + Specify name of the MSC device + + config USB_DESC_HID_STRING + string "HID Device String" + default "Espressif HID Device" + depends on USB_HID_ENABLED + help + Specify name of the HID device + endmenu + config USB_CDC_ENABLED + bool "Enable USB Serial (CDC) TinyUSB driver" + default n + depends on USB_ENABLED + help + Enable USB Serial (CDC) TinyUSB driver. + + config USB_CDC_RX_BUFSIZE + int "CDC FIFO size of RX" + default 64 + depends on USB_CDC_ENABLED + help + CDC FIFO size of RX + + config USB_CDC_TX_BUFSIZE + int "CDC FIFO size of TX" + default 64 + depends on USB_CDC_ENABLED + help + CDC FIFO size of TX + + + config USB_DEBUG_LEVEL + int "TinyUSB log level (0-3)" + default 0 + range 0 3 + depends on USB_ENABLED + help + Define amount of log output from TinyUSB + endmenu diff --git a/components/tinyusb/port/esp32s2/include/tinyusb.h b/components/tinyusb/additions/include/tinyusb.h similarity index 94% rename from components/tinyusb/port/esp32s2/include/tinyusb.h rename to components/tinyusb/additions/include/tinyusb.h index 5fb03954b4..84f6e453ce 100644 --- a/components/tinyusb/port/esp32s2/include/tinyusb.h +++ b/components/tinyusb/additions/include/tinyusb.h @@ -15,10 +15,11 @@ #pragma once #include -#include "descriptors_control.h" -#include "board.h" #include "tusb.h" +#include "tusb_option.h" #include "tusb_config.h" +#include "tinyusb_types.h" + #ifdef __cplusplus extern "C" { @@ -66,6 +67,9 @@ extern "C" { # endif #endif +/** + * @brief Configuration structure of the tinyUSB core + */ typedef struct { tusb_desc_device_t *descriptor; char **string_descriptor; @@ -75,6 +79,7 @@ typedef struct { esp_err_t tinyusb_driver_install(const tinyusb_config_t *config); // TODO esp_err_t tinyusb_driver_uninstall(void); (IDF-1474) + #ifdef __cplusplus } #endif diff --git a/components/tinyusb/port/common/include/usb_descriptors.h b/components/tinyusb/additions/include/tinyusb_types.h similarity index 70% rename from components/tinyusb/port/common/include/usb_descriptors.h rename to components/tinyusb/additions/include/tinyusb_types.h index 15cc3bd69d..ad4a22de26 100644 --- a/components/tinyusb/port/common/include/usb_descriptors.h +++ b/components/tinyusb/additions/include/tinyusb_types.h @@ -1,4 +1,4 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,17 +14,19 @@ #pragma once -#include "tusb.h" - -#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#ifdef __cplusplus +extern "C" { +#endif #define USB_ESPRESSIF_VID 0x303A - #define USB_STRING_DESCRIPTOR_ARRAY_SIZE 7 + +typedef enum{ + TINYUSB_USBDEV_0, +} tinyusb_usbdev_t; + typedef char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; -tusb_desc_device_t descriptor_tinyusb; -tusb_desc_strarray_device_t descriptor_str_tinyusb; - -tusb_desc_device_t descriptor_kconfig; -tusb_desc_strarray_device_t descriptor_str_kconfig; +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/tusb_cdc_acm.h b/components/tinyusb/additions/include/tusb_cdc_acm.h new file mode 100644 index 0000000000..f17f375d10 --- /dev/null +++ b/components/tinyusb/additions/include/tusb_cdc_acm.h @@ -0,0 +1,197 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/ringbuf.h" +#include "freertos/semphr.h" +#include "freertos/timers.h" +#include "tusb.h" +#include "tinyusb.h" + +/** + * @brief CDC ports available to setup + */ +typedef enum{ + TINYUSB_CDC_ACM_0 = 0x0 +}tinyusb_cdcacm_itf_t; + +/* Callbacks and events + ********************************************************************* */ + +/** + * @brief Data provided to the input of the `callback_rx_wanted_char` callback + */ +typedef struct { + char wanted_char; +} cdcacm_event_rx_wanted_char_data_t; + +/** + * @brief Data provided to the input of the `callback_line_state_changed` callback + */ +typedef struct { + bool dtr; + bool rts; +} cdcacm_event_line_state_changed_data_t; + +/** + * @brief Data provided to the input of the `line_coding_changed` callback + */ +typedef struct { + cdc_line_coding_t const *p_line_coding; +} cdcacm_event_line_coding_changed_data_t; + +/** + * @brief Types of CDC ACM events + */ +typedef enum { + CDC_EVENT_RX, + CDC_EVENT_RX_WANTED_CHAR, + CDC_EVENT_LINE_STATE_CHANGED, + CDC_EVENT_LINE_CODING_CHANGED +} cdcacm_event_type_t; + +/** + * @brief Describes an event passing to the input of a callbacks + */ +typedef struct { + cdcacm_event_type_t type; + union { + cdcacm_event_rx_wanted_char_data_t rx_wanted_char_data; + cdcacm_event_line_state_changed_data_t line_state_changed_data; + cdcacm_event_line_coding_changed_data_t line_coding_changed_data; + }; +} cdcacm_event_t; + +/** + * @brief CDC-ACM callback type + */ +typedef void(*tusb_cdcacm_callback_t)(int itf, cdcacm_event_t *event); + +/*********************************************************************** Callbacks and events*/ +/* Other structs + ********************************************************************* */ + +/** + * @brief Configuration structure for CDC-ACM + */ +typedef struct { + tinyusb_usbdev_t usb_dev; /*!< Usb device to set up */ + tinyusb_cdcacm_itf_t cdc_port; /*!< CDC port */ + size_t rx_unread_buf_sz; /*!< Amount of data that can be passed to the AMC at once */ + tusb_cdcacm_callback_t callback_rx; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ + tusb_cdcacm_callback_t callback_rx_wanted_char; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ + tusb_cdcacm_callback_t callback_line_state_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ + tusb_cdcacm_callback_t callback_line_coding_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */ +} tinyusb_config_cdcacm_t; + +/*********************************************************************** Other structs*/ +/* Public functions + ********************************************************************* */ +/** + * @brief Initialize CDC ACM. Initialization will be finished with + * the `tud_cdc_line_state_cb` callback + * + * @param cfg - init configuration structure + * @return esp_err_t + */ +esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg); + + +/** + * @brief Register a callback invoking on CDC event. If the callback had been + * already registered, it will be overwritten + * + * @param itf - number of a CDC object + * @param event_type - type of registered event for a callback + * @param callback - callback function + * @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + */ +esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf, + cdcacm_event_type_t event_type, + tusb_cdcacm_callback_t callback); + + +/** + * @brief Unregister a callback invoking on CDC event. + * + * @param itf - number of a CDC object + * @param event_type - type of registered event for a callback + * @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + */ +esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf, cdcacm_event_type_t event_type); + + +/** + * @brief Sent one character to a write buffer + * + * @param itf - number of a CDC object + * @param ch - character to send + * @return size_t - amount of queued bytes + */ +size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch); + + +/** + * @brief Write data to write buffer from a byte array + * + * @param itf - number of a CDC object + * @param in_buf - a source array + * @param in_size - size to write from arr_src + * @return size_t - amount of queued bytes + */ +size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, uint8_t *in_buf, size_t in_size); + +/** + * @brief Send all data from a write buffer. Use `tinyusb_cdcacm_write_queue` to add data to the buffer + * + * @param itf - number of a CDC object + * @param timeout_ticks - waiting until flush will be considered as failed + * @return esp_err_t - ESP_OK if (timeout_ticks > 0) and and flush was successful, + * ESP_ERR_TIMEOUT if timeout occurred3 or flush was successful with (timeout_ticks == 0) + * ESP_FAIL if flush was unsuccessful + */ +esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks); + +/** + * @brief Read a content to the array, and defines it's size to the sz_store + * + * @param itf - number of a CDC object + * @param out_buf - to this array will be stored the object from a CDC buffer + * @param out_buf_sz - size of buffer for results + * @param rx_data_size - to this address will be stored the object's size + * @return esp_err_t ESP_OK, ESP_FAIL or ESP_ERR_INVALID_STATE + */ +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); + + +/** + * @brief Check if the ACM initialized + * + * @param itf - number of a CDC object + * @return true or false + */ +bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf); + +/*********************************************************************** Public functions*/ + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/port/esp32s2/include/tusb_config.h b/components/tinyusb/additions/include/tusb_config.h similarity index 82% rename from components/tinyusb/port/esp32s2/include/tusb_config.h rename to components/tinyusb/additions/include/tusb_config.h index 5f82ec04fe..05509d5e4a 100644 --- a/components/tinyusb/port/esp32s2/include/tusb_config.h +++ b/components/tinyusb/additions/include/tusb_config.h @@ -25,26 +25,20 @@ */ #pragma once +#include "tusb_option.h" #include "sdkconfig.h" #ifdef __cplusplus extern "C" { #endif - - -//-------------------------------------------------------------------- -// COMMON CONFIGURATION -//-------------------------------------------------------------------- -#define OPT_MCU_ESP32_S2 900 // TODO remove after rebase to the last TUSB (IDF-1473) -#define CFG_TUSB_MCU OPT_MCU_ESP32_S2 +/* */ +/* COMMON CONFIGURATION */ +/* */ #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE #define CFG_TUSB_OS OPT_OS_FREERTOS -// CFG_TUSB_DEBUG is defined by compiler in DEBUG build -#define CFG_TUSB_DEBUG CONFIG_USB_DEBUG - /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. * Tinyusb use follows macros to declare transferring memory so that they can be put * into those specific section. @@ -60,15 +54,33 @@ extern "C" { # define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) #endif +/* */ +/* DRIVER CONFIGURATION */ +/* */ - -//-------------------------------------------------------------------- -// DEVICE CONFIGURATION -//-------------------------------------------------------------------- - +#define CFG_TUD_MAINTASK_SIZE 4096 #define CFG_TUD_ENDOINT0_SIZE 64 -//------kconfig adaptor part -------// +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE CONFIG_USB_CDC_RX_BUFSIZE +#define CFG_TUD_CDC_TX_BUFSIZE CONFIG_USB_CDC_TX_BUFSIZE + +// MSC Buffer size of Device Mass storage: +#define CFG_TUD_MSC_BUFSIZE CONFIG_USB_MSC_BUFSIZE + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_BUFSIZE CONFIG_USB_HID_BUFSIZE + +#define CFG_TUD_CDC CONFIG_USB_CDC_ENABLED +#define CFG_TUD_MSC CONFIG_USB_MSC_ENABLED +#define CFG_TUD_HID CONFIG_USB_HID_ENABLED +#define CFG_TUD_MIDI CONFIG_USB_MIDI_ENABLED +#define CFG_TUD_CUSTOM_CLASS CONFIG_USB_CUSTOM_CLASS_ENABLED + +/* */ +/* KCONFIG */ +/* */ + #ifndef CONFIG_USB_CDC_ENABLED # define CONFIG_USB_CDC_ENABLED 0 #endif @@ -89,26 +101,6 @@ extern "C" { # define CONFIG_USB_CUSTOM_CLASS_ENABLED 0 #endif -//------------- CLASS -------------// -#define CFG_TUD_CDC CONFIG_USB_CDC_ENABLED -#define CFG_TUD_MSC CONFIG_USB_MSC_ENABLED -#define CFG_TUD_HID CONFIG_USB_HID_ENABLED - -#define CFG_TUD_MIDI CONFIG_USB_MIDI_ENABLED -#define CFG_TUD_CUSTOM_CLASS CONFIG_USB_CUSTOM_CLASS_ENABLED - - - -// CDC FIFO size of TX and RX -#define CFG_TUD_CDC_RX_BUFSIZE CONFIG_USB_CDC_RX_BUFSIZE -#define CFG_TUD_CDC_TX_BUFSIZE CONFIG_USB_CDC_TX_BUFSIZE - -// MSC Buffer size of Device Mass storage: -#define CFG_TUD_MSC_BUFSIZE CONFIG_USB_MSC_BUFSIZE - -// HID buffer size Should be sufficient to hold ID (if any) + Data -#define CFG_TUD_HID_BUFSIZE CONFIG_USB_HID_BUFSIZE - #ifdef __cplusplus } #endif diff --git a/components/tinyusb/additions/include/tusb_console.h b/components/tinyusb/additions/include/tusb_console.h new file mode 100644 index 0000000000..f7304eea31 --- /dev/null +++ b/components/tinyusb/additions/include/tusb_console.h @@ -0,0 +1,33 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "esp_err.h" + +/** + * @brief Redirect output to the USB serial + * @param cdc_intf - interface number of TinyUSB's CDC + * + * @return esp_err_t - ESP_OK, ESP_FAIL or an error code + */ +esp_err_t esp_tusb_init_console(int cdc_intf); + +/** + * @brief Switch log to the default output + * @param cdc_intf - interface number of TinyUSB's CDC + * + * @return esp_err_t + */ +esp_err_t esp_tusb_deinit_console(int cdc_intf); diff --git a/components/tinyusb/additions/include/tusb_tasks.h b/components/tinyusb/additions/include/tusb_tasks.h new file mode 100644 index 0000000000..f1a47991b5 --- /dev/null +++ b/components/tinyusb/additions/include/tusb_tasks.h @@ -0,0 +1,44 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "esp_err.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief This API starts a task with a wrapper function of tud_task and default task parameters. + * + * The wrapper function basically wraps tud_task and some log. Default parameters: stack size and priority as configured, argument = NULL, + * not pinned to any core. + * If you have more requirements for this task, you can create your own task which calls tud_task as the last step. + * + * @return ESP_OK or ESP_FAIL + */ +esp_err_t tusb_run_task(void); + +/** + * @brief Stops a FreeRTOS task with @ref tusb_device_task + * + * @return ESP_OK or ESP_FAIL + */ +esp_err_t tusb_stop_task(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include/vfs_tinyusb.h b/components/tinyusb/additions/include/vfs_tinyusb.h new file mode 100644 index 0000000000..233b7f149f --- /dev/null +++ b/components/tinyusb/additions/include/vfs_tinyusb.h @@ -0,0 +1,42 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Register TinyUSB CDC at VFS with path + * @param cdc_intf - interface number of TinyUSB's CDC + * @param path - path where the CDC will be registered, `/dev/tusb_cdc` will be used if left NULL. + * + * @return esp_err_t ESP_OK or ESP_FAIL + */ +esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path); + +/** + * @brief Unregister TinyUSB CDC from VFS + * @param path - path where the CDC will be unregistered if NULL will be used `/dev/tusb_cdc` + * + * @return esp_err_t ESP_OK or ESP_FAIL + */ +esp_err_t esp_vfs_tusb_cdc_unregister(char const *path); + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/additions/include_private/cdc.h b/components/tinyusb/additions/include_private/cdc.h new file mode 100644 index 0000000000..b3aa870f25 --- /dev/null +++ b/components/tinyusb/additions/include_private/cdc.h @@ -0,0 +1,99 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/ringbuf.h" +#include "freertos/semphr.h" +#include "freertos/timers.h" +#include "tusb.h" +#include "tinyusb_types.h" + +/* CDC classification + ********************************************************************* */ +typedef enum { + TINYUSB_CDC_DATA = 0x00, +} cdc_data_sublcass_type_t; // CDC120 specification + +/* Note:other classification is represented in the file components\tinyusb\tinyusb\src\class\cdc\cdc.h */ + +/*********************************************************************** CDC classification*/ +/* Structs + ********************************************************************* */ +typedef struct { + tinyusb_usbdev_t usb_dev; /*!< USB device to set up */ + tusb_class_code_t cdc_class; /*!< CDC device class : Communications or Data device */ + union { + cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: AMC, ECM, etc. */ + cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/ + } cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */ +} tinyusb_config_cdc_t; /*!< Main configuration structure of a CDC device */ + +typedef struct { + tinyusb_usbdev_t usb_dev; /*!< USB device used for the instance */ + tusb_class_code_t type; + union { + cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: AMC, ECM, etc. */ + cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/ + } cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */ + void *subclass_obj; /*!< Dynamically allocated subclass specific object */ +} esp_tusb_cdc_t; +/*********************************************************************** Structs*/ +/* Functions + ********************************************************************* */ +/** + * @brief Initializing CDC basic object + * @param itf - number of a CDC object + * @param cfg - CDC configuration structure + * + * @return esp_err_t ESP_OK or ESP_FAIL + */ +esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg); + + +/** + * @brief De-initializing CDC. Clean its objects + * @param itf - number of a CDC object + * @return esp_err_t ESP_OK, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_STATE + * + */ +esp_err_t tinyusb_cdc_deinit(int itf); + + +/** + * @brief Checks if the CDC initialized and ready to interaction + * + * @return true or false + */ +bool tinyusb_cdc_initialized(int itf); + + +/** + * @brief Return interface of a CDC device + * + * @param itf_num + * @return esp_tusb_cdc_t* pointer to the interface or (NULL) on error + */ +esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num); +/*********************************************************************** Functions*/ + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/port/common/include/descriptors_control.h b/components/tinyusb/additions/include_private/descriptors_control.h similarity index 93% rename from components/tinyusb/port/common/include/descriptors_control.h rename to components/tinyusb/additions/include_private/descriptors_control.h index b942966794..4ef341932f 100644 --- a/components/tinyusb/port/common/include/descriptors_control.h +++ b/components/tinyusb/additions/include_private/descriptors_control.h @@ -56,7 +56,7 @@ enum { }; enum { - CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN + CFG_TUD_MSC * TUD_MSC_DESC_LEN + + TUSB_DESC_TOTAL_LEN = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN + CFG_TUD_MSC * TUD_MSC_DESC_LEN + CFG_TUD_HID * TUD_HID_DESC_LEN }; diff --git a/components/tinyusb/port/esp32s2/include/device_controller_driver.h b/components/tinyusb/additions/include_private/usb_descriptors.h similarity index 56% rename from components/tinyusb/port/esp32s2/include/device_controller_driver.h rename to components/tinyusb/additions/include_private/usb_descriptors.h index f8cbec7b78..76c2b54b91 100644 --- a/components/tinyusb/port/esp32s2/include/device_controller_driver.h +++ b/components/tinyusb/additions/include_private/usb_descriptors.h @@ -14,36 +14,20 @@ #pragma once +#include "tusb.h" +#include "tinyusb_types.h" + #ifdef __cplusplus extern "C" { #endif -#include -#include -// Espressif -#include "driver/periph_ctrl.h" -#include "freertos/xtensa_api.h" -#include "esp_intr_alloc.h" -#include "esp_log.h" -#include "soc/dport_reg.h" -#include "soc/usb_periph.h" -#include "tusb_config.h" -// TinyUSB -#include "tusb_option.h" -#include "descriptors_control.h" -#include "device/dcd.h" +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +extern tusb_desc_device_t descriptor_tinyusb; +extern tusb_desc_strarray_device_t descriptor_str_tinyusb; -#define USB_EP_DIRECTIONS 2 -#define USB_MAX_EP_NUM 16 - -typedef struct { - uint8_t *buffer; - uint16_t total_len; - uint16_t queued_len; - uint16_t max_size; - bool short_packet; -} xfer_ctl_t; +extern tusb_desc_device_t descriptor_kconfig; +extern tusb_desc_strarray_device_t descriptor_str_kconfig; #ifdef __cplusplus } diff --git a/components/tinyusb/additions/src/cdc.c b/components/tinyusb/additions/src/cdc.c new file mode 100644 index 0000000000..bee57ee2df --- /dev/null +++ b/components/tinyusb/additions/src/cdc.c @@ -0,0 +1,162 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp_err.h" +#include "esp_log.h" +#include "tusb.h" +#include "cdc.h" +#include "sdkconfig.h" + +static const char *TAG = "tusb_cdc"; + +#define ESP_RETURN_ON_ERROR(x) do {esp_err_t r = (x); if (r != ESP_OK) return r;} while(0) + +#define CDC_INTF_NUM CFG_TUD_CDC // number of cdc blocks + +static esp_tusb_cdc_t *cdc_obj[CDC_INTF_NUM] = {}; + +/* Common CDC functions + ********************************************************************* */ +bool tinyusb_cdc_initialized(int itf) +{ + return (cdc_obj[itf] != NULL); +} + +static esp_err_t cdc_interface_check(int itf) +{ + if (tinyusb_cdc_initialized(itf)) { + return ESP_OK; + } else { + ESP_LOGE(TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization"); + return ESP_ERR_INVALID_STATE; + } +} + +/** + * @brief + * + * @param itf + * @param expected_inited + * @param expected_type use -1 if you don't care + * @return esp_err_t + */ +static esp_err_t cdc_obj_check(int itf, bool expected_inited, tusb_class_code_t expected_type) +{ + bool inited = (cdc_obj[itf] != NULL); + if (expected_inited != inited) { + ESP_LOGE(TAG, "Wrong state of the interface. Expected state: %s", + expected_inited ? "initialized" : "not initialized"); + return ESP_ERR_INVALID_STATE; + } + if (inited && (expected_type != -1) && !(cdc_obj[itf]->type == expected_type)) { + ESP_LOGE(TAG, "Wrong type of the interface. Should be : 0x%x (tusb_class_code_t)", expected_type); + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} + +esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num) +{ + if (cdc_interface_check(itf_num) != ESP_OK) { + return NULL; + } + return cdc_obj[itf_num]; +} + +/*********************************************************************** Common CDC functions*/ +/* CDC class funcs + ********************************************************************* */ +static esp_err_t tusb_cdc_comm_init(int itf) +{ + ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1)); + 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)); + 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)); + 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)); + free(cdc_obj[itf]); + cdc_obj[itf] = NULL; + return ESP_OK; +} +/*********************************************************************** CDC class funcs*/ +/* CDC initialization + ********************************************************************* */ +esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg) +{ + ESP_LOGD(TAG, "CDC initialization..."); + if (itf != 0) { + ESP_LOGE(TAG, "There is not CDC no.%d", itf); + return ESP_ERR_INVALID_ARG; + } + if (cfg->cdc_class == TUSB_CLASS_CDC) { + ESP_RETURN_ON_ERROR(tusb_cdc_comm_init(itf)); + cdc_obj[itf]->cdc_subclass.comm_subclass = cfg->cdc_subclass.comm_subclass; + } else { + ESP_RETURN_ON_ERROR(tusb_cdc_data_init(itf)); + 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) +{ + if (itf != 0) { + ESP_LOGE(TAG, "There is not CDC no.%d", itf); + return ESP_ERR_INVALID_ARG; + } + if (cdc_obj[itf]->type == TUSB_CLASS_CDC) { + ESP_RETURN_ON_ERROR(tusb_cdc_deinit_comm(itf)); + } else if (cdc_obj[itf]->type == TUSB_CLASS_CDC_DATA) { + ESP_RETURN_ON_ERROR(tusb_cdc_deinit_data(itf)); + } else { + return ESP_ERR_INVALID_ARG; + } + ESP_LOGD(TAG, "De-initialized"); + return ESP_OK; +} +/*********************************************************************** CDC initialization*/ diff --git a/components/tinyusb/port/common/src/descriptors_control.c b/components/tinyusb/additions/src/descriptors_control.c similarity index 85% rename from components/tinyusb/port/common/src/descriptors_control.c rename to components/tinyusb/additions/src/descriptors_control.c index 7fe82fe0c1..659deeaf36 100644 --- a/components/tinyusb/port/common/src/descriptors_control.c +++ b/components/tinyusb/additions/src/descriptors_control.c @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "descriptors_control.h" #include "esp_log.h" +#include "descriptors_control.h" -static const char *TAG = "TUSB:descriptors_control"; +static const char *TAG = "tusb_desc"; static tusb_desc_device_t s_descriptor; static char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; - +#define MAX_DESC_BUF_SIZE 32 #if CFG_TUD_HID //HID Report Descriptor uint8_t const desc_hid_report[] = { @@ -29,7 +29,7 @@ uint8_t const desc_hid_report[] = { uint8_t const desc_configuration[] = { // interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + 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. @@ -73,42 +73,41 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index) return desc_configuration; } -static uint16_t _desc_str[32]; +static uint16_t _desc_str[MAX_DESC_BUF_SIZE]; -/** - * @brief Invoked when received GET STRING DESCRIPTOR request. - * Application returns pointer to descriptor, whose contents must exist long - * enough for transfer to complete - * - * @param index - * @return uint16_t const* - */ -uint16_t const *tud_descriptor_string_cb(uint8_t index) +// 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) { + 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])) { + + if ( index >= sizeof(s_str_descriptor) / sizeof(s_str_descriptor[0]) ) { return NULL; } + const char *str = s_str_descriptor[index]; + // Cap at max char chr_count = strlen(str); - if (chr_count > 31) { - chr_count = 31; + 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 len, second byte is string type - _desc_str[0] = TUD_DESC_STR_HEADER(chr_count); + // 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; } diff --git a/components/tinyusb/port/esp32s2/src/tinyusb.c b/components/tinyusb/additions/src/tinyusb.c similarity index 79% rename from components/tinyusb/port/esp32s2/src/tinyusb.c rename to components/tinyusb/additions/src/tinyusb.c index 09fa53f68b..e2fd061fc7 100644 --- a/components/tinyusb/port/esp32s2/src/tinyusb.c +++ b/components/tinyusb/additions/src/tinyusb.c @@ -12,14 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "esp_log.h" +#include "esp_rom_gpio.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" -#include "esp_rom_gpio.h" +#include "esp32s2/rom/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "hal/gpio_ll.h" #include "hal/usb_hal.h" #include "soc/gpio_periph.h" #include "soc/usb_periph.h" #include "tinyusb.h" +#include "descriptors_control.h" +#include "tusb.h" +#include "tusb_tasks.h" +#include "sdkconfig.h" + +const static char *TAG = "TinyUSB"; static void configure_pins(usb_hal_context_t *usb) { @@ -34,7 +44,7 @@ static void configure_pins(usb_hal_context_t *usb) esp_rom_gpio_connect_out_signal(iopin->pin, iopin->func, false, false); } else { esp_rom_gpio_connect_in_signal(iopin->pin, iopin->func, false); - if ((iopin->pin != GPIO_MATRIX_CONST_ZERO_INPUT) && (iopin->pin != GPIO_MATRIX_CONST_ONE_INPUT)) { + if ((iopin->pin != GPIO_FUNC_IN_LOW) && (iopin->pin != GPIO_FUNC_IN_HIGH)) { gpio_ll_input_enable(&GPIO, iopin->pin); } } @@ -62,7 +72,9 @@ static void configure_pins(usb_hal_context_t *usb) esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) { tusb_desc_device_t *descriptor; + int res; char **string_descriptor; + ESP_LOGI(TAG, "Driver installation..."); periph_module_reset(PERIPH_USB_MODULE); periph_module_enable(PERIPH_USB_MODULE); @@ -90,6 +102,18 @@ esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) tusb_set_descriptor(descriptor, string_descriptor); - ESP_ERROR_CHECK(tusb_init()); + res = tusb_init(); + if (res != TUSB_ERROR_NONE) { + ESP_LOGE(TAG, "Can't initialize the TinyUSB stack. TinyUSB error: %d", res); + return res; + } +#if !CONFIG_USB_DO_NOT_CREATE_TASK + res = tusb_run_task(); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Can't create the TinyUSB task."); + return res; + } +#endif + ESP_LOGI(TAG, "Driver installed"); return ESP_OK; } diff --git a/components/tinyusb/additions/src/tusb_cdc_acm.c b/components/tinyusb/additions/src/tusb_cdc_acm.c new file mode 100644 index 0000000000..725584db0e --- /dev/null +++ b/components/tinyusb/additions/src/tusb_cdc_acm.c @@ -0,0 +1,412 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "esp_err.h" +#include "esp_log.h" +#include "tusb.h" +#include "tusb_cdc_acm.h" +#include "cdc.h" +#include "sdkconfig.h" + +#define ESP_RETURN_ON_ERROR(x) do{esp_err_t r = (x); if (r != ESP_OK) return r;} while(0) + +#define RX_UNREADBUF_SZ_DEFAULT 64 // buffer storing all unread RX data + + +typedef struct { + bool initialized; + size_t rx_unread_buf_sz; + RingbufHandle_t rx_unread_buf; + 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; + xTimerHandle flush_timer; +} esp_tusb_cdcacm_t; /*!< CDC_AMC 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 when cdc 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_USB_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 + ********************************************************************* */ +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); + if (!acm) { + ESP_LOGE(TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization"); + return ESP_ERR_INVALID_STATE; + } + uint8_t *buf = xRingbufferReceiveUpTo(acm->rx_unread_buf, rx_data_size, 0, out_buf_sz); + if (buf) { + memcpy(out_buf, buf, *rx_data_size); + vRingbufferReturnItem(acm->rx_unread_buf, (void *)buf); + return ESP_OK; + } else { + ESP_LOGE(TAG, "Failed to receive item"); + return ESP_ERR_NO_MEM; + } +} + + +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, uint8_t *in_buf, size_t in_size) +{ + if (!get_acm(itf)) { // non-initialized + return 0; + } + return tud_cdc_n_write(itf, in_buf, in_size); +} + + +static inline bool timer_isactive(tinyusb_cdcacm_itf_t itf) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + return xTimerIsTimerActive(acm->flush_timer); +} + +static inline esp_err_t timer_start(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks) +{ + esp_tusb_cdcacm_t *acm = get_acm(itf); + xTimerChangePeriod(acm->flush_timer, timeout_ticks, 0); // set the timer + if (!xTimerIsTimerActive(acm->flush_timer)) { + if (xTimerStart(acm->flush_timer, 0) != pdPASS) { // start + ESP_LOGE(TAG, "Can't start the timer"); + return ESP_FAIL; + } + } + return ESP_OK; +} + +static inline void timer_stop(tinyusb_cdcacm_itf_t itf) +{ + if (timer_isactive(itf)) { + xTimerStop(get_acm(itf)->flush_timer, 0); + } +} + + +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) { + return ESP_FAIL; + } else { + if (tud_cdc_n_write_available(itf)) { + return ESP_FAIL; + } + } + return ESP_ERR_TIMEOUT; + } else { // if timeout use timer + ESP_RETURN_ON_ERROR(timer_start(itf, timeout_ticks)); + while (1) { // loop until success or until the time runs out + if (!tud_cdc_n_write_available(itf)) { // if nothing to write - nothing to flush + break; + } + if (tud_cdc_n_write_flush(itf)) { // Success + break; + } + if (!timer_isactive(itf)) { // Time is up + ESP_LOGW(TAG, "Flush failed"); + return ESP_ERR_TIMEOUT; + } + vTaskDelay(1); + } + timer_stop(itf); + return ESP_OK; + } +} + + +static void flush_timer_cb(xTimerHandle pxTimer) +{ + ESP_LOGV(TAG, "flush_timer stopped"); + xTimerStop(pxTimer, 0); +} + +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)); + ESP_RETURN_ON_ERROR(alloc_obj(itf)); + + 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); + } + + /* SW timer*/ + acm->flush_timer = xTimerCreate( + "flush_timer", 10 / portTICK_PERIOD_MS, pdTRUE, (void *)itf, flush_timer_cb); + + /* Buffers */ + acm->rx_tfbuf = malloc(CONFIG_USB_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*/ diff --git a/components/tinyusb/additions/src/tusb_console.c b/components/tinyusb/additions/src/tusb_console.c new file mode 100644 index 0000000000..1942a05b1f --- /dev/null +++ b/components/tinyusb/additions/src/tusb_console.c @@ -0,0 +1,142 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include +#include +#include "esp_log.h" +#include "cdc.h" +#include "tusb_console.h" +#include "tinyusb.h" +#include "vfs_tinyusb.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) +{ + if (!tinyusb_cdc_initialized(cdc_intf)) { + ESP_LOGE(TAG, "Can't init the console because TinyUSB's CDC is not initialized!"); + return ESP_ERR_INVALID_STATE; + } + /* Registering TUSB at VFS */ + int res = esp_vfs_tusb_cdc_register(cdc_intf, NULL); + if (res != ESP_OK) { + return res; + } + + res = redirect_std_streams_to(&con.in, &con.out, &con.err, "/dev/tusb_cdc"); + if (res != ESP_OK) { + return res; + } + + return ESP_OK; +} + +esp_err_t esp_tusb_deinit_console(int cdc_intf) +{ + if (!tinyusb_cdc_initialized(cdc_intf)) { + ESP_LOGE(TAG, "Can't deinit the console because TinyUSB's CDC is not initialized!"); + return ESP_ERR_INVALID_STATE; + } + + int res = restore_std_streams(&con.in, &con.out, &con.err); + if (res != ESP_OK) { + return res; + } + esp_vfs_tusb_cdc_unregister(NULL); + return ESP_OK; +} diff --git a/components/tinyusb/additions/src/tusb_tasks.c b/components/tinyusb/additions/src/tusb_tasks.c new file mode 100644 index 0000000000..e70e69cf94 --- /dev/null +++ b/components/tinyusb/additions/src/tusb_tasks.c @@ -0,0 +1,62 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "hal/usb_hal.h" +#include "tinyusb.h" +#include "tusb_tasks.h" +#include "sdkconfig.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) +{ + // Create a task for tinyusb device stack: + xTaskCreate(tusb_device_task, "tinyUSB: main task", CFG_TUD_MAINTASK_SIZE, NULL, CONFIG_USB_TASK_PRIORITY, &s_tusb_tskh); + if (!s_tusb_tskh) { + return ESP_FAIL; + } else { + return ESP_OK; + } +} + +esp_err_t tusb_stop_task(void) +{ + if ( s_tusb_tskh != NULL ) { + vTaskDelete(s_tusb_tskh); + } else { + ESP_LOGE(TAG, "tinyusb task is not started"); + return ESP_FAIL; + } + if (s_tusb_tskh) { + return ESP_FAIL; + } else { + return ESP_OK; + } +} diff --git a/components/tinyusb/port/common/src/usb_descriptors.c b/components/tinyusb/additions/src/usb_descriptors.c similarity index 97% rename from components/tinyusb/port/common/src/usb_descriptors.c rename to components/tinyusb/additions/src/usb_descriptors.c index 8668cc43d2..98491e5f00 100644 --- a/components/tinyusb/port/common/src/usb_descriptors.c +++ b/components/tinyusb/additions/src/usb_descriptors.c @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sdkconfig.h" #include "usb_descriptors.h" +#include "sdkconfig.h" #define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3)) @@ -45,7 +45,8 @@ tusb_desc_device_t descriptor_tinyusb = { .iProduct = 0x02, .iSerialNumber = 0x03, - .bNumConfigurations = 0x01}; + .bNumConfigurations = 0x01 +}; tusb_desc_strarray_device_t descriptor_str_tinyusb = { // array of pointer to string descriptors @@ -97,7 +98,8 @@ tusb_desc_device_t descriptor_kconfig = { .iProduct = 0x02, .iSerialNumber = 0x03, - .bNumConfigurations = 0x01}; + .bNumConfigurations = 0x01 +}; tusb_desc_strarray_device_t descriptor_str_kconfig = { // array of pointer to string descriptors @@ -125,4 +127,4 @@ tusb_desc_strarray_device_t descriptor_str_kconfig = { #endif }; -/* End of Kconfig driven Descriptor */ \ No newline at end of file +/* End of Kconfig driven Descriptor */ diff --git a/components/tinyusb/additions/src/vfs_tinyusb.c b/components/tinyusb/additions/src/vfs_tinyusb.c new file mode 100644 index 0000000000..400af1a8c3 --- /dev/null +++ b/components/tinyusb/additions/src/vfs_tinyusb.c @@ -0,0 +1,296 @@ +// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, path_len); + } else { + strncpy(s_vfstusb.vfs_path, + VFS_TUSB_PATH_DEFAULT, + strlen(VFS_TUSB_PATH_DEFAULT) + 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 + } + + } + tinyusb_cdcacm_write_flush(s_vfstusb.cdc_intf, 0); + _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); + 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; +} diff --git a/components/tinyusb/port/common/src/usbd.c b/components/tinyusb/port/common/src/usbd.c deleted file mode 100644 index 2cdb1002de..0000000000 --- a/components/tinyusb/port/common/src/usbd.c +++ /dev/null @@ -1,830 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org), - * 2020 Espressif Systems (Shanghai) Co. Ltd. - * - * 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. - * - * This file is part of the TinyUSB stack. - */ - -#include "tusb_option.h" - -#if TUSB_OPT_DEVICE_ENABLED - -#include "tusb.h" -#include "usbd.h" -#include "device/usbd_pvt.h" -#include "dcd.h" -#include "esp_log.h" - -static const char *TAG = "TUSB:device"; -#ifndef CFG_TUD_TASK_QUEUE_SZ -#define CFG_TUD_TASK_QUEUE_SZ 16 -#endif - -//--------------------------------------------------------------------+ -// Device Data -//--------------------------------------------------------------------+ -typedef struct { - struct TU_ATTR_PACKED { - volatile uint8_t connected : 1; - volatile uint8_t configured : 1; - volatile uint8_t suspended : 1; - - uint8_t remote_wakeup_en : 1; // enable/disable by host - uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute - uint8_t self_powered : 1; // configuration descriptor's attribute - }; - - uint8_t ep_busy_map[2]; // bit mask for busy endpoint - uint8_t ep_stall_map[2]; // bit map for stalled endpoint - - uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) - uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid ) -} usbd_device_t; - -static usbd_device_t _usbd_dev = {0}; - -//--------------------------------------------------------------------+ -// Class Driver -//--------------------------------------------------------------------+ -typedef struct { - uint8_t class_code; - - void (*init)(void); - bool (*open)(uint8_t rhport, tusb_desc_interface_t const *desc_intf, uint16_t *p_length); - bool (*control_request)(uint8_t rhport, tusb_control_request_t const *request); - bool (*control_request_complete)(uint8_t rhport, tusb_control_request_t const *request); - bool (*xfer_cb)(uint8_t rhport, uint8_t ep_addr, xfer_result_t, uint32_t); - void (*sof)(uint8_t rhport); - void (*reset)(uint8_t); -} usbd_class_driver_t; - -static usbd_class_driver_t const usbd_class_drivers[] = { -#if CFG_TUD_CDC - { - .class_code = TUSB_CLASS_CDC, - .init = cdcd_init, - .open = cdcd_open, - .control_request = cdcd_control_request, - .control_request_complete = cdcd_control_request_complete, - .xfer_cb = cdcd_xfer_cb, - .sof = NULL, - .reset = cdcd_reset - }, -#endif - -#if CFG_TUD_MSC - { - .class_code = TUSB_CLASS_MSC, - .init = mscd_init, - .open = mscd_open, - .control_request = mscd_control_request, - .control_request_complete = mscd_control_request_complete, - .xfer_cb = mscd_xfer_cb, - .sof = NULL, - .reset = mscd_reset - }, -#endif - -#if CFG_TUD_HID - { - .class_code = TUSB_CLASS_HID, - .init = hidd_init, - .open = hidd_open, - .control_request = hidd_control_request, - .control_request_complete = hidd_control_request_complete, - .xfer_cb = hidd_xfer_cb, - .sof = NULL, - .reset = hidd_reset - }, -#endif - -#if CFG_TUD_MIDI - { - .class_code = TUSB_CLASS_AUDIO, - .init = midid_init, - .open = midid_open, - .control_request = midid_control_request, - .control_request_complete = midid_control_request_complete, - .xfer_cb = midid_xfer_cb, - .sof = NULL, - .reset = midid_reset - }, -#endif - -#if CFG_TUD_CUSTOM_CLASS - { - .class_code = TUSB_CLASS_VENDOR_SPECIFIC, - .init = cusd_init, - .open = cusd_open, - .control_request = cusd_control_request, - .control_request_complete = cusd_control_request_complete, - .xfer_cb = cusd_xfer_cb, - .sof = NULL, - .reset = cusd_reset - }, -#endif -}; - -enum { - USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SZIE(usbd_class_drivers) -}; - -//--------------------------------------------------------------------+ -// DCD Event -//--------------------------------------------------------------------+ - -// Event queue -// OPT_MODE_DEVICE is used by OS NONE for mutex (disable usb isr) -OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); -static osal_queue_t _usbd_q; - -//--------------------------------------------------------------------+ -// Prototypes -//--------------------------------------------------------------------+ -static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const *p_desc, uint16_t desc_len, uint8_t driver_id); -static bool process_control_request(uint8_t rhport, tusb_control_request_t const *p_request); -static bool process_set_config(uint8_t rhport, uint8_t cfg_num); -static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const *p_request); - -void usbd_control_reset(uint8_t rhport); -bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void usbd_control_set_complete_callback(bool (*fp)(uint8_t, tusb_control_request_t const *)); - -//--------------------------------------------------------------------+ -// Application API -//--------------------------------------------------------------------+ -bool tud_mounted(void) -{ - return _usbd_dev.configured; -} - -bool tud_suspended(void) -{ - return _usbd_dev.suspended; -} - -bool tud_remote_wakeup(void) -{ - // only wake up host if this feature is supported and enabled and we are suspended - TU_VERIFY(_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en); - dcd_remote_wakeup(TUD_OPT_RHPORT); - return true; -} - -//--------------------------------------------------------------------+ -// USBD Task -//--------------------------------------------------------------------+ -bool usbd_init(void) -{ - tu_varclr(&_usbd_dev); - - // Init device queue & task - - ESP_LOGV(TAG, "Init device queue & task..."); - _usbd_q = osal_queue_create(&_usbd_qdef); - TU_ASSERT(_usbd_q != NULL); - ESP_LOGV(TAG, "Init device queue & task: Done"); - -// Init class drivers -# if USBD_CLASS_DRIVER_COUNT - for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) { - usbd_class_drivers[i].init(); - } -# endif - - // Init device controller driver - - ESP_LOGV(TAG, "dcd_init..."); - dcd_init(TUD_OPT_RHPORT); - ESP_LOGV(TAG, "dcd_init: Done"); - ESP_LOGV(TAG, "dcd_int_enable..."); - dcd_int_enable(TUD_OPT_RHPORT); - ESP_LOGV(TAG, "dcd_int_enable: Done"); - - return true; -} - -static void usbd_reset(uint8_t rhport) -{ - tu_varclr(&_usbd_dev); - - memset(_usbd_dev.itf2drv, 0xff, sizeof(_usbd_dev.itf2drv)); // invalid mapping - memset(_usbd_dev.ep2drv, 0xff, sizeof(_usbd_dev.ep2drv)); // invalid mapping - - usbd_control_reset(rhport); -# if USBD_CLASS_DRIVER_COUNT - for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) { - if (usbd_class_drivers[i].reset) { - usbd_class_drivers[i].reset(rhport); - } - } -# endif -} - -/* USB Device Driver task - * This top level thread manages all device controller event and delegates events to class-specific drivers. - * This should be called periodically within the mainloop or rtos thread. - * - @code - int main(void) - { - application_init(); - tusb_init(); - - while(1) // the mainloop - { - application_code(); - - tud_task(); // tinyusb device task - } - } - @endcode - */ -void tud_task(void) -{ - // Skip if stack is not initialized - bool tusb_ready = tusb_inited(); - if (!tusb_ready) { - - ESP_LOGV(TAG, "is not ready"); - return; - } - - ESP_LOGV(TAG, "started"); - // Loop until there is no more events in the queue - while (1) { - dcd_event_t event; - - volatile bool ev = osal_queue_receive(_usbd_q, &event); - if (!ev) { - ESP_LOGV(TAG, "USB EVENT ...empty..."); - return; - } - - ESP_LOGV(TAG, "USB EVENT: %u", event.event_id); - switch (event.event_id) { - case DCD_EVENT_BUS_RESET: - ESP_LOGV(TAG, "USB EVENT bus_reset"); - usbd_reset(event.rhport); - break; - - case DCD_EVENT_UNPLUGGED: - ESP_LOGV(TAG, "USB EVENT unplugged"); - usbd_reset(event.rhport); - - // invoke callback - if (tud_umount_cb) { - tud_umount_cb(); - } - break; - - case DCD_EVENT_SETUP_RECEIVED: - - ESP_LOGV(TAG, "USB EVENT setup_received"); - // Mark as connected after receiving 1st setup packet. - // But it is easier to set it every time instead of wasting time to check then set - _usbd_dev.connected = 1; - - // Process control request - if (!process_control_request(event.rhport, &event.setup_received)) { - // Failed -> stall both control endpoint IN and OUT - dcd_edpt_stall(event.rhport, 0); - dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); - } - break; - - case DCD_EVENT_XFER_COMPLETE: - // Only handle xfer callback in ready state - // if (_usbd_dev.connected && !_usbd_dev.suspended) - - ESP_LOGV(TAG, "USB EVENT xfer_complete"); - { - // Invoke the class callback associated with the endpoint address - uint8_t const ep_addr = event.xfer_complete.ep_addr; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - _usbd_dev.ep_busy_map[dir] = (uint8_t)tu_bit_clear(_usbd_dev.ep_busy_map[dir], epnum); - - if (0 == tu_edpt_number(ep_addr)) { - // control transfer DATA stage callback - usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len); - } else { - uint8_t const drv_id = _usbd_dev.ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)]; -# if USBD_CLASS_DRIVER_COUNT - TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT, ); -# endif - usbd_class_drivers[drv_id].xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len); - } - } - break; - - case DCD_EVENT_SUSPEND: - ESP_LOGV(TAG, "USB EVENT suspend"); - if (tud_suspend_cb) { - tud_suspend_cb(_usbd_dev.remote_wakeup_en); - } - break; - - case DCD_EVENT_RESUME: - ESP_LOGV(TAG, "USB EVENT resume"); - if (tud_resume_cb) { - tud_resume_cb(); - } - break; - - case DCD_EVENT_SOF: - ESP_LOGV(TAG, "USB EVENT sof"); -# if USBD_CLASS_DRIVER_COUNT - for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) { - if (usbd_class_drivers[i].sof) { - usbd_class_drivers[i].sof(event.rhport); - } - } -# endif - break; - - case USBD_EVENT_FUNC_CALL: - ESP_LOGV(TAG, "USB EVENT func_call"); - if (event.func_call.func) { - event.func_call.func(event.func_call.param); - } - break; - - default: - ESP_LOGV(TAG, "USB EVENT unknown"); - TU_BREAKPOINT(); - break; - } - } -} - -//--------------------------------------------------------------------+ -// Control Request Parser & Handling -//--------------------------------------------------------------------+ - -// This handles the actual request and its response. -// return false will cause its caller to stall control endpoint -static bool process_control_request(uint8_t rhport, tusb_control_request_t const *p_request) -{ - usbd_control_set_complete_callback(NULL); - - switch (p_request->bmRequestType_bit.recipient) { - //------------- Device Requests e.g in enumeration -------------// - case TUSB_REQ_RCPT_DEVICE: - if (TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type) { - // Non standard request is not supported - TU_BREAKPOINT(); - return false; - } - - switch (p_request->bRequest) { - case TUSB_REQ_SET_ADDRESS: - ESP_LOGV(TAG, "TUSB_REQ_SET_ADDRESS"); - // Depending on mcu, status phase could be sent either before or after changing device address - // Therefore DCD must include zero-length status response - dcd_set_address(rhport, (uint8_t)p_request->wValue); - return true; // skip status - break; - - case TUSB_REQ_GET_CONFIGURATION: { - ESP_LOGV(TAG, "TUSB_REQ_GET_CONFIGURATION"); - uint8_t cfgnum = _usbd_dev.configured ? 1 : 0; - usbd_control_xfer(rhport, p_request, &cfgnum, 1); - } - break; - - case TUSB_REQ_SET_CONFIGURATION: { - ESP_LOGV(TAG, "TUSB_REQ_SET_CONFIGURATION"); - uint8_t const cfg_num = (uint8_t)p_request->wValue; - - dcd_set_config(rhport, cfg_num); - _usbd_dev.configured = cfg_num ? 1 : 0; - - if (cfg_num) { - TU_ASSERT(process_set_config(rhport, cfg_num)); - } - usbd_control_status(rhport, p_request); - } - break; - - case TUSB_REQ_GET_DESCRIPTOR: - ESP_LOGV(TAG, "TUSB_REQ_GET_DESCRIPTOR"); - TU_VERIFY(process_get_descriptor(rhport, p_request)); - break; - - case TUSB_REQ_SET_FEATURE: - ESP_LOGV(TAG, "TUSB_REQ_SET_FEATURE"); - // Only support remote wakeup for device feature - TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); - - // Host may enable remote wake up before suspending especially HID device - _usbd_dev.remote_wakeup_en = true; - usbd_control_status(rhport, p_request); - break; - - case TUSB_REQ_CLEAR_FEATURE: - ESP_LOGV(TAG, "TUSB_REQ_CLEAR_FEATURE"); - // Only support remote wakeup for device feature - TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); - - // Host may disable remote wake up after resuming - _usbd_dev.remote_wakeup_en = false; - usbd_control_status(rhport, p_request); - break; - - case TUSB_REQ_GET_STATUS: { - ESP_LOGV(TAG, "TUSB_REQ_GET_STATUS"); - // Device status bit mask - // - Bit 0: Self Powered - // - Bit 1: Remote Wakeup enabled - uint16_t status = (_usbd_dev.self_powered ? 1 : 0) | (_usbd_dev.remote_wakeup_en ? 2 : 0); - usbd_control_xfer(rhport, p_request, &status, 2); - } - break; - - // Unknown/Unsupported request - default: - TU_BREAKPOINT(); - return false; - } - break; - - //------------- Class/Interface Specific Request -------------// - case TUSB_REQ_RCPT_INTERFACE: { - uint8_t const itf = tu_u16_low(p_request->wIndex); - uint8_t const drvid = _usbd_dev.itf2drv[itf]; -# if USBD_CLASS_DRIVER_COUNT - TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT); -# endif - usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_request_complete); - - // stall control endpoint if driver return false - return usbd_class_drivers[drvid].control_request(rhport, p_request); - } - break; - - //------------- Endpoint Request -------------// - case TUSB_REQ_RCPT_ENDPOINT: - // Non standard request is not supported - TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type); - - switch (p_request->bRequest) { - case TUSB_REQ_GET_STATUS: { - uint16_t status = usbd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000; - usbd_control_xfer(rhport, p_request, &status, 2); - } - break; - - case TUSB_REQ_CLEAR_FEATURE: - if (TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue) { - usbd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex)); - } - usbd_control_status(rhport, p_request); - break; - - case TUSB_REQ_SET_FEATURE: - if (TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue) { - usbd_edpt_stall(rhport, tu_u16_low(p_request->wIndex)); - } - usbd_control_status(rhport, p_request); - break; - - // Unknown/Unsupported request - default: - TU_BREAKPOINT(); - return false; - } - break; - - // Unknown recipient - default: - TU_BREAKPOINT(); - return false; - } - - return true; -} - -// Process Set Configure Request -// This function parse configuration descriptor & open drivers accordingly -static bool process_set_config(uint8_t rhport, uint8_t cfg_num) -{ - tusb_desc_configuration_t const *desc_cfg = (tusb_desc_configuration_t const *)tud_descriptor_configuration_cb(cfg_num - 1); // index is cfg_num-1 - TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION); - - // Parse configuration descriptor - _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1 : 0; - _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1 : 0; - - // Parse interface descriptor - uint8_t const *p_desc = ((uint8_t const *)desc_cfg) + sizeof(tusb_desc_configuration_t); - uint8_t const *desc_end = ((uint8_t const *)desc_cfg) + desc_cfg->wTotalLength; - - while (p_desc < desc_end) { - // Each interface always starts with Interface or Association descriptor - if (TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc)) { - p_desc = tu_desc_next(p_desc); // ignore Interface Association - } else { - TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc)); - - tusb_desc_interface_t *desc_itf = (tusb_desc_interface_t *)p_desc; - - // Check if class is supported - uint8_t drv_id = 0; -# if USBD_CLASS_DRIVER_COUNT - for (; drv_id < USBD_CLASS_DRIVER_COUNT; drv_id++) { - if (usbd_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass) { - break; - } - } -# endif - // Interface number must not be used already TODO alternate interface - TU_ASSERT(0xff == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber]); - _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id; - - uint16_t itf_len = 0; - TU_ASSERT(usbd_class_drivers[drv_id].open(rhport, desc_itf, &itf_len)); - TU_ASSERT(itf_len >= sizeof(tusb_desc_interface_t)); - - mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, itf_len, drv_id); - - p_desc += itf_len; // next interface - } - } - - // invoke callback - if (tud_mount_cb) { - tud_mount_cb(); - } - - return true; -} - -// Helper marking endpoint of interface belongs to class driver -static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const *p_desc, uint16_t desc_len, uint8_t driver_id) -{ - uint16_t len = 0; - - while (len < desc_len) { - if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { - uint8_t const ep_addr = ((tusb_desc_endpoint_t const *)p_desc)->bEndpointAddress; - - ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; - } - - len += tu_desc_len(p_desc); - p_desc = tu_desc_next(p_desc); - } -} - -// return descriptor's buffer and update desc_len -static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const *p_request) -{ - tusb_desc_type_t const desc_type = (tusb_desc_type_t)tu_u16_high(p_request->wValue); - uint8_t const desc_index = tu_u16_low(p_request->wValue); - - switch (desc_type) { - case TUSB_DESC_DEVICE: - return usbd_control_xfer(rhport, p_request, (void *)tud_descriptor_device_cb(), sizeof(tusb_desc_device_t)); - break; - - case TUSB_DESC_CONFIGURATION: { - tusb_desc_configuration_t const *desc_config = (tusb_desc_configuration_t const *)tud_descriptor_configuration_cb(desc_index); - return usbd_control_xfer(rhport, p_request, (void *)desc_config, desc_config->wTotalLength); - } - break; - - case TUSB_DESC_STRING: - // String Descriptor always uses the desc set from user - if (desc_index == 0xEE) { - // The 0xEE index string is a Microsoft USB extension. - // It can be used to tell Windows what driver it should use for the device !!! - return false; - } else { - uint8_t const *desc_str = (uint8_t const *)tud_descriptor_string_cb(desc_index); - TU_ASSERT(desc_str); - - // first byte of descriptor is its size - return usbd_control_xfer(rhport, p_request, (void *)desc_str, desc_str[0]); - } - break; - - case TUSB_DESC_DEVICE_QUALIFIER: - return false; - break; - - default: - return false; - } - - return true; -} - -//--------------------------------------------------------------------+ -// DCD Event Handler -//--------------------------------------------------------------------+ -void dcd_event_handler(dcd_event_t const *event, bool in_isr) -{ - switch (event->event_id) { - case DCD_EVENT_BUS_RESET: - osal_queue_send(_usbd_q, event, in_isr); - break; - - case DCD_EVENT_UNPLUGGED: - _usbd_dev.connected = 0; - _usbd_dev.configured = 0; - _usbd_dev.suspended = 0; - osal_queue_send(_usbd_q, event, in_isr); - break; - - case DCD_EVENT_SOF: - // nothing to do now - break; - - case DCD_EVENT_SUSPEND: - // NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the - // SUSPEND condition ( Idle for 3ms ). Some MCUs such as SAMD doesn't distinguish suspend vs disconnect as well. - // We will skip handling SUSPEND/RESUME event if not currently connected - if (_usbd_dev.connected) { - _usbd_dev.suspended = 1; - osal_queue_send(_usbd_q, event, in_isr); - } - break; - - case DCD_EVENT_RESUME: - if (_usbd_dev.connected) { - _usbd_dev.suspended = 0; - osal_queue_send(_usbd_q, event, in_isr); - } - break; - - case DCD_EVENT_SETUP_RECEIVED: - osal_queue_send(_usbd_q, event, in_isr); - break; - - case DCD_EVENT_XFER_COMPLETE: - // skip zero-length control status complete event, should DCD notify us. - if ((0 == tu_edpt_number(event->xfer_complete.ep_addr)) && (event->xfer_complete.len == 0)) { - break; - } - - osal_queue_send(_usbd_q, event, in_isr); - TU_ASSERT(event->xfer_complete.result == XFER_RESULT_SUCCESS, ); - break; - - // Not an DCD event, just a convenient way to defer ISR function should we need to - case USBD_EVENT_FUNC_CALL: - osal_queue_send(_usbd_q, event, in_isr); - break; - - default: - break; - } -} - -// helper to send bus signal event -void dcd_event_bus_signal(uint8_t rhport, dcd_eventid_t eid, bool in_isr) -{ - dcd_event_t event = { - .rhport = rhport, - .event_id = eid, - }; - dcd_event_handler(&event, in_isr); -} - -// helper to send setup received -void dcd_event_setup_received(uint8_t rhport, uint8_t const *setup, bool in_isr) -{ - dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED}; - memcpy(&event.setup_received, setup, 8); - - dcd_event_handler(&event, in_isr); -} - -// helper to send transfer complete event -void dcd_event_xfer_complete(uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) -{ - dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE}; - - event.xfer_complete.ep_addr = ep_addr; - event.xfer_complete.len = xferred_bytes; - event.xfer_complete.result = result; - - dcd_event_handler(&event, in_isr); -} - -//--------------------------------------------------------------------+ -// Helper -//--------------------------------------------------------------------+ - -// Parse consecutive endpoint descriptors (IN & OUT) -bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const *p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t *ep_out, uint8_t *ep_in) -{ - for (int i = 0; i < ep_count; i++) { - tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *)p_desc; - - TU_VERIFY(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && xfer_type == desc_ep->bmAttributes.xfer); - TU_ASSERT(dcd_edpt_open(rhport, desc_ep)); - - if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { - (*ep_in) = desc_ep->bEndpointAddress; - } else { - (*ep_out) = desc_ep->bEndpointAddress; - } - - p_desc = tu_desc_next(p_desc); - } - - return true; -} - -// Helper to defer an isr function -void usbd_defer_func(osal_task_func_t func, void *param, bool in_isr) -{ - dcd_event_t event = { - .rhport = 0, - .event_id = USBD_EVENT_FUNC_CALL, - }; - - event.func_call.func = func; - event.func_call.param = param; - - dcd_event_handler(&event, in_isr); -} - -//--------------------------------------------------------------------+ -// USBD Endpoint API -//--------------------------------------------------------------------+ - -bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - TU_VERIFY(dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes)); - - _usbd_dev.ep_busy_map[dir] = (uint8_t)tu_bit_set(_usbd_dev.ep_busy_map[dir], epnum); - - return true; -} - -bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - return tu_bit_test(_usbd_dev.ep_busy_map[dir], epnum); -} - -void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - dcd_edpt_stall(rhport, ep_addr); - _usbd_dev.ep_stall_map[dir] = (uint8_t)tu_bit_set(_usbd_dev.ep_stall_map[dir], epnum); -} - -void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - dcd_edpt_clear_stall(rhport, ep_addr); - _usbd_dev.ep_stall_map[dir] = (uint8_t)tu_bit_clear(_usbd_dev.ep_stall_map[dir], epnum); -} - -bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - return tu_bit_test(_usbd_dev.ep_stall_map[dir], epnum); -} - -#endif diff --git a/components/tinyusb/port/esp32s2/src/device_controller_driver.c b/components/tinyusb/port/esp32s2/src/device_controller_driver.c deleted file mode 100644 index fe943250e4..0000000000 --- a/components/tinyusb/port/esp32s2/src/device_controller_driver.c +++ /dev/null @@ -1,725 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * Additions Copyright (c) 2020, Espressif Systems (Shanghai) Co. Ltd. - * - * 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. - * - * This file is part of the TinyUSB stack. - */ - -#include "device_controller_driver.h" -#include "esp_rom_sys.h" - -static const char *TAG = "TUSB:DCD"; -static intr_handle_t usb_ih; -static volatile TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; -static uint8_t s_setup_phase = 0; /* 00 - got setup, - 01 - got done setup, - 02 - setup cmd sent*/ - -#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] -static xfer_ctl_t xfer_status[USB_MAX_EP_NUM][USB_EP_DIRECTIONS]; - -static inline void readyfor1setup_pkg(int ep_num) -{ - USB0.out_ep_reg[ep_num].doeptsiz |= (1 << USB_SUPCNT0_S); // doeptsiz 29:30 will decremented on every setup received -} - -// Setup the control endpoint 0. -static void bus_reset(void) -{ - - for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) { - USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK - } - - USB0.dcfg &= ~USB_DEVADDR_M; // reset address - - // Peripheral FIFO architecture - // - // --------------- 320 ( 1280 bytes ) - // | IN FIFO 3 | - // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | - // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | - // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // FIFO sizes are set up by the following rules (each word 32-bits): - // All EP OUT shared a unique OUT FIFO which uses (based on page 1354 of Rev 17 of reference manual): - // * 10 locations in hardware for setup packets + setup control words - // (up to 3 setup packets). - // * 2 locations for OUT endpoint control words. - // * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes) - // * 1 location for global NAK (not required/used here). - // - // It is recommended to allocate 2 times the largest packet size, therefore - // Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 50 - USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10, - USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo - USB0.grxfsiz = 50; - - USB0.gintmsk = USB_MODEMISMSK_M | - USB_SOFMSK_M | - USB_RXFLVIMSK_M | - USB_ERLYSUSPMSK_M | - USB_USBSUSPMSK_M | - USB_USBRSTMSK_M | - USB_ENUMDONEMSK_M | - USB_IEPINTMSK_M | - USB_OEPINTMSK_M | - USB_RESETDETMSK_M | - USB_DISCONNINTMSK_M; - - USB0.daintmsk |= USB_OUTEPMSK0_M | USB_INEPMSK0_M; - USB0.doepmsk |= USB_SETUPMSK_M | USB_XFERCOMPLMSK; - USB0.diepmsk |= USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M; - - USB0.gnptxfsiz = 16 << USB_NPTXFDEP_S; // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - - readyfor1setup_pkg(0); -} - -static void enum_done_processing(void) -{ - - ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then"); - // On current silicon on the Full Speed core, speed is fixed to Full Speed. - // However, keep for debugging and in case Low Speed is ever supported. - uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V); - - // Maximum packet size for EP 0 is set for both directions by writing DIEPCTL - if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz) - USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes - USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall - xfer_status[0][TUSB_DIR_OUT].max_size = 64; - xfer_status[0][TUSB_DIR_IN].max_size = 64; - } else { - USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes - USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall - xfer_status[0][TUSB_DIR_OUT].max_size = 8; - xfer_status[0][TUSB_DIR_IN].max_size = 8; - } - - USB0.gintmsk |= USB_SOFMSK_M; // SOF unmask -} - - - - -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ -void dcd_init(uint8_t rhport) -{ - ESP_LOGV(TAG, "DCD init - Start"); - - // A. Disconnect - ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up"); - USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect - - // B. Programming DCFG - /* If USB host misbehaves during status portion of control xfer - (non zero-length packet), send STALL back and discard. Full speed. */ - USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL - (3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476) - - USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON - USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode - - USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides -#ifdef CONFIG_IDF_TARGET_ESP32S2BETA // needed for beta chip only - //C. chip 7.2.2 hack - ESP_LOGV(TAG, "DCD init - chip ESP32-S2 beta hack"); - USB0.gotgctl = (0 << USB_BVALIDOVVAL_S); //B override value - esp_rom_delay_us(20); - USB0.gotgctl = (0 << USB_BVALIDOVVAL_S) | (1 << USB_BVALIDOVEN_S); //B override value & enable - esp_rom_delay_us(20); -#endif - - // C. Setting SNAKs, then connect - for (int n = 0; n < USB_OUT_EP_NUM; n++) { - USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK - } - ESP_LOGV(TAG, "DCD init - Soft CONNECT"); - USB0.dctl &= ~USB_SFTDISCON_M; // Connect - - // D. Interruption masking - USB0.gintmsk = 0; //mask all - USB0.gotgint = ~0U; //clear OTG ints - USB0.gintsts = ~0U; //clear pending ints - USB0.gintmsk = USB_MODEMISMSK_M | - USB_SOFMSK_M | - USB_RXFLVIMSK_M | - USB_ERLYSUSPMSK_M | - USB_USBSUSPMSK_M | - USB_USBRSTMSK_M | - USB_ENUMDONEMSK_M | - USB_RESETDETMSK_M | - USB_DISCONNINTMSK_M; - esp_rom_delay_us(100); -} - -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void)rhport; - ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr); - USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S); - // Response with status after changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); -} - -void dcd_set_config(uint8_t rhport, uint8_t config_num) -{ - (void)rhport; - (void)config_num; - // Nothing to do -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void)rhport; -} - -/*------------------------------------------------------------------*/ -/* DCD Endpoint port - *------------------------------------------------------------------*/ - -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) -{ - - ESP_LOGV(TAG, "DCD endpoint opened"); - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - // Unsupported endpoint numbers/size. - if ((desc_edpt->wMaxPacketSize.size > 64) || (epnum > 3)) { - return false; - } - - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); - xfer->max_size = desc_edpt->wMaxPacketSize.size; - - if (dir == TUSB_DIR_OUT) { - out_ep[epnum].doepctl |= USB_USBACTEP0_M | - desc_edpt->bmAttributes.xfer << USB_EPTYPE0_S | - desc_edpt->wMaxPacketSize.size << USB_MPS0_S; - USB0.daintmsk |= (1 << (16 + epnum)); - } else { - // Peripheral FIFO architecture (Rev18 RM 29.11) - // - // --------------- 320 ( 1280 bytes ) - // | IN FIFO 3 | - // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | - // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | - // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // Since OUT FIFO = 50, FIFO 0 = 16, average of FIFOx = (312-50-16) / 3 = 82 ~ 80 - in_ep[epnum].diepctl |= USB_D_USBACTEP1_M | - (epnum - 1) << USB_D_TXFNUM1_S | - desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) | - desc_edpt->wMaxPacketSize.size << 0; - USB0.daintmsk |= (1 << (0 + epnum)); - - // Both TXFD and TXSA are in unit of 32-bit words - uint16_t const fifo_size = 80; - uint32_t const fifo_offset = (USB0.grxfsiz & USB_NPTXFDEP_V) + 16 + fifo_size * (epnum - 1); - USB0.dieptxf[epnum - 1] = (80 << USB_NPTXFDEP_S) | fifo_offset; - } - return true; -} - -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - - (void)rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = buffer; - xfer->total_len = total_bytes; - xfer->queued_len = 0; - xfer->short_packet = false; - - uint16_t num_packets = (total_bytes / xfer->max_size); - uint8_t short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if (short_packet_size > 0 || (total_bytes == 0)) { - num_packets++; - } - - ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i", - epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"), - num_packets, total_bytes); - - // IN and OUT endpoint xfers are interrupt-driven, we just schedule them - // here. - if (dir == TUSB_DIR_IN) { - // A full IN transfer (multiple packets, possibly) triggers XFRC. - int bytes2fifo_left = total_bytes; - uint32_t val; // 32 bit val from 4 buff addresses - - USB0.in_ep_reg[epnum].diepint = ~0U; // clear all ints - USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes; - USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK - while (bytes2fifo_left > 0) { // TODO move it to ep_in_handle (IDF-1475) - /* ATTENTION! In cases when CFG_TUD_ENDOINT0_SIZE, CFG_TUD_CDC_EPSIZE, CFG_TUD_MIDI_EPSIZE or - CFG_TUD_MSC_BUFSIZE < 4 next line can be a cause of an error.*/ - val = (*(buffer + 3) << 24) | - (*(buffer + 2) << 16) | - (*(buffer + 1) << 8) | - (*(buffer + 0) << 0); - ESP_LOGV(TAG, "Transfer 0x%08x -> FIFO%d", val, epnum); - USB0.fifo[epnum][0] = val; //copy and next buffer address - buffer += 4; - bytes2fifo_left -= 4; - } - // USB0.dtknqr4_fifoemptymsk |= (1 << epnum); - } else { - // Each complete packet for OUT xfers triggers XFRC. - USB0.out_ep_reg[epnum].doeptsiz = USB_PKTCNT0_M | - ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S); - USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M; - } - return true; -} - -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) { - in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M); - } else { - // Stop transmitting packets and NAK IN xfers. - in_ep[epnum].diepctl |= USB_DI_SNAK1_M; - while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0) - ; - - // Disable the endpoint. Note that both SNAK and STALL are set here. - in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M | - USB_D_EPDIS1_M); - while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0) - ; - in_ep[epnum].diepint = USB_D_EPDISBLD0_M; - } - - // Flush the FIFO, and wait until we have confirmed it cleared. - USB0.grstctl |= ((epnum - 1) << USB_TXFNUM_S); - USB0.grstctl |= USB_TXFFLSH_M; - while ((USB0.grstctl & USB_TXFFLSH_M) != 0) - ; - } else { - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) { - out_ep[epnum].doepctl |= USB_STALL0_M; - } else { - // Asserting GONAK is required to STALL an OUT endpoint. - // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt - // anyway, and it can't be cleared by user code. If this while loop never - // finishes, we have bigger problems than just the stack. - USB0.dctl |= USB_SGOUTNAK_M; - while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0) - ; - - // Ditto here- disable the endpoint. Note that only STALL and not SNAK - // is set here. - out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M); - while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0) - ; - out_ep[epnum].doepint = USB_EPDISBLD0_M; - - // Allow other OUT endpoints to keep receiving. - USB0.dctl |= USB_CGOUTNAK_M; - } - } -} - -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - in_ep[epnum].diepctl &= ~USB_D_STALL1_M; - - uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S; - // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt - // and bulk endpoints. - if (eptype == 2 || eptype == 3) { - in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M; - } - } else { - out_ep[epnum].doepctl &= ~USB_STALL1_M; - - uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S; - // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt - // and bulk endpoints. - if (eptype == 2 || eptype == 3) { - out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M; - } - } -} - -/*------------------------------------------------------------------*/ - -static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size) -{ - ESP_EARLY_LOGV(TAG, "USB - receive_packet"); - uint32_t *rx_fifo = USB0.fifo[0]; - - // See above TODO - // uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos; - // xfer->queued_len = xfer->total_len - remaining; - - uint16_t remaining = xfer->total_len - xfer->queued_len; - uint16_t to_recv_size; - - if (remaining <= xfer->max_size) { - // Avoid buffer overflow. - to_recv_size = (xfer_size > remaining) ? remaining : xfer_size; - } else { - // Room for full packet, choose recv_size based on what the microcontroller - // claims. - to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size; - } - - uint8_t to_recv_rem = to_recv_size % 4; - uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem; - - // Do not assume xfer buffer is aligned. - uint8_t *base = (xfer->buffer + xfer->queued_len); - - // This for loop always runs at least once- skip if less than 4 bytes - // to collect. - if (to_recv_size >= 4) { - for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) { - uint32_t tmp = (*rx_fifo); - base[i] = tmp & 0x000000FF; - base[i + 1] = (tmp & 0x0000FF00) >> 8; - base[i + 2] = (tmp & 0x00FF0000) >> 16; - base[i + 3] = (tmp & 0xFF000000) >> 24; - } - } - - // Do not read invalid bytes from RX FIFO. - if (to_recv_rem != 0) { - uint32_t tmp = (*rx_fifo); - uint8_t *last_32b_bound = base + to_recv_size_aligned; - - last_32b_bound[0] = tmp & 0x000000FF; - if (to_recv_rem > 1) { - last_32b_bound[1] = (tmp & 0x0000FF00) >> 8; - } - if (to_recv_rem > 2) { - last_32b_bound[2] = (tmp & 0x00FF0000) >> 16; - } - } - - xfer->queued_len += xfer_size; - - // Per USB spec, a short OUT packet (including length 0) is always - // indicative of the end of a transfer (at least for ctl, bulk, int). - xfer->short_packet = (xfer_size < xfer->max_size); -} - -static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num) -{ - - ESP_EARLY_LOGV(TAG, "USB - transmit_packet"); - uint32_t *tx_fifo = USB0.fifo[0]; - - uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S; - xfer->queued_len = xfer->total_len - remaining; - - uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining; - uint8_t to_xfer_rem = to_xfer_size % 4; - uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem; - - // Buffer might not be aligned to 32b, so we need to force alignment - // by copying to a temp var. - uint8_t *base = (xfer->buffer + xfer->queued_len); - - // This for loop always runs at least once- skip if less than 4 bytes - // to send off. - if (to_xfer_size >= 4) { - for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) { - uint32_t tmp = base[i] | (base[i + 1] << 8) | - (base[i + 2] << 16) | (base[i + 3] << 24); - (*tx_fifo) = tmp; - } - } - - // Do not read beyond end of buffer if not divisible by 4. - if (to_xfer_rem != 0) { - uint32_t tmp = 0; - uint8_t *last_32b_bound = base + to_xfer_size_aligned; - - tmp |= last_32b_bound[0]; - if (to_xfer_rem > 1) { - tmp |= (last_32b_bound[1] << 8); - } - if (to_xfer_rem > 2) { - tmp |= (last_32b_bound[2] << 16); - } - - (*tx_fifo) = tmp; - } -} - -static void read_rx_fifo(void) -{ - // Pop control word off FIFO (completed xfers will have 2 control words, - // we only pop one ctl word each interrupt). - volatile uint32_t ctl_word = USB0.grxstsp; - uint8_t pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S; - uint8_t epnum = (ctl_word & USB_CHNUM_M) >> USB_CHNUM_S; - uint16_t bcnt = (ctl_word & USB_BCNT_M) >> USB_BCNT_S; - switch (pktsts) { - case 0x01: // Global OUT NAK (Interrupt) - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK"); - break; - case 0x02: { // Out packet recvd - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet"); - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - receive_packet(xfer, bcnt); - } - break; - case 0x03: // Out packet done (Interrupt) - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done"); - break; - case 0x04: // Setup packet done (Interrupt) - if (s_setup_phase == 0) { // only if setup is started - s_setup_phase = 1; - ESP_EARLY_LOGV(TAG, "TUSB IRQ - setup_phase 1"); //finished - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done"); - } - - break; - case 0x06: { // Setup packet recvd - s_setup_phase = 0; - ESP_EARLY_LOGV(TAG, "TUSB IRQ - setup_phase 0"); // new setup process - // For some reason, it's possible to get a mismatch between - // how many setup packets were received versus the location - // of the Setup packet done word. This leads to situations - // where stale setup packets are in the RX FIFO that were received - // after the core loaded the Setup packet done word. Workaround by - // only accepting one setup packet at a time for now. - _setup_packet[0] = (USB0.grxstsp); - _setup_packet[1] = (USB0.grxstsp); - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x", - _setup_packet[0], _setup_packet[1]); - } - break; - default: // Invalid, do something here, like breakpoint? - break; - } -} - -static void handle_epout_ints(void) -{ - // GINTSTS will be cleared with DAINT == 0 - // DAINT for a given EP clears when DOEPINTx is cleared. - // DOEPINT will be cleared when DAINT's out bits are cleared. - for (int n = 0; n < USB_OUT_EP_NUM; n++) { - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); - if (USB0.daint & (1 << (16 + n))) { - // SETUP packet Setup Phase done. - if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) { - USB0.out_ep_reg[n].doepint |= USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear - if (s_setup_phase == 1) { // only if setup is done, but not handled - s_setup_phase = 2; - ESP_EARLY_LOGV(TAG, "TUSB IRQ - setup_phase 2"); // sending to a handling queue - ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - Setup Phase done (irq-s 0x%08x)", USB0.out_ep_reg[n].doepint); - dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true); - } - readyfor1setup_pkg(0); - } - - // OUT XFER complete (single packet).q - if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) { - - ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)"); - USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M; - - // Transfer complete if short packet or total len is transferred - if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) { - xfer->short_packet = false; - dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true); - } else { - // Schedule another packet to be received. - USB0.out_ep_reg[n].doeptsiz = USB_PKTCNT0_M | - ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S); - USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M; - } - } - } - } -} - -static void handle_epin_ints(void) -{ - - // GINTSTS will be cleared with DAINT == 0 - // DAINT for a given EP clears when DIEPINTx is cleared. - // IEPINT will be cleared when DAINT's out bits are cleared. - for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) { - xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN]; - - if (USB0.daint & (1 << (0 + n))) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n); - // IN XFER complete (entire xfer). - if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!"); - USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M; - // USB0.dtknqr4_fifoemptymsk &= ~(1 << n); // Turn off TXFE b/c xfer inactive. - dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); - } - - // XFER FIFO empty - if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!"); - USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M; - transmit_packet(xfer, &USB0.in_ep_reg[n], n); - } - } - } -} - - -static void dcd_int_handler(void) -{ - uint32_t int_status = USB0.gintsts; - uint32_t int_msk = USB0.gintmsk; - - if (int_status & USB_DISCONNINT_M) { - ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected"); - USB0.gintsts = USB_DISCONNINT_M; - } - - if (int_status & USB_USBRST_M) { - - ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset"); - USB0.gintsts = USB_USBRST_M; - bus_reset(); - } - - if (int_status & USB_RESETDET_M) { - ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend"); - USB0.gintsts = USB_RESETDET_M; - bus_reset(); - } - - if (int_status & USB_ENUMDONE_M) { - // ENUMDNE detects speed of the link. For full-speed, we - // always expect the same value. This interrupt is considered - // the end of reset. - USB0.gintsts = USB_ENUMDONE_M; - enum_done_processing(); - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); - } - - if (int_status & USB_SOF_M) { - USB0.gintsts = USB_SOF_M; - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); // do nothing actually - } - - if ((int_status & USB_RXFLVI_M) & (int_msk & USB_RXFLVIMSK_M)) { - ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!"); - read_rx_fifo(); - } - - // OUT endpoint interrupt handling. - if (int_status & USB_OEPINT_M) { - ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!"); - handle_epout_ints(); - } - - // IN endpoint interrupt handling. - if (int_status & USB_IEPINT_M) { - ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!"); - handle_epin_ints(); - } - - // Without handling - USB0.gintsts |= USB_CURMOD_INT_M | - USB_MODEMIS_M | - USB_OTGINT_M | - USB_NPTXFEMP_M | - USB_GINNAKEFF_M | - USB_GOUTNAKEFF | - USB_ERLYSUSP_M | - USB_USBSUSP_M | - USB_ISOOUTDROP_M | - USB_EOPF_M | - USB_EPMIS_M | - USB_INCOMPISOIN_M | - USB_INCOMPIP_M | - USB_FETSUSP_M | - USB_PTXFEMP_M; -} - -void dcd_int_enable(uint8_t rhport) -{ - (void)rhport; - esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t)dcd_int_handler, NULL, &usb_ih); -} - -void dcd_int_disable(uint8_t rhport) -{ - (void)rhport; - esp_intr_free(usb_ih); -} diff --git a/components/tinyusb/tinyusb b/components/tinyusb/tinyusb index 28f89e1347..a2ba3dcccc 160000 --- a/components/tinyusb/tinyusb +++ b/components/tinyusb/tinyusb @@ -1 +1 @@ -Subproject commit 28f89e13473d40637574bcbfe4142633b39899fd +Subproject commit a2ba3dccccf94022d31e939fa2ce4dca5f0a34f0 diff --git a/examples/peripherals/usb/tusb_console/CMakeLists.txt b/examples/peripherals/usb/tusb_console/CMakeLists.txt new file mode 100644 index 0000000000..278c00c5ec --- /dev/null +++ b/examples/peripherals/usb/tusb_console/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(tusb_console) diff --git a/examples/peripherals/usb/tusb_console/README.md b/examples/peripherals/usb/tusb_console/README.md new file mode 100644 index 0000000000..4b656c0147 --- /dev/null +++ b/examples/peripherals/usb/tusb_console/README.md @@ -0,0 +1,87 @@ +| Supported Targets | ESP32-S2 | +| ----------------- | -------- | + +# TinyUSB Sample Descriptor + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to set up ESP32-S2 chip to get log output via Serial Device connection + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Hardware Required + +- Any board with the ESP32-S2 chip with USB connectors or with exposed USB's D+ and D- (DATA+/DATA-) pins. + +If the board has no USB connector, but has the pins connect pins directly to the host (e.g. with DIY cable from any USB connection cable) + +``` +ESP32-S2 BOARD USB CONNECTOR (type A) + -- + | || VCC + [GPIO 19] --------> | || D- + [GPIO 20] --------> | || D+ + | || GND + -- +``` + +You can also use power from the USB connector. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Serial Connection + +After program's start and getting of the message of readiness (`Serial device is ready to connect`) you can connect to the board using any serial port terminal application (e.g. CoolTerm). + +Note: if you want to send data to the target see how to implement it via `tud_cdc_rx_cb` at the `tusb_serial_device` example. + +## Example Output + +After the flashing you should see the output at idf monitor: + +``` +I (340) example: USB initialization +I (340) TinyUSB: Driver installation... +I (340) TinyUSB - Descriptors Control: Setting of a descriptor: +.bDeviceClass = 239 +.bDeviceSubClass = 2, +.bDeviceProtocol = 1, +.bMaxPacketSize0 = 64, +.idVendor = 0x0000303a, +.idProduct = 0x00004001, +.bcdDevice = 0x00000100, +.iManufacturer = 0x01, +.iProduct = 0x02, +.iSerialNumber = 0x03, +.bNumConfigurations = 0x01 + +I (373) TinyUSB: Driver installed +I (373) example: USB initialization DONE +I (383) example: log -> UART +example: print -> stdout +example: print -> stderr +... + +``` + +Other log will be printed to USB: +``` +I (5382) example: log -> USB +example: print -> stdout +example: print -> stderr +... +``` diff --git a/examples/peripherals/usb/tusb_console/main/CMakeLists.txt b/examples/peripherals/usb/tusb_console/main/CMakeLists.txt new file mode 100644 index 0000000000..f1ad99b555 --- /dev/null +++ b/examples/peripherals/usb/tusb_console/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "tusb_console_main.c" + INCLUDE_DIRS .) diff --git a/examples/peripherals/usb/tusb_console/main/tusb_console_main.c b/examples/peripherals/usb/tusb_console/main/tusb_console_main.c new file mode 100644 index 0000000000..cefb349c90 --- /dev/null +++ b/examples/peripherals/usb/tusb_console/main/tusb_console_main.c @@ -0,0 +1,58 @@ +/* USB Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +// DESCRIPTION: +// This example contains minimal code to make ESP32-S2 based device +// recognizable by USB-host devices as a USB Serial Device printing output from +// the application. + +#include +#include +#include +#include "esp_log.h" +#include "esp_vfs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "tinyusb.h" +#include "tusb_cdc_acm.h" +#include "tusb_console.h" +#include "sdkconfig.h" + +static const char *TAG = "example"; + +void app_main(void) +{ + /* Setting TinyUSB up */ + ESP_LOGI(TAG, "USB initialization"); + + tinyusb_config_t tusb_cfg = { 0 }; // the configuration uses default values + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + tinyusb_config_cdcacm_t amc_cfg = { 0 }; // the configuration uses default values + ESP_ERROR_CHECK(tusb_cdc_acm_init(&amc_cfg)); + + ESP_LOGI(TAG, "USB initialization DONE"); + while (1) { + ESP_LOGI(TAG, "log -> UART"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stdout, "example: print -> stdout\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stderr, "example: print -> stderr\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + + esp_tusb_init_console(TINYUSB_CDC_ACM_0); // log to usb + ESP_LOGI(TAG, "log -> USB"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stdout, "example: print -> stdout\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + fprintf(stderr, "example: print -> stderr\n"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + esp_tusb_deinit_console(TINYUSB_CDC_ACM_0); // log to uart + } +} diff --git a/examples/peripherals/usb/tusb_console/sdkconfig.defaults b/examples/peripherals/usb/tusb_console/sdkconfig.defaults new file mode 100644 index 0000000000..25b9a19cb0 --- /dev/null +++ b/examples/peripherals/usb/tusb_console/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_IDF_TARGET="esp32s2" +CONFIG_USB_ENABLED=y +CONFIG_USB_CDC_ENABLED=y \ No newline at end of file diff --git a/examples/peripherals/usb/tusb_sample_descriptor/README.md b/examples/peripherals/usb/tusb_sample_descriptor/README.md index 8f9b08df11..6b463f195d 100644 --- a/examples/peripherals/usb/tusb_sample_descriptor/README.md +++ b/examples/peripherals/usb/tusb_sample_descriptor/README.md @@ -7,14 +7,27 @@ This example is demonstrating how to set up ESP32-S2 chip to work as a Generic USB Device with a user-defined descriptor. You can specify a manufacturer, device's name, ID and other USB-devices parameters responsible for identification by host. - As a USB stack, a TinyUSB component is used. ## How to use example ### Hardware Required -- Any board with the ESP32-S2 chip +- Any board with the ESP32-S2 chip with USB connectors or with exposed USB's D+ and D- (DATA+/DATA-) pins. + +If the board has no USB connector, but has the pins connect pins directly to the host (e.g. with DIY cable from any USB connection cable) + +``` +ESP32-S2 BOARD USB CONNECTOR (type A) + -- + | || VCC + [GPIO 19] --------> | || D- + [GPIO 20] --------> | || D+ + | || GND + -- +``` + +You can also use power from the USB connector. ### Configure the project @@ -26,13 +39,13 @@ For the manual descriptor's configuration use the default example's settings and #### Menuconfig -If you want to set up the desctiptor using Menuconfig UI: +If you want to set up the descriptor using Menuconfig UI: 1. Execute in the terminal from the example's directory: `idf.py menuconfig` 2. Turn off `Set up a USB descriptor manually in code` parameter at `Example Configuration` -3. Folow to `Component config -> TinyUSB -> Descriptor configuration` for all available configurations. +3. Follow `Component config -> TinyUSB -> Descriptor configuration` for all available configurations. ### Build and Flash @@ -53,21 +66,20 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui After the flashing you should see the output: ``` -I (314) example: USB initialization -I (314) TUSB:descriptors_control: Setting of a descriptor: +I (349) TinyUSB: Driver installation... +I (349) TinyUSB - Descriptors Control: Setting of a descriptor: .bDeviceClass = 0 .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, .idVendor = 0x0000303a, -.idProduct = 0x00004000, -.bcdDevice = 0x00000100, +.idProduct = 0x00003000, +.bcdDevice = 0x00000101, .iManufacturer = 0x01, .iProduct = 0x02, .iSerialNumber = 0x03, .bNumConfigurations = 0x01 -I (344) example: USB initialization DONE -I (354) example: USB task started - +I (389) TinyUSB: Driver installed +I (389) example: USB initialization DONE ``` diff --git a/examples/peripherals/usb/tusb_sample_descriptor/main/CMakeLists.txt b/examples/peripherals/usb/tusb_sample_descriptor/main/CMakeLists.txt index 649d1f7610..6581258cb9 100644 --- a/examples/peripherals/usb/tusb_sample_descriptor/main/CMakeLists.txt +++ b/examples/peripherals/usb/tusb_sample_descriptor/main/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "tusb_sample_descriptor.c" - INCLUDE_DIRS . ${COMPONENT_DIR}) +idf_component_register(SRCS "tusb_sample_descriptor_main.c" + INCLUDE_DIRS .) diff --git a/examples/peripherals/usb/tusb_sample_descriptor/main/tusb_sample_descriptor.c b/examples/peripherals/usb/tusb_sample_descriptor/main/tusb_sample_descriptor_main.c similarity index 82% rename from examples/peripherals/usb/tusb_sample_descriptor/main/tusb_sample_descriptor.c rename to examples/peripherals/usb/tusb_sample_descriptor/main/tusb_sample_descriptor_main.c index 1d82721db4..c0923529af 100644 --- a/examples/peripherals/usb/tusb_sample_descriptor/main/tusb_sample_descriptor.c +++ b/examples/peripherals/usb/tusb_sample_descriptor/main/tusb_sample_descriptor_main.c @@ -9,26 +9,16 @@ #include #include "esp_log.h" +#include "driver/gpio.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "driver/gpio.h" -#include "sdkconfig.h" #include "tinyusb.h" +#include "sdkconfig.h" static const char *TAG = "example"; -// USB Device Driver task -// This top level thread processes all usb events and invokes callbacks -static void usb_device_task(void *param) { - (void)param; - ESP_LOGI(TAG, "USB task started"); - while (1) { - tud_task(); // RTOS forever loop - } -} - -void app_main(void) { - +void app_main(void) +{ ESP_LOGI(TAG, "USB initialization"); #if CONFIG_EXAMPLE_MANUAL_DESC @@ -49,7 +39,8 @@ void app_main(void) { .iProduct = 0x02, // see string_descriptor[2] bellow .iSerialNumber = 0x03, // see string_descriptor[3] bellow - .bNumConfigurations = 0x01}; + .bNumConfigurations = 0x01 + }; tusb_desc_strarray_device_t my_string_descriptor = { // array of pointer to string descriptors @@ -77,8 +68,4 @@ void app_main(void) { ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); ESP_LOGI(TAG, "USB initialization DONE"); - - // Create a task for tinyusb device stack: - xTaskCreate(usb_device_task, "usbd", 4096, NULL, 5, NULL); - return; } diff --git a/examples/peripherals/usb/tusb_serial_device/CMakeLists.txt b/examples/peripherals/usb/tusb_serial_device/CMakeLists.txt new file mode 100644 index 0000000000..34f0d9ebe0 --- /dev/null +++ b/examples/peripherals/usb/tusb_serial_device/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(tusb_serial_device) diff --git a/examples/peripherals/usb/tusb_serial_device/README.md b/examples/peripherals/usb/tusb_serial_device/README.md new file mode 100644 index 0000000000..1f7943df98 --- /dev/null +++ b/examples/peripherals/usb/tusb_serial_device/README.md @@ -0,0 +1,79 @@ +| Supported Targets | ESP32-S2 | +| ----------------- | -------- | + +# TinyUSB Sample Descriptor + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to set up ESP32-S2 chip to work as a USB Serial Device. + +As a USB stack, a TinyUSB component is used. + +## How to use example + +### Hardware Required + +- Any board with the ESP32-S2 chip with USB connectors or with exposed USB's D+ and D- (DATA+/DATA-) pins. + +If the board has no USB connector, but has the pins connect pins directly to the host (e.g. with DIY cable from any USB connection cable) + +``` +ESP32-S2 BOARD USB CONNECTOR (type A) + -- + | || VCC + [GPIO 19] --------> | || D- + [GPIO 20] --------> | || D+ + | || GND + -- +``` + +You can also use power from the USB connector. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +```bash +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Serial Connection + +After program's start and getting of the message of readiness (`Serial device is ready to connect`) you can connect to the board using any serial port terminal application (e.g. CoolTerm). + +## Example Output + +After the flashing you should see the output: + +``` +I (346) example: USB initialization +I (346) TinyUSB: Driver installation... +I (346) TinyUSB - Descriptors Control: Setting of a descriptor: +.bDeviceClass = 239 +.bDeviceSubClass = 2, +.bDeviceProtocol = 1, +.bMaxPacketSize0 = 64, +.idVendor = 0x0000303a, +.idProduct = 0x00004001, +.bcdDevice = 0x00000100, +.iManufacturer = 0x01, +.iProduct = 0x02, +.iSerialNumber = 0x03, +.bNumConfigurations = 0x01 + +I (362) TinyUSB: Driver installed +I (362) example: USB initialization DONE +I (922) example: Line state changed! dtr:0, rst:0 +``` + +Let's try to send a string "espressif" and get the return string in your console on PC: + +``` +I (18346) example: Got data (9 bytes): espressif +``` diff --git a/examples/peripherals/usb/tusb_serial_device/main/CMakeLists.txt b/examples/peripherals/usb/tusb_serial_device/main/CMakeLists.txt new file mode 100644 index 0000000000..a3d8eb603b --- /dev/null +++ b/examples/peripherals/usb/tusb_serial_device/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "tusb_serial_device_main.c" + INCLUDE_DIRS .) diff --git a/examples/peripherals/usb/tusb_serial_device/main/tusb_serial_device_main.c b/examples/peripherals/usb/tusb_serial_device/main/tusb_serial_device_main.c new file mode 100644 index 0000000000..8d5652b404 --- /dev/null +++ b/examples/peripherals/usb/tusb_serial_device/main/tusb_serial_device_main.c @@ -0,0 +1,74 @@ +/* USB Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +// DESCRIPTION: +// This example contains minimal code to make ESP32-S2 based device +// recognizable by USB-host devices as a USB Serial Device. + +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "tinyusb.h" +#include "tusb_cdc_acm.h" +#include "sdkconfig.h" + +static const char *TAG = "example"; +static uint8_t buf[CONFIG_USB_CDC_RX_BUFSIZE + 1]; + +void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) +{ + /* initialization */ + size_t rx_size = 0; + + /* read */ + esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_USB_CDC_RX_BUFSIZE, &rx_size); + if (ret == ESP_OK) { + buf[rx_size] = '\0'; + ESP_LOGI(TAG, "Got data (%d bytes): %s", rx_size, buf); + } else { + ESP_LOGE(TAG, "Read error"); + } + + /* write back */ + tinyusb_cdcacm_write_queue(itf, buf, rx_size); + tinyusb_cdcacm_write_flush(itf, portMAX_DELAY); +} + +void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) +{ + int dtr = event->line_state_changed_data.dtr; + int rst = event->line_state_changed_data.rts; + ESP_LOGI(TAG, "Line state changed! dtr:%d, rst:%d", dtr, rst); +} + +void app_main(void) +{ + ESP_LOGI(TAG, "USB initialization"); + tinyusb_config_t tusb_cfg = {}; // the configuration using default values + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + tinyusb_config_cdcacm_t amc_cfg = { + .usb_dev = TINYUSB_USBDEV_0, + .cdc_port = TINYUSB_CDC_ACM_0, + .rx_unread_buf_sz = 64, + .callback_rx = &tinyusb_cdc_rx_callback, // the first way to register a callback + .callback_rx_wanted_char = NULL, + .callback_line_state_changed = NULL, + .callback_line_coding_changed = NULL + }; + + ESP_ERROR_CHECK(tusb_cdc_acm_init(&amc_cfg)); + /* the second way to register a callback */ + ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( + TINYUSB_CDC_ACM_0, + CDC_EVENT_LINE_STATE_CHANGED, + &tinyusb_cdc_line_state_changed_callback)); + ESP_LOGI(TAG, "USB initialization DONE"); +} diff --git a/examples/peripherals/usb/tusb_serial_device/sdkconfig.defaults b/examples/peripherals/usb/tusb_serial_device/sdkconfig.defaults new file mode 100644 index 0000000000..25b9a19cb0 --- /dev/null +++ b/examples/peripherals/usb/tusb_serial_device/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_IDF_TARGET="esp32s2" +CONFIG_USB_ENABLED=y +CONFIG_USB_CDC_ENABLED=y \ No newline at end of file diff --git a/tools/ci/check_examples_cmake_make-cmake_ignore.txt b/tools/ci/check_examples_cmake_make-cmake_ignore.txt new file mode 100644 index 0000000000..9552fb642d --- /dev/null +++ b/tools/ci/check_examples_cmake_make-cmake_ignore.txt @@ -0,0 +1,6 @@ +components/ +common_components/ +cxx/experimental/experimental_cpp_component/ +main/ +build_system/cmake/ +mb_example_common/ diff --git a/tools/ci/check_examples_cmake_make-make_ignore.txt b/tools/ci/check_examples_cmake_make-make_ignore.txt new file mode 100644 index 0000000000..5924a202eb --- /dev/null +++ b/tools/ci/check_examples_cmake_make-make_ignore.txt @@ -0,0 +1,2 @@ +build_system/cmake +temp_ diff --git a/tools/ci/check_examples_cmake_make.py b/tools/ci/check_examples_cmake_make.py new file mode 100644 index 0000000000..efcd40837e --- /dev/null +++ b/tools/ci/check_examples_cmake_make.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python + +import os +import sys +import pprint +import json +import subprocess + +# ============================================================================= +# Service funcs +# ============================================================================= + + +def _build_path(path, *paths): + return str(os.path.normpath(os.path.join(path, *paths)).replace("\\", "/")) + + +def _unify_paths(path_list): + return [_build_path(p) for p in path_list] + + +def _exclude_by_pat_list(path_list, ignore_list): + print("- Applying ignore list") + path_list_res = list(path_list) + for ign in ignore_list: + if len(ign.strip()): + for p in path_list: + if p.find(ign) != -1: + try: + path_list_res.remove(p) + except ValueError: + pass + return path_list_res + + +def _file2linelist(path): + with open(path) as f: + lines = [line.rstrip() for line in f] + return [str(line) for line in lines] + + +# ============================================================================= +# Test funcs +# ============================================================================= + + +def get_idf_path(path, *paths): + IDF_PATH = os.getenv('IDF_PATH') + return _build_path(IDF_PATH, path, *paths) + + +def _get_apps(target, build_system): + print("- Getting paths of apps") + output = subprocess.check_output( + sys.executable + " " + os.getenv('IDF_PATH') + + "/tools/find_apps.py -p examples --recursive --target %s --build-system %s" + % (target, build_system), + shell=True) + o_list = output.split("\n") + json_list = [] + for j in o_list: + if j: + json_list.append(json.loads(j)) + app_paths = [] + for j in json_list: + app_paths.append(j['app_dir']) + return _unify_paths(app_paths) + + +def get_apps(target, build_system, ignorelist): + apps = _get_apps(target, build_system) + if len(ignorelist): + return _exclude_by_pat_list(apps, ignorelist) + else: + return apps + + +def get_cmake_ignore_list(): + print("- Getting CMake ignore list") + return _file2linelist( + get_idf_path("tools", "ci", + "check_examples_cmake_make-cmake_ignore.txt")) + + +def get_make_ignore_list(): + print("- Getting Make ignore list") + return _file2linelist( + get_idf_path("tools", "ci", + "check_examples_cmake_make-make_ignore.txt")) + + +def diff(first, second): + print("- Comparing...") + first = set(first) + second = set(second) + res = list(first - second) + list(second - first) + return res + + +def main(): + cmake_ignore = get_cmake_ignore_list() + make_ignore = get_make_ignore_list() + cmakes = get_apps("esp32", "cmake", cmake_ignore) + makes = get_apps("esp32", "make", make_ignore) + + res = diff(cmakes, makes) + + if len(res): + pp = pprint.PrettyPrinter(indent=4) + print( + "[ ERROR ] Some projects are not containing Make and Cmake project files:" + ) + pp.pprint(res) + raise ValueError("Test is not passed") + else: + print("[ DONE ]") + + +if __name__ == "__main__": + main() diff --git a/tools/ci/check_examples_cmake_make.sh b/tools/ci/check_examples_cmake_make.sh deleted file mode 100755 index 53599dc19b..0000000000 --- a/tools/ci/check_examples_cmake_make.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash - -# While we support GNU Make & CMake together, check the same examples are present for both. But only for ESP32 - - -echo "- Getting paths of CMakeLists and Makefiles" -IFS= -CMAKELISTS=$( find ${IDF_PATH}/examples/ -type f -name CMakeLists.txt | grep -v "/components/" | grep -v "/common_components/" | grep -v "/cxx/experimental/experimental_cpp_component/" | grep -v "/main/" | grep -v "/build_system/cmake/" | grep -v "/mb_example_common/" | sort -n) -MAKEFILES=$( find ${IDF_PATH}/examples/ -type f -name Makefile | grep -v "/build_system/cmake/" | sort -n) -echo " [DONE]" - - -echo "- Building the ignore list" -IGNORE_LIST= -while read line -do - STL=$(grep "set[(]SUPPORTED_TARGETS" $line) - if test -n $STL # if specified SUPPORTED_TARGETS - then - WO_ESP32=$(grep -v -w "esp32" <<< $STL) - if test -n "$WO_ESP32" # if not specified esp32 at SUPPORTED_TARGETS - then # then consider that the example should not have a Makefile - PATH2IGNORE=$(/usr/bin/dirname $line) - echo " Adding to ignore $PATH2IGNORE" - IGNORE_LIST="$IGNORE_LIST$PATH2IGNORE -" - fi - fi -done <<< $CMAKELISTS -IGNORE_LIST=$(grep -v -e '^$' <<< $IGNORE_LIST) # remove empty lines -echo -e " [DONE] - Ignore list:\n$IGNORE_LIST\n" - - -echo "- Applying the Ignore list" -while read line -do - echo $line - CMAKELISTS=$(echo $CMAKELISTS | grep -v $line) - MAKEFILES=$(echo $MAKEFILES | grep -v $line) -done <<< $IGNORE_LIST -echo " [DONE]" - - -echo "- Getting paths of examples" -while read line -do - new_path="$(/usr/bin/dirname $line)" - CMAKE_EXAMPLE_PATHS="$CMAKE_EXAMPLE_PATHS$new_path -" -done <<< $CMAKELISTS -CMAKE_EXAMPLE_PATHS=$(grep -v -e '^$' <<< $CMAKE_EXAMPLE_PATHS) # remove empty lines - -while read line -do - new_path="$(/usr/bin/dirname $line)" - MAKE_EXAMPLE_PATHS="$MAKE_EXAMPLE_PATHS$new_path -" -done <<< $MAKEFILES -MAKE_EXAMPLE_PATHS=$(grep -v -e '^$' <<< $MAKE_EXAMPLE_PATHS) # remove empty lines -echo " [DONE]" - -echo "- Analysing matches" -MISMATCH=$(comm -3 <(echo "$MAKE_EXAMPLE_PATHS") <(echo "$CMAKE_EXAMPLE_PATHS")) -if [ -n "$MISMATCH" ]; then - echo " [ERROR] Some examples are not in both CMake and GNU Make:" - echo "$MISMATCH" - exit 1 -fi - -echo " [DONE] Example lists match" -exit 0 diff --git a/tools/ci/config/pre_check.yml b/tools/ci/config/pre_check.yml index cc89f17010..02497570e0 100644 --- a/tools/ci/config/pre_check.yml +++ b/tools/ci/config/pre_check.yml @@ -46,7 +46,7 @@ check_examples_cmake_make: - /^release\/v/ - /^v\d+\.\d+(\.\d+)?($|-)/ script: - - tools/ci/check_examples_cmake_make.sh + - python ${IDF_PATH}/tools/ci/check_examples_cmake_make.py check_rom_api_header: extends: .check_job_template_with_filter diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 7d9cc904b1..26831e6144 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -39,7 +39,7 @@ tools/ci/check-executable.sh tools/ci/check-line-endings.sh tools/ci/check_build_warnings.py tools/ci/check_deprecated_kconfigs.py -tools/ci/check_examples_cmake_make.sh +tools/ci/check_examples_cmake_make.py tools/ci/check_examples_rom_header.sh tools/ci/check_idf_version.sh tools/ci/check_readme_links.py