feat(usb/host): Extend USB Host Library example with interactive console

This commit is contained in:
Tomas Rezucha
2026-06-09 12:27:00 +02:00
parent 05c70c1fd4
commit 3bb1d62b32
10 changed files with 633 additions and 336 deletions
@@ -1,4 +1,4 @@
idf_component_register(SRCS "usb_host_lib_main.c" "class_driver.c"
idf_component_register(SRCS "usb_host_lib_main.c" "class_driver.c" "usb_commands.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_driver_gpio
PRIV_REQUIRES fatfs console vfs wear_levelling spi_flash
)
@@ -1,12 +1,19 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
config APP_QUIT_PIN
int "APP Quit button GPIO pin"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 0
config CONSOLE_STORE_HISTORY
bool "Store command history in flash"
default y
help
GPIO pin number to be used as APP_QUIT button.
Linenoise line editing library provides functions to save and load
command history. If this option is enabled, initializes a FAT filesystem
and uses it to store command history.
config CONSOLE_MAX_COMMAND_LINE_LENGTH
int "Maximum command line length"
default 1024
range 128 2048
help
This value marks the maximum length of a single command line. Once it is
reached, no more characters will be accepted by the console.
endmenu
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -78,9 +78,16 @@ static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *
}
}
xSemaphoreGive(driver_obj->constant.mux_lock);
ESP_LOGI(TAG, "Device gone");
break;
case USB_HOST_CLIENT_EVENT_DEV_SUSPENDED:
ESP_LOGI(TAG, "Device suspended");
break;
case USB_HOST_CLIENT_EVENT_DEV_RESUMED:
ESP_LOGI(TAG, "Device resumed");
break;
default:
ESP_LOGW(TAG, "Unsupported client event: %d (possibly suspend/resume)", event_msg->event);
ESP_LOGW(TAG, "Unsupported client event: %d", (int)event_msg->event);
break;
}
}
@@ -103,16 +110,6 @@ static void action_get_info(usb_device_t *device_obj)
ESP_LOGI(TAG, "\t%s speed", (char *[]) {
"Low", "Full", "High"
}[dev_info.speed]);
ESP_LOGI(TAG, "\tParent info:");
if (dev_info.parent.dev_hdl) {
usb_device_info_t parent_dev_info;
ESP_ERROR_CHECK(usb_host_device_info(dev_info.parent.dev_hdl, &parent_dev_info));
ESP_LOGI(TAG, "\t\tBus addr: %d", parent_dev_info.dev_addr);
ESP_LOGI(TAG, "\t\tPort: %d", dev_info.parent.port_num);
} else {
ESP_LOGI(TAG, "\t\tPort: ROOT");
}
ESP_LOGI(TAG, "\tbConfigurationValue %d", dev_info.bConfigurationValue);
// Get the device descriptor next
device_obj->actions |= ACTION_GET_DEV_DESC;
@@ -198,6 +195,7 @@ static void class_driver_device_handle(usb_device_t *device_obj)
void class_driver_task(void *arg)
{
TaskHandle_t task_to_notify = (TaskHandle_t)arg;
class_driver_t driver_obj = {0};
usb_host_client_handle_t class_driver_client_hdl = NULL;
@@ -206,7 +204,7 @@ void class_driver_task(void *arg)
SemaphoreHandle_t mux_lock = xSemaphoreCreateMutex();
if (mux_lock == NULL) {
ESP_LOGE(TAG, "Unable to create class driver mutex");
vTaskSuspend(NULL);
vTaskDelete(NULL);
return;
}
@@ -218,7 +216,11 @@ void class_driver_task(void *arg)
.callback_arg = (void *) &driver_obj,
},
};
ESP_ERROR_CHECK(usb_host_client_register(&client_config, &class_driver_client_hdl));
if (ESP_OK != usb_host_client_register(&client_config, &class_driver_client_hdl)) {
ESP_LOGE(TAG, "Failed to register class driver client");
vTaskDelete(NULL);
return;
}
driver_obj.constant.mux_lock = mux_lock;
driver_obj.constant.client_hdl = class_driver_client_hdl;
@@ -229,6 +231,8 @@ void class_driver_task(void *arg)
s_driver_obj = &driver_obj;
xTaskNotifyGive(task_to_notify);
while (1) {
// Driver has unhandled devices, handle all devices first
if (driver_obj.mux_protected.flags.unhandled_devices) {
@@ -256,11 +260,13 @@ void class_driver_task(void *arg)
if (mux_lock != NULL) {
vSemaphoreDelete(mux_lock);
}
vTaskSuspend(NULL);
s_driver_obj = NULL;
vTaskDelete(NULL);
}
void class_driver_client_deregister(void)
{
assert(s_driver_obj != NULL);
// Mark all opened devices
xSemaphoreTake(s_driver_obj->constant.mux_lock, portMAX_DELAY);
for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) {
@@ -1,4 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
cmd_system:
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system
espressif/usb:
version: "^1.0.0"
version: "^1.3.0"
@@ -0,0 +1,412 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_console.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "usb/usb_host.h"
#include "usb_host_lib_example.h"
#include "sdkconfig.h"
static const char *TAG = "usb_cmd";
#if SOC_USB_OTG_PERIPH_NUM > 1
#define USB_INSTALL_CMD_HINT "<HS|FS|both>"
#define USB_INSTALL_CMD_HELP "Install USB Host stack and class driver on specified port (HS: High Speed port, FS: Full Speed port, both: all available ports; no argument defaults to HS)"
#else
#define USB_INSTALL_CMD_HINT NULL
#define USB_INSTALL_CMD_HELP "Install USB Host stack and class driver (no-op if already installed)"
#endif
#define HOST_LIB_TASK_PRIORITY 2
#define CLASS_TASK_PRIORITY 3
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define ENABLE_ENUM_FILTER_CALLBACK
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
static uint8_t s_usb_stack_running = 0; // Bitmap of running USB host ports, 0 means not installed
static SemaphoreHandle_t s_usb_lifecycle_mx = NULL;
static SemaphoreHandle_t s_host_stopped_sem = NULL;
typedef struct {
TaskHandle_t task_to_notify;
unsigned peripheral_map;
} usb_host_lib_task_args_t;
static int usb_install_cmd(int argc, char **argv)
{
unsigned peripheral_map;
#if SOC_USB_OTG_PERIPH_NUM > 1
if (argc > 2) {
ESP_LOGE(TAG, "Usage: usb_install <HS|FS|both>");
return 1;
}
if (argc == 1 || strcmp(argv[1], "HS") == 0) {
peripheral_map = BIT0;
} else if (strcmp(argv[1], "FS") == 0) {
peripheral_map = BIT1;
} else if (strcmp(argv[1], "both") == 0) {
peripheral_map = BIT0 | BIT1;
} else {
ESP_LOGE(TAG, "Invalid argument '%s', expected HS, FS, or both", argv[1]);
return 1;
}
#else
if (argc != 1) {
ESP_LOGE(TAG, "Usage: usb_install");
return 1;
}
peripheral_map = BIT0;
#endif
esp_err_t err = usb_example_install(peripheral_map);
if (err != ESP_OK) {
ESP_LOGE(TAG, "USB install failed: %s", esp_err_to_name(err));
return 1;
}
return 0;
}
/**
* @brief Set configuration callback
*
* Set the USB device configuration during the enumeration process, must be enabled in the menuconfig
*
* @note bConfigurationValue starts at index 1
*
* @param[in] dev_desc device descriptor of the USB device currently being enumerated
* @param[out] bConfigurationValue configuration descriptor index, that will be user for enumeration
*
* @return bool
* - true: USB device will be enumerated
* - false: USB device will not be enumerated
*/
#ifdef ENABLE_ENUM_FILTER_CALLBACK
static bool set_config_cb(const usb_device_desc_t *dev_desc, uint8_t *bConfigurationValue)
{
if (dev_desc->bNumConfigurations > 1) {
*bConfigurationValue = 2;
} else {
*bConfigurationValue = 1;
}
return true;
}
#endif // ENABLE_ENUM_FILTER_CALLBACK
/**
* @brief USB Host install and event loop; uninstalls when there are no clients and no devices.
*/
static void usb_host_lib_task(void *arg)
{
usb_host_lib_task_args_t *task_args = (usb_host_lib_task_args_t *)arg;
ESP_LOGI(TAG, "Installing USB Host Library");
usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LOWMED,
#ifdef ENABLE_ENUM_FILTER_CALLBACK
.enum_filter_cb = set_config_cb,
#endif // ENABLE_ENUM_FILTER_CALLBACK
.peripheral_map = task_args->peripheral_map,
};
if (ESP_OK != usb_host_install(&host_config)) {
ESP_LOGE(TAG, "Failed to install USB Host Library");
vTaskDelete(NULL);
}
xTaskNotifyGive(task_args->task_to_notify);
ESP_LOGI(TAG, "USB Host installed with peripheral map 0x%x", host_config.peripheral_map);
bool has_clients = true;
bool has_devices = false;
while (has_clients) {
uint32_t event_flags;
ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags)); // Returns error only if the library is not installed, which should not happen since we are inside the library task
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
if (ESP_OK == usb_host_device_free_all()) {
ESP_LOGI(TAG, "All devices marked as free, no need to wait FLAGS_ALL_FREE event");
has_clients = false;
} else {
ESP_LOGI(TAG, "Wait for the FLAGS_ALL_FREE");
has_devices = true;
}
}
if (has_devices && event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
ESP_LOGI(TAG, "Got FLAGS_ALL_FREE");
has_clients = false;
}
}
ESP_LOGI(TAG, "No more clients and devices, uninstall USB Host library");
ESP_ERROR_CHECK(usb_host_uninstall());
if (s_host_stopped_sem) {
xSemaphoreGive(s_host_stopped_sem);
}
vTaskDelete(NULL);
}
esp_err_t usb_example_install(unsigned peripheral_map)
{
if (s_usb_lifecycle_mx == NULL) {
s_usb_lifecycle_mx = xSemaphoreCreateMutex();
assert(s_usb_lifecycle_mx);
}
if (s_host_stopped_sem == NULL) {
s_host_stopped_sem = xSemaphoreCreateBinary();
assert(s_host_stopped_sem);
}
xSemaphoreTake(s_usb_lifecycle_mx, portMAX_DELAY);
esp_err_t ret = ESP_OK;
if (s_usb_stack_running != 0) {
ESP_LOGW(TAG, "USB host stack already installed");
xSemaphoreGive(s_usb_lifecycle_mx);
return ESP_OK;
}
usb_host_lib_task_args_t task_args = {
.task_to_notify = xTaskGetCurrentTaskHandle(),
.peripheral_map = peripheral_map,
};
BaseType_t task_created = xTaskCreatePinnedToCore(usb_host_lib_task,
"usb_host",
4096,
&task_args,
HOST_LIB_TASK_PRIORITY,
NULL,
0);
assert(task_created == pdTRUE);
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000)) == 0) {
ESP_LOGE(TAG, "Timeout waiting for USB host install");
abort();
}
task_created = xTaskCreatePinnedToCore(class_driver_task,
"class",
5 * 1024,
xTaskGetCurrentTaskHandle(),
CLASS_TASK_PRIORITY,
NULL,
0);
assert(task_created == pdTRUE);
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000)) == 0) {
ESP_LOGE(TAG, "Timeout waiting client registration");
abort();
}
s_usb_stack_running = peripheral_map;
ESP_LOGI(TAG, "USB host stack installed (class driver running)");
xSemaphoreGive(s_usb_lifecycle_mx);
return ret;
}
/**
* @brief Tear down the class driver client and uninstall the USB Host Library
*
* Deregisters the class driver, waits for the host library task to call `usb_host_uninstall()`,
* and clears the installed state.
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if the USB host stack is not installed
*/
static esp_err_t usb_example_teardown_locked(void)
{
xSemaphoreTake(s_usb_lifecycle_mx, portMAX_DELAY);
if (s_usb_stack_running == 0) {
ESP_LOGW(TAG, "USB host stack not installed, nothing to uninstall");
xSemaphoreGive(s_usb_lifecycle_mx);
return ESP_ERR_INVALID_STATE;
}
usb_host_lib_info_t lib_info;
if (usb_host_lib_info(&lib_info) == ESP_OK && lib_info.num_devices != 0) {
ESP_LOGW(TAG, "Uninstall with %u device(s) still attached", (unsigned)lib_info.num_devices);
}
class_driver_client_deregister();
if (xSemaphoreTake(s_host_stopped_sem, pdMS_TO_TICKS(15000)) != pdTRUE) {
ESP_LOGW(TAG, "Timeout waiting for USB host library uninstall");
abort();
}
s_usb_stack_running = 0;
xSemaphoreGive(s_usb_lifecycle_mx);
return ESP_OK;
}
static int usb_uninstall_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
if (usb_example_teardown_locked() != ESP_OK) {
ESP_LOGE(TAG, "Failed to teardown USB host library");
return 1;
}
printf("\tUSB Host Library uninstalled\n");
return 0;
}
static int usb_suspend_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
esp_err_t err = usb_host_lib_root_port_suspend();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to suspend USB root port: %s", esp_err_to_name(err));
return 1;
}
if (s_usb_stack_running == (BIT0 | BIT1)) {
ESP_LOGW(TAG, "In USB dual host mode, only HS port is suspended"); // This is a software limitation of the current implementation
}
printf("\tUSB root port suspended\n");
return 0;
}
static int usb_resume_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
esp_err_t err = usb_host_lib_root_port_resume();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to resume USB root port: %s", esp_err_to_name(err));
return 1;
}
if (s_usb_stack_running == (BIT0 | BIT1)) {
ESP_LOGW(TAG, "In USB dual host mode, only HS port is resumed"); // This is a software limitation of the current implementation
}
printf("\tUSB root port resumed\n");
return 0;
}
static int usb_info_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
usb_host_lib_info_t info;
esp_err_t err = usb_host_lib_info(&info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "usb_host_lib_info failed: %s", esp_err_to_name(err));
return 1;
}
if (s_usb_stack_running == (BIT0 | BIT1)) {
ESP_LOGW(TAG, "In USB dual host mode, only HS port can be suspended"); // This is a software limitation of the current implementation
}
printf("\tUSB host lib: Devices=%d, Clients=%d, Root port suspended=%s\n\tDevice addresses: ",
info.num_devices, info.num_clients, info.root_port_suspended ? "yes" : "no");
if (info.num_devices == 0) {
printf("None\n");
return 0;
}
uint8_t *dev_addrs = malloc(info.num_devices * sizeof(uint8_t));
if (dev_addrs == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for device addresses");
return 1;
}
int num_addrs = 0;
err = usb_host_device_addr_list_fill(info.num_devices, dev_addrs, &num_addrs);
if (err != ESP_OK) {
ESP_LOGE(TAG, "usb_host_device_addr_list_fill failed: %s", esp_err_to_name(err));
free(dev_addrs);
return 1;
}
for (int i = 0; i < num_addrs; i++) {
printf(" %u", (unsigned)dev_addrs[i]);
}
printf("\n");
free(dev_addrs);
return 0;
}
static int usb_power_cmd(int argc, char **argv)
{
if (argc != 2) {
ESP_LOGE(TAG, "Usage: usb_power <0|1>");
return 1;
}
bool enable;
if (strcmp(argv[1], "0") == 0) {
enable = false;
} else if (strcmp(argv[1], "1") == 0) {
enable = true;
} else {
ESP_LOGE(TAG, "Invalid argument '%s', expected 0 or 1", argv[1]);
return 1;
}
esp_err_t err = usb_host_lib_set_root_port_power(enable);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set USB root port power: %s", esp_err_to_name(err));
return 1;
}
printf("\tUSB root port power %s\n", enable ? "enabled" : "disabled");
return 0;
}
void register_usb(void)
{
const esp_console_cmd_t cmd_install = {
.command = "usb_install",
.help = USB_INSTALL_CMD_HELP,
.hint = USB_INSTALL_CMD_HINT,
.func = &usb_install_cmd,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_install));
const esp_console_cmd_t cmd_uninstall = {
.command = "usb_uninstall",
.help = "Uninstall USB Host stack (use usb_install to restore)",
.hint = NULL,
.func = &usb_uninstall_cmd,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_uninstall));
const esp_console_cmd_t cmd_info = {
.command = "usb_info",
.help = "Print USB Host Library info and connected device bus addresses",
.hint = NULL,
.func = &usb_info_cmd,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_info));
const esp_console_cmd_t cmd_suspend = {
.command = "usb_suspend",
.help = "Suspend USB root port",
.hint = NULL,
.func = &usb_suspend_cmd,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_suspend));
const esp_console_cmd_t cmd_resume = {
.command = "usb_resume",
.help = "Resume USB root port",
.hint = NULL,
.func = &usb_resume_cmd,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_resume));
const esp_console_cmd_t cmd_power = {
.command = "usb_power",
.help = "Set USB root port power",
.hint = "<0|1>",
.func = &usb_power_cmd,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_power));
}
@@ -0,0 +1,41 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include "esp_err.h"
/**
* @brief Register USB-related console commands
*
* Registers `usb_install`, `usb_uninstall`, `usb_info`, `usb_suspend`, `usb_resume`, and
* `usb_power` with the ESP console REPL. Call once after `esp_console` is initialized.
*/
void register_usb(void);
/**
* @brief Install the USB Host Library and start the example class driver
*
* Creates the USB host library task (which calls `usb_host_install()`) and the class driver task.
*
* @param peripheral_map Value for `usb_host_config_t.peripheral_map` passed to `usb_host_install()`.
* @return
* - ESP_OK on success or if the stack is already installed
* - ESP_ERR_INVALID_STATE if lifecycle mutexes were not created (call from `app_main` first)
*/
esp_err_t usb_example_install(unsigned peripheral_map);
/**
* @brief FreeRTOS task entry for the example pseudo class driver
*
* @param[in] arg `TaskHandle_t` of the task to notify with `xTaskNotifyGive()` after client registration
*/
extern void class_driver_task(void *arg);
/**
* @brief Request shutdown of the class driver task and client deregistration
*/
extern void class_driver_client_deregister(void);
@@ -1,226 +1,98 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <assert.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "esp_console.h"
#include "esp_vfs_fat.h"
#include "wear_levelling.h"
#include "soc/soc_caps.h"
#include "cmd_system.h"
#include "usb/usb_host.h"
#include "driver/gpio.h"
#define HOST_LIB_TASK_PRIORITY 2
#define CLASS_TASK_PRIORITY 3
#define APP_QUIT_PIN CONFIG_APP_QUIT_PIN
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define ENABLE_ENUM_FILTER_CALLBACK
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
extern void class_driver_task(void *arg);
extern void class_driver_client_deregister(void);
#include "usb_host_lib_example.h"
static const char *TAG = "USB host lib";
#define PROMPT_STR CONFIG_IDF_TARGET
QueueHandle_t app_event_queue = NULL;
/**
* @brief APP event group
*
* APP_EVENT - General event, which is APP_QUIT_PIN press event in this example.
/* Console command history can be stored to and loaded from a file.
* The easiest way to do this is to use FATFS filesystem on top of
* wear_levelling library.
*/
typedef enum {
APP_EVENT = 0,
} app_event_group_t;
#if CONFIG_CONSOLE_STORE_HISTORY
/**
* @brief APP event queue
*
* This event is used for delivering events from callback to a task.
*/
typedef struct {
app_event_group_t event_group;
} app_event_queue_t;
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
/**
* @brief BOOT button pressed callback
*
* Signal application to exit the Host lib task
*
* @param[in] arg Unused
*/
static void gpio_cb(void *arg)
static void initialize_filesystem(void)
{
const app_event_queue_t evt_queue = {
.event_group = APP_EVENT,
static wl_handle_t wl_handle;
const esp_vfs_fat_mount_config_t mount_config = {
.max_files = 4,
.format_if_mount_failed = true
};
BaseType_t xTaskWoken = pdFALSE;
if (app_event_queue) {
xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken);
}
if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(MOUNT_PATH, "storage", &mount_config, &wl_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
return;
}
}
/**
* @brief Set configuration callback
*
* Set the USB device configuration during the enumeration process, must be enabled in the menuconfig
* @note bConfigurationValue starts at index 1
*
* @param[in] dev_desc device descriptor of the USB device currently being enumerated
* @param[out] bConfigurationValue configuration descriptor index, that will be user for enumeration
*
* @return bool
* - true: USB device will be enumerated
* - false: USB device will not be enumerated
*/
#ifdef ENABLE_ENUM_FILTER_CALLBACK
static bool set_config_cb(const usb_device_desc_t *dev_desc, uint8_t *bConfigurationValue)
{
// If the USB device has more than one configuration, set the second configuration
if (dev_desc->bNumConfigurations > 1) {
*bConfigurationValue = 2;
} else {
*bConfigurationValue = 1;
}
// Return true to enumerate the USB device
return true;
}
#endif // ENABLE_ENUM_FILTER_CALLBACK
/**
* @brief Start USB Host install and handle common USB host library events while app pin not low
*
* @param[in] arg Not used
*/
static void usb_host_lib_task(void *arg)
{
ESP_LOGI(TAG, "Installing USB Host Library");
usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LOWMED,
# ifdef ENABLE_ENUM_FILTER_CALLBACK
.enum_filter_cb = set_config_cb,
# endif // ENABLE_ENUM_FILTER_CALLBACK
.peripheral_map = BIT0,
};
ESP_ERROR_CHECK(usb_host_install(&host_config));
ESP_LOGI(TAG, "USB Host installed with peripheral map 0x%x", host_config.peripheral_map);
//Signalize the app_main, the USB host library has been installed
xTaskNotifyGive(arg);
bool has_clients = true;
bool has_devices = false;
while (has_clients) {
uint32_t event_flags;
ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags));
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
ESP_LOGI(TAG, "Get FLAGS_NO_CLIENTS");
if (ESP_OK == usb_host_device_free_all()) {
ESP_LOGI(TAG, "All devices marked as free, no need to wait FLAGS_ALL_FREE event");
has_clients = false;
} else {
ESP_LOGI(TAG, "Wait for the FLAGS_ALL_FREE");
has_devices = true;
}
}
if (has_devices && event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
ESP_LOGI(TAG, "Get FLAGS_ALL_FREE");
has_clients = false;
}
}
ESP_LOGI(TAG, "No more clients and devices, uninstall USB Host library");
//Uninstall the USB Host Library
ESP_ERROR_CHECK(usb_host_uninstall());
vTaskSuspend(NULL);
}
#endif // CONFIG_CONSOLE_STORE_HISTORY
void app_main(void)
{
ESP_LOGI(TAG, "USB host library example");
// Init BOOT button: Pressing the button simulates app request to exit
// It will uninstall the class driver and USB Host Lib
const gpio_config_t input_pin = {
.pin_bit_mask = BIT64(APP_QUIT_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE,
};
ESP_ERROR_CHECK(gpio_config(&input_pin));
ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LOWMED));
ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_cb, NULL));
#if SOC_USB_SERIAL_JTAG_SUPPORTED && !CONFIG_ESP_CONSOLE_SECONDARY_NONE
ESP_LOGW(TAG, "A secondary serial console is output-only; consider CONFIG_ESP_CONSOLE_SECONDARY_NONE for interactive use");
#endif
app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t));
app_event_queue_t evt_queue;
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
repl_config.prompt = PROMPT_STR ">";
repl_config.max_cmdline_length = CONFIG_CONSOLE_MAX_COMMAND_LINE_LENGTH;
TaskHandle_t host_lib_task_hdl, class_driver_task_hdl;
#if CONFIG_CONSOLE_STORE_HISTORY
initialize_filesystem();
repl_config.history_save_path = HISTORY_PATH;
ESP_LOGI(TAG, "Command history enabled");
#else
ESP_LOGI(TAG, "Command history disabled");
#endif
// Create usb host lib task
BaseType_t task_created;
task_created = xTaskCreatePinnedToCore(usb_host_lib_task,
"usb_host",
4096,
xTaskGetCurrentTaskHandle(),
HOST_LIB_TASK_PRIORITY,
&host_lib_task_hdl,
0);
assert(task_created == pdTRUE);
esp_console_register_help_command();
register_system_common();
register_usb();
#if SOC_LIGHT_SLEEP_SUPPORTED
register_system_light_sleep();
#endif
#if SOC_DEEP_SLEEP_SUPPORTED
register_system_deep_sleep();
#endif
// Wait until the USB host library is installed
ulTaskNotifyTake(false, 1000);
ESP_ERROR_CHECK(usb_example_install(BIT0));
// Create class driver task
task_created = xTaskCreatePinnedToCore(class_driver_task,
"class",
5 * 1024,
NULL,
CLASS_TASK_PRIORITY,
&class_driver_task_hdl,
0);
assert(task_created == pdTRUE);
// Add a short delay to let the tasks run
vTaskDelay(10);
#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM)
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
while (1) {
if (xQueueReceive(app_event_queue, &evt_queue, portMAX_DELAY)) {
if (APP_EVENT == evt_queue.event_group) {
// User pressed button
usb_host_lib_info_t lib_info;
ESP_ERROR_CHECK(usb_host_lib_info(&lib_info));
if (lib_info.num_devices != 0) {
ESP_LOGW(TAG, "Shutdown with attached devices.");
}
// End while cycle
break;
}
}
}
#elif defined(CONFIG_ESP_CONSOLE_USB_CDC)
esp_console_dev_usb_cdc_config_t hw_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&hw_config, &repl_config, &repl));
// Deregister client
class_driver_client_deregister();
vTaskDelay(10);
#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG)
esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl));
// Delete the tasks
vTaskDelete(class_driver_task_hdl);
vTaskDelete(host_lib_task_hdl);
#else
#error Unsupported console type
#endif
// Delete interrupt and queue
gpio_isr_handler_remove(APP_QUIT_PIN);
xQueueReset(app_event_queue);
vQueueDelete(app_event_queue);
ESP_LOGI(TAG, "End of the example");
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}