forked from espressif/esp-idf
refactor(usb/hub): Update Hub driver port request logic
This commit is contained in:
@@ -50,9 +50,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
|
// 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_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 HUB_DRIVER_FLAG_ACTION_ENUM_EVENT 0x04
|
||||||
|
|
||||||
|
#define PORT_REQ_DISABLE 0x01
|
||||||
|
#define PORT_REQ_RECOVER 0x02
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Root port states
|
* @brief Root port states
|
||||||
*
|
*
|
||||||
@@ -185,6 +188,7 @@ typedef struct {
|
|||||||
uint32_t val;
|
uint32_t val;
|
||||||
} flags;
|
} flags;
|
||||||
root_port_state_t root_port_state;
|
root_port_state_t root_port_state;
|
||||||
|
unsigned int port_reqs;
|
||||||
} dynamic;
|
} dynamic;
|
||||||
// Single thread members don't require a critical section so long as they are never accessed from multiple threads
|
// Single thread members don't require a critical section so long as they are never accessed from multiple threads
|
||||||
struct {
|
struct {
|
||||||
@@ -679,7 +683,8 @@ static void enum_stage_cleanup_failed(enum_ctrl_t *enum_ctrl)
|
|||||||
HUB_DRIVER_ENTER_CRITICAL();
|
HUB_DRIVER_ENTER_CRITICAL();
|
||||||
// Enum could have failed due to a port error. If so, we need to trigger a port recovery
|
// 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) {
|
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;
|
p_hub_driver_obj->dynamic.port_reqs |= PORT_REQ_RECOVER;
|
||||||
|
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_REQ;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we move to the enum failed state and wait for the device to disconnect
|
// 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;
|
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_ENUM_FAILED;
|
||||||
@@ -839,7 +844,8 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
|
|||||||
case ROOT_PORT_STATE_POWERED: // This occurred before enumeration
|
case ROOT_PORT_STATE_POWERED: // This occurred before enumeration
|
||||||
case ROOT_PORT_STATE_ENUM_FAILED: // This occurred after a failed 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
|
// 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;
|
p_hub_driver_obj->dynamic.port_reqs |= PORT_REQ_RECOVER;
|
||||||
|
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_REQ;
|
||||||
break;
|
break;
|
||||||
case ROOT_PORT_STATE_ENUM:
|
case ROOT_PORT_STATE_ENUM:
|
||||||
// This occurred during enumeration. Therefore, we need to cleanup the failed enumeration
|
// This occurred during enumeration. Therefore, we need to cleanup the failed enumeration
|
||||||
@@ -867,6 +873,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)
|
static void enum_handle_events(void)
|
||||||
{
|
{
|
||||||
bool stage_pass;
|
bool stage_pass;
|
||||||
@@ -1055,10 +1085,24 @@ esp_err_t hub_dev_is_free(uint8_t dev_addr)
|
|||||||
{
|
{
|
||||||
assert(dev_addr == ENUM_DEV_ADDR);
|
assert(dev_addr == ENUM_DEV_ADDR);
|
||||||
assert(p_hub_driver_obj->single_thread.root_dev_hdl);
|
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
|
// 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_hdl = NULL;
|
||||||
|
|
||||||
HUB_DRIVER_ENTER_CRITICAL();
|
HUB_DRIVER_ENTER_CRITICAL();
|
||||||
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT;
|
// 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();
|
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);
|
||||||
@@ -1076,29 +1120,8 @@ esp_err_t hub_process(void)
|
|||||||
if (action_flags & HUB_DRIVER_FLAG_ACTION_ROOT_EVENT) {
|
if (action_flags & HUB_DRIVER_FLAG_ACTION_ROOT_EVENT) {
|
||||||
root_port_handle_events(p_hub_driver_obj->constant.root_port_hdl);
|
root_port_handle_events(p_hub_driver_obj->constant.root_port_hdl);
|
||||||
}
|
}
|
||||||
if (action_flags & HUB_DRIVER_FLAG_ACTION_PORT) {
|
if (action_flags & HUB_DRIVER_FLAG_ACTION_PORT_REQ) {
|
||||||
// Check current state of port
|
root_port_req(p_hub_driver_obj->constant.root_port_hdl);
|
||||||
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_ENUM_EVENT) {
|
if (action_flags & HUB_DRIVER_FLAG_ACTION_ENUM_EVENT) {
|
||||||
enum_handle_events();
|
enum_handle_events();
|
||||||
|
Reference in New Issue
Block a user