esp-idf/components/usb/private_include/usbh.h
Darian Leung 99ec1c98f5
refactor(usb/usbh): Update USBH device creation and enumeration handling
This commit updates how the USBH handles device creation and enumeration so that
upper layers (such as the Hub driver) can use the USBH API for enumeration instead
of calling the HCD.

USBH Updates:

USBH now creates unenumerated devices set to address 0 with no device/config
descriptor. A newly created device can be opened and communicated with immediately
(using control transfers). This allows the Hub driver to call the USBH instead of
the HCD. Summary of USBH changes:

- Added new APIs to add/remove a device. Devices are now created as unenumerated
and can be immediately opened and communicated with.
- Added new APIs to enumerate a device (see 'usbh_dev_set_...()' functions). Device
must be locked (see 'usbh_dev_enum_lock()') before enumeration functions can be called.
- Added UID for each device. This allows the particular USBH without needing to
use the device's handle (which implies opening the device).

Hub Driver Updates:

Hub driver now calls the USBH for enumeration. Summary of USBH changes:

- Replace all 'hcd_pipe_...()' calls with 'usbh_dev_...()' calls
- Refactored port event handling to fit with new USBH API
- Updated to use UID to uniquely identify devices without opening them

USB Host Updates:

- Reroute USBH control transfers to clients and hub driver
2024-04-23 17:18:56 +08:00

522 lines
18 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <sys/queue.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hcd.h"
#include "usb/usb_types_ch9.h"
#include "usb/usb_types_stack.h"
#ifdef __cplusplus
extern "C" {
#endif
// ------------------------------------------------------ Types --------------------------------------------------------
// ----------------------- Handles -------------------------
/**
* @brief Handle of a allocated endpoint
*/
typedef struct usbh_ep_handle_s *usbh_ep_handle_t;
// ----------------------- Events --------------------------
/**
* @brief Enumerator for various USBH events
*/
typedef enum {
USBH_EVENT_CTRL_XFER, /**< A control transfer has completed */
USBH_EVENT_NEW_DEV, /**< A new device has been enumerated and added to the device pool */
USBH_EVENT_DEV_GONE, /**< A device is gone. Clients should close the device */
USBH_EVENT_DEV_FREE, /**< A device has been freed. Its upstream port can now be recycled */
USBH_EVENT_ALL_FREE, /**< All devices have been freed */
} usbh_event_t;
/**
* @brief Event data object for USBH events
*/
typedef struct {
usbh_event_t event;
union {
struct {
usb_device_handle_t dev_hdl;
urb_t *urb;
} ctrl_xfer_data;
struct {
uint8_t dev_addr;
} new_dev_data;
struct {
uint8_t dev_addr;
usb_device_handle_t dev_hdl;
} dev_gone_data;
struct {
unsigned int dev_uid;
} dev_free_data;
};
} usbh_event_data_t;
/**
* @brief Endpoint events
*
* @note Optimization: Keep this identical to hcd_pipe_event_t
*/
typedef enum {
USBH_EP_EVENT_NONE, /**< The EP has no events (used to indicate no events when polling) */
USBH_EP_EVENT_URB_DONE, /**< The EP has completed a URB. The URB can be dequeued */
USBH_EP_EVENT_ERROR_XFER, /**< The EP encountered excessive errors when transferring a URB i.e., three three consecutive transaction errors (e.g., no ACK, bad CRC etc) */
USBH_EP_EVENT_ERROR_URB_NOT_AVAIL, /**< The EP tried to execute a transfer but no URB was available */
USBH_EP_EVENT_ERROR_OVERFLOW, /**< The EP received more data than requested. Usually a Packet babble error (i.e., an IN packet has exceeded the EP's MPS) */
USBH_EP_EVENT_ERROR_STALL, /**< EP received a STALL response */
} usbh_ep_event_t;
// ------------------ Requests/Commands --------------------
/**
* @brief Endpoint commands
*
* @note Optimization: Keep this identical to hcd_pipe_cmd_t
*/
typedef enum {
USBH_EP_CMD_HALT, /**< Halt an active endpoint. Any currently executing URB will be canceled. Enqueued URBs are left untouched */
USBH_EP_CMD_FLUSH, /**< Can only be called when halted. Will cause all enqueued URBs to be canceled */
USBH_EP_CMD_CLEAR, /**< Causes a halted endpoint to become active again. Any enqueued URBs will being executing.*/
} usbh_ep_cmd_t;
// ---------------------- Callbacks ------------------------
/**
* @brief Callback used to indicate that the USBH has an event
*
* @note This callback is called from within usbh_process()
*/
typedef void (*usbh_event_cb_t)(usbh_event_data_t *event_data, void *arg);
/**
* @brief Callback used to indicate an event on an endpoint
*
* Return whether to yield or not if called from an ISR. Always return false if not called from an ISR
*/
typedef bool (*usbh_ep_cb_t)(usbh_ep_handle_t ep_hdl, usbh_ep_event_t ep_event, void *arg, bool in_isr);
// ----------------------- Objects -------------------------
/**
* @brief Configuration for an endpoint being allocated using usbh_ep_alloc()
*/
typedef struct {
uint8_t bInterfaceNumber; /**< Interface number */
uint8_t bAlternateSetting; /**< Alternate setting number of the interface */
uint8_t bEndpointAddress; /**< Endpoint address */
usbh_ep_cb_t ep_cb; /**< Endpoint event callback */
void *ep_cb_arg; /**< Endpoint callback argument */
void *context; /**< Endpoint context */
} usbh_ep_config_t;
/**
* @brief USBH configuration used in usbh_install()
*/
typedef struct {
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
void *proc_req_cb_arg; /**< Processing request callback argument */
usbh_event_cb_t event_cb; /**< USBH event callback */
void *event_cb_arg; /**< USBH event callback argument */
} usbh_config_t;
// -------------------------------------------- USBH Processing Functions ----------------------------------------------
/**
* @brief Installs the USBH driver
*
* - This function will internally install the HCD
* - This must be called before calling any Hub driver functions
*
* @note Before calling this function, the Host Controller must already be un-clock gated and reset. The USB PHY
* (internal or external, and associated GPIOs) must already be configured.
* @param usbh_config USBH driver configuration
* @return esp_err_t
*/
esp_err_t usbh_install(const usbh_config_t *usbh_config);
/**
* @brief Uninstall the USBH driver
*
* - This function will uninstall the HCD
* - The Hub driver must be uninstalled before calling this function
*
* @note This function will simply free the resources used by the USBH. The underlying Host Controller and USB PHY will
* not be disabled.
* @return esp_err_t
*/
esp_err_t usbh_uninstall(void);
/**
* @brief USBH processing function
*
* - USBH processing function that must be called repeatedly to process USBH events
* - If blocking, the caller can block until the proc_req_cb() is called with USB_PROC_REQ_SOURCE_USBH as the request
* source. The USB_PROC_REQ_SOURCE_USBH source indicates that this function should be called.
*
* @note This function can block
* @return esp_err_t
*/
esp_err_t usbh_process(void);
// ---------------------------------------------- Device Pool Functions ------------------------------------------------
/**
* @brief Get the current number of devices
*
* @note This function can block
* @param[out] num_devs_ret Current number of devices
* @return esp_err_t
*/
esp_err_t usbh_devs_num(int *num_devs_ret);
/**
* @brief Fill list with address of currently connected devices
*
* - This function fills the provided list with the address of current connected devices
* - Device address can then be used in usbh_devs_open()
* - If there are more devices than the list_len, this function will only fill
* up to list_len number of devices.
*
* @param[in] list_len Length of empty list
* @param[inout] dev_addr_list Empty list to be filled
* @param[out] num_dev_ret Number of devices filled into list
* @return esp_err_t
*/
esp_err_t usbh_devs_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret);
/**
* @brief Create a device and add it to the device pool
*
* The created device will not be enumerated where the device's address is 0,
* device and config descriptor are NULL. The device will still have a default
* pipe, thus allowing control transfers to be submitted.
*
* - Call usbh_devs_open() before communicating with the device
* - Call usbh_dev_enum_lock() before enumerating the device via the various
* usbh_dev_set_...() functions.
*
* @param[in] uid Unique ID assigned to the device
* @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
*/
esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle_t port_hdl);
/**
* @brief Indicates to the USBH that a device is gone
*
* @param[in] uid Unique ID assigned to the device on creation (see 'usbh_devs_add()')
* @return esp_err_t
*/
esp_err_t usbh_devs_remove(unsigned int uid);
/**
* @brief Mark that all devices should be freed at the next possible opportunity
*
* A device marked as free will not be freed until the last client using the device has called usbh_devs_close()
*
* @return
* - ESP_OK: There were no devices to free to begin with. Current state is all free
* - ESP_ERR_NOT_FINISHED: One or more devices still need to be freed (but have been marked "to be freed")
*/
esp_err_t usbh_devs_mark_all_free(void);
/**
* @brief Open a device by address
*
* A device must be opened before it can be used
*
* @param[in] dev_addr Device address
* @param[out] dev_hdl Device handle
* @return esp_err_t
*/
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
*
* This is typically called after a device has been fully enumerated.
*
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl);
// ------------------------------------------------ Device Functions ---------------------------------------------------
// ----------------------- Getters -------------------------
/**
* @brief Get a device's address
*
* @note Can be called without opening the device
*
* @param[in] dev_hdl Device handle
* @param[out] dev_addr Device's address
* @return esp_err_t
*/
esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr);
/**
* @brief Get a device's information
*
* @note It is possible that the device has not been enumerated yet, thus some
* fields may be NULL.
* @param[in] dev_hdl Device handle
* @param[out] dev_info Device information
* @return esp_err_t
*/
esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_info);
/**
* @brief Get a device's device descriptor
*
* - 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
* device descriptor could be NULL.
* @param[in] dev_hdl Device handle
* @param[out] dev_desc_ret Device descriptor
* @return esp_err_t
*/
esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t **dev_desc_ret);
/**
* @brief Get a device's active 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
* configuration descriptor could be NULL.
* @param[in] dev_hdl Device handle
* @param config_desc_ret
* @return esp_err_t
*/
esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc_ret);
// ----------------------- Setters -------------------------
/**
* @brief Lock a device for enumeration
*
* - A device's enumeration lock must be set before any of its enumeration fields
* (e.g., address, device/config descriptors) can be set/updated.
* - The caller must be the sole opener of the device (see 'usbh_devs_open()')
* when locking the device for enumeration.
*
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_dev_enum_lock(usb_device_handle_t dev_hdl);
/**
* @brief Release a device's enumeration lock
*
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_dev_enum_unlock(usb_device_handle_t dev_hdl);
/**
* @brief Set the maximum packet size of EP0 for a device
*
* Typically called during enumeration after obtaining the first 8 bytes of the
* device's descriptor.
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
* @param[in] dev_hdl Device handle
* @param[in] wMaxPacketSize Maximum packet size
* @return esp_err_t
*/
esp_err_t usbh_dev_set_ep0_mps(usb_device_handle_t dev_hdl, uint16_t wMaxPacketSize);
/**
* @brief Set a device's address
*
* Typically called during enumeration after a SET_ADDRESSS request has be
* sent to the device.
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
* @param[in] dev_hdl Device handle
* @param[in] dev_addr
* @return esp_err_t
*/
esp_err_t usbh_dev_set_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
/**
* @brief Set a device's descriptor
*
* Typically called during enumeration after obtaining the device's descriptor
* via a GET_DESCRIPTOR request.
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
* @param[in] dev_hdl Device handle
* @param[in] device_desc Device descriptor to copy
* @return esp_err_t
*/
esp_err_t usbh_dev_set_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc);
/**
* @brief Set a device's configuration descriptor
*
* Typically called during enumeration after obtaining the device's configuration
* descriptor via a GET_DESCRIPTOR request.
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
* @param[in] dev_hdl Device handle
* @param[in] config_desc_full Configuration descriptor to copy
* @return esp_err_t
*/
esp_err_t usbh_dev_set_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc_full);
/**
* @brief Set a device's string descriptor
*
* Typically called during enumeration after obtaining one of the device's string
* descriptor via a GET_DESCRIPTOR request.
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
* @param[in] dev_hdl Device handle
* @param[in] str_desc String descriptor to copy
* @param[in] select Select string descriptor. 0/1/2 for Manufacturer/Product/Serial
* Number string descriptors respectively
* @return esp_err_t
*/
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 -------------------------------------------------
/**
* @brief Allocate an endpoint on a device
*
* This function allows clients to allocate a non-default endpoint (i.e., not EP0) on a connected device
*
* - A client must have opened the device using usbh_devs_open() before attempting to allocate an endpoint on the device
* - A client should call this function to allocate all endpoints in an interface that the client has claimed.
* - A client must allocate an endpoint using this function before attempting to communicate with it
* - Once the client allocates an endpoint, the client is now owns/manages the endpoint. No other client should use or
* deallocate the endpoint.
*
* @note This function can block
* @note Default endpoints (EP0) are owned by the USBH. For control transfers, use usbh_dev_submit_ctrl_urb() instead
*
* @param[in] dev_hdl Device handle
* @param[in] ep_config Endpoint configuration
* @param[out] ep_hdl_ret Endpoint handle
* @return esp_err_t
*/
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);
/**
* @brief Free and endpoint on a device
*
* This function frees an endpoint previously allocated by the client using usbh_ep_alloc()
*
* - Only the client that allocated the endpoint should free it
* - The client must have halted and flushed the endpoint using usbh_ep_command() before attempting to free it
* - The client must ensure that there are no more function calls to the endpoint before freeing it
*
* @note This function can block
* @param[in] ep_hdl Endpoint handle
* @return esp_err_t
*/
esp_err_t usbh_ep_free(usbh_ep_handle_t ep_hdl);
/**
* @brief Get the handle of an endpoint using its address
*
* The endpoint must have been previously allocated using usbh_ep_alloc()
*
* @param[in] dev_hdl Device handle
* @param[in] bEndpointAddress Endpoint address
* @param[out] ep_hdl_ret Endpoint handle
* @return esp_err_t
*/
esp_err_t usbh_ep_get_handle(usb_device_handle_t dev_hdl, uint8_t bEndpointAddress, usbh_ep_handle_t *ep_hdl_ret);
/**
* @brief Execute a command on a particular endpoint
*
* Endpoint commands allows executing a certain action on an endpoint (e.g., halting, flushing, clearing etc)
*
* @param[in] ep_hdl Endpoint handle
* @param[in] command Endpoint command
* @return esp_err_t
*/
esp_err_t usbh_ep_command(usbh_ep_handle_t ep_hdl, usbh_ep_cmd_t command);
/**
* @brief Get the context of an endpoint
*
* Get the context variable assigned to and endpoint on allocation.
*
* @note This function can block
* @param[in] ep_hdl Endpoint handle
* @return Endpoint context
*/
void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl);
// ----------------------------------------------- Transfer Functions --------------------------------------------------
/**
* @brief Submit a control transfer (URB) to a device
*
* @param[in] dev_hdl Device handle
* @param[in] urb URB
* @return esp_err_t
*/
esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb);
/**
* @brief Enqueue a URB to an endpoint
*
* The URB will remain enqueued until it completes (successfully or errors out). Use usbh_ep_dequeue_urb() to dequeue
* a completed URB.
*
* @param[in] ep_hdl Endpoint handle
* @param[in] urb URB to enqueue
* @return esp_err_t
*/
esp_err_t usbh_ep_enqueue_urb(usbh_ep_handle_t ep_hdl, urb_t *urb);
/**
* @brief Dequeue a URB from an endpoint
*
* Dequeue a completed URB from an endpoint. The USBH_EP_EVENT_URB_DONE indicates that URBs can be dequeued
*
* @param[in] ep_hdl Endpoint handle
* @param[out] urb_ret Dequeued URB, or NULL if no more URBs to dequeue
* @return esp_err_t
*/
esp_err_t usbh_ep_dequeue_urb(usbh_ep_handle_t ep_hdl, urb_t **urb_ret);
#ifdef __cplusplus
}
#endif