mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
USB HCD: Add pipe persistence feature
This commit adds a pipe persistence feature allowing HCD pipes to survive a port reset.
This commit is contained in:
parent
a413cbd784
commit
d37451f2c7
@ -411,7 +411,6 @@ static inline bool usbh_hal_port_check_resume(usbh_hal_context_t *hal)
|
||||
*/
|
||||
static inline void usbh_hal_port_set_frame_list(usbh_hal_context_t *hal, uint32_t *frame_list, usb_hal_frame_list_len_t len)
|
||||
{
|
||||
HAL_ASSERT(!hal->flags.periodic_sched_enabled);
|
||||
//Clear and save frame list
|
||||
hal->periodic_frame_list = frame_list;
|
||||
hal->frame_list_len = len;
|
||||
|
@ -272,7 +272,9 @@ struct pipe_obj {
|
||||
uint32_t paused: 1;
|
||||
uint32_t pipe_cmd_processing: 1;
|
||||
uint32_t is_active: 1;
|
||||
uint32_t reserved28: 28;
|
||||
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 reserved26: 26;
|
||||
};
|
||||
uint32_t val;
|
||||
} cs_flags;
|
||||
@ -596,6 +598,28 @@ static bool _port_pause_all_pipes(port_t *port);
|
||||
*/
|
||||
static void _port_unpause_all_pipes(port_t *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 Send a reset condition on a port's bus
|
||||
*
|
||||
@ -1203,6 +1227,43 @@ static void _port_unpause_all_pipes(port_t *port)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
usbh_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;
|
||||
usbh_hal_chan_alloc(port->hal, pipe->chan_obj, (void *)pipe);
|
||||
usbh_hal_chan_set_ep_char(port->hal, pipe->chan_obj, &pipe->ep_char);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _port_bus_reset(port_t *port)
|
||||
{
|
||||
assert(port->state == HCD_PORT_STATE_ENABLED || port->state == HCD_PORT_STATE_DISABLED);
|
||||
@ -1409,6 +1470,14 @@ esp_err_t hcd_port_command(hcd_port_handle_t port_hdl, hcd_port_cmd_t command)
|
||||
case HCD_PORT_CMD_RESET: {
|
||||
//Port can only a reset when it is in the enabled or disabled states (in case of new connection)
|
||||
if (port->state == HCD_PORT_STATE_ENABLED || port->state == HCD_PORT_STATE_DISABLED) {
|
||||
bool is_runtime_reset = (port->state == HCD_PORT_STATE_ENABLED) ? true : false;
|
||||
if (is_runtime_reset) {
|
||||
//Check all pipes that are still allocated are persistent before we execute the reset
|
||||
if (!_port_persist_all_pipes(port)) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_port_bus_reset(port)) {
|
||||
//Set FIFO sizes to default
|
||||
usbh_hal_set_fifo_size(port->hal, &fifo_config_default);
|
||||
@ -1420,6 +1489,10 @@ esp_err_t hcd_port_command(hcd_port_handle_t port_hdl, hcd_port_cmd_t command)
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
if (is_runtime_reset) {
|
||||
_port_recover_all_pipes(port);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1909,7 +1982,8 @@ esp_err_t hcd_pipe_free(hcd_pipe_handle_t pipe_hdl)
|
||||
&& pipe->multi_buffer_control.buffer_num_to_parse == 0
|
||||
&& pipe->multi_buffer_control.buffer_num_to_exec == 0
|
||||
&& pipe->num_urb_pending == 0
|
||||
&& pipe->num_urb_done == 0,
|
||||
&& pipe->num_urb_done == 0
|
||||
&& !pipe->cs_flags.reset_lock,
|
||||
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);
|
||||
@ -1934,7 +2008,8 @@ esp_err_t hcd_pipe_update_mps(hcd_pipe_handle_t pipe_hdl, int mps)
|
||||
HCD_CHECK_FROM_CRIT(pipe->state != HCD_PIPE_STATE_INVALID
|
||||
&& !pipe->cs_flags.pipe_cmd_processing
|
||||
&& pipe->num_urb_pending == 0
|
||||
&& pipe->num_urb_done == 0,
|
||||
&& pipe->num_urb_done == 0
|
||||
&& !pipe->cs_flags.reset_lock,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
pipe->ep_char.mps = mps;
|
||||
//Update the underlying channel's registers
|
||||
@ -1951,7 +2026,8 @@ esp_err_t hcd_pipe_update_dev_addr(hcd_pipe_handle_t pipe_hdl, uint8_t dev_addr)
|
||||
HCD_CHECK_FROM_CRIT(pipe->state != HCD_PIPE_STATE_INVALID
|
||||
&& !pipe->cs_flags.pipe_cmd_processing
|
||||
&& pipe->num_urb_pending == 0
|
||||
&& pipe->num_urb_done == 0,
|
||||
&& pipe->num_urb_done == 0
|
||||
&& !pipe->cs_flags.reset_lock,
|
||||
ESP_ERR_INVALID_STATE);
|
||||
pipe->ep_char.dev_addr = dev_addr;
|
||||
//Update the underlying channel's registers
|
||||
@ -1960,6 +2036,22 @@ 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_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->state != HCD_PIPE_STATE_INVALID
|
||||
&& !pipe->cs_flags.pipe_cmd_processing
|
||||
&& pipe->num_urb_pending == 0
|
||||
&& pipe->num_urb_done == 0
|
||||
&& !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;
|
||||
@ -1994,7 +2086,7 @@ esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command)
|
||||
|
||||
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.pipe_cmd_processing || !pipe->port->flags.conn_dev_ena || pipe->state == HCD_PIPE_STATE_INVALID) {
|
||||
if (pipe->cs_flags.pipe_cmd_processing || pipe->cs_flags.reset_lock || !pipe->port->flags.conn_dev_ena || pipe->state == HCD_PIPE_STATE_INVALID) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
} else {
|
||||
pipe->cs_flags.pipe_cmd_processing = 1;
|
||||
@ -2538,7 +2630,8 @@ 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.pipe_cmd_processing //Pipe cannot currently be processing a pipe command
|
||||
&& !pipe->cs_flags.reset_lock, //Pipe cannot be persisting through a port reset
|
||||
ESP_ERR_INVALID_STATE);
|
||||
//Use the URB's reserved_ptr to store the pipe's
|
||||
urb->hcd_ptr = (void *)pipe;
|
||||
|
@ -421,6 +421,20 @@ 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 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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user