mirror of
https://github.com/espressif/esp-idf.git
synced 2025-11-14 14:30:28 +01:00
Merge branch 'refactor/usb_host_usbh_api' into 'master'
refactor(usb/host): Refactor USBH API See merge request espressif/esp-idf!30029
This commit is contained in:
@@ -205,7 +205,7 @@ typedef struct {
|
||||
* - The peripheral must have been reset and clock un-gated
|
||||
* - The USB PHY (internal or external) and associated GPIOs must already be configured
|
||||
* - GPIO pins configured
|
||||
* - Interrupt allocated but DISABLED (in case of an unknown interupt state)
|
||||
* - Interrupt allocated but DISABLED (in case of an unknown interrupt state)
|
||||
* Exit:
|
||||
* - Checks to see if DWC_OTG is alive, and if HW version/config is correct
|
||||
* - HAl context initialized
|
||||
@@ -290,7 +290,7 @@ static inline void usb_dwc_hal_port_init(usb_dwc_hal_context_t *hal)
|
||||
/**
|
||||
* @brief Deinitialize the host port
|
||||
*
|
||||
* - Will disable the host port's interrupts preventing further port aand channel events from ocurring
|
||||
* - Will disable the host port's interrupts preventing further port aand channel events from occurring
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
@@ -333,7 +333,6 @@ static inline void usb_dwc_hal_port_toggle_power(usb_dwc_hal_context_t *hal, boo
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_toggle_reset(usb_dwc_hal_context_t *hal, bool enable)
|
||||
{
|
||||
HAL_ASSERT(hal->channels.num_allocd == 0); //Cannot reset if there are still allocated channels
|
||||
usb_dwc_ll_hprt_set_port_reset(hal->dev, enable);
|
||||
}
|
||||
|
||||
@@ -447,7 +446,7 @@ static inline void usb_dwc_hal_port_periodic_enable(usb_dwc_hal_context_t *hal)
|
||||
/**
|
||||
* @brief Disable periodic scheduling
|
||||
*
|
||||
* Disabling periodic scheduling will save a bit of DMA bandwith (as the controller will no longer fetch the schedule
|
||||
* Disabling periodic scheduling will save a bit of DMA bandwidth (as the controller will no longer fetch the schedule
|
||||
* from the frame list).
|
||||
*
|
||||
* @note Before disabling periodic scheduling, it is the user's responsibility to ensure that all periodic channels have
|
||||
@@ -505,17 +504,17 @@ static inline usb_dwc_speed_t usb_dwc_hal_port_get_conn_speed(usb_dwc_hal_contex
|
||||
* @brief Disable the debounce lock
|
||||
*
|
||||
* This function must be called after calling usb_dwc_hal_port_check_if_connected() and will allow connection/disconnection
|
||||
* events to occur again. Any pending connection or disconenction interrupts are cleared.
|
||||
* events to occur again. Any pending connection or disconnection interrupts are cleared.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usb_dwc_hal_disable_debounce_lock(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
hal->flags.dbnc_lock_enabled = 0;
|
||||
//Clear Conenction and disconenction interrupt in case it triggered again
|
||||
//Clear Connection and disconnection interrupt in case it triggered again
|
||||
usb_dwc_ll_gintsts_clear_intrs(hal->dev, USB_DWC_LL_INTR_CORE_DISCONNINT);
|
||||
usb_dwc_ll_hprt_intr_clear(hal->dev, USB_DWC_LL_INTR_HPRT_PRTCONNDET);
|
||||
//Reenable the hprt (connection) and disconnection interrupts
|
||||
//Re-enable the hprt (connection) and disconnection interrupts
|
||||
usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_DISCONNINT);
|
||||
}
|
||||
|
||||
@@ -672,10 +671,10 @@ bool usb_dwc_hal_chan_request_halt(usb_dwc_hal_chan_t *chan_obj);
|
||||
/**
|
||||
* @brief Indicate that a channel is halted after a port error
|
||||
*
|
||||
* When a port error occurs (e.g., discconect, overcurrent):
|
||||
* When a port error occurs (e.g., disconnect, overcurrent):
|
||||
* - Any previously active channels will remain active (i.e., they will not receive a channel interrupt)
|
||||
* - Attempting to disable them using usb_dwc_hal_chan_request_halt() will NOT generate an interrupt for ISOC channels
|
||||
* (probalby something to do with the periodic scheduling)
|
||||
* (probably something to do with the periodic scheduling)
|
||||
*
|
||||
* However, the channel's enable bit can be left as 1 since after a port error, a soft reset will be done anyways.
|
||||
* This function simply updates the channels internal state variable to indicate it is halted (thus allowing it to be
|
||||
|
||||
@@ -214,9 +214,7 @@ struct pipe_obj {
|
||||
uint32_t waiting_halt: 1;
|
||||
uint32_t pipe_cmd_processing: 1;
|
||||
uint32_t has_urb: 1; // Indicates there is at least one URB either pending, in-flight, or done
|
||||
uint32_t persist: 1; // indicates that this pipe should persist through a run-time port reset
|
||||
uint32_t reset_lock: 1; // Indicates that this pipe is undergoing a run-time reset
|
||||
uint32_t reserved27: 27;
|
||||
uint32_t reserved29: 29;
|
||||
};
|
||||
uint32_t val;
|
||||
} cs_flags;
|
||||
@@ -560,28 +558,6 @@ static esp_err_t _pipe_cmd_clear(pipe_t *pipe);
|
||||
|
||||
// ------------------------ Port ---------------------------
|
||||
|
||||
/**
|
||||
* @brief Prepare persistent pipes for reset
|
||||
*
|
||||
* This function checks if all pipes are reset persistent and proceeds to free their underlying HAL channels for the
|
||||
* persistent pipes. This should be called before a run time reset
|
||||
*
|
||||
* @param port Port object
|
||||
* @return true All pipes are persistent and their channels are freed
|
||||
* @return false Not all pipes are persistent
|
||||
*/
|
||||
static bool _port_persist_all_pipes(port_t *port);
|
||||
|
||||
/**
|
||||
* @brief Recovers all persistent pipes after a reset
|
||||
*
|
||||
* This function will recover all persistent pipes after a reset and reallocate their underlying HAl channels. This
|
||||
* function should be called after a reset.
|
||||
*
|
||||
* @param port Port object
|
||||
*/
|
||||
static void _port_recover_all_pipes(port_t *port);
|
||||
|
||||
/**
|
||||
* @brief Checks if all pipes are in the halted state
|
||||
*
|
||||
@@ -1162,44 +1138,6 @@ esp_err_t hcd_uninstall(void)
|
||||
|
||||
// ----------------------- Helpers -------------------------
|
||||
|
||||
static bool _port_persist_all_pipes(port_t *port)
|
||||
{
|
||||
if (port->num_pipes_queued > 0) {
|
||||
// All pipes must be idle before we run-time reset
|
||||
return false;
|
||||
}
|
||||
bool all_persist = true;
|
||||
pipe_t *pipe;
|
||||
// Check that each pipe is persistent
|
||||
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
|
||||
if (!pipe->cs_flags.persist) {
|
||||
all_persist = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!all_persist) {
|
||||
// At least one pipe is not persistent. All pipes must be freed or made persistent before we can reset
|
||||
return false;
|
||||
}
|
||||
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
|
||||
pipe->cs_flags.reset_lock = 1;
|
||||
usb_dwc_hal_chan_free(port->hal, pipe->chan_obj);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _port_recover_all_pipes(port_t *port)
|
||||
{
|
||||
pipe_t *pipe;
|
||||
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
|
||||
pipe->cs_flags.persist = 0;
|
||||
pipe->cs_flags.reset_lock = 0;
|
||||
usb_dwc_hal_chan_alloc(port->hal, pipe->chan_obj, (void *)pipe);
|
||||
usb_dwc_hal_chan_set_ep_char(port->hal, pipe->chan_obj, &pipe->ep_char);
|
||||
}
|
||||
CACHE_SYNC_FRAME_LIST(port->frame_list);
|
||||
}
|
||||
|
||||
static bool _port_check_all_pipes_halted(port_t *port)
|
||||
{
|
||||
bool all_halted = true;
|
||||
@@ -1276,20 +1214,26 @@ static esp_err_t _port_cmd_power_off(port_t *port)
|
||||
static esp_err_t _port_cmd_reset(port_t *port)
|
||||
{
|
||||
esp_err_t ret;
|
||||
// Port can only a reset when it is in the enabled or disabled states (in case of new connection)
|
||||
|
||||
// Port can only a reset when it is in the enabled or disabled (in the case of a new connection)states.
|
||||
if (port->state != HCD_PORT_STATE_ENABLED && port->state != HCD_PORT_STATE_DISABLED) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto exit;
|
||||
}
|
||||
bool is_runtime_reset = (port->state == HCD_PORT_STATE_ENABLED) ? true : false;
|
||||
if (is_runtime_reset && !_port_persist_all_pipes(port)) {
|
||||
// If this is a run time reset, check all pipes that are still allocated can persist the reset
|
||||
// Port can only be reset if all pipes are idle
|
||||
if (port->num_pipes_queued > 0) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto exit;
|
||||
}
|
||||
// All pipes (if any_) are guaranteed to be persistent at this point. Proceed to resetting the bus
|
||||
/*
|
||||
Proceed to resetting the bus
|
||||
- Update the port's state variable
|
||||
- Hold the bus in the reset state for RESET_HOLD_MS.
|
||||
- Return the bus to the idle state for RESET_RECOVERY_MS
|
||||
*/
|
||||
port->state = HCD_PORT_STATE_RESETTING;
|
||||
// Put and hold the bus in the reset state. If the port was previously enabled, a disabled event will occur after this
|
||||
|
||||
// Place the bus into the reset state. If the port was previously enabled, a disabled event will occur after this
|
||||
usb_dwc_hal_port_toggle_reset(port->hal, true);
|
||||
HCD_EXIT_CRITICAL();
|
||||
vTaskDelay(pdMS_TO_TICKS(RESET_HOLD_MS));
|
||||
@@ -1299,7 +1243,8 @@ static esp_err_t _port_cmd_reset(port_t *port)
|
||||
ret = ESP_ERR_INVALID_RESPONSE;
|
||||
goto bailout;
|
||||
}
|
||||
// Return the bus to the idle state and hold it for the required reset recovery time. Port enabled event should occur
|
||||
|
||||
// Return the bus to the idle state. Port enabled event should occur
|
||||
usb_dwc_hal_port_toggle_reset(port->hal, false);
|
||||
HCD_EXIT_CRITICAL();
|
||||
vTaskDelay(pdMS_TO_TICKS(RESET_RECOVERY_MS));
|
||||
@@ -1309,16 +1254,20 @@ static esp_err_t _port_cmd_reset(port_t *port)
|
||||
ret = ESP_ERR_INVALID_RESPONSE;
|
||||
goto bailout;
|
||||
}
|
||||
// Set FIFO sizes based on the selected biasing
|
||||
usb_dwc_hal_set_fifo_bias(port->hal, port->fifo_bias);
|
||||
// We start periodic scheduling only after a RESET command since SOFs only start after a reset
|
||||
usb_dwc_hal_port_set_frame_list(port->hal, port->frame_list, FRAME_LIST_LEN);
|
||||
usb_dwc_hal_port_periodic_enable(port->hal);
|
||||
|
||||
// Reinitialize port registers.
|
||||
usb_dwc_hal_set_fifo_bias(port->hal, port->fifo_bias); // Set FIFO biases
|
||||
usb_dwc_hal_port_set_frame_list(port->hal, port->frame_list, FRAME_LIST_LEN); // Set periodic frame list
|
||||
usb_dwc_hal_port_periodic_enable(port->hal); // Enable periodic scheduling
|
||||
|
||||
ret = ESP_OK;
|
||||
bailout:
|
||||
if (is_runtime_reset) {
|
||||
_port_recover_all_pipes(port);
|
||||
// Reinitialize channel registers
|
||||
pipe_t *pipe;
|
||||
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
|
||||
usb_dwc_hal_chan_set_ep_char(port->hal, pipe->chan_obj, &pipe->ep_char);
|
||||
}
|
||||
CACHE_SYNC_FRAME_LIST(port->frame_list);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@@ -1987,8 +1936,7 @@ esp_err_t hcd_pipe_free(hcd_pipe_handle_t pipe_hdl)
|
||||
HCD_ENTER_CRITICAL();
|
||||
// Check that all URBs have been removed and pipe has no pending events
|
||||
HCD_CHECK_FROM_CRIT(!pipe->multi_buffer_control.buffer_is_executing
|
||||
&& !pipe->cs_flags.has_urb
|
||||
&& !pipe->cs_flags.reset_lock,
|
||||
&& !pipe->cs_flags.has_urb,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
// Remove pipe from the list of idle pipes (it must be in the idle list because it should have no queued URBs)
|
||||
TAILQ_REMOVE(&pipe->port->pipes_idle_tailq, pipe, tailq_entry);
|
||||
@@ -2011,8 +1959,7 @@ esp_err_t hcd_pipe_update_mps(hcd_pipe_handle_t pipe_hdl, int mps)
|
||||
HCD_ENTER_CRITICAL();
|
||||
// Check if pipe is in the correct state to be updated
|
||||
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
|
||||
!pipe->cs_flags.has_urb &&
|
||||
!pipe->cs_flags.reset_lock,
|
||||
!pipe->cs_flags.has_urb,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
pipe->ep_char.mps = mps;
|
||||
// Update the underlying channel's registers
|
||||
@@ -2027,8 +1974,7 @@ esp_err_t hcd_pipe_update_dev_addr(hcd_pipe_handle_t pipe_hdl, uint8_t dev_addr)
|
||||
HCD_ENTER_CRITICAL();
|
||||
// Check if pipe is in the correct state to be updated
|
||||
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
|
||||
!pipe->cs_flags.has_urb &&
|
||||
!pipe->cs_flags.reset_lock,
|
||||
!pipe->cs_flags.has_urb,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
pipe->ep_char.dev_addr = dev_addr;
|
||||
// Update the underlying channel's registers
|
||||
@@ -2037,35 +1983,6 @@ esp_err_t hcd_pipe_update_dev_addr(hcd_pipe_handle_t pipe_hdl, uint8_t dev_addr)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t hcd_pipe_update_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_callback_t callback, void *user_arg)
|
||||
{
|
||||
pipe_t *pipe = (pipe_t *)pipe_hdl;
|
||||
HCD_ENTER_CRITICAL();
|
||||
// Check if pipe is in the correct state to be updated
|
||||
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
|
||||
!pipe->cs_flags.has_urb &&
|
||||
!pipe->cs_flags.reset_lock,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
pipe->callback = callback;
|
||||
pipe->callback_arg = user_arg;
|
||||
HCD_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t hcd_pipe_set_persist_reset(hcd_pipe_handle_t pipe_hdl)
|
||||
{
|
||||
pipe_t *pipe = (pipe_t *)pipe_hdl;
|
||||
HCD_ENTER_CRITICAL();
|
||||
// Check if pipe is in the correct state to be updated
|
||||
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
|
||||
!pipe->cs_flags.has_urb &&
|
||||
!pipe->cs_flags.reset_lock,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
pipe->cs_flags.persist = 1;
|
||||
HCD_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void *hcd_pipe_get_context(hcd_pipe_handle_t pipe_hdl)
|
||||
{
|
||||
pipe_t *pipe = (pipe_t *)pipe_hdl;
|
||||
@@ -2102,27 +2019,22 @@ esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command)
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
HCD_ENTER_CRITICAL();
|
||||
// Cannot execute pipe commands the pipe is already executing a command, or if the pipe or its port are no longer valid
|
||||
if (pipe->cs_flags.reset_lock) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
} else {
|
||||
pipe->cs_flags.pipe_cmd_processing = 1;
|
||||
switch (command) {
|
||||
case HCD_PIPE_CMD_HALT: {
|
||||
ret = _pipe_cmd_halt(pipe);
|
||||
break;
|
||||
}
|
||||
case HCD_PIPE_CMD_FLUSH: {
|
||||
ret = _pipe_cmd_flush(pipe);
|
||||
break;
|
||||
}
|
||||
case HCD_PIPE_CMD_CLEAR: {
|
||||
ret = _pipe_cmd_clear(pipe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pipe->cs_flags.pipe_cmd_processing = 0;
|
||||
pipe->cs_flags.pipe_cmd_processing = 1;
|
||||
switch (command) {
|
||||
case HCD_PIPE_CMD_HALT: {
|
||||
ret = _pipe_cmd_halt(pipe);
|
||||
break;
|
||||
}
|
||||
case HCD_PIPE_CMD_FLUSH: {
|
||||
ret = _pipe_cmd_flush(pipe);
|
||||
break;
|
||||
}
|
||||
case HCD_PIPE_CMD_CLEAR: {
|
||||
ret = _pipe_cmd_clear(pipe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pipe->cs_flags.pipe_cmd_processing = 0;
|
||||
HCD_EXIT_CRITICAL();
|
||||
return ret;
|
||||
}
|
||||
@@ -2658,8 +2570,7 @@ esp_err_t hcd_urb_enqueue(hcd_pipe_handle_t pipe_hdl, urb_t *urb)
|
||||
// Check that pipe and port are in the correct state to receive URBs
|
||||
HCD_CHECK_FROM_CRIT(pipe->port->state == HCD_PORT_STATE_ENABLED // The pipe's port must be in the correct state
|
||||
&& pipe->state == HCD_PIPE_STATE_ACTIVE // The pipe must be in the correct state
|
||||
&& !pipe->cs_flags.pipe_cmd_processing // Pipe cannot currently be processing a pipe command
|
||||
&& !pipe->cs_flags.reset_lock, // Pipe cannot be persisting through a port reset
|
||||
&& !pipe->cs_flags.pipe_cmd_processing, // Pipe cannot currently be processing a pipe command
|
||||
ESP_ERR_INVALID_STATE);
|
||||
// Use the URB's reserved_ptr to store the pipe's
|
||||
urb->hcd_ptr = (void *)pipe;
|
||||
|
||||
@@ -40,6 +40,7 @@ implement the bare minimum to control the root HCD port.
|
||||
|
||||
#define ENUM_CTRL_TRANSFER_MAX_DATA_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE
|
||||
#define ENUM_DEV_ADDR 1 // Device address used in enumeration
|
||||
#define ENUM_DEV_UID 1 // Unique ID for device connected to root port
|
||||
#define ENUM_CONFIG_INDEX_DEFAULT 0 // Index used to get the first configuration descriptor of the device
|
||||
#define ENUM_SHORT_DESC_REQ_LEN 8 // Number of bytes to request when getting a short descriptor (just enough to get bMaxPacketSize0 or wTotalLength)
|
||||
#define ENUM_WORST_CASE_MPS_LS 8 // The worst case MPS of EP0 for a LS device
|
||||
@@ -50,9 +51,12 @@ implement the bare minimum to control the root HCD port.
|
||||
|
||||
// Hub driver action flags. LISTED IN THE ORDER THEY SHOULD BE HANDLED IN within hub_process(). Some actions are mutually exclusive
|
||||
#define HUB_DRIVER_FLAG_ACTION_ROOT_EVENT 0x01
|
||||
#define HUB_DRIVER_FLAG_ACTION_PORT 0x02
|
||||
#define HUB_DRIVER_FLAG_ACTION_PORT_REQ 0x02
|
||||
#define HUB_DRIVER_FLAG_ACTION_ENUM_EVENT 0x04
|
||||
|
||||
#define PORT_REQ_DISABLE 0x01
|
||||
#define PORT_REQ_RECOVER 0x02
|
||||
|
||||
/**
|
||||
* @brief Root port states
|
||||
*
|
||||
@@ -60,9 +64,8 @@ implement the bare minimum to control the root HCD port.
|
||||
typedef enum {
|
||||
ROOT_PORT_STATE_NOT_POWERED, /**< Root port initialized and/or not powered */
|
||||
ROOT_PORT_STATE_POWERED, /**< Root port is powered, device is not connected */
|
||||
ROOT_PORT_STATE_ENUM, /**< A device has been connected to the root port and is undergoing enumeration */
|
||||
ROOT_PORT_STATE_ENUM_FAILED, /**< Enumeration of a connected device has failed. Waiting for that device to be disconnected */
|
||||
ROOT_PORT_ACTIVE, /**< The connected device was enumerated and port is active */
|
||||
ROOT_PORT_STATE_DISABLED, /**< A device is connected but is disabled (i.e., not reset, no SOFs are sent) */
|
||||
ROOT_PORT_STATE_ENABLED, /**< A device is connected, port has been reset, SOFs are sent */
|
||||
ROOT_PORT_STATE_RECOVERY, /**< Root port encountered an error and needs to be recovered */
|
||||
} root_port_state_t;
|
||||
|
||||
@@ -156,7 +159,6 @@ typedef struct {
|
||||
urb_t *urb; /**< URB used for enumeration control transfers. Max data length of ENUM_CTRL_TRANSFER_MAX_DATA_LEN */
|
||||
// Initialized at start of a particular enumeration
|
||||
usb_device_handle_t dev_hdl; /**< Handle of device being enumerated */
|
||||
hcd_pipe_handle_t pipe; /**< Default pipe handle of the device being enumerated */
|
||||
// Updated during enumeration
|
||||
enum_stage_t stage; /**< Current enumeration stage */
|
||||
int expect_num_bytes; /**< Expected number of bytes for IN transfers stages. Set to 0 for OUT transfer */
|
||||
@@ -185,10 +187,11 @@ typedef struct {
|
||||
uint32_t val;
|
||||
} flags;
|
||||
root_port_state_t root_port_state;
|
||||
unsigned int port_reqs;
|
||||
} dynamic;
|
||||
// Single thread members don't require a critical section so long as they are never accessed from multiple threads
|
||||
struct {
|
||||
usb_device_handle_t root_dev_hdl; // Indicates if an enumerated device is connected to the root port
|
||||
unsigned int root_dev_uid; // UID of the device connected to root port. 0 if no device connected
|
||||
enum_ctrl_t enum_ctrl;
|
||||
} single_thread;
|
||||
// Constant members do no change after installation thus do not require a critical section
|
||||
@@ -241,40 +244,26 @@ const char *HUB_DRIVER_TAG = "HUB";
|
||||
static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr);
|
||||
|
||||
/**
|
||||
* @brief HCD pipe callback for the default pipe of the device under enumeration
|
||||
* @brief Control transfer callback used for enumeration
|
||||
*
|
||||
* - This callback is called from the context of the HCD, so any event handling should be deferred to hub_process()
|
||||
* - This callback needs to call proc_req_cb to ensure that hub_process() gets a chance to run
|
||||
*
|
||||
* @param pipe_hdl HCD pipe handle
|
||||
* @param pipe_event Pipe event
|
||||
* @param user_arg Callback argument
|
||||
* @param in_isr Whether callback is in an ISR context
|
||||
* @return Whether a yield is required
|
||||
* @param transfer Transfer object
|
||||
*/
|
||||
static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr);
|
||||
static void enum_transfer_callback(usb_transfer_t *transfer);
|
||||
|
||||
// ------------------------------------------------- Enum Functions ----------------------------------------------------
|
||||
|
||||
static bool enum_stage_start(enum_ctrl_t *enum_ctrl)
|
||||
{
|
||||
// Get the speed of the device, and set the enum MPS to the worst case size for now
|
||||
usb_speed_t speed;
|
||||
if (hcd_port_get_speed(p_hub_driver_obj->constant.root_port_hdl, &speed) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
enum_ctrl->bMaxPacketSize0 = (speed == USB_SPEED_LOW) ? ENUM_WORST_CASE_MPS_LS : ENUM_WORST_CASE_MPS_FS;
|
||||
// Try to add the device to USBH
|
||||
usb_device_handle_t enum_dev_hdl;
|
||||
hcd_pipe_handle_t enum_dflt_pipe_hdl;
|
||||
// We use NULL as the parent device to indicate the Root Hub port 1. We currently only support a single device
|
||||
if (usbh_hub_add_dev(p_hub_driver_obj->constant.root_port_hdl, speed, &enum_dev_hdl, &enum_dflt_pipe_hdl) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
// Set our own default pipe callback
|
||||
ESP_ERROR_CHECK(hcd_pipe_update_callback(enum_dflt_pipe_hdl, enum_dflt_pipe_callback, NULL));
|
||||
enum_ctrl->dev_hdl = enum_dev_hdl;
|
||||
enum_ctrl->pipe = enum_dflt_pipe_hdl;
|
||||
// Open the newly added device (at address 0)
|
||||
ESP_ERROR_CHECK(usbh_devs_open(0, &p_hub_driver_obj->single_thread.enum_ctrl.dev_hdl));
|
||||
|
||||
// Get the speed of the device to set the initial MPS of EP0
|
||||
usb_device_info_t dev_info;
|
||||
ESP_ERROR_CHECK(usbh_dev_get_info(p_hub_driver_obj->single_thread.enum_ctrl.dev_hdl, &dev_info));
|
||||
enum_ctrl->bMaxPacketSize0 = (dev_info.speed == USB_SPEED_LOW) ? ENUM_WORST_CASE_MPS_LS : ENUM_WORST_CASE_MPS_FS;
|
||||
|
||||
// Lock the device for enumeration. This allows us call usbh_dev_set_...() functions during enumeration
|
||||
ESP_ERROR_CHECK(usbh_dev_enum_lock(p_hub_driver_obj->single_thread.enum_ctrl.dev_hdl));
|
||||
|
||||
// Flag to gracefully exit the enumeration process if requested by the user in the enumeration filter cb
|
||||
#ifdef ENABLE_ENUM_FILTER_CALLBACK
|
||||
@@ -285,7 +274,6 @@ static bool enum_stage_start(enum_ctrl_t *enum_ctrl)
|
||||
|
||||
static bool enum_stage_second_reset(enum_ctrl_t *enum_ctrl)
|
||||
{
|
||||
ESP_ERROR_CHECK(hcd_pipe_set_persist_reset(enum_ctrl->pipe)); // Persist the default pipe through the reset
|
||||
if (hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_RESET) != ESP_OK) {
|
||||
ESP_LOGE(HUB_DRIVER_TAG, "Failed to issue second reset");
|
||||
return false;
|
||||
@@ -442,7 +430,7 @@ static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
if (hcd_urb_enqueue(enum_ctrl->pipe, enum_ctrl->urb) != ESP_OK) {
|
||||
if (usbh_dev_submit_ctrl_urb(enum_ctrl->dev_hdl, enum_ctrl->urb) != ESP_OK) {
|
||||
ESP_LOGE(HUB_DRIVER_TAG, "Failed to submit: %s", enum_stage_strings[enum_ctrl->stage]);
|
||||
return false;
|
||||
}
|
||||
@@ -467,18 +455,10 @@ static bool enum_stage_wait(enum_ctrl_t *enum_ctrl)
|
||||
|
||||
static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
|
||||
{
|
||||
// Dequeue the URB
|
||||
urb_t *dequeued_enum_urb = hcd_urb_dequeue(enum_ctrl->pipe);
|
||||
assert(dequeued_enum_urb == enum_ctrl->urb);
|
||||
|
||||
// Check transfer status
|
||||
usb_transfer_t *transfer = &dequeued_enum_urb->transfer;
|
||||
usb_transfer_t *transfer = &enum_ctrl->urb->transfer;
|
||||
if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) {
|
||||
ESP_LOGE(HUB_DRIVER_TAG, "Bad transfer status %d: %s", transfer->status, enum_stage_strings[enum_ctrl->stage]);
|
||||
if (transfer->status == USB_TRANSFER_STATUS_STALL) {
|
||||
// EP stalled, clearing the pipe to execute further stages
|
||||
ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_CLEAR));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Check IN transfer returned the expected correct number of bytes
|
||||
@@ -505,8 +485,8 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
// Update and save the MPS of the default pipe
|
||||
if (hcd_pipe_update_mps(enum_ctrl->pipe, device_desc->bMaxPacketSize0) != ESP_OK) {
|
||||
// Update and save the MPS of the EP0
|
||||
if (usbh_dev_set_ep0_mps(enum_ctrl->dev_hdl, device_desc->bMaxPacketSize0) != ESP_OK) {
|
||||
ESP_LOGE(HUB_DRIVER_TAG, "Failed to update MPS");
|
||||
ret = false;
|
||||
break;
|
||||
@@ -517,16 +497,15 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
|
||||
break;
|
||||
}
|
||||
case ENUM_STAGE_CHECK_ADDR: {
|
||||
// Update the pipe and device's address, and fill the address into the device object
|
||||
ESP_ERROR_CHECK(hcd_pipe_update_dev_addr(enum_ctrl->pipe, ENUM_DEV_ADDR));
|
||||
ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_addr(enum_ctrl->dev_hdl, ENUM_DEV_ADDR));
|
||||
// Update the device's address
|
||||
ESP_ERROR_CHECK(usbh_dev_set_addr(enum_ctrl->dev_hdl, ENUM_DEV_ADDR));
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
case ENUM_STAGE_CHECK_FULL_DEV_DESC: {
|
||||
// Fill device descriptor into the device object
|
||||
// Set the device's descriptor
|
||||
const usb_device_desc_t *device_desc = (const usb_device_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
|
||||
ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_desc(enum_ctrl->dev_hdl, device_desc));
|
||||
ESP_ERROR_CHECK(usbh_dev_set_desc(enum_ctrl->dev_hdl, device_desc));
|
||||
enum_ctrl->iManufacturer = device_desc->iManufacturer;
|
||||
enum_ctrl->iProduct = device_desc->iProduct;
|
||||
enum_ctrl->iSerialNumber = device_desc->iSerialNumber;
|
||||
@@ -555,10 +534,10 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
|
||||
break;
|
||||
}
|
||||
case ENUM_STAGE_CHECK_FULL_CONFIG_DESC: {
|
||||
// Fill configuration descriptor into the device object
|
||||
// Set the device's configuration descriptor
|
||||
const usb_config_desc_t *config_desc = (usb_config_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t));
|
||||
enum_ctrl->bConfigurationValue = config_desc->bConfigurationValue;
|
||||
ESP_ERROR_CHECK(usbh_hub_enum_fill_config_desc(enum_ctrl->dev_hdl, config_desc));
|
||||
ESP_ERROR_CHECK(usbh_dev_set_config_desc(enum_ctrl->dev_hdl, config_desc));
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
@@ -635,7 +614,7 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
|
||||
} else { // ENUM_STAGE_CHECK_FULL_PROD_STR_DESC
|
||||
select = 2;
|
||||
}
|
||||
ESP_ERROR_CHECK(usbh_hub_enum_fill_str_desc(enum_ctrl->dev_hdl, str_desc, select));
|
||||
ESP_ERROR_CHECK(usbh_dev_set_str_desc(enum_ctrl->dev_hdl, str_desc, select));
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
@@ -650,42 +629,27 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
|
||||
|
||||
static void enum_stage_cleanup(enum_ctrl_t *enum_ctrl)
|
||||
{
|
||||
// We currently only support a single device connected to the root port. Move the device handle from enum to root
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_ACTIVE;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
p_hub_driver_obj->single_thread.root_dev_hdl = enum_ctrl->dev_hdl;
|
||||
usb_device_handle_t dev_hdl = enum_ctrl->dev_hdl;
|
||||
// Unlock the device as we are done with the enumeration
|
||||
ESP_ERROR_CHECK(usbh_dev_enum_unlock(enum_ctrl->dev_hdl));
|
||||
// Propagate a new device event
|
||||
ESP_ERROR_CHECK(usbh_devs_new_dev_event(enum_ctrl->dev_hdl));
|
||||
// We are done with using the device. Close it.
|
||||
ESP_ERROR_CHECK(usbh_devs_close(enum_ctrl->dev_hdl));
|
||||
// Clear values in enum_ctrl
|
||||
enum_ctrl->dev_hdl = NULL;
|
||||
enum_ctrl->pipe = NULL;
|
||||
// Update device object after enumeration is done
|
||||
ESP_ERROR_CHECK(usbh_hub_enum_done(dev_hdl));
|
||||
}
|
||||
|
||||
static void enum_stage_cleanup_failed(enum_ctrl_t *enum_ctrl)
|
||||
{
|
||||
// Enumeration failed. Clear the enum device handle and pipe
|
||||
if (enum_ctrl->dev_hdl) {
|
||||
// If enumeration failed due to a port event, we need to Halt, flush, and dequeue enum default pipe in case there
|
||||
// was an in-flight URB.
|
||||
ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_HALT));
|
||||
ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_FLUSH));
|
||||
hcd_urb_dequeue(enum_ctrl->pipe); // This could return NULL if there
|
||||
ESP_ERROR_CHECK(usbh_hub_enum_failed(enum_ctrl->dev_hdl)); // Free the underlying device object first before recovering the port
|
||||
// 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_devs_close(enum_ctrl->dev_hdl));
|
||||
// We allow this to fail in case the device object was already freed
|
||||
usbh_devs_remove(ENUM_DEV_UID);
|
||||
}
|
||||
// Clear values in enum_ctrl
|
||||
enum_ctrl->dev_hdl = NULL;
|
||||
enum_ctrl->pipe = NULL;
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
// Enum could have failed due to a port error. If so, we need to trigger a port recovery
|
||||
if (p_hub_driver_obj->dynamic.root_port_state == ROOT_PORT_STATE_RECOVERY) {
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT;
|
||||
} else {
|
||||
// Otherwise, we move to the enum failed state and wait for the device to disconnect
|
||||
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_ENUM_FAILED;
|
||||
}
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
}
|
||||
|
||||
static enum_stage_t get_next_stage(enum_stage_t old_stage, enum_ctrl_t *enum_ctrl)
|
||||
@@ -799,13 +763,13 @@ static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port
|
||||
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 bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
|
||||
static void enum_transfer_callback(usb_transfer_t *transfer)
|
||||
{
|
||||
// Note: This callback may have triggered when a failed enumeration is already cleaned up (e.g., due to a failed port reset)
|
||||
// We simply trigger a processing request to handle the completed enumeration control transfer
|
||||
HUB_DRIVER_ENTER_CRITICAL_SAFE();
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
|
||||
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);
|
||||
p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, false, p_hub_driver_obj->constant.proc_req_cb_arg);
|
||||
}
|
||||
|
||||
// ---------------------- Handlers -------------------------
|
||||
@@ -818,17 +782,32 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
|
||||
// Nothing to do
|
||||
break;
|
||||
case HCD_PORT_EVENT_CONNECTION: {
|
||||
if (hcd_port_command(root_port_hdl, HCD_PORT_CMD_RESET) == ESP_OK) {
|
||||
ESP_LOGD(HUB_DRIVER_TAG, "Root port reset");
|
||||
// Start enumeration
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
|
||||
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_ENUM;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
p_hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_START;
|
||||
} else {
|
||||
if (hcd_port_command(root_port_hdl, HCD_PORT_CMD_RESET) != ESP_OK) {
|
||||
ESP_LOGE(HUB_DRIVER_TAG, "Root port reset failed");
|
||||
goto reset_err;
|
||||
}
|
||||
ESP_LOGD(HUB_DRIVER_TAG, "Root port reset");
|
||||
usb_speed_t speed;
|
||||
if (hcd_port_get_speed(p_hub_driver_obj->constant.root_port_hdl, &speed) != ESP_OK) {
|
||||
goto new_dev_err;
|
||||
}
|
||||
// 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) {
|
||||
ESP_LOGE(HUB_DRIVER_TAG, "Failed to add device");
|
||||
goto new_dev_err;
|
||||
}
|
||||
p_hub_driver_obj->single_thread.root_dev_uid = ENUM_DEV_UID;
|
||||
// Start enumeration
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
|
||||
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_ENABLED;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
p_hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_START;
|
||||
break;
|
||||
new_dev_err:
|
||||
// We allow this to fail in case a disconnect/port error happens while disabling.
|
||||
hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_DISABLE);
|
||||
reset_err:
|
||||
break;
|
||||
}
|
||||
case HCD_PORT_EVENT_DISCONNECTION:
|
||||
@@ -838,17 +817,13 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
switch (p_hub_driver_obj->dynamic.root_port_state) {
|
||||
case ROOT_PORT_STATE_POWERED: // This occurred before enumeration
|
||||
case ROOT_PORT_STATE_ENUM_FAILED: // This occurred after a failed enumeration.
|
||||
// Therefore, there's no device and we can go straight to port recovery
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT;
|
||||
case ROOT_PORT_STATE_DISABLED: // This occurred after the device has already been disabled
|
||||
// Therefore, there's no device object to clean up, and we can go straight to port recovery
|
||||
p_hub_driver_obj->dynamic.port_reqs |= PORT_REQ_RECOVER;
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_REQ;
|
||||
break;
|
||||
case ROOT_PORT_STATE_ENUM:
|
||||
// This occurred during enumeration. Therefore, we need to cleanup the failed enumeration
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT;
|
||||
p_hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_CLEANUP_FAILED;
|
||||
break;
|
||||
case ROOT_PORT_ACTIVE:
|
||||
// There was an enumerated device. We need to indicate to USBH that the device is gone
|
||||
case ROOT_PORT_STATE_ENABLED:
|
||||
// There is an enabled (active) device. We need to indicate to USBH that the device is gone
|
||||
pass_event_to_usbh = true;
|
||||
break;
|
||||
default:
|
||||
@@ -858,7 +833,10 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
|
||||
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_RECOVERY;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
if (pass_event_to_usbh) {
|
||||
ESP_ERROR_CHECK(usbh_hub_dev_gone(p_hub_driver_obj->single_thread.root_dev_hdl));
|
||||
// The port must have a device object
|
||||
assert(p_hub_driver_obj->single_thread.root_dev_uid != 0);
|
||||
// We allow this to fail in case the device object was already freed
|
||||
usbh_devs_remove(p_hub_driver_obj->single_thread.root_dev_uid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -868,6 +846,30 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
|
||||
}
|
||||
}
|
||||
|
||||
static void root_port_req(hcd_port_handle_t root_port_hdl)
|
||||
{
|
||||
unsigned int port_reqs;
|
||||
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
port_reqs = p_hub_driver_obj->dynamic.port_reqs;
|
||||
p_hub_driver_obj->dynamic.port_reqs = 0;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
|
||||
if (port_reqs & PORT_REQ_DISABLE) {
|
||||
ESP_LOGD(HUB_DRIVER_TAG, "Disabling root port");
|
||||
// We allow this to fail in case a disconnect/port error happens while disabling.
|
||||
hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_DISABLE);
|
||||
}
|
||||
if (port_reqs & PORT_REQ_RECOVER) {
|
||||
ESP_LOGD(HUB_DRIVER_TAG, "Recovering root port");
|
||||
ESP_ERROR_CHECK(hcd_port_recover(p_hub_driver_obj->constant.root_port_hdl));
|
||||
ESP_ERROR_CHECK(hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON));
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_POWERED;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
}
|
||||
}
|
||||
|
||||
static void enum_handle_events(void)
|
||||
{
|
||||
bool stage_pass;
|
||||
@@ -926,7 +928,6 @@ static void enum_handle_events(void)
|
||||
stage_pass = true;
|
||||
break;
|
||||
default:
|
||||
// Note: Don't abort here. The enum_dflt_pipe_callback() can trigger a HUB_DRIVER_FLAG_ACTION_ENUM_EVENT after a cleanup.
|
||||
stage_pass = true;
|
||||
break;
|
||||
}
|
||||
@@ -948,18 +949,22 @@ static void enum_handle_events(void)
|
||||
|
||||
// ---------------------------------------------- Hub Driver Functions -------------------------------------------------
|
||||
|
||||
esp_err_t hub_install(hub_config_t *hub_config)
|
||||
esp_err_t hub_install(hub_config_t *hub_config, void **client_ret)
|
||||
{
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj == NULL, ESP_ERR_INVALID_STATE);
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
esp_err_t ret;
|
||||
|
||||
// Allocate Hub driver object
|
||||
hub_driver_t *hub_driver_obj = heap_caps_calloc(1, sizeof(hub_driver_t), MALLOC_CAP_DEFAULT);
|
||||
urb_t *enum_urb = urb_alloc(sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_DATA_LEN, 0);
|
||||
if (hub_driver_obj == NULL || enum_urb == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_err_t ret;
|
||||
enum_urb->usb_host_client = (void *)hub_driver_obj;
|
||||
enum_urb->transfer.callback = enum_transfer_callback;
|
||||
|
||||
// Install HCD port
|
||||
hcd_port_config_t port_config = {
|
||||
.fifo_bias = HUB_ROOT_HCD_PORT_FIFO_BIAS,
|
||||
@@ -972,6 +977,7 @@ esp_err_t hub_install(hub_config_t *hub_config)
|
||||
if (ret != ESP_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Initialize Hub driver object
|
||||
hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_NONE;
|
||||
hub_driver_obj->single_thread.enum_ctrl.urb = enum_urb;
|
||||
@@ -991,6 +997,9 @@ esp_err_t hub_install(hub_config_t *hub_config)
|
||||
}
|
||||
p_hub_driver_obj = hub_driver_obj;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
|
||||
// Write-back client_ret pointer
|
||||
*client_ret = (void *)hub_driver_obj;
|
||||
ret = ESP_OK;
|
||||
|
||||
return ret;
|
||||
@@ -1052,17 +1061,31 @@ esp_err_t hub_root_stop(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t hub_dev_is_free(uint8_t dev_addr)
|
||||
esp_err_t hub_port_recycle(unsigned int dev_uid)
|
||||
{
|
||||
assert(dev_addr == ENUM_DEV_ADDR);
|
||||
assert(p_hub_driver_obj->single_thread.root_dev_hdl);
|
||||
p_hub_driver_obj->single_thread.root_dev_hdl = NULL;
|
||||
// Device is free, we can now request its port be recycled
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
if (dev_uid == p_hub_driver_obj->single_thread.root_dev_uid) {
|
||||
// Device is free, we can now request its port be recycled
|
||||
hcd_port_state_t port_state = hcd_port_get_state(p_hub_driver_obj->constant.root_port_hdl);
|
||||
p_hub_driver_obj->single_thread.root_dev_uid = 0;
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
// How the port is recycled will depend on the port's state
|
||||
switch (port_state) {
|
||||
case HCD_PORT_STATE_ENABLED:
|
||||
p_hub_driver_obj->dynamic.port_reqs |= PORT_REQ_DISABLE;
|
||||
break;
|
||||
case HCD_PORT_STATE_RECOVERY:
|
||||
p_hub_driver_obj->dynamic.port_reqs |= PORT_REQ_RECOVER;
|
||||
break;
|
||||
default:
|
||||
abort(); // Should never occur
|
||||
break;
|
||||
}
|
||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_REQ;
|
||||
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);
|
||||
}
|
||||
|
||||
p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, false, p_hub_driver_obj->constant.proc_req_cb_arg);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -1077,29 +1100,8 @@ esp_err_t hub_process(void)
|
||||
if (action_flags & HUB_DRIVER_FLAG_ACTION_ROOT_EVENT) {
|
||||
root_port_handle_events(p_hub_driver_obj->constant.root_port_hdl);
|
||||
}
|
||||
if (action_flags & HUB_DRIVER_FLAG_ACTION_PORT) {
|
||||
// Check current state of port
|
||||
hcd_port_state_t port_state = hcd_port_get_state(p_hub_driver_obj->constant.root_port_hdl);
|
||||
switch (port_state) {
|
||||
case HCD_PORT_STATE_ENABLED:
|
||||
// Port is still enabled with a connect device. Disable it.
|
||||
ESP_LOGD(HUB_DRIVER_TAG, "Disabling root port");
|
||||
// We allow this to fail in case a disconnect/port error happens while disabling.
|
||||
hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_DISABLE);
|
||||
break;
|
||||
case HCD_PORT_STATE_RECOVERY:
|
||||
// Port is in recovery after a disconnect/error. Recover it.
|
||||
ESP_LOGD(HUB_DRIVER_TAG, "Recovering root port");
|
||||
ESP_ERROR_CHECK(hcd_port_recover(p_hub_driver_obj->constant.root_port_hdl));
|
||||
ESP_ERROR_CHECK(hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON));
|
||||
HUB_DRIVER_ENTER_CRITICAL();
|
||||
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_POWERED;
|
||||
HUB_DRIVER_EXIT_CRITICAL();
|
||||
break;
|
||||
default:
|
||||
abort(); // Should never occur
|
||||
break;
|
||||
}
|
||||
if (action_flags & HUB_DRIVER_FLAG_ACTION_PORT_REQ) {
|
||||
root_port_req(p_hub_driver_obj->constant.root_port_hdl);
|
||||
}
|
||||
if (action_flags & HUB_DRIVER_FLAG_ACTION_ENUM_EVENT) {
|
||||
enum_handle_events();
|
||||
|
||||
@@ -418,36 +418,6 @@ esp_err_t hcd_pipe_update_mps(hcd_pipe_handle_t pipe_hdl, int mps);
|
||||
*/
|
||||
esp_err_t hcd_pipe_update_dev_addr(hcd_pipe_handle_t pipe_hdl, uint8_t dev_addr);
|
||||
|
||||
/**
|
||||
* @brief Update a pipe's callback
|
||||
*
|
||||
* This function is intended to be called on default pipes at the end of enumeration to switch to a callback that
|
||||
* handles the completion of regular control transfer.
|
||||
* - Pipe is not current processing a command
|
||||
* - Pipe does not have any enqueued URBs
|
||||
* - Port cannot be resetting
|
||||
*
|
||||
* @param pipe_hdl Pipe handle
|
||||
* @param callback Callback
|
||||
* @param user_arg Callback argument
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hcd_pipe_update_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_callback_t callback, void *user_arg);
|
||||
|
||||
/**
|
||||
* @brief Make a pipe persist through a run time reset
|
||||
*
|
||||
* Normally when a HCD_PORT_CMD_RESET is called, all pipes should already have been freed. However There may be cases
|
||||
* (such as during enumeration) when a pipe must persist through a reset. This function will mark a pipe as
|
||||
* persistent allowing it to survive a reset. When HCD_PORT_CMD_RESET is called, the pipe can continue to be used after
|
||||
* the reset.
|
||||
*
|
||||
* @param pipe_hdl Pipe handle
|
||||
* @retval ESP_OK: Pipe successfully marked as persistent
|
||||
* @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be made persistent
|
||||
*/
|
||||
esp_err_t hcd_pipe_set_persist_reset(hcd_pipe_handle_t pipe_hdl);
|
||||
|
||||
/**
|
||||
* @brief Get the context variable of a pipe from its handle
|
||||
*
|
||||
|
||||
@@ -42,9 +42,10 @@ typedef struct {
|
||||
* - Initializes the HCD root port
|
||||
*
|
||||
* @param[in] hub_config Hub driver configuration
|
||||
* @param[out] client_ret Unique pointer to identify the Hub as a USB Host client
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hub_install(hub_config_t *hub_config);
|
||||
esp_err_t hub_install(hub_config_t *hub_config, void **client_ret);
|
||||
|
||||
/**
|
||||
* @brief Uninstall Hub driver
|
||||
@@ -78,15 +79,16 @@ esp_err_t hub_root_start(void);
|
||||
esp_err_t hub_root_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Indicate to the Hub driver that a device has been freed
|
||||
* @brief Indicate to the Hub driver that a device's port can be recycled
|
||||
*
|
||||
* Hub driver can now recover the port that the device was connected to
|
||||
* The device connected to the port has been freed. The Hub driver can now
|
||||
* recycled the port.
|
||||
*
|
||||
* @param dev_addr Device address
|
||||
* @param dev_uid Device's unique ID
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
*/
|
||||
esp_err_t hub_dev_is_free(uint8_t dev_addr);
|
||||
esp_err_t hub_port_recycle(unsigned int dev_uid);
|
||||
|
||||
/**
|
||||
* @brief Hub driver's processing function
|
||||
|
||||
@@ -58,7 +58,7 @@ typedef struct {
|
||||
usb_device_handle_t dev_hdl;
|
||||
} dev_gone_data;
|
||||
struct {
|
||||
uint8_t dev_addr;
|
||||
unsigned int dev_uid;
|
||||
} dev_free_data;
|
||||
};
|
||||
} usbh_event_data_t;
|
||||
@@ -130,7 +130,7 @@ typedef struct {
|
||||
void *event_cb_arg; /**< USBH event callback argument */
|
||||
} usbh_config_t;
|
||||
|
||||
// ------------------------------------------------- USBH Functions ----------------------------------------------------
|
||||
// -------------------------------------------- USBH Processing Functions ----------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Installs the USBH driver
|
||||
@@ -169,6 +169,8 @@ esp_err_t usbh_uninstall(void);
|
||||
*/
|
||||
esp_err_t usbh_process(void);
|
||||
|
||||
// ---------------------------------------------- Device Pool Functions ------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Get the current number of devices
|
||||
*
|
||||
@@ -176,17 +178,13 @@ esp_err_t usbh_process(void);
|
||||
* @param[out] num_devs_ret Current number of devices
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_num_devs(int *num_devs_ret);
|
||||
|
||||
// ------------------------------------------------ Device Functions ---------------------------------------------------
|
||||
|
||||
// --------------------- Device Pool -----------------------
|
||||
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_dev_open()
|
||||
* - 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.
|
||||
*
|
||||
@@ -195,7 +193,44 @@ esp_err_t usbh_num_devs(int *num_devs_ret);
|
||||
* @param[out] num_dev_ret Number of devices filled into list
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret);
|
||||
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
|
||||
@@ -206,30 +241,31 @@ esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num
|
||||
* @param[out] dev_hdl Device handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_dev_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_dev_open()
|
||||
* Device can be opened by calling 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);
|
||||
esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl);
|
||||
|
||||
/**
|
||||
* @brief Mark that all devices should be freed at the next possible opportunity
|
||||
* @brief Trigger a USBH_EVENT_NEW_DEV event for the device
|
||||
*
|
||||
* A device marked as free will not be freed until the last client using the device has called usbh_dev_close()
|
||||
* This is typically called after a device has been fully enumerated.
|
||||
*
|
||||
* @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")
|
||||
* @param[in] dev_hdl Device handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_dev_mark_all_free(void);
|
||||
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl);
|
||||
|
||||
// ------------------- Single Device ----------------------
|
||||
// ------------------------------------------------ Device Functions ---------------------------------------------------
|
||||
|
||||
// ----------------------- Getters -------------------------
|
||||
|
||||
/**
|
||||
* @brief Get a device's address
|
||||
@@ -245,7 +281,8 @@ esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr);
|
||||
/**
|
||||
* @brief Get a device's information
|
||||
*
|
||||
* @note This function can block
|
||||
* @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
|
||||
@@ -257,6 +294,8 @@ esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_
|
||||
*
|
||||
* - 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
|
||||
@@ -268,21 +307,108 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
|
||||
*
|
||||
* Simply returns a reference to the internally cached configuration descriptor
|
||||
*
|
||||
* @note This function can block
|
||||
* @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 Submit a control transfer (URB) to a device
|
||||
* @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
|
||||
* @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);
|
||||
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 -------------------------------------------------
|
||||
|
||||
@@ -291,7 +417,7 @@ esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb);
|
||||
*
|
||||
* 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_dev_open() before attempting to allocate an endpoint on the 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
|
||||
@@ -334,6 +460,39 @@ esp_err_t usbh_ep_free(usbh_ep_handle_t ep_hdl);
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -357,132 +516,6 @@ esp_err_t usbh_ep_enqueue_urb(usbh_ep_handle_t ep_hdl, urb_t *urb);
|
||||
*/
|
||||
esp_err_t usbh_ep_dequeue_urb(usbh_ep_handle_t ep_hdl, urb_t **urb_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);
|
||||
|
||||
// -------------------------------------------------- Hub Functions ----------------------------------------------------
|
||||
|
||||
// ------------------- Device Related ----------------------
|
||||
|
||||
/**
|
||||
* @brief Indicates to USBH the start of enumeration for a device
|
||||
*
|
||||
* - The Hub driver calls this function before it starts enumerating a new device.
|
||||
* - The USBH will allocate a new device that will be initialized by the Hub driver using the remaining hub enumeration
|
||||
* functions.
|
||||
* - The new device's default pipe handle is returned to all the Hub driver to be used during enumeration.
|
||||
*
|
||||
* @note Hub Driver only
|
||||
* @param[in] port_hdl Handle of the port that the device is connected to
|
||||
* @param[in] dev_speed Device's speed
|
||||
* @param[out] new_dev_hdl Device's handle
|
||||
* @param[out] default_pipe_hdl Device's default pipe handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_add_dev(hcd_port_handle_t port_hdl, usb_speed_t dev_speed, usb_device_handle_t *new_dev_hdl, hcd_pipe_handle_t *default_pipe_hdl);
|
||||
|
||||
/**
|
||||
* @brief Indicates to the USBH that a device is gone
|
||||
*
|
||||
* @param dev_hdl Device handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_dev_gone(usb_device_handle_t dev_hdl);
|
||||
|
||||
// ----------------- Enumeration Related -------------------
|
||||
|
||||
/**
|
||||
* @brief Assign the enumerating device's address
|
||||
*
|
||||
* @note Hub Driver only
|
||||
* @note Must call in sequence
|
||||
* @param[in] dev_hdl Device handle
|
||||
* @param dev_addr
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
|
||||
|
||||
/**
|
||||
* @brief Fill the enumerating device's descriptor
|
||||
*
|
||||
* @note Hub Driver only
|
||||
* @note Must call in sequence
|
||||
* @param[in] dev_hdl Device handle
|
||||
* @param device_desc
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc);
|
||||
|
||||
/**
|
||||
* @brief Fill the enumerating device's active configuration descriptor
|
||||
*
|
||||
* @note Hub Driver only
|
||||
* @note Must call in sequence
|
||||
* @note This function can block
|
||||
* @param[in] dev_hdl Device handle
|
||||
* @param config_desc_full
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_enum_fill_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc_full);
|
||||
|
||||
/**
|
||||
* @brief Fill one of the string descriptors of the enumerating device
|
||||
*
|
||||
* @note Hub Driver only
|
||||
* @note Must call in sequence
|
||||
* @param dev_hdl Device handle
|
||||
* @param str_desc Pointer to string descriptor
|
||||
* @param select Select which string descriptor. 0/1/2 for Manufacturer/Product/Serial Number string descriptors respectively
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_enum_fill_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select);
|
||||
|
||||
/**
|
||||
* @brief Indicate the device enumeration is completed
|
||||
*
|
||||
* This will allow the device to be opened by clients, and also trigger a USBH_EVENT_NEW_DEV event.
|
||||
*
|
||||
* @note Hub Driver only
|
||||
* @note Must call in sequence
|
||||
* @note This function can block
|
||||
* @param[in] dev_hdl Device handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_enum_done(usb_device_handle_t dev_hdl);
|
||||
|
||||
/**
|
||||
* @brief Indicate that device enumeration has failed
|
||||
*
|
||||
* This will cause the enumerating device's resources to be cleaned up
|
||||
* The Hub Driver must guarantee that the enumerating device's default pipe is already halted, flushed, and dequeued.
|
||||
*
|
||||
* @note Hub Driver only
|
||||
* @note Must call in sequence
|
||||
* @note This function can block
|
||||
* @param[in] dev_hdl Device handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t usbh_hub_enum_failed(usb_device_handle_t dev_hdl);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -148,6 +148,7 @@ typedef struct {
|
||||
SemaphoreHandle_t event_sem;
|
||||
SemaphoreHandle_t mux_lock;
|
||||
usb_phy_handle_t phy_handle; // Will be NULL if host library is installed with skip_phy_setup
|
||||
void *hub_client; // Pointer to Hub driver (acting as a client). Used to reroute completed USBH control transfers
|
||||
} constant;
|
||||
} host_lib_t;
|
||||
|
||||
@@ -171,8 +172,15 @@ static inline void _clear_client_opened_device(client_t *client_obj, uint8_t dev
|
||||
|
||||
static inline bool _check_client_opened_device(client_t *client_obj, uint8_t dev_addr)
|
||||
{
|
||||
assert(dev_addr != 0);
|
||||
return (client_obj->dynamic.opened_dev_addr_map & (1 << (dev_addr - 1)));
|
||||
bool ret;
|
||||
|
||||
if (dev_addr != 0) {
|
||||
ret = client_obj->dynamic.opened_dev_addr_map & (1 << (dev_addr - 1));
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool _unblock_client(client_t *client_obj, bool in_isr)
|
||||
@@ -268,14 +276,18 @@ static void usbh_event_callback(usbh_event_data_t *event_data, void *arg)
|
||||
case USBH_EVENT_CTRL_XFER: {
|
||||
assert(event_data->ctrl_xfer_data.urb != NULL);
|
||||
assert(event_data->ctrl_xfer_data.urb->usb_host_client != NULL);
|
||||
// Redistribute done control transfer to the clients that submitted them
|
||||
client_t *client_obj = (client_t *)event_data->ctrl_xfer_data.urb->usb_host_client;
|
||||
|
||||
HOST_ENTER_CRITICAL();
|
||||
TAILQ_INSERT_TAIL(&client_obj->dynamic.done_ctrl_xfer_tailq, event_data->ctrl_xfer_data.urb, tailq_entry);
|
||||
client_obj->dynamic.num_done_ctrl_xfer++;
|
||||
_unblock_client(client_obj, false);
|
||||
HOST_EXIT_CRITICAL();
|
||||
// Redistribute completed control transfers to the clients that submitted them
|
||||
if (event_data->ctrl_xfer_data.urb->usb_host_client == p_host_lib_obj->constant.hub_client) {
|
||||
// Redistribute to Hub driver. Simply call the transfer callback
|
||||
event_data->ctrl_xfer_data.urb->transfer.callback(&event_data->ctrl_xfer_data.urb->transfer);
|
||||
} else {
|
||||
client_t *client_obj = (client_t *)event_data->ctrl_xfer_data.urb->usb_host_client;
|
||||
HOST_ENTER_CRITICAL();
|
||||
TAILQ_INSERT_TAIL(&client_obj->dynamic.done_ctrl_xfer_tailq, event_data->ctrl_xfer_data.urb, tailq_entry);
|
||||
client_obj->dynamic.num_done_ctrl_xfer++;
|
||||
_unblock_client(client_obj, false);
|
||||
HOST_EXIT_CRITICAL();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case USBH_EVENT_NEW_DEV: {
|
||||
@@ -297,8 +309,8 @@ static void usbh_event_callback(usbh_event_data_t *event_data, void *arg)
|
||||
break;
|
||||
}
|
||||
case USBH_EVENT_DEV_FREE: {
|
||||
// Let the Hub driver know that the device is free
|
||||
ESP_ERROR_CHECK(hub_dev_is_free(event_data->dev_free_data.dev_addr));
|
||||
// Let the Hub driver know that the device is free and its port can be recycled
|
||||
ESP_ERROR_CHECK(hub_port_recycle(event_data->dev_free_data.dev_uid));
|
||||
break;
|
||||
}
|
||||
case USBH_EVENT_ALL_FREE: {
|
||||
@@ -420,7 +432,7 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
|
||||
.enum_filter_cb = config->enum_filter_cb,
|
||||
#endif // ENABLE_ENUM_FILTER_CALLBACK
|
||||
};
|
||||
ret = hub_install(&hub_config);
|
||||
ret = hub_install(&hub_config, &host_lib_obj->constant.hub_client);
|
||||
if (ret != ESP_OK) {
|
||||
goto hub_err;
|
||||
}
|
||||
@@ -574,7 +586,7 @@ esp_err_t usb_host_lib_info(usb_host_lib_info_t *info_ret)
|
||||
HOST_CHECK_FROM_CRIT(p_host_lib_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
num_clients_temp = p_host_lib_obj->dynamic.flags.num_clients;
|
||||
HOST_EXIT_CRITICAL();
|
||||
usbh_num_devs(&num_devs_temp);
|
||||
usbh_devs_num(&num_devs_temp);
|
||||
|
||||
// Write back return values
|
||||
info_ret->num_devices = num_devs_temp;
|
||||
@@ -820,7 +832,7 @@ esp_err_t usb_host_device_open(usb_host_client_handle_t client_hdl, uint8_t dev_
|
||||
|
||||
esp_err_t ret;
|
||||
usb_device_handle_t dev_hdl;
|
||||
ret = usbh_dev_open(dev_addr, &dev_hdl);
|
||||
ret = usbh_devs_open(dev_addr, &dev_hdl);
|
||||
if (ret != ESP_OK) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -841,7 +853,7 @@ esp_err_t usb_host_device_open(usb_host_client_handle_t client_hdl, uint8_t dev_
|
||||
return ret;
|
||||
|
||||
already_opened:
|
||||
ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
|
||||
ESP_ERROR_CHECK(usbh_devs_close(dev_hdl));
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@@ -883,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);
|
||||
HOST_EXIT_CRITICAL();
|
||||
|
||||
ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
|
||||
ESP_ERROR_CHECK(usbh_devs_close(dev_hdl));
|
||||
ret = ESP_OK;
|
||||
exit:
|
||||
xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
|
||||
@@ -896,7 +908,7 @@ esp_err_t usb_host_device_free_all(void)
|
||||
HOST_CHECK_FROM_CRIT(p_host_lib_obj->dynamic.flags.num_clients == 0, ESP_ERR_INVALID_STATE); // All clients must have been deregistered
|
||||
HOST_EXIT_CRITICAL();
|
||||
esp_err_t ret;
|
||||
ret = usbh_dev_mark_all_free();
|
||||
ret = usbh_devs_mark_all_free();
|
||||
// If ESP_ERR_NOT_FINISHED is returned, caller must wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE to confirm all devices are free
|
||||
return ret;
|
||||
}
|
||||
@@ -904,7 +916,7 @@ esp_err_t usb_host_device_free_all(void)
|
||||
esp_err_t usb_host_device_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret)
|
||||
{
|
||||
HOST_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG);
|
||||
return usbh_dev_addr_list_fill(list_len, dev_addr_list, num_dev_ret);
|
||||
return usbh_devs_addr_list_fill(list_len, dev_addr_list, num_dev_ret);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- Device Requests ---------------------------------------------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user