From e09fc3e47444f8b096a5cabd6114b9a4cfd4e107 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Wed, 5 Apr 2023 08:01:40 +0200 Subject: [PATCH] usb: Update CDC and MSC host examples to use drivers from ESP Registry --- .../usb/host/cdc/cdc_acm_bg96/README.md | 99 -- .../host/cdc/cdc_acm_bg96/main/CMakeLists.txt | 2 - .../host/cdc/cdc_acm_bg96/main/bg96_usb.hpp | 84 -- .../cdc_acm_bg96/main/cdc_acm_host_bg96.cpp | 208 --- .../cdc/cdc_acm_bg96/main/idf_component.yml | 3 - .../usb/host/cdc/cdc_acm_host/CMakeLists.txt | 1 - .../usb/host/cdc/cdc_acm_host/README.md | 14 +- .../host/cdc/cdc_acm_host/main/CMakeLists.txt | 2 +- .../cdc/cdc_acm_host/main/idf_component.yml | 4 + .../usb/host/cdc/cdc_acm_host/main/usb-cdc.c | 113 -- .../cdc_acm_host/main/usb_cdc_example_main.c | 179 +++ .../CMakeLists.txt | 3 +- .../usb/host/cdc/cdc_acm_vcp/README.md | 43 + .../host/cdc/cdc_acm_vcp/main/CMakeLists.txt | 9 + .../main/cdc_acm_vcp_example_main.cpp | 178 +++ .../cdc/cdc_acm_vcp/main/idf_component.yml | 7 + .../host/cdc/cdc_acm_vcp/sdkconfig.defaults | 4 + .../cdc/common/cdc_acm_host/CMakeLists.txt | 15 - .../host/cdc/common/cdc_acm_host/README.md | 46 - .../cdc/common/cdc_acm_host/cdc_acm_host.c | 1170 ----------------- .../cdc_acm_host/include/usb/cdc_acm_host.h | 305 ----- .../cdc_acm_host/include/usb/usb_types_cdc.h | 206 --- .../common/cdc_acm_host/test/CMakeLists.txt | 3 - .../cdc_acm_host/test/test_cdc_acm_host.c | 378 ------ examples/peripherals/usb/host/msc/README.md | 5 +- .../host/msc/components/msc/CMakeLists.txt | 9 - .../usb/host/msc/components/msc/README.md | 32 - .../msc/components/msc/include/msc_host.h | 169 --- .../msc/components/msc/include/msc_host_vfs.h | 44 - .../msc/private_include/diskio_usb.h | 39 - .../msc/private_include/msc_common.h | 61 - .../msc/private_include/msc_scsi_bot.h | 56 - .../host/msc/components/msc/src/diskio_usb.c | 118 -- .../host/msc/components/msc/src/msc_host.c | 553 -------- .../msc/components/msc/src/msc_host_vfs.c | 124 -- .../msc/components/msc/src/msc_scsi_bot.c | 434 ------ .../msc/components/msc/test/CMakeLists.txt | 3 - .../host/msc/components/msc/test/msc_device.c | 295 ----- .../msc/components/msc/test/test_common.h | 19 - .../host/msc/components/msc/test/test_msc.c | 321 ----- .../usb/host/msc/main/CMakeLists.txt | 2 +- .../usb/host/msc/main/idf_component.yml | 4 + .../usb/host/msc/main/msc_example_main.c | 141 +- 43 files changed, 528 insertions(+), 4977 deletions(-) delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml delete mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c rename examples/peripherals/usb/host/cdc/{cdc_acm_bg96 => cdc_acm_vcp}/CMakeLists.txt (76%) create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml create mode 100644 examples/peripherals/usb/host/cdc/cdc_acm_vcp/sdkconfig.defaults delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c delete mode 100644 examples/peripherals/usb/host/msc/components/msc/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/msc/components/msc/README.md delete mode 100644 examples/peripherals/usb/host/msc/components/msc/include/msc_host.h delete mode 100644 examples/peripherals/usb/host/msc/components/msc/include/msc_host_vfs.h delete mode 100644 examples/peripherals/usb/host/msc/components/msc/private_include/diskio_usb.h delete mode 100644 examples/peripherals/usb/host/msc/components/msc/private_include/msc_common.h delete mode 100644 examples/peripherals/usb/host/msc/components/msc/private_include/msc_scsi_bot.h delete mode 100644 examples/peripherals/usb/host/msc/components/msc/src/diskio_usb.c delete mode 100644 examples/peripherals/usb/host/msc/components/msc/src/msc_host.c delete mode 100644 examples/peripherals/usb/host/msc/components/msc/src/msc_host_vfs.c delete mode 100644 examples/peripherals/usb/host/msc/components/msc/src/msc_scsi_bot.c delete mode 100644 examples/peripherals/usb/host/msc/components/msc/test/CMakeLists.txt delete mode 100644 examples/peripherals/usb/host/msc/components/msc/test/msc_device.c delete mode 100644 examples/peripherals/usb/host/msc/components/msc/test/test_common.h delete mode 100644 examples/peripherals/usb/host/msc/components/msc/test/test_msc.c create mode 100644 examples/peripherals/usb/host/msc/main/idf_component.yml diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md deleted file mode 100644 index 58d7c22d79..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/README.md +++ /dev/null @@ -1,99 +0,0 @@ -| Supported Targets | ESP32-S2 | ESP32-S3 | -| ----------------- | -------- | -------- | - -# USB CDC-ACM Host Driver BG96 Example - -(See the README.md file in the upper level 'examples' directory for more information about examples.) - -This example shows how to set up ESP chip to interface with CDC-like device by using the CDC-ACM Host Driver. CDC-like devices implement a Vendor-specific class, and support a subset of the functions of a fully compliant CDC-ACM device. - -## How to use example - -### Hardware Required - -Any ESP board with USB-OTG supported and a Quectel BG96 LTE/GPS modem. - -Connect USB_D+, USB_D-, GND and +5V signals of ESP board to BG96. - -_Note:_ Quectel BG96 modem must be started after power-up by applying low pulse on PWRKEY (pin 15). - -#### Pin Assignment - -See common pin assignments for USB Device examples from [upper level](../../../README.md#common-pin-assignments). - -### 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. - -## Example Output - -After the flashing you should see the output at idf monitor: - -``` -I (276) BG96: USB Host installed -I (24446) AT: ATE0 -I (24446) AT: -OK - -I (24526) AT: -+QIND: SMS DONE - -I (24646) AT: -APP RDY - -I (25446) BG96: Sending AT -I (25446) AT: -OK - -I (26446) BG96: Enabling GNSS -I (26446) AT: -OK - -GPVTG Sentence: - Track [deg]: 0.00 - Speed [kmph]: 0.00 - Speed [knots]: 0.00 -GPGSA Sentence: - Mode: A - Fix: 1 - PDOP: 0.00 - HDOP: 0.00 - VDOP: 0.00 -GPGGA sentence -Number of satellites: 0 -Altitude: 0.000000 -GPRMC sentence -Longitude: - Degrees: 0 - Minutes: 0.000000 - Cardinal: -Latitude: - Degrees: 0 - Minutes: 0.000000 - Cardinal: -Date & Time: 00 Jan 00:00:00 1900 -Speed, in Knots: 0.000000 -Track, in degrees: 0.000000 -Magnetic Variation: - Degrees: 0.000000 - Cardinal: -Invalid Magnetic Variation Direction! -Adjusted Track (heading): 0.000000 -I (27446) BG96: Sending AT+GSN -I (27446) AT: -860517045660414 - -OK -... - -``` diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt deleted file mode 100644 index 8027d6161d..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "cdc_acm_host_bg96.cpp" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp deleted file mode 100644 index dc3cefa96c..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/bg96_usb.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#pragma once - -#include "usb/cdc_acm_host.h" -#include "esp_log.h" - -#define BG96_VID (0x2C7C) -#define BG96_PID (0x0296) -#define BG96_AT_INTERFACE (2) -#define BG96_NMEA_INTERFACE (1) - -class Bg96Usb { -public: - - explicit Bg96Usb() : at_opened(false) { - }; - - esp_err_t at_start(cdc_acm_data_callback_t data_cb, void *user_arg) - { - // This driver doesn't support CDC notifications. This can lead to silent failures - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 10000, - .out_buffer_size = 64, - .event_cb = NULL, - .data_cb = data_cb, - .user_arg = user_arg, - }; - ESP_ERROR_CHECK(this->at_port.open_vendor_specific(BG96_VID, BG96_PID, BG96_AT_INTERFACE, &dev_config)); - this->at_opened = true; - - // Some FW versions have Echo enabled by default. Disable it with ATE0 command - ESP_LOGD("BG96_USB", "Turning off echo with ATE0"); - ESP_ERROR_CHECK(this->at_port.tx_blocking((uint8_t *)"ATE0\r", 5, 1000)); - vTaskDelay(100); - return ESP_OK; - } - - void at_stop() - { - this->at_port.close(); - this->at_opened = false; - } - - esp_err_t at_write(uint8_t *data, size_t len) - { - ESP_LOG_BUFFER_HEXDUMP("BG96_USB", data, len, ESP_LOG_DEBUG); - return this->at_port.tx_blocking(data, len, 1000); - } - - esp_err_t gnss_start(cdc_acm_data_callback_t data_cb) - { - if (!this->at_opened) { - return ESP_ERR_INVALID_STATE; - } - - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 1000, - .out_buffer_size = 0, // Read-only - .event_cb = NULL, - .data_cb = data_cb, - .user_arg = this, - }; - ESP_ERROR_CHECK(this->nmea_port.open_vendor_specific(BG96_VID, BG96_PID, BG96_NMEA_INTERFACE, &dev_config)); - return this->at_port.tx_blocking((uint8_t*)"AT+QGPS=1\r", 10, 1000); - } - - esp_err_t gnss_stop() - { - esp_err_t ret = this->at_port.tx_blocking((uint8_t*)"AT+QGPSEND\r", 11, 1000); - this->nmea_port.close(); - return ret; - } - -protected: - CdcAcmDevice at_port; // Main control port for AT commands - CdcAcmDevice nmea_port; // Read only port for NMEA messages -private: - bool at_opened; -}; diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp deleted file mode 100644 index 5e7581788c..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/cdc_acm_host_bg96.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" - -#include "usb/usb_host.h" -#include "bg96_usb.hpp" - -#include "nmea.h" -#include "gpgll.h" -#include "gpgga.h" -#include "gprmc.h" -#include "gpgsa.h" -#include "gpvtg.h" -#include "gptxt.h" -#include "gpgsv.h" - -#define EXAMPLE_USB_HOST_PRIORITY 20 - -static const char* TAG = "BG96"; - -static char fmt_buf[32]; - -/* ------------------------------- Callbacks -------------------------------- */ - -static void handle_rx(uint8_t *data, size_t data_len, void *user_arg) -{ - data[data_len] = '\0'; - ESP_LOGI("AT", "%s", data); -} - -static void handle_gps(uint8_t* data, size_t data_len, void *user_arg) -{ - // handle nmea_data - nmea_s *nmea_data = nmea_parse((char *)data, data_len, 0); - if (nmea_data == NULL) { - printf("Failed to parse the sentence!\n"); - printf(" Type: %.5s (%d)\n", data + 1, nmea_get_type((const char *)data)); - } else { - if (nmea_data->errors != 0) { - printf("WARN: The sentence struct contains parse errors!\n"); - } - - if (NMEA_GPGGA == nmea_data->type) { - printf("GPGGA sentence\n"); - nmea_gpgga_s *gpgga = (nmea_gpgga_s *)nmea_data; - printf("Number of satellites: %d\n", gpgga->n_satellites); - printf("Altitude: %f %c\n", gpgga->altitude, gpgga->altitude_unit); - } - - if (NMEA_GPGLL == nmea_data->type) { - printf("GPGLL sentence\n"); - nmea_gpgll_s *pos = (nmea_gpgll_s *)nmea_data; - printf("Longitude:\n"); - printf(" Degrees: %d\n", pos->longitude.degrees); - printf(" Minutes: %f\n", pos->longitude.minutes); - printf(" Cardinal: %c\n", (char)pos->longitude.cardinal); - printf("Latitude:\n"); - printf(" Degrees: %d\n", pos->latitude.degrees); - printf(" Minutes: %f\n", pos->latitude.minutes); - printf(" Cardinal: %c\n", (char)pos->latitude.cardinal); - strftime(fmt_buf, sizeof(fmt_buf), "%H:%M:%S", &pos->time); - printf("Time: %s\n", fmt_buf); - } - - if (NMEA_GPRMC == nmea_data->type) { - printf("GPRMC sentence\n"); - nmea_gprmc_s *pos = (nmea_gprmc_s *)nmea_data; - printf("Longitude:\n"); - printf(" Degrees: %d\n", pos->longitude.degrees); - printf(" Minutes: %f\n", pos->longitude.minutes); - printf(" Cardinal: %c\n", (char)pos->longitude.cardinal); - printf("Latitude:\n"); - printf(" Degrees: %d\n", pos->latitude.degrees); - printf(" Minutes: %f\n", pos->latitude.minutes); - printf(" Cardinal: %c\n", (char)pos->latitude.cardinal); - strftime(fmt_buf, sizeof(fmt_buf), "%d %b %T %Y", &pos->date_time); - printf("Date & Time: %s\n", fmt_buf); - printf("Speed, in Knots: %f\n", pos->gndspd_knots); - printf("Track, in degrees: %f\n", pos->track_deg); - printf("Magnetic Variation:\n"); - printf(" Degrees: %f\n", pos->magvar_deg); - printf(" Cardinal: %c\n", (char)pos->magvar_cardinal); - double adjusted_course = pos->track_deg; - if (NMEA_CARDINAL_DIR_EAST == pos->magvar_cardinal) { - adjusted_course -= pos->magvar_deg; - } else if (NMEA_CARDINAL_DIR_WEST == pos->magvar_cardinal) { - adjusted_course += pos->magvar_deg; - } else { - printf("Invalid Magnetic Variation Direction!\n"); - } - - printf("Adjusted Track (heading): %f\n", adjusted_course); - } - - if (NMEA_GPGSA == nmea_data->type) { - nmea_gpgsa_s *gpgsa = (nmea_gpgsa_s *)nmea_data; - - printf("GPGSA Sentence:\n"); - printf(" Mode: %c\n", gpgsa->mode); - printf(" Fix: %d\n", gpgsa->fixtype); - printf(" PDOP: %.2lf\n", gpgsa->pdop); - printf(" HDOP: %.2lf\n", gpgsa->hdop); - printf(" VDOP: %.2lf\n", gpgsa->vdop); - } - - if (NMEA_GPGSV == nmea_data->type) { - nmea_gpgsv_s *gpgsv = (nmea_gpgsv_s *)nmea_data; - - printf("GPGSV Sentence:\n"); - printf(" Num: %d\n", gpgsv->sentences); - printf(" ID: %d\n", gpgsv->sentence_number); - printf(" SV: %d\n", gpgsv->satellites); - printf(" #1: %d %d %d %d\n", gpgsv->sat[0].prn, gpgsv->sat[0].elevation, gpgsv->sat[0].azimuth, - gpgsv->sat[0].snr); - printf(" #2: %d %d %d %d\n", gpgsv->sat[1].prn, gpgsv->sat[1].elevation, gpgsv->sat[1].azimuth, - gpgsv->sat[1].snr); - printf(" #3: %d %d %d %d\n", gpgsv->sat[2].prn, gpgsv->sat[2].elevation, gpgsv->sat[2].azimuth, - gpgsv->sat[2].snr); - printf(" #4: %d %d %d %d\n", gpgsv->sat[3].prn, gpgsv->sat[3].elevation, gpgsv->sat[3].azimuth, - gpgsv->sat[3].snr); - } - - if (NMEA_GPTXT == nmea_data->type) { - nmea_gptxt_s *gptxt = (nmea_gptxt_s *)nmea_data; - - printf("GPTXT Sentence:\n"); - printf(" ID: %d %d %d\n", gptxt->id_00, gptxt->id_01, gptxt->id_02); - printf(" %s\n", gptxt->text); - } - - if (NMEA_GPVTG == nmea_data->type) { - nmea_gpvtg_s *gpvtg = (nmea_gpvtg_s *)nmea_data; - - printf("GPVTG Sentence:\n"); - printf(" Track [deg]: %.2lf\n", gpvtg->track_deg); - printf(" Speed [kmph]: %.2lf\n", gpvtg->gndspd_kmph); - printf(" Speed [knots]: %.2lf\n", gpvtg->gndspd_knots); - } - - nmea_free(nmea_data); - } -} - -void usb_lib_task(void *arg) -{ - while (1) { - //Start handling system events - uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - printf("No more clients\n"); - ESP_ERROR_CHECK(usb_host_device_free_all()); - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - break; - } - } - - //Short delay to allow task to be cleaned up - vTaskDelay(10); - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - vTaskDelete(NULL); -} - -/* ---------------------------------- Main ---------------------------------- */ -extern "C" void app_main(void) -{ - //Install USB Host driver. Should only be called once in entire application - ESP_LOGI(TAG, "Installing USB Host"); - usb_host_config_t host_config = { - .skip_phy_setup = false, - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - - // Create a task that will handle USB library events - xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, EXAMPLE_USB_HOST_PRIORITY, NULL); - - ESP_LOGI(TAG, "Installing CDC-ACM driver"); - ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); - - Bg96Usb *bg96 = new Bg96Usb(); - bg96->at_start(handle_rx, NULL); - - static char text1[] = "AT\r"; - static char text2[] = "AT+GSN\r"; - - ESP_LOGI(TAG, "Sending AT"); - bg96->at_write((uint8_t *)text1, strlen(text1)); - vTaskDelay(100); - - ESP_LOGI(TAG, "Enabling GNSS"); - bg96->gnss_start(handle_gps); - - vTaskDelay(100); - ESP_LOGI(TAG, "Sending AT+GSN"); - bg96->at_write((uint8_t *)text2, strlen(text2)); -} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml deleted file mode 100644 index 4bdd9d0d51..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/main/idf_component.yml +++ /dev/null @@ -1,3 +0,0 @@ -dependencies: - idf: ">=4.4" - igrr/libnmea: ">=0.1.1" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt index 4a10de7eaf..77a40f7564 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/CMakeLists.txt @@ -4,6 +4,5 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/peripherals/usb/host/cdc/common) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(cdc_acm_host) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md index 50ed5485b1..619432b5b8 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/README.md @@ -38,18 +38,12 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui After the flashing you should see the output at idf monitor: ``` +... I (256) USB-CDC: USB Host installed I (256) USB-CDC: Opening CDC ACM device 0x303A:0x4001 -CDC Header Descriptor: - bcdCDC: 1.20 -CDC Call Descriptor: - bmCapabilities: 0x00 - bDataInterface: 1 -CDC ACM Descriptor: - bmCapabilities: 0x02 -CDC Union Descriptor: - bControlInterface: 0 - bSubordinateInterface[0]: 1 +... +Device descriptor is printed here +... I (1666) USB-CDC: Data received I (1666) USB-CDC: 0x3ffc4c20 41 54 0d |AT.| I (2666) USB-CDC: Data received diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt index 5d09b5c7fc..72a9796821 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "usb-cdc.c" +idf_component_register(SRCS "usb_cdc_example_main.c" INCLUDE_DIRS ".") diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml new file mode 100644 index 0000000000..7865f53578 --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + usb_host_cdc_acm: "2.*" + idf: ">=4.4" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c deleted file mode 100644 index 8534c4416c..0000000000 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb-cdc.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "esp_err.h" -#include "usb/usb_host.h" -#include "usb/cdc_acm_host.h" - -#define EXAMPLE_USB_HOST_PRIORITY 20 -#define EXAMPLE_USB_DEVICE_VID 0x303A // 0x303A:0x4001 (TinyUSB CDC device) -#define EXAMPLE_USB_DEVICE_PID 0x4001 - -static const char *TAG = "USB-CDC"; - -/* ------------------------------- Callbacks -------------------------------- */ -static void handle_rx(uint8_t *data, size_t data_len, void *arg) -{ - ESP_LOGI(TAG, "Data received"); - ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_INFO); -} - -void usb_lib_task(void *arg) -{ - while (1) { - //Start handling system events - uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - ESP_LOGI(TAG, "All clients deregistered"); - ESP_ERROR_CHECK(usb_host_device_free_all()); - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - break; - } - } - - //Clean up USB Host - ESP_ERROR_CHECK(usb_host_uninstall()); - vTaskDelete(NULL); -} - -/* ---------------------------------- Main ---------------------------------- */ -void app_main(void) -{ - //Install USB Host driver. Should only be called once in entire application - ESP_LOGI(TAG, "Installing USB Host"); - usb_host_config_t host_config = { - .skip_phy_setup = false, - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_ERROR_CHECK(usb_host_install(&host_config)); - - // Create a task that will handle USB library events - xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); - - ESP_LOGI(TAG, "Installing CDC-ACM driver"); - ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); - - ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); - cdc_acm_dev_hdl_t cdc_dev; - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 5000, - .out_buffer_size = 64, - .user_arg = NULL, - .event_cb = NULL, - .data_cb = handle_rx - }; - ESP_ERROR_CHECK(cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &cdc_dev)); - assert(cdc_dev); - cdc_acm_host_desc_print(cdc_dev); - vTaskDelay(100); - - // Test sending and receiving: Send AT commands, responses are handled in handle_rx callback - static char text1[] = "AT\r"; - ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (uint8_t *)text1, strlen(text1), 1000)); - vTaskDelay(100); - - static char text2[] = "AT+GSN\r"; - ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (uint8_t *)text2, strlen(text2), 1000)); - vTaskDelay(100); - - // Test Line Coding commands: Get current line coding, change it 9600 7N1 and read again - ESP_LOGI(TAG, "Setting up line coding"); - - cdc_acm_line_coding_t line_coding; - ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - line_coding.dwDTERate = 9600; - line_coding.bDataBits = 7; - line_coding.bParityType = 1; - line_coding.bCharFormat = 1; - ESP_ERROR_CHECK(cdc_acm_host_line_coding_set(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Set: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); - ESP_LOGI(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding.dwDTERate, - line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); - - ESP_ERROR_CHECK(cdc_acm_host_set_control_line_state(cdc_dev, true, false)); - - ESP_LOGI(TAG, "Example finished successfully!"); -} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c new file mode 100644 index 0000000000..01a5862202 --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "usb/usb_host.h" +#include "usb/cdc_acm_host.h" + +#define EXAMPLE_USB_HOST_PRIORITY (20) +#define EXAMPLE_USB_DEVICE_VID (0x303A) +#define EXAMPLE_USB_DEVICE_PID (0x4001) // 0x303A:0x4001 (TinyUSB CDC device) +#define EXAMPLE_USB_DEVICE_DUAL_PID (0x4002) // 0x303A:0x4002 (TinyUSB Dual CDC device) +#define EXAMPLE_TX_STRING ("CDC test string!") +#define EXAMPLE_TX_TIMEOUT_MS (1000) + +static const char *TAG = "USB-CDC"; +static SemaphoreHandle_t device_disconnected_sem; + +/** + * @brief Data received callback + * + * @param[in] data Pointer to received data + * @param[in] data_len Length of received data in bytes + * @param[in] arg Argument we passed to the device open function + * @return + * true: We have processed the received data + * false: We expect more data + */ +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) +{ + ESP_LOGI(TAG, "Data received"); + ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_INFO); + return true; +} + +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case CDC_ACM_HOST_ERROR: + ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %i", event->data.error); + break; + case CDC_ACM_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + ESP_ERROR_CHECK(cdc_acm_host_close(event->data.cdc_hdl)); + xSemaphoreGive(device_disconnected_sem); + break; + case CDC_ACM_HOST_SERIAL_STATE: + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); + break; + case CDC_ACM_HOST_NETWORK_CONNECTION: + default: + ESP_LOGW(TAG, "Unsupported CDC event: %i", event->type); + break; + } +} + +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} + +/** + * @brief Main application + * + * Here we open a USB CDC device and send some data to it + */ +void app_main(void) +{ + device_disconnected_sem = xSemaphoreCreateBinary(); + assert(device_disconnected_sem); + + // Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing CDC-ACM driver"); + ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 1000, + .out_buffer_size = 512, + .in_buffer_size = 512, + .user_arg = NULL, + .event_cb = handle_event, + .data_cb = handle_rx + }; + + while (true) { + cdc_acm_dev_hdl_t cdc_dev = NULL; + + // Open USB device from tusb_serial_device example example. Either single or dual port configuration. + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); + esp_err_t err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID); + err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_DUAL_PID, 0, &dev_config, &cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Failed to open device"); + continue; + } + } + cdc_acm_host_desc_print(cdc_dev); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test sending and receiving: responses are handled in handle_rx callback + ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(cdc_dev, (const uint8_t *)EXAMPLE_TX_STRING, strlen(EXAMPLE_TX_STRING), EXAMPLE_TX_TIMEOUT_MS)); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Test Line Coding commands: Get current line coding, change it 9600 7N1 and read again + ESP_LOGI(TAG, "Setting up line coding"); + + cdc_acm_line_coding_t line_coding; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + line_coding.dwDTERate = 9600; + line_coding.bDataBits = 7; + line_coding.bParityType = 1; + line_coding.bCharFormat = 1; + ESP_ERROR_CHECK(cdc_acm_host_line_coding_set(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Set: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_line_coding_get(cdc_dev, &line_coding)); + ESP_LOGI(TAG, "Line Get: Rate: %"PRIu32", Stop bits: %"PRIu8", Parity: %"PRIu8", Databits: %"PRIu8"", + line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits); + + ESP_ERROR_CHECK(cdc_acm_host_set_control_line_state(cdc_dev, true, false)); + + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Example finished successfully! You can reconnect the device to run again."); + xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); + } +} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/CMakeLists.txt similarity index 76% rename from examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt rename to examples/peripherals/usb/host/cdc/cdc_acm_vcp/CMakeLists.txt index bfed1da7b4..c8f80957fc 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_bg96/CMakeLists.txt +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/CMakeLists.txt @@ -4,6 +4,5 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/peripherals/usb/host/cdc/common) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(cdc_acm_host_bg96) +project(cdc_acm_vcp) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md new file mode 100644 index 0000000000..71fdfb331d --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/README.md @@ -0,0 +1,43 @@ +| Supported Targets | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# USB CDC-ACM Virtual COM Port example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to extend CDC-ACM driver for Virtual Communication Port (VCP) devices, +such as CP210x, FTDI FT23x or CH34x devices. + +The drivers are fetched from [IDF Component Registry](https://components.espressif.com/) together with VCP service that automatically loads correct driver for plugged-in device. + +## How to use example + +1. Connect your USB<->UART converter to ESP32-S2/S3, the device will be automatically enumerated and correct driver will be loaded +2. Change baudrate and other line coding parameters in [cdc_acm_vcp_example_main.cpp](main/cdc_acm_vcp_example_main.cpp) to match your needs +3. Now you can use the usual CDC-ACM API to control the device and send data. Data are received in `handle_rx` callback +4. Try disconnecting and then reconnecting of the USB device to experiment with USB hotplugging + +### Hardware Required + +* ESP board with USB-OTG supported +* Silicon Labs CP210x, FTDI FT23x or CP34x USB to UART converter + +Connect USB_D+, USB_D-, GND and +5V signals of your ESP chip to matching signals on USB to UART converter. + +#### Pin Assignment + +See common pin assignments for USB Device examples from [upper level](../../../README.md#common-pin-assignments). + +### Build and Flash + +Build this project and flash it to the USB host 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. diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt new file mode 100644 index 0000000000..25ee8c0d13 --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register( + SRCS "cdc_acm_vcp_example_main.cpp" + INCLUDE_DIRS "." + ) + +# We cannot set property CXX_STANDARD to '17' +# because CMake 3.5 (shipped with IDF 4.4) does not know this standard +# -fconcepts flag should be propagated from usb_host_vcp component... +target_compile_options(${COMPONENT_LIB} PRIVATE -fconcepts -std=gnu++17) diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp new file mode 100644 index 0000000000..f90620ec87 --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/cdc_acm_vcp_example_main.cpp @@ -0,0 +1,178 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "usb/cdc_acm_host.h" +#include "usb/vcp_ch34x.hpp" +#include "usb/vcp_cp210x.hpp" +#include "usb/vcp_ftdi.hpp" +#include "usb/vcp.hpp" +#include "usb/usb_host.h" + +using namespace esp_usb; + +// Change these values to match your needs +#define EXAMPLE_BAUDRATE (115200) +#define EXAMPLE_STOP_BITS (0) // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits +#define EXAMPLE_PARITY (0) // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space +#define EXAMPLE_DATA_BITS (8) + +namespace { +static const char *TAG = "VCP example"; +static SemaphoreHandle_t device_disconnected_sem; + +/** + * @brief Data received callback + * + * Just pass received data to stdout + * + * @param[in] data Pointer to received data + * @param[in] data_len Length of received data in bytes + * @param[in] arg Argument we passed to the device open function + * @return + * true: We have processed the received data + * false: We expect more data + */ +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) +{ + printf("%.*s", data_len, data); + return true; +} + +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case CDC_ACM_HOST_ERROR: + ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %d", event->data.error); + break; + case CDC_ACM_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + xSemaphoreGive(device_disconnected_sem); + break; + case CDC_ACM_HOST_SERIAL_STATE: + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); + break; + case CDC_ACM_HOST_NETWORK_CONNECTION: + default: break; + } +} + +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} +} + +/** + * @brief Main application + * + * This function shows how you can use Virtual COM Port drivers + */ +extern "C" void app_main(void) +{ + device_disconnected_sem = xSemaphoreCreateBinary(); + assert(device_disconnected_sem); + + // Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, NULL); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing CDC-ACM driver"); + ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + + // Register VCP drivers to VCP service + VCP::register_driver(); + VCP::register_driver(); + VCP::register_driver(); + + // Do everything else in a loop, so we can demonstrate USB device reconnections + while (true) { + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 5000, // 5 seconds, enough time to plug the device in or experiment with timeout + .out_buffer_size = 512, + .in_buffer_size = 512, + .event_cb = handle_event, + .data_cb = handle_rx, + .user_arg = NULL, + }; + + // You don't need to know the device's VID and PID. Just plug in any device and the VCP service will load correct (already registered) driver for the device + ESP_LOGI(TAG, "Opening any VCP device..."); + auto vcp = std::unique_ptr(VCP::open(&dev_config)); + + if (vcp == nullptr) { + ESP_LOGI(TAG, "Failed to open VCP device"); + continue; + } + vTaskDelay(10); + + ESP_LOGI(TAG, "Setting up line coding"); + cdc_acm_line_coding_t line_coding = { + .dwDTERate = EXAMPLE_BAUDRATE, + .bCharFormat = EXAMPLE_STOP_BITS, + .bParityType = EXAMPLE_PARITY, + .bDataBits = EXAMPLE_DATA_BITS, + }; + ESP_ERROR_CHECK(vcp->line_coding_set(&line_coding)); + + /* + Now the USB-to-UART converter is configured and receiving data. + You can use standard CDC-ACM API to interact with it. E.g. + + ESP_ERROR_CHECK(vcp->set_control_line_state(false, true)); + ESP_ERROR_CHECK(vcp->tx_blocking((uint8_t *)"Test string", 12)); + */ + + // Send some dummy data + ESP_LOGI(TAG, "Sending data through CdcAcmDevice"); + uint8_t data[] = "test_string"; + ESP_ERROR_CHECK(vcp->tx_blocking(data, sizeof(data))); + ESP_ERROR_CHECK(vcp->set_control_line_state(true, true)); + + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Done. You can reconnect the VCP device to run again."); + xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); + } +} diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml new file mode 100644 index 0000000000..81ded83041 --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + usb_host_ch34x_vcp: "^2" + usb_host_cp210x_vcp: "^2" + usb_host_ftdi_vcp: "^2" + usb_host_vcp: "^1" + idf: ">=4.4.0" diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_vcp/sdkconfig.defaults b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/sdkconfig.defaults new file mode 100644 index 0000000000..990777805d --- /dev/null +++ b/examples/peripherals/usb/host/cdc/cdc_acm_vcp/sdkconfig.defaults @@ -0,0 +1,4 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt deleted file mode 100644 index f2ff73f8fc..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -set(srcs) -set(include) -# As CONFIG_USB_OTG_SUPPORTED comes from Kconfig, it is not evaluated yet -# when components are being registered. -set(require usb) - -if(CONFIG_USB_OTG_SUPPORTED) - list(APPEND srcs "cdc_acm_host.c") - list(APPEND include "include") -endif() - -idf_component_register(SRCS ${srcs} - INCLUDE_DIRS ${include} - REQUIRES ${require} - ) diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md deleted file mode 100644 index aeb28b107f..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# USB Host CDC-ACM Class Driver - -This directory contains an implementation of a USB CDC-ACM Host Class Driver that is implemented on top of the [USB Host Library](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html). - -## Supported Devices - -The CDC-ACM Host driver supports the following types of CDC devices: - -1. CDC-ACM devices -2. CDC-like vendor specific devices (usually found on USB to UART bridge devices) - -### CDC-ACM Devices - -The CDC-ACM Class driver supports CDC-ACM devices that meet the following requirements: -- The device class code must be set to the CDC class `0x02` or implement Interface Association Descriptor (IAD) -- The CDC-ACM must contain the following interfaces: - - A Communication Class Interface containing a management element (EP0) and may also contain a notification element (an interrupt endpoint). The driver will check this interface for CDC Functional Descriptors. - - A Data Class Interface with two BULK endpoints (IN and OUT). Other transfer types are not supported by the driver - -### CDC-Like Vendor Specific Devices - -The CDC-ACM Class driver supports CDC-like devices that meet the following requirements: -- The device class code must be set to the vendor specific class code `0xFF` -- The device needs to provide and interface containing the following endpoints: - - (Mandatory) Two Bulk endpoints (IN and OUT) for data - - (Optional) An interrupt endpoint (IN) for the notification element - -For CDC-like devices, users are responsible for ensuring that they only call APIs (e.g., `cdc_acm_host_send_break()`) that are supported by the target device. - - -## Usage - -The following steps outline the typical API call pattern of the CDC-ACM Class Driver - -1. Install the USB Host Library via `usb_host_install()` -2. Install the CDC-ACM driver via `cdc_acm_host_install()` -3. Call `cdc_acm_host_open()`/`cdc_acm_host_open_vendor_specific()` to open a target CDC-ACM/CDC-like device. These functions will block until the target device is connected -4. To transmit data, call `cdc_acm_host_data_tx_blocking()` -5. When data is received, the driver will automatically run the receive data callback -6. An opened device can be closed via `cdc_acm_host_close()` -7. The CDC-ACM driver can be uninstalled via `cdc_acm_host_uninstall()` - -## Examples - -- For an example with a CDC-ACM device, refer to [cdc_acm_host](../../cdc_acm_host) -- For an example with a CDC-like device, refer to [cdc_acm_host_bg96](../../cdc_acm_bg96) diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c deleted file mode 100644 index fb9eb26e9b..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c +++ /dev/null @@ -1,1170 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "esp_log.h" -#include -#include -#include -#include "usb/usb_host.h" -#include "usb/cdc_acm_host.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/event_groups.h" -#include "esp_check.h" -#include "esp_system.h" - -#define TAG "cdc_acm" - -// CDC devices often implement Interface Association Descriptor (IAD). Parse IAD only when -// bDeviceClass = 0xEF (Miscellaneous Device Class), bDeviceSubClass = 0x02 (Common Class), bDeviceProtocol = 0x01 (Interface Association Descriptor) -// @see USB Interface Association Descriptor: Device Class Code and Use Model rev 1.0, Table 1-1 -#define USB_SUBCLASS_COMMON 0x02 -#define USB_DEVICE_PROTOCOL_IAD 0x01 - -// CDC-ACM spinlock -static portMUX_TYPE cdc_acm_lock = portMUX_INITIALIZER_UNLOCKED; -#define CDC_ACM_ENTER_CRITICAL() portENTER_CRITICAL(&cdc_acm_lock) -#define CDC_ACM_EXIT_CRITICAL() portEXIT_CRITICAL(&cdc_acm_lock) - -// CDC-ACM events -#define CDC_ACM_TEARDOWN BIT0 -#define CDC_ACM_TEARDOWN_COMPLETE BIT1 - -// CDC-ACM check macros -#define CDC_ACM_CHECK(cond, ret_val) ({ \ - if (!(cond)) { \ - return (ret_val); \ - } \ -}) - -#define CDC_ACM_CHECK_FROM_CRIT(cond, ret_val) ({ \ - if (!(cond)) { \ - CDC_ACM_EXIT_CRITICAL(); \ - return ret_val; \ - } \ -}) - -// CDC-ACM driver object -typedef struct { - usb_host_client_handle_t cdc_acm_client_hdl; /*!< USB Host handle reused for all CDC-ACM devices in the system */ - SemaphoreHandle_t open_close_mutex; - EventGroupHandle_t event_group; - SLIST_HEAD(list_dev, cdc_dev_s) cdc_devices_list; /*!< List of open pseudo devices */ -} cdc_acm_obj_t; - -static cdc_acm_obj_t *p_cdc_acm_obj = NULL; - -/** - * @brief Default CDC-ACM driver configuration - * - * This configuration is used when user passes NULL to config pointer during device open. - */ -static const cdc_acm_host_driver_config_t cdc_acm_driver_config_default = { - .driver_task_stack_size = 4096, - .driver_task_priority = 10, - .xCoreID = 0 -}; - -/** - * @brief USB CDC PSTN Call Descriptor - * - * @see Table 3, USB CDC-PSTN specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; - const cdc_desc_subtype_t bDescriptorSubtype; - union { - struct { - uint8_t call_management: 1; // Device handles call management itself - uint8_t call_over_data_if: 1; // Device sends/receives call management information over Data Class interface - uint8_t reserved: 6; - }; - uint8_t val; - } bmCapabilities; - uint8_t bDataInterface; // Interface number of Data Class interface optionally used for call management -} __attribute__((packed)) cdc_acm_call_desc_t; - -/** - * @brief USB CDC PSTN Abstract Control Model Descriptor - * - * @see Table 4, USB CDC-PSTN specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; - const cdc_desc_subtype_t bDescriptorSubtype; - union { - struct { - uint8_t feature: 1; // Device supports Set/Clear/Get_Comm_Feature requests - uint8_t serial: 1; // Device supports Set/Get_Line_Coding, Set_Control_Line_State and Serial_State request and notifications - uint8_t send_break: 1; // Device supports Send_Break request - uint8_t network: 1; // Device supports Network_Connection notification - uint8_t reserved: 4; - }; - uint8_t val; - } bmCapabilities; -} __attribute__((packed)) cdc_acm_acm_desc_t; - -typedef struct cdc_dev_s cdc_dev_t; -struct cdc_dev_s{ - usb_device_handle_t dev_hdl; // USB device handle - void *cb_arg; // Common argument for user's callbacks (data IN and Notification) - struct { - usb_transfer_t *out_xfer; // OUT data transfer - usb_transfer_t *in_xfer; // IN data transfer - cdc_acm_data_callback_t in_cb; // User's callback for async (non-blocking) data IN - const usb_intf_desc_t *intf_desc; // Pointer to data interface descriptor - SemaphoreHandle_t out_mux; // OUT mutex - } data; - - struct { - usb_transfer_t *xfer; // IN notification transfer - const usb_intf_desc_t *intf_desc; // Pointer to notification interface descriptor, can be NULL if there is no notification channel in the device - cdc_acm_host_dev_callback_t cb; // User's callback for device events - } notif; // Structure with Notif pipe data - - usb_transfer_t *ctrl_transfer; // CTRL (endpoint 0) transfer - SemaphoreHandle_t ctrl_mux; // CTRL mutex - cdc_acm_uart_state_t serial_state; // Serial State - cdc_comm_protocol_t comm_protocol; - cdc_data_protocol_t data_protocol; - int num_cdc_intf_desc; // Number of CDC Interface descriptors in following array - const usb_standard_desc_t **cdc_intf_desc; // CDC Interface descriptors - SLIST_ENTRY(cdc_dev_s) list_entry; -}; - -/** - * @brief Notification received callback - * - * Notification (interrupt) IN transfer is submitted at the end of this function to ensure periodic poll of IN endpoint. - * - * @param[in] transfer Transfer that triggered the callback - */ -static void notif_xfer_cb(usb_transfer_t *transfer); - -/** - * @brief Data received callback - * - * Data (bulk) IN transfer is submitted at the end of this function to ensure continuous poll of IN endpoint. - * - * @param[in] transfer Transfer that triggered the callback - */ -static void in_xfer_cb(usb_transfer_t *transfer); - -/** - * @brief Data send callback - * - * Reused for bulk OUT and CTRL transfers - * - * @param[in] transfer Transfer that triggered the callback - */ -static void out_xfer_cb(usb_transfer_t *transfer); - -/** - * @brief USB Host Client event callback - * - * Handling of USB device connection/disconnection to/from root HUB. - * - * @param[in] event_msg Event message type - * @param[in] arg Caller's argument (not used in this driver) - */ -static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg); - -/** - * @brief Send CDC specific request - * - * Helper function that will send CDC specific request to default endpoint. - * Both IN and OUT requests are sent through this API, depending on the in_transfer parameter. - * - * @see Chapter 6.2, USB CDC specification rev. 1.2 - * @note CDC specific requests are only supported by devices that have dedicated management element. - * - * @param[in] cdc_dev Pointer to CDC device - * @param[in] in_transfer Direction of data phase. true: IN, false: OUT - * @param[in] request CDC request code - * @param[inout] data Pointer to data buffer. Input for OUT transfers, output for IN transfers. - * @param[in] data_len Length of data buffer - * @param[in] value Value to be set in bValue of Setup packet - * @return esp_err_t - */ -static esp_err_t send_cdc_request(cdc_dev_t *cdc_dev, bool in_transfer, cdc_request_code_t request, uint8_t *data, uint16_t data_len, uint16_t value); - -/** - * @brief CDC-ACM driver handling task - * - * USB host client registration and deregistration is handled here. - * - * @param[in] arg User's argument. Handle of a task that started this task. - */ -static void cdc_acm_client_task(void *arg) -{ - vTaskSuspend(NULL); // Task will be resumed from cdc_acm_host_install() - cdc_acm_obj_t *cdc_acm_obj = p_cdc_acm_obj; // Make local copy of the driver's handle - assert(cdc_acm_obj->cdc_acm_client_hdl); - - // Start handling client's events - while (1) { - usb_host_client_handle_events(cdc_acm_obj->cdc_acm_client_hdl, portMAX_DELAY); - EventBits_t events = xEventGroupGetBits(cdc_acm_obj->event_group); - if (events & CDC_ACM_TEARDOWN) { - break; - } - } - - ESP_LOGD(TAG, "Deregistering client"); - ESP_ERROR_CHECK(usb_host_client_deregister(cdc_acm_obj->cdc_acm_client_hdl)); - xEventGroupSetBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN_COMPLETE); - vTaskDelete(NULL); -} - -/** - * @brief Cancel transfer and reset endpoint - * - * This function will cancel ongoing transfer a reset its endpoint to ready state. - * - * @param[in] dev_hdl USB device handle - * @param[in] transfer Transfer to be cancelled - * @return esp_err_t - */ -static esp_err_t cdc_acm_reset_transfer_endpoint(usb_device_handle_t dev_hdl, usb_transfer_t *transfer) -{ - assert(dev_hdl); - assert(transfer); - - ESP_RETURN_ON_ERROR(usb_host_endpoint_halt(dev_hdl, transfer->bEndpointAddress), TAG,); - ESP_RETURN_ON_ERROR(usb_host_endpoint_flush(dev_hdl, transfer->bEndpointAddress), TAG,); - usb_host_endpoint_clear(dev_hdl, transfer->bEndpointAddress); - return ESP_OK; -} - -/** - * @brief Start CDC device - * - * After this call, USB host peripheral will continuously poll IN endpoints. - * - * @param cdc_dev - * @param[in] event_cb Device event callback - * @param[in] in_cb Data received callback - * @param[in] user_arg Optional user's argument, that will be passed to the callbacks - * @return esp_err_t - */ -static esp_err_t cdc_acm_start(cdc_dev_t *cdc_dev, cdc_acm_host_dev_callback_t event_cb, cdc_acm_data_callback_t in_cb, void *user_arg) -{ - esp_err_t ret = ESP_OK; - assert(cdc_dev); - - CDC_ACM_ENTER_CRITICAL(); - cdc_dev->notif.cb = event_cb; - cdc_dev->data.in_cb = in_cb; - cdc_dev->cb_arg = user_arg; - CDC_ACM_EXIT_CRITICAL(); - - // Claim data interface and start polling its IN endpoint - ESP_GOTO_ON_ERROR(usb_host_interface_claim(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber, 0), err, TAG,); - ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); - ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->data.in_xfer)); - - // If notification are supported, claim its interface and start polling its IN endpoint - if (cdc_dev->notif.intf_desc != NULL) { - if (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc) { - ESP_GOTO_ON_ERROR(usb_host_interface_claim(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, - cdc_dev->notif.intf_desc->bInterfaceNumber, 0), err, TAG,); - } - ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); - ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->notif.xfer)); - } - - // Everything OK, add the device into list and return - CDC_ACM_ENTER_CRITICAL(); - SLIST_INSERT_HEAD(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, list_entry); - CDC_ACM_EXIT_CRITICAL(); - return ret; - -err: - usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber); - if (cdc_dev->notif.intf_desc != NULL) { - usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber); - } - return ret; -} - -static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev); -/** - * @brief Helper function that releases resources claimed by CDC device - * - * Close underlying USB device, free device driver memory - * - * @note All interfaces claimed by this device must be release before calling this function - * @param cdc_dev CDC device handle to be removed - */ -static void cdc_acm_device_remove(cdc_dev_t *cdc_dev) -{ - assert(cdc_dev); - cdc_acm_transfers_free(cdc_dev); - free(cdc_dev->cdc_intf_desc); - // We don't check the error code of usb_host_device_close, as the close might fail, if someone else is still using the device (not all interfaces are released) - usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl); // Gracefully continue on error - free(cdc_dev); -} - -/** - * @brief Open USB device with requested VID/PID - * - * This function has two regular return paths: - * 1. USB device with matching VID/PID is already opened by this driver: allocate new CDC device on top of the already opened USB device. - * 2. USB device with matching VID/PID is NOT opened by this driver yet: poll USB connected devices until it is found. - * - * @note This function will block for timeout_ms, if the device is not enumerated at the moment of calling this function. - * @param[in] vid Vendor ID - * @param[in] pid Product ID - * @param[in] timeout_ms Connection timeout [ms] - * @param[out] dev CDC-ACM device - * @return esp_err_t - */ -static esp_err_t cdc_acm_find_and_open_usb_device(uint16_t vid, uint16_t pid, int timeout_ms, cdc_dev_t **dev) -{ - assert(p_cdc_acm_obj); - assert(dev); - - *dev = calloc(1, sizeof(cdc_dev_t)); - if (*dev == NULL) { - return ESP_ERR_NO_MEM; - } - - // First, check list of already opened CDC devices - ESP_LOGD(TAG, "Checking list of opened USB devices"); - cdc_dev_t *cdc_dev; - SLIST_FOREACH(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry) { - const usb_device_desc_t *device_desc; - ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); - if (device_desc->idVendor == vid && device_desc->idProduct == pid) { - // Return path 1: - (*dev)->dev_hdl = cdc_dev->dev_hdl; - return ESP_OK; - } - } - - // Second, poll connected devices until new device is connected or timeout - TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); - TimeOut_t connection_timeout; - vTaskSetTimeOutState(&connection_timeout); - - while (true) { - ESP_LOGD(TAG, "Checking list of connected USB devices"); - uint8_t dev_addr_list[10]; - int num_of_devices; - ESP_ERROR_CHECK(usb_host_device_addr_list_fill(sizeof(dev_addr_list), dev_addr_list, &num_of_devices)); - - // Go through device address list and find the one we are looking for - for (int i = 0; i < num_of_devices; i++) { - usb_device_handle_t current_device; - // Open USB device - if (usb_host_device_open(p_cdc_acm_obj->cdc_acm_client_hdl, dev_addr_list[i], ¤t_device) != ESP_OK) { - continue; // In case we failed to open this device, continue with next one in the list - } - assert(current_device); - const usb_device_desc_t *device_desc; - ESP_ERROR_CHECK(usb_host_get_device_descriptor(current_device, &device_desc)); - if (device_desc->idVendor == vid && device_desc->idProduct == pid) { - // Return path 2: - (*dev)->dev_hdl = current_device; - return ESP_OK; - } - usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, current_device); - } - - if (xTaskCheckForTimeOut(&connection_timeout, &timeout_ticks) != pdFALSE) { - break; // Timeout elapsed and the device is not connected - } - vTaskDelay(pdMS_TO_TICKS(50)); - } - - // Timeout was reached, clean-up - free(*dev); - *dev = NULL; - return ESP_ERR_NOT_FOUND; -} - -esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config) -{ - CDC_ACM_CHECK(!p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - - // Check driver configuration, use default if NULL is passed - if (driver_config == NULL) { - driver_config = &cdc_acm_driver_config_default; - } - - // Allocate all we need for this driver - esp_err_t ret; - cdc_acm_obj_t *cdc_acm_obj = heap_caps_calloc(1, sizeof(cdc_acm_obj_t), MALLOC_CAP_DEFAULT); - EventGroupHandle_t event_group = xEventGroupCreate(); - SemaphoreHandle_t mutex = xSemaphoreCreateMutex(); - TaskHandle_t driver_task_h = NULL; - xTaskCreatePinnedToCore( - cdc_acm_client_task, "USB-CDC", driver_config->driver_task_stack_size, NULL, - driver_config->driver_task_priority, &driver_task_h, driver_config->xCoreID); - - if (cdc_acm_obj == NULL || driver_task_h == NULL || event_group == NULL || mutex == NULL) { - ret = ESP_ERR_NO_MEM; - goto err; - } - - // Register USB Host client - usb_host_client_handle_t usb_client = NULL; - const usb_host_client_config_t client_config = { - .is_synchronous = false, - .max_num_event_msg = 3, - .async.client_event_callback = usb_event_cb, - .async.callback_arg = NULL - }; - ESP_GOTO_ON_ERROR(usb_host_client_register(&client_config, &usb_client), err, TAG, "Failed to register USB host client"); - - // Initialize CDC-ACM driver structure - SLIST_INIT(&(cdc_acm_obj->cdc_devices_list)); - cdc_acm_obj->event_group = event_group; - cdc_acm_obj->open_close_mutex = mutex; - cdc_acm_obj->cdc_acm_client_hdl = usb_client; - - // Between 1st call of this function and following section, another task might try to install this driver: - // Make sure that there is only one instance of this driver in the system - CDC_ACM_ENTER_CRITICAL(); - if (p_cdc_acm_obj) { - // Already created - ret = ESP_ERR_INVALID_STATE; - CDC_ACM_EXIT_CRITICAL(); - goto client_err; - } else { - p_cdc_acm_obj = cdc_acm_obj; - } - CDC_ACM_EXIT_CRITICAL(); - - // Everything OK: Start CDC-Driver task and return - vTaskResume(driver_task_h); - return ESP_OK; - -client_err: - usb_host_client_deregister(usb_client); -err: // Clean-up - free(cdc_acm_obj); - if (event_group) { - vEventGroupDelete(event_group); - } - if (driver_task_h) { - vTaskDelete(driver_task_h); - } - if (mutex) { - vSemaphoreDelete(mutex); - } - return ret; -} - -esp_err_t cdc_acm_host_uninstall() -{ - esp_err_t ret; - - CDC_ACM_ENTER_CRITICAL(); - CDC_ACM_CHECK_FROM_CRIT(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - cdc_acm_obj_t *cdc_acm_obj = p_cdc_acm_obj; // Save Driver's handle to temporary handle - CDC_ACM_EXIT_CRITICAL(); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); // Wait for all open/close calls to finish - - CDC_ACM_ENTER_CRITICAL(); - if (SLIST_EMPTY(&p_cdc_acm_obj->cdc_devices_list)) { // Check that device list is empty (all devices closed) - p_cdc_acm_obj = NULL; // NULL static driver pointer: No open/close calls form this point - } else { - ret = ESP_ERR_INVALID_STATE; - CDC_ACM_EXIT_CRITICAL(); - goto unblock; - } - CDC_ACM_EXIT_CRITICAL(); - - // Signal to CDC task to stop, unblock it and wait for its deletion - xEventGroupSetBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN); - usb_host_client_unblock(cdc_acm_obj->cdc_acm_client_hdl); - ESP_GOTO_ON_FALSE( - xEventGroupWaitBits(cdc_acm_obj->event_group, CDC_ACM_TEARDOWN_COMPLETE, pdFALSE, pdFALSE, pdMS_TO_TICKS(100)), - ESP_ERR_NOT_FINISHED, unblock, TAG,); - - // Free remaining resources and return - vEventGroupDelete(cdc_acm_obj->event_group); - xSemaphoreGive(cdc_acm_obj->open_close_mutex); - vSemaphoreDelete(cdc_acm_obj->open_close_mutex); - free(cdc_acm_obj); - return ESP_OK; - -unblock: - xSemaphoreGive(cdc_acm_obj->open_close_mutex); - return ret; -} - -/** - * @brief Free USB transfers used by this device - * - * @note There can be no transfers in flight, at the moment of calling this function. - * @param[in] cdc_dev Pointer to CDC device - */ -static void cdc_acm_transfers_free(cdc_dev_t *cdc_dev) -{ - assert(cdc_dev); - usb_host_transfer_free(cdc_dev->notif.xfer); - usb_host_transfer_free(cdc_dev->data.in_xfer); - if (cdc_dev->data.out_xfer != NULL) { - if (cdc_dev->data.out_xfer->context != NULL) { - vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->data.out_xfer->context); - } - if (cdc_dev->data.out_mux != NULL) { - vSemaphoreDelete(cdc_dev->data.out_mux); - } - usb_host_transfer_free(cdc_dev->data.out_xfer); - } - if (cdc_dev->ctrl_transfer != NULL) { - if (cdc_dev->ctrl_transfer->context != NULL) { - vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context); - } - if (cdc_dev->ctrl_mux != NULL) { - vSemaphoreDelete(cdc_dev->ctrl_mux); - } - usb_host_transfer_free(cdc_dev->ctrl_transfer); - } -} - -/** - * @brief Allocate CDC transfers - * - * @param[in] cdc_dev Pointer to CDC device - * @param[in] notif_ep_desc Pointer to notification EP descriptor - * @param[in] in_ep_desc- Pointer to data IN EP descriptor - * @param[in] out_ep_desc Pointer to data OUT EP descriptor - * @param[in] out_buf_len Length of data OUT buffer - * @return esp_err_t - */ -static esp_err_t cdc_acm_transfers_allocate(cdc_dev_t *cdc_dev, const usb_ep_desc_t *notif_ep_desc, const usb_ep_desc_t *in_ep_desc, const usb_ep_desc_t *out_ep_desc, size_t out_buf_len) -{ - esp_err_t ret; - - // 1. Setup notification and control transfers if they are supported - if (notif_ep_desc) { - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(notif_ep_desc), 0, &cdc_dev->notif.xfer), - err, TAG,); - cdc_dev->notif.xfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->notif.xfer->bEndpointAddress = notif_ep_desc->bEndpointAddress; - cdc_dev->notif.xfer->callback = notif_xfer_cb; - cdc_dev->notif.xfer->context = cdc_dev; - cdc_dev->notif.xfer->num_bytes = USB_EP_DESC_GET_MPS(notif_ep_desc); - - usb_device_info_t dev_info; - ESP_ERROR_CHECK(usb_host_device_info(cdc_dev->dev_hdl, &dev_info)); - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(dev_info.bMaxPacketSize0, 0, &cdc_dev->ctrl_transfer), - err, TAG,); - cdc_dev->ctrl_transfer->timeout_ms = 1000; - cdc_dev->ctrl_transfer->bEndpointAddress = 0; - cdc_dev->ctrl_transfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->ctrl_transfer->context = cdc_dev; - cdc_dev->ctrl_transfer->callback = out_xfer_cb; - cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary(); - ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->context, ESP_ERR_NO_MEM, err, TAG,); - cdc_dev->ctrl_mux = xSemaphoreCreateMutex(); - ESP_GOTO_ON_FALSE(cdc_dev->ctrl_mux, ESP_ERR_NO_MEM, err, TAG,); - } - - // 2. Setup IN data transfer - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(in_ep_desc), 0, &cdc_dev->data.in_xfer), - err, TAG, - ); - assert(cdc_dev->data.in_xfer); - cdc_dev->data.in_xfer->callback = in_xfer_cb; - cdc_dev->data.in_xfer->num_bytes = USB_EP_DESC_GET_MPS(in_ep_desc); - cdc_dev->data.in_xfer->bEndpointAddress = in_ep_desc->bEndpointAddress; - cdc_dev->data.in_xfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->data.in_xfer->context = cdc_dev; - - // 3. Setup OUT bulk transfer (if it is required (out_buf_len > 0)) - if (out_buf_len != 0) { - ESP_GOTO_ON_ERROR( - usb_host_transfer_alloc(out_buf_len, 0, &cdc_dev->data.out_xfer), - err, TAG, - ); - assert(cdc_dev->data.out_xfer); - cdc_dev->data.out_xfer->device_handle = cdc_dev->dev_hdl; - cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary(); - ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->context, ESP_ERR_NO_MEM, err, TAG,); - cdc_dev->data.out_mux = xSemaphoreCreateMutex(); - ESP_GOTO_ON_FALSE(cdc_dev->data.out_mux, ESP_ERR_NO_MEM, err, TAG,); - cdc_dev->data.out_xfer->bEndpointAddress = out_ep_desc->bEndpointAddress; - cdc_dev->data.out_xfer->callback = out_xfer_cb; - } - return ESP_OK; - -err: - cdc_acm_transfers_free(cdc_dev); - return ret; -} - -/** - * @brief Find CDC interface descriptor and its endpoint descriptors - * - * @note This function is called in open procedure of CDC compliant devices only. - * @param[in] cdc_dev Pointer to CDC device - * @param[in] intf_idx Index of CDC interface that should be used for this device - * @param[out] notif_ep Pointer to notification EP descriptor - * @param[out] in_ep Pointer to data IN EP descriptor - * @param[out] out_ep Pointer to data OUT EP descriptor - * @return esp_err_t - */ -static esp_err_t cdc_acm_find_intf_and_ep_desc(cdc_dev_t *cdc_dev, uint8_t intf_idx, const usb_ep_desc_t **notif_ep, const usb_ep_desc_t **in_ep, const usb_ep_desc_t **out_ep) -{ - bool interface_found = false; - const usb_config_desc_t *config_desc; - const usb_device_desc_t *device_desc; - int data_intf_idx, notif_intf_idx; - int desc_offset = 0; - - // Get required descriptors - ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); - ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); - - if ((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) && - (device_desc->bDeviceProtocol == USB_DEVICE_PROTOCOL_IAD)) { - // This is a composite device, that uses Interface Association Descriptor - const usb_standard_desc_t *this_desc = (const usb_standard_desc_t *)config_desc; - do { - this_desc = usb_parse_next_descriptor_of_type( - this_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, &desc_offset); - - if (this_desc == NULL) - break; // Reached end of configuration descriptor - - const usb_iad_desc_t *iad_desc = (const usb_iad_desc_t *)this_desc; - if (iad_desc->bFirstInterface == intf_idx) { - // IAD with correct interface number was found: Check Class/Subclass codes, save Interface indexes - assert(iad_desc->bInterfaceCount == 2); - assert(iad_desc->bFunctionClass == USB_CLASS_COMM); - assert(iad_desc->bFunctionSubClass == CDC_SUBCLASS_ACM); - notif_intf_idx = iad_desc->bFirstInterface; - data_intf_idx = iad_desc->bFirstInterface + 1; - interface_found = true; - } - } while (!interface_found); - } else if ((device_desc->bDeviceClass == USB_CLASS_COMM) && (intf_idx == 0)) { - // This is a Communication Device Class - notif_intf_idx = 0; - data_intf_idx = 1; - interface_found = true; - } - - // Save found interfaces descriptors: - if (interface_found) { - // Notification IF and EP - cdc_dev->notif.intf_desc = usb_parse_interface_descriptor(config_desc, notif_intf_idx, 0, &desc_offset); - assert(cdc_dev->notif.intf_desc); - - // CDC specific descriptors should be right after CDC-Communication interface descriptor - // Note: That's why we use usb_parse_next_descriptor instead of usb_parse_next_descriptor_of_type. - // The latter could return CDC specific descriptors that don't belong to this interface - const usb_standard_desc_t *cdc_desc = (usb_standard_desc_t *)cdc_dev->notif.intf_desc; - do { - cdc_desc = usb_parse_next_descriptor(cdc_desc, config_desc->wTotalLength, &desc_offset); - if ((cdc_desc == NULL) || (cdc_desc->bDescriptorType != ((USB_CLASS_COMM << 4) | USB_W_VALUE_DT_INTERFACE))) - break; // We found all CDC specific descriptors - cdc_dev->num_cdc_intf_desc++; - cdc_dev->cdc_intf_desc = - realloc(cdc_dev->cdc_intf_desc, cdc_dev->num_cdc_intf_desc * (sizeof(usb_standard_desc_t *))); - assert(cdc_dev->cdc_intf_desc); - cdc_dev->cdc_intf_desc[cdc_dev->num_cdc_intf_desc - 1] = cdc_desc; - } while (1); - *notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->notif.intf_desc, 0, config_desc->wTotalLength, &desc_offset); - assert(notif_ep); - - // Data IF and EP - cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, data_intf_idx, 0, &desc_offset); - assert(cdc_dev->data.intf_desc); - int temp_offset = desc_offset; - for (int i = 0; i < 2; i++) { - const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset); - assert(this_ep); - if (USB_EP_DESC_GET_EP_DIR(this_ep)) { - *in_ep = this_ep; - } else { - *out_ep = this_ep; - } - desc_offset = temp_offset; - } - return ESP_OK; - } - return ESP_ERR_NOT_FOUND; -} - -esp_err_t cdc_acm_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret) -{ - esp_err_t ret; - CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG); - CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); - // Find underlying USB device - cdc_dev_t *cdc_dev; - ESP_GOTO_ON_ERROR( - cdc_acm_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev), - exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid); - - // Find and save relevant interface and endpoint descriptors - const usb_ep_desc_t *notif_ep = NULL; - const usb_ep_desc_t *in_ep = NULL; - const usb_ep_desc_t *out_ep = NULL; - ESP_GOTO_ON_ERROR( - cdc_acm_find_intf_and_ep_desc(cdc_dev, interface_idx, ¬if_ep, &in_ep, &out_ep), - err, TAG, "Could not find required interface"); - - // Check whether found Interfaces are really CDC-ACM - assert(cdc_dev->notif.intf_desc->bInterfaceClass == USB_CLASS_COMM); - assert(cdc_dev->notif.intf_desc->bInterfaceSubClass == CDC_SUBCLASS_ACM); - assert(cdc_dev->notif.intf_desc->bNumEndpoints == 1); - assert(cdc_dev->data.intf_desc->bInterfaceClass == USB_CLASS_CDC_DATA); - assert(cdc_dev->data.intf_desc->bNumEndpoints == 2); - - // Save Communication and Data protocols - cdc_dev->comm_protocol = (cdc_comm_protocol_t)cdc_dev->notif.intf_desc->bInterfaceProtocol; - cdc_dev->data_protocol = (cdc_data_protocol_t)cdc_dev->data.intf_desc->bInterfaceProtocol; - - // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle - ESP_GOTO_ON_ERROR(cdc_acm_transfers_allocate(cdc_dev, notif_ep, in_ep, out_ep, dev_config->out_buffer_size), err, TAG,); - ESP_GOTO_ON_ERROR(cdc_acm_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG,); - *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev; - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ESP_OK; - -err: - cdc_acm_device_remove(cdc_dev); -exit: - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - *cdc_hdl_ret = NULL; - return ret; -} - -esp_err_t cdc_acm_host_open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_num, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret) -{ - esp_err_t ret; - CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG); - CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); - - // Find underlying USB device - cdc_dev_t *cdc_dev; - ESP_GOTO_ON_ERROR( - cdc_acm_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev), - exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid); - - // Open procedure for CDC-ACM non-compliant devices: - const usb_config_desc_t *config_desc; - int desc_offset; - ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); - cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, interface_num, 0, &desc_offset); - const int temp_offset = desc_offset; // Save this offset for later - assert(cdc_dev->data.intf_desc); - - // The interface can have 2-3 endpoints. 2 for data and 1 optional for notifications - const usb_ep_desc_t *in_ep = NULL; - const usb_ep_desc_t *out_ep = NULL; - const usb_ep_desc_t *notif_ep = NULL; - int ep_idx = 0; - if (cdc_dev->data.intf_desc->bNumEndpoints == 3) { - // Notification channel does not have its dedicated interface (data and notif interface is the same) - // First endpoint of this interface is used as notification channel - cdc_dev->notif.intf_desc = cdc_dev->data.intf_desc; - notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, 0, config_desc->wTotalLength, &desc_offset); - desc_offset = temp_offset; - ep_idx++; - } - - for (int i = ep_idx; i < ep_idx + 2; i++) { - const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset); - assert(this_ep); - if (USB_EP_DESC_GET_EP_DIR(this_ep)) { - in_ep = this_ep; - } else { - out_ep = this_ep; - } - desc_offset = temp_offset; - } - - // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle - ESP_GOTO_ON_ERROR(cdc_acm_transfers_allocate(cdc_dev, notif_ep, in_ep, out_ep, dev_config->out_buffer_size), err, TAG, ); - ESP_GOTO_ON_ERROR(cdc_acm_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG,); - *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev; - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ESP_OK; -err: - cdc_acm_device_remove(cdc_dev); -exit: - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ret; -} - -esp_err_t cdc_acm_host_close(cdc_acm_dev_hdl_t cdc_hdl) -{ - CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - - xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); - - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - - // Cancel polling of BULK IN and INTERRUPT IN endpoints - cdc_dev->notif.cb = NULL; - cdc_dev->data.in_cb = NULL; - ESP_ERROR_CHECK(cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.in_xfer)); - if (cdc_dev->notif.intf_desc != NULL) { - ESP_ERROR_CHECK(cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->notif.xfer)); - } - - // Release all interfaces - ESP_ERROR_CHECK(usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber)); - if ((cdc_dev->notif.intf_desc != NULL) && (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc)) { - ESP_ERROR_CHECK(usb_host_interface_release(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber)); - } - - CDC_ACM_ENTER_CRITICAL(); - SLIST_REMOVE(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, cdc_dev_s, list_entry); - CDC_ACM_EXIT_CRITICAL(); - - cdc_acm_device_remove(cdc_dev); - xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); - return ESP_OK; -} - -void cdc_acm_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl) -{ - assert(cdc_hdl); - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - - ESP_RETURN_ON_FALSE(cdc_dev->num_cdc_intf_desc > 0,, TAG, "No CDC-ACM specific descriptors found"); - - for (int i = 0; i < cdc_dev->num_cdc_intf_desc; i++) { - switch (((cdc_header_desc_t *)cdc_dev->cdc_intf_desc[i])->bDescriptorSubtype) { - case CDC_DESC_SUBTYPE_HEADER: { - cdc_header_desc_t *desc = (cdc_header_desc_t *)cdc_dev->cdc_intf_desc[i]; - printf("CDC Header Descriptor:\n"); - printf("\tbcdCDC: %d.%d0\n", ((desc->bcdCDC >> 8) & 0xF), ((desc->bcdCDC >> 4) & 0xF)); - break; - } - case CDC_DESC_SUBTYPE_CALL: { - cdc_acm_call_desc_t *desc = (cdc_acm_call_desc_t *)cdc_dev->cdc_intf_desc[i]; - printf("CDC Call Descriptor:\n"); - printf("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val); - printf("\tbDataInterface: %d\n", desc->bDataInterface); - break; - } - case CDC_DESC_SUBTYPE_ACM: { - cdc_acm_acm_desc_t *desc = (cdc_acm_acm_desc_t *)cdc_dev->cdc_intf_desc[i]; - printf("CDC ACM Descriptor:\n"); - printf("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val); - break; - } - case CDC_DESC_SUBTYPE_UNION: { - cdc_union_desc_t *desc = (cdc_union_desc_t *)cdc_dev->cdc_intf_desc[i]; - printf("CDC Union Descriptor:\n"); - printf("\tbControlInterface: %d\n", desc->bControlInterface); - printf("\tbSubordinateInterface[0]: %d\n", desc->bSubordinateInterface[0]); - break; - } - default: - ESP_LOGW(TAG, "Unsupported CDC specific descriptor"); - break; - } - } -} - -/** - * @brief Check finished transfer status - * - * Return to on transfer completed OK. - * Cancel the transfer and issue user's callback in case of an error. - * - * @param[in] transfer Transfer to be checked - * @return true Transfer completed - * @return false Transfer NOT completed - */ -static bool cdc_acm_is_transfer_completed(usb_transfer_t *transfer) -{ - cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; - bool completed = false; - - switch (transfer->status) { - case USB_TRANSFER_STATUS_COMPLETED: - completed = true; - break; - case USB_TRANSFER_STATUS_NO_DEVICE: // User is notified about device disconnection from usb_event_cb - case USB_TRANSFER_STATUS_CANCELED: - break; - case USB_TRANSFER_STATUS_ERROR: - case USB_TRANSFER_STATUS_TIMED_OUT: - case USB_TRANSFER_STATUS_STALL: - case USB_TRANSFER_STATUS_OVERFLOW: - case USB_TRANSFER_STATUS_SKIPPED: - default: - // Transfer was not completed or cancelled by user. Inform user about this - if (cdc_dev->notif.cb) { - const cdc_acm_host_dev_event_data_t error_event = { - .type = CDC_ACM_HOST_ERROR, - .data.error = (int) transfer->status - }; - cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &error_event, cdc_dev->cb_arg); - } - } - return completed; -} - -static void in_xfer_cb(usb_transfer_t *transfer) -{ - ESP_LOGD("CDC_ACM", "in xfer cb"); - cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; - - if (cdc_acm_is_transfer_completed(transfer)) { - if (cdc_dev->data.in_cb) { - cdc_dev->data.in_cb(transfer->data_buffer, transfer->actual_num_bytes, cdc_dev->cb_arg); - } - - ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); - usb_host_transfer_submit(cdc_dev->data.in_xfer); - } -} - -static void notif_xfer_cb(usb_transfer_t *transfer) -{ - ESP_LOGD("CDC_ACM", "notif xfer cb"); - cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; - - if (cdc_acm_is_transfer_completed(transfer)) { - cdc_notification_t *notif = (cdc_notification_t *)transfer->data_buffer; - switch (notif->bNotificationCode) { - case CDC_NOTIF_NETWORK_CONNECTION: { - if (cdc_dev->notif.cb) { - const cdc_acm_host_dev_event_data_t net_conn_event = { - .type = CDC_ACM_HOST_NETWORK_CONNECTION, - .data.network_connected = (bool) notif->wValue - }; - cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &net_conn_event, cdc_dev->cb_arg); - } - break; - } - case CDC_NOTIF_SERIAL_STATE: { - cdc_dev->serial_state.val = *((uint16_t *)notif->Data); - if (cdc_dev->notif.cb) { - const cdc_acm_host_dev_event_data_t serial_state_event = { - .type = CDC_ACM_HOST_SERIAL_STATE, - .data.serial_state = cdc_dev->serial_state - }; - cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &serial_state_event, cdc_dev->cb_arg); - } - break; - } - case CDC_NOTIF_RESPONSE_AVAILABLE: // Encapsulated commands not implemented - fallthrough - default: - ESP_LOGW("CDC_ACM", "Unsupported notification type 0x%02X", notif->bNotificationCode); - ESP_LOG_BUFFER_HEX("CDC_ACM", transfer->data_buffer, transfer->actual_num_bytes); - break; - } - - // Start polling for new data again - ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); - usb_host_transfer_submit(cdc_dev->notif.xfer); - } -} - -static void out_xfer_cb(usb_transfer_t *transfer) -{ - ESP_LOGD("CDC_ACM", "out/ctrl xfer cb"); - assert(transfer->context); - xSemaphoreGive((SemaphoreHandle_t)transfer->context); -} - -static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg) -{ - switch (event_msg->event) { - case USB_HOST_CLIENT_EVENT_NEW_DEV: - ESP_LOGD(TAG, "New device connected"); - break; - case USB_HOST_CLIENT_EVENT_DEV_GONE: { - ESP_LOGD(TAG, "Device suddenly disconnected"); - // Find CDC pseudo-devices associated with this USB device and close them - cdc_dev_t *cdc_dev; - cdc_dev_t *tcdc_dev; - // We are using 'SAFE' version of 'SLIST_FOREACH' which enables user to close the disconnected device in the callback - SLIST_FOREACH_SAFE(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry, tcdc_dev) { - if (cdc_dev->dev_hdl == event_msg->dev_gone.dev_hdl && cdc_dev->notif.cb) { - // The suddenly disconnected device was opened by this driver: inform user about this - const cdc_acm_host_dev_event_data_t disconn_event = { - .type = CDC_ACM_HOST_DEVICE_DISCONNECTED, - }; - cdc_dev->notif.cb((cdc_acm_dev_hdl_t) cdc_dev, &disconn_event, cdc_dev->cb_arg); - } - } - break; - } - default: - assert(false); - break; - } -} - -esp_err_t cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl, const uint8_t *data, size_t data_len, uint32_t timeout_ms) -{ - esp_err_t ret; - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - CDC_ACM_CHECK(data && (data_len > 0), ESP_ERR_INVALID_ARG); - CDC_ACM_CHECK(cdc_dev->data.out_xfer, ESP_ERR_NOT_SUPPORTED); // Device was opened as read-only. - CDC_ACM_CHECK(data_len <= cdc_dev->data.out_xfer->data_buffer_size, ESP_ERR_INVALID_SIZE); - - // Take OUT mutex and fill the OUT transfer - BaseType_t taken = xSemaphoreTake(cdc_dev->data.out_mux, pdMS_TO_TICKS(timeout_ms)); - if (taken != pdTRUE) { - return ESP_ERR_TIMEOUT; - } - - ESP_LOGD("CDC_ACM", "Submitting BULK OUT transfer"); - memcpy(cdc_dev->data.out_xfer->data_buffer, data, data_len); - cdc_dev->data.out_xfer->num_bytes = data_len; - cdc_dev->data.out_xfer->timeout_ms = timeout_ms; - ESP_GOTO_ON_ERROR(usb_host_transfer_submit(cdc_dev->data.out_xfer), unblock, TAG,); - - // Wait for OUT transfer completion - taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->data.out_xfer->context, pdMS_TO_TICKS(timeout_ms)); - if (!taken) { - // Reset the endpoint - cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.out_xfer); - ret = ESP_ERR_TIMEOUT; - goto unblock; - } - - ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Bulk OUT transfer error"); - ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->actual_num_bytes == data_len, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred"); - ret = ESP_OK; - -unblock: - xSemaphoreGive(cdc_dev->data.out_mux); - return ret; -} - -esp_err_t cdc_acm_host_line_coding_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_acm_line_coding_t *line_coding) -{ - CDC_ACM_CHECK(cdc_hdl && line_coding, ESP_ERR_INVALID_ARG); - - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, true, CDC_REQ_GET_LINE_CODING, (uint8_t *)line_coding, sizeof(cdc_acm_line_coding_t), 0), - TAG,); - ESP_LOGD(TAG, "Line Get: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding->dwDTERate, - line_coding->bCharFormat, line_coding->bParityType, line_coding->bDataBits); - return ESP_OK; -} - -esp_err_t cdc_acm_host_line_coding_set(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_line_coding_t *line_coding) -{ - CDC_ACM_CHECK(cdc_hdl && line_coding, ESP_ERR_INVALID_ARG); - - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, false, CDC_REQ_SET_LINE_CODING, (uint8_t *)line_coding, sizeof(cdc_acm_line_coding_t), 0), - TAG,); - ESP_LOGD(TAG, "Line Set: Rate: %d, Stop bits: %d, Parity: %d, Databits: %d", line_coding->dwDTERate, - line_coding->bCharFormat, line_coding->bParityType, line_coding->bDataBits); - return ESP_OK; -} - -esp_err_t cdc_acm_host_set_control_line_state(cdc_acm_dev_hdl_t cdc_hdl, bool dtr, bool rts) -{ - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - - const uint16_t ctrl_bitmap = (uint16_t)dtr | ((uint16_t)rts << 1); - - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, false, CDC_REQ_SET_CONTROL_LINE_STATE, NULL, 0, ctrl_bitmap), - TAG,); - ESP_LOGD(TAG, "Control Line Set: DTR: %d, RTS: %d", dtr, rts); - return ESP_OK; -} - -esp_err_t cdc_acm_host_send_break(cdc_acm_dev_hdl_t cdc_hdl, uint16_t duration_ms) -{ - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - - ESP_RETURN_ON_ERROR( - send_cdc_request((cdc_dev_t *)cdc_hdl, false, CDC_REQ_SEND_BREAK, NULL, 0, duration_ms), - TAG,); - - // Block until break is deasserted - vTaskDelay(pdMS_TO_TICKS(duration_ms + 1)); - return ESP_OK; -} - -static esp_err_t send_cdc_request(cdc_dev_t *cdc_dev, bool in_transfer, cdc_request_code_t request, uint8_t *data, uint16_t data_len, uint16_t value) -{ - esp_err_t ret; - CDC_ACM_CHECK(cdc_dev->ctrl_transfer, ESP_ERR_NOT_SUPPORTED); - CDC_ACM_CHECK(cdc_dev->ctrl_transfer->data_buffer_size >= data_len, ESP_ERR_INVALID_SIZE); - - // Take Mutex and fill the CTRL request - BaseType_t taken = xSemaphoreTake(cdc_dev->ctrl_mux, pdMS_TO_TICKS(1000)); - if (!taken) { - return ESP_ERR_TIMEOUT; - } - usb_setup_packet_t *req = (usb_setup_packet_t *)(cdc_dev->ctrl_transfer->data_buffer); - uint8_t *start_of_data = (uint8_t *)req + sizeof(usb_setup_packet_t); - req->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; - req->bRequest = request; - req->wValue = value; - req->wIndex = cdc_dev->notif.intf_desc->bInterfaceNumber; - req->wLength = data_len; - - if (in_transfer) { - req->bmRequestType |= USB_BM_REQUEST_TYPE_DIR_IN; - } else { - memcpy(start_of_data, data, data_len); - } - - cdc_dev->ctrl_transfer->num_bytes = data_len + sizeof(usb_setup_packet_t); - ESP_GOTO_ON_ERROR( - usb_host_transfer_submit_control(p_cdc_acm_obj->cdc_acm_client_hdl, cdc_dev->ctrl_transfer), - unblock, TAG, "CTRL transfer failed"); - - taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context, pdMS_TO_TICKS(1000)); // This is a fixed timeout. Every CDC device should be able to respond to CTRL transfer in 1 second - if (!taken) { - // Transfer was not finished, error in USB LIB. Reset the endpoint - cdc_acm_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->ctrl_transfer); - ret = ESP_ERR_TIMEOUT; - goto unblock; - } - - ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Control transfer error"); - ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->actual_num_bytes == cdc_dev->ctrl_transfer->num_bytes, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred"); - - if (in_transfer) { - memcpy(data, start_of_data, data_len); - } - ret = ESP_OK; - -unblock: - xSemaphoreGive(cdc_dev->ctrl_mux); - return ret; -} - -esp_err_t cdc_acm_host_protocols_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_comm_protocol_t *comm, cdc_data_protocol_t *data) -{ - CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); - cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; - - if (comm != NULL) *comm = cdc_dev->comm_protocol; - if (data != NULL) *data = cdc_dev->data_protocol; - return ESP_OK; -} diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h deleted file mode 100644 index 2709a8b38b..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include "usb_types_cdc.h" -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct cdc_dev_s *cdc_acm_dev_hdl_t; - -/** - * @brief Line Coding structure - * @see Table 17, USB CDC-PSTN specification rev. 1.2 - */ -typedef struct { - uint32_t dwDTERate; // in bits per second - uint8_t bCharFormat; // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits - uint8_t bParityType; // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space - uint8_t bDataBits; // 5, 6, 7, 8 or 16 -} __attribute__((packed)) cdc_acm_line_coding_t; - -/** - * @brief UART State Bitmap - * @see Table 31, USB CDC-PSTN specification rev. 1.2 - */ -typedef union { - struct { - uint16_t bRxCarrier : 1; // State of receiver carrier detection mechanism of device. This signal corresponds to V.24 signal 109 and RS-232 signal DCD. - uint16_t bTxCarrier : 1; // State of transmission carrier. This signal corresponds to V.24 signal 106 and RS-232 signal DSR. - uint16_t bBreak : 1; // State of break detection mechanism of the device. - uint16_t bRingSignal : 1; // State of ring signal detection of the device. - uint16_t bFraming : 1; // A framing error has occurred. - uint16_t bParity : 1; // A parity error has occurred. - uint16_t bOverRun : 1; // Received data has been discarded due to overrun in the device. - uint16_t reserved : 9; - }; - uint16_t val; -} cdc_acm_uart_state_t; - -/** - * @brief CDC-ACM Device Event types to upper layer - * - */ -typedef enum { - CDC_ACM_HOST_ERROR, - CDC_ACM_HOST_SERIAL_STATE, - CDC_ACM_HOST_NETWORK_CONNECTION, - CDC_ACM_HOST_DEVICE_DISCONNECTED -} cdc_acm_host_dev_event_t; - -/** - * @brief CDC-ACM Device Event data structure - * - */ -typedef struct { - cdc_acm_host_dev_event_t type; - union { - int error; // Error code from USB Host - cdc_acm_uart_state_t serial_state; // Serial (UART) state - bool network_connected; // Network connection event - } data; -} cdc_acm_host_dev_event_data_t; - -/** - * @brief Data receive callback type - */ -typedef void (*cdc_acm_data_callback_t)(uint8_t* data, size_t data_len, void *user_arg); - -/** - * @brief Device event callback type - * @see cdc_acm_host_dev_event_t - */ -typedef void (*cdc_acm_host_dev_callback_t)(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_host_dev_event_data_t *event, void *user_ctx); - -/** - * @brief Configuration structure of USB Host CDC-ACM driver - * - */ -typedef struct { - size_t driver_task_stack_size; /**< Stack size of the driver's task */ - unsigned driver_task_priority; /**< Priority of the driver's task */ - int xCoreID; /**< Core affinity of the driver's task */ -} cdc_acm_host_driver_config_t; - -/** - * @brief Configuration structure of CDC-ACM device - * - */ -typedef struct { - uint32_t connection_timeout_ms; /**< Timeout for USB device connection in [ms] */ - size_t out_buffer_size; /**< Maximum size of USB bulk out transfer, set to 0 for read-only devices */ - cdc_acm_host_dev_callback_t event_cb; /**< Device's event callback function. Can be NULL */ - cdc_acm_data_callback_t data_cb; /**< Device's data RX callback function. Can be NULL for write-only devices */ - void *user_arg; /**< User's argument that will be passed to the callbacks */ -} cdc_acm_host_device_config_t; - -/** - * @brief Install CDC-ACM driver - * - * - USB Host Library must already be installed before calling this function (via usb_host_install()) - * - This function should be called before calling any other CDC driver functions - * - * @param[in] driver_config Driver configuration structure. If set to NULL, a default configuration will be used. - * @return esp_err_t - */ -esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config); - -/** - * @brief Uninstall CDC-ACM driver - * - * - Users must ensure that all CDC devices must be closed via cdc_acm_host_close() before calling this function - * - * @return esp_err_t - */ -esp_err_t cdc_acm_host_uninstall(void); - -/** - * @brief Open CDC-ACM compliant device - * - * CDC-ACM compliant device must contain either an Interface Association Descriptor or CDC-Union descriptor, - * which are used for the driver's configuration. - * - * @param[in] vid Device's Vendor ID - * @param[in] pid Device's Product ID - * @param[in] interface_idx Index of device's interface used for CDC-ACM communication - * @param[in] dev_config Configuration structure of the device - * @param[out] cdc_hdl_ret CDC device handle - * @return esp_err_t - */ -esp_err_t cdc_acm_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret); - -/** - * @brief Open CDC-ACM non-compliant device - * - * CDC-ACM non-compliant device acts as CDC-ACM device but doesn't support all its features. - * User must provide the interface index that will be used (zero for non-composite devices). - * - * @param[in] vid Device's Vendor ID - * @param[in] pid Device's Product ID - * @param[in] interface_idx Index of device's interface used for CDC-ACM like communication - * @param[in] dev_config Configuration structure of the device - * @param[out] cdc_hdl_ret CDC device handle - * @return esp_err_t - */ -esp_err_t cdc_acm_host_open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_num, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret); - -/** - * @brief Close CDC device and release its resources - * - * @note All in-flight transfers will be prematurely canceled. - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @return esp_err_t - */ -esp_err_t cdc_acm_host_close(cdc_acm_dev_hdl_t cdc_hdl); - -/** - * @brief Transmit data - blocking mode - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] data Data to be sent - * @param[in] data_len Data length - * @param[in] timeout_ms Timeout in [ms] - * @return esp_err_t - */ -esp_err_t cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl, const uint8_t *data, size_t data_len, uint32_t timeout_ms); - -/** - * @brief SetLineCoding function - * - * @see Chapter 6.3.10, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] line_coding Line Coding structure - * @return esp_err_t - */ -esp_err_t cdc_acm_host_line_coding_set(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_line_coding_t *line_coding); - -/** - * @brief GetLineCoding function - * - * @see Chapter 6.3.11, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[out] line_coding Line Coding structure to be filled - * @return esp_err_t - */ -esp_err_t cdc_acm_host_line_coding_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_acm_line_coding_t *line_coding); - -/** - * @brief SetControlLineState function - * - * @see Chapter 6.3.12, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] dtr Indicates to DCE if DTE is present or not. This signal corresponds to V.24 signal 108/2 and RS-232 signal Data Terminal Ready. - * @param[in] rts Carrier control for half duplex modems. This signal corresponds to V.24 signal 105 and RS-232 signal Request To Send. - * @return esp_err_t - */ -esp_err_t cdc_acm_host_set_control_line_state(cdc_acm_dev_hdl_t cdc_hdl, bool dtr, bool rts); - -/** - * @brief SendBreak function - * - * This function will block until the duration_ms has passed. - * - * @see Chapter 6.3.13, USB CDC-PSTN specification rev. 1.2 - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[in] duration_ms Duration of the Break signal in [ms] - * @return esp_err_t - */ -esp_err_t cdc_acm_host_send_break(cdc_acm_dev_hdl_t cdc_hdl, uint16_t duration_ms); - -/** - * @brief Print CDC-ACM specific descriptors - * - * Descriptors are printed in human readable format to stdout. - * Intended for debugging and for CDC-ACM compliant devices only. - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - */ -void cdc_acm_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl); - -/** - * @brief Get protocols defined in USB-CDC interface descriptors - * - * @param cdc_hdl CDC handle obtained from cdc_acm_host_open() - * @param[out] comm Communication protocol - * @param[out] data Data protocol - * @return esp_err_t - */ -esp_err_t cdc_acm_host_protocols_get(cdc_acm_dev_hdl_t cdc_hdl, cdc_comm_protocol_t *comm, cdc_data_protocol_t *data); - -#ifdef __cplusplus -} -class CdcAcmDevice -{ -public: - // Operators - CdcAcmDevice() : cdc_hdl(NULL){}; - ~CdcAcmDevice() - { - // Close CDC-ACM device, if it wasn't explicitly closed - if (this->cdc_hdl != NULL) { - this->close(); - } - } - - inline esp_err_t tx_blocking(uint8_t *data, size_t len, uint32_t timeout_ms = 100) - { - return cdc_acm_host_data_tx_blocking(this->cdc_hdl, data, len, timeout_ms); - } - - inline esp_err_t open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t* dev_config) - { - return cdc_acm_host_open(vid, pid, interface_idx, dev_config, &this->cdc_hdl); - } - - inline esp_err_t open_vendor_specific(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t* dev_config) - { - return cdc_acm_host_open_vendor_specific(vid, pid, interface_idx, dev_config, &this->cdc_hdl); - } - - inline void close() - { - cdc_acm_host_close(this->cdc_hdl); - this->cdc_hdl = NULL; - } - - inline esp_err_t line_coding_get(cdc_acm_line_coding_t *line_coding) - { - return cdc_acm_host_line_coding_get(this->cdc_hdl, line_coding); - } - - inline esp_err_t line_coding_set(cdc_acm_line_coding_t *line_coding) - { - return cdc_acm_host_line_coding_set(this->cdc_hdl, line_coding); - } - - inline esp_err_t set_control_line_state(bool dtr, bool rts) - { - return cdc_acm_host_set_control_line_state(this->cdc_hdl, dtr, rts); - } - - inline esp_err_t send_break(uint16_t duration_ms) - { - return cdc_acm_host_send_break(this->cdc_hdl, duration_ms); - } - -private: - CdcAcmDevice(const CdcAcmDevice &Copy); - CdcAcmDevice &operator= (const CdcAcmDevice &Copy); - bool operator== (const CdcAcmDevice ¶m) const; - bool operator!= (const CdcAcmDevice ¶m) const; - cdc_acm_dev_hdl_t cdc_hdl; -}; -#endif diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h deleted file mode 100644 index 242d4e2c69..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/usb_types_cdc.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once -#include - -/** - * @brief USB CDC Descriptor Subtypes - * - * @see Table 13, USB CDC specification rev. 1.2 - */ -typedef enum { - CDC_DESC_SUBTYPE_HEADER = 0x00, // Header Functional Descriptor - CDC_DESC_SUBTYPE_CALL = 0x01, // Call Management Functional Descriptor - CDC_DESC_SUBTYPE_ACM = 0x02, // Abstract Control Management Functional Descriptor - CDC_DESC_SUBTYPE_DLM = 0x03, // Direct Line Management Functional Descriptor - CDC_DESC_SUBTYPE_TEL_RINGER = 0x04, // Telephone Ringer Functional Descriptor - CDC_DESC_SUBTYPE_TEL_CLSR = 0x05, // Telephone Call and Line State Reporting Capabilities Functional Descriptor - CDC_DESC_SUBTYPE_UNION = 0x06, // Union Functional Descriptor - CDC_DESC_SUBTYPE_COUNTRY = 0x07, // Country Selection Functional Descriptor - CDC_DESC_SUBTYPE_TEL_MODE = 0x08, // Telephone Operational Modes Functional Descriptor - CDC_DESC_SUBTYPE_TERMINAL = 0x09, // USB Terminal - CDC_DESC_SUBTYPE_NCHT = 0x0A, // Network Channel Terminal - CDC_DESC_SUBTYPE_PROTOCOL = 0x08, // Protocol Unit - CDC_DESC_SUBTYPE_EXTENSION = 0x0C, // Extension Unit - CDC_DESC_SUBTYPE_MULTI_CHAN = 0x0D, // Multi-Channel Management Functional Descriptor - CDC_DESC_SUBTYPE_CAPI = 0x0E, // CAPI Control - CDC_DESC_SUBTYPE_ETH = 0x0F, // Ethernet Networking - CDC_DESC_SUBTYPE_ATM = 0x10, // ATM Networking - CDC_DESC_SUBTYPE_WHANDSET = 0x11, // Wireless Handset Control Model Functional Descriptor - CDC_DESC_SUBTYPE_MDLM = 0x12, // Mobile Direct Line Model - CDC_DESC_SUBTYPE_MDLM_DETAIL = 0x13, // MDLM Detail - CDC_DESC_SUBTYPE_DMM = 0x14, // Device Management Model - CDC_DESC_SUBTYPE_OBEX = 0x15, // OBEX Functional - CDC_DESC_SUBTYPE_COMMAND_SET = 0x16, // Command Set - CDC_DESC_SUBTYPE_COMMAND_SET_DETAIL = 0x17, // Command Set Detail Functional Descriptor - CDC_DESC_SUBTYPE_TEL_CM = 0x18, // Telephone Control Model Functional Descriptor - CDC_DESC_SUBTYPE_OBEX_SERVICE = 0x19, // OBEX Service Identifier Functional Descriptor - CDC_DESC_SUBTYPE_NCM = 0x1A // NCM Functional Descriptor -} __attribute__((packed)) cdc_desc_subtype_t; - -/** - * @brief USB CDC Subclass codes - * - * @see Table 4, USB CDC specification rev. 1.2 - */ -typedef enum { - CDC_SUBCLASS_DLCM = 0x01, // Direct Line Control Model - CDC_SUBCLASS_ACM = 0x02, // Abstract Control Model - CDC_SUBCLASS_TCM = 0x03, // Telephone Control Model - CDC_SUBCLASS_MCHCM = 0x04, // Multi-Channel Control Model - CDC_SUBCLASS_CAPI = 0x05, // CAPI Control Model - CDC_SUBCLASS_ECM = 0x06, // Ethernet Networking Control Model - CDC_SUBCLASS_ATM = 0x07, // ATM Networking Model - CDC_SUBCLASS_HANDSET = 0x08, // Wireless Handset Control Model - CDC_SUBCLASS_DEV_MAN = 0x09, // Device Management - CDC_SUBCLASS_MOBILE = 0x0A, // Mobile Direct Line Model - CDC_SUBCLASS_OBEX = 0x0B, // OBEX - CDC_SUBCLASS_EEM = 0x0C, // Ethernet Emulation Model - CDC_SUBCLASS_NCM = 0x0D // Network Control Model -} __attribute__((packed)) cdc_subclass_t; - -/** - * @brief USB CDC Communications Protocol Codes - * - * @see Table 5, USB CDC specification rev. 1.2 - */ -typedef enum { - CDC_COMM_PROTOCOL_NONE = 0x00, // No class specific protocol required - CDC_COMM_PROTOCOL_V250 = 0x01, // AT Commands: V.250 etc - CDC_COMM_PROTOCOL_PCAA = 0x02, // AT Commands defined by PCCA-101 - CDC_COMM_PROTOCOL_PCAA_A = 0x03, // AT Commands defined by PCAA-101 & Annex O - CDC_COMM_PROTOCOL_GSM = 0x04, // AT Commands defined by GSM 07.07 - CDC_COMM_PROTOCOL_3GPP = 0x05, // AT Commands defined by 3GPP 27.007 - CDC_COMM_PROTOCOL_TIA = 0x06, // AT Commands defined by TIA for CDMA - CDC_COMM_PROTOCOL_EEM = 0x07, // Ethernet Emulation Model - CDC_COMM_PROTOCOL_EXT = 0xFE, // External Protocol: Commands defined by Command Set Functional Descriptor - CDC_COMM_PROTOCOL_VENDOR = 0xFF // Vendor-specific -} __attribute__((packed)) cdc_comm_protocol_t; - -/** - * @brief USB CDC Data Protocol Codes - * - * @see Table 7, USB CDC specification rev. 1.2 - */ -typedef enum { - CDC_DATA_PROTOCOL_NONE = 0x00, // No class specific protocol required - CDC_DATA_PROTOCOL_NCM = 0x01, // Network Transfer Block - CDC_DATA_PROTOCOL_I430 = 0x30, // Physical interface protocol for ISDN BRI - CDC_DATA_PROTOCOL_HDLC = 0x31, // HDLC - CDC_DATA_PROTOCOL_Q921M = 0x50, // Management protocol for Q.921 data link protocol - CDC_DATA_PROTOCOL_Q921 = 0x51, // Data link protocol for Q.931 - CDC_DATA_PROTOCOL_Q921TM = 0x52, // TEI-multiplexor for Q.921 data link protocol - CDC_DATA_PROTOCOL_V42BIS = 0x90, // Data compression procedures - CDC_DATA_PROTOCOL_Q931 = 0x91, // Euro-ISDN protocol control - CDC_DATA_PROTOCOL_V120 = 0x92, // V.24 rate adaptation to ISDN - CDC_DATA_PROTOCOL_CAPI = 0x93, // CAPI Commands - CDC_DATA_PROTOCOL_VENDOR = 0xFF // Vendor-specific -} __attribute__((packed)) cdc_data_protocol_t; - -/** - * @brief USB CDC Request Codes - * - * @see Table 19, USB CDC specification rev. 1.2 - */ -typedef enum { - CDC_REQ_SEND_ENCAPSULATED_COMMAND = 0x00, - CDC_REQ_GET_ENCAPSULATED_RESPONSE = 0x01, - CDC_REQ_SET_COMM_FEATURE = 0x02, - CDC_REQ_GET_COMM_FEATURE = 0x03, - CDC_REQ_CLEAR_COMM_FEATURE = 0x04, - CDC_REQ_SET_AUX_LINE_STATE = 0x10, - CDC_REQ_SET_HOOK_STATE = 0x11, - CDC_REQ_PULSE_SETUP = 0x12, - CDC_REQ_SEND_PULSE = 0x13, - CDC_REQ_SET_PULSE_TIME = 0x14, - CDC_REQ_RING_AUX_JACK = 0x15, - CDC_REQ_SET_LINE_CODING = 0x20, - CDC_REQ_GET_LINE_CODING = 0x21, - CDC_REQ_SET_CONTROL_LINE_STATE = 0x22, - CDC_REQ_SEND_BREAK = 0x23, - CDC_REQ_SET_RINGER_PARMS = 0x30, - CDC_REQ_GET_RINGER_PARMS = 0x31, - CDC_REQ_SET_OPERATION_PARMS = 0x32, - CDC_REQ_GET_OPERATION_PARMS = 0x33, - CDC_REQ_SET_LINE_PARMS = 0x34, - CDC_REQ_GET_LINE_PARMS = 0x35, - CDC_REQ_DIAL_DIGITS = 0x36, - CDC_REQ_SET_UNIT_PARAMETER = 0x37, - CDC_REQ_GET_UNIT_PARAMETER = 0x38, - CDC_REQ_CLEAR_UNIT_PARAMETER = 0x39, - CDC_REQ_GET_PROFILE = 0x3A, - CDC_REQ_SET_ETHERNET_MULTICAST_FILTERS = 0x40, - CDC_REQ_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41, - CDC_REQ_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42, - CDC_REQ_SET_ETHERNET_PACKET_FILTER = 0x43, - CDC_REQ_GET_ETHERNET_STATISTIC = 0x44, - CDC_REQ_SET_ATM_DATA_FORMAT = 0x50, - CDC_REQ_GET_ATM_DEVICE_STATISTICS = 0x51, - CDC_REQ_SET_ATM_DEFAULT_VC = 0x52, - CDC_REQ_GET_ATM_VC_STATISTICS = 0x53, - CDC_REQ_GET_NTB_PARAMETERS = 0x80, - CDC_REQ_GET_NET_ADDRESS = 0x81, - CDC_REQ_SET_NET_ADDRESS = 0x82, - CDC_REQ_GET_NTB_FORMAT = 0x83, - CDC_REQ_SET_NTB_FORMAT = 0x84, - CDC_REQ_GET_NTB_INPUT_SIZE = 0x85, - CDC_REQ_SET_NTB_INPUT_SIZE = 0x86, - CDC_REQ_GET_MAX_DATAGRAM_SIZE = 0x87, - CDC_REQ_SET_MAX_DATAGRAM_SIZE = 0x88, - CDC_REQ_GET_CRC_MODE = 0x89, - CDC_REQ_SET_CRC_MODE = 0x8A -} __attribute__((packed)) cdc_request_code_t; - -/** - * @brief USB CDC Notification Codes - * - * @see Table 20, USB CDC specification rev. 1.2 - */ -typedef enum { - CDC_NOTIF_NETWORK_CONNECTION = 0x00, - CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, - CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08, - CDC_NOTIF_RING_DETECT = 0x09, - CDC_NOTIF_SERIAL_STATE = 0x20, - CDC_NOTIF_CALL_STATE_CHANGE = 0x28, - CDC_NOTIF_LINE_STATE_CHANGE = 0x29, - CDC_NOTIF_CONNECTION_SPEED_CHANGE = 0x2A -} __attribute__((packed)) cdc_notification_code_t; - -typedef struct { - uint8_t bmRequestType; - cdc_notification_code_t bNotificationCode; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; - uint8_t Data[]; -} __attribute__((packed)) cdc_notification_t; - -/** - * @brief USB CDC Header Functional Descriptor - * - * @see Table 15, USB CDC specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; // Upper nibble: CDC code 0x02, Lower nibble: intf/ep descriptor type 0x04/0x05 - const cdc_desc_subtype_t bDescriptorSubtype; - uint16_t bcdCDC; // CDC version as binary-coded decimal. This driver is written for version 1.2 -} __attribute__((packed)) cdc_header_desc_t; - -/** - * @brief USB CDC Union Functional Descriptor - * - * @see Table 16, USB CDC specification rev. 1.2 - */ -typedef struct { - uint8_t bFunctionLength; - const uint8_t bDescriptorType; // Upper nibble: CDC code 0x02, Lower nibble: intf/ep descriptor type 0x04/0x05 - const cdc_desc_subtype_t bDescriptorSubtype; - const uint8_t bControlInterface; // Master/controlling interface - uint8_t bSubordinateInterface[]; // Slave/subordinate interfaces -} __attribute__((packed)) cdc_union_desc_t; diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt deleted file mode 100644 index 067cc9bb4e..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "test_cdc_acm_host.c" - INCLUDE_DIRS "." - REQUIRES cdc_acm_host unity) diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c deleted file mode 100644 index b1bf076238..0000000000 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "soc/soc_caps.h" -#if SOC_USB_OTG_SUPPORTED - -#include -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "esp_err.h" - -#include "esp_private/usb_phy.h" -#include "usb/usb_host.h" -#include "usb/cdc_acm_host.h" -#include - -#include "esp_intr_alloc.h" - -#include "unity.h" -#include "soc/usb_wrap_struct.h" - -static uint8_t tx_buf[] = "HELLO"; -static uint8_t tx_buf2[] = "WORLD"; -static int nb_of_responses; -static int nb_of_responses2; -static usb_phy_handle_t phy_hdl = NULL; - -static void force_conn_state(bool connected, TickType_t delay_ticks) -{ - TEST_ASSERT_NOT_EQUAL(NULL, phy_hdl); - if (delay_ticks > 0) { - //Delay of 0 ticks causes a yield. So skip if delay_ticks is 0. - vTaskDelay(delay_ticks); - } - ESP_ERROR_CHECK(usb_phy_action(phy_hdl, (connected) ? USB_PHY_ACTION_HOST_ALLOW_CONN : USB_PHY_ACTION_HOST_FORCE_DISCONN)); -} - -void usb_lib_task(void *arg) -{ - //Initialize the internal USB PHY to connect to the USB OTG peripheral. We manually install the USB PHY for testing - usb_phy_config_t phy_config = { - .controller = USB_PHY_CTRL_OTG, - .target = USB_PHY_TARGET_INT, - .otg_mode = USB_OTG_MODE_HOST, - .otg_speed = USB_PHY_SPEED_UNDEFINED, //In Host mode, the speed is determined by the connected device - .gpio_conf = NULL, - }; - TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_hdl)); - // Install USB Host driver. Should only be called once in entire application - const usb_host_config_t host_config = { - .skip_phy_setup = true, - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - TEST_ASSERT_EQUAL(ESP_OK, usb_host_install(&host_config)); - printf("USB Host installed\n"); - xTaskNotifyGive(arg); - - while (1) { - // Start handling system events - uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - printf("No more clients: clean up\n"); - // The device should not have been freed yet, so we expect an ESP_ERR_NOT_FINISHED - TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all()); - } - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - printf("All free: uninstall USB lib\n"); - break; - } - } - - // Clean up USB Host - vTaskDelay(10); // Short delay to allow clients clean-up - usb_host_lib_handle_events(0, NULL); // Make sure there are now pending events - TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall()); - //Tear down USB PHY - TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_hdl)); - phy_hdl = NULL; - vTaskDelete(NULL); -} - -void test_install_cdc_driver(void) -{ - // Create a task that will handle USB library events - TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(usb_lib_task, "usb_lib", 4*4096, xTaskGetCurrentTaskHandle(), 10, NULL)); - ulTaskNotifyTake(false, 1000); - - printf("Installing CDC-ACM driver\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_install(NULL)); -} - -/* ------------------------------- Callbacks -------------------------------- */ -static void handle_rx(uint8_t *data, size_t data_len, void *arg) -{ - printf("Data received\n"); - nb_of_responses++; - TEST_ASSERT_EQUAL_STRING_LEN(data, arg, data_len); -} - -static void handle_rx2(uint8_t *data, size_t data_len, void *arg) -{ - printf("Data received 2\n"); - nb_of_responses2++; - TEST_ASSERT_EQUAL_STRING_LEN(data, arg, data_len); -} - -static void notif_cb(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_host_dev_event_data_t *event, void *user_ctx) -{ - switch (event->type) { - case CDC_ACM_HOST_ERROR: - printf("Error event %d\n", event->data.error); - break; - case CDC_ACM_HOST_SERIAL_STATE: - break; - case CDC_ACM_HOST_NETWORK_CONNECTION: - break; - case CDC_ACM_HOST_DEVICE_DISCONNECTED: - printf("Disconnection event\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_hdl)); - xTaskNotifyGive(user_ctx); - break; - default: - assert(false); - } -} - -/* Basic test to check CDC communication: - * open/read/write/close device - * CDC-ACM specific commands: set/get_line_coding, set_control_line_state */ -TEST_CASE("USB Host CDC-ACM driver: Basic test", "[cdc_acm][ignore]") -{ - nb_of_responses = 0; - cdc_acm_dev_hdl_t cdc_dev = NULL; - - test_install_cdc_driver(); - - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 500, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx, - .user_arg = tx_buf, - }; - - printf("Opening CDC-ACM device\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - TEST_ASSERT_NOT_NULL(cdc_dev); - cdc_acm_host_desc_print(cdc_dev); - vTaskDelay(100); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - vTaskDelay(100); // Wait until responses are processed - - // We sent two messages, should get two responses - TEST_ASSERT_EQUAL(2, nb_of_responses); - - cdc_acm_line_coding_t line_coding_get; - const cdc_acm_line_coding_t line_coding_set = { - .dwDTERate = 9600, - .bDataBits = 7, - .bParityType = 1, - .bCharFormat = 1, - }; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_line_coding_set(cdc_dev, &line_coding_set)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_line_coding_get(cdc_dev, &line_coding_get)); - TEST_ASSERT_EQUAL_MEMORY(&line_coding_set, &line_coding_get, sizeof(cdc_acm_line_coding_t)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_set_control_line_state(cdc_dev, true, false)); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - - vTaskDelay(20); //Short delay to allow task to be cleaned up -} - -/* Test communication with multiple CDC-ACM devices from one thread */ -TEST_CASE("USB Host CDC-ACM driver: Multiple devices test", "[cdc_acm][ignore]") -{ - nb_of_responses = 0; - nb_of_responses2 = 0; - - test_install_cdc_driver(); - - printf("Opening 2 CDC-ACM devices\n"); - cdc_acm_dev_hdl_t cdc_dev1, cdc_dev2; - cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 1000, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx, - .user_arg = tx_buf, - }; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev1)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - dev_config.data_cb = handle_rx2; - dev_config.user_arg = tx_buf2; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 2, &dev_config, &cdc_dev2)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - TEST_ASSERT_NOT_NULL(cdc_dev1); - TEST_ASSERT_NOT_NULL(cdc_dev2); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev1, tx_buf, sizeof(tx_buf), 1000)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev2, tx_buf2, sizeof(tx_buf2), 1000)); - - vTaskDelay(100); // Wait for RX callbacks - - // We sent two messages, should get two responses - TEST_ASSERT_EQUAL(1, nb_of_responses); - TEST_ASSERT_EQUAL(1, nb_of_responses2); - - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev1)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev2)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - - //Short delay to allow task to be cleaned up - vTaskDelay(20); -} - -#define MULTIPLE_THREADS_TRANSFERS_NUM 5 -#define MULTIPLE_THREADS_TASKS_NUM 4 -void tx_task(void *arg) -{ - cdc_acm_dev_hdl_t cdc_dev = (cdc_acm_dev_hdl_t) arg; - // Send multiple transfers to make sure that some of them will run at the same time - for (int i = 0; i < MULTIPLE_THREADS_TRANSFERS_NUM; i++) { - // BULK endpoints - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - - // CTRL endpoints - cdc_acm_line_coding_t line_coding_get; - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_line_coding_get(cdc_dev, &line_coding_get)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_set_control_line_state(cdc_dev, true, false)); - } - vTaskDelete(NULL); -} - -/** - * @brief Multiple threads test - * - * In this test, one CDC device is accessed from multiple threads. - * It has to be opened/closed just once, though. - */ -TEST_CASE("USB Host CDC-ACM driver: Multiple threads test", "[cdc_acm][ignore]") -{ - nb_of_responses = 0; - cdc_acm_dev_hdl_t cdc_dev; - test_install_cdc_driver(); - - const cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 5000, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx, - .user_arg = tx_buf, - }; - - printf("Opening CDC-ACM device\n"); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); // 0x303A:0x4002 (TinyUSB Dual CDC device) - TEST_ASSERT_NOT_NULL(cdc_dev); - - // Create two tasks that will try to access cdc_dev - for (int i = 0; i < MULTIPLE_THREADS_TASKS_NUM; i++) { - TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(tx_task, "CDC TX", 4096, cdc_dev, i + 3, NULL)); - } - - // Wait until all tasks finish - vTaskDelay(pdMS_TO_TICKS(500)); - TEST_ASSERT_EQUAL(MULTIPLE_THREADS_TASKS_NUM * MULTIPLE_THREADS_TRANSFERS_NUM, nb_of_responses); - - // Clean-up - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); -} - -/* Test CDC driver reaction to USB device sudden disconnection */ -TEST_CASE("USB Host CDC-ACM driver: Sudden disconnection test", "[cdc_acm][ignore]") -{ - test_install_cdc_driver(); - - cdc_acm_dev_hdl_t cdc_dev; - cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 1000, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx - }; - dev_config.user_arg = xTaskGetCurrentTaskHandle(); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - TEST_ASSERT_NOT_NULL(cdc_dev); - - force_conn_state(false, pdMS_TO_TICKS(10)); - // Notify will succeed only if CDC_ACM_HOST_DEVICE_DISCONNECTED notification was generated - TEST_ASSERT_EQUAL(1, ulTaskNotifyTake(false, pdMS_TO_TICKS(100))); - - force_conn_state(true, 0); // Switch back to real PHY - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); //Short delay to allow task to be cleaned up -} - -/** - * @brief CDC-ACM error handling test - * - * There are multiple erroneous scenarios checked in this test: - * - * -# Install CDC-ACM driver without USB Host - * -# Open device without installed driver - * -# Uninstall driver before installing it - * -# Open non-existent device - * -# Open the same device twice - * -# Uninstall driver with open devices - * -# Send data that is too large - * -# Send unsupported CDC request - * -# Write to read-only device - */ -TEST_CASE("USB Host CDC-ACM driver: Error handling", "[cdc_acm][ignore]") -{ - cdc_acm_dev_hdl_t cdc_dev; - cdc_acm_host_device_config_t dev_config = { - .connection_timeout_ms = 500, - .out_buffer_size = 64, - .event_cb = notif_cb, - .data_cb = handle_rx - }; - - // Install CDC-ACM driver without USB Host - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_install(NULL)); - - // Open device without installed driver - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - - // Uninstall driver before installing it - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_uninstall()); - - // Properly install USB and CDC drivers - test_install_cdc_driver(); - - // Open non-existent device - TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, cdc_acm_host_open(0x303A, 0x1234, 0, &dev_config, &cdc_dev)); // 0x303A:0x1234 this device is not connected to USB Host - TEST_ASSERT_NULL(cdc_dev); - - // Open regular device - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - TEST_ASSERT_NOT_NULL(cdc_dev); - - // Open one CDC-ACM device twice //@todo this test is commented out due to bug in usb_host - //cdc_acm_dev_hdl_t cdc_dev_test; - //TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev_test)); - //TEST_ASSERT_NULL(cdc_dev_test); - - // Uninstall driver with open devices - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, cdc_acm_host_uninstall()); - - // Send data that is too large and NULL data - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, 1024, 1000)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, cdc_acm_host_data_tx_blocking(cdc_dev, NULL, 10, 1000)); - - // Change mode to read-only and try to write to it - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - dev_config.out_buffer_size = 0; // Read-only device - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, 0x4002, 0, &dev_config, &cdc_dev)); - TEST_ASSERT_NOT_NULL(cdc_dev); - TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, cdc_acm_host_data_tx_blocking(cdc_dev, tx_buf, sizeof(tx_buf), 1000)); - - // Send unsupported CDC request (TinyUSB accepts SendBreak command, eventhough it doesn't support it) - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_send_break(cdc_dev, 100)); - - // Clean-up - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); - TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); - vTaskDelay(20); -} - -#endif diff --git a/examples/peripherals/usb/host/msc/README.md b/examples/peripherals/usb/host/msc/README.md index 934ba54771..ad2f782401 100644 --- a/examples/peripherals/usb/host/msc/README.md +++ b/examples/peripherals/usb/host/msc/README.md @@ -6,7 +6,8 @@ ## Overview This example demonstrates usage of Mass Storage Class to get access to storage on USB memory stick. -Example caries out read and write file operations, as USB storage is mounted to Virtual filesystem. +Upon connection of USB stick, storage is mounted to Virtual filesystem. Example then creates `ESP` subdirectory(if not present already), as well as `text.txt` file. Its content is then repetitively printed to monitor until USB stick is manually ejected. User can decide whether or not to deinitialize the whole +USB stack or not by shorting GPIO10 to ground. When GPIO10 is left unconnected USB stack is not deinitialized, USB stick can be plugged-in again. ### Hardware Required @@ -29,6 +30,8 @@ ESP BOARD USB CONNECTOR (type A) -- ``` +Additionally, GPIO10 can be shorted to ground in order to deinitialize USB stack after ejecting USB stick. + ### Build and Flash Build the project and flash it to the board, then run monitor tool to view serial output: diff --git a/examples/peripherals/usb/host/msc/components/msc/CMakeLists.txt b/examples/peripherals/usb/host/msc/components/msc/CMakeLists.txt deleted file mode 100644 index 36848f1682..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(sources src/msc_scsi_bot.c - src/diskio_usb.c - src/msc_host.c - src/msc_host_vfs.c) - -idf_component_register( SRCS ${sources} - INCLUDE_DIRS include - PRIV_INCLUDE_DIRS private_include - REQUIRES usb fatfs ) diff --git a/examples/peripherals/usb/host/msc/components/msc/README.md b/examples/peripherals/usb/host/msc/components/msc/README.md deleted file mode 100644 index 4f722d1fcd..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# USB Host MSC (Mass Storage Class) Driver - -This directory contains an implementation of a USB Mass Storage Class Driver implemented on top of the [USB Host Library](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html). - -MSC driver allows access to USB flash drivers using the BOT “Bulk-Only Transport” protocol and the Transparent SCSI command set. - -## Usage - -- First, usb host library has to be initialized by calling `usb_host_install` -- USB Host Library events have to be handled by invoking `usb_host_lib_handle_events` periodically. - In general, an application should spawn a dedicated task handle USB Host Library events. - However, in order to save RAM, an already existing task can also be used to call `usb_host_lib_handle_events`. -- Mass Storage Class driver is installed by calling `usb_msc_install` function along side with configuration. -- Supplied configuration contains user provided callback function invoked whenever MSC device is connected/disconnected - and optional parameters for creating background task handling MSC related events. - Alternatively, user can call `usb_msc_handle_events` function from already existing task. -- After receiving `MSC_DEVICE_CONNECTED` event, user has to install device with `usb_msc_install_device` function, - obtaining MSC device handle. -- USB descriptors can be printed out with `usb_msc_print_descriptors` and general information about MSC device retrieved - with `from usb_msc_get_device_info` function. -- Obtained device handle is then used in helper function `usb_msc_vfs_register` mounting USB Disk to Virtual filesystem. -- At this point, standard C functions for accessing storage (`fopen`, `fwrite`, `fread`, `mkdir` etc.) can be carried out. -- In order to uninstall the whole USB stack, deinitializing counterparts to functions above has to be called in reverse order. - -## Known issues - -- Driver only supports USB 2.0 flash drives using the BOT “Bulk-Only Transport” protocol and the Transparent SCSI command set -- Composite USB devices are not supported - -## Troubleshooting - -After connecting composite USB device, driver prints `COMPOSITE DEVICES UNSUPPORTED` diff --git a/examples/peripherals/usb/host/msc/components/msc/include/msc_host.h b/examples/peripherals/usb/host/msc/components/msc/include/msc_host.h deleted file mode 100644 index 058037649d..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/include/msc_host.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include "esp_err.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define ESP_ERR_MSC_HOST_BASE 0x1700 /*!< MSC host error code base */ -#define ESP_ERR_MSC_MOUNT_FAILED (ESP_ERR_MSC_HOST_BASE + 1) /*!< Failed to mount storage */ -#define ESP_ERR_MSC_FORMAT_FAILED (ESP_ERR_MSC_HOST_BASE + 2) /*!< Failed to format storage */ -#define ESP_ERR_MSC_INTERNAL (ESP_ERR_MSC_HOST_BASE + 3) /*!< MSC host internal error */ - -#define MSC_STR_DESC_SIZE 32 - -typedef struct msc_host_device *msc_host_device_handle_t; /**< Handle to a Mass Storage Device */ - -/** - * @brief USB Mass Storage event containing event type and associated device handle. -*/ -typedef struct { - enum { - MSC_DEVICE_CONNECTED, /**< MSC device has been connected to the system.*/ - MSC_DEVICE_DISCONNECTED, /**< MSC device has been disconnected from the system.*/ - } event; - union { - uint8_t address; /**< Address of connected MSC device.*/ - msc_host_device_handle_t handle; /**< MSC device handle to disconnected device.*/ - } device; -} msc_host_event_t; - -/** - * @brief USB Mass Storage event callback. - * - * @param[in] event mass storage event -*/ -typedef void (*msc_host_event_cb_t)(const msc_host_event_t *event, void *arg); - -/** - * @brief MSC configuration structure. -*/ -typedef struct { - bool create_backround_task; /**< When set to true, background task handling usb events is created. - Otherwise user has to periodically call msc_host_handle_events function */ - size_t task_priority; /**< Task priority of crated background task */ - size_t stack_size; /**< Stack size of crated background task */ - BaseType_t core_id; /**< Select core on which background task will run or tskNO_AFFINITY */ - msc_host_event_cb_t callback; /**< Callback invoked when MSC event occurs. Must not be NULL. */ - void *callback_arg; /**< User provided argument passed to callback */ -} msc_host_driver_config_t; - -/** - * @brief MSC device info. -*/ -typedef struct { - uint32_t sector_count; - uint32_t sector_size; - uint16_t idProduct; - uint16_t idVendor; - wchar_t iManufacturer[MSC_STR_DESC_SIZE]; - wchar_t iProduct[MSC_STR_DESC_SIZE]; - wchar_t iSerialNumber[MSC_STR_DESC_SIZE]; -} msc_host_device_info_t; - -/** - * @brief Install USB Host Mass Storage Class driver - * - * @param[in] config configuration structure MSC to create - * @return esp_err_r - */ -esp_err_t msc_host_install(const msc_host_driver_config_t *config); - -/** - * @brief Uninstall Mass Storage Class driver - * @return esp_err_t - */ -esp_err_t msc_host_uninstall(void); - -/** - * @brief Initialization of MSC device. - * - * @param[in] device_address Device address obtained from MSC callback provided upon connection and enumeration - * @param[out] device Mass storage device handle to be used for subsequent calls. - * @return esp_err_t - */ -esp_err_t msc_host_install_device(uint8_t device_address, msc_host_device_handle_t *device); - -/** - * @brief Deinitialization of MSC device. - * - * @param[in] device Device handle obtained from msc_host_install_device function - * @return esp_err_t - */ -esp_err_t msc_host_uninstall_device(msc_host_device_handle_t device); - -/** - * @brief Helper function for reading sector from mass storage device. - * - * @warning This call is not thread safe and should not be combined - * with accesses to storage through file system. - * - * @note Provided sector and size cannot exceed - * sector_count and sector_size obtained from msc_host_device_info_t - * - * @param[in] device Device handle - * @param[in] sector Number of sector to be read - * @param[out] data Buffer into which data will be written - * @param[in] size Number of bytes to be read - * @return esp_err_t - */ -esp_err_t msc_host_read_sector(msc_host_device_handle_t device, size_t sector, void *data, size_t size); - -/** - * @brief Helper function for writing sector to mass storage device. - * - * @warning This call is not thread safe and should not be combined - * with accesses to storare through file system. - * - * @note Provided sector and size cannot exceed - * sector_count and sector_size obtained from msc_host_device_info_t - * - * @param[in] device Device handle - * @param[in] sector Number of sector to be read - * @param[in] data Data to be written to the sector - * @param[in] size Number of bytes to be written - * @return esp_err_t - */ -esp_err_t msc_host_write_sector(msc_host_device_handle_t device, size_t sector, const void *data, size_t size); - -/** - * @brief Handle MSC HOST events. - * - * @param[in] timeout_ms Timeout in miliseconds - * @return esp_err_t - */ -esp_err_t msc_host_handle_events(uint32_t timeout_ms); - -/** - * @brief Gets devices information. - * - * @warning This call is not thread safe and should not be combined - * with accesses to storare through file system. - * - * @param[in] device Handle to device - * @param[out] info Structure to be populated with device info - * @return esp_err_t - */ -esp_err_t msc_host_get_device_info(msc_host_device_handle_t device, msc_host_device_info_t *info); - -/** - * @brief Print configuration descriptor. - * - * @param[in] device Handle of MSC device - * @return esp_err_t - */ -esp_err_t msc_host_print_descriptors(msc_host_device_handle_t device); - -#ifdef __cplusplus -} -#endif //__cplusplus diff --git a/examples/peripherals/usb/host/msc/components/msc/include/msc_host_vfs.h b/examples/peripherals/usb/host/msc/components/msc/include/msc_host_vfs.h deleted file mode 100644 index af9137f9d9..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/include/msc_host_vfs.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "esp_vfs_fat.h" -#include "msc_host.h" -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct msc_host_vfs *msc_host_vfs_handle_t; /**< VFS handle to attached Mass Storage device */ - -/** - * @brief Register MSC device to Virtual filesystem. - * - * @param[in] device Device handle obtained from MSC callback provided upon initialization - * @param[in] base_path Base VFS path to be used to access file storage - * @param[in] mount_config Mount configuration. - * @param[out] vfs_handle Handle to MSC device associated with registered VFS - * @return esp_err_t - */ -esp_err_t msc_host_vfs_register(msc_host_device_handle_t device, - const char *base_path, - const esp_vfs_fat_mount_config_t *mount_config, - msc_host_vfs_handle_t *vfs_handle); - - -/** - * @brief Unregister MSC device from Virtual filesystem. - * - * @param[in] vfs_handle VFS handle obtained from MSC callback provided upon initialization - * @return esp_err_t - */ -esp_err_t msc_host_vfs_unregister(msc_host_vfs_handle_t vfs_handle); - -#ifdef __cplusplus -} -#endif diff --git a/examples/peripherals/usb/host/msc/components/msc/private_include/diskio_usb.h b/examples/peripherals/usb/host/msc/components/msc/private_include/diskio_usb.h deleted file mode 100644 index 6327d6eee5..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/private_include/diskio_usb.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Mass storage disk initialization structure - */ -typedef struct { - uint32_t block_size; /**< Block size */ - uint32_t block_count; /**< Block count */ -} usb_disk_t; - -/** - * @brief Register mass storage disk to fat file system - * - * @param[in] pdrv Number of free drive obtained from ff_diskio_get_drive() function - * @param[in] disk usb_disk_t structure - */ -void ff_diskio_register_msc(uint8_t pdrv, usb_disk_t *disk); - -/** - * @brief Obtains number of drive assigned to usb disk upon calling ff_diskio_register_msc() - * - * @param[in] disk usb_disk_t structure - * @return Drive number - */ -uint8_t ff_diskio_get_pdrv_disk(const usb_disk_t *disk); - -#ifdef __cplusplus -} -#endif //__cplusplus diff --git a/examples/peripherals/usb/host/msc/components/msc/private_include/msc_common.h b/examples/peripherals/usb/host/msc/components/msc/private_include/msc_common.h deleted file mode 100644 index a5bed31e1d..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/private_include/msc_common.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include "esp_err.h" -#include "esp_check.h" -#include "diskio_usb.h" -#include "usb/usb_host.h" -#include "usb/usb_types_stack.h" -#include "freertos/semphr.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef enum { - MSC_EP_OUT, - MSC_EP_IN -} msc_endpoint_t; - -typedef struct { - uint16_t bulk_in_mps; - uint8_t bulk_in_ep; - uint8_t bulk_out_ep; - uint8_t iface_num; -} msc_config_t; - -typedef struct msc_host_device { - STAILQ_ENTRY(msc_host_device) tailq_entry; - usb_transfer_status_t transfer_status; - SemaphoreHandle_t transfer_done; - usb_device_handle_t handle; - usb_transfer_t *xfer; - msc_config_t config; - usb_disk_t disk; -} msc_device_t; - -esp_err_t msc_bulk_transfer(msc_device_t *device_handle, uint8_t *data, size_t size, msc_endpoint_t ep); - -esp_err_t msc_control_transfer(msc_device_t *device_handle, usb_transfer_t *xfer, size_t len); - -#define MSC_GOTO_ON_ERROR(exp) ESP_GOTO_ON_ERROR(exp, fail, TAG, "") - -#define MSC_GOTO_ON_FALSE(exp, err) ESP_GOTO_ON_FALSE( (exp), err, fail, TAG, "" ) - -#define MSC_RETURN_ON_ERROR(exp) ESP_RETURN_ON_ERROR((exp), TAG, "") - -#define MSC_RETURN_ON_FALSE(exp, err) ESP_RETURN_ON_FALSE( (exp), (err), TAG, "") - -#define MSC_RETURN_ON_INVALID_ARG(exp) ESP_RETURN_ON_FALSE((exp) != NULL, ESP_ERR_INVALID_ARG, TAG, "") - -#ifdef __cplusplus -} -#endif diff --git a/examples/peripherals/usb/host/msc/components/msc/private_include/msc_scsi_bot.h b/examples/peripherals/usb/host/msc/components/msc/private_include/msc_scsi_bot.h deleted file mode 100644 index d4ee210347..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/private_include/msc_scsi_bot.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include "esp_err.h" -#include "msc_common.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct { - uint8_t key; - uint8_t code; - uint8_t code_q; -} scsi_sense_data_t; - -esp_err_t scsi_cmd_read10(msc_device_t *device, - uint8_t *data, - uint32_t sector_address, - uint32_t num_sectors, - uint32_t sector_size); - -esp_err_t scsi_cmd_write10(msc_device_t *device, - const uint8_t *data, - uint32_t sector_address, - uint32_t num_sectors, - uint32_t sector_size); - -esp_err_t scsi_cmd_read_capacity(msc_device_t *device, - uint32_t *block_size, - uint32_t *block_count); - -esp_err_t scsi_cmd_sense(msc_device_t *device, scsi_sense_data_t *sense); - -esp_err_t scsi_cmd_unit_ready(msc_device_t *device); - -esp_err_t scsi_cmd_inquiry(msc_device_t *device); - -esp_err_t scsi_cmd_prevent_removal(msc_device_t *device, bool prevent); - -esp_err_t scsi_cmd_mode_sense(msc_device_t *device); - -esp_err_t msc_mass_reset(msc_device_t *device); - -esp_err_t msc_get_max_lun(msc_device_t *device, uint8_t *lun); - -#ifdef __cplusplus -} -#endif diff --git a/examples/peripherals/usb/host/msc/components/msc/src/diskio_usb.c b/examples/peripherals/usb/host/msc/components/msc/src/diskio_usb.c deleted file mode 100644 index 0191f289c9..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/src/diskio_usb.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "diskio_impl.h" -#include "ffconf.h" -#include "ff.h" -#include "esp_log.h" -#include "diskio_usb.h" -#include "msc_scsi_bot.h" -#include "msc_common.h" -#include "usb/usb_types_stack.h" - -static usb_disk_t *s_disks[FF_VOLUMES] = { NULL }; - -static const char *TAG = "diskio_usb"; - -static DSTATUS usb_disk_initialize (BYTE pdrv) -{ - return RES_OK; -} - -static DSTATUS usb_disk_status (BYTE pdrv) -{ - return RES_OK; -} - -static DRESULT usb_disk_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) -{ - assert(pdrv < FF_VOLUMES); - assert(s_disks[pdrv]); - - esp_err_t err; - usb_disk_t *disk = s_disks[pdrv]; - size_t sector_size = disk->block_size; - msc_device_t *dev = __containerof(disk, msc_device_t, disk); - - for (int i = 0; i < count; i++) { - err = scsi_cmd_read10(dev, &buff[i * sector_size], sector + i, 1, sector_size); - if (err != ESP_OK) { - ESP_LOGE(TAG, "scsi_cmd_read10 failed (%d)", err); - return RES_ERROR; - } - - } - - return RES_OK; -} - -static DRESULT usb_disk_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) -{ - assert(pdrv < FF_VOLUMES); - assert(s_disks[pdrv]); - - esp_err_t err; - usb_disk_t *disk = s_disks[pdrv]; - size_t sector_size = disk->block_size; - msc_device_t *dev = __containerof(disk, msc_device_t, disk); - - for (int i = 0; i < count; i++) { - err = scsi_cmd_write10(dev, &buff[i * sector_size], sector + i, 1, sector_size); - if (err != ESP_OK) { - ESP_LOGE(TAG, "scsi_cmd_write10 failed (%d)", err); - return RES_ERROR; - } - - } - return RES_OK; -} - -static DRESULT usb_disk_ioctl (BYTE pdrv, BYTE cmd, void *buff) -{ - assert(pdrv < FF_VOLUMES); - assert(s_disks[pdrv]); - - usb_disk_t *disk = s_disks[pdrv]; - - switch (cmd) { - case CTRL_SYNC: - return RES_OK; - case GET_SECTOR_COUNT: - *((DWORD *) buff) = disk->block_count; - return RES_OK; - case GET_SECTOR_SIZE: - *((WORD *) buff) = disk->block_size; - return RES_OK; - case GET_BLOCK_SIZE: - return RES_ERROR; - } - return RES_ERROR; -} - -void ff_diskio_register_msc(BYTE pdrv, usb_disk_t *disk) -{ - assert(pdrv < FF_VOLUMES); - - static const ff_diskio_impl_t usb_disk_impl = { - .init = &usb_disk_initialize, - .status = &usb_disk_status, - .read = &usb_disk_read, - .write = &usb_disk_write, - .ioctl = &usb_disk_ioctl - }; - s_disks[pdrv] = disk; - ff_diskio_register(pdrv, &usb_disk_impl); -} - -BYTE ff_diskio_get_pdrv_disk(const usb_disk_t *disk) -{ - for (int i = 0; i < FF_VOLUMES; i++) { - if (disk == s_disks[i]) { - return i; - } - } - return 0xff; -} diff --git a/examples/peripherals/usb/host/msc/components/msc/src/msc_host.c b/examples/peripherals/usb/host/msc/components/msc/src/msc_host.c deleted file mode 100644 index 402b4fcf09..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/src/msc_host.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "usb/usb_host.h" -#include "diskio_usb.h" -#include "msc_common.h" -#include "msc_host.h" -#include "msc_scsi_bot.h" -#include "usb/usb_types_ch9.h" -#include "usb/usb_helpers.h" - -static portMUX_TYPE msc_lock = portMUX_INITIALIZER_UNLOCKED; - -#define MSC_ENTER_CRITICAL() portENTER_CRITICAL(&msc_lock) -#define MSC_EXIT_CRITICAL() portEXIT_CRITICAL(&msc_lock) - -#define MSC_GOTO_ON_FALSE_CRITICAL(exp, err) \ - do { \ - if(!(exp)) { \ - MSC_EXIT_CRITICAL(); \ - ret = err; \ - goto fail; \ - } \ - } while(0) - -#define MSC_RETURN_ON_FALSE_CRITICAL(exp, err) \ - do { \ - if(!(exp)) { \ - MSC_EXIT_CRITICAL(); \ - return err; \ - } \ - } while(0) - -#define WAIT_FOR_READY_TIMEOUT_MS 3000 -#define TAG "USB_MSC" - -#define SCSI_COMMAND_SET 0x06 -#define BULK_ONLY_TRANSFER 0x50 -#define MSC_NO_SENSE 0x00 -#define MSC_NOT_READY 0x02 -#define MSC_UNIT_ATTENTION 0x06 - -typedef struct { - usb_host_client_handle_t client_handle; - msc_host_event_cb_t user_cb; - void *user_arg; - SemaphoreHandle_t all_events_handled; - volatile bool end_client_event_handling; -} msc_driver_t; - -static msc_driver_t *s_msc_driver; - -STAILQ_HEAD(devices, msc_host_device) devices_tailq; - -static const usb_standard_desc_t *next_interface_desc(const usb_standard_desc_t *desc, size_t len, size_t *offset) -{ - return usb_parse_next_descriptor_of_type(desc, len, USB_W_VALUE_DT_INTERFACE, (int *)offset); -} - -static const usb_standard_desc_t *next_endpoint_desc(const usb_standard_desc_t *desc, size_t len, size_t *offset) -{ - return usb_parse_next_descriptor_of_type(desc, len, USB_B_DESCRIPTOR_TYPE_ENDPOINT, (int *)offset); -} - -static const usb_intf_desc_t *find_msc_interface(const usb_config_desc_t *config_desc, size_t *offset) -{ - size_t total_length = config_desc->wTotalLength; - const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *)config_desc; - - next_desc = next_interface_desc(next_desc, total_length, offset); - - while ( next_desc ) { - - const usb_intf_desc_t *ifc_desc = (const usb_intf_desc_t *)next_desc; - - if ( ifc_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE && - ifc_desc->bInterfaceSubClass == SCSI_COMMAND_SET && - ifc_desc->bInterfaceProtocol == BULK_ONLY_TRANSFER ) { - return ifc_desc; - } - - next_desc = next_interface_desc(next_desc, total_length, offset); - }; - return NULL; -} - -/** - * @brief Extracts configuration from configuration descriptor. - * - * @note Passes interface and endpoint descriptors to obtain: - - * - interface number, IN endpoint, OUT endpoint, max. packet size - * - * @param[in] cfg_desc Configuration descriptor - * @param[out] cfg Obtained configuration - * @return esp_err_t - */ -static esp_err_t extract_config_from_descriptor(const usb_config_desc_t *cfg_desc, msc_config_t *cfg) -{ - size_t offset = 0; - size_t total_len = cfg_desc->wTotalLength; - const usb_intf_desc_t *ifc_desc = find_msc_interface(cfg_desc, &offset); - assert(ifc_desc); - const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *)ifc_desc; - const usb_ep_desc_t *ep_desc = NULL; - - cfg->iface_num = ifc_desc->bInterfaceNumber; - - next_desc = next_endpoint_desc(next_desc, total_len, &offset); - MSC_RETURN_ON_FALSE(next_desc, ESP_ERR_NOT_SUPPORTED); - ep_desc = (const usb_ep_desc_t *)next_desc; - - if (ep_desc->bEndpointAddress & 0x80) { - cfg->bulk_in_ep = ep_desc->bEndpointAddress; - cfg->bulk_in_mps = ep_desc->wMaxPacketSize; - } else { - cfg->bulk_out_ep = ep_desc->bEndpointAddress; - } - - next_desc = next_endpoint_desc(next_desc, total_len, &offset); - MSC_RETURN_ON_FALSE(next_desc, ESP_ERR_NOT_SUPPORTED); - ep_desc = (const usb_ep_desc_t *)next_desc; - - if (ep_desc->bEndpointAddress & 0x80) { - cfg->bulk_in_ep = ep_desc->bEndpointAddress; - cfg->bulk_in_mps = ep_desc->wMaxPacketSize; - } else { - cfg->bulk_out_ep = ep_desc->bEndpointAddress; - } - - return ESP_OK; -} - -static esp_err_t msc_deinit_device(msc_device_t *dev, bool install_failed) -{ - MSC_ENTER_CRITICAL(); - MSC_RETURN_ON_FALSE_CRITICAL( dev, ESP_ERR_INVALID_STATE ); - STAILQ_REMOVE(&devices_tailq, dev, msc_host_device, tailq_entry); - MSC_EXIT_CRITICAL(); - - if (dev->transfer_done) { - vSemaphoreDelete(dev->transfer_done); - } - if (install_failed) { - // Error code is unchecked, as it's unknown at what point installation failed. - usb_host_interface_release(s_msc_driver->client_handle, dev->handle, dev->config.iface_num); - usb_host_device_close(s_msc_driver->client_handle, dev->handle); - usb_host_transfer_free(dev->xfer); - } else { - MSC_RETURN_ON_ERROR( usb_host_interface_release(s_msc_driver->client_handle, dev->handle, dev->config.iface_num) ); - MSC_RETURN_ON_ERROR( usb_host_device_close(s_msc_driver->client_handle, dev->handle) ); - MSC_RETURN_ON_ERROR( usb_host_transfer_free(dev->xfer) ); - } - - free(dev); - return ESP_OK; -} - -// Some MSC devices requires to change its internal state from non-ready to ready -static esp_err_t msc_wait_for_ready_state(msc_device_t *dev, size_t timeout_ms) -{ - esp_err_t err; - scsi_sense_data_t sense; - uint32_t trials = MAX(1, timeout_ms / 100); - - do { - err = scsi_cmd_unit_ready(dev); - if (err != ESP_OK) { - MSC_RETURN_ON_ERROR( scsi_cmd_sense(dev, &sense) ); - if (sense.key != MSC_NOT_READY && - sense.key != MSC_UNIT_ATTENTION && - sense.key != MSC_NO_SENSE) { - return ESP_ERR_MSC_INTERNAL; - } - } - vTaskDelay( pdMS_TO_TICKS(100) ); - } while (trials-- && err); - - return err; -} - -static bool is_mass_storage_device(uint8_t dev_addr) -{ - size_t dummy = 0; - bool is_msc_device = false; - usb_device_handle_t device; - const usb_config_desc_t *config_desc; - - if ( usb_host_device_open(s_msc_driver->client_handle, dev_addr, &device) == ESP_OK) { - if ( usb_host_get_active_config_descriptor(device, &config_desc) == ESP_OK ) { - if ( find_msc_interface(config_desc, &dummy) ) { - is_msc_device = true; - } else { - ESP_LOGD(TAG, "Connected USB device is not MSC"); - } - } - usb_host_device_close(s_msc_driver->client_handle, device); - } - - return is_msc_device; -} - -static void event_handler_task(void *arg) -{ - while (1) { - usb_host_client_handle_events(s_msc_driver->client_handle, pdMS_TO_TICKS(50)); - - if (s_msc_driver->end_client_event_handling) { - break; - } - } - usb_host_client_unblock(s_msc_driver->client_handle); - ESP_ERROR_CHECK( usb_host_client_deregister(s_msc_driver->client_handle) ); - xSemaphoreGive(s_msc_driver->all_events_handled); - vTaskDelete(NULL); -} - -static msc_device_t *find_msc_device(usb_device_handle_t device_handle) -{ - msc_host_device_handle_t device; - - STAILQ_FOREACH(device, &devices_tailq, tailq_entry) { - if (device_handle == device->handle) { - return device; - } - } - - return NULL; -} - -static void client_event_cb(const usb_host_client_event_msg_t *event, void *arg) -{ - if (event->event == USB_HOST_CLIENT_EVENT_NEW_DEV) { - if (is_mass_storage_device(event->new_dev.address)) { - const msc_host_event_t msc_event = { - .event = MSC_DEVICE_CONNECTED, - .device.address = event->new_dev.address, - }; - s_msc_driver->user_cb(&msc_event, s_msc_driver->user_arg); - } - } else if (event->event == USB_HOST_CLIENT_EVENT_DEV_GONE) { - msc_device_t *msc_device = find_msc_device(event->dev_gone.dev_hdl); - if (msc_device) { - const msc_host_event_t msc_event = { - .event = MSC_DEVICE_DISCONNECTED, - .device.handle = msc_device, - }; - s_msc_driver->user_cb(&msc_event, s_msc_driver->user_arg); - } - } -} - -esp_err_t msc_host_install(const msc_host_driver_config_t *config) -{ - esp_err_t ret; - - MSC_RETURN_ON_INVALID_ARG(config); - MSC_RETURN_ON_INVALID_ARG(config->callback); - if ( config->create_backround_task ) { - MSC_RETURN_ON_FALSE(config->stack_size != 0, ESP_ERR_INVALID_ARG); - MSC_RETURN_ON_FALSE(config->task_priority != 0, ESP_ERR_INVALID_ARG); - } - MSC_RETURN_ON_FALSE(!s_msc_driver, ESP_ERR_INVALID_STATE); - - msc_driver_t *driver = calloc(1, sizeof(msc_driver_t)); - MSC_RETURN_ON_FALSE(driver, ESP_ERR_NO_MEM); - driver->user_cb = config->callback; - driver->user_arg = config->callback_arg; - - usb_host_client_config_t client_config = { - .async.client_event_callback = client_event_cb, - .async.callback_arg = NULL, - .max_num_event_msg = 10, - }; - - driver->end_client_event_handling = false; - driver->all_events_handled = xSemaphoreCreateBinary(); - MSC_GOTO_ON_FALSE(driver->all_events_handled, ESP_ERR_NO_MEM); - - MSC_GOTO_ON_ERROR( usb_host_client_register(&client_config, &driver->client_handle) ); - - MSC_ENTER_CRITICAL(); - MSC_GOTO_ON_FALSE_CRITICAL(!s_msc_driver, ESP_ERR_INVALID_STATE); - s_msc_driver = driver; - STAILQ_INIT(&devices_tailq); - MSC_EXIT_CRITICAL(); - - if (config->create_backround_task) { - BaseType_t task_created = xTaskCreatePinnedToCore( - event_handler_task, "USB MSC", config->stack_size, - NULL, config->task_priority, NULL, config->core_id); - MSC_GOTO_ON_FALSE(task_created, ESP_ERR_NO_MEM); - } - - return ESP_OK; - -fail: - s_msc_driver = NULL; - usb_host_client_deregister(driver->client_handle); - if (driver->all_events_handled) { - vSemaphoreDelete(driver->all_events_handled); - } - free(driver); - return ret; -} - -esp_err_t msc_host_uninstall(void) -{ - // Make sure msc driver is installed, - // not being uninstalled from other task - // and no msc device is registered - MSC_ENTER_CRITICAL(); - MSC_RETURN_ON_FALSE_CRITICAL( s_msc_driver != NULL, ESP_ERR_INVALID_STATE ); - MSC_RETURN_ON_FALSE_CRITICAL( !s_msc_driver->end_client_event_handling, ESP_ERR_INVALID_STATE ); - MSC_RETURN_ON_FALSE_CRITICAL( STAILQ_EMPTY(&devices_tailq), ESP_ERR_INVALID_STATE ); - s_msc_driver->end_client_event_handling = true; - MSC_EXIT_CRITICAL(); - - xSemaphoreTake(s_msc_driver->all_events_handled, portMAX_DELAY); - vSemaphoreDelete(s_msc_driver->all_events_handled); - free(s_msc_driver); - s_msc_driver = NULL; - return ESP_OK; -} - -esp_err_t msc_host_install_device(uint8_t device_address, msc_host_device_handle_t *msc_device_handle) -{ - esp_err_t ret; - uint32_t block_size, block_count; - const usb_config_desc_t *config_desc; - msc_device_t *msc_device; - uint8_t lun; - size_t transfer_size = 512; // Normally the smallest block size - - MSC_GOTO_ON_FALSE( msc_device = calloc(1, sizeof(msc_device_t)), ESP_ERR_NO_MEM ); - - MSC_ENTER_CRITICAL(); - MSC_GOTO_ON_FALSE_CRITICAL( s_msc_driver, ESP_ERR_INVALID_STATE ); - MSC_GOTO_ON_FALSE_CRITICAL( s_msc_driver->client_handle, ESP_ERR_INVALID_STATE ); - STAILQ_INSERT_TAIL(&devices_tailq, msc_device, tailq_entry); - MSC_EXIT_CRITICAL(); - - MSC_GOTO_ON_FALSE( msc_device->transfer_done = xSemaphoreCreateBinary(), ESP_ERR_NO_MEM); - MSC_GOTO_ON_ERROR( usb_host_device_open(s_msc_driver->client_handle, device_address, &msc_device->handle) ); - MSC_GOTO_ON_ERROR( usb_host_get_active_config_descriptor(msc_device->handle, &config_desc) ); - MSC_GOTO_ON_ERROR( extract_config_from_descriptor(config_desc, &msc_device->config) ); - MSC_GOTO_ON_ERROR( usb_host_transfer_alloc(transfer_size, 0, &msc_device->xfer) ); - MSC_GOTO_ON_ERROR( usb_host_interface_claim(s_msc_driver->client_handle, - msc_device->handle, - msc_device->config.iface_num, 0) ); - - MSC_GOTO_ON_ERROR( msc_get_max_lun(msc_device, &lun) ); - MSC_GOTO_ON_ERROR( scsi_cmd_inquiry(msc_device) ); - MSC_GOTO_ON_ERROR( msc_wait_for_ready_state(msc_device, WAIT_FOR_READY_TIMEOUT_MS) ); - MSC_GOTO_ON_ERROR( scsi_cmd_read_capacity(msc_device, &block_size, &block_count) ); - - // Configuration descriptor size of simple MSC device is 32 bytes. - if (config_desc->wTotalLength != 32) { - ESP_LOGE(TAG, "COMPOSITE DEVICES UNSUPPORTED"); - } - - msc_device->disk.block_size = block_size; - msc_device->disk.block_count = block_count; - - if (block_size > transfer_size) { - usb_transfer_t *larger_xfer; - MSC_GOTO_ON_ERROR( usb_host_transfer_alloc(block_size, 0, &larger_xfer) ); - usb_host_transfer_free(msc_device->xfer); - msc_device->xfer = larger_xfer; - } - - *msc_device_handle = msc_device; - - return ESP_OK; - -fail: - msc_deinit_device(msc_device, true); - return ret; -} - -esp_err_t msc_host_uninstall_device(msc_host_device_handle_t device) -{ - MSC_RETURN_ON_INVALID_ARG(device); - return msc_deinit_device((msc_device_t *)device, false); -} - - -esp_err_t msc_host_read_sector(msc_host_device_handle_t device, size_t sector, void *data, size_t size) -{ - MSC_RETURN_ON_INVALID_ARG(device); - msc_device_t *dev = (msc_device_t *)device; - - return scsi_cmd_read10(dev, data, sector, 1, dev->disk.block_size); -} - -esp_err_t msc_host_write_sector(msc_host_device_handle_t device, size_t sector, const void *data, size_t size) -{ - MSC_RETURN_ON_INVALID_ARG(device); - msc_device_t *dev = (msc_device_t *)device; - - return scsi_cmd_write10(dev, data, sector, 1, dev->disk.block_size); -} - -esp_err_t msc_host_handle_events(uint32_t timeout_ms) -{ - MSC_RETURN_ON_FALSE(s_msc_driver != NULL, ESP_ERR_INVALID_STATE); - - return usb_host_client_handle_events(s_msc_driver->client_handle, timeout_ms); -} - -static esp_err_t msc_read_string_desc(msc_device_t *dev, uint8_t index, wchar_t *str) -{ - if (index == 0) { - // String descriptor not available - str[0] = 0; - return ESP_OK; - } - - usb_transfer_t *xfer = dev->xfer; - USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)xfer->data_buffer, index, 0x409, 64); - MSC_RETURN_ON_ERROR( msc_control_transfer(dev, xfer, USB_SETUP_PACKET_SIZE + 64) ); - - usb_standard_desc_t *desc = (usb_standard_desc_t *)(xfer->data_buffer + USB_SETUP_PACKET_SIZE); - wchar_t *data = (wchar_t *)(xfer->data_buffer + USB_SETUP_PACKET_SIZE + 2); - size_t len = MIN((desc->bLength - USB_STANDARD_DESC_SIZE) / 2, MSC_STR_DESC_SIZE - 1); - - wcsncpy(str, data, len); - str[len] = 0; - - return ESP_OK; -} - -esp_err_t msc_host_get_device_info(msc_host_device_handle_t device, msc_host_device_info_t *info) -{ - MSC_RETURN_ON_INVALID_ARG(device); - MSC_RETURN_ON_INVALID_ARG(info); - - msc_device_t *dev = (msc_device_t *)device; - const usb_device_desc_t *desc; - - MSC_RETURN_ON_ERROR( usb_host_get_device_descriptor(dev->handle, &desc) ); - - info->idProduct = desc->idProduct; - info->idVendor = desc->idVendor; - info->sector_size = dev->disk.block_size; - info->sector_count = dev->disk.block_count; - - MSC_RETURN_ON_ERROR( msc_read_string_desc(dev, desc->iManufacturer, info->iManufacturer) ); - MSC_RETURN_ON_ERROR( msc_read_string_desc(dev, desc->iProduct, info->iProduct) ); - MSC_RETURN_ON_ERROR( msc_read_string_desc(dev, desc->iSerialNumber, info->iSerialNumber) ); - - return ESP_OK; -} - -esp_err_t msc_host_print_descriptors(msc_host_device_handle_t device) -{ - msc_device_t *dev = (msc_device_t *)device; - const usb_device_desc_t *device_desc; - const usb_config_desc_t *config_desc; - MSC_RETURN_ON_ERROR( usb_host_get_device_descriptor(dev->handle, &device_desc) ); - MSC_RETURN_ON_ERROR( usb_host_get_active_config_descriptor(dev->handle, &config_desc) ); - usb_print_device_descriptor(device_desc); - usb_print_config_descriptor(config_desc, NULL); - return ESP_OK; -} - -static void transfer_callback(usb_transfer_t *transfer) -{ - msc_device_t *device = (msc_device_t *)transfer->context; - - if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) { - ESP_LOGE("Transfer failed", "Status %d", transfer->status); - } - - device->transfer_status = transfer->status; - xSemaphoreGive(device->transfer_done); -} - -static esp_err_t wait_for_transfer_done(usb_transfer_t *xfer) -{ - msc_device_t *device = (msc_device_t *)xfer->context; - BaseType_t received = xSemaphoreTake(device->transfer_done, pdMS_TO_TICKS(xfer->timeout_ms)); - - if (received != pdTRUE) { - usb_host_endpoint_halt(xfer->device_handle, xfer->bEndpointAddress); - usb_host_endpoint_flush(xfer->device_handle, xfer->bEndpointAddress); - xSemaphoreTake(device->transfer_done, portMAX_DELAY); - return ESP_ERR_TIMEOUT; - } - - return (device->transfer_status == USB_TRANSFER_STATUS_COMPLETED) ? ESP_OK : ESP_FAIL; -} - -static inline bool is_in_endpoint(uint8_t endpoint) -{ - return endpoint & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK ? true : false; -} - -esp_err_t msc_bulk_transfer(msc_device_t *device, uint8_t *data, size_t size, msc_endpoint_t ep) -{ - usb_transfer_t *xfer = device->xfer; - MSC_RETURN_ON_FALSE(size <= xfer->data_buffer_size, ESP_ERR_INVALID_SIZE); - uint8_t endpoint = (ep == MSC_EP_IN) ? device->config.bulk_in_ep : device->config.bulk_out_ep; - - if (is_in_endpoint(endpoint)) { - xfer->num_bytes = usb_round_up_to_mps(size, device->config.bulk_in_mps); - } else { - memcpy(xfer->data_buffer, data, size); - xfer->num_bytes = size; - } - - xfer->device_handle = device->handle; - xfer->bEndpointAddress = endpoint; - xfer->callback = transfer_callback; - xfer->timeout_ms = 1000; - xfer->context = device; - - MSC_RETURN_ON_ERROR( usb_host_transfer_submit(xfer) ); - MSC_RETURN_ON_ERROR( wait_for_transfer_done(xfer) ); - - if (is_in_endpoint(endpoint)) { - memcpy(data, xfer->data_buffer, size); - } - - return ESP_OK; -} - -esp_err_t msc_control_transfer(msc_device_t *device, usb_transfer_t *xfer, size_t len) -{ - xfer->device_handle = device->handle; - xfer->bEndpointAddress = 0; - xfer->callback = transfer_callback; - xfer->timeout_ms = 1000; - xfer->num_bytes = len; - xfer->context = device; - - MSC_RETURN_ON_ERROR( usb_host_transfer_submit_control(s_msc_driver->client_handle, xfer)); - return wait_for_transfer_done(xfer); -} diff --git a/examples/peripherals/usb/host/msc/components/msc/src/msc_host_vfs.c b/examples/peripherals/usb/host/msc/components/msc/src/msc_host_vfs.c deleted file mode 100644 index 21638635ed..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/src/msc_host_vfs.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include "msc_common.h" -#include "msc_host_vfs.h" -#include "diskio_impl.h" -#include "ffconf.h" -#include "ff.h" - -#define DRIVE_STR_LEN 3 - -typedef struct msc_host_vfs { - char drive[DRIVE_STR_LEN]; - char *base_path; - uint8_t pdrv; -} msc_host_vfs_t; - -static const char *TAG = "MSC VFS"; - -static esp_err_t msc_format_storage(size_t block_size, size_t allocation_size, const char *drv) -{ - void *workbuf = NULL; - const size_t workbuf_size = 4096; - - MSC_RETURN_ON_FALSE( workbuf = ff_memalloc(workbuf_size), ESP_ERR_NO_MEM ); - - // Valid value of cluster size is between sector_size and 128 * sector_size. - size_t cluster_size = MIN(MAX(allocation_size, block_size), 128 * block_size); - - FRESULT err = f_mkfs(drv, FM_ANY | FM_SFD, cluster_size, workbuf, workbuf_size); - if (err) { - ESP_LOGE(TAG, "Formatting failed with error: %d", err); - free(workbuf); - return ESP_ERR_MSC_FORMAT_FAILED; - } - - free(workbuf); - return ESP_OK; -} - -static void dealloc_msc_vfs(msc_host_vfs_t *vfs) -{ - free(vfs->base_path); - free(vfs); -} - -esp_err_t msc_host_vfs_register(msc_host_device_handle_t device, - const char *base_path, - const esp_vfs_fat_mount_config_t *mount_config, - msc_host_vfs_handle_t *vfs_handle) -{ - MSC_RETURN_ON_INVALID_ARG(device); - MSC_RETURN_ON_INVALID_ARG(base_path); - MSC_RETURN_ON_INVALID_ARG(mount_config); - MSC_RETURN_ON_INVALID_ARG(vfs_handle); - - FATFS *fs = NULL; - BYTE pdrv; - bool diskio_registered = false; - esp_err_t ret = ESP_ERR_MSC_MOUNT_FAILED; - msc_device_t *dev = (msc_device_t *)device; - size_t block_size = dev->disk.block_size; - size_t alloc_size = mount_config->allocation_unit_size; - - msc_host_vfs_t *vfs = calloc(1, sizeof(msc_host_vfs_t)); - MSC_RETURN_ON_FALSE(vfs != NULL, ESP_ERR_NO_MEM); - - MSC_GOTO_ON_ERROR( ff_diskio_get_drive(&pdrv) ); - - ff_diskio_register_msc(pdrv, &dev->disk); - char drive[DRIVE_STR_LEN] = {(char)('0' + pdrv), ':', 0}; - diskio_registered = true; - - strncpy(vfs->drive, drive, DRIVE_STR_LEN); - MSC_GOTO_ON_FALSE( vfs->base_path = strdup(base_path), ESP_ERR_NO_MEM ); - vfs->pdrv = pdrv; - - MSC_GOTO_ON_ERROR( esp_vfs_fat_register(base_path, drive, mount_config->max_files, &fs) ); - - FRESULT fresult = f_mount(fs, drive, 1); - - if ( fresult != FR_OK) { - if (mount_config->format_if_mount_failed && - (fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR)) { - MSC_GOTO_ON_ERROR( msc_format_storage(block_size, alloc_size, drive) ); - MSC_GOTO_ON_FALSE( f_mount(fs, drive, 0) == FR_OK, ESP_ERR_MSC_MOUNT_FAILED ); - } else { - goto fail; - } - } - - *vfs_handle = vfs; - return ESP_OK; - -fail: - if (diskio_registered) { - ff_diskio_unregister(pdrv); - } - esp_vfs_fat_unregister_path(base_path); - if(fs) { - f_mount(NULL, drive, 0); - } - dealloc_msc_vfs(vfs); - return ret; -} - -esp_err_t msc_host_vfs_unregister(msc_host_vfs_handle_t vfs_handle) -{ - MSC_RETURN_ON_INVALID_ARG(vfs_handle); - msc_host_vfs_t *vfs = (msc_host_vfs_t *)vfs_handle; - - f_mount(NULL, vfs->drive, 0); - ff_diskio_unregister(vfs->pdrv); - esp_vfs_fat_unregister_path(vfs->base_path); - dealloc_msc_vfs(vfs); - return ESP_OK; -} diff --git a/examples/peripherals/usb/host/msc/components/msc/src/msc_scsi_bot.c b/examples/peripherals/usb/host/msc/components/msc/src/msc_scsi_bot.c deleted file mode 100644 index 2e548f0b9e..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/src/msc_scsi_bot.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "esp_log.h" -#include -#include -#include -#include -#include "esp_check.h" -#include "esp_log.h" -#include "msc_common.h" -#include "msc_scsi_bot.h" - -#define TAG "USB_MSC_SCSI" - -/* --------------------------- SCSI Definitions ----------------------------- */ -#define CMD_SENSE_VALID_BIT (1 << 7) -#define SCSI_FLAG_DPO (1<<4) -#define SCSI_FLAG_FUA (1<<3) - -#define SCSI_CMD_FORMAT_UNIT 0x04 -#define SCSI_CMD_INQUIRY 0x12 -#define SCSI_CMD_MODE_SELECT 0x55 -#define SCSI_CMD_MODE_SENSE 0x5A -#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E -#define SCSI_CMD_READ10 0x28 -#define SCSI_CMD_READ12 0xA8 -#define SCSI_CMD_READ_CAPACITY 0x25 -#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23 -#define SCSI_CMD_REQUEST_SENSE 0x03 -#define SCSI_CMD_REZERO 0x01 -#define SCSI_CMD_SEEK10 0x2B -#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D -#define SCSI_CMD_START_STOP Unit 0x1B -#define SCSI_CMD_TEST_UNIT_READY 0x00 -#define SCSI_CMD_VERIFY 0x2F -#define SCSI_CMD_WRITE10 0x2A -#define SCSI_CMD_WRITE12 0xAA -#define SCSI_CMD_WRITE_AND_VERIFY 0x2E - -#define IN_DIR CWB_FLAG_DIRECTION_IN -#define OUT_DIR 0 - -#define INQUIRY_VID_SIZE 8 -#define INQUIRY_PID_SIZE 16 -#define INQUIRY_REV_SIZE 4 - -#define CBW_CMD_SIZE(cmd) (sizeof(cmd) - sizeof(msc_cbw_t)) - -#define CBW_BASE_INIT(dir, cbw_len, data_len) \ - .base = { \ - .signature = 0x43425355, \ - .tag = ++cbw_tag, \ - .flags = dir, \ - .lun = 0, \ - .data_length = data_len, \ - .cbw_length = cbw_len, \ - } - -#define FEATURE_SELECTOR_ENDPOINT 0 -#define CSW_SIGNATURE 0x53425355 -#define CBW_SIZE 31 - -#define USB_MASS_REQ_INIT_RESET(ctrl_req_ptr, intf_num) ({ \ - (ctrl_req_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | \ - USB_BM_REQUEST_TYPE_TYPE_CLASS | \ - USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ - (ctrl_req_ptr)->bRequest = 0xFF; \ - (ctrl_req_ptr)->wValue = 0; \ - (ctrl_req_ptr)->wIndex = (intf_num); \ - (ctrl_req_ptr)->wLength = 0; \ -}) - -#define USB_MASS_REQ_INIT_GET_MAX_LUN(ctrl_req_ptr, intf_num) ({ \ - (ctrl_req_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | \ - USB_BM_REQUEST_TYPE_TYPE_CLASS | \ - USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ - (ctrl_req_ptr)->bRequest = 0xFE; \ - (ctrl_req_ptr)->wValue = 0; \ - (ctrl_req_ptr)->wIndex = (intf_num); \ - (ctrl_req_ptr)->wLength = 1; \ -}) - -#define USB_SETUP_PACKET_INIT_CLEAR_FEATURE_EP(ctrl_req_ptr, ep_num) ({ \ - (ctrl_req_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | \ - USB_BM_REQUEST_TYPE_TYPE_STANDARD | \ - USB_BM_REQUEST_TYPE_RECIP_ENDPOINT; \ - (ctrl_req_ptr)->bRequest = USB_B_REQUEST_CLEAR_FEATURE; \ - (ctrl_req_ptr)->wValue = FEATURE_SELECTOR_ENDPOINT; \ - (ctrl_req_ptr)->wIndex = (ep_num); \ - (ctrl_req_ptr)->wLength = 0; \ -}) - -#define CWB_FLAG_DIRECTION_IN (1<<7) // device -> host - -/** - * @brief Command Block Wrapper structure - */ -typedef struct __attribute__((packed)) -{ - uint32_t signature; - uint32_t tag; - uint32_t data_length; - uint8_t flags; - uint8_t lun; - uint8_t cbw_length; -} msc_cbw_t; - -/** - * @brief Command Status Wrapper structure - */ -typedef struct __attribute__((packed)) -{ - uint32_t signature; - uint32_t tag; - uint32_t dataResidue; - uint8_t status; -} msc_csw_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint32_t address; - uint8_t reserved1; - uint16_t length; - uint8_t reserved2[3]; -} cbw_read10_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint32_t address; - uint8_t reserved1; - uint16_t length; - uint8_t reserved2[1]; -} cbw_write10_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint32_t address; - uint8_t reserved[6]; -} cbw_read_capacity_t; - -typedef struct __attribute__((packed)) -{ - uint32_t block_count; - uint32_t block_size; -} cbw_read_capacity_response_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint8_t reserved[10]; -} cbw_unit_ready_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint8_t reserved_0[2]; - uint8_t allocation_length; - uint8_t reserved_1[7]; -} cbw_sense_t; - -typedef struct __attribute__((packed)) -{ - uint8_t error_code; - uint8_t reserved_0; - uint8_t sense_key; - uint32_t info; - uint8_t sense_len; - uint32_t reserved_1; - uint8_t sense_code; - uint8_t sense_code_qualifier; - uint32_t reserved_2; -} cbw_sense_response_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint8_t page_code; - uint8_t reserved_0; - uint8_t allocation_length; - uint8_t reserved_1[7]; -} cbw_inquiry_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint8_t pc_page_code; - uint8_t reserved_1[4]; - uint16_t parameter_list_length; - uint8_t reserved_2[3]; -} mode_sense_t; - -typedef struct __attribute__((packed)) -{ - uint8_t data[8]; -} mode_sense_response_t; - -typedef struct __attribute__((packed)) -{ - msc_cbw_t base; - uint8_t opcode; - uint8_t flags; - uint8_t reserved_1[2]; - uint8_t prevent; - uint8_t reserved_2[7]; -} prevent_allow_medium_removal_t; - -typedef struct __attribute__((packed)) -{ - uint8_t data[36]; -} cbw_inquiry_response_t; - -// Unique number based on which MSC protocol pairs request and response -static uint32_t cbw_tag; - -static esp_err_t check_csw(msc_csw_t *csw, uint32_t tag) -{ - bool csw_ok = csw->signature == CSW_SIGNATURE && csw->tag == tag && - csw->dataResidue == 0 && csw->status == 0; - - if (!csw_ok) { - ESP_LOGD(TAG, "CSW failed: status %d", csw->status); - } - - return csw_ok ? ESP_OK : ESP_FAIL; -} - -static esp_err_t clear_feature(msc_device_t *device, uint8_t endpoint) -{ - usb_device_handle_t dev = device->handle; - usb_transfer_t *xfer = device->xfer; - - MSC_RETURN_ON_ERROR( usb_host_endpoint_clear(dev, endpoint) ); - USB_SETUP_PACKET_INIT_CLEAR_FEATURE_EP((usb_setup_packet_t *)xfer->data_buffer, endpoint); - MSC_RETURN_ON_ERROR( msc_control_transfer(device, xfer, USB_SETUP_PACKET_SIZE) ); - - return ESP_OK; -} - -esp_err_t msc_mass_reset(msc_device_t *device) -{ - usb_transfer_t *xfer = device->xfer; - - USB_MASS_REQ_INIT_RESET((usb_setup_packet_t *)xfer->data_buffer, 0); - MSC_RETURN_ON_ERROR( msc_control_transfer(device, xfer, USB_SETUP_PACKET_SIZE) ); - - return ESP_OK; -} - -esp_err_t msc_get_max_lun(msc_device_t *device, uint8_t *lun) -{ - usb_transfer_t *xfer = device->xfer; - - USB_MASS_REQ_INIT_GET_MAX_LUN((usb_setup_packet_t *)xfer->data_buffer, 0); - MSC_RETURN_ON_ERROR( msc_control_transfer(device, xfer, USB_SETUP_PACKET_SIZE + 1) ); - - *lun = xfer->data_buffer[USB_SETUP_PACKET_SIZE]; - - return ESP_OK; -} - -static esp_err_t bot_execute_command(msc_device_t *device, msc_cbw_t *cbw, void *data, size_t size) -{ - msc_csw_t csw; - msc_endpoint_t ep = (cbw->flags & CWB_FLAG_DIRECTION_IN) ? MSC_EP_IN : MSC_EP_OUT; - - MSC_RETURN_ON_ERROR( msc_bulk_transfer(device, (uint8_t *)cbw, CBW_SIZE, MSC_EP_OUT) ); - - if (data) { - MSC_RETURN_ON_ERROR( msc_bulk_transfer(device, (uint8_t *)data, size, ep) ); - } - - esp_err_t err = msc_bulk_transfer(device, (uint8_t *)&csw, sizeof(msc_csw_t), MSC_EP_IN); - - if (err == ESP_FAIL && device->transfer_status == USB_TRANSFER_STATUS_STALL) { - ESP_RETURN_ON_ERROR( clear_feature(device, MSC_EP_IN), TAG, "Clear feature failed" ); - // Try to read csw again after clearing feature - err = msc_bulk_transfer(device, (uint8_t *)&csw, sizeof(msc_csw_t), MSC_EP_IN); - if (err) { - ESP_RETURN_ON_ERROR( clear_feature(device, MSC_EP_IN), TAG, "Clear feature failed" ); - ESP_RETURN_ON_ERROR( msc_mass_reset(device), TAG, "Mass reset failed" ); - return ESP_FAIL; - } - } - - MSC_RETURN_ON_ERROR(err); - - return check_csw(&csw, cbw->tag); -} - - -esp_err_t scsi_cmd_read10(msc_device_t *device, - uint8_t *data, - uint32_t sector_address, - uint32_t num_sectors, - uint32_t sector_size) -{ - cbw_read10_t cbw = { - CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_read10_t), num_sectors * sector_size), - .opcode = SCSI_CMD_READ10, - .flags = 0, // lun - .address = __builtin_bswap32(sector_address), - .length = __builtin_bswap16(num_sectors), - }; - - return bot_execute_command(device, &cbw.base, data, num_sectors * sector_size); -} - -esp_err_t scsi_cmd_write10(msc_device_t *device, - const uint8_t *data, - uint32_t sector_address, - uint32_t num_sectors, - uint32_t sector_size) -{ - cbw_write10_t cbw = { - CBW_BASE_INIT(OUT_DIR, CBW_CMD_SIZE(cbw_write10_t), num_sectors * sector_size), - .opcode = SCSI_CMD_WRITE10, - .address = __builtin_bswap32(sector_address), - .length = __builtin_bswap16(num_sectors), - }; - - return bot_execute_command(device, &cbw.base, (void *)data, num_sectors * sector_size); -} - -esp_err_t scsi_cmd_read_capacity(msc_device_t *device, uint32_t *block_size, uint32_t *block_count) -{ - cbw_read_capacity_response_t response; - - cbw_read_capacity_t cbw = { - CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_read_capacity_t), sizeof(response)), - .opcode = SCSI_CMD_READ_CAPACITY, - }; - - MSC_RETURN_ON_ERROR( bot_execute_command(device, &cbw.base, &response, sizeof(response)) ); - - *block_count = __builtin_bswap32(response.block_count); - *block_size = __builtin_bswap32(response.block_size); - - return ESP_OK; -} - -esp_err_t scsi_cmd_unit_ready(msc_device_t *device) -{ - cbw_unit_ready_t cbw = { - CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_unit_ready_t), 0), - .opcode = SCSI_CMD_TEST_UNIT_READY, - }; - - return bot_execute_command(device, &cbw.base, NULL, 0); -} - -esp_err_t scsi_cmd_sense(msc_device_t *device, scsi_sense_data_t *sense) -{ - cbw_sense_response_t response; - - cbw_sense_t cbw = { - CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_sense_t), sizeof(response)), - .opcode = SCSI_CMD_REQUEST_SENSE, - .allocation_length = sizeof(response), - }; - - MSC_RETURN_ON_ERROR( bot_execute_command(device, &cbw.base, &response, sizeof(response)) ); - - if (sense->key) { - ESP_LOGD(TAG, "sense_key: 0x%02X, code: 0x%02X, qualifier: 0x%02X", - response.sense_key, response.sense_code, response.sense_code_qualifier); - } - - sense->key = response.sense_key; - sense->code = response.sense_code; - sense->code_q = response.sense_code_qualifier; - - return ESP_OK; -} - -esp_err_t scsi_cmd_inquiry(msc_device_t *device) -{ - cbw_inquiry_response_t response = { 0 }; - - cbw_inquiry_t cbw = { - CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(cbw_inquiry_t), sizeof(response)), - .opcode = SCSI_CMD_INQUIRY, - .allocation_length = sizeof(response), - }; - - return bot_execute_command(device, &cbw.base, &response, sizeof(response) ); -} - -esp_err_t scsi_cmd_mode_sense(msc_device_t *device) -{ - mode_sense_response_t response = { 0 }; - - mode_sense_t cbw = { - CBW_BASE_INIT(IN_DIR, CBW_CMD_SIZE(mode_sense_t), sizeof(response)), - .opcode = SCSI_CMD_MODE_SENSE, - .pc_page_code = 0x3F, - .parameter_list_length = sizeof(response), - }; - - return bot_execute_command(device, &cbw.base, &response, sizeof(response) ); -} - -esp_err_t scsi_cmd_prevent_removal(msc_device_t *device, bool prevent) -{ - prevent_allow_medium_removal_t cbw = { - CBW_BASE_INIT(OUT_DIR, CBW_CMD_SIZE(prevent_allow_medium_removal_t), 0), - .opcode = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL, - .prevent = 1, - }; - - return bot_execute_command(device, &cbw.base, NULL, 0); -} diff --git a/examples/peripherals/usb/host/msc/components/msc/test/CMakeLists.txt b/examples/peripherals/usb/host/msc/components/msc/test/CMakeLists.txt deleted file mode 100644 index 829f046e33..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/test/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRC_DIRS . - INCLUDE_DIRS . - REQUIRES unity usb msc tinyusb) diff --git a/examples/peripherals/usb/host/msc/components/msc/test/msc_device.c b/examples/peripherals/usb/host/msc/components/msc/test/msc_device.c deleted file mode 100644 index 6f244287d3..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/test/msc_device.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org) - * - * SPDX-License-Identifier: MIT - * - * SPDX-FileContributor: 2019-2021 Espressif Systems (Shanghai) CO LTD - * - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * 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. - * - */ - -#include -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "tinyusb.h" -#include "test_common.h" -#include "soc/soc_caps.h" - -#if SOC_USB_OTG_SUPPORTED - -#define MASS_STORAGE_CLASS 0x08 -#define SCSI_COMMAND_SET 0x06 -#define BULK_ONLY_TRANSFER 0x50 - -static const char *TAG = "msc_example"; - - -/**** Kconfig driven Descriptor ****/ -tusb_desc_device_t device_descriptor = { - .bLength = sizeof(device_descriptor), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = MASS_STORAGE_CLASS, - .bDeviceSubClass = SCSI_COMMAND_SET, - .bDeviceProtocol = BULK_ONLY_TRANSFER, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = USB_ESPRESSIF_VID, - .idProduct = 0x1234, - .bcdDevice = 0x0100, - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - .bNumConfigurations = 0x01 -}; - -void device_app(void) -{ - ESP_LOGI(TAG, "USB initialization"); - - tinyusb_config_t tusb_cfg = { - .descriptor = &device_descriptor - }; - - ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); - ESP_LOGI(TAG, "USB initialization DONE"); - - while (1) { - vTaskDelay(100); - } -} - - -// whether host does safe-eject -static bool ejected = false; - -// Some MCU doesn't have enough 8KB SRAM to store the whole disk -// We will use Flash as read-only disk with board that has -// CFG_EXAMPLE_MSC_READONLY defined - -uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = { - //------------- Block0: Boot Sector -------------// - // byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM; - // sector_per_cluster = 1; reserved_sectors = 1; - // fat_num = 1; fat12_root_entry_num = 16; - // sector_per_fat = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0; - // drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29; - // filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC"; - // FAT magic code at offset 510-511 - { - 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U', - 'S', 'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, - - // Zero up to 2 last bytes of FAT magic code - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA - }, - - //------------- Block1: FAT12 Table -------------// - { - 0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file - }, - - //------------- Block2: Root Directory -------------// - { - // first entry is volume label - 'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // second entry is readme file - 'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6, 0x52, 0x6D, - 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00, - sizeof(README_CONTENTS) - 1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes) - }, - - //------------- Block3: Readme Content -------------// - README_CONTENTS -}; - -// Invoked when received SCSI_CMD_INQUIRY -// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively -void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) -{ - (void) lun; - - const char vid[] = "TinyUSB"; - const char pid[] = "Mass Storage"; - const char rev[] = "1.0"; - - memcpy(vendor_id, vid, strlen(vid)); - memcpy(product_id, pid, strlen(pid)); - memcpy(product_rev, rev, strlen(rev)); -} - -// Invoked when received Test Unit Ready command. -// return true allowing host to read/write this LUN e.g SD card inserted -bool tud_msc_test_unit_ready_cb(uint8_t lun) -{ - (void) lun; - - // RAM disk is ready until ejected - if (ejected) { - tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); - return false; - } - - return true; -} - -// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size -// Application update block count and block size -void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) -{ - (void) lun; - - *block_count = DISK_BLOCK_NUM; - *block_size = DISK_BLOCK_SIZE; -} - -// Invoked when received Start Stop Unit command -// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage -// - Start = 1 : active mode, if load_eject = 1 : load disk storage -bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) -{ - (void) lun; - (void) power_condition; - - if ( load_eject ) { - if (start) { - // load disk storage - } else { - // unload disk storage - ejected = true; - } - } - - return true; -} - -// Callback invoked when received READ10 command. -// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. -int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) -{ - (void) lun; - - uint8_t const *addr = msc_disk[lba] + offset; - memcpy(buffer, addr, bufsize); - - return bufsize; -} - -// Callback invoked when received WRITE10 command. -// Process data in buffer to disk's storage and return number of written bytes -int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) -{ - (void) lun; - -#ifndef CFG_EXAMPLE_MSC_READONLY - uint8_t *addr = msc_disk[lba] + offset; - memcpy(addr, buffer, bufsize); -#else - (void) lba; (void) offset; (void) buffer; -#endif - - return bufsize; -} - -// Callback invoked when received an SCSI command not in built-in list below -// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE -// - READ10 and WRITE10 has their own callbacks -int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) -{ - // read10 & write10 has their own callback and MUST not be handled here - - void const *response = NULL; - uint16_t resplen = 0; - - // most scsi handled is input - bool in_xfer = true; - - switch (scsi_cmd[0]) { - case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: - // Host is about to read/write etc ... better not to disconnect disk - resplen = 0; - break; - - default: - // Set Sense = Invalid Command Operation - tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); - - // negative means error -> tinyusb could stall and/or response with failed status - resplen = -1; - break; - } - - // return resplen must not larger than bufsize - if ( resplen > bufsize ) { - resplen = bufsize; - } - - if ( response && (resplen > 0) ) { - if (in_xfer) { - memcpy(buffer, response, resplen); - } else { - // SCSI output - } - } - - return resplen; -} - -#endif diff --git a/examples/peripherals/usb/host/msc/components/msc/test/test_common.h b/examples/peripherals/usb/host/msc/components/msc/test/test_common.h deleted file mode 100644 index b9acfaa9c9..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/test/test_common.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#pragma once - -enum { - // FatFS only allows to format disks with number of blocks greater than 128 - DISK_BLOCK_NUM = 128 + 1, - DISK_BLOCK_SIZE = 512 -}; - -#define README_CONTENTS \ -"This is tinyusb's MassStorage Class demo.\r\n\r\n\ -If you find any bugs or get any questions, feel free to file an\r\n\ -issue at github.com/hathach/tinyusb" - -void device_app(void); diff --git a/examples/peripherals/usb/host/msc/components/msc/test/test_msc.c b/examples/peripherals/usb/host/msc/components/msc/test/test_msc.c deleted file mode 100644 index 58adb6369c..0000000000 --- a/examples/peripherals/usb/host/msc/components/msc/test/test_msc.c +++ /dev/null @@ -1,321 +0,0 @@ - -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "unity.h" -#include -#include -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "esp_private/usb_phy.h" -#include "usb/usb_host.h" -#include "msc_host.h" -#include "msc_host_vfs.h" -#include "ffconf.h" -#include "ff.h" -#include "esp_vfs.h" -#include "test_common.h" -#include "soc/usb_wrap_struct.h" -#include "soc/soc_caps.h" - -#if SOC_USB_OTG_SUPPORTED - -static const char *TAG = "APP"; - -#define ESP_OK_ASSERT(exp) TEST_ASSERT_EQUAL(ESP_OK, exp) - -static esp_vfs_fat_mount_config_t mount_config = { - .format_if_mount_failed = false, - .max_files = 3, - .allocation_unit_size = 1024, -}; - -static QueueHandle_t app_queue; -static SemaphoreHandle_t ready_to_deinit_usb; -static msc_host_device_handle_t device; -static msc_host_vfs_handle_t vfs_handle; -static volatile bool waiting_for_sudden_disconnect; -static usb_phy_handle_t phy_hdl = NULL; - -static void force_conn_state(bool connected, TickType_t delay_ticks) -{ - TEST_ASSERT_NOT_EQUAL(NULL, phy_hdl); - if (delay_ticks > 0) { - //Delay of 0 ticks causes a yield. So skip if delay_ticks is 0. - vTaskDelay(delay_ticks); - } - ESP_ERROR_CHECK(usb_phy_action(phy_hdl, (connected) ? USB_PHY_ACTION_HOST_ALLOW_CONN : USB_PHY_ACTION_HOST_FORCE_DISCONN)); -} - -static void msc_event_cb(const msc_host_event_t *event, void *arg) -{ - if (waiting_for_sudden_disconnect) { - waiting_for_sudden_disconnect = false; - TEST_ASSERT(event->event == MSC_DEVICE_DISCONNECTED); - } - - if (event->event == MSC_DEVICE_CONNECTED) { - printf("MSC_DEVICE_CONNECTED\n"); - } else { - printf("MSC_DEVICE_DISCONNECTED\n"); - } - - xQueueSend(app_queue, event, 10); -} - -static const char *TEST_STRING = "Hello World!"; -static const char *FILE_NAME = "/usb/ESP32.txt"; - -static void write_read_file(const char *file_path) -{ - char line[64]; - - ESP_LOGI(TAG, "Writing file"); - FILE *f = fopen(file_path, "w"); - TEST_ASSERT( f != NULL); - fprintf(f, TEST_STRING); - fclose(f); - - ESP_LOGI(TAG, "Reading file"); - TEST_ASSERT( fopen(file_path, "r") != NULL); - fgets(line, sizeof(line), f); - fclose(f); - // strip newline - char *pos = strchr(line, '\n'); - if (pos) { - *pos = '\0'; - } - TEST_ASSERT_EQUAL_STRING(line, TEST_STRING); - ESP_LOGI(TAG, "Done"); -} - -static bool file_exists(const char *file_path) -{ - return ( access(file_path, F_OK) == 0 ); -} - -// Handles common USB host library events -static void handle_usb_events(void *args) -{ - uint32_t end_flags = 0; - - while (1) { - uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - // Release devices once all clients has deregistered - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - printf("USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS\n"); - usb_host_device_free_all(); - end_flags |= 1; - } - // Give ready_to_deinit_usb semaphore to indicate that USB Host library - // can be deinitialized, and terminate this task. - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - printf("USB_HOST_LIB_EVENT_FLAGS_ALL_FREE\n"); - end_flags |= 2; - } - - if (end_flags == 3) { - xSemaphoreGive(ready_to_deinit_usb); - break; - } - } - vTaskDelete(NULL); -} - -static void check_file_content(const char *file_path, const char *expected) -{ - ESP_LOGI(TAG, "Reading %s:", file_path); - FILE *file = fopen(file_path, "r"); - TEST_ASSERT(file != NULL) - - char content[200]; - fread(content, 1, sizeof(content), file); - TEST_ASSERT_EQUAL_STRING(content, expected); - fclose(file); -} - -static void check_sudden_disconnect(void) -{ - uint8_t data[512]; - const size_t DATA_SIZE = sizeof(data); - - ESP_LOGI(TAG, "Creating test.tx"); - FILE *file = fopen("/usb/test.txt", "w"); - TEST_ASSERT( file != NULL); - - ESP_LOGI(TAG, "Write data"); - TEST_ASSERT( fwrite(data, 1, DATA_SIZE, file) == DATA_SIZE ); - TEST_ASSERT( fwrite(data, 1, DATA_SIZE, file) == DATA_SIZE ); - TEST_ASSERT( fflush(file) == 0 ); - - ESP_LOGI(TAG, "Trigger a disconnect"); - //Trigger a disconnect - waiting_for_sudden_disconnect = true; - force_conn_state(false, 0); - - // Make sure flag was leared in callback - vTaskDelay( pdMS_TO_TICKS(100) ); - TEST_ASSERT( waiting_for_sudden_disconnect == false ); - - ESP_LOGI(TAG, "Write data after disconnect"); - TEST_ASSERT( fwrite(data, 1, DATA_SIZE, file) != DATA_SIZE ); - - fclose(file); -} - -static void msc_setup(void) -{ - BaseType_t task_created; - - ready_to_deinit_usb = xSemaphoreCreateBinary(); - - TEST_ASSERT( app_queue = xQueueCreate(5, sizeof(msc_host_event_t)) ); - - //Initialize the internal USB PHY to connect to the USB OTG peripheral. We manually install the USB PHY for testing - usb_phy_config_t phy_config = { - .controller = USB_PHY_CTRL_OTG, - .target = USB_PHY_TARGET_INT, - .otg_mode = USB_OTG_MODE_HOST, - .otg_speed = USB_PHY_SPEED_UNDEFINED, //In Host mode, the speed is determined by the connected device - .gpio_conf = NULL, - }; - TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_hdl)); - const usb_host_config_t host_config = { - .skip_phy_setup = true, - .intr_flags = ESP_INTR_FLAG_LEVEL1, - }; - ESP_OK_ASSERT( usb_host_install(&host_config) ); - - task_created = xTaskCreate(handle_usb_events, "usb_events", 2048, NULL, 2, NULL); - TEST_ASSERT(task_created); - - const msc_host_driver_config_t msc_config = { - .create_backround_task = true, - .callback = msc_event_cb, - .stack_size = 4096, - .task_priority = 5, - }; - ESP_OK_ASSERT( msc_host_install(&msc_config) ); - - ESP_LOGI(TAG, "Waiting for USB stick to be connected"); - msc_host_event_t app_event; - xQueueReceive(app_queue, &app_event, portMAX_DELAY); - TEST_ASSERT( app_event.event == MSC_DEVICE_CONNECTED ); - uint8_t device_addr = app_event.device.address; - - ESP_OK_ASSERT( msc_host_install_device(device_addr, &device) ); - ESP_OK_ASSERT( msc_host_vfs_register(device, "/usb", &mount_config, &vfs_handle) ); -} - -static void msc_teardown(void) -{ - // Wait to finish any ongoing USB operations - vTaskDelay(100); - - ESP_OK_ASSERT( msc_host_vfs_unregister(vfs_handle) ); - ESP_OK_ASSERT( msc_host_uninstall_device(device) ); - ESP_OK_ASSERT( msc_host_uninstall() ); - - xSemaphoreTake(ready_to_deinit_usb, portMAX_DELAY); - vSemaphoreDelete(ready_to_deinit_usb); - ESP_OK_ASSERT( usb_host_uninstall() ); - //Tear down USB PHY - TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_hdl)); - phy_hdl = NULL; - - vQueueDelete(app_queue); -} - -static void write_read_sectors(void) -{ - uint8_t write_data[DISK_BLOCK_SIZE]; - uint8_t read_data[DISK_BLOCK_SIZE]; - - memset(write_data, 0x55, DISK_BLOCK_SIZE); - memset(read_data, 0, DISK_BLOCK_SIZE); - - msc_host_write_sector(device, 10, write_data, DISK_BLOCK_SIZE); - msc_host_read_sector(device, 10, read_data, DISK_BLOCK_SIZE); - - TEST_ASSERT_EQUAL_MEMORY(write_data, read_data, DISK_BLOCK_SIZE); -} - -static void erase_storage(void) -{ - uint8_t data[DISK_BLOCK_SIZE]; - memset(data, 0xFF, DISK_BLOCK_SIZE); - - for (int block = 0; block < DISK_BLOCK_NUM; block++) { - msc_host_write_sector(device, block, data, DISK_BLOCK_SIZE); - } -} - -static void check_readme_content(void) -{ - msc_setup(); - check_file_content("/usb/README.TXT", README_CONTENTS); - msc_teardown(); -} - -TEST_CASE("Write and read file", "[usb_msc][ignore]") -{ - msc_setup(); - write_read_file(FILE_NAME); - msc_teardown(); -} - -TEST_CASE("Sudden disconnect", "[usb_msc][ignore]") -{ - msc_setup(); - check_sudden_disconnect(); - msc_teardown(); -} - -void read_write_sectors(void) -{ - msc_setup(); - write_read_sectors(); - msc_teardown(); -} - -void check_formatting(void) -{ - printf("Create file\n"); - msc_setup(); - write_read_file(FILE_NAME); - msc_teardown(); - - printf("File exists after mounting again\n"); - msc_setup(); - TEST_ASSERT( file_exists(FILE_NAME) ); - printf("Erase storage device\n"); - erase_storage(); - msc_teardown(); - - printf("Check file does not exist after formatting\n"); - mount_config.format_if_mount_failed = true; - msc_setup(); - TEST_ASSERT( !file_exists(FILE_NAME) ); - msc_teardown(); - mount_config.format_if_mount_failed = false; -} - -TEST_CASE_MULTIPLE_DEVICES("Sectors can be written and read", "[usb_msc][ignore]", read_write_sectors, device_app); - -TEST_CASE_MULTIPLE_DEVICES("Can be Formated", "[usb_msc][ignore]", check_formatting, device_app); - -TEST_CASE_MULTIPLE_DEVICES("Check README content", "[usb_msc][ignore]", check_readme_content, device_app); - -#endif diff --git a/examples/peripherals/usb/host/msc/main/CMakeLists.txt b/examples/peripherals/usb/host/msc/main/CMakeLists.txt index 1503b83c6d..e4e3bca4b3 100644 --- a/examples/peripherals/usb/host/msc/main/CMakeLists.txt +++ b/examples/peripherals/usb/host/msc/main/CMakeLists.txt @@ -1,3 +1,3 @@ idf_component_register(SRCS "msc_example_main.c" INCLUDE_DIRS "" - REQUIRES usb msc fatfs) + REQUIRES usb vfs) diff --git a/examples/peripherals/usb/host/msc/main/idf_component.yml b/examples/peripherals/usb/host/msc/main/idf_component.yml new file mode 100644 index 0000000000..57f225551e --- /dev/null +++ b/examples/peripherals/usb/host/msc/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=4.4" + usb_host_msc: "^1.0.0" diff --git a/examples/peripherals/usb/host/msc/main/msc_example_main.c b/examples/peripherals/usb/host/msc/main/msc_example_main.c index 4a3d7371dd..4f7bd1c874 100644 --- a/examples/peripherals/usb/host/msc/main/msc_example_main.c +++ b/examples/peripherals/usb/host/msc/main/msc_example_main.c @@ -1,41 +1,53 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * - * SPDX-License-Identifier: Apache-2.0 + * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ -#include #include #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" +#include "freertos/event_groups.h" #include "esp_err.h" #include "esp_log.h" #include "usb/usb_host.h" #include "msc_host.h" #include "msc_host_vfs.h" #include "ffconf.h" -#include "ff.h" #include "esp_vfs.h" #include "errno.h" #include "hal/usb_hal.h" +#include "driver/gpio.h" +#include -static const char* TAG = "example"; +#define USB_DISCONNECT_PIN GPIO_NUM_10 -static QueueHandle_t app_queue; -static SemaphoreHandle_t ready_to_uninstall_usb; +#define READY_TO_UNINSTALL (HOST_NO_CLIENT | HOST_ALL_FREE) + +typedef enum { + HOST_NO_CLIENT = 0x1, + HOST_ALL_FREE = 0x2, + DEVICE_CONNECTED = 0x4, + DEVICE_DISCONNECTED = 0x8, + DEVICE_ADDRESS_MASK = 0xFF0, +} app_event_t; + +static const char *TAG = "example"; +static EventGroupHandle_t usb_flags; static void msc_event_cb(const msc_host_event_t *event, void *arg) { if (event->event == MSC_DEVICE_CONNECTED) { ESP_LOGI(TAG, "MSC device connected"); + // Obtained USB device address is placed after application events + xEventGroupSetBits(usb_flags, DEVICE_CONNECTED | (event->device.address << 4)); } else if (event->event == MSC_DEVICE_DISCONNECTED) { + xEventGroupSetBits(usb_flags, DEVICE_DISCONNECTED); ESP_LOGI(TAG, "MSC device disconnected"); } - xQueueSend(app_queue, event, 10); } static void print_device_info(msc_host_device_info_t *info) @@ -45,8 +57,8 @@ static void print_device_info(msc_host_device_info_t *info) printf("Device info:\n"); printf("\t Capacity: %llu MB\n", capacity); - printf("\t Sector size: %u\n", info->sector_size); - printf("\t Sector count: %u\n", info->sector_count); + printf("\t Sector size: %"PRIu32"\n", info->sector_size); + printf("\t Sector count: %"PRIu32"\n", info->sector_count); printf("\t PID: 0x%4X \n", info->idProduct); printf("\t VID: 0x%4X \n", info->idVendor); wprintf(L"\t iProduct: %S \n", info->iProduct); @@ -54,6 +66,12 @@ static void print_device_info(msc_host_device_info_t *info) wprintf(L"\t iSerialNumber: %S \n", info->iSerialNumber); } +static bool file_exists(const char *file_path) +{ + struct stat buffer; + return stat(file_path, &buffer) == 0; +} + static void file_operations(void) { const char *directory = "/usb/esp"; @@ -67,15 +85,18 @@ static void file_operations(void) } } - ESP_LOGI(TAG, "Writing file"); - FILE *f = fopen(file_path, "w"); - if (f == NULL) { - ESP_LOGE(TAG, "Failed to open file for writing"); - return; + if (!file_exists(file_path)) { + ESP_LOGI(TAG, "Creating file"); + FILE *f = fopen(file_path, "w"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + fprintf(f, "Hello World!\n"); + fclose(f); } - fprintf(f, "Hello World!\n"); - fclose(f); + FILE *f; ESP_LOGI(TAG, "Reading file"); f = fopen(file_path, "r"); if (f == NULL) { @@ -99,15 +120,16 @@ static void handle_usb_events(void *args) while (1) { uint32_t event_flags; usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + // Release devices once all clients has deregistered if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { usb_host_device_free_all(); + xEventGroupSetBits(usb_flags, HOST_NO_CLIENT); } // Give ready_to_uninstall_usb semaphore to indicate that USB Host library // can be deinitialized, and terminate this task. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { - xSemaphoreGive(ready_to_uninstall_usb); - break; + xEventGroupSetBits(usb_flags, HOST_ALL_FREE); } } @@ -116,29 +138,40 @@ static void handle_usb_events(void *args) static uint8_t wait_for_msc_device(void) { - msc_host_event_t app_event; + EventBits_t event; + ESP_LOGI(TAG, "Waiting for USB stick to be connected"); - xQueueReceive(app_queue, &app_event, portMAX_DELAY); - assert( app_event.event == MSC_DEVICE_CONNECTED ); - return app_event.device.address; + event = xEventGroupWaitBits(usb_flags, DEVICE_CONNECTED | DEVICE_ADDRESS_MASK, + pdTRUE, pdFALSE, portMAX_DELAY); + ESP_LOGI(TAG, "connection..."); + // Extract USB device address from event group bits + return (event & DEVICE_ADDRESS_MASK) >> 4; +} + +static bool wait_for_event(EventBits_t event, TickType_t timeout) +{ + return xEventGroupWaitBits(usb_flags, event, pdTRUE, pdTRUE, timeout) & event; } void app_main(void) { msc_host_device_handle_t msc_device; + msc_host_vfs_handle_t vfs_handle; + msc_host_device_info_t info; BaseType_t task_created; - ready_to_uninstall_usb = xSemaphoreCreateBinary(); - - app_queue = xQueueCreate(3, sizeof(msc_host_event_t)); - assert(app_queue); - - const usb_host_config_t host_config = { - .skip_phy_setup = false, - .intr_flags = ESP_INTR_FLAG_LEVEL1, + const gpio_config_t input_pin = { + .pin_bit_mask = BIT64(USB_DISCONNECT_PIN), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, }; - ESP_ERROR_CHECK( usb_host_install(&host_config) ); + ESP_ERROR_CHECK( gpio_config(&input_pin) ); + usb_flags = xEventGroupCreate(); + assert(usb_flags); + + const usb_host_config_t host_config = { .intr_flags = ESP_INTR_FLAG_LEVEL1 }; + ESP_ERROR_CHECK( usb_host_install(&host_config) ); task_created = xTaskCreate(handle_usb_events, "usb_events", 2048, NULL, 2, NULL); assert(task_created); @@ -150,33 +183,37 @@ void app_main(void) }; ESP_ERROR_CHECK( msc_host_install(&msc_config) ); - uint8_t device_address = wait_for_msc_device(); - - ESP_ERROR_CHECK( msc_host_install_device(device_address, &msc_device) ); - - msc_host_print_descriptors(msc_device); - - msc_host_device_info_t info; - ESP_ERROR_CHECK( msc_host_get_device_info(msc_device, &info) ); - print_device_info(&info); - - msc_host_vfs_handle_t vfs_handle; const esp_vfs_fat_mount_config_t mount_config = { .format_if_mount_failed = false, .max_files = 3, .allocation_unit_size = 1024, }; - ESP_ERROR_CHECK( msc_host_vfs_register(msc_device, "/usb", &mount_config, &vfs_handle) ); + do { + uint8_t device_address = wait_for_msc_device(); - file_operations(); + ESP_ERROR_CHECK( msc_host_install_device(device_address, &msc_device) ); - ESP_ERROR_CHECK( msc_host_vfs_unregister(vfs_handle) ); - ESP_ERROR_CHECK( msc_host_uninstall_device(msc_device) ); + msc_host_print_descriptors(msc_device); + + ESP_ERROR_CHECK( msc_host_get_device_info(msc_device, &info) ); + print_device_info(&info); + + ESP_ERROR_CHECK( msc_host_vfs_register(msc_device, "/usb", &mount_config, &vfs_handle) ); + + while (!wait_for_event(DEVICE_DISCONNECTED, 200)) { + file_operations(); + } + + xEventGroupClearBits(usb_flags, READY_TO_UNINSTALL); + ESP_ERROR_CHECK( msc_host_vfs_unregister(vfs_handle) ); + ESP_ERROR_CHECK( msc_host_uninstall_device(msc_device) ); + + } while (gpio_get_level(USB_DISCONNECT_PIN) != 0); + + ESP_LOGI(TAG, "Uninitializing USB ..."); ESP_ERROR_CHECK( msc_host_uninstall() ); - - xSemaphoreTake(ready_to_uninstall_usb, portMAX_DELAY); + wait_for_event(READY_TO_UNINSTALL, portMAX_DELAY); ESP_ERROR_CHECK( usb_host_uninstall() ); - ESP_LOGI(TAG, "Done"); }