summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2025-04-24 17:59:45 +0700
committerhathach <[email protected]>2025-04-24 17:59:45 +0700
commit0f784e8a072d30aaa6f4c6ef0a8f22a085ea2516 (patch)
tree4241f60c1c316c4fc6e42f22719d67ddada61d49 /src
parent89f8d0cffbe259baed3163a22a42390c21ba1f53 (diff)
refactor hub driver and move port reset on connection change to usbh.
hub: add hub_port_get_status_local(), ignore resp in hub_port_get_status(pot != 0) usbh properly deboucning with hub/rootport accordingly to usb specs, also add 10ms of reset recovery
Diffstat (limited to 'src')
-rw-r--r--src/host/hub.c222
-rw-r--r--src/host/hub.h8
-rw-r--r--src/host/usbh.c208
3 files changed, 221 insertions, 217 deletions
diff --git a/src/host/hub.c b/src/host/hub.c
index 61efa8ba5..c87289a14 100644
--- a/src/host/hub.c
+++ b/src/host/hub.c
@@ -57,9 +57,11 @@ typedef struct {
TUH_EPBUF_DEF(ctrl_buf, CFG_TUH_HUB_BUFSIZE);
} hub_epbuf_t;
+static tuh_xfer_cb_t user_complete_cb = NULL;
static hub_interface_t hub_itfs[CFG_TUH_HUB];
CFG_TUH_MEM_SECTION static hub_epbuf_t hub_epbufs[CFG_TUH_HUB];
+
TU_ATTR_ALWAYS_INLINE static inline hub_interface_t* get_hub_itf(uint8_t daddr) {
return &hub_itfs[daddr-1-CFG_TUH_DEVICE_MAX];
}
@@ -142,10 +144,23 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
};
TU_LOG_DRV("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
- TU_ASSERT( tuh_control_xfer(&xfer) );
+ TU_ASSERT(tuh_control_xfer(&xfer));
return true;
}
+static void port_get_status_complete (tuh_xfer_t* xfer) {
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ hub_interface_t* p_hub = get_hub_itf(xfer->daddr);
+ p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
+ }
+
+ xfer->complete_cb = user_complete_cb;
+ user_complete_cb = NULL;
+ if (xfer->complete_cb) {
+ xfer->complete_cb(xfer);
+ }
+}
+
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
@@ -169,8 +184,26 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
.user_data = user_data
};
+ if (hub_port != 0) {
+ // intercept complete callback to save port status, ignore resp
+ hub_epbuf_t* p_epbuf = get_hub_epbuf(hub_addr);
+ xfer.complete_cb = port_get_status_complete;
+ xfer.buffer = p_epbuf->ctrl_buf;
+ user_complete_cb = complete_cb;
+ } else {
+ user_complete_cb = NULL;
+ }
+
TU_LOG_DRV("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
- TU_VERIFY( tuh_control_xfer(&xfer) );
+ TU_VERIFY(tuh_control_xfer(&xfer));
+ return true;
+}
+
+bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp) {
+ (void) hub_port;
+ TU_VERIFY(hub_addr > CFG_TUH_DEVICE_MAX);
+ hub_interface_t* p_hub = get_hub_itf(hub_addr);
+ *resp = p_hub->port_status;
return true;
}
@@ -238,10 +271,10 @@ bool hub_edpt_status_xfer(uint8_t daddr) {
static void config_set_port_power (tuh_xfer_t* xfer);
static void config_port_power_complete (tuh_xfer_t* xfer);
-bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) {
- hub_interface_t* p_hub = get_hub_itf(dev_addr);
+bool hub_set_config(uint8_t daddr, uint8_t itf_num) {
+ hub_interface_t* p_hub = get_hub_itf(daddr);
TU_ASSERT(itf_num == p_hub->itf_num);
- hub_epbuf_t* p_epbuf = get_hub_epbuf(dev_addr);
+ hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
// Get Hub Descriptor
tusb_control_request_t const request = {
@@ -257,7 +290,7 @@ bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) {
};
tuh_xfer_t xfer = {
- .daddr = dev_addr,
+ .daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = p_epbuf->ctrl_buf,
@@ -312,11 +345,15 @@ static void config_port_power_complete (tuh_xfer_t* xfer) {
//--------------------------------------------------------------------+
// Connection Changes
//--------------------------------------------------------------------+
-static void get_status_complete (tuh_xfer_t* xfer);
-static void port_get_status_complete (tuh_xfer_t* xfer);
-static void port_clear_feature_complete_stub(tuh_xfer_t* xfer);
-static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
-static void connection_port_reset_complete (tuh_xfer_t* xfer);
+enum {
+ STATE_IDLE = 0,
+ STATE_HUB_STATUS,
+ STATE_CLEAR_CHANGE,
+ STATE_CHECK_CONN,
+ STATE_COMPLETE
+};
+
+static void process_new_status(tuh_xfer_t* xfer);
// callback as response of interrupt endpoint polling
bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
@@ -337,12 +374,12 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
processed = false;
} else if (tu_bit_test(status_change, 0)) {
// Hub bit 0 is for the hub device events
- processed = hub_get_status(daddr, p_epbuf->ctrl_buf, get_status_complete, 0);
+ processed = hub_get_status(daddr, p_epbuf->ctrl_buf, process_new_status, STATE_HUB_STATUS);
} else {
// Hub bits 1 to n are hub port events
for (uint8_t port=1; port <= p_hub->bNbrPorts; port++) {
if (tu_bit_test(status_change, port)) {
- processed = hub_port_get_status(daddr, port, p_epbuf->ctrl_buf, port_get_status_complete, 0);
+ processed = hub_port_get_status(daddr, port, NULL, process_new_status, STATE_CLEAR_CHANGE);
break; // after completely processed one port, we will re-queue the status poll and handle next one
}
}
@@ -358,117 +395,84 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
return true;
}
-static void port_clear_feature_complete_stub(tuh_xfer_t* xfer) {
- hub_edpt_status_xfer(xfer->daddr);
-}
-
-static void get_status_complete(tuh_xfer_t *xfer) {
+static void process_new_status(tuh_xfer_t* xfer) {
const uint8_t daddr = xfer->daddr;
- bool processed = false; // true if new status is processed
- if (xfer->result == XFER_RESULT_SUCCESS) {
- hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
-
- TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
-
- if (hub_status.change.local_power_source) {
- TU_LOG_DRV(" Local Power Change\r\n");
- processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE, port_clear_feature_complete_stub, 0);
- } else if (hub_status.change.over_current) {
- TU_LOG_DRV(" Over Current\r\n");
- processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
- }
- }
-
- if (!processed) {
- TU_ASSERT(hub_edpt_status_xfer(daddr), );
+ if (xfer->result != XFER_RESULT_SUCCESS) {
+ TU_ASSERT(hub_edpt_status_xfer(daddr),);
+ return;
}
-}
-static void port_get_status_complete(tuh_xfer_t *xfer) {
- const uint8_t daddr = xfer->daddr;
+ const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ hub_interface_t *p_hub = get_hub_itf(daddr);
+ const uintptr_t state = xfer->user_data;
bool processed = false; // true if new status is processed
- if (xfer->result == XFER_RESULT_SUCCESS) {
- const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
- hub_interface_t *p_hub = get_hub_itf(daddr);
- p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
-
- // Clear port status change interrupts
- if (p_hub->port_status.change.connection) {
- // Connection change
- // Port is powered and enabled
- //TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
-
- // Acknowledge Port Connection Change
- processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
- } else if (p_hub->port_status.change.port_enable) {
- processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE, port_clear_feature_complete_stub, 0);
- } else if (p_hub->port_status.change.suspend) {
- processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE, port_clear_feature_complete_stub, 0);
- } else if (p_hub->port_status.change.over_current) {
- processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
- } else if (p_hub->port_status.change.reset) {
- processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE, port_clear_feature_complete_stub, 0);
+ switch (state) {
+ case STATE_HUB_STATUS: {
+ hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
+ TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
+ if (hub_status.change.local_power_source) {
+ TU_LOG_DRV(" Local Power Change\r\n");
+ processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE,
+ process_new_status, STATE_COMPLETE);
+ } else if (hub_status.change.over_current) {
+ TU_LOG_DRV(" Over Current\r\n");
+ processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE,
+ process_new_status, STATE_COMPLETE);
+ }
+ break;
}
- }
- if (!processed) {
- TU_ASSERT(hub_edpt_status_xfer(daddr), );
- }
-}
+ case STATE_CLEAR_CHANGE:
+ // Get port status complete --> clear change
+ if (p_hub->port_status.change.connection) {
+ // Connection change
+ // Port is powered and enabled
+ //TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
-static void connection_clear_conn_change_complete (tuh_xfer_t* xfer) {
- const uint8_t daddr = xfer->daddr;
+ // Acknowledge Port Connection Change
+ processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE,
+ process_new_status, STATE_CHECK_CONN);
+ } else if (p_hub->port_status.change.port_enable) {
+ processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE,
+ process_new_status, STATE_COMPLETE);
+ } else if (p_hub->port_status.change.suspend) {
+ processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE,
+ process_new_status, STATE_COMPLETE);
+ } else if (p_hub->port_status.change.over_current) {
+ processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE,
+ process_new_status, STATE_COMPLETE);
+ } else if (p_hub->port_status.change.reset) {
+ processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE,
+ process_new_status, STATE_COMPLETE);
+ }
+ break;
- if (xfer->result != XFER_RESULT_SUCCESS) {
- TU_ASSERT(hub_edpt_status_xfer(daddr), );
- return;
- }
+ case STATE_CHECK_CONN: {
+ const hcd_event_t event = {
+ .rhport = usbh_get_rhport(daddr),
+ .event_id = p_hub->port_status.status.connection ? HCD_EVENT_DEVICE_ATTACH : HCD_EVENT_DEVICE_REMOVE,
+ .connection = {
+ .hub_addr = daddr,
+ .hub_port = port_num
+ }
+ };
+ hcd_event_handler(&event, false);
+ processed = true; // usbh queue status after handled this in (de)enumeration
+ break;
+ }
- hub_interface_t *p_hub = get_hub_itf(daddr);
- const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ case STATE_COMPLETE:
+ default:
+ processed = false; // complete this status, queue next status
+ break;
- if (p_hub->port_status.status.connection) {
- // Reset port if attach event
- hub_port_reset(daddr, port_num, connection_port_reset_complete, 0);
- } else {
- // submit detach event
- const hcd_event_t event = {
- .rhport = usbh_get_rhport(daddr),
- .event_id = HCD_EVENT_DEVICE_REMOVE,
- .connection = {
- .hub_addr = daddr,
- .hub_port = port_num
- }
- };
- hcd_event_handler(&event, false);
}
-}
-
-static void connection_port_reset_complete (tuh_xfer_t* xfer) {
- const uint8_t daddr = xfer->daddr;
- if (xfer->result != XFER_RESULT_SUCCESS) {
- // retry port reset if failed
- if (!tuh_control_xfer(xfer)) {
- TU_ASSERT(hub_edpt_status_xfer(daddr), ); // back to status poll if failed to queue request
- }
- return;
+ if (!processed) {
+ TU_ASSERT(hub_edpt_status_xfer(daddr),);
}
-
- const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
-
- // submit attach event
- hcd_event_t event = {
- .rhport = usbh_get_rhport(daddr),
- .event_id = HCD_EVENT_DEVICE_ATTACH,
- .connection = {
- .hub_addr = daddr,
- .hub_port = port_num
- }
- };
- hcd_event_handler(&event, false);
}
#endif
diff --git a/src/host/hub.h b/src/host/hub.h
index e4e576661..3587f0ee3 100644
--- a/src/host/hub.h
+++ b/src/host/hub.h
@@ -170,9 +170,13 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Get port status
+// If hub_port != 0, resp is ignored. hub_port_get_status_local() can be used to retrieve the status
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void *resp,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+// Get port status from local cache. This does not send a request to the device
+bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp);
+
// Get status from Interrupt endpoint
bool hub_edpt_status_xfer(uint8_t daddr);
@@ -188,7 +192,7 @@ bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
}
-// Get Hub status
+// Get Hub status (port = 0)
TU_ATTR_ALWAYS_INLINE static inline
bool hub_get_status(uint8_t hub_addr, void* resp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return hub_port_get_status(hub_addr, 0, resp, complete_cb, user_data);
@@ -205,7 +209,7 @@ bool hub_clear_feature(uint8_t hub_addr, uint8_t feature, tuh_xfer_cb_t complete
bool hub_init (void);
bool hub_deinit (void);
bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-bool hub_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool hub_set_config (uint8_t daddr, uint8_t itf_num);
bool hub_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void hub_close (uint8_t dev_addr);
diff --git a/src/host/usbh.c b/src/host/usbh.c
index f4a6e1697..94a1fa247 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -360,7 +360,6 @@ bool tuh_rhport_reset_bus(uint8_t rhport, bool active) {
//--------------------------------------------------------------------+
// PUBLIC API (Parameter Verification is required)
//--------------------------------------------------------------------+
-
bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
return hcd_configure(rhport, cfg_id, cfg_param);
}
@@ -534,7 +533,6 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
}
} else {
TU_LOG1("[%u:] USBH Device Attach\r\n", event.rhport);
- _usbh_data.enumerating_daddr = 0;
enum_new_device(&event);
}
break;
@@ -1022,9 +1020,11 @@ bool tuh_bus_info_get(uint8_t daddr, tuh_bus_info_t* bus_info) {
TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) {
switch (event->event_id) {
+ case HCD_EVENT_DEVICE_ATTACH:
+
+ break;
+
case HCD_EVENT_DEVICE_REMOVE:
- // FIXME device remove from a hub need an HCD API for hcd to free up endpoint
- // mark device as removing to prevent further xfer before the event is processed in usbh task
break;
default: break;
@@ -1228,8 +1228,7 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \
return (uint8_t) result
-uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index,
- void* buffer, uint16_t len) {
+uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len);
}
@@ -1237,33 +1236,27 @@ uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len
_CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len);
}
-uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index,
- void* buffer, uint16_t len) {
+uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len);
}
-uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index,
- void* buffer, uint16_t len) {
+uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len);
}
-uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id,
- void* buffer, uint16_t len) {
+uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_string, daddr, index, language_id, buffer, len);
}
-uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id,
- void* buffer, uint16_t len) {
+uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len);
}
-uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id,
- void* buffer, uint16_t len) {
+uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_product_string, daddr, language_id, buffer, len);
}
-uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id,
- void* buffer, uint16_t len) {
+uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len);
}
@@ -1361,27 +1354,24 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
//--------------------------------------------------------------------+
// Enumeration Process
-// is a lengthy process with a series of control transfer to configure
-// newly attached device.
+// is a lengthy process with a series of control transfer to configure newly attached device.
// NOTE: due to the shared control buffer, we must complete enumerating
// one device before enumerating another one.
//--------------------------------------------------------------------+
-
-enum {
- ENUM_DEBOUNCING_DELAY_MS = 200, // when plug/unplug a device, physical connection can be bouncing and may
- // generate a series of attach/detach event. This delay wait for stable connection
- ENUM_RESET_DELAY_MS = 50, // USB specs: 10 to 50ms
+enum { // USB 2.0 specs 7.1.7 for timing
+ ENUM_DEBOUNCING_DELAY_MS = 150, // T(ATTDB) minimum 100 ms for stable connection
+ ENUM_RESET_ROOT_DELAY_MS = 50, // T(DRSTr) minimum 50 ms for reset from root port
+ ENUM_RESET_HUB_DELAY_MS = 20, // T(DRST) 10-20 ms for hub reset
+ ENUM_RESET_RECOVERY_DELAY_MS = 10, // T(RSTRCY) minimum 10 ms for reset recovery
};
enum {
ENUM_IDLE,
- ENUM_RESET_1, // 1st reset when attached
- //ENUM_HUB_GET_STATUS_1,
- ENUM_HUB_CLEAR_RESET_1,
+ ENUM_HUB_RERSET,
+ ENUM_HUB_GET_STATUS_AFTER_RESET,
+ ENUM_HUB_CLEAR_RESET,
+
ENUM_ADDR0_DEVICE_DESC,
- ENUM_RESET_2, // 2nd reset before set address (not used)
- ENUM_HUB_GET_STATUS_2,
- ENUM_HUB_CLEAR_RESET_2,
ENUM_SET_ADDR,
ENUM_GET_DEVICE_DESC,
ENUM_GET_STRING_LANGUAGE_ID_LEN,
@@ -1401,6 +1391,63 @@ enum {
static uint8_t enum_get_new_address(bool is_hub);
static bool enum_parse_configuration_desc (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
static void enum_full_complete(void);
+static void process_enumeration(tuh_xfer_t* xfer);
+
+// start a new enumeration process
+static bool enum_new_device(hcd_event_t* event) {
+ tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
+ dev0_bus->rhport = event->rhport;
+ dev0_bus->hub_addr = event->connection.hub_addr;
+ dev0_bus->hub_port = event->connection.hub_port;
+
+ _usbh_data.enumerating_daddr = 0;
+
+ // wait until device connection is stable TODO non blocking
+ tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
+
+ if (dev0_bus->hub_addr == 0) {
+ // connected directly to roothub
+ // USB bus not active and frame number is not available yet.
+ // need to depend on tusb_time_millis_api() TODO non blocking
+
+ if (!hcd_port_connect_status(dev0_bus->rhport)) {
+ TU_LOG_USBH("Device unplugged while debouncing\r\n");
+ enum_full_complete();
+ return true;
+ }
+
+ // reset device
+ hcd_port_reset(dev0_bus->rhport);
+ tusb_time_delay_ms_api(ENUM_RESET_ROOT_DELAY_MS);
+ hcd_port_reset_end(dev0_bus->rhport);
+
+ if (!hcd_port_connect_status(dev0_bus->rhport)) {
+ // device unplugged while delaying
+ enum_full_complete();
+ return true;
+ }
+
+ dev0_bus->speed = hcd_port_speed_get(dev0_bus->rhport);
+ TU_LOG_USBH("%s Speed\r\n", tu_str_speed[dev0_bus->speed]);
+
+ // fake transfer to kick-off the enumeration process
+ tuh_xfer_t xfer;
+ xfer.daddr = 0;
+ xfer.result = XFER_RESULT_SUCCESS;
+ xfer.user_data = ENUM_ADDR0_DEVICE_DESC;
+ process_enumeration(&xfer);
+ }
+ #if CFG_TUH_HUB
+ else {
+ // connected via hub
+ TU_VERIFY(dev0_bus->hub_port != 0);
+ TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, NULL,
+ process_enumeration, ENUM_HUB_RERSET));
+ }
+ #endif // hub
+
+ return true;
+}
// process device enumeration
static void process_enumeration(tuh_xfer_t* xfer) {
@@ -1431,6 +1478,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
uint8_t const daddr = xfer->daddr;
uintptr_t const state = xfer->user_data;
usbh_device_t* dev = get_device(daddr);
+ tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
if (daddr > 0) {
TU_ASSERT(dev,);
}
@@ -1438,53 +1486,54 @@ static void process_enumeration(tuh_xfer_t* xfer) {
switch (state) {
#if CFG_TUH_HUB
- //case ENUM_HUB_GET_STATUS_1: break;
- case ENUM_HUB_CLEAR_RESET_1: {
+ case ENUM_HUB_RERSET: {
hub_port_status_response_t port_status;
- memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
+ hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
if (!port_status.status.connection) {
- // device unplugged while delaying, nothing else to do
+ TU_LOG_USBH("Device unplugged from hub while debouncing\r\n");
enum_full_complete();
return;
}
- _usbh_data.dev0_bus.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
- (port_status.status.low_speed) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
-
- // Acknowledge Port Reset Change
- if (port_status.change.reset) {
- hub_port_clear_reset_change(_usbh_data.dev0_bus.hub_addr, _usbh_data.dev0_bus.hub_port,
- process_enumeration, ENUM_ADDR0_DEVICE_DESC);
- }
+ TU_ASSERT(hub_port_reset(dev0_bus->hub_addr, dev0_bus->hub_port, process_enumeration, ENUM_HUB_GET_STATUS_AFTER_RESET),);
break;
}
- case ENUM_HUB_GET_STATUS_2:
- tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
- TU_ASSERT(hub_port_get_status(_usbh_data.dev0_bus.hub_addr, _usbh_data.dev0_bus.hub_port, _usbh_epbuf.ctrl,
- process_enumeration, ENUM_HUB_CLEAR_RESET_2),);
+ case ENUM_HUB_GET_STATUS_AFTER_RESET: {
+ tusb_time_delay_ms_api(ENUM_RESET_HUB_DELAY_MS); // wait for reset to take effect
+
+ // get status to check for reset change
+ TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, NULL, process_enumeration, ENUM_HUB_CLEAR_RESET),);
break;
+ }
- case ENUM_HUB_CLEAR_RESET_2: {
+ case ENUM_HUB_CLEAR_RESET: {
hub_port_status_response_t port_status;
- memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
+ hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
+ dev0_bus->speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
+ (port_status.status.low_speed) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
- // Acknowledge Port Reset Change if Reset Successful
if (port_status.change.reset) {
- TU_ASSERT(hub_port_clear_reset_change(_usbh_data.dev0_bus.hub_addr, _usbh_data.dev0_bus.hub_port,
- process_enumeration, ENUM_SET_ADDR),);
+ // Acknowledge Port Reset Change
+ TU_ASSERT(hub_port_clear_reset_change(dev0_bus->hub_addr, dev0_bus->hub_port, process_enumeration, ENUM_ADDR0_DEVICE_DESC),);
+ } else {
+ // maybe retry if reset change not set but we need timeout to prevent infinite loop
+ // TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, NULL, process_enumeration, ENUM_HUB_CLEAR_RESET),);
}
+
break;
}
#endif
case ENUM_ADDR0_DEVICE_DESC: {
+ tusb_time_delay_ms_api(ENUM_RESET_RECOVERY_DELAY_MS); // reset recovery
+
// TODO probably doesn't need to open/close each enumeration
uint8_t const addr0 = 0;
TU_ASSERT(usbh_edpt_control_open(addr0, 8),);
- // Get first 8 bytes of device descriptor for Control Endpoint size
+ // Get first 8 bytes of device descriptor for control endpoint size
TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n");
TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_epbuf.ctrl, 8,
process_enumeration, ENUM_SET_ADDR),);
@@ -1686,59 +1735,6 @@ static void process_enumeration(tuh_xfer_t* xfer) {
}
}
-static bool enum_new_device(hcd_event_t* event) {
- tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
- dev0_bus->rhport = event->rhport;
- dev0_bus->hub_addr = event->connection.hub_addr;
- dev0_bus->hub_port = event->connection.hub_port;
-
- // wait until device connection is stable TODO non blocking
- tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
-
- if (dev0_bus->hub_addr == 0) {
- // connected directly to roothub
-
- if (!hcd_port_connect_status(dev0_bus->rhport)) {
- TU_LOG_USBH("Device unplugged while debouncing\r\n");
- enum_full_complete();
- return true;
- }
-
- hcd_port_reset(dev0_bus->rhport); // reset device
-
- // Since we are in middle of rhport reset, frame number is not available yet.
- // need to depend on tusb_time_millis_api() TODO non blocking
- tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
-
- hcd_port_reset_end(dev0_bus->rhport);
-
- // device unplugged while delaying
- if (!hcd_port_connect_status(dev0_bus->rhport)) {
- enum_full_complete();
- return true;
- }
-
- dev0_bus->speed = hcd_port_speed_get(dev0_bus->rhport);
- TU_LOG_USBH("%s Speed\r\n", tu_str_speed[dev0_bus->speed]);
-
- // fake transfer to kick-off the enumeration process
- tuh_xfer_t xfer;
- xfer.daddr = 0;
- xfer.result = XFER_RESULT_SUCCESS;
- xfer.user_data = ENUM_ADDR0_DEVICE_DESC;
- process_enumeration(&xfer);
- }
-#if CFG_TUH_HUB
- else {
- // connected via external hub
- TU_ASSERT(hub_port_get_status(dev0_bus->hub_addr, dev0_bus->hub_port, _usbh_epbuf.ctrl,
- process_enumeration, ENUM_HUB_CLEAR_RESET_1));
- }
-#endif // hub
-
- return true;
-}
-
static uint8_t enum_get_new_address(bool is_hub) {
uint8_t start;
uint8_t end;