Skip to content

Commit b5d4d0f

Browse files
authored
Merge pull request #3096 from hathach/usbh-improve-hub
Usbh improve hub
2 parents 1afe8a0 + b5b7a4b commit b5d4d0f

File tree

5 files changed

+320
-299
lines changed

5 files changed

+320
-299
lines changed

.idea/cmake.xml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/host/hub.c

Lines changed: 115 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ typedef struct {
5757
TUH_EPBUF_DEF(ctrl_buf, CFG_TUH_HUB_BUFSIZE);
5858
} hub_epbuf_t;
5959

60+
static tuh_xfer_cb_t user_complete_cb = NULL;
6061
static hub_interface_t hub_itfs[CFG_TUH_HUB];
6162
CFG_TUH_MEM_SECTION static hub_epbuf_t hub_epbufs[CFG_TUH_HUB];
6263

64+
6365
TU_ATTR_ALWAYS_INLINE static inline hub_interface_t* get_hub_itf(uint8_t daddr) {
6466
return &hub_itfs[daddr-1-CFG_TUH_DEVICE_MAX];
6567
}
@@ -142,10 +144,23 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
142144
};
143145

144146
TU_LOG_DRV("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
145-
TU_ASSERT( tuh_control_xfer(&xfer) );
147+
TU_ASSERT(tuh_control_xfer(&xfer));
146148
return true;
147149
}
148150

151+
static void port_get_status_complete (tuh_xfer_t* xfer) {
152+
if (xfer->result == XFER_RESULT_SUCCESS) {
153+
hub_interface_t* p_hub = get_hub_itf(xfer->daddr);
154+
p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
155+
}
156+
157+
xfer->complete_cb = user_complete_cb;
158+
user_complete_cb = NULL;
159+
if (xfer->complete_cb) {
160+
xfer->complete_cb(xfer);
161+
}
162+
}
163+
149164
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
150165
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
151166
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,
169184
.user_data = user_data
170185
};
171186

187+
if (hub_port != 0) {
188+
// intercept complete callback to save port status, ignore resp
189+
hub_epbuf_t* p_epbuf = get_hub_epbuf(hub_addr);
190+
xfer.complete_cb = port_get_status_complete;
191+
xfer.buffer = p_epbuf->ctrl_buf;
192+
user_complete_cb = complete_cb;
193+
} else {
194+
user_complete_cb = NULL;
195+
}
196+
172197
TU_LOG_DRV("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
173-
TU_VERIFY( tuh_control_xfer(&xfer) );
198+
TU_VERIFY(tuh_control_xfer(&xfer));
199+
return true;
200+
}
201+
202+
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp) {
203+
(void) hub_port;
204+
TU_VERIFY(hub_addr > CFG_TUH_DEVICE_MAX);
205+
hub_interface_t* p_hub = get_hub_itf(hub_addr);
206+
*resp = p_hub->port_status;
174207
return true;
175208
}
176209

@@ -238,10 +271,10 @@ bool hub_edpt_status_xfer(uint8_t daddr) {
238271
static void config_set_port_power (tuh_xfer_t* xfer);
239272
static void config_port_power_complete (tuh_xfer_t* xfer);
240273

241-
bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) {
242-
hub_interface_t* p_hub = get_hub_itf(dev_addr);
274+
bool hub_set_config(uint8_t daddr, uint8_t itf_num) {
275+
hub_interface_t* p_hub = get_hub_itf(daddr);
243276
TU_ASSERT(itf_num == p_hub->itf_num);
244-
hub_epbuf_t* p_epbuf = get_hub_epbuf(dev_addr);
277+
hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
245278

246279
// Get Hub Descriptor
247280
tusb_control_request_t const request = {
@@ -257,7 +290,7 @@ bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) {
257290
};
258291

259292
tuh_xfer_t xfer = {
260-
.daddr = dev_addr,
293+
.daddr = daddr,
261294
.ep_addr = 0,
262295
.setup = &request,
263296
.buffer = p_epbuf->ctrl_buf,
@@ -312,11 +345,15 @@ static void config_port_power_complete (tuh_xfer_t* xfer) {
312345
//--------------------------------------------------------------------+
313346
// Connection Changes
314347
//--------------------------------------------------------------------+
315-
static void get_status_complete (tuh_xfer_t* xfer);
316-
static void port_get_status_complete (tuh_xfer_t* xfer);
317-
static void port_clear_feature_complete_stub(tuh_xfer_t* xfer);
318-
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
319-
static void connection_port_reset_complete (tuh_xfer_t* xfer);
348+
enum {
349+
STATE_IDLE = 0,
350+
STATE_HUB_STATUS,
351+
STATE_CLEAR_CHANGE,
352+
STATE_CHECK_CONN,
353+
STATE_COMPLETE
354+
};
355+
356+
static void process_new_status(tuh_xfer_t* xfer);
320357

321358
// callback as response of interrupt endpoint polling
322359
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
337374
processed = false;
338375
} else if (tu_bit_test(status_change, 0)) {
339376
// Hub bit 0 is for the hub device events
340-
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, get_status_complete, 0);
377+
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, process_new_status, STATE_HUB_STATUS);
341378
} else {
342379
// Hub bits 1 to n are hub port events
343380
for (uint8_t port=1; port <= p_hub->bNbrPorts; port++) {
344381
if (tu_bit_test(status_change, port)) {
345-
processed = hub_port_get_status(daddr, port, p_epbuf->ctrl_buf, port_get_status_complete, 0);
382+
processed = hub_port_get_status(daddr, port, NULL, process_new_status, STATE_CLEAR_CHANGE);
346383
break; // after completely processed one port, we will re-queue the status poll and handle next one
347384
}
348385
}
@@ -358,117 +395,84 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
358395
return true;
359396
}
360397

361-
static void port_clear_feature_complete_stub(tuh_xfer_t* xfer) {
362-
hub_edpt_status_xfer(xfer->daddr);
363-
}
364-
365-
static void get_status_complete(tuh_xfer_t *xfer) {
398+
static void process_new_status(tuh_xfer_t* xfer) {
366399
const uint8_t daddr = xfer->daddr;
367400

368-
bool processed = false; // true if new status is processed
369-
if (xfer->result == XFER_RESULT_SUCCESS) {
370-
hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
371-
372-
TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
373-
374-
if (hub_status.change.local_power_source) {
375-
TU_LOG_DRV(" Local Power Change\r\n");
376-
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE, port_clear_feature_complete_stub, 0);
377-
} else if (hub_status.change.over_current) {
378-
TU_LOG_DRV(" Over Current\r\n");
379-
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
380-
}
381-
}
382-
383-
if (!processed) {
384-
TU_ASSERT(hub_edpt_status_xfer(daddr), );
401+
if (xfer->result != XFER_RESULT_SUCCESS) {
402+
TU_ASSERT(hub_edpt_status_xfer(daddr),);
403+
return;
385404
}
386-
}
387405

388-
static void port_get_status_complete(tuh_xfer_t *xfer) {
389-
const uint8_t daddr = xfer->daddr;
406+
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
407+
hub_interface_t *p_hub = get_hub_itf(daddr);
408+
const uintptr_t state = xfer->user_data;
390409
bool processed = false; // true if new status is processed
391410

392-
if (xfer->result == XFER_RESULT_SUCCESS) {
393-
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
394-
hub_interface_t *p_hub = get_hub_itf(daddr);
395-
p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
396-
397-
// Clear port status change interrupts
398-
if (p_hub->port_status.change.connection) {
399-
// Connection change
400-
// Port is powered and enabled
401-
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
402-
403-
// Acknowledge Port Connection Change
404-
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
405-
} else if (p_hub->port_status.change.port_enable) {
406-
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE, port_clear_feature_complete_stub, 0);
407-
} else if (p_hub->port_status.change.suspend) {
408-
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE, port_clear_feature_complete_stub, 0);
409-
} else if (p_hub->port_status.change.over_current) {
410-
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
411-
} else if (p_hub->port_status.change.reset) {
412-
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE, port_clear_feature_complete_stub, 0);
411+
switch (state) {
412+
case STATE_HUB_STATUS: {
413+
hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
414+
TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
415+
if (hub_status.change.local_power_source) {
416+
TU_LOG_DRV(" Local Power Change\r\n");
417+
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE,
418+
process_new_status, STATE_COMPLETE);
419+
} else if (hub_status.change.over_current) {
420+
TU_LOG_DRV(" Over Current\r\n");
421+
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE,
422+
process_new_status, STATE_COMPLETE);
423+
}
424+
break;
413425
}
414-
}
415426

416-
if (!processed) {
417-
TU_ASSERT(hub_edpt_status_xfer(daddr), );
418-
}
419-
}
420-
421-
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer) {
422-
const uint8_t daddr = xfer->daddr;
423-
424-
if (xfer->result != XFER_RESULT_SUCCESS) {
425-
TU_ASSERT(hub_edpt_status_xfer(daddr), );
426-
return;
427-
}
427+
case STATE_CLEAR_CHANGE:
428+
// Get port status complete --> clear change
429+
if (p_hub->port_status.change.connection) {
430+
// Connection change
431+
// Port is powered and enabled
432+
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
433+
434+
// Acknowledge Port Connection Change
435+
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE,
436+
process_new_status, STATE_CHECK_CONN);
437+
} else if (p_hub->port_status.change.port_enable) {
438+
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE,
439+
process_new_status, STATE_COMPLETE);
440+
} else if (p_hub->port_status.change.suspend) {
441+
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE,
442+
process_new_status, STATE_COMPLETE);
443+
} else if (p_hub->port_status.change.over_current) {
444+
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE,
445+
process_new_status, STATE_COMPLETE);
446+
} else if (p_hub->port_status.change.reset) {
447+
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE,
448+
process_new_status, STATE_COMPLETE);
449+
}
450+
break;
451+
452+
case STATE_CHECK_CONN: {
453+
const hcd_event_t event = {
454+
.rhport = usbh_get_rhport(daddr),
455+
.event_id = p_hub->port_status.status.connection ? HCD_EVENT_DEVICE_ATTACH : HCD_EVENT_DEVICE_REMOVE,
456+
.connection = {
457+
.hub_addr = daddr,
458+
.hub_port = port_num
459+
}
460+
};
461+
hcd_event_handler(&event, false);
462+
processed = true; // usbh queue status after handled this in (de)enumeration
463+
break;
464+
}
428465

429-
hub_interface_t *p_hub = get_hub_itf(daddr);
430-
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
466+
case STATE_COMPLETE:
467+
default:
468+
processed = false; // complete this status, queue next status
469+
break;
431470

432-
if (p_hub->port_status.status.connection) {
433-
// Reset port if attach event
434-
hub_port_reset(daddr, port_num, connection_port_reset_complete, 0);
435-
} else {
436-
// submit detach event
437-
const hcd_event_t event = {
438-
.rhport = usbh_get_rhport(daddr),
439-
.event_id = HCD_EVENT_DEVICE_REMOVE,
440-
.connection = {
441-
.hub_addr = daddr,
442-
.hub_port = port_num
443-
}
444-
};
445-
hcd_event_handler(&event, false);
446471
}
447-
}
448-
449-
static void connection_port_reset_complete (tuh_xfer_t* xfer) {
450-
const uint8_t daddr = xfer->daddr;
451472

452-
if (xfer->result != XFER_RESULT_SUCCESS) {
453-
// retry port reset if failed
454-
if (!tuh_control_xfer(xfer)) {
455-
TU_ASSERT(hub_edpt_status_xfer(daddr), ); // back to status poll if failed to queue request
456-
}
457-
return;
473+
if (!processed) {
474+
TU_ASSERT(hub_edpt_status_xfer(daddr),);
458475
}
459-
460-
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
461-
462-
// submit attach event
463-
hcd_event_t event = {
464-
.rhport = usbh_get_rhport(daddr),
465-
.event_id = HCD_EVENT_DEVICE_ATTACH,
466-
.connection = {
467-
.hub_addr = daddr,
468-
.hub_port = port_num
469-
}
470-
};
471-
hcd_event_handler(&event, false);
472476
}
473477

474478
#endif

src/host/hub.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,13 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
170170
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
171171

172172
// Get port status
173+
// If hub_port != 0, resp is ignored. hub_port_get_status_local() can be used to retrieve the status
173174
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void *resp,
174175
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
175176

177+
// Get port status from local cache. This does not send a request to the device
178+
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp);
179+
176180
// Get status from Interrupt endpoint
177181
bool hub_edpt_status_xfer(uint8_t daddr);
178182

@@ -188,7 +192,7 @@ bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb
188192
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
189193
}
190194

191-
// Get Hub status
195+
// Get Hub status (port = 0)
192196
TU_ATTR_ALWAYS_INLINE static inline
193197
bool hub_get_status(uint8_t hub_addr, void* resp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
194198
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
205209
bool hub_init (void);
206210
bool hub_deinit (void);
207211
bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
208-
bool hub_set_config (uint8_t dev_addr, uint8_t itf_num);
212+
bool hub_set_config (uint8_t daddr, uint8_t itf_num);
209213
bool hub_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
210214
void hub_close (uint8_t dev_addr);
211215

0 commit comments

Comments
 (0)