Procházet zdrojové kódy

HCD: Fix multiple bugs

This commit fixes the following bugs with the HCD and USB Host HAL

- Make the setting to periodic frame list and scheduling to occur after
  a reset command
- All port errors states should put the port into the HCD_PORT_STATE_RECOVERY
  state.
- Fixed incorrect return type of hcd_port_command() function
Darian Leung před 4 roky
rodič
revize
5f9692ae97

+ 1 - 1
components/hal/include/hal/usbh_hal.h

@@ -438,7 +438,7 @@ static inline uint32_t *usbh_hal_port_get_frame_list(usbh_hal_context_t *hal)
  */
 static inline void usbh_hal_port_periodic_enable(usbh_hal_context_t *hal)
 {
-    assert(hal->periodic_frame_list != NULL && !hal->flags.periodic_sched_enabled);
+    assert(hal->periodic_frame_list != NULL);
     usbh_ll_set_frame_list_base_addr(hal->dev, (uint32_t)hal->periodic_frame_list);
     usbh_ll_hcfg_set_num_frame_list_entries(hal->dev, hal->frame_list_len);
     usbh_ll_hcfg_en_perio_sched(hal->dev);

+ 2 - 0
components/hal/usbh_hal.c

@@ -239,6 +239,8 @@ void usbh_hal_chan_free(usbh_hal_context_t *hal, usbh_hal_chan_t *chan_obj)
     }
     //Can only free a channel when in the disabled state and descriptor list released
     assert(!chan_obj->flags.active && !chan_obj->flags.error_pending);
+    //Disable channel's interrupt
+    usbh_ll_haintmsk_dis_chan_intr(hal->dev, 1 << chan_obj->flags.chan_idx);
     //Deallocate channel
     hal->channels.hdls[chan_obj->flags.chan_idx] = NULL;
     hal->channels.num_allocd--;

+ 14 - 6
components/usb/hcd.c

@@ -821,13 +821,14 @@ static hcd_port_event_t _intr_hdlr_hprt(port_t *port, usbh_hal_port_event_t hal_
             port->flags.conn_dev_ena = 0;
             //Disabled could be due to a disable request or reset request, or due to a port error
             if (port->state != HCD_PORT_STATE_RESETTING) {  //Ignore the disable event if it's due to a reset request
-                port->state = HCD_PORT_STATE_DISABLED;
                 if (port->flags.disable_requested) {
                     //Disabled by request (i.e. by port command). Generate an internal event
+                    port->state = HCD_PORT_STATE_DISABLED;
                     port->flags.disable_requested = 0;
                     *yield |= _internal_port_event_notify_from_isr(port);
                 } else {
                     //Disabled due to a port error
+                    port->state = HCD_PORT_STATE_RECOVERY;
                     port_event = HCD_PORT_EVENT_ERROR;
                 }
             }
@@ -838,7 +839,7 @@ static hcd_port_event_t _intr_hdlr_hprt(port_t *port, usbh_hal_port_event_t hal_
             if (port->state != HCD_PORT_STATE_NOT_POWERED) {
                 //We need to power OFF the port to protect it
                 usbh_hal_port_toggle_power(port->hal, false);
-                port->state = HCD_PORT_STATE_NOT_POWERED;
+                port->state = HCD_PORT_STATE_RECOVERY;
                 port_event = HCD_PORT_EVENT_OVERCURRENT;
             }
             port->flags.conn_dev_ena = 0;
@@ -1347,6 +1348,8 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h
     port_obj->context = port_config->context;
     usbh_hal_init(port_obj->hal);
     port_obj->initialized = true;
+    //Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
+    memset(port_obj->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
     esp_intr_enable(s_hcd_obj->isr_hdl);
     *port_hdl = (hcd_port_handle_t)port_obj;
     HCD_EXIT_CRITICAL();
@@ -1410,8 +1413,7 @@ esp_err_t hcd_port_command(hcd_port_handle_t port_hdl, hcd_port_cmd_t command)
                         //Set FIFO sizes to default
                         usbh_hal_set_fifo_size(port->hal, &fifo_config_default);
                         port->fifo_bias = HCD_PORT_FIFO_BIAS_BALANCED;
-                        //Reset frame list and enable periodic scheduling
-                        memset(port->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
+                        //We start periodic scheduling only after a RESET command since SOFs only start after a reset
                         usbh_hal_port_set_frame_list(port->hal, port->frame_list, FRAME_LIST_LEN);
                         usbh_hal_port_periodic_enable(port->hal);
                         ret = ESP_OK;
@@ -1501,7 +1503,9 @@ hcd_port_event_t hcd_port_handle_event(hcd_port_handle_t port_hdl)
                     ret = HCD_PORT_EVENT_NONE;
                 } else {
                     //No device connected after debounce delay. This is an actual disconnection
-                    port->state = HCD_PORT_STATE_DISCONNECTED;
+                    if (port->state != HCD_PORT_STATE_NOT_POWERED) {    //Don't update state if disconnect was due to power-off
+                        port->state = HCD_PORT_STATE_DISCONNECTED;
+                    }
                     ret = HCD_PORT_EVENT_DISCONNECTION;
                 }
                 break;
@@ -1538,6 +1542,10 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl)
     port->state = HCD_PORT_STATE_NOT_POWERED;
     port->last_event = HCD_PORT_EVENT_NONE;
     port->flags.val = 0;
+    //Soft reset wipes all registers so we need to reinitialize the HAL
+    usbh_hal_init(port->hal);
+    //Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset
+    memset(port->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t));
     esp_intr_enable(s_hcd_obj->isr_hdl);
     HCD_EXIT_CRITICAL();
     return ESP_OK;
@@ -1982,7 +1990,7 @@ hcd_pipe_state_t hcd_pipe_get_state(hcd_pipe_handle_t pipe_hdl)
 esp_err_t hcd_pipe_command(hcd_pipe_handle_t pipe_hdl, hcd_pipe_cmd_t command)
 {
     pipe_t *pipe = (pipe_t *)pipe_hdl;
-    bool ret = ESP_OK;
+    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