Skip to content

Commit 754d357

Browse files
committed
refactor(ext_hub): Fixed device release, optimized the order of closing usbh device
1 parent 0dbce72 commit 754d357

File tree

1 file changed

+48
-106
lines changed

1 file changed

+48
-106
lines changed

components/usb/ext_hub.c

Lines changed: 48 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,6 @@ static esp_err_t device_port_free(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
420420
EXT_HUB_CHECK(ext_hub_dev->constant.ports[port_idx] != NULL, ESP_ERR_INVALID_STATE);
421421

422422
bool call_proc_req_cb = false;
423-
bool all_ports_freed = false;
424423

425424
assert(ext_hub_dev->single_thread.maxchild != 0);
426425
assert(p_ext_hub_driver->constant.port_driver);
@@ -435,25 +434,11 @@ static esp_err_t device_port_free(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
435434
ext_hub_dev->single_thread.maxchild--;
436435

437436
EXT_HUB_ENTER_CRITICAL();
438-
if (ext_hub_dev->dynamic.flags.is_gone) {
439-
all_ports_freed = (ext_hub_dev->single_thread.maxchild == 0);
440-
441-
if (all_ports_freed) {
442-
ext_hub_dev->dynamic.flags.waiting_children = 0;
443-
ext_hub_dev->dynamic.flags.waiting_free = 1;
444-
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_FREE);
445-
} else {
446-
ext_hub_dev->dynamic.flags.waiting_children = 1;
447-
}
437+
if (ext_hub_dev->dynamic.flags.waiting_free && ext_hub_dev->single_thread.maxchild == 0) {
438+
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_FREE);
448439
}
449440
EXT_HUB_EXIT_CRITICAL();
450441

451-
// Close the device if all children were freed
452-
if (all_ports_freed) {
453-
ESP_LOGD(EXT_HUB_TAG, "[%d] Release USBH device object", ext_hub_dev->constant.dev_addr);
454-
ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl));
455-
}
456-
457442
if (call_proc_req_cb) {
458443
p_ext_hub_driver->constant.proc_req_cb(false, p_ext_hub_driver->constant.proc_req_cb_arg);
459444
}
@@ -463,71 +448,60 @@ static esp_err_t device_port_free(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
463448
return ret;
464449
}
465450

466-
static esp_err_t device_release_ports(ext_hub_dev_t *ext_hub_dev)
451+
static esp_err_t device_release_port(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
467452
{
468-
esp_err_t ret = ESP_OK;
469-
// Mark all ports as gone
470-
for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) {
471-
// Only for ports, that were created
472-
if (ext_hub_dev->constant.ports[i] != NULL) {
473-
// Mark port as gone
474-
ret = p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[i]);
475-
if (ret == ESP_OK) {
476-
// Port doesn't have a device and can be recycled right now
477-
ESP_ERROR_CHECK(device_port_free(ext_hub_dev, i));
478-
} else if (ret == ESP_ERR_NOT_FINISHED) {
479-
// Port has a device and will be recycled after USBH device will be released by all clients and freed
480-
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port is gone", ext_hub_dev->constant.dev_addr, i + 1);
481-
// Not an error case, but it's good to notify this with the error message
482-
// TODO: IDF-12173 remove the error, instead set up the flag and verify the flag while recycle
483-
ret = ESP_OK;
484-
} else {
485-
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to mark port as gone: %s",
486-
ext_hub_dev->constant.dev_addr, i + 1, esp_err_to_name(ret));
487-
return ret;
488-
}
489-
} else {
490-
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port was not created", ext_hub_dev->constant.dev_addr, i + 1);
491-
}
453+
assert(ext_hub_dev->constant.ports[port_idx] != NULL);
454+
assert(ext_hub_dev->single_thread.maxchild != 0);
455+
assert(p_ext_hub_driver->constant.port_driver);
456+
457+
// Mark port as gone
458+
esp_err_t ret = p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[port_idx]);
459+
if (ret == ESP_OK) {
460+
// Port doesn't have a device and can be freed right now
461+
ESP_ERROR_CHECK(device_port_free(ext_hub_dev, port_idx));
462+
} else if (ret == ESP_ERR_NOT_FINISHED) {
463+
// Port has a device and will be freed on recycle
464+
EXT_HUB_ENTER_CRITICAL();
465+
ext_hub_dev->dynamic.flags.waiting_children = 1;
466+
EXT_HUB_EXIT_CRITICAL();
467+
ret = ESP_OK;
468+
} else {
469+
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to mark port as gone: %s",
470+
ext_hub_dev->constant.dev_addr, port_idx + 1, esp_err_to_name(ret));
471+
return ret;
492472
}
473+
493474
return ret;
494475
}
495476

477+
static void device_release_ports(ext_hub_dev_t *ext_hub_dev)
478+
{
479+
assert(ext_hub_dev->constant.hub_desc->bNbrPorts); // Sanity check
480+
for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) {
481+
ESP_ERROR_CHECK(device_release_port(ext_hub_dev, i));
482+
}
483+
}
484+
496485
static void device_release(ext_hub_dev_t *ext_hub_dev)
497486
{
498487
ESP_LOGD(EXT_HUB_TAG, "[%d] Device release", ext_hub_dev->constant.dev_addr);
499488

500489
EXT_HUB_ENTER_CRITICAL();
501-
assert(ext_hub_dev->dynamic.flags.waiting_release); // Device should waiting the release
502-
ext_hub_dev->dynamic.flags.is_gone = 1;
490+
assert(ext_hub_dev->dynamic.flags.waiting_release); // Sanity check
503491
ext_hub_dev->dynamic.flags.waiting_release = 0;
492+
ext_hub_dev->dynamic.flags.waiting_free = 1;
504493
EXT_HUB_EXIT_CRITICAL();
505494

506-
// Release IN EP
507-
ESP_ERROR_CHECK(usbh_ep_command(ext_hub_dev->constant.ep_in_hdl, USBH_EP_CMD_HALT));
508-
509495
switch (ext_hub_dev->single_thread.state) {
510-
case EXT_HUB_STATE_ATTACHED:
511-
// Device has no configured ports, release the USBH device object
512-
ESP_LOGD(EXT_HUB_TAG, "[%d] Release USBH device object", ext_hub_dev->constant.dev_addr);
513-
ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl));
514-
break;
515496
case EXT_HUB_STATE_CONFIGURED:
516497
case EXT_HUB_STATE_SUSPENDED:
517498
assert(ext_hub_dev->constant.hub_desc != NULL); // Device should have a Hub descriptor
518499
assert(p_ext_hub_driver->constant.port_driver); // Port driver should be available
519-
520-
// Release ports if device has them
521-
if (ext_hub_dev->constant.hub_desc->bNbrPorts) {
522-
ESP_ERROR_CHECK(device_release_ports(ext_hub_dev));
523-
}
500+
device_release_ports(ext_hub_dev);
524501
break;
525502
default:
526-
// Should never occur
527-
abort();
528503
break;
529504
}
530-
531505
ext_hub_dev->single_thread.state = EXT_HUB_STATE_RELEASED;
532506
}
533507

@@ -771,17 +745,18 @@ static void device_free(ext_hub_dev_t *ext_hub_dev)
771745
TAILQ_REMOVE(&p_ext_hub_driver->dynamic.ext_hubs_tailq, ext_hub_dev, dynamic.tailq_entry);
772746
EXT_HUB_EXIT_CRITICAL();
773747

748+
ESP_ERROR_CHECK(usbh_ep_free(ext_hub_dev->constant.ep_in_hdl));
749+
ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl));
750+
774751
if (ext_hub_dev->constant.hub_desc) {
775752
heap_caps_free(ext_hub_dev->constant.hub_desc);
776753
}
777754
if (ext_hub_dev->constant.ports) {
778755
heap_caps_free(ext_hub_dev->constant.ports);
779756
}
780757

781-
ESP_ERROR_CHECK(usbh_ep_free(ext_hub_dev->constant.ep_in_hdl));
782758
urb_free(ext_hub_dev->constant.ctrl_urb);
783759
urb_free(ext_hub_dev->constant.in_urb);
784-
785760
heap_caps_free(ext_hub_dev);
786761
}
787762

@@ -1387,6 +1362,7 @@ esp_err_t ext_hub_dev_gone(uint8_t dev_addr)
13871362
ESP_LOGD(EXT_HUB_TAG, "[%d] Device gone", ext_hub_dev->constant.dev_addr);
13881363

13891364
EXT_HUB_ENTER_CRITICAL();
1365+
ext_hub_dev->dynamic.flags.is_gone = 1;
13901366
if (ext_hub_dev->dynamic.flags.waiting_release ||
13911367
ext_hub_dev->dynamic.flags.waiting_children) {
13921368
EXT_HUB_EXIT_CRITICAL();
@@ -1396,7 +1372,7 @@ esp_err_t ext_hub_dev_gone(uint8_t dev_addr)
13961372
}
13971373

13981374
if ((ext_hub_dev->single_thread.maxchild == 0) && !ext_hub_dev->dynamic.flags.in_pending_list) {
1399-
// Not in pending list and doesn't have any children
1375+
// Not in pending list and doesn't have any children release and free right away
14001376
ext_hub_dev->dynamic.flags.waiting_free = 1;
14011377
ext_hub_dev->dynamic.flags.waiting_release = 1;
14021378
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_RELEASE |
@@ -1438,7 +1414,9 @@ esp_err_t ext_hub_all_free(void)
14381414
while (hub_curr != NULL) {
14391415
hub_next = TAILQ_NEXT(hub_curr, dynamic.tailq_entry);
14401416
hub_curr->dynamic.flags.waiting_release = 1;
1441-
call_proc_req_cb = _device_set_actions(hub_curr, DEV_ACTION_RELEASE);
1417+
call_proc_req_cb = _device_set_actions(hub_curr, DEV_ACTION_EP1_FLUSH |
1418+
DEV_ACTION_EP1_DEQUEUE |
1419+
DEV_ACTION_RELEASE);
14421420
// At least one hub should be released
14431421
wait_for_free = true;
14441422
hub_curr = hub_next;
@@ -1575,66 +1553,30 @@ esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
15751553
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
15761554
ext_hub_dev_t *ext_hub_dev = (ext_hub_dev_t *)ext_hub_hdl;
15771555

1578-
esp_err_t ret;
15791556
uint8_t port_idx = port_num - 1;
1580-
bool free_port = false;
15811557
bool release_port = false;
1582-
15831558
EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
15841559
EXT_HUB_CHECK(p_ext_hub_driver->constant.port_driver != NULL, ESP_ERR_NOT_SUPPORTED);
15851560
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED ||
15861561
ext_hub_dev->single_thread.state == EXT_HUB_STATE_RELEASED, ESP_ERR_INVALID_STATE);
15871562

15881563
EXT_HUB_ENTER_CRITICAL();
1589-
if (ext_hub_dev->dynamic.flags.waiting_release) {
1564+
if (ext_hub_dev->dynamic.flags.waiting_release ||
1565+
ext_hub_dev->dynamic.flags.waiting_children) {
15901566
release_port = true;
1591-
} else if (ext_hub_dev->dynamic.flags.waiting_children) {
1592-
assert(ext_hub_dev->dynamic.flags.waiting_release == 0); // Device should be already released
1593-
assert(ext_hub_dev->dynamic.flags.is_gone == 1); // Device should be gone by now
1594-
free_port = true;
15951567
}
15961568
EXT_HUB_EXIT_CRITICAL();
15971569

1598-
if (!release_port && !free_port) {
1570+
esp_err_t ret;
1571+
if (release_port) {
1572+
ret = device_release_port(ext_hub_dev, port_idx);
1573+
} else {
15991574
// Parent still present, recycle the port
16001575
ret = p_ext_hub_driver->constant.port_driver->recycle(ext_hub_dev->constant.ports[port_idx]);
16011576
if (ret != ESP_OK) {
16021577
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to recycle the port: %s", ext_hub_dev->constant.dev_addr, port_num, esp_err_to_name(ret));
1603-
goto exit;
1604-
}
1605-
} else {
1606-
if (release_port) {
1607-
// Notify the port that parent is not available anymore and port should be recycled then freed
1608-
ret = p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[port_idx]);
1609-
if (ret == ESP_OK) {
1610-
ESP_LOGD(EXT_HUB_TAG, "[%d:%d] Port doesn't have a device and can be freed right now",
1611-
ext_hub_dev->constant.dev_addr,
1612-
port_num);
1613-
assert(free_port == false);
1614-
free_port = true;
1615-
} else if (ret == ESP_ERR_NOT_FINISHED) {
1616-
// Port has a device and will be recycled after USBH device will be released by all clients and freed
1617-
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port is gone",
1618-
ext_hub_dev->constant.dev_addr,
1619-
port_num);
1620-
// Logically, recycling logic are finished for the Hub Driver, return ESP_OK to free the node
1621-
assert(free_port == false);
1622-
} else {
1623-
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to mark port as gone: %s",
1624-
ext_hub_dev->constant.dev_addr, port_num, esp_err_to_name(ret));
1625-
}
1626-
}
1627-
1628-
if (free_port) {
1629-
ret = device_port_free(ext_hub_dev, port_idx);
1630-
if (ret != ESP_OK) {
1631-
goto exit;
1632-
}
16331578
}
16341579
}
1635-
1636-
ret = ESP_OK;
1637-
exit:
16381580
return ret;
16391581
}
16401582

0 commit comments

Comments
 (0)