Merge branch 'feat/usb_host_set_device_config' into 'master'

USB Host: Add enumeration callback filter

Closes IDFGH-11534

See merge request espressif/esp-idf!27370
This commit is contained in:
Peter Marcisovsky
2024-01-05 00:11:31 +08:00
8 changed files with 163 additions and 12 deletions

View File

@@ -96,6 +96,17 @@ menu "USB-OTG"
endmenu #Root Hub configuration endmenu #Root Hub configuration
config USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
bool "Enable enumeration filter callback"
default n
help
The enumeration filter callback is called before enumeration of each newly attached device. This callback
allows users to control whether a device should be enumerated, and what configuration number to use when
enumerating a device.
If enabled, the enumeration filter callback can be set via 'usb_host_config_t' when calling
'usb_host_install()'.
# Hidden or compatibility options # Hidden or compatibility options
config USB_OTG_SUPPORTED config USB_OTG_SUPPORTED

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -32,11 +32,15 @@ implement the bare minimum to control the root HCD port.
#define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_BALANCED #define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_BALANCED
#endif #endif
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define ENABLE_ENUM_FILTER_CALLBACK
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define SET_ADDR_RECOVERY_INTERVAL_MS CONFIG_USB_HOST_SET_ADDR_RECOVERY_MS #define SET_ADDR_RECOVERY_INTERVAL_MS CONFIG_USB_HOST_SET_ADDR_RECOVERY_MS
#define ENUM_CTRL_TRANSFER_MAX_DATA_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE #define ENUM_CTRL_TRANSFER_MAX_DATA_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE
#define ENUM_DEV_ADDR 1 // Device address used in enumeration #define ENUM_DEV_ADDR 1 // Device address used in enumeration
#define ENUM_CONFIG_INDEX 0 // Index used to get the first configuration descriptor of the device #define ENUM_CONFIG_INDEX_DEFAULT 0 // Index used to get the first configuration descriptor of the device
#define ENUM_SHORT_DESC_REQ_LEN 8 // Number of bytes to request when getting a short descriptor (just enough to get bMaxPacketSize0 or wTotalLength) #define ENUM_SHORT_DESC_REQ_LEN 8 // Number of bytes to request when getting a short descriptor (just enough to get bMaxPacketSize0 or wTotalLength)
#define ENUM_WORST_CASE_MPS_LS 8 // The worst case MPS of EP0 for a LS device #define ENUM_WORST_CASE_MPS_LS 8 // The worst case MPS of EP0 for a LS device
#define ENUM_WORST_CASE_MPS_FS 64 // The worst case MPS of EP0 for a FS device #define ENUM_WORST_CASE_MPS_FS 64 // The worst case MPS of EP0 for a FS device
@@ -165,6 +169,11 @@ typedef struct {
uint8_t iSerialNumber; /**< Index of the Serial Number string descriptor */ uint8_t iSerialNumber; /**< Index of the Serial Number string descriptor */
uint8_t str_desc_bLength; /**< Saved bLength from getting a short string descriptor */ uint8_t str_desc_bLength; /**< Saved bLength from getting a short string descriptor */
uint8_t bConfigurationValue; /**< Device's current configuration number */ uint8_t bConfigurationValue; /**< Device's current configuration number */
uint8_t enum_config_index; /**< Configuration index used during enumeration */
#ifdef ENABLE_ENUM_FILTER_CALLBACK
usb_host_enum_filter_cb_t enum_filter_cb; /**< Set device configuration callback */
bool graceful_exit; /**< Exit enumeration by user's request from the callback function */
#endif // ENABLE_ENUM_FILTER_CALLBACK
} enum_ctrl_t; } enum_ctrl_t;
typedef struct { typedef struct {
@@ -280,6 +289,11 @@ static bool enum_stage_start(enum_ctrl_t *enum_ctrl)
ESP_ERROR_CHECK(hcd_pipe_update_callback(enum_dflt_pipe_hdl, enum_dflt_pipe_callback, NULL)); ESP_ERROR_CHECK(hcd_pipe_update_callback(enum_dflt_pipe_hdl, enum_dflt_pipe_callback, NULL));
enum_ctrl->dev_hdl = enum_dev_hdl; enum_ctrl->dev_hdl = enum_dev_hdl;
enum_ctrl->pipe = enum_dflt_pipe_hdl; enum_ctrl->pipe = enum_dflt_pipe_hdl;
// Flag to gracefully exit the enumeration process if requested by the user in the enumeration filter cb
#ifdef ENABLE_ENUM_FILTER_CALLBACK
enum_ctrl->graceful_exit = false;
#endif // ENABLE_ENUM_FILTER_CALLBACK
return true; return true;
} }
@@ -323,6 +337,39 @@ static void get_string_desc_index_and_langid(enum_ctrl_t *enum_ctrl, uint8_t *in
} }
} }
static bool set_config_index(enum_ctrl_t *enum_ctrl, const usb_device_desc_t *device_desc)
{
#ifdef ENABLE_ENUM_FILTER_CALLBACK
// Callback enabled in the menuncofig, but the callback function was not defined
if (enum_ctrl->enum_filter_cb == NULL) {
enum_ctrl->enum_config_index = ENUM_CONFIG_INDEX_DEFAULT;
return true;
}
uint8_t enum_config_index;
const bool enum_continue = enum_ctrl->enum_filter_cb(device_desc, &enum_config_index);
// User's request NOT to enumerate the USB device
if (!enum_continue) {
ESP_LOGW(HUB_DRIVER_TAG, "USB device (PID = 0x%x, VID = 0x%x) will not be enumerated", device_desc->idProduct, device_desc->idVendor);
enum_ctrl->graceful_exit = true;
return false;
}
// Set configuration descriptor
if ((enum_config_index == 0) || (enum_config_index > device_desc->bNumConfigurations)) {
ESP_LOGW(HUB_DRIVER_TAG, "bConfigurationValue %d provided by user, device will be configured with configuration descriptor 1", enum_config_index);
enum_ctrl->enum_config_index = ENUM_CONFIG_INDEX_DEFAULT;
} else {
enum_ctrl->enum_config_index = enum_config_index - 1;
}
#else // ENABLE_ENUM_FILTER_CALLBACK
enum_ctrl->enum_config_index = ENUM_CONFIG_INDEX_DEFAULT;
#endif // ENABLE_ENUM_FILTER_CALLBACK
return true;
}
static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl) static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
{ {
usb_transfer_t *transfer = &enum_ctrl->urb->transfer; usb_transfer_t *transfer = &enum_ctrl->urb->transfer;
@@ -351,7 +398,7 @@ static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
} }
case ENUM_STAGE_GET_SHORT_CONFIG_DESC: { case ENUM_STAGE_GET_SHORT_CONFIG_DESC: {
// Get a short config descriptor at index 0 // Get a short config descriptor at index 0
USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX, ENUM_SHORT_DESC_REQ_LEN); USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, enum_ctrl->enum_config_index, ENUM_SHORT_DESC_REQ_LEN);
transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(ENUM_SHORT_DESC_REQ_LEN, enum_ctrl->bMaxPacketSize0); transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(ENUM_SHORT_DESC_REQ_LEN, enum_ctrl->bMaxPacketSize0);
// IN data stage should return exactly ENUM_SHORT_DESC_REQ_LEN bytes // IN data stage should return exactly ENUM_SHORT_DESC_REQ_LEN bytes
enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + ENUM_SHORT_DESC_REQ_LEN; enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + ENUM_SHORT_DESC_REQ_LEN;
@@ -359,7 +406,7 @@ static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
} }
case ENUM_STAGE_GET_FULL_CONFIG_DESC: { case ENUM_STAGE_GET_FULL_CONFIG_DESC: {
// Get the full configuration descriptor at index 0, requesting its exact length. // Get the full configuration descriptor at index 0, requesting its exact length.
USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX, enum_ctrl->wTotalLength); USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, enum_ctrl->enum_config_index, enum_ctrl->wTotalLength);
transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(enum_ctrl->wTotalLength, enum_ctrl->bMaxPacketSize0); transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(enum_ctrl->wTotalLength, enum_ctrl->bMaxPacketSize0);
// IN data stage should return exactly wTotalLength bytes // IN data stage should return exactly wTotalLength bytes
enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + enum_ctrl->wTotalLength; enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + enum_ctrl->wTotalLength;
@@ -497,7 +544,7 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
enum_ctrl->iManufacturer = device_desc->iManufacturer; enum_ctrl->iManufacturer = device_desc->iManufacturer;
enum_ctrl->iProduct = device_desc->iProduct; enum_ctrl->iProduct = device_desc->iProduct;
enum_ctrl->iSerialNumber = device_desc->iSerialNumber; enum_ctrl->iSerialNumber = device_desc->iSerialNumber;
ret = true; ret = set_config_index(enum_ctrl, device_desc);
break; break;
} }
case ENUM_STAGE_CHECK_SHORT_CONFIG_DESC: { case ENUM_STAGE_CHECK_SHORT_CONFIG_DESC: {
@@ -924,7 +971,15 @@ static void enum_handle_events(void)
if (stage_pass) { if (stage_pass) {
ESP_LOGD(HUB_DRIVER_TAG, "Stage done: %s", enum_stage_strings[enum_ctrl->stage]); ESP_LOGD(HUB_DRIVER_TAG, "Stage done: %s", enum_stage_strings[enum_ctrl->stage]);
} else { } else {
ESP_LOGE(HUB_DRIVER_TAG, "Stage failed: %s", enum_stage_strings[enum_ctrl->stage]); #ifdef ENABLE_ENUM_FILTER_CALLBACK
if (!enum_ctrl->graceful_exit) {
ESP_LOGE(HUB_DRIVER_TAG, "Stage failed: %s", enum_stage_strings[enum_ctrl->stage]);
} else {
ESP_LOGD(HUB_DRIVER_TAG, "Stage done: %s", enum_stage_strings[enum_ctrl->stage]);
}
#else // ENABLE_ENUM_FILTER_CALLBACK
ESP_LOGE(HUB_DRIVER_TAG, "Stage failed: %s", enum_stage_strings[enum_ctrl->stage]);
#endif // ENABLE_ENUM_FILTER_CALLBACK
} }
enum_set_next_stage(enum_ctrl, stage_pass); enum_set_next_stage(enum_ctrl, stage_pass);
} }
@@ -959,9 +1014,13 @@ esp_err_t hub_install(hub_config_t *hub_config)
hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_INSTALLED; hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_INSTALLED;
hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_NONE; hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_NONE;
hub_driver_obj->single_thread.enum_ctrl.urb = enum_urb; hub_driver_obj->single_thread.enum_ctrl.urb = enum_urb;
#ifdef ENABLE_ENUM_FILTER_CALLBACK
hub_driver_obj->single_thread.enum_ctrl.enum_filter_cb = hub_config->enum_filter_cb;
#endif // ENABLE_ENUM_FILTER_CALLBACK
hub_driver_obj->constant.root_port_hdl = port_hdl; hub_driver_obj->constant.root_port_hdl = port_hdl;
hub_driver_obj->constant.proc_req_cb = hub_config->proc_req_cb; hub_driver_obj->constant.proc_req_cb = hub_config->proc_req_cb;
hub_driver_obj->constant.proc_req_cb_arg = hub_config->proc_req_cb_arg; hub_driver_obj->constant.proc_req_cb_arg = hub_config->proc_req_cb_arg;
HUB_DRIVER_ENTER_CRITICAL(); HUB_DRIVER_ENTER_CRITICAL();
if (p_hub_driver_obj != NULL) { if (p_hub_driver_obj != NULL) {
HUB_DRIVER_EXIT_CRITICAL(); HUB_DRIVER_EXIT_CRITICAL();

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -106,6 +106,8 @@ typedef struct {
set this if they want to use an external USB PHY. Otherwise, the USB Host Library set this if they want to use an external USB PHY. Otherwise, the USB Host Library
will automatically configure the internal USB PHY */ will automatically configure the internal USB PHY */
int intr_flags; /**< Interrupt flags for the underlying ISR used by the USB Host stack */ int intr_flags; /**< Interrupt flags for the underlying ISR used by the USB Host stack */
usb_host_enum_filter_cb_t enum_filter_cb; /**< Enumeration filter callback. Enable CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
to use this feature. Set to NULL otherwise. */
} usb_host_config_t; } usb_host_config_t;
/** /**

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,6 +10,7 @@ Warning: The USB Host Library API is still a beta version and may be subject to
#pragma once #pragma once
#include <stdbool.h>
#include "usb/usb_types_ch9.h" #include "usb/usb_types_ch9.h"
#ifdef __cplusplus #ifdef __cplusplus
@@ -46,6 +47,29 @@ typedef enum {
*/ */
typedef struct usb_device_handle_s *usb_device_handle_t; typedef struct usb_device_handle_s *usb_device_handle_t;
/**
* @brief Enumeration filter callback
*
* This callback is called at the beginning of the enumeration process for a newly attached device.
* Through this callback, users are able to:
*
* - filter which devices should be enumerated
* - select the configuration number to use when enumerating the device
*
* The device descriptor is passed to this callback to allow users to filter devices based on
* Vendor ID, Product ID, and class code.
*
* @attention This callback must be non-blocking
* @attention This callback must not submit any USB transfers
* @param[in] dev_desc Device descriptor of the device to enumerate
* @param[out] bConfigurationValue Configuration number to use when enumerating the device (starts with 1)
*
* @return bool
* - true: USB device will be enumerated
* - false: USB device will not be enumerated
*/
typedef bool (*usb_host_enum_filter_cb_t)(const usb_device_desc_t *dev_desc, uint8_t *bConfigurationValue);
/** /**
* @brief Basic information of an enumerated device * @brief Basic information of an enumerated device
*/ */

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -8,6 +8,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h" #include "esp_err.h"
#include "usb_private.h" #include "usb_private.h"
#include "usbh.h" #include "usbh.h"
@@ -22,8 +23,11 @@ extern "C" {
* @brief Hub driver configuration * @brief Hub driver configuration
*/ */
typedef struct { typedef struct {
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */ usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
void *proc_req_cb_arg; /**< Processing request callback argument */ void *proc_req_cb_arg; /**< Processing request callback argument */
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
usb_host_enum_filter_cb_t enum_filter_cb; /**< Set device configuration callback */
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
} hub_config_t; } hub_config_t;
// ---------------------------------------------- Hub Driver Functions ------------------------------------------------- // ---------------------------------------------- Hub Driver Functions -------------------------------------------------

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,6 +10,7 @@ Warning: The USB Host Library API is still a beta version and may be subject to
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/queue.h" #include "freertos/queue.h"
@@ -47,6 +48,10 @@ static portMUX_TYPE host_lock = portMUX_INITIALIZER_UNLOCKED;
#define PROCESS_REQUEST_PENDING_FLAG_USBH 0x01 #define PROCESS_REQUEST_PENDING_FLAG_USBH 0x01
#define PROCESS_REQUEST_PENDING_FLAG_HUB 0x02 #define PROCESS_REQUEST_PENDING_FLAG_HUB 0x02
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define ENABLE_ENUM_FILTER_CALLBACK
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
typedef struct ep_wrapper_s ep_wrapper_t; typedef struct ep_wrapper_s ep_wrapper_t;
typedef struct interface_s interface_t; typedef struct interface_s interface_t;
typedef struct client_s client_t; typedef struct client_s client_t;
@@ -404,10 +409,18 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
goto usbh_err; goto usbh_err;
} }
#ifdef ENABLE_ENUM_FILTER_CALLBACK
if (config->enum_filter_cb == NULL) {
ESP_LOGW(USB_HOST_TAG, "User callback to set USB device configuration is enabled, but not used");
}
#endif // ENABLE_ENUM_FILTER_CALLBACK
// Install Hub // Install Hub
hub_config_t hub_config = { hub_config_t hub_config = {
.proc_req_cb = proc_req_callback, .proc_req_cb = proc_req_callback,
.proc_req_cb_arg = NULL, .proc_req_cb_arg = NULL,
#ifdef ENABLE_ENUM_FILTER_CALLBACK
.enum_filter_cb = config->enum_filter_cb,
#endif // ENABLE_ENUM_FILTER_CALLBACK
}; };
ret = hub_install(&hub_config); ret = hub_install(&hub_config);
if (ret != ESP_OK) { if (ret != ESP_OK) {

View File

@@ -3,3 +3,5 @@ idf_component_register(
INCLUDE_DIRS "." INCLUDE_DIRS "."
PRIV_REQUIRES usb PRIV_REQUIRES usb
) )
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-missing-field-initializers")

View File

@@ -17,6 +17,10 @@
#define CLASS_TASK_PRIORITY 3 #define CLASS_TASK_PRIORITY 3
#define APP_QUIT_PIN CONFIG_APP_QUIT_PIN #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_task(void *arg);
extern void class_driver_client_deregister(void); extern void class_driver_client_deregister(void);
@@ -66,6 +70,35 @@ static void gpio_cb(void *arg)
} }
} }
/**
* @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 * @brief Start USB Host install and handle common USB host library events while app pin not low
* *
@@ -77,6 +110,9 @@ static void usb_host_lib_task(void *arg)
usb_host_config_t host_config = { usb_host_config_t host_config = {
.skip_phy_setup = false, .skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1, .intr_flags = ESP_INTR_FLAG_LEVEL1,
# ifdef ENABLE_ENUM_FILTER_CALLBACK
.enum_filter_cb = set_config_cb,
# endif // ENABLE_ENUM_FILTER_CALLBACK
}; };
ESP_ERROR_CHECK(usb_host_install(&host_config)); ESP_ERROR_CHECK(usb_host_install(&host_config));