mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-02 04:04:31 +02:00
Merge branch 'feature/usb_host_hub_support_collective_backport_p3_v5.2' into 'release/v5.2'
feat(usb_host): Hub Support Collective backport part 3/3 (v5.2) See merge request espressif/esp-idf!33283
This commit is contained in:
2
Kconfig
2
Kconfig
@@ -620,3 +620,5 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
|||||||
- CONFIG_ESPTOOLPY_FLASHFREQ_120M && CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
|
- CONFIG_ESPTOOLPY_FLASHFREQ_120M && CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR
|
||||||
- CONFIG_SPIRAM_SPEED_120M && CONFIG_SPIRAM_MODE_OCT
|
- CONFIG_SPIRAM_SPEED_120M && CONFIG_SPIRAM_MODE_OCT
|
||||||
- CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
|
- CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
|
||||||
|
- CONFIG_USB_HOST_EXT_PORT_SUPPORT_LS
|
||||||
|
- CONFIG_USB_HOST_EXT_PORT_RESET_ATTEMPTS
|
||||||
|
@@ -26,7 +26,8 @@ if(CONFIG_SOC_USB_OTG_SUPPORTED)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CONFIG_USB_HOST_HUBS_SUPPORTED)
|
if(CONFIG_USB_HOST_HUBS_SUPPORTED)
|
||||||
list(APPEND srcs "ext_hub.c")
|
list(APPEND srcs "ext_hub.c"
|
||||||
|
"ext_port.c")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
idf_component_register(SRCS ${srcs}
|
idf_component_register(SRCS ${srcs}
|
||||||
|
@@ -112,6 +112,63 @@ menu "USB-OTG"
|
|||||||
help
|
help
|
||||||
Enables support for connecting multiple Hubs simultaneously.
|
Enables support for connecting multiple Hubs simultaneously.
|
||||||
|
|
||||||
|
menu "Downstream Port configuration"
|
||||||
|
depends on USB_HOST_HUBS_SUPPORTED
|
||||||
|
|
||||||
|
config USB_HOST_EXT_PORT_SUPPORT_LS
|
||||||
|
depends on IDF_EXPERIMENTAL_FEATURES
|
||||||
|
bool "Support LS"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enables support of Low-speed devices, connected through the external Hub.
|
||||||
|
|
||||||
|
config USB_HOST_EXT_PORT_RESET_ATTEMPTS
|
||||||
|
depends on IDF_EXPERIMENTAL_FEATURES
|
||||||
|
# Invisible config option
|
||||||
|
# Todo: IDF-11283
|
||||||
|
int
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Amount of attempts to reset the device.
|
||||||
|
|
||||||
|
The default value is 1.
|
||||||
|
|
||||||
|
config USB_HOST_EXT_PORT_RESET_RECOVERY_DELAY_MS
|
||||||
|
int "Reset recovery delay in ms"
|
||||||
|
default 30
|
||||||
|
help
|
||||||
|
After a port stops driving the reset signal, the USB 2.0 specification requires that
|
||||||
|
the "USB System Software guarantees a minimum of 10 ms for reset recovery" before the
|
||||||
|
attached device is expected to respond to data transfers (see USB 2.0 chapter 7.1.7.3 for
|
||||||
|
more details).
|
||||||
|
The device may ignore any data transfers during the recovery interval.
|
||||||
|
|
||||||
|
The default value is set to 30 ms to be safe.
|
||||||
|
|
||||||
|
config USB_HOST_EXT_PORT_CUSTOM_POWER_ON_DELAY_ENABLE
|
||||||
|
bool "Custom bPwrOn2PwrGood value"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enables the possibility to configure custom time for the power-on sequence on a port
|
||||||
|
until power is good on that port.
|
||||||
|
|
||||||
|
When enabled, applies the custom PwrOn2PwrGood delay.
|
||||||
|
When disabled, applies the PwrOn2PwrGood value from the Hub Descriptor.
|
||||||
|
|
||||||
|
config USB_HOST_EXT_PORT_CUSTOM_POWER_ON_DELAY_MS
|
||||||
|
depends on USB_HOST_EXT_PORT_CUSTOM_POWER_ON_DELAY_ENABLE
|
||||||
|
int "PwrOn2PwrGood delay in ms"
|
||||||
|
default 100
|
||||||
|
range 0 5000
|
||||||
|
help
|
||||||
|
Custom value of delay from the time the power-on sequence begins on a port
|
||||||
|
until power is good on that port.
|
||||||
|
Value 0 is used for a hub with no power switches.
|
||||||
|
|
||||||
|
The default value is 100 ms.
|
||||||
|
|
||||||
|
endmenu #Downstream Port configuration
|
||||||
|
|
||||||
endmenu #Hub Driver Configuration
|
endmenu #Hub Driver Configuration
|
||||||
|
|
||||||
# Hidden or compatibility options
|
# Hidden or compatibility options
|
||||||
|
@@ -765,9 +765,15 @@ static esp_err_t control_response_handling(enum_stage_t stage)
|
|||||||
usb_transfer_t *ctrl_xfer = &p_enum_driver->constant.urb->transfer;
|
usb_transfer_t *ctrl_xfer = &p_enum_driver->constant.urb->transfer;
|
||||||
|
|
||||||
if (ctrl_xfer->status != USB_TRANSFER_STATUS_COMPLETED) {
|
if (ctrl_xfer->status != USB_TRANSFER_STATUS_COMPLETED) {
|
||||||
ESP_LOGE(ENUM_TAG, "Bad transfer status %d: %s",
|
if (ctrl_xfer->status == USB_TRANSFER_STATUS_STALL &&
|
||||||
ctrl_xfer->status,
|
stage >= ENUM_STAGE_CHECK_SHORT_LANGID_TABLE &&
|
||||||
enum_stage_strings[stage]);
|
stage <= ENUM_STAGE_CHECK_FULL_SER_STR_DESC) {
|
||||||
|
// String Descriptor request could be STALLed, if the device doesn't have them
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(ENUM_TAG, "Bad transfer status %d: %s",
|
||||||
|
ctrl_xfer->status,
|
||||||
|
enum_stage_strings[stage]);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1015,10 +1021,6 @@ static bool set_next_stage(bool last_stage_pass)
|
|||||||
next_stage = last_stage + 1;
|
next_stage = last_stage + 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(ENUM_TAG, "[%d:%d] %s FAILED",
|
|
||||||
p_enum_driver->single_thread.parent_dev_addr,
|
|
||||||
p_enum_driver->single_thread.parent_port_num,
|
|
||||||
enum_stage_strings[last_stage]);
|
|
||||||
// These stages cannot fail
|
// These stages cannot fail
|
||||||
assert(last_stage != ENUM_STAGE_SET_ADDR_RECOVERY &&
|
assert(last_stage != ENUM_STAGE_SET_ADDR_RECOVERY &&
|
||||||
last_stage != ENUM_STAGE_SELECT_CONFIG &&
|
last_stage != ENUM_STAGE_SELECT_CONFIG &&
|
||||||
@@ -1055,6 +1057,10 @@ static bool set_next_stage(bool last_stage_pass)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Stage is not allowed to failed. Cancel enumeration.
|
// Stage is not allowed to failed. Cancel enumeration.
|
||||||
|
ESP_LOGE(ENUM_TAG, "[%d:%d] %s FAILED",
|
||||||
|
p_enum_driver->single_thread.parent_dev_addr,
|
||||||
|
p_enum_driver->single_thread.parent_port_num,
|
||||||
|
enum_stage_strings[last_stage]);
|
||||||
next_stage = ENUM_STAGE_CANCEL;
|
next_stage = ENUM_STAGE_CANCEL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -5,18 +5,17 @@
|
|||||||
*/
|
*/
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
|
||||||
#include "usb_private.h"
|
#include "usb_private.h"
|
||||||
#include "ext_hub.h"
|
#include "ext_hub.h"
|
||||||
|
#include "ext_port.h"
|
||||||
#include "usb/usb_helpers.h"
|
#include "usb/usb_helpers.h"
|
||||||
|
|
||||||
typedef struct ext_port_s *ext_port_hdl_t; /* This will be implemented during ext_port driver implementation */
|
|
||||||
|
|
||||||
#define EXT_HUB_MAX_STATUS_BYTES_SIZE (sizeof(uint32_t))
|
#define EXT_HUB_MAX_STATUS_BYTES_SIZE (sizeof(uint32_t))
|
||||||
#define EXT_HUB_STATUS_CHANGE_FLAG (1 << 0)
|
#define EXT_HUB_STATUS_CHANGE_FLAG (1 << 0)
|
||||||
#define EXT_HUB_STATUS_PORT1_CHANGE_FLAG (1 << 1)
|
#define EXT_HUB_STATUS_PORT1_CHANGE_FLAG (1 << 1)
|
||||||
@@ -396,8 +395,15 @@ static void device_error(ext_hub_dev_t *ext_hub_dev)
|
|||||||
|
|
||||||
static esp_err_t device_port_new(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
|
static esp_err_t device_port_new(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
|
||||||
{
|
{
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (ext_hub_handle_t) ext_hub_dev,
|
||||||
|
.parent_dev_hdl = ext_hub_dev->constant.dev_hdl,
|
||||||
|
.parent_port_num = port_idx + 1,
|
||||||
|
.port_power_delay_ms = ext_hub_dev->constant.hub_desc->bPwrOn2PwrGood * 2,
|
||||||
|
};
|
||||||
|
|
||||||
assert(p_ext_hub_driver->constant.port_driver);
|
assert(p_ext_hub_driver->constant.port_driver);
|
||||||
esp_err_t ret = p_ext_hub_driver->constant.port_driver->new (NULL, (void**) &ext_hub_dev->constant.ports[port_idx]);
|
esp_err_t ret = p_ext_hub_driver->constant.port_driver->new (&port_config, (void**) &ext_hub_dev->constant.ports[port_idx]);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port allocation error: %s", ext_hub_dev->constant.dev_addr, port_idx + 1, esp_err_to_name(ret));
|
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port allocation error: %s", ext_hub_dev->constant.dev_addr, port_idx + 1, esp_err_to_name(ret));
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -418,7 +424,7 @@ static esp_err_t device_port_free(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
|
|||||||
|
|
||||||
assert(ext_hub_dev->single_thread.maxchild != 0);
|
assert(ext_hub_dev->single_thread.maxchild != 0);
|
||||||
assert(p_ext_hub_driver->constant.port_driver);
|
assert(p_ext_hub_driver->constant.port_driver);
|
||||||
esp_err_t ret = p_ext_hub_driver->constant.port_driver->free(ext_hub_dev->constant.ports[port_idx]);
|
esp_err_t ret = p_ext_hub_driver->constant.port_driver->del(ext_hub_dev->constant.ports[port_idx]);
|
||||||
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to free port: %s", ext_hub_dev->constant.dev_addr, port_idx + 1, esp_err_to_name(ret));
|
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to free port: %s", ext_hub_dev->constant.dev_addr, port_idx + 1, esp_err_to_name(ret));
|
||||||
@@ -1038,6 +1044,7 @@ static void handle_device(ext_hub_dev_t *ext_hub_dev)
|
|||||||
// FSM for external Hub
|
// FSM for external Hub
|
||||||
switch (ext_hub_dev->single_thread.stage) {
|
switch (ext_hub_dev->single_thread.stage) {
|
||||||
case EXT_HUB_STAGE_IDLE:
|
case EXT_HUB_STAGE_IDLE:
|
||||||
|
stage_pass = true;
|
||||||
break;
|
break;
|
||||||
case EXT_HUB_STAGE_GET_DEVICE_STATUS:
|
case EXT_HUB_STAGE_GET_DEVICE_STATUS:
|
||||||
case EXT_HUB_STAGE_GET_HUB_DESCRIPTOR:
|
case EXT_HUB_STAGE_GET_HUB_DESCRIPTOR:
|
||||||
@@ -1118,8 +1125,7 @@ esp_err_t ext_hub_install(const ext_hub_config_t *config)
|
|||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
ext_hub_driver_t *ext_hub_drv = heap_caps_calloc(1, sizeof(ext_hub_driver_t), MALLOC_CAP_DEFAULT);
|
ext_hub_driver_t *ext_hub_drv = heap_caps_calloc(1, sizeof(ext_hub_driver_t), MALLOC_CAP_DEFAULT);
|
||||||
SemaphoreHandle_t mux_lock = xSemaphoreCreateMutex();
|
if (ext_hub_drv == NULL) {
|
||||||
if (ext_hub_drv == NULL || mux_lock == NULL) {
|
|
||||||
ret = ESP_ERR_NO_MEM;
|
ret = ESP_ERR_NO_MEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@@ -1151,9 +1157,6 @@ esp_err_t ext_hub_install(const ext_hub_config_t *config)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (mux_lock != NULL) {
|
|
||||||
vSemaphoreDelete(mux_lock);
|
|
||||||
}
|
|
||||||
heap_caps_free(ext_hub_drv);
|
heap_caps_free(ext_hub_drv);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1672,7 +1675,7 @@ esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num,
|
|||||||
esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature)
|
esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
@@ -1701,7 +1704,7 @@ esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_nu
|
|||||||
esp_err_t ext_hub_clear_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature)
|
esp_err_t ext_hub_clear_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
@@ -1730,7 +1733,7 @@ esp_err_t ext_hub_clear_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_
|
|||||||
esp_err_t ext_hub_get_port_status(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
esp_err_t ext_hub_get_port_status(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
|
1404
components/usb/ext_port.c
Normal file
1404
components/usb/ext_port.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1731,6 +1731,8 @@ esp_err_t hcd_pipe_alloc(hcd_port_handle_t port_hdl, const hcd_pipe_config_t *pi
|
|||||||
bool chan_allocated = usb_dwc_hal_chan_alloc(port->hal, pipe->chan_obj, (void *) pipe);
|
bool chan_allocated = usb_dwc_hal_chan_alloc(port->hal, pipe->chan_obj, (void *) pipe);
|
||||||
if (!chan_allocated) {
|
if (!chan_allocated) {
|
||||||
HCD_EXIT_CRITICAL();
|
HCD_EXIT_CRITICAL();
|
||||||
|
// The only reason why alloc channel could return false is no more free channels
|
||||||
|
ESP_LOGE(HCD_DWC_TAG, "No more HCD channels available");
|
||||||
ret = ESP_ERR_NOT_SUPPORTED;
|
ret = ESP_ERR_NOT_SUPPORTED;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
#include "usb/usb_helpers.h"
|
#include "usb/usb_helpers.h"
|
||||||
|
|
||||||
#if ENABLE_USB_HUBS
|
#if ENABLE_USB_HUBS
|
||||||
#include "ext_hub.h"
|
#include "ext_port.h"
|
||||||
#endif // ENABLE_USB_HUBS
|
#endif // ENABLE_USB_HUBS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -320,6 +320,72 @@ static bool ext_hub_callback(bool in_isr, void *user_arg)
|
|||||||
HUB_DRIVER_EXIT_CRITICAL_SAFE();
|
HUB_DRIVER_EXIT_CRITICAL_SAFE();
|
||||||
return p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.proc_req_cb_arg);
|
return p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.proc_req_cb_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ext_port_callback(void *user_arg)
|
||||||
|
{
|
||||||
|
HUB_DRIVER_ENTER_CRITICAL();
|
||||||
|
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_ACTION_EXT_PORT;
|
||||||
|
HUB_DRIVER_EXIT_CRITICAL();
|
||||||
|
p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, false, p_hub_driver_obj->constant.proc_req_cb_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ext_port_event_callback(ext_port_event_data_t *event_data, void *arg)
|
||||||
|
{
|
||||||
|
switch (event_data->event) {
|
||||||
|
case EXT_PORT_CONNECTED:
|
||||||
|
// First reset is done by ext_port logic
|
||||||
|
usb_speed_t port_speed;
|
||||||
|
|
||||||
|
if (ext_hub_port_get_speed(event_data->connected.ext_hub_hdl,
|
||||||
|
event_data->connected.parent_port_num,
|
||||||
|
&port_speed) != ESP_OK) {
|
||||||
|
goto new_ds_dev_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: IDF-10023 Move responsibility of parent-child tree building to Hub Driver instead of USBH
|
||||||
|
usb_device_info_t parent_dev_info;
|
||||||
|
ESP_ERROR_CHECK(usbh_dev_get_info(event_data->connected.parent_dev_hdl, &parent_dev_info));
|
||||||
|
if (parent_dev_info.speed == USB_SPEED_HIGH) {
|
||||||
|
if (port_speed != parent_dev_info.speed) {
|
||||||
|
ESP_LOGE(HUB_DRIVER_TAG, "Connected device is %s, transaction translator (TT) is not supported",
|
||||||
|
(char *[]) {
|
||||||
|
"LS", "FS", "HS"
|
||||||
|
}[port_speed]);
|
||||||
|
goto new_ds_dev_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (!CONFIG_USB_HOST_EXT_PORT_SUPPORT_LS)
|
||||||
|
if (port_speed == USB_SPEED_LOW) {
|
||||||
|
ESP_LOGE(HUB_DRIVER_TAG, "Connected %s-speed device, not supported",
|
||||||
|
(char *[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
goto new_ds_dev_err;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_USB_HOST_EXT_PORT_SUPPORT_LS
|
||||||
|
|
||||||
|
if (new_dev_tree_node(event_data->connected.parent_dev_hdl, event_data->connected.parent_port_num, port_speed) != ESP_OK) {
|
||||||
|
ESP_LOGE(HUB_DRIVER_TAG, "Failed to add new downstream device");
|
||||||
|
goto new_ds_dev_err;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
new_ds_dev_err:
|
||||||
|
ext_hub_port_disable(event_data->connected.ext_hub_hdl, event_data->connected.parent_port_num);
|
||||||
|
break;
|
||||||
|
case EXT_PORT_RESET_COMPLETED:
|
||||||
|
ESP_ERROR_CHECK(dev_tree_node_reset_completed(event_data->reset_completed.parent_dev_hdl, event_data->reset_completed.parent_port_num));
|
||||||
|
break;
|
||||||
|
case EXT_PORT_DISCONNECTED:
|
||||||
|
// The node could be freed by now, no need to verify the result here
|
||||||
|
dev_tree_node_dev_gone(event_data->disconnected.parent_dev_hdl, event_data->disconnected.parent_port_num);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Should never occur
|
||||||
|
abort();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // ENABLE_USB_HUBS
|
#endif // ENABLE_USB_HUBS
|
||||||
|
|
||||||
// ---------------------- Handlers -------------------------
|
// ---------------------- Handlers -------------------------
|
||||||
@@ -479,10 +545,20 @@ esp_err_t hub_install(hub_config_t *hub_config, void **client_ret)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_USB_HUBS
|
#if ENABLE_USB_HUBS
|
||||||
|
// Install External Port driver
|
||||||
|
ext_port_driver_config_t ext_port_config = {
|
||||||
|
.proc_req_cb = ext_port_callback,
|
||||||
|
.event_cb = ext_port_event_callback,
|
||||||
|
};
|
||||||
|
ret = ext_port_install(&ext_port_config);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
goto err_ext_port;
|
||||||
|
}
|
||||||
|
|
||||||
// Install External HUB driver
|
// Install External HUB driver
|
||||||
ext_hub_config_t ext_hub_config = {
|
ext_hub_config_t ext_hub_config = {
|
||||||
.proc_req_cb = ext_hub_callback,
|
.proc_req_cb = ext_hub_callback,
|
||||||
.port_driver = NULL,
|
.port_driver = ext_port_get_driver(),
|
||||||
};
|
};
|
||||||
ret = ext_hub_install(&ext_hub_config);
|
ret = ext_hub_install(&ext_hub_config);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
@@ -536,6 +612,8 @@ err:
|
|||||||
#if ENABLE_USB_HUBS
|
#if ENABLE_USB_HUBS
|
||||||
ext_hub_uninstall();
|
ext_hub_uninstall();
|
||||||
err_ext_hub:
|
err_ext_hub:
|
||||||
|
ext_port_uninstall();
|
||||||
|
err_ext_port:
|
||||||
#endif // ENABLE_USB_HUBS
|
#endif // ENABLE_USB_HUBS
|
||||||
heap_caps_free(hub_driver_obj);
|
heap_caps_free(hub_driver_obj);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -552,6 +630,7 @@ esp_err_t hub_uninstall(void)
|
|||||||
|
|
||||||
#if ENABLE_USB_HUBS
|
#if ENABLE_USB_HUBS
|
||||||
ESP_ERROR_CHECK(ext_hub_uninstall());
|
ESP_ERROR_CHECK(ext_hub_uninstall());
|
||||||
|
ESP_ERROR_CHECK(ext_port_uninstall());
|
||||||
#endif // ENABLE_USB_HUBS
|
#endif // ENABLE_USB_HUBS
|
||||||
|
|
||||||
ESP_ERROR_CHECK(hcd_port_deinit(hub_driver_obj->constant.root_port_hdl));
|
ESP_ERROR_CHECK(hcd_port_deinit(hub_driver_obj->constant.root_port_hdl));
|
||||||
@@ -739,10 +818,7 @@ esp_err_t hub_process(void)
|
|||||||
while (action_flags) {
|
while (action_flags) {
|
||||||
#if ENABLE_USB_HUBS
|
#if ENABLE_USB_HUBS
|
||||||
if (action_flags & HUB_DRIVER_ACTION_EXT_PORT) {
|
if (action_flags & HUB_DRIVER_ACTION_EXT_PORT) {
|
||||||
ESP_LOGW(HUB_DRIVER_TAG, "ext_port_process() has not been implemented yet");
|
|
||||||
/*
|
|
||||||
ESP_ERROR_CHECK(ext_port_process());
|
ESP_ERROR_CHECK(ext_port_process());
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
if (action_flags & HUB_DRIVER_ACTION_EXT_HUB) {
|
if (action_flags & HUB_DRIVER_ACTION_EXT_HUB) {
|
||||||
ESP_ERROR_CHECK(ext_hub_process());
|
ESP_ERROR_CHECK(ext_hub_process());
|
||||||
|
@@ -43,6 +43,28 @@ typedef enum {
|
|||||||
USB_B_REQUEST_HUB_STOP_TT = 0x0B, /**< Stop TT. */
|
USB_B_REQUEST_HUB_STOP_TT = 0x0B, /**< Stop TT. */
|
||||||
} usb_hub_class_request_t ;
|
} usb_hub_class_request_t ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief USB Hub Port state
|
||||||
|
*
|
||||||
|
* See USB 2.0 spec, 11.5.1 Downstream Facing Port State Descriptions
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
USB_PORT_STATE_NOT_CONFIGURED = 0x00, /**< The hub is not configured. A port transitions to and remains in this state whenever the value of the hub configuration is zero. */
|
||||||
|
USB_PORT_STATE_POWERED_OFF, /**< Powered_off: Port requires explicit request to transition. */
|
||||||
|
USB_PORT_STATE_DISCONNECTED, /**< In the Disconnected state, the port’s differential transmitter and receiver are disabled and only connection detection is possible.*/
|
||||||
|
USB_PORT_STATE_DISABLED, /**< A port in the Disabled state will not propagate signaling in either the upstream or the downstream direction */
|
||||||
|
USB_PORT_STATE_RESETTING, /**< The duration of the Resetting state is nominally 10 ms to 20 ms (10 ms is preferred). */
|
||||||
|
USB_PORT_STATE_ENABLED, /**< While in this state, the output of the port’s differential receiver is available to the Hub Repeater so that appropriate signaling transitions can establish upstream connectivity*/
|
||||||
|
USB_PORT_STATE_TRANSMIT, /**< This state is entered from the Enabled state on the transition of the Hub Repeater to the WFEOPFU state */
|
||||||
|
USB_PORT_STATE_TRANSMIT_R, /**< When in this state, the port repeats the resume ‘K’ at the upstream facing port to the downstream facing port. */
|
||||||
|
USB_PORT_STATE_SUSPENDED, /**< While a port is in the Suspended state, the port's differential transmitter is disabled. */
|
||||||
|
USB_PORT_STATE_RESUMING, /**< While in this state, the hub drives a 'K' on the port. */
|
||||||
|
USB_PORT_STATE_SEND_EOR, /**< This state is entered from the Resuming state if the 20 ms timer expires. */
|
||||||
|
USB_PORT_STATE_RESTART_S, /**< A port enters the Restart_S state from the Suspended state when an SE0 or ‘K’ is seen at the port and the Receiver is in the Suspended state */
|
||||||
|
USB_PORT_STATE_RESTART_E, /**< A port enters the Restart_E state from the Enabled state when an ‘SE0’ or ‘K’ is seen at the port and the Receiver is in the Suspended state. */
|
||||||
|
USB_PORT_STATE_TESTING, /**< A port transitions to this state from any state when the port sees SetTest. */
|
||||||
|
} usb_hub_port_state_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief USB Hub Port feature selector codes
|
* @brief USB Hub Port feature selector codes
|
||||||
*
|
*
|
||||||
|
@@ -46,7 +46,7 @@ typedef struct {
|
|||||||
esp_err_t (*active)(void *port_hdl);
|
esp_err_t (*active)(void *port_hdl);
|
||||||
esp_err_t (*disable)(void *port_hdl);
|
esp_err_t (*disable)(void *port_hdl);
|
||||||
esp_err_t (*gone)(void *port_hdl);
|
esp_err_t (*gone)(void *port_hdl);
|
||||||
esp_err_t (*free)(void *port_hdl);
|
esp_err_t (*del)(void *port_hdl);
|
||||||
esp_err_t (*get_speed)(void *por_hdl, usb_speed_t *speed);
|
esp_err_t (*get_speed)(void *por_hdl, usb_speed_t *speed);
|
||||||
esp_err_t (*get_status)(void *port_hdl);
|
esp_err_t (*get_status)(void *port_hdl);
|
||||||
esp_err_t (*set_status)(void *port_hdl, const usb_port_status_t *status);
|
esp_err_t (*set_status)(void *port_hdl, const usb_port_status_t *status);
|
||||||
@@ -278,7 +278,7 @@ esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num,
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Port's feature set successfully
|
* - ESP_OK: Port's feature set successfully
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed;
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
*/
|
*/
|
||||||
@@ -293,7 +293,7 @@ esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_nu
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Port's feature cleared successfully
|
* - ESP_OK: Port's feature cleared successfully
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed;
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
*/
|
*/
|
||||||
@@ -310,7 +310,7 @@ esp_err_t ext_hub_clear_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Port's status obtained successfully
|
* - ESP_OK: Port's status obtained successfully
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed;
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
*/
|
*/
|
||||||
|
143
components/usb/private_include/ext_port.h
Normal file
143
components/usb/private_include/ext_port.h
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "ext_hub.h"
|
||||||
|
#include "usb/usb_types_stack.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct ext_port_s *ext_port_hdl_t;
|
||||||
|
|
||||||
|
// ------------------------------ Events ---------------------------------------
|
||||||
|
typedef enum {
|
||||||
|
EXT_PORT_CONNECTED = 0x01, /**< Port has a device connection event */
|
||||||
|
EXT_PORT_RESET_COMPLETED, /**< Port has completed the reset routine */
|
||||||
|
EXT_PORT_DISCONNECTED, /**< Port has a device disconnection event */
|
||||||
|
} ext_port_event_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Event data object for External Port driver events
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
ext_port_event_t event;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
ext_hub_handle_t ext_hub_hdl; /**< Ports' parent external Hub handle */
|
||||||
|
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
||||||
|
uint8_t parent_port_num; /**< Ports' parent port number */
|
||||||
|
} connected; /**< EXT_PORT_CONNECTED event specific data */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
||||||
|
uint8_t parent_port_num; /**< Ports' parent port number */
|
||||||
|
} reset_completed; /**< EXT_PORT_RESET_COMPLETED event specific data */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
||||||
|
uint8_t parent_port_num; /**< Ports' parent port number */
|
||||||
|
} disconnected; /**< EXT_PORT_DISCONNECTED event specific data */
|
||||||
|
};
|
||||||
|
} ext_port_event_data_t;
|
||||||
|
|
||||||
|
// ------------------------------ Callbacks ------------------------------------
|
||||||
|
/**
|
||||||
|
* @brief Callback used to indicate that the External Port Driver requires process callback
|
||||||
|
*
|
||||||
|
* @note For the Hub Driver only
|
||||||
|
*/
|
||||||
|
typedef void (*ext_port_cb_t)(void *user_arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback used to indicate that the External Port driver has an event
|
||||||
|
*
|
||||||
|
* @note For the Hub Driver only
|
||||||
|
*/
|
||||||
|
typedef void (*ext_port_event_cb_t)(ext_port_event_data_t *event_data, void *arg);
|
||||||
|
|
||||||
|
// ----------------- External Port Driver configuration ------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief External Port driver configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
ext_port_cb_t proc_req_cb; /**< External Port process callback */
|
||||||
|
void *proc_req_cb_arg; /**< External Port process callback argument */
|
||||||
|
ext_port_event_cb_t event_cb; /**< External Port event callback */
|
||||||
|
void *event_cb_arg; /**< External Port event callback argument */
|
||||||
|
} ext_port_driver_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief External Port configuration
|
||||||
|
*
|
||||||
|
* Structure is used to create new port
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
ext_hub_handle_t ext_hub_hdl; /**< Ports' parent external Hub handle */
|
||||||
|
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
||||||
|
uint8_t parent_port_num; /**< Ports' parent port number */
|
||||||
|
uint16_t port_power_delay_ms; /**< Ports' Power on time to Power Good, ms */
|
||||||
|
} ext_port_config_t;
|
||||||
|
|
||||||
|
// -------------------- External Port Processing Functions ---------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Install the External Port Driver
|
||||||
|
*
|
||||||
|
* @note This function should only be called from the Hub Driver
|
||||||
|
*
|
||||||
|
* @param[in] config External Port Driver configuration
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_NOT_ALLOWED: The Driver was already installed
|
||||||
|
* - ESP_ERR_NO_MEM: Unable to install the Driver, no memory
|
||||||
|
* - ESP_OK: The Driver has been installed successfully
|
||||||
|
*/
|
||||||
|
esp_err_t ext_port_install(const ext_port_driver_config_t *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Uninstall the External Port Driver
|
||||||
|
*
|
||||||
|
* @note This function should only be called from the Hub Driver
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_NOT_ALLOWED: The Driver was not installed
|
||||||
|
* - ESP_ERR_INVALID_STATE: The Driver has ports in the pending list and can't be uninstalled
|
||||||
|
* - ESP_OK: The Driver has been uninstall successfully
|
||||||
|
*/
|
||||||
|
esp_err_t ext_port_uninstall(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief External Port Driver's process function
|
||||||
|
*
|
||||||
|
* @note This function should only be called from the Hub Driver
|
||||||
|
*
|
||||||
|
* External Port Driver process function that must be called repeatedly to process the driver's actions and events.
|
||||||
|
* If blocking, the caller can block on the notification callback of source USB_PROC_REQ_SOURCE_HUB
|
||||||
|
* to run this function.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_NOT_ALLOWED: The Driver was not installed
|
||||||
|
* - ESP_OK: The Driver processed completed
|
||||||
|
*/
|
||||||
|
esp_err_t ext_port_process(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns External Port Driver's API
|
||||||
|
*
|
||||||
|
* @note This is a specific API for the External Hub Driver to handle the ports.
|
||||||
|
* @return
|
||||||
|
* - NULL: The Driver has not been installed
|
||||||
|
* - not NULL: Pointer to the External Port Driver API
|
||||||
|
*/
|
||||||
|
const ext_hub_port_driver_t *ext_port_get_driver(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@@ -134,8 +134,8 @@ esp_err_t hub_root_stop(void);
|
|||||||
*
|
*
|
||||||
* @note This function should only be called from the Host Library task
|
* @note This function should only be called from the Host Library task
|
||||||
*
|
*
|
||||||
* @param[in] parent_dev_hdl
|
* @param[in] parent_dev_hdl Parent device handle (is used to get the External Hub handle)
|
||||||
* @param[in] parent_port_num
|
* @param[in] parent_port_num Parent number (is used to specify the External Port)
|
||||||
* @param[in] dev_uid Device's unique ID
|
* @param[in] dev_uid Device's unique ID
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
@@ -151,9 +151,8 @@ esp_err_t hub_port_recycle(usb_device_handle_t parent_dev_hdl, uint8_t parent_po
|
|||||||
*
|
*
|
||||||
* @note This function should only be called from the Host Library task
|
* @note This function should only be called from the Host Library task
|
||||||
*
|
*
|
||||||
* @param[in] parent_dev_hdl
|
* @param[in] parent_dev_hdl Parent device handle (is used to get the External Hub handle)
|
||||||
* @param[in] parent_port_num
|
* @param[in] parent_port_num Parent number (is used to specify the External Port)
|
||||||
*
|
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Port reset successful
|
* - ESP_OK: Port reset successful
|
||||||
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
|
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
|
||||||
@@ -198,6 +197,8 @@ esp_err_t hub_port_disable(usb_device_handle_t parent_dev_hdl, uint8_t parent_po
|
|||||||
*
|
*
|
||||||
* If device is has a HUB class, then it will be added as External Hub to Hub Driver.
|
* If device is has a HUB class, then it will be added as External Hub to Hub Driver.
|
||||||
*
|
*
|
||||||
|
* @note This function should only be called from the Host Library task
|
||||||
|
*
|
||||||
* @param[in] dev_addr Device bus address
|
* @param[in] dev_addr Device bus address
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
@@ -211,6 +212,8 @@ esp_err_t hub_notify_new_dev(uint8_t dev_addr);
|
|||||||
*
|
*
|
||||||
* If the device was an External Hub, then it will be removed from the Hub Driver.
|
* If the device was an External Hub, then it will be removed from the Hub Driver.
|
||||||
*
|
*
|
||||||
|
* @note This function should only be called from the Host Library task
|
||||||
|
*
|
||||||
* @param[in] dev_addr Device bus address
|
* @param[in] dev_addr Device bus address
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
@@ -222,6 +225,8 @@ esp_err_t hub_notify_dev_gone(uint8_t dev_addr);
|
|||||||
/**
|
/**
|
||||||
* @brief Notify Hub driver that all devices should be freed
|
* @brief Notify Hub driver that all devices should be freed
|
||||||
*
|
*
|
||||||
|
* @note This function should only be called from the Host Library task
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: All the devices can be freed
|
* - ESP_OK: All the devices can be freed
|
||||||
* - ESP_ERR_INVALID_STATE: Hub driver is not in a correct state
|
* - ESP_ERR_INVALID_STATE: Hub driver is not in a correct state
|
||||||
|
@@ -6,3 +6,4 @@ CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
|||||||
# CONFIG_UNITY_ENABLE_FLOAT is not set
|
# CONFIG_UNITY_ENABLE_FLOAT is not set
|
||||||
# CONFIG_UNITY_ENABLE_DOUBLE is not set
|
# CONFIG_UNITY_ENABLE_DOUBLE is not set
|
||||||
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||||
|
CONFIG_USB_HOST_HUBS_SUPPORTED=y
|
||||||
|
@@ -6,3 +6,4 @@ CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
|||||||
# CONFIG_UNITY_ENABLE_FLOAT is not set
|
# CONFIG_UNITY_ENABLE_FLOAT is not set
|
||||||
# CONFIG_UNITY_ENABLE_DOUBLE is not set
|
# CONFIG_UNITY_ENABLE_DOUBLE is not set
|
||||||
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||||
|
CONFIG_USB_HOST_HUBS_SUPPORTED=y
|
||||||
|
@@ -1050,7 +1050,6 @@ esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl)
|
|||||||
if (dev_obj->dynamic.open_count == 0) {
|
if (dev_obj->dynamic.open_count == 0) {
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
assert(dev_obj->dynamic.num_ctrl_xfers_inflight == 0); // There cannot be any control transfer in-flight
|
assert(dev_obj->dynamic.num_ctrl_xfers_inflight == 0); // There cannot be any control transfer in-flight
|
||||||
assert(!dev_obj->dynamic.flags.waiting_free); // This can only be set when open_count reaches 0
|
|
||||||
if (dev_obj->dynamic.flags.is_gone || dev_obj->dynamic.flags.waiting_free) {
|
if (dev_obj->dynamic.flags.is_gone || dev_obj->dynamic.flags.waiting_free) {
|
||||||
// Device is already gone or is awaiting to be freed. Trigger the USBH process to free the device
|
// Device is already gone or is awaiting to be freed. Trigger the USBH process to free the device
|
||||||
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_FREE);
|
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_FREE);
|
||||||
|
@@ -2,3 +2,4 @@
|
|||||||
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
||||||
#
|
#
|
||||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||||
|
CONFIG_USB_HOST_HUBS_SUPPORTED=y
|
||||||
|
4
examples/peripherals/usb/host/hid/sdkconfig.defaults
Normal file
4
examples/peripherals/usb/host/hid/sdkconfig.defaults
Normal file
@@ -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_USB_HOST_HUBS_SUPPORTED=y
|
@@ -28,6 +28,11 @@ static const char *TAG = "example";
|
|||||||
#define APP_QUIT_PIN GPIO_NUM_0 // BOOT button on most boards
|
#define APP_QUIT_PIN GPIO_NUM_0 // BOOT button on most boards
|
||||||
#define BUFFER_SIZE 4096 // The read/write performance can be improved with larger buffer for the cost of RAM, 4kB is enough for most usecases
|
#define BUFFER_SIZE 4096 // The read/write performance can be improved with larger buffer for the cost of RAM, 4kB is enough for most usecases
|
||||||
|
|
||||||
|
// IMPORTANT NOTE
|
||||||
|
// MSC Class Driver is not fully support connecting devices through external Hub.
|
||||||
|
// TODO: Remove this line after MSC Class Driver will support it
|
||||||
|
static bool dev_present = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Application Queue and its messages ID
|
* @brief Application Queue and its messages ID
|
||||||
*/
|
*/
|
||||||
@@ -77,7 +82,7 @@ static void gpio_cb(void *arg)
|
|||||||
static void msc_event_cb(const msc_host_event_t *event, void *arg)
|
static void msc_event_cb(const msc_host_event_t *event, void *arg)
|
||||||
{
|
{
|
||||||
if (event->event == MSC_DEVICE_CONNECTED) {
|
if (event->event == MSC_DEVICE_CONNECTED) {
|
||||||
ESP_LOGI(TAG, "MSC device connected");
|
ESP_LOGI(TAG, "MSC device connected (usb_addr=%d)", event->device.address);
|
||||||
app_message_t message = {
|
app_message_t message = {
|
||||||
.id = APP_DEVICE_CONNECTED,
|
.id = APP_DEVICE_CONNECTED,
|
||||||
.data.new_dev_address = event->device.address,
|
.data.new_dev_address = event->device.address,
|
||||||
@@ -271,47 +276,56 @@ void app_main(void)
|
|||||||
xQueueReceive(app_queue, &msg, portMAX_DELAY);
|
xQueueReceive(app_queue, &msg, portMAX_DELAY);
|
||||||
|
|
||||||
if (msg.id == APP_DEVICE_CONNECTED) {
|
if (msg.id == APP_DEVICE_CONNECTED) {
|
||||||
// 1. MSC flash drive connected. Open it and map it to Virtual File System
|
if (dev_present) {
|
||||||
ESP_ERROR_CHECK(msc_host_install_device(msg.data.new_dev_address, &msc_device));
|
ESP_LOGW(TAG, "MSC Example handles only one device at a time");
|
||||||
const esp_vfs_fat_mount_config_t mount_config = {
|
} else {
|
||||||
.format_if_mount_failed = false,
|
// 0. Change flag
|
||||||
.max_files = 3,
|
dev_present = true;
|
||||||
.allocation_unit_size = 8192,
|
// 1. MSC flash drive connected. Open it and map it to Virtual File System
|
||||||
};
|
ESP_ERROR_CHECK(msc_host_install_device(msg.data.new_dev_address, &msc_device));
|
||||||
ESP_ERROR_CHECK(msc_host_vfs_register(msc_device, MNT_PATH, &mount_config, &vfs_handle));
|
const esp_vfs_fat_mount_config_t mount_config = {
|
||||||
|
.format_if_mount_failed = false,
|
||||||
|
.max_files = 3,
|
||||||
|
.allocation_unit_size = 8192,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(msc_host_vfs_register(msc_device, MNT_PATH, &mount_config, &vfs_handle));
|
||||||
|
|
||||||
// 2. Print information about the connected disk
|
// 2. Print information about the connected disk
|
||||||
msc_host_device_info_t info;
|
msc_host_device_info_t info;
|
||||||
ESP_ERROR_CHECK(msc_host_get_device_info(msc_device, &info));
|
ESP_ERROR_CHECK(msc_host_get_device_info(msc_device, &info));
|
||||||
msc_host_print_descriptors(msc_device);
|
msc_host_print_descriptors(msc_device);
|
||||||
print_device_info(&info);
|
print_device_info(&info);
|
||||||
|
|
||||||
// 3. List all the files in root directory
|
// 3. List all the files in root directory
|
||||||
ESP_LOGI(TAG, "ls command output:");
|
ESP_LOGI(TAG, "ls command output:");
|
||||||
struct dirent *d;
|
struct dirent *d;
|
||||||
DIR *dh = opendir(MNT_PATH);
|
DIR *dh = opendir(MNT_PATH);
|
||||||
assert(dh);
|
assert(dh);
|
||||||
while ((d = readdir(dh)) != NULL) {
|
while ((d = readdir(dh)) != NULL) {
|
||||||
printf("%s\n", d->d_name);
|
printf("%s\n", d->d_name);
|
||||||
|
}
|
||||||
|
closedir(dh);
|
||||||
|
|
||||||
|
// 4. The disk is mounted to Virtual File System, perform some basic demo file operation
|
||||||
|
file_operations();
|
||||||
|
|
||||||
|
// 5. Perform speed test
|
||||||
|
speed_test();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Example finished, you can disconnect the USB flash drive");
|
||||||
}
|
}
|
||||||
closedir(dh);
|
|
||||||
|
|
||||||
// 4. The disk is mounted to Virtual File System, perform some basic demo file operation
|
|
||||||
file_operations();
|
|
||||||
|
|
||||||
// 5. Perform speed test
|
|
||||||
speed_test();
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Example finished, you can disconnect the USB flash drive");
|
|
||||||
}
|
}
|
||||||
if ((msg.id == APP_DEVICE_DISCONNECTED) || (msg.id == APP_QUIT)) {
|
if ((msg.id == APP_DEVICE_DISCONNECTED) || (msg.id == APP_QUIT)) {
|
||||||
if (vfs_handle) {
|
if (dev_present) {
|
||||||
ESP_ERROR_CHECK(msc_host_vfs_unregister(vfs_handle));
|
dev_present = false;
|
||||||
vfs_handle = NULL;
|
if (vfs_handle) {
|
||||||
}
|
ESP_ERROR_CHECK(msc_host_vfs_unregister(vfs_handle));
|
||||||
if (msc_device) {
|
vfs_handle = NULL;
|
||||||
ESP_ERROR_CHECK(msc_host_uninstall_device(msc_device));
|
}
|
||||||
msc_device = NULL;
|
if (msc_device) {
|
||||||
|
ESP_ERROR_CHECK(msc_host_uninstall_device(msc_device));
|
||||||
|
msc_device = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (msg.id == APP_QUIT) {
|
if (msg.id == APP_QUIT) {
|
||||||
// This will cause the usb_task to exit
|
// This will cause the usb_task to exit
|
||||||
|
4
examples/peripherals/usb/host/msc/sdkconfig.defaults
Normal file
4
examples/peripherals/usb/host/msc/sdkconfig.defaults
Normal file
@@ -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_USB_HOST_HUBS_SUPPORTED=y
|
@@ -5,19 +5,21 @@
|
|||||||
|
|
||||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
This example demonstrates the basic usage of the [USB Host Library API](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html) by implementing a pseudo class driver and a Host Library daemon task. The example does the following:
|
This example demonstrates the basic usage of the [USB Host Library API](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html) by implementing a pseudo class driver and a Host Library task. The example does the following:
|
||||||
|
|
||||||
1. Install Host Library and register a client
|
1. Install Host Library and register a client
|
||||||
2. Waits for a device connection
|
2. Waits for a device connection
|
||||||
3. Prints the device's information (such as device/configuration/string descriptors)
|
3. Prints the device's information (such as device/configuration/string descriptors)
|
||||||
4. Waits for the device to disconnect
|
4. Waits for the device to disconnect
|
||||||
5. Deregister the client and uninstall the Host Library
|
5. Repeats steps 2 to 4 until a user pressess a button, which quits the `app`
|
||||||
|
6. If the button has been pressed, while a USB device is still connected, the user will be prompted to remove the device and push the button again to quit the `app`
|
||||||
|
7. Deregister the client, uninstall the Host Library and quit the `app`
|
||||||
|
|
||||||
The example demonstrates the following aspects of the USB Host Library API:
|
The example demonstrates the following aspects of the USB Host Library API:
|
||||||
|
|
||||||
- How to use the Library API to:
|
- How to use the Library API to:
|
||||||
- Install and uninstall the USB Host Library
|
- Install and uninstall the USB Host Library
|
||||||
- Run the library event handler function a daemon task
|
- Run the library event handler function and usb host library task
|
||||||
- How to handle library events
|
- How to handle library events
|
||||||
- How to use the Client API from a client task to:
|
- How to use the Client API from a client task to:
|
||||||
- Register and deregister a client of the USB Host Library
|
- Register and deregister a client of the USB Host Library
|
||||||
@@ -30,7 +32,7 @@ The example demonstrates the following aspects of the USB Host Library API:
|
|||||||
|
|
||||||
### Hardware Required
|
### Hardware Required
|
||||||
|
|
||||||
An ESP board that supports USB-OTG. The example uses the ESP's internal USB PHY, however the internal USB PHY's pins will need to be connected to a USB port (i.e., a USB breakout board) as follows:
|
An ESP board that has a push button and supports USB-OTG. The example uses the ESP's internal USB PHY, however the internal USB PHY's pins will need to be connected to a USB port (i.e., a USB breakout board) as follows:
|
||||||
|
|
||||||
- GND and 5V signals of the ESP board to the GND and 5V lines of the USB port
|
- GND and 5V signals of the ESP board to the GND and 5V lines of the USB port
|
||||||
- GPIO 19 to D-
|
- GPIO 19 to D-
|
||||||
@@ -43,6 +45,7 @@ idf.py menuconfig
|
|||||||
```
|
```
|
||||||
|
|
||||||
* The USB Host Stack has a maximum supported transfer size for control transfer during device enumeration. This size is specified via the USB_HOST_CONTROL_TRANSFER_MAX_SIZE configuration option and has a default value of 256 bytes. Therefore, if devices with length config/string descriptors are used, users may want to increase the size of this configuration.
|
* The USB Host Stack has a maximum supported transfer size for control transfer during device enumeration. This size is specified via the USB_HOST_CONTROL_TRANSFER_MAX_SIZE configuration option and has a default value of 256 bytes. Therefore, if devices with length config/string descriptors are used, users may want to increase the size of this configuration.
|
||||||
|
* Push button GPIO selection
|
||||||
|
|
||||||
### Build and Flash
|
### Build and Flash
|
||||||
|
|
||||||
@@ -61,14 +64,17 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
|
|||||||
## Example Output
|
## Example Output
|
||||||
|
|
||||||
```
|
```
|
||||||
I (261) cpu_start: Starting scheduler on PRO CPU.
|
I (305) main_task: Started on CPU0
|
||||||
I (267) DAEMON: Installing USB Host Library
|
I (315) main_task: Calling app_main()
|
||||||
I (297) CLASS: Registering Client
|
I (315) USB host lib: USB host library example
|
||||||
I (5067) CLASS: Opening device at address 1
|
I (315) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
|
||||||
I (5067) CLASS: Getting device information
|
I (325) USB host lib: Installing USB Host Library
|
||||||
I (5067) CLASS: Full speed
|
I (365) CLASS: Registering Client
|
||||||
I (5067) CLASS: bConfigurationValue 1
|
I (745) CLASS: Opening device at address 1
|
||||||
I (5067) CLASS: Getting device descriptor
|
I (745) CLASS: Getting device information
|
||||||
|
I (745) CLASS: Full speed
|
||||||
|
I (745) CLASS: bConfigurationValue 1
|
||||||
|
I (745) CLASS: Getting device descriptor
|
||||||
*** Device descriptor ***
|
*** Device descriptor ***
|
||||||
bLength 18
|
bLength 18
|
||||||
bDescriptorType 1
|
bDescriptorType 1
|
||||||
@@ -84,7 +90,7 @@ iManufacturer 1
|
|||||||
iProduct 2
|
iProduct 2
|
||||||
iSerialNumber 3
|
iSerialNumber 3
|
||||||
bNumConfigurations 1
|
bNumConfigurations 1
|
||||||
I (5097) CLASS: Getting config descriptor
|
I (775) CLASS: Getting config descriptor
|
||||||
*** Configuration descriptor ***
|
*** Configuration descriptor ***
|
||||||
bLength 9
|
bLength 9
|
||||||
bDescriptorType 2
|
bDescriptorType 2
|
||||||
@@ -153,12 +159,20 @@ bMaxPower 500mA
|
|||||||
bmAttributes 0x2 BULK
|
bmAttributes 0x2 BULK
|
||||||
wMaxPacketSize 64
|
wMaxPacketSize 64
|
||||||
bInterval 1
|
bInterval 1
|
||||||
I (5227) CLASS: Getting Manufacturer string descriptor
|
I (855) CLASS: Getting Manufacturer string descriptor
|
||||||
Espressif
|
Espressif
|
||||||
I (5237) CLASS: Getting Product string descriptor
|
I (855) CLASS: Getting Product string descriptor
|
||||||
USB JTAG/serial debug unit
|
USB JTAG/serial debug unit
|
||||||
I (5247) CLASS: Getting Serial Number string descriptor
|
I (865) CLASS: Getting Serial Number string descriptor
|
||||||
7C:DF:A1:E0:10:50
|
7C:DF:A1:E0:10:50
|
||||||
|
W (2855) USB host lib: To shutdown example, remove all USB devices and press button again.
|
||||||
|
E (6135) USBH: Device 1 gone
|
||||||
|
I (9545) CLASS: Deregistering Client
|
||||||
|
I (9545) USB host lib: No more clients
|
||||||
|
I (9545) USB host lib: All devices marked as free
|
||||||
|
I (9545) USB host lib: No more clients and devices
|
||||||
|
I (9645) USB host lib: End of the example
|
||||||
|
I (9645) main_task: Returned from app_main()
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
@@ -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"
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
PRIV_REQUIRES usb
|
PRIV_REQUIRES usb driver
|
||||||
)
|
)
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
|
help
|
||||||
|
GPIO pin number to be used as APP_QUIT button.
|
||||||
|
|
||||||
|
endmenu
|
@@ -12,99 +12,139 @@
|
|||||||
|
|
||||||
#define CLIENT_NUM_EVENT_MSG 5
|
#define CLIENT_NUM_EVENT_MSG 5
|
||||||
|
|
||||||
#define ACTION_OPEN_DEV 0x01
|
typedef enum {
|
||||||
#define ACTION_GET_DEV_INFO 0x02
|
ACTION_OPEN_DEV = (1 << 0),
|
||||||
#define ACTION_GET_DEV_DESC 0x04
|
ACTION_GET_DEV_INFO = (1 << 1),
|
||||||
#define ACTION_GET_CONFIG_DESC 0x08
|
ACTION_GET_DEV_DESC = (1 << 2),
|
||||||
#define ACTION_GET_STR_DESC 0x10
|
ACTION_GET_CONFIG_DESC = (1 << 3),
|
||||||
#define ACTION_CLOSE_DEV 0x20
|
ACTION_GET_STR_DESC = (1 << 4),
|
||||||
#define ACTION_EXIT 0x40
|
ACTION_CLOSE_DEV = (1 << 5),
|
||||||
|
} action_t;
|
||||||
|
|
||||||
|
#define DEV_MAX_COUNT 128
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
usb_host_client_handle_t client_hdl;
|
usb_host_client_handle_t client_hdl;
|
||||||
uint8_t dev_addr;
|
uint8_t dev_addr;
|
||||||
usb_device_handle_t dev_hdl;
|
usb_device_handle_t dev_hdl;
|
||||||
uint32_t actions;
|
action_t actions;
|
||||||
|
} usb_device_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t unhandled_devices: 1; /**< Device has unhandled devices */
|
||||||
|
uint8_t shutdown: 1; /**< */
|
||||||
|
uint8_t reserved6: 6; /**< Reserved */
|
||||||
|
};
|
||||||
|
uint8_t val; /**< Class drivers' flags value */
|
||||||
|
} flags; /**< Class drivers' flags */
|
||||||
|
usb_device_t device[DEV_MAX_COUNT]; /**< Class drivers' static array of devices */
|
||||||
|
} mux_protected; /**< Mutex protected members. Must be protected by the Class mux_lock when accessed */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
usb_host_client_handle_t client_hdl;
|
||||||
|
SemaphoreHandle_t mux_lock; /**< Mutex for protected members */
|
||||||
|
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
|
||||||
} class_driver_t;
|
} class_driver_t;
|
||||||
|
|
||||||
static const char *TAG = "CLASS";
|
static const char *TAG = "CLASS";
|
||||||
|
static class_driver_t *s_driver_obj;
|
||||||
|
|
||||||
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
|
static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
|
||||||
{
|
{
|
||||||
class_driver_t *driver_obj = (class_driver_t *)arg;
|
class_driver_t *driver_obj = (class_driver_t *)arg;
|
||||||
switch (event_msg->event) {
|
switch (event_msg->event) {
|
||||||
case USB_HOST_CLIENT_EVENT_NEW_DEV:
|
case USB_HOST_CLIENT_EVENT_NEW_DEV:
|
||||||
if (driver_obj->dev_addr == 0) {
|
// Save the device address
|
||||||
driver_obj->dev_addr = event_msg->new_dev.address;
|
xSemaphoreTake(driver_obj->constant.mux_lock, portMAX_DELAY);
|
||||||
//Open the device next
|
driver_obj->mux_protected.device[event_msg->new_dev.address].dev_addr = event_msg->new_dev.address;
|
||||||
driver_obj->actions |= ACTION_OPEN_DEV;
|
driver_obj->mux_protected.device[event_msg->new_dev.address].dev_hdl = NULL;
|
||||||
}
|
// Open the device next
|
||||||
|
driver_obj->mux_protected.device[event_msg->new_dev.address].actions |= ACTION_OPEN_DEV;
|
||||||
|
// Set flag
|
||||||
|
driver_obj->mux_protected.flags.unhandled_devices = 1;
|
||||||
|
xSemaphoreGive(driver_obj->constant.mux_lock);
|
||||||
break;
|
break;
|
||||||
case USB_HOST_CLIENT_EVENT_DEV_GONE:
|
case USB_HOST_CLIENT_EVENT_DEV_GONE:
|
||||||
if (driver_obj->dev_hdl != NULL) {
|
// Cancel any other actions and close the device next
|
||||||
//Cancel any other actions and close the device next
|
xSemaphoreTake(driver_obj->constant.mux_lock, portMAX_DELAY);
|
||||||
driver_obj->actions = ACTION_CLOSE_DEV;
|
for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) {
|
||||||
|
if (driver_obj->mux_protected.device[i].dev_hdl == event_msg->dev_gone.dev_hdl) {
|
||||||
|
driver_obj->mux_protected.device[i].actions = ACTION_CLOSE_DEV;
|
||||||
|
// Set flag
|
||||||
|
driver_obj->mux_protected.flags.unhandled_devices = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
xSemaphoreGive(driver_obj->constant.mux_lock);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
//Should never occur
|
// Should never occur
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void action_open_dev(class_driver_t *driver_obj)
|
static void action_open_dev(usb_device_t *device_obj)
|
||||||
{
|
{
|
||||||
assert(driver_obj->dev_addr != 0);
|
assert(device_obj->dev_addr != 0);
|
||||||
ESP_LOGI(TAG, "Opening device at address %d", driver_obj->dev_addr);
|
ESP_LOGI(TAG, "Opening device at address %d", device_obj->dev_addr);
|
||||||
ESP_ERROR_CHECK(usb_host_device_open(driver_obj->client_hdl, driver_obj->dev_addr, &driver_obj->dev_hdl));
|
ESP_ERROR_CHECK(usb_host_device_open(device_obj->client_hdl, device_obj->dev_addr, &device_obj->dev_hdl));
|
||||||
//Get the device's information next
|
// Get the device's information next
|
||||||
driver_obj->actions &= ~ACTION_OPEN_DEV;
|
device_obj->actions |= ACTION_GET_DEV_INFO;
|
||||||
driver_obj->actions |= ACTION_GET_DEV_INFO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void action_get_info(class_driver_t *driver_obj)
|
static void action_get_info(usb_device_t *device_obj)
|
||||||
{
|
{
|
||||||
assert(driver_obj->dev_hdl != NULL);
|
assert(device_obj->dev_hdl != NULL);
|
||||||
ESP_LOGI(TAG, "Getting device information");
|
ESP_LOGI(TAG, "Getting device information");
|
||||||
usb_device_info_t dev_info;
|
usb_device_info_t dev_info;
|
||||||
ESP_ERROR_CHECK(usb_host_device_info(driver_obj->dev_hdl, &dev_info));
|
ESP_ERROR_CHECK(usb_host_device_info(device_obj->dev_hdl, &dev_info));
|
||||||
ESP_LOGI(TAG, "\t%s speed", (dev_info.speed == USB_SPEED_LOW) ? "Low" : "Full");
|
ESP_LOGI(TAG, "\t%s speed", (char *[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[dev_info.speed]);
|
||||||
ESP_LOGI(TAG, "\tbConfigurationValue %d", dev_info.bConfigurationValue);
|
ESP_LOGI(TAG, "\tbConfigurationValue %d", dev_info.bConfigurationValue);
|
||||||
|
ESP_LOGI(TAG, "\tParent info:");
|
||||||
//Get the device descriptor next
|
if (dev_info.parent.dev_hdl) {
|
||||||
driver_obj->actions &= ~ACTION_GET_DEV_INFO;
|
usb_device_info_t parent_dev_info;
|
||||||
driver_obj->actions |= ACTION_GET_DEV_DESC;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void action_get_dev_desc(class_driver_t *driver_obj)
|
static void action_get_dev_desc(usb_device_t *device_obj)
|
||||||
{
|
{
|
||||||
assert(driver_obj->dev_hdl != NULL);
|
assert(device_obj->dev_hdl != NULL);
|
||||||
ESP_LOGI(TAG, "Getting device descriptor");
|
ESP_LOGI(TAG, "Getting device descriptor");
|
||||||
const usb_device_desc_t *dev_desc;
|
const usb_device_desc_t *dev_desc;
|
||||||
ESP_ERROR_CHECK(usb_host_get_device_descriptor(driver_obj->dev_hdl, &dev_desc));
|
ESP_ERROR_CHECK(usb_host_get_device_descriptor(device_obj->dev_hdl, &dev_desc));
|
||||||
usb_print_device_descriptor(dev_desc);
|
usb_print_device_descriptor(dev_desc);
|
||||||
//Get the device's config descriptor next
|
// Get the device's config descriptor next
|
||||||
driver_obj->actions &= ~ACTION_GET_DEV_DESC;
|
device_obj->actions |= ACTION_GET_CONFIG_DESC;
|
||||||
driver_obj->actions |= ACTION_GET_CONFIG_DESC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void action_get_config_desc(class_driver_t *driver_obj)
|
static void action_get_config_desc(usb_device_t *device_obj)
|
||||||
{
|
{
|
||||||
assert(driver_obj->dev_hdl != NULL);
|
assert(device_obj->dev_hdl != NULL);
|
||||||
ESP_LOGI(TAG, "Getting config descriptor");
|
ESP_LOGI(TAG, "Getting config descriptor");
|
||||||
const usb_config_desc_t *config_desc;
|
const usb_config_desc_t *config_desc;
|
||||||
ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(driver_obj->dev_hdl, &config_desc));
|
ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(device_obj->dev_hdl, &config_desc));
|
||||||
usb_print_config_descriptor(config_desc, NULL);
|
usb_print_config_descriptor(config_desc, NULL);
|
||||||
//Get the device's string descriptors next
|
// Get the device's string descriptors next
|
||||||
driver_obj->actions &= ~ACTION_GET_CONFIG_DESC;
|
device_obj->actions |= ACTION_GET_STR_DESC;
|
||||||
driver_obj->actions |= ACTION_GET_STR_DESC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void action_get_str_desc(class_driver_t *driver_obj)
|
static void action_get_str_desc(usb_device_t *device_obj)
|
||||||
{
|
{
|
||||||
assert(driver_obj->dev_hdl != NULL);
|
assert(device_obj->dev_hdl != NULL);
|
||||||
usb_device_info_t dev_info;
|
usb_device_info_t dev_info;
|
||||||
ESP_ERROR_CHECK(usb_host_device_info(driver_obj->dev_hdl, &dev_info));
|
ESP_ERROR_CHECK(usb_host_device_info(device_obj->dev_hdl, &dev_info));
|
||||||
if (dev_info.str_desc_manufacturer) {
|
if (dev_info.str_desc_manufacturer) {
|
||||||
ESP_LOGI(TAG, "Getting Manufacturer string descriptor");
|
ESP_LOGI(TAG, "Getting Manufacturer string descriptor");
|
||||||
usb_print_string_descriptor(dev_info.str_desc_manufacturer);
|
usb_print_string_descriptor(dev_info.str_desc_manufacturer);
|
||||||
@@ -117,29 +157,59 @@ static void action_get_str_desc(class_driver_t *driver_obj)
|
|||||||
ESP_LOGI(TAG, "Getting Serial Number string descriptor");
|
ESP_LOGI(TAG, "Getting Serial Number string descriptor");
|
||||||
usb_print_string_descriptor(dev_info.str_desc_serial_num);
|
usb_print_string_descriptor(dev_info.str_desc_serial_num);
|
||||||
}
|
}
|
||||||
//Nothing to do until the device disconnects
|
|
||||||
driver_obj->actions &= ~ACTION_GET_STR_DESC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aciton_close_dev(class_driver_t *driver_obj)
|
static void action_close_dev(usb_device_t *device_obj)
|
||||||
{
|
{
|
||||||
ESP_ERROR_CHECK(usb_host_device_close(driver_obj->client_hdl, driver_obj->dev_hdl));
|
ESP_ERROR_CHECK(usb_host_device_close(device_obj->client_hdl, device_obj->dev_hdl));
|
||||||
driver_obj->dev_hdl = NULL;
|
device_obj->dev_hdl = NULL;
|
||||||
driver_obj->dev_addr = 0;
|
device_obj->dev_addr = 0;
|
||||||
//We need to exit the event handler loop
|
}
|
||||||
driver_obj->actions &= ~ACTION_CLOSE_DEV;
|
|
||||||
driver_obj->actions |= ACTION_EXIT;
|
static void class_driver_device_handle(usb_device_t *device_obj)
|
||||||
|
{
|
||||||
|
uint8_t actions = device_obj->actions;
|
||||||
|
device_obj->actions = 0;
|
||||||
|
|
||||||
|
while (actions) {
|
||||||
|
if (actions & ACTION_OPEN_DEV) {
|
||||||
|
action_open_dev(device_obj);
|
||||||
|
}
|
||||||
|
if (actions & ACTION_GET_DEV_INFO) {
|
||||||
|
action_get_info(device_obj);
|
||||||
|
}
|
||||||
|
if (actions & ACTION_GET_DEV_DESC) {
|
||||||
|
action_get_dev_desc(device_obj);
|
||||||
|
}
|
||||||
|
if (actions & ACTION_GET_CONFIG_DESC) {
|
||||||
|
action_get_config_desc(device_obj);
|
||||||
|
}
|
||||||
|
if (actions & ACTION_GET_STR_DESC) {
|
||||||
|
action_get_str_desc(device_obj);
|
||||||
|
}
|
||||||
|
if (actions & ACTION_CLOSE_DEV) {
|
||||||
|
action_close_dev(device_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
actions = device_obj->actions;
|
||||||
|
device_obj->actions = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void class_driver_task(void *arg)
|
void class_driver_task(void *arg)
|
||||||
{
|
{
|
||||||
SemaphoreHandle_t signaling_sem = (SemaphoreHandle_t)arg;
|
|
||||||
class_driver_t driver_obj = {0};
|
class_driver_t driver_obj = {0};
|
||||||
|
usb_host_client_handle_t class_driver_client_hdl = NULL;
|
||||||
//Wait until daemon task has installed USB Host Library
|
|
||||||
xSemaphoreTake(signaling_sem, portMAX_DELAY);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Registering Client");
|
ESP_LOGI(TAG, "Registering Client");
|
||||||
|
|
||||||
|
SemaphoreHandle_t mux_lock = xSemaphoreCreateMutex();
|
||||||
|
if (mux_lock == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Unable to create class driver mutex");
|
||||||
|
vTaskSuspend(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
usb_host_client_config_t client_config = {
|
usb_host_client_config_t client_config = {
|
||||||
.is_synchronous = false, //Synchronous clients currently not supported. Set this to false
|
.is_synchronous = false, //Synchronous clients currently not supported. Set this to false
|
||||||
.max_num_event_msg = CLIENT_NUM_EVENT_MSG,
|
.max_num_event_msg = CLIENT_NUM_EVENT_MSG,
|
||||||
@@ -148,40 +218,62 @@ void class_driver_task(void *arg)
|
|||||||
.callback_arg = (void *) &driver_obj,
|
.callback_arg = (void *) &driver_obj,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(usb_host_client_register(&client_config, &driver_obj.client_hdl));
|
ESP_ERROR_CHECK(usb_host_client_register(&client_config, &class_driver_client_hdl));
|
||||||
|
|
||||||
|
driver_obj.constant.mux_lock = mux_lock;
|
||||||
|
driver_obj.constant.client_hdl = class_driver_client_hdl;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) {
|
||||||
|
driver_obj.mux_protected.device[i].client_hdl = class_driver_client_hdl;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_driver_obj = &driver_obj;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (driver_obj.actions == 0) {
|
// Driver has unhandled devices, handle all devices first
|
||||||
usb_host_client_handle_events(driver_obj.client_hdl, portMAX_DELAY);
|
if (driver_obj.mux_protected.flags.unhandled_devices) {
|
||||||
|
xSemaphoreTake(driver_obj.constant.mux_lock, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) {
|
||||||
|
if (driver_obj.mux_protected.device[i].actions) {
|
||||||
|
class_driver_device_handle(&driver_obj.mux_protected.device[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
driver_obj.mux_protected.flags.unhandled_devices = 0;
|
||||||
|
xSemaphoreGive(driver_obj.constant.mux_lock);
|
||||||
} else {
|
} else {
|
||||||
if (driver_obj.actions & ACTION_OPEN_DEV) {
|
// Driver is active, handle client events
|
||||||
action_open_dev(&driver_obj);
|
if (driver_obj.mux_protected.flags.shutdown == 0) {
|
||||||
}
|
usb_host_client_handle_events(class_driver_client_hdl, portMAX_DELAY);
|
||||||
if (driver_obj.actions & ACTION_GET_DEV_INFO) {
|
} else {
|
||||||
action_get_info(&driver_obj);
|
// Shutdown the driver
|
||||||
}
|
|
||||||
if (driver_obj.actions & ACTION_GET_DEV_DESC) {
|
|
||||||
action_get_dev_desc(&driver_obj);
|
|
||||||
}
|
|
||||||
if (driver_obj.actions & ACTION_GET_CONFIG_DESC) {
|
|
||||||
action_get_config_desc(&driver_obj);
|
|
||||||
}
|
|
||||||
if (driver_obj.actions & ACTION_GET_STR_DESC) {
|
|
||||||
action_get_str_desc(&driver_obj);
|
|
||||||
}
|
|
||||||
if (driver_obj.actions & ACTION_CLOSE_DEV) {
|
|
||||||
aciton_close_dev(&driver_obj);
|
|
||||||
}
|
|
||||||
if (driver_obj.actions & ACTION_EXIT) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Deregistering Client");
|
ESP_LOGI(TAG, "Deregistering Class Client");
|
||||||
ESP_ERROR_CHECK(usb_host_client_deregister(driver_obj.client_hdl));
|
ESP_ERROR_CHECK(usb_host_client_deregister(class_driver_client_hdl));
|
||||||
|
if (mux_lock != NULL) {
|
||||||
//Wait to be deleted
|
vSemaphoreDelete(mux_lock);
|
||||||
xSemaphoreGive(signaling_sem);
|
}
|
||||||
vTaskSuspend(NULL);
|
vTaskSuspend(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void class_driver_client_deregister(void)
|
||||||
|
{
|
||||||
|
// Mark all opened devices
|
||||||
|
xSemaphoreTake(s_driver_obj->constant.mux_lock, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < DEV_MAX_COUNT; i++) {
|
||||||
|
if (s_driver_obj->mux_protected.device[i].dev_hdl != NULL) {
|
||||||
|
// Mark device to close
|
||||||
|
s_driver_obj->mux_protected.device[i].actions |= ACTION_CLOSE_DEV;
|
||||||
|
// Set flag
|
||||||
|
s_driver_obj->mux_protected.flags.unhandled_devices = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s_driver_obj->mux_protected.flags.shutdown = 1;
|
||||||
|
xSemaphoreGive(s_driver_obj->constant.mux_lock);
|
||||||
|
|
||||||
|
// Unblock, exit the loop and proceed to deregister client
|
||||||
|
ESP_ERROR_CHECK(usb_host_client_unblock(s_driver_obj->constant.client_hdl));
|
||||||
|
}
|
||||||
|
@@ -6,21 +6,69 @@
|
|||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "usb/usb_host.h"
|
#include "usb/usb_host.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
#define DAEMON_TASK_PRIORITY 2
|
#define HOST_LIB_TASK_PRIORITY 2
|
||||||
#define CLASS_TASK_PRIORITY 3
|
#define CLASS_TASK_PRIORITY 3
|
||||||
|
#define APP_QUIT_PIN CONFIG_APP_QUIT_PIN
|
||||||
|
|
||||||
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
|
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
|
||||||
#define ENABLE_ENUM_FILTER_CALLBACK
|
#define ENABLE_ENUM_FILTER_CALLBACK
|
||||||
#endif // CONFIG_USB_HOST_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);
|
||||||
|
|
||||||
static const char *TAG = "DAEMON";
|
static const char *TAG = "USB host lib";
|
||||||
|
|
||||||
|
QueueHandle_t app_event_queue = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief APP event group
|
||||||
|
*
|
||||||
|
* APP_EVENT - General event, which is APP_QUIT_PIN press event in this example.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
APP_EVENT = 0,
|
||||||
|
} app_event_group_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BOOT button pressed callback
|
||||||
|
*
|
||||||
|
* Signal application to exit the Host lib task
|
||||||
|
*
|
||||||
|
* @param[in] arg Unused
|
||||||
|
*/
|
||||||
|
static void gpio_cb(void *arg)
|
||||||
|
{
|
||||||
|
const app_event_queue_t evt_queue = {
|
||||||
|
.event_group = APP_EVENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
BaseType_t xTaskWoken = pdFALSE;
|
||||||
|
|
||||||
|
if (app_event_queue) {
|
||||||
|
xQueueSendFromISR(app_event_queue, &evt_queue, &xTaskWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xTaskWoken == pdTRUE) {
|
||||||
|
portYIELD_FROM_ISR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set configuration callback
|
* @brief Set configuration callback
|
||||||
@@ -51,10 +99,13 @@ static bool set_config_cb(const usb_device_desc_t *dev_desc, uint8_t *bConfigura
|
|||||||
}
|
}
|
||||||
#endif // ENABLE_ENUM_FILTER_CALLBACK
|
#endif // ENABLE_ENUM_FILTER_CALLBACK
|
||||||
|
|
||||||
static void host_lib_daemon_task(void *arg)
|
/**
|
||||||
|
* @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)
|
||||||
{
|
{
|
||||||
SemaphoreHandle_t signaling_sem = (SemaphoreHandle_t)arg;
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Installing USB Host Library");
|
ESP_LOGI(TAG, "Installing USB Host Library");
|
||||||
usb_host_config_t host_config = {
|
usb_host_config_t host_config = {
|
||||||
.skip_phy_setup = false,
|
.skip_phy_setup = false,
|
||||||
@@ -65,62 +116,109 @@ static void host_lib_daemon_task(void *arg)
|
|||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(usb_host_install(&host_config));
|
ESP_ERROR_CHECK(usb_host_install(&host_config));
|
||||||
|
|
||||||
//Signal to the class driver task that the host library is installed
|
//Signalize the app_main, the USB host library has been installed
|
||||||
xSemaphoreGive(signaling_sem);
|
xTaskNotifyGive(arg);
|
||||||
vTaskDelay(10); //Short delay to let client task spin up
|
|
||||||
|
|
||||||
bool has_clients = true;
|
bool has_clients = true;
|
||||||
bool has_devices = true;
|
bool has_devices = false;
|
||||||
while (has_clients || has_devices) {
|
while (has_clients) {
|
||||||
uint32_t event_flags;
|
uint32_t event_flags;
|
||||||
ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags));
|
ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags));
|
||||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
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;
|
has_clients = false;
|
||||||
}
|
}
|
||||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
|
||||||
has_devices = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "No more clients and devices");
|
ESP_LOGI(TAG, "No more clients and devices, uninstall USB Host library");
|
||||||
|
|
||||||
//Uninstall the USB Host Library
|
//Uninstall the USB Host Library
|
||||||
ESP_ERROR_CHECK(usb_host_uninstall());
|
ESP_ERROR_CHECK(usb_host_uninstall());
|
||||||
//Wait to be deleted
|
|
||||||
xSemaphoreGive(signaling_sem);
|
|
||||||
vTaskSuspend(NULL);
|
vTaskSuspend(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
SemaphoreHandle_t signaling_sem = xSemaphoreCreateBinary();
|
ESP_LOGI(TAG, "USB host library example");
|
||||||
|
|
||||||
TaskHandle_t daemon_task_hdl;
|
// Init BOOT button: Pressing the button simulates app request to exit
|
||||||
TaskHandle_t class_driver_task_hdl;
|
// It will uninstall the class driver and USB Host Lib
|
||||||
//Create daemon task
|
const gpio_config_t input_pin = {
|
||||||
xTaskCreatePinnedToCore(host_lib_daemon_task,
|
.pin_bit_mask = BIT64(APP_QUIT_PIN),
|
||||||
"daemon",
|
.mode = GPIO_MODE_INPUT,
|
||||||
4096,
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
(void *)signaling_sem,
|
.intr_type = GPIO_INTR_NEGEDGE,
|
||||||
DAEMON_TASK_PRIORITY,
|
};
|
||||||
&daemon_task_hdl,
|
ESP_ERROR_CHECK(gpio_config(&input_pin));
|
||||||
0);
|
ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1));
|
||||||
//Create the class driver task
|
ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_cb, NULL));
|
||||||
xTaskCreatePinnedToCore(class_driver_task,
|
|
||||||
"class",
|
|
||||||
4096,
|
|
||||||
(void *)signaling_sem,
|
|
||||||
CLASS_TASK_PRIORITY,
|
|
||||||
&class_driver_task_hdl,
|
|
||||||
0);
|
|
||||||
|
|
||||||
vTaskDelay(10); //Add a short delay to let the tasks run
|
app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t));
|
||||||
|
app_event_queue_t evt_queue;
|
||||||
|
|
||||||
//Wait for the tasks to complete
|
TaskHandle_t host_lib_task_hdl, class_driver_task_hdl;
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
xSemaphoreTake(signaling_sem, portMAX_DELAY);
|
// 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);
|
||||||
|
|
||||||
|
// Wait unit the USB host library is installed
|
||||||
|
ulTaskNotifyTake(false, 1000);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete the tasks
|
// Deregister client
|
||||||
|
class_driver_client_deregister();
|
||||||
|
vTaskDelay(10);
|
||||||
|
|
||||||
|
// Delete the tasks
|
||||||
vTaskDelete(class_driver_task_hdl);
|
vTaskDelete(class_driver_task_hdl);
|
||||||
vTaskDelete(daemon_task_hdl);
|
vTaskDelete(host_lib_task_hdl);
|
||||||
|
|
||||||
|
// 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");
|
||||||
}
|
}
|
||||||
|
@@ -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_USB_HOST_HUBS_SUPPORTED=y
|
Reference in New Issue
Block a user