Merge branch 'refactor/usb_host_usbh_api_prereq_for_enum_driver' into 'master'

refactor(usbh): Updated USBH api for ENUM driver

Closes IDF-9723

See merge request espressif/esp-idf!29913
This commit is contained in:
Roman Leonov 2024-05-08 15:02:08 +08:00
commit 7b7b8680ca
5 changed files with 242 additions and 131 deletions

View File

@ -606,15 +606,15 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
break; break;
} else { } else {
// Fill the string descriptor into the device object // Fill the string descriptor into the device object
int select; int str_index;
if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_MANU_STR_DESC) { if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_MANU_STR_DESC) {
select = 0; str_index = 0;
} else if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_PROD_STR_DESC) { } else if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_PROD_STR_DESC) {
select = 1; str_index = 1;
} else { // ENUM_STAGE_CHECK_FULL_PROD_STR_DESC } else { // ENUM_STAGE_CHECK_FULL_PROD_STR_DESC
select = 2; str_index = 2;
} }
ESP_ERROR_CHECK(usbh_dev_set_str_desc(enum_ctrl->dev_hdl, str_desc, select)); ESP_ERROR_CHECK(usbh_dev_set_str_desc(enum_ctrl->dev_hdl, str_desc, str_index));
ret = true; ret = true;
break; break;
} }
@ -634,7 +634,7 @@ static void enum_stage_cleanup(enum_ctrl_t *enum_ctrl)
// Propagate a new device event // Propagate a new device event
ESP_ERROR_CHECK(usbh_devs_new_dev_event(enum_ctrl->dev_hdl)); ESP_ERROR_CHECK(usbh_devs_new_dev_event(enum_ctrl->dev_hdl));
// We are done with using the device. Close it. // We are done with using the device. Close it.
ESP_ERROR_CHECK(usbh_devs_close(enum_ctrl->dev_hdl)); ESP_ERROR_CHECK(usbh_dev_close(enum_ctrl->dev_hdl));
// Clear values in enum_ctrl // Clear values in enum_ctrl
enum_ctrl->dev_hdl = NULL; enum_ctrl->dev_hdl = NULL;
} }
@ -644,7 +644,7 @@ static void enum_stage_cleanup_failed(enum_ctrl_t *enum_ctrl)
if (enum_ctrl->dev_hdl) { if (enum_ctrl->dev_hdl) {
// Close the device and unlock it as we done with enumeration // Close the device and unlock it as we done with enumeration
ESP_ERROR_CHECK(usbh_dev_enum_unlock(enum_ctrl->dev_hdl)); ESP_ERROR_CHECK(usbh_dev_enum_unlock(enum_ctrl->dev_hdl));
ESP_ERROR_CHECK(usbh_devs_close(enum_ctrl->dev_hdl)); ESP_ERROR_CHECK(usbh_dev_close(enum_ctrl->dev_hdl));
// We allow this to fail in case the device object was already freed // We allow this to fail in case the device object was already freed
usbh_devs_remove(ENUM_DEV_UID); usbh_devs_remove(ENUM_DEV_UID);
} }
@ -791,8 +791,15 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
if (hcd_port_get_speed(p_hub_driver_obj->constant.root_port_hdl, &speed) != ESP_OK) { if (hcd_port_get_speed(p_hub_driver_obj->constant.root_port_hdl, &speed) != ESP_OK) {
goto new_dev_err; goto new_dev_err;
} }
// Allocate a new device. We use a fixed ENUM_DEV_UID for now since we only support a single device // Allocate a new device. We use a fixed ENUM_DEV_UID for now since we only support a single device
if (usbh_devs_add(ENUM_DEV_UID, speed, p_hub_driver_obj->constant.root_port_hdl) != ESP_OK) { usbh_dev_params_t params = {
.uid = ENUM_DEV_UID,
.speed = speed,
.root_port_hdl = p_hub_driver_obj->constant.root_port_hdl,
};
if (usbh_devs_add(&params) != ESP_OK) {
ESP_LOGE(HUB_DRIVER_TAG, "Failed to add device"); ESP_LOGE(HUB_DRIVER_TAG, "Failed to add device");
goto new_dev_err; goto new_dev_err;
} }

View File

@ -70,10 +70,19 @@ typedef struct usb_device_handle_s *usb_device_handle_t;
*/ */
typedef bool (*usb_host_enum_filter_cb_t)(const usb_device_desc_t *dev_desc, uint8_t *bConfigurationValue); typedef bool (*usb_host_enum_filter_cb_t)(const usb_device_desc_t *dev_desc, uint8_t *bConfigurationValue);
/**
* @brief Parent device information
*/
typedef struct {
usb_device_handle_t dev_hdl; /**< Device's parent handle */
uint8_t port_num; /**< Device's parent port number */
} usb_parent_dev_info_t;
/** /**
* @brief Basic information of an enumerated device * @brief Basic information of an enumerated device
*/ */
typedef struct { typedef struct {
usb_parent_dev_info_t parent; /**< Device's parent information */
usb_speed_t speed; /**< Device's speed */ usb_speed_t speed; /**< Device's speed */
uint8_t dev_addr; /**< Device's address */ uint8_t dev_addr; /**< Device's address */
uint8_t bMaxPacketSize0; /**< The maximum packet size of the device's default endpoint */ uint8_t bMaxPacketSize0; /**< The maximum packet size of the device's default endpoint */

View File

@ -59,6 +59,8 @@ typedef struct {
} dev_gone_data; } dev_gone_data;
struct { struct {
unsigned int dev_uid; unsigned int dev_uid;
usb_device_handle_t parent_dev_hdl;
uint8_t port_num;
} dev_free_data; } dev_free_data;
}; };
} usbh_event_data_t; } usbh_event_data_t;
@ -130,7 +132,18 @@ typedef struct {
void *event_cb_arg; /**< USBH event callback argument */ void *event_cb_arg; /**< USBH event callback argument */
} usbh_config_t; } usbh_config_t;
// -------------------------------------------- USBH Processing Functions ---------------------------------------------- /**
* @brief USBH device parameters used in usbh_devs_add()
*/
typedef struct {
unsigned int uid; /**< Unique ID assigned to the device */
usb_speed_t speed; /**< Device's speed */
hcd_port_handle_t root_port_hdl; /**< Handle of the port that the device is connected to */
usb_device_handle_t parent_dev_hdl; /**< Parent's device handle */
uint8_t parent_port_num; /**< Parent's port number */
} usbh_dev_params_t;
// ---------------------- USBH Processing Functions ----------------------------
/** /**
* @brief Installs the USBH driver * @brief Installs the USBH driver
@ -169,7 +182,7 @@ esp_err_t usbh_uninstall(void);
*/ */
esp_err_t usbh_process(void); esp_err_t usbh_process(void);
// ---------------------------------------------- Device Pool Functions ------------------------------------------------ // ---------------------- Device Pool Functions --------------------------------
/** /**
* @brief Get the current number of devices * @brief Get the current number of devices
@ -206,12 +219,10 @@ esp_err_t usbh_devs_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *nu
* - Call usbh_dev_enum_lock() before enumerating the device via the various * - Call usbh_dev_enum_lock() before enumerating the device via the various
* usbh_dev_set_...() functions. * usbh_dev_set_...() functions.
* *
* @param[in] uid Unique ID assigned to the device * @param[in] params Device parameters, using for device creation
* @param[in] dev_speed Device's speed
* @param[in] port_hdl Handle of the port that the device is connected to
* @return esp_err_t * @return esp_err_t
*/ */
esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle_t port_hdl); esp_err_t usbh_devs_add(usbh_dev_params_t *params);
/** /**
* @brief Indicates to the USBH that a device is gone * @brief Indicates to the USBH that a device is gone
@ -221,6 +232,18 @@ esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle
*/ */
esp_err_t usbh_devs_remove(unsigned int uid); esp_err_t usbh_devs_remove(unsigned int uid);
/**
* @brief Get a device's connection information
*
* @note Can be called without opening the device
*
* @param[in] uid Unique ID assigned to the device
* @param[out] parent_info Parent device handle
* @param[out] port_num Parent port number
* @return esp_err_t
*/
esp_err_t usbh_devs_get_parent_info(unsigned int uid, usb_parent_dev_info_t *parent_info);
/** /**
* @brief Mark that all devices should be freed at the next possible opportunity * @brief Mark that all devices should be freed at the next possible opportunity
* *
@ -243,16 +266,6 @@ esp_err_t usbh_devs_mark_all_free(void);
*/ */
esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl); esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl);
/**
* @brief CLose a device
*
* Device can be opened by calling usbh_devs_open()
*
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl);
/** /**
* @brief Trigger a USBH_EVENT_NEW_DEV event for the device * @brief Trigger a USBH_EVENT_NEW_DEV event for the device
* *
@ -263,14 +276,23 @@ esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl);
*/ */
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl); esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl);
// ------------------------------------------------ Device Functions --------------------------------------------------- // ------------------------ Device Functions -----------------------------------
// ----------------------- Getters ------------------------- /**
* @brief Close a device
*
* @note Callers of this function must have opened the device via usbh_devs_open()
* *
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl);
// ------------------------------ Getters --------------------------------------
/** /**
* @brief Get a device's address * @brief Get a device's address
* *
* @note Can be called without opening the device * @note Callers of this function must have opened the device via usbh_devs_open()
* *
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @param[out] dev_addr Device's address * @param[out] dev_addr Device's address
@ -281,8 +303,10 @@ esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr);
/** /**
* @brief Get a device's information * @brief Get a device's information
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
* @note It is possible that the device has not been enumerated yet, thus some * @note It is possible that the device has not been enumerated yet, thus some
* fields may be NULL. * fields may be NULL.
*
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @param[out] dev_info Device information * @param[out] dev_info Device information
* @return esp_err_t * @return esp_err_t
@ -292,7 +316,7 @@ esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_
/** /**
* @brief Get a device's device descriptor * @brief Get a device's device descriptor
* *
* - The device descriptor is cached when the device is created by the Hub driver * The device descriptor is cached when the device is created by the Hub driver
* *
* @note It is possible that the device has not been enumerated yet, thus the * @note It is possible that the device has not been enumerated yet, thus the
* device descriptor could be NULL. * device descriptor could be NULL.
@ -305,6 +329,7 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
/** /**
* @brief Get a device's active configuration descriptor * @brief Get a device's active configuration descriptor
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
* Simply returns a reference to the internally cached configuration descriptor * Simply returns a reference to the internally cached configuration descriptor
* *
* @note It is possible that the device has not been enumerated yet, thus the * @note It is possible that the device has not been enumerated yet, thus the
@ -315,11 +340,13 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
*/ */
esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc_ret); esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc_ret);
// ----------------------- Setters ------------------------- // ------------------------------- Setters -------------------------------------
/** /**
* @brief Lock a device for enumeration * @brief Lock a device for enumeration
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* - A device's enumeration lock must be set before any of its enumeration fields * - A device's enumeration lock must be set before any of its enumeration fields
* (e.g., address, device/config descriptors) can be set/updated. * (e.g., address, device/config descriptors) can be set/updated.
* - The caller must be the sole opener of the device (see 'usbh_devs_open()') * - The caller must be the sole opener of the device (see 'usbh_devs_open()')
@ -333,6 +360,8 @@ esp_err_t usbh_dev_enum_lock(usb_device_handle_t dev_hdl);
/** /**
* @brief Release a device's enumeration lock * @brief Release a device's enumeration lock
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @return esp_err_t * @return esp_err_t
*/ */
@ -344,8 +373,11 @@ esp_err_t usbh_dev_enum_unlock(usb_device_handle_t dev_hdl);
* Typically called during enumeration after obtaining the first 8 bytes of the * Typically called during enumeration after obtaining the first 8 bytes of the
* device's descriptor. * device's descriptor.
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function * @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()') * (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @param[in] wMaxPacketSize Maximum packet size * @param[in] wMaxPacketSize Maximum packet size
* @return esp_err_t * @return esp_err_t
@ -355,13 +387,15 @@ esp_err_t usbh_dev_set_ep0_mps(usb_device_handle_t dev_hdl, uint16_t wMaxPacketS
/** /**
* @brief Set a device's address * @brief Set a device's address
* *
* Typically called during enumeration after a SET_ADDRESSS request has be * Typically called during enumeration after a SET_ADDRESS request has been
* sent to the device. * sent to the device.
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function * @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()') * (see 'usbh_dev_enum_lock()')
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @param[in] dev_addr * @param[in] dev_addr Device address to set
* @return esp_err_t * @return esp_err_t
*/ */
esp_err_t usbh_dev_set_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr); esp_err_t usbh_dev_set_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
@ -372,8 +406,11 @@ esp_err_t usbh_dev_set_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
* Typically called during enumeration after obtaining the device's descriptor * Typically called during enumeration after obtaining the device's descriptor
* via a GET_DESCRIPTOR request. * via a GET_DESCRIPTOR request.
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function * @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()') * (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @param[in] device_desc Device descriptor to copy * @param[in] device_desc Device descriptor to copy
* @return esp_err_t * @return esp_err_t
@ -386,8 +423,11 @@ esp_err_t usbh_dev_set_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
* Typically called during enumeration after obtaining the device's configuration * Typically called during enumeration after obtaining the device's configuration
* descriptor via a GET_DESCRIPTOR request. * descriptor via a GET_DESCRIPTOR request.
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function * @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()') * (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @param[in] config_desc_full Configuration descriptor to copy * @param[in] config_desc_full Configuration descriptor to copy
* @return esp_err_t * @return esp_err_t
@ -400,8 +440,11 @@ esp_err_t usbh_dev_set_config_desc(usb_device_handle_t dev_hdl, const usb_config
* Typically called during enumeration after obtaining one of the device's string * Typically called during enumeration after obtaining one of the device's string
* descriptor via a GET_DESCRIPTOR request. * descriptor via a GET_DESCRIPTOR request.
* *
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function * @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()') * (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle * @param[in] dev_hdl Device handle
* @param[in] str_desc String descriptor to copy * @param[in] str_desc String descriptor to copy
* @param[in] select Select string descriptor. 0/1/2 for Manufacturer/Product/Serial * @param[in] select Select string descriptor. 0/1/2 for Manufacturer/Product/Serial
@ -410,7 +453,7 @@ esp_err_t usbh_dev_set_config_desc(usb_device_handle_t dev_hdl, const usb_config
*/ */
esp_err_t usbh_dev_set_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select); esp_err_t usbh_dev_set_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select);
// ----------------------------------------------- Endpoint Functions ------------------------------------------------- // ----------------------- Endpoint Functions ----------------------------------
/** /**
* @brief Allocate an endpoint on a device * @brief Allocate an endpoint on a device
@ -482,7 +525,7 @@ esp_err_t usbh_ep_command(usbh_ep_handle_t ep_hdl, usbh_ep_cmd_t command);
*/ */
void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl); void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl);
// ----------------------------------------------- Transfer Functions -------------------------------------------------- // ------------------------- Transfer Functions --------------------------------
/** /**
* @brief Submit a control transfer (URB) to a device * @brief Submit a control transfer (URB) to a device

View File

@ -853,7 +853,7 @@ esp_err_t usb_host_device_open(usb_host_client_handle_t client_hdl, uint8_t dev_
return ret; return ret;
already_opened: already_opened:
ESP_ERROR_CHECK(usbh_devs_close(dev_hdl)); ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
exit: exit:
return ret; return ret;
} }
@ -895,7 +895,7 @@ esp_err_t usb_host_device_close(usb_host_client_handle_t client_hdl, usb_device_
_clear_client_opened_device(client_obj, dev_addr); _clear_client_opened_device(client_obj, dev_addr);
HOST_EXIT_CRITICAL(); HOST_EXIT_CRITICAL();
ESP_ERROR_CHECK(usbh_devs_close(dev_hdl)); ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
ret = ESP_OK; ret = ESP_OK;
exit: exit:
xSemaphoreGive(p_host_lib_obj->constant.mux_lock); xSemaphoreGive(p_host_lib_obj->constant.mux_lock);

View File

@ -40,78 +40,79 @@ typedef struct device_s device_t;
typedef struct { typedef struct {
struct { struct {
usbh_ep_cb_t ep_cb; usbh_ep_cb_t ep_cb; /**< Endpoint callback is called when transfer in complete or error occurred */
void *ep_cb_arg; void *ep_cb_arg; /**< Endpoint callback argument */
hcd_pipe_handle_t pipe_hdl; hcd_pipe_handle_t pipe_hdl; /**< Endpoint HCD pipe handle */
device_t *dev; // Pointer to the device object that this endpoint is contained in device_t *dev; /**< Pointer to the device object that this endpoint is contained in */
const usb_ep_desc_t *ep_desc; // This just stores a pointer endpoint descriptor inside the device's "config_desc" const usb_ep_desc_t *ep_desc; /**< This just stores a pointer endpoint descriptor inside the device's "config_desc" */
} constant; } constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
} endpoint_t; } endpoint_t;
struct device_s { struct device_s {
// Dynamic members require a critical section
struct { struct {
TAILQ_ENTRY(device_s) tailq_entry; TAILQ_ENTRY(device_s) tailq_entry; /**< Entry for the device object tailq */
union { union {
struct { struct {
uint32_t in_pending_list: 1; uint32_t in_pending_list: 1; /**< Device is in pending list */
uint32_t is_gone: 1; // Device is gone (disconnected or port error) uint32_t is_gone: 1; /**< Device is gone (disconnected or port error) */
uint32_t waiting_free: 1; // Device object is awaiting to be freed uint32_t waiting_free: 1; /**< Device object is awaiting to be freed */
uint32_t enum_lock: 1; // Device is locked for enumeration. Enum information (e.g., address, device/config desc etc) may change uint32_t enum_lock: 1; /**< Device is locked for enumeration. Enum information (e.g., address, device/config desc etc) may change */
uint32_t reserved28: 28; uint32_t reserved28: 28; /**< Reserved */
}; };
uint32_t val; uint32_t val; /**< Device flags value */
} flags; } flags;
uint32_t action_flags; uint32_t action_flags; /**< Device action flags */
int num_ctrl_xfers_inflight; int num_ctrl_xfers_inflight; /**< Amount of ongoing Control transfers */
usb_device_state_t state; usb_device_state_t state; /**< Device state */
uint32_t open_count; uint32_t open_count; /**< Amount of clients which opened this device */
} dynamic; } dynamic; /**< Dynamic members. Require a critical section */
// Mux protected members must be protected by the USBH mux_lock when accessed
struct { struct {
/* /*
- Endpoint object pointers for each possible non-default endpoint - Endpoint object pointers for each possible non-default endpoint
- All OUT EPs are listed before IN EPs (i.e., EP_NUM_MIN OUT ... EP_NUM_MAX OUT ... EP_NUM_MIN IN ... EP_NUM_MAX) - All OUT EPs are listed before IN EPs (i.e., EP_NUM_MIN OUT ... EP_NUM_MAX OUT ... EP_NUM_MIN IN ... EP_NUM_MAX)
*/ */
endpoint_t *endpoints[NUM_NON_DEFAULT_EP]; endpoint_t *endpoints[NUM_NON_DEFAULT_EP];
} mux_protected; } mux_protected; /**< Mutex protected members. Must be protected by the USBH mux_lock when accessed */
// Constant members do not require a critical section // Constant members do not require a critical section
struct { struct {
// Assigned on device allocation and remain constant for the device's lifetime // Assigned on device allocation and remain constant for the device's lifetime
hcd_pipe_handle_t default_pipe; hcd_pipe_handle_t default_pipe; /**< Pipe handle for Control EP0 */
hcd_port_handle_t port_hdl; hcd_port_handle_t port_hdl; /**< HCD port handle */
usb_speed_t speed; usb_device_handle_t parent_dev_hdl; /**< Device's parent device handle. NULL if device is connected to the root port */
unsigned int uid; uint8_t parent_port_num; /**< Device's parent port number. 0 if device connected to the root port */
usb_speed_t speed; /**< Device's speed */
unsigned int uid; /**< Device's Unique ID */
/* /*
These fields are can only be changed when enum_lock is set, thus can be treated as constant These fields are can only be changed when enum_lock is set, thus can be treated as constant
*/ */
uint8_t address; uint8_t address; /**< Device's bus address */
usb_device_desc_t *desc; usb_device_desc_t *desc; /**< Device's descriptor pointer */
usb_config_desc_t *config_desc; usb_config_desc_t *config_desc; /**< Device's configuration descriptor pointer. NULL if not configured. */
usb_str_desc_t *str_desc_manu; usb_str_desc_t *str_desc_manu; /**< Device's Manufacturer string descriptor pointer */
usb_str_desc_t *str_desc_product; usb_str_desc_t *str_desc_product; /**< Device's Product string descriptor pointer */
usb_str_desc_t *str_desc_ser_num; usb_str_desc_t *str_desc_ser_num; /**< Device's Serial string descriptor pointer */
} constant; } constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
}; };
typedef struct { typedef struct {
// Dynamic members require a critical section
struct { struct {
TAILQ_HEAD(tailhead_devs, device_s) devs_idle_tailq; // Tailq of all enum and configured devices TAILQ_HEAD(tailhead_devs, device_s) devs_idle_tailq; /**< Tailq of all enum and configured devices */
TAILQ_HEAD(tailhead_devs_cb, device_s) devs_pending_tailq; // Tailq of devices that need to have their cb called TAILQ_HEAD(tailhead_devs_cb, device_s) devs_pending_tailq; /**< Tailq of devices that need to have their cb called */
} dynamic; } dynamic; /**< Dynamic members. Require a critical section */
// Mux protected members must be protected by the USBH mux_lock when accessed
struct { struct {
uint8_t num_device; // Number of enumerated devices uint8_t num_device; /**< Current number of device objects */
} mux_protected; } mux_protected; /**< Mutex protected members. Must be protected by the USBH mux_lock when accessed */
// Constant members do no change after installation thus do not require a critical section
struct { struct {
usb_proc_req_cb_t proc_req_cb; usb_proc_req_cb_t proc_req_cb; /**< USB Host process request callback. Refer to proc_req_callback() in usb_host.c */
void *proc_req_cb_arg; void *proc_req_cb_arg; /**< USB Host process request callback argument */
usbh_event_cb_t event_cb; usbh_event_cb_t event_cb; /**< USBH event callback */
void *event_cb_arg; void *event_cb_arg; /**< USBH event callback argument */
SemaphoreHandle_t mux_lock; SemaphoreHandle_t mux_lock; /**< Mutex for protected members */
} constant; } constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
} usbh_t; } usbh_t;
static usbh_t *p_usbh_obj = NULL; static usbh_t *p_usbh_obj = NULL;
@ -147,7 +148,9 @@ static bool epN_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_
static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags); static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags);
// ----------------------------------------------------- Helpers ------------------------------------------------------- // -----------------------------------------------------------------------------
// ---------------------------- Helpers ----------------------------------------
// -----------------------------------------------------------------------------
static device_t *_find_dev_from_uid(unsigned int uid) static device_t *_find_dev_from_uid(unsigned int uid)
{ {
@ -305,7 +308,9 @@ static bool transfer_check_usb_compliance(usb_transfer_t *transfer, usb_transfer
return true; return true;
} }
// --------------------------------------------------- Allocation ------------------------------------------------------ // -----------------------------------------------------------------------------
// ----------------------------- Allocation ------------------------------------
// -----------------------------------------------------------------------------
static esp_err_t endpoint_alloc(device_t *dev_obj, const usb_ep_desc_t *ep_desc, usbh_ep_config_t *ep_config, endpoint_t **ep_obj_ret) static esp_err_t endpoint_alloc(device_t *dev_obj, const usb_ep_desc_t *ep_desc, usbh_ep_config_t *ep_config, endpoint_t **ep_obj_ret)
{ {
@ -358,10 +363,7 @@ static void endpoint_free(endpoint_t *ep_obj)
heap_caps_free(ep_obj); heap_caps_free(ep_obj);
} }
static esp_err_t device_alloc(unsigned int uid, static esp_err_t device_alloc(usbh_dev_params_t *params, device_t **dev_obj_ret)
usb_speed_t speed,
hcd_port_handle_t port_hdl,
device_t **dev_obj_ret)
{ {
device_t *dev_obj = heap_caps_calloc(1, sizeof(device_t), MALLOC_CAP_DEFAULT); device_t *dev_obj = heap_caps_calloc(1, sizeof(device_t), MALLOC_CAP_DEFAULT);
if (dev_obj == NULL) { if (dev_obj == NULL) {
@ -375,22 +377,23 @@ static esp_err_t device_alloc(unsigned int uid,
.callback_arg = (void *)dev_obj, .callback_arg = (void *)dev_obj,
.context = (void *)dev_obj, .context = (void *)dev_obj,
.ep_desc = NULL, // No endpoint descriptor means we're allocating a pipe for EP0 .ep_desc = NULL, // No endpoint descriptor means we're allocating a pipe for EP0
.dev_speed = speed, .dev_speed = params->speed,
.dev_addr = 0, .dev_addr = 0,
}; };
hcd_pipe_handle_t default_pipe_hdl; hcd_pipe_handle_t default_pipe_hdl;
ret = hcd_pipe_alloc(port_hdl, &pipe_config, &default_pipe_hdl); ret = hcd_pipe_alloc(params->root_port_hdl, &pipe_config, &default_pipe_hdl);
if (ret != ESP_OK) { if (ret != ESP_OK) {
goto err; goto err;
} }
// Initialize device object // Initialize device object
dev_obj->dynamic.state = USB_DEVICE_STATE_DEFAULT; dev_obj->dynamic.state = USB_DEVICE_STATE_DEFAULT;
dev_obj->constant.default_pipe = default_pipe_hdl; dev_obj->constant.default_pipe = default_pipe_hdl;
dev_obj->constant.port_hdl = port_hdl; dev_obj->constant.port_hdl = params->root_port_hdl;
dev_obj->constant.speed = speed; dev_obj->constant.parent_dev_hdl = params->parent_dev_hdl;
dev_obj->constant.uid = uid; dev_obj->constant.parent_port_num = params->parent_port_num;
dev_obj->constant.speed = params->speed;
dev_obj->constant.uid = params->uid;
// Note: Enumeration related dev_obj->constant fields are initialized later using usbh_dev_set_...() functions // Note: Enumeration related dev_obj->constant fields are initialized later using usbh_dev_set_...() functions
// Write-back device object // Write-back device object
*dev_obj_ret = dev_obj; *dev_obj_ret = dev_obj;
ret = ESP_OK; ret = ESP_OK;
@ -407,11 +410,11 @@ static void device_free(device_t *dev_obj)
if (dev_obj == NULL) { if (dev_obj == NULL) {
return; return;
} }
// Device descriptor might not have been set yet // Device descriptor might not have been allocated (in case of early enumeration failure)
if (dev_obj->constant.desc) { if (dev_obj->constant.desc) {
heap_caps_free(dev_obj->constant.desc); heap_caps_free(dev_obj->constant.desc);
} }
// Configuration descriptor might not have been set yet // Configuration might not have been allocated (in case of early enumeration failure)
if (dev_obj->constant.config_desc) { if (dev_obj->constant.config_desc) {
heap_caps_free(dev_obj->constant.config_desc); heap_caps_free(dev_obj->constant.config_desc);
} }
@ -429,7 +432,9 @@ static void device_free(device_t *dev_obj)
heap_caps_free(dev_obj); heap_caps_free(dev_obj);
} }
// ---------------------------------------------------- Callbacks ------------------------------------------------------ // -----------------------------------------------------------------------------
// -------------------------- Callbacks ----------------------------------------
// -----------------------------------------------------------------------------
static bool ep0_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr) static bool ep0_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
{ {
@ -487,7 +492,9 @@ static bool epN_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_
in_isr); in_isr);
} }
// -------------------------------------------------- Event Related ---------------------------------------------------- // -----------------------------------------------------------------------------
// ------------------------- Event Related -------------------------------------
// -----------------------------------------------------------------------------
static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags) static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags)
{ {
@ -581,6 +588,8 @@ static inline void handle_free(device_t *dev_obj)
{ {
// Cache a copy of the device's address as we are about to free the device object // Cache a copy of the device's address as we are about to free the device object
const unsigned int dev_uid = dev_obj->constant.uid; const unsigned int dev_uid = dev_obj->constant.uid;
usb_device_handle_t parent_dev_hdl = dev_obj->constant.parent_dev_hdl;
const uint8_t parent_port_num = dev_obj->constant.parent_port_num;
bool all_free; bool all_free;
ESP_LOGD(USBH_TAG, "Freeing device %d", dev_obj->constant.address); ESP_LOGD(USBH_TAG, "Freeing device %d", dev_obj->constant.address);
@ -605,6 +614,8 @@ static inline void handle_free(device_t *dev_obj)
.event = USBH_EVENT_DEV_FREE, .event = USBH_EVENT_DEV_FREE,
.dev_free_data = { .dev_free_data = {
.dev_uid = dev_uid, .dev_uid = dev_uid,
.parent_dev_hdl = parent_dev_hdl,
.port_num = parent_port_num,
} }
}; };
p_usbh_obj->constant.event_cb(&event_data, p_usbh_obj->constant.event_cb_arg); p_usbh_obj->constant.event_cb(&event_data, p_usbh_obj->constant.event_cb_arg);
@ -629,7 +640,9 @@ static inline void handle_prop_new_dev(device_t *dev_obj)
p_usbh_obj->constant.event_cb(&event_data, p_usbh_obj->constant.event_cb_arg); p_usbh_obj->constant.event_cb(&event_data, p_usbh_obj->constant.event_cb_arg);
} }
// -------------------------------------------- USBH Processing Functions ---------------------------------------------- // -----------------------------------------------------------------------------
// ------------------------- USBH Processing Functions -------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_install(const usbh_config_t *usbh_config) esp_err_t usbh_install(const usbh_config_t *usbh_config)
{ {
@ -766,7 +779,9 @@ esp_err_t usbh_process(void)
return ESP_OK; return ESP_OK;
} }
// ---------------------------------------------- Device Pool Functions ------------------------------------------------ // -----------------------------------------------------------------------------
// ------------------------- Device Pool Functions -----------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_devs_num(int *num_devs_ret) esp_err_t usbh_devs_num(int *num_devs_ret)
{ {
@ -825,14 +840,15 @@ esp_err_t usbh_devs_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *nu
return ESP_OK; return ESP_OK;
} }
esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle_t port_hdl) esp_err_t usbh_devs_add(usbh_dev_params_t *params)
{ {
USBH_CHECK(port_hdl != NULL, ESP_ERR_INVALID_ARG); USBH_CHECK(params != NULL, ESP_ERR_NOT_ALLOWED);
USBH_CHECK(params->root_port_hdl != NULL, ESP_ERR_INVALID_ARG);
esp_err_t ret; esp_err_t ret;
device_t *dev_obj; device_t *dev_obj;
// Allocate a device object (initialized to address 0) // Allocate a device object (initialized to address 0)
ret = device_alloc(uid, dev_speed, port_hdl, &dev_obj); ret = device_alloc(params, &dev_obj);
if (ret != ESP_OK) { if (ret != ESP_OK) {
return ret; return ret;
} }
@ -842,7 +858,7 @@ esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle
USBH_ENTER_CRITICAL(); USBH_ENTER_CRITICAL();
// Check that there is not already a device with the same uid // Check that there is not already a device with the same uid
if (_find_dev_from_uid(uid) != NULL) { if (_find_dev_from_uid(params->uid) != NULL) {
ret = ESP_ERR_INVALID_ARG; ret = ESP_ERR_INVALID_ARG;
goto exit; goto exit;
} }
@ -860,6 +876,11 @@ exit:
USBH_EXIT_CRITICAL(); USBH_EXIT_CRITICAL();
xSemaphoreGive(p_usbh_obj->constant.mux_lock); xSemaphoreGive(p_usbh_obj->constant.mux_lock);
if (ret != ESP_OK) {
// Free dev_obj for memory not to leak
device_free(dev_obj);
}
return ret; return ret;
} }
@ -901,6 +922,27 @@ exit:
return ret; return ret;
} }
esp_err_t usbh_devs_get_parent_info(unsigned int uid, usb_parent_dev_info_t *parent_info)
{
USBH_CHECK(parent_info, ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_FAIL;
device_t *dev_obj = NULL;
USBH_ENTER_CRITICAL();
dev_obj = _find_dev_from_uid(uid);
if (dev_obj == NULL) {
ret = ESP_ERR_NOT_FOUND;
goto exit;
} else {
parent_info->dev_hdl = dev_obj->constant.parent_dev_hdl;
parent_info->port_num = dev_obj->constant.parent_port_num;
ret = ESP_OK;
}
exit:
USBH_EXIT_CRITICAL();
return ret;
}
esp_err_t usbh_devs_mark_all_free(void) esp_err_t usbh_devs_mark_all_free(void)
{ {
USBH_ENTER_CRITICAL(); USBH_ENTER_CRITICAL();
@ -971,7 +1013,30 @@ esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
return ret; return ret;
} }
esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl) esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl)
{
device_t *dev_obj = (device_t *)dev_hdl;
bool call_proc_req_cb = false;
USBH_ENTER_CRITICAL();
// Device must be in the configured state
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_PROP_NEW_DEV);
USBH_EXIT_CRITICAL();
// Call the processing request callback
if (call_proc_req_cb) {
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
}
return ESP_OK;
}
// -----------------------------------------------------------------------------
// ---------------------------- Device Functions -------------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl)
{ {
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG); USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
device_t *dev_obj = (device_t *)dev_hdl; device_t *dev_obj = (device_t *)dev_hdl;
@ -1000,28 +1065,9 @@ esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl)
return ESP_OK; return ESP_OK;
} }
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl) // -----------------------------------------------------------------------------
{ // ---------------------------- Getters ----------------------------------------
device_t *dev_obj = (device_t *)dev_hdl; // -----------------------------------------------------------------------------
bool call_proc_req_cb = false;
USBH_ENTER_CRITICAL();
// Device must be in the configured state
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_PROP_NEW_DEV);
USBH_EXIT_CRITICAL();
// Call the processing request callback
if (call_proc_req_cb) {
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
}
return ESP_OK;
}
// ------------------------------------------------ Device Functions ---------------------------------------------------
// ----------------------- Getters -------------------------
esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr) esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr)
{ {
@ -1029,7 +1075,6 @@ esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr)
device_t *dev_obj = (device_t *)dev_hdl; device_t *dev_obj = (device_t *)dev_hdl;
USBH_ENTER_CRITICAL(); USBH_ENTER_CRITICAL();
USBH_CHECK_FROM_CRIT(dev_obj->constant.address > 0, ESP_ERR_INVALID_STATE);
*dev_addr = dev_obj->constant.address; *dev_addr = dev_obj->constant.address;
USBH_EXIT_CRITICAL(); USBH_EXIT_CRITICAL();
@ -1041,6 +1086,8 @@ esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_
USBH_CHECK(dev_hdl != NULL && dev_info != NULL, ESP_ERR_INVALID_ARG); USBH_CHECK(dev_hdl != NULL && dev_info != NULL, ESP_ERR_INVALID_ARG);
device_t *dev_obj = (device_t *)dev_hdl; device_t *dev_obj = (device_t *)dev_hdl;
dev_info->parent.dev_hdl = dev_obj->constant.parent_dev_hdl;
dev_info->parent.port_num = dev_obj->constant.parent_port_num;
dev_info->speed = dev_obj->constant.speed; dev_info->speed = dev_obj->constant.speed;
dev_info->dev_addr = dev_obj->constant.address; dev_info->dev_addr = dev_obj->constant.address;
// Device descriptor might not have been set yet // Device descriptor might not have been set yet
@ -1067,7 +1114,6 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
{ {
USBH_CHECK(dev_hdl != NULL && dev_desc_ret != NULL, ESP_ERR_INVALID_ARG); USBH_CHECK(dev_hdl != NULL && dev_desc_ret != NULL, ESP_ERR_INVALID_ARG);
device_t *dev_obj = (device_t *)dev_hdl; device_t *dev_obj = (device_t *)dev_hdl;
*dev_desc_ret = dev_obj->constant.desc; *dev_desc_ret = dev_obj->constant.desc;
return ESP_OK; return ESP_OK;
} }
@ -1082,7 +1128,9 @@ esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config
return ESP_OK; return ESP_OK;
} }
// ----------------------- Setters ------------------------- // -----------------------------------------------------------------------------
// -------------------------------- Setters ------------------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_dev_enum_lock(usb_device_handle_t dev_hdl) esp_err_t usbh_dev_enum_lock(usb_device_handle_t dev_hdl)
{ {
@ -1330,7 +1378,9 @@ err:
return ret; return ret;
} }
// ----------------------------------------------- Endpoint Functions ------------------------------------------------- // -----------------------------------------------------------------------------
// ----------------------------- Endpoint Functions ----------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config, usbh_ep_handle_t *ep_hdl_ret) esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config, usbh_ep_handle_t *ep_hdl_ret)
{ {
@ -1460,7 +1510,9 @@ void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl)
return hcd_pipe_get_context(ep_obj->constant.pipe_hdl); return hcd_pipe_get_context(ep_obj->constant.pipe_hdl);
} }
// ----------------------------------------------- Transfer Functions -------------------------------------------------- // -----------------------------------------------------------------------------
// ------------------------ Transfer Functions ---------------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb) esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb)
{ {