Skip to content

Commit 3358f3e

Browse files
Dazza0espressif-bot
authored andcommitted
refactor(usb/hcd): Allow port resets with allocated pipes
This commit updates the HCD API to allow port resets to occur even if pipes are allocated. The pipes cannot be active and the port reset will simply restore the pipes (by reinitializing their channel registers) following the reset. Changes: - Allow port resets while channels are allocated - Remove pipe persistance API 'hcd_pipe_set_persist_reset()'
1 parent 8aa9a42 commit 3358f3e

File tree

4 files changed

+54
-145
lines changed

4 files changed

+54
-145
lines changed

components/hal/include/hal/usb_dwc_hal.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ typedef struct {
205205
* - The peripheral must have been reset and clock un-gated
206206
* - The USB PHY (internal or external) and associated GPIOs must already be configured
207207
* - GPIO pins configured
208-
* - Interrupt allocated but DISABLED (in case of an unknown interupt state)
208+
* - Interrupt allocated but DISABLED (in case of an unknown interrupt state)
209209
* Exit:
210210
* - Checks to see if DWC_OTG is alive, and if HW version/config is correct
211211
* - HAl context initialized
@@ -290,7 +290,7 @@ static inline void usb_dwc_hal_port_init(usb_dwc_hal_context_t *hal)
290290
/**
291291
* @brief Deinitialize the host port
292292
*
293-
* - Will disable the host port's interrupts preventing further port aand channel events from ocurring
293+
* - Will disable the host port's interrupts preventing further port aand channel events from occurring
294294
*
295295
* @param hal Context of the HAL layer
296296
*/
@@ -333,7 +333,6 @@ static inline void usb_dwc_hal_port_toggle_power(usb_dwc_hal_context_t *hal, boo
333333
*/
334334
static inline void usb_dwc_hal_port_toggle_reset(usb_dwc_hal_context_t *hal, bool enable)
335335
{
336-
HAL_ASSERT(hal->channels.num_allocd == 0); //Cannot reset if there are still allocated channels
337336
usb_dwc_ll_hprt_set_port_reset(hal->dev, enable);
338337
}
339338

@@ -447,7 +446,7 @@ static inline void usb_dwc_hal_port_periodic_enable(usb_dwc_hal_context_t *hal)
447446
/**
448447
* @brief Disable periodic scheduling
449448
*
450-
* Disabling periodic scheduling will save a bit of DMA bandwith (as the controller will no longer fetch the schedule
449+
* Disabling periodic scheduling will save a bit of DMA bandwidth (as the controller will no longer fetch the schedule
451450
* from the frame list).
452451
*
453452
* @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
505504
* @brief Disable the debounce lock
506505
*
507506
* This function must be called after calling usb_dwc_hal_port_check_if_connected() and will allow connection/disconnection
508-
* events to occur again. Any pending connection or disconenction interrupts are cleared.
507+
* events to occur again. Any pending connection or disconnection interrupts are cleared.
509508
*
510509
* @param hal Context of the HAL layer
511510
*/
512511
static inline void usb_dwc_hal_disable_debounce_lock(usb_dwc_hal_context_t *hal)
513512
{
514513
hal->flags.dbnc_lock_enabled = 0;
515-
//Clear Conenction and disconenction interrupt in case it triggered again
514+
//Clear Connection and disconnection interrupt in case it triggered again
516515
usb_dwc_ll_gintsts_clear_intrs(hal->dev, USB_DWC_LL_INTR_CORE_DISCONNINT);
517516
usb_dwc_ll_hprt_intr_clear(hal->dev, USB_DWC_LL_INTR_HPRT_PRTCONNDET);
518-
//Reenable the hprt (connection) and disconnection interrupts
517+
//Re-enable the hprt (connection) and disconnection interrupts
519518
usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_DISCONNINT);
520519
}
521520

@@ -672,10 +671,10 @@ bool usb_dwc_hal_chan_request_halt(usb_dwc_hal_chan_t *chan_obj);
672671
/**
673672
* @brief Indicate that a channel is halted after a port error
674673
*
675-
* When a port error occurs (e.g., discconect, overcurrent):
674+
* When a port error occurs (e.g., disconnect, overcurrent):
676675
* - Any previously active channels will remain active (i.e., they will not receive a channel interrupt)
677676
* - Attempting to disable them using usb_dwc_hal_chan_request_halt() will NOT generate an interrupt for ISOC channels
678-
* (probalby something to do with the periodic scheduling)
677+
* (probably something to do with the periodic scheduling)
679678
*
680679
* However, the channel's enable bit can be left as 1 since after a port error, a soft reset will be done anyways.
681680
* This function simply updates the channels internal state variable to indicate it is halted (thus allowing it to be

components/usb/hcd_dwc.c

Lines changed: 46 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,7 @@ struct pipe_obj {
214214
uint32_t waiting_halt: 1;
215215
uint32_t pipe_cmd_processing: 1;
216216
uint32_t has_urb: 1; // Indicates there is at least one URB either pending, in-flight, or done
217-
uint32_t persist: 1; // indicates that this pipe should persist through a run-time port reset
218-
uint32_t reset_lock: 1; // Indicates that this pipe is undergoing a run-time reset
219-
uint32_t reserved27: 27;
217+
uint32_t reserved29: 29;
220218
};
221219
uint32_t val;
222220
} cs_flags;
@@ -560,28 +558,6 @@ static esp_err_t _pipe_cmd_clear(pipe_t *pipe);
560558

561559
// ------------------------ Port ---------------------------
562560

563-
/**
564-
* @brief Prepare persistent pipes for reset
565-
*
566-
* This function checks if all pipes are reset persistent and proceeds to free their underlying HAL channels for the
567-
* persistent pipes. This should be called before a run time reset
568-
*
569-
* @param port Port object
570-
* @return true All pipes are persistent and their channels are freed
571-
* @return false Not all pipes are persistent
572-
*/
573-
static bool _port_persist_all_pipes(port_t *port);
574-
575-
/**
576-
* @brief Recovers all persistent pipes after a reset
577-
*
578-
* This function will recover all persistent pipes after a reset and reallocate their underlying HAl channels. This
579-
* function should be called after a reset.
580-
*
581-
* @param port Port object
582-
*/
583-
static void _port_recover_all_pipes(port_t *port);
584-
585561
/**
586562
* @brief Checks if all pipes are in the halted state
587563
*
@@ -1162,44 +1138,6 @@ esp_err_t hcd_uninstall(void)
11621138

11631139
// ----------------------- Helpers -------------------------
11641140

1165-
static bool _port_persist_all_pipes(port_t *port)
1166-
{
1167-
if (port->num_pipes_queued > 0) {
1168-
// All pipes must be idle before we run-time reset
1169-
return false;
1170-
}
1171-
bool all_persist = true;
1172-
pipe_t *pipe;
1173-
// Check that each pipe is persistent
1174-
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
1175-
if (!pipe->cs_flags.persist) {
1176-
all_persist = false;
1177-
break;
1178-
}
1179-
}
1180-
if (!all_persist) {
1181-
// At least one pipe is not persistent. All pipes must be freed or made persistent before we can reset
1182-
return false;
1183-
}
1184-
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
1185-
pipe->cs_flags.reset_lock = 1;
1186-
usb_dwc_hal_chan_free(port->hal, pipe->chan_obj);
1187-
}
1188-
return true;
1189-
}
1190-
1191-
static void _port_recover_all_pipes(port_t *port)
1192-
{
1193-
pipe_t *pipe;
1194-
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
1195-
pipe->cs_flags.persist = 0;
1196-
pipe->cs_flags.reset_lock = 0;
1197-
usb_dwc_hal_chan_alloc(port->hal, pipe->chan_obj, (void *)pipe);
1198-
usb_dwc_hal_chan_set_ep_char(port->hal, pipe->chan_obj, &pipe->ep_char);
1199-
}
1200-
CACHE_SYNC_FRAME_LIST(port->frame_list);
1201-
}
1202-
12031141
static bool _port_check_all_pipes_halted(port_t *port)
12041142
{
12051143
bool all_halted = true;
@@ -1276,20 +1214,26 @@ static esp_err_t _port_cmd_power_off(port_t *port)
12761214
static esp_err_t _port_cmd_reset(port_t *port)
12771215
{
12781216
esp_err_t ret;
1279-
// Port can only a reset when it is in the enabled or disabled states (in case of new connection)
1217+
1218+
// Port can only a reset when it is in the enabled or disabled (in the case of a new connection)states.
12801219
if (port->state != HCD_PORT_STATE_ENABLED && port->state != HCD_PORT_STATE_DISABLED) {
12811220
ret = ESP_ERR_INVALID_STATE;
12821221
goto exit;
12831222
}
1284-
bool is_runtime_reset = (port->state == HCD_PORT_STATE_ENABLED) ? true : false;
1285-
if (is_runtime_reset && !_port_persist_all_pipes(port)) {
1286-
// If this is a run time reset, check all pipes that are still allocated can persist the reset
1223+
// Port can only be reset if all pipes are idle
1224+
if (port->num_pipes_queued > 0) {
12871225
ret = ESP_ERR_INVALID_STATE;
12881226
goto exit;
12891227
}
1290-
// All pipes (if any_) are guaranteed to be persistent at this point. Proceed to resetting the bus
1228+
/*
1229+
Proceed to resetting the bus
1230+
- Update the port's state variable
1231+
- Hold the bus in the reset state for RESET_HOLD_MS.
1232+
- Return the bus to the idle state for RESET_RECOVERY_MS
1233+
*/
12911234
port->state = HCD_PORT_STATE_RESETTING;
1292-
// Put and hold the bus in the reset state. If the port was previously enabled, a disabled event will occur after this
1235+
1236+
// Place the bus into the reset state. If the port was previously enabled, a disabled event will occur after this
12931237
usb_dwc_hal_port_toggle_reset(port->hal, true);
12941238
HCD_EXIT_CRITICAL();
12951239
vTaskDelay(pdMS_TO_TICKS(RESET_HOLD_MS));
@@ -1299,7 +1243,8 @@ static esp_err_t _port_cmd_reset(port_t *port)
12991243
ret = ESP_ERR_INVALID_RESPONSE;
13001244
goto bailout;
13011245
}
1302-
// Return the bus to the idle state and hold it for the required reset recovery time. Port enabled event should occur
1246+
1247+
// Return the bus to the idle state. Port enabled event should occur
13031248
usb_dwc_hal_port_toggle_reset(port->hal, false);
13041249
HCD_EXIT_CRITICAL();
13051250
vTaskDelay(pdMS_TO_TICKS(RESET_RECOVERY_MS));
@@ -1309,16 +1254,20 @@ static esp_err_t _port_cmd_reset(port_t *port)
13091254
ret = ESP_ERR_INVALID_RESPONSE;
13101255
goto bailout;
13111256
}
1312-
// Set FIFO sizes based on the selected biasing
1313-
usb_dwc_hal_set_fifo_bias(port->hal, port->fifo_bias);
1314-
// We start periodic scheduling only after a RESET command since SOFs only start after a reset
1315-
usb_dwc_hal_port_set_frame_list(port->hal, port->frame_list, FRAME_LIST_LEN);
1316-
usb_dwc_hal_port_periodic_enable(port->hal);
1257+
1258+
// Reinitialize port registers.
1259+
usb_dwc_hal_set_fifo_bias(port->hal, port->fifo_bias); // Set FIFO biases
1260+
usb_dwc_hal_port_set_frame_list(port->hal, port->frame_list, FRAME_LIST_LEN); // Set periodic frame list
1261+
usb_dwc_hal_port_periodic_enable(port->hal); // Enable periodic scheduling
1262+
13171263
ret = ESP_OK;
13181264
bailout:
1319-
if (is_runtime_reset) {
1320-
_port_recover_all_pipes(port);
1265+
// Reinitialize channel registers
1266+
pipe_t *pipe;
1267+
TAILQ_FOREACH(pipe, &port->pipes_idle_tailq, tailq_entry) {
1268+
usb_dwc_hal_chan_set_ep_char(port->hal, pipe->chan_obj, &pipe->ep_char);
13211269
}
1270+
CACHE_SYNC_FRAME_LIST(port->frame_list);
13221271
exit:
13231272
return ret;
13241273
}
@@ -1987,8 +1936,7 @@ esp_err_t hcd_pipe_free(hcd_pipe_handle_t pipe_hdl)
19871936
HCD_ENTER_CRITICAL();
19881937
// Check that all URBs have been removed and pipe has no pending events
19891938
HCD_CHECK_FROM_CRIT(!pipe->multi_buffer_control.buffer_is_executing
1990-
&& !pipe->cs_flags.has_urb
1991-
&& !pipe->cs_flags.reset_lock,
1939+
&& !pipe->cs_flags.has_urb,
19921940
ESP_ERR_INVALID_STATE);
19931941
// Remove pipe from the list of idle pipes (it must be in the idle list because it should have no queued URBs)
19941942
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)
20111959
HCD_ENTER_CRITICAL();
20121960
// Check if pipe is in the correct state to be updated
20131961
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
2014-
!pipe->cs_flags.has_urb &&
2015-
!pipe->cs_flags.reset_lock,
1962+
!pipe->cs_flags.has_urb,
20161963
ESP_ERR_INVALID_STATE);
20171964
pipe->ep_char.mps = mps;
20181965
// 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)
20271974
HCD_ENTER_CRITICAL();
20281975
// Check if pipe is in the correct state to be updated
20291976
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
2030-
!pipe->cs_flags.has_urb &&
2031-
!pipe->cs_flags.reset_lock,
1977+
!pipe->cs_flags.has_urb,
20321978
ESP_ERR_INVALID_STATE);
20331979
pipe->ep_char.dev_addr = dev_addr;
20341980
// Update the underlying channel's registers
@@ -2043,29 +1989,14 @@ esp_err_t hcd_pipe_update_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_callback
20431989
HCD_ENTER_CRITICAL();
20441990
// Check if pipe is in the correct state to be updated
20451991
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
2046-
!pipe->cs_flags.has_urb &&
2047-
!pipe->cs_flags.reset_lock,
1992+
!pipe->cs_flags.has_urb,
20481993
ESP_ERR_INVALID_STATE);
20491994
pipe->callback = callback;
20501995
pipe->callback_arg = user_arg;
20511996
HCD_EXIT_CRITICAL();
20521997
return ESP_OK;
20531998
}
20541999

2055-
esp_err_t hcd_pipe_set_persist_reset(hcd_pipe_handle_t pipe_hdl)
2056-
{
2057-
pipe_t *pipe = (pipe_t *)pipe_hdl;
2058-
HCD_ENTER_CRITICAL();
2059-
// Check if pipe is in the correct state to be updated
2060-
HCD_CHECK_FROM_CRIT(!pipe->cs_flags.pipe_cmd_processing &&
2061-
!pipe->cs_flags.has_urb &&
2062-
!pipe->cs_flags.reset_lock,
2063-
ESP_ERR_INVALID_STATE);
2064-
pipe->cs_flags.persist = 1;
2065-
HCD_EXIT_CRITICAL();
2066-
return ESP_OK;
2067-
}
2068-
20692000
void *hcd_pipe_get_context(hcd_pipe_handle_t pipe_hdl)
20702001
{
20712002
pipe_t *pipe = (pipe_t *)pipe_hdl;
@@ -2102,27 +2033,22 @@ esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command)
21022033
esp_err_t ret = ESP_OK;
21032034

21042035
HCD_ENTER_CRITICAL();
2105-
// Cannot execute pipe commands the pipe is already executing a command, or if the pipe or its port are no longer valid
2106-
if (pipe->cs_flags.reset_lock) {
2107-
ret = ESP_ERR_INVALID_STATE;
2108-
} else {
2109-
pipe->cs_flags.pipe_cmd_processing = 1;
2110-
switch (command) {
2111-
case HCD_PIPE_CMD_HALT: {
2112-
ret = _pipe_cmd_halt(pipe);
2113-
break;
2114-
}
2115-
case HCD_PIPE_CMD_FLUSH: {
2116-
ret = _pipe_cmd_flush(pipe);
2117-
break;
2118-
}
2119-
case HCD_PIPE_CMD_CLEAR: {
2120-
ret = _pipe_cmd_clear(pipe);
2121-
break;
2122-
}
2123-
}
2124-
pipe->cs_flags.pipe_cmd_processing = 0;
2036+
pipe->cs_flags.pipe_cmd_processing = 1;
2037+
switch (command) {
2038+
case HCD_PIPE_CMD_HALT: {
2039+
ret = _pipe_cmd_halt(pipe);
2040+
break;
2041+
}
2042+
case HCD_PIPE_CMD_FLUSH: {
2043+
ret = _pipe_cmd_flush(pipe);
2044+
break;
2045+
}
2046+
case HCD_PIPE_CMD_CLEAR: {
2047+
ret = _pipe_cmd_clear(pipe);
2048+
break;
2049+
}
21252050
}
2051+
pipe->cs_flags.pipe_cmd_processing = 0;
21262052
HCD_EXIT_CRITICAL();
21272053
return ret;
21282054
}
@@ -2658,8 +2584,7 @@ esp_err_t hcd_urb_enqueue(hcd_pipe_handle_t pipe_hdl, urb_t *urb)
26582584
// Check that pipe and port are in the correct state to receive URBs
26592585
HCD_CHECK_FROM_CRIT(pipe->port->state == HCD_PORT_STATE_ENABLED // The pipe's port must be in the correct state
26602586
&& pipe->state == HCD_PIPE_STATE_ACTIVE // The pipe must be in the correct state
2661-
&& !pipe->cs_flags.pipe_cmd_processing // Pipe cannot currently be processing a pipe command
2662-
&& !pipe->cs_flags.reset_lock, // Pipe cannot be persisting through a port reset
2587+
&& !pipe->cs_flags.pipe_cmd_processing, // Pipe cannot currently be processing a pipe command
26632588
ESP_ERR_INVALID_STATE);
26642589
// Use the URB's reserved_ptr to store the pipe's
26652590
urb->hcd_ptr = (void *)pipe;

components/usb/hub.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@ static bool enum_stage_start(enum_ctrl_t *enum_ctrl)
285285

286286
static bool enum_stage_second_reset(enum_ctrl_t *enum_ctrl)
287287
{
288-
ESP_ERROR_CHECK(hcd_pipe_set_persist_reset(enum_ctrl->pipe)); // Persist the default pipe through the reset
289288
if (hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_RESET) != ESP_OK) {
290289
ESP_LOGE(HUB_DRIVER_TAG, "Failed to issue second reset");
291290
return false;

components/usb/private_include/hcd.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -434,20 +434,6 @@ esp_err_t hcd_pipe_update_dev_addr(hcd_pipe_handle_t pipe_hdl, uint8_t dev_addr)
434434
*/
435435
esp_err_t hcd_pipe_update_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_callback_t callback, void *user_arg);
436436

437-
/**
438-
* @brief Make a pipe persist through a run time reset
439-
*
440-
* Normally when a HCD_PORT_CMD_RESET is called, all pipes should already have been freed. However There may be cases
441-
* (such as during enumeration) when a pipe must persist through a reset. This function will mark a pipe as
442-
* persistent allowing it to survive a reset. When HCD_PORT_CMD_RESET is called, the pipe can continue to be used after
443-
* the reset.
444-
*
445-
* @param pipe_hdl Pipe handle
446-
* @retval ESP_OK: Pipe successfully marked as persistent
447-
* @retval ESP_ERR_INVALID_STATE: Pipe is not in a condition to be made persistent
448-
*/
449-
esp_err_t hcd_pipe_set_persist_reset(hcd_pipe_handle_t pipe_hdl);
450-
451437
/**
452438
* @brief Get the context variable of a pipe from its handle
453439
*

0 commit comments

Comments
 (0)