Skip to content

Fully asynchrounous host code#3482

Closed
ceedriic wants to merge 15 commits intohathach:masterfrom
Precidata:non-blocking-host
Closed

Fully asynchrounous host code#3482
ceedriic wants to merge 15 commits intohathach:masterfrom
Precidata:non-blocking-host

Conversation

@ceedriic
Copy link
Copy Markdown
Contributor

@ceedriic ceedriic commented Feb 4, 2026

Describe the PR
This remove all calls to tusb_time_delay_ms_api() from inside tuh_task_ext().

Additional context
Use of the host API was not really possible with OPT_OS_NONE due to blocking calls in tuh_task_ext().

With an OS, it will also improve the situation because:

  1. The timeout_ms parameter of tuh_task_ext() is now respected.
  2. The traffic with device 1 will not stop when device 2 is plugged.

I've split this in 3 main patches (the last 6 are fixes for warnings discovered after generating the PR)

  1. Long boring preparation patch to split the functions calling tusb_time_delay_ms_api().
  2. Executing the continuation functions asynchronously.
  3. cleaning up the handling of deferred attachments with a separate queue (note: I've not really tested that last patch because with my hub and 2 devices for some reason I never got into this situation)

…r continuation chunks

This introduce a usbh_wait_delay_ms() function that calls tusb_time_delay_ms_api() followed by the continuation function. After this preparatory patch, no behaviour change should occur.
This is the interesting part of the patchset, which removes tusb_time_delay_ms_api() and instead call continuation functions asynchronously
This addresses a TODO and use a proper separate queue for deferred attachments instead of the existing hack which busy loop during deferred attachments.
Comment thread src/host/usbh.c Fixed
Comment thread src/host/usbh.c Fixed
Comment thread src/host/usbh.c
usbh_wait_delay_ms(ENUM_DEBOUNCING_DELAY_MS, enum_after_debouncing_delay);
}

static void enum_after_debouncing_delay(void) {

Check notice

Code scanning / CodeQL

Unused static function Note

Static function enum_after_debouncing_delay is unreachable
@ceedriic
Copy link
Copy Markdown
Contributor Author

ceedriic commented Feb 4, 2026

How to handle the few platforms missing tusb_time_millis_api()?
Is there a define that indicates that this function is available?
Is there really platforms that wants the HOST API but cannot implement tusb_time_millis_api()?

@HiFiPhile
Copy link
Copy Markdown
Collaborator

How to handle the few platforms missing tusb_time_millis_api()?

I see you can't wait longer :)

I've asked Claude if OSAL ports has millis API:

✅ Already supported:

  • OPT_OS_FREERTOS: Uses xTaskGetTickCount()
  • OPT_OS_MYNEWT: Uses os_time_ticks_to_ms32(os_time_get()
  • OPT_OS_PICO: Uses to_ms_since_boot(get_absolute_time()
  • OPT_OS_RTTHREAD: Uses rt_tick_get() converted to milliseconds
  • OPT_OS_ZEPHYR: Uses k_uptime_get_32()
  • OPT_OS_CUSTOM: Requires user implementation

❌ Not yet supported in board_api.h:

  • OPT_OS_RTX4: Would need to add time API (RTL.h doesn't expose millisecond counter directly)
    Recommendation

We can add a osal_millis_get for those who support.


Another challenge is control transfer and buffer management. To save ressource USBH only manage one control transfer (composed by setup+data+status packets) each time.

bool tuh_control_xfer (tuh_xfer_t* xfer) {

With the state managed here:

usbh_ctrl_xfer_info_t ctrl_xfer_info; // control transfer

And the buffer, which is also exposed to class driver as temporary buffer by usbh_get_enum_buf()

CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf;

Comment thread src/host/usbh.c
xfer.daddr = 0;
xfer.result = XFER_RESULT_SUCCESS;
xfer.user_data = ENUM_ADDR0_DEVICE_DESC;
process_enumeration(&xfer);

Check notice

Code scanning / CodeQL

Unused static function Note

Static function process_enumeration is unreachable (
enum_after_debouncing_delay
must be removed at the same time)
Static function process_enumeration is unreachable
Comment thread src/host/usbh.c
// Stop enumeration gracefully
enum_full_complete(false);
TU_ASSERT(false,);
}

Check notice

Code scanning / CodeQL

Unused static function Note

Static function enum_get_new_address is unreachable (
process_enumeration
must be removed at the same time)
Static function enum_get_new_address is unreachable (
enum_delay_async
must be removed at the same time)
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 5, 2026

MemBrowse Memory Report

stm32u0-stm32u083cdk-cdc_msc

  • FLASH: .text +8 B (+0.0%, 20,012 B / 262,144 B, total: 8% used)

No memory changes detected for:

Comment thread src/host/usbh.c Fixed
@ceedriic
Copy link
Copy Markdown
Contributor Author

ceedriic commented Feb 5, 2026

Ok, now all compilation checks pass.

How to handle the few platforms missing tusb_time_millis_api()?

I see you can't wait longer :)

Hey, I've customers waiting :)

Regarding tusb_time_millis_api(), I've added a configuration option, CFG_TUH_TASK_USE_TIME_MILLIS_API, which is enabled by default except for OPT_OS_RTX4 and OPT_OS_PICO. I've also disabled the feature for expressif.

For OPT_OS_PICO this is weird because in ./hw/bsp/board_api.h, board_millis() is defined, but I think ./hw/bsp/board.c, which implements tusb_time_millis_api() by calling board_millis(), is simply not compiled for some platforms (rp2040, expressif in particular).

I'll look at the control transfer issue after lunch.

@ceedriic
Copy link
Copy Markdown
Contributor Author

ceedriic commented Feb 5, 2026

Another challenge is control transfer and buffer management. To save ressource USBH only manage one control transfer (composed by setup+data+status packets) each time.

Ok, I've looked at tuh_control_xfer() and I think I see some of the issues. here are a few of my observations:

  1. tuh_control_xfer() without a callback can recurse into tuh_task(). I was scared when I saw that but fortunately tuh_control_xfer() is never called without callback in the enumeration code or anywhere in my app. However, I believe I will need to update the patch to make sur tuh_task() does not wake up timers inside such a recursive path.

  2. As you mentionned, ctrl_xfer_info can be owned by class driver code, which means the enumeration will fail if we're unlucky and if device 2 start an enumeration when device 1 set the DTR or something. The whole enumeration will forever fail, because:
    2.1) The return value of tuh_control_xfer() is not handled by enumaration code (ignored or ASSERTed).
    2.2) tuh_control_xfer() does not call the callback if it cannot acquire the buffer, therefore we do not get to the 3x retry code.
    2.3) there is no timeouts anywhere in the enumeration code.

  3. In the same way, application code for Device 1 could fail unexpectedly if an enumeration from Device 2 use the buffer needed by, say, acm_set_line_coding().

  4. Worse, in the case of acm_set_line_coding(), the failed call will also corrupt the enumeration buffer, and likely trash the enumeration process, because it writes to enum_buf outside of the protection of the ctrl_info->stage state check and the mutex.

  5. All of this means that the current code is fine with one device (which is my immediate need, luckily) but need some overhaul / refactoring / improvements to work well with multiple devices.

  6. None of the above is related to this PR (except the first point which I will fix) and can be handled later by further improvements / PRs. I'd be happy to help for the parts that I understand.

Am I correct? do we agree on all that?

Also, use a timeout of 0 instead of tuh_task_event_ready() check.
@ceedriic
Copy link
Copy Markdown
Contributor Author

ceedriic commented Feb 5, 2026

My goodness, is there a way to silence this "advanced" security bot?

Comment thread src/host/usbh.c Fixed
@hathach
Copy link
Copy Markdown
Owner

hathach commented Feb 5, 2026

My goodness, is there a way to silence this "advanced" security bot?

that is OK, just ignore it if it does not make sense. It is static analyzer warning from PVS-studio and/or other tool, but I currently configure some of the tusb config (this thing take lots of time to tweak and tune-up) therefore code may be mistaken as unreachable. I got annoyed by it as well, but we do review with a pair of human eye.

@HiFiPhile
Copy link
Copy Markdown
Collaborator

Ok, I've looked at tuh_control_xfer() and I think I see some of the issues. here are a few of my observations:

1 . Yes
2.1/2.2 Yes, currently there is no retry if tuh_control_xfer failed early.
2.3 To support timeout we need to modify HCD drivers to manage devices who sends NAK but no transfer error. (Windows use 20s timeout)
3. Yes
4. Oh, I saw it....
5. I tried to do something last week but realized that to have full concurrency we may need to add transfer status and buffer to each devices, it's a hard decision to make for smaller MCUs ...
6. I think you are right

@hathach what do you think ?

@ceedriic
Copy link
Copy Markdown
Contributor Author

ceedriic commented Feb 5, 2026

  1. I tried to do something last week but realized that to have full concurrency we may need to add transfer status and buffer to each devices, it's a hard decision to make for smaller MCUs ...

Reading the code, adding a transfer status to each device for use after enumeration was also my first idea (but keeping a global transfer status and buffer global for enumeration).

For the buffer, acm_set_line_coding() and friends could maybe just put the small buffers they need right into the CDC private structure, or worst case use malloc/free instead of the shared buffer when the system is configured with > 1 device.

Honestly, I can see Logitech selling a mouse with TinyUSB and a 8051... but I don't think many people would want to use TinyUSB as host with a "smaller MCU", especially with multiple devices....

Comment thread src/host/usbh.c

#if OSAL_MUTEX_REQUIRED
#if CFG_TUH_HUB
osal_queue_delete(_usbh_daq);

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
osal_queue_delete
has no external side effects).
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 11, 2026

Size Difference Report

Because TinyUSB code size varies by port and configuration, the metrics below represent the averaged totals across all example builds.

Note: If there is no change, only one value is shown.

Changes >1% in size

file .text .rodata .data .bss size % diff
usbh.c 4395 ➙ 4672 (+277) 60 ➙ 61 (+1) 63 ➙ 120 (+57) 954 ➙ 1008 (+54) 5436 ➙ 5822 (+386) +7.1%
TOTAL 4395 ➙ 4672 (+277) 60 ➙ 61 (+1) 63 ➙ 120 (+57) 954 ➙ 1008 (+54) 5436 ➙ 5822 (+386) +7.1%

Changes <1% in size

file .text .rodata .data .bss size % diff
hub.c 1235 ➙ 1236 (+1) 8 8 29 1268 ➙ 1269 (+1) +0.1%
tusb.c 429 ➙ 428 (-1) 0 368 3 430 ➙ 429 (-1) -0.2%
TOTAL 1664 8 376 32 1698 +0.0%
No changes
file .text .rodata .data .bss size % diff
audio_device.c 2849 0 1248 1673 4518 +0.0%
cdc_device.c 1328 16 19 661 1988 +0.0%
cdc_host.c 6596 487 15 1539 8357 +0.0%
dcd_ch32_usbfs.c 1472 0 0 2444 3916 +0.0%
dcd_ch32_usbhs.c 1648 0 0 448 2096 +0.0%
dcd_ci_fs.c 1925 0 0 1290 3215 +0.0%
dcd_ci_hs.c 1762 0 0 1280 2530 +0.0%
dcd_da146xx.c 3067 0 0 144 3211 +0.0%
dcd_dwc2.c 4101 24 0 263 4387 +0.0%
dcd_eptri.c 2270 0 0 259 2529 +0.0%
dcd_khci.c 1953 0 0 1290 3243 +0.0%
dcd_lpc17_40.c 1470 0 0 648 1794 +0.0%
dcd_lpc_ip3511.c 1463 0 0 264 1639 +0.0%
dcd_mm32f327x_otg.c 1478 0 0 1290 2768 +0.0%
dcd_msp430x5xx.c 1796 0 0 176 1972 +0.0%
dcd_musb.c 2446 0 0 160 2606 +0.0%
dcd_nrf5x.c 2919 0 0 292 3211 +0.0%
dcd_nuc120.c 1093 0 0 78 1171 +0.0%
dcd_nuc121.c 1167 0 0 101 1268 +0.0%
dcd_nuc505.c 0 0 1529 157 1686 +0.0%
dcd_rp2040.c 859 20 604 655 2138 +0.0%
dcd_rusb2.c 2917 0 0 156 3073 +0.0%
dcd_samd.c 1032 0 0 266 1298 +0.0%
dcd_samg.c 1319 0 0 72 1391 +0.0%
dcd_stm32_fsdev.c 2557 0 0 291 2848 +0.0%
dfu_device.c 744 28 712 183 926 +0.0%
dfu_rt_device.c 156 0 134 0 156 +0.0%
dwc2_common.c 582 29 0 0 597 +0.0%
ecm_rndis_device.c 1037 0 1 2272 3310 +0.0%
ehci.c 2761 0 0 5970 7537 +0.0%
fsdev_common.c 180 0 0 0 180 +0.0%
hcd_ch32_usbfs.c 2484 0 0 498 2982 +0.0%
hcd_ci_hs.c 190 0 0 0 190 +0.0%
hcd_dwc2.c 4893 30 0 512 5434 +0.0%
hcd_khci.c 2442 0 0 449 2891 +0.0%
hcd_musb.c 3073 0 0 157 3230 +0.0%
hcd_pio_usb.c 262 0 240 0 502 +0.0%
hcd_rp2040.c 976 73 416 384 1849 +0.0%
hcd_rusb2.c 2923 0 0 245 3168 +0.0%
hcd_samd.c 2220 0 0 324 2544 +0.0%
hcd_stm32_fsdev.c 3282 0 1 420 3703 +0.0%
hid_device.c 1118 44 997 115 1233 +0.0%
hid_host.c 1206 0 0 1250 2456 +0.0%
midi_device.c 1127 0 991 589 1714 +0.0%
midi_host.c 1353 7 7 3740 5097 +0.0%
msc_device.c 2503 108 2274 538 3041 +0.0%
msc_host.c 1589 0 0 394 1984 +0.0%
mtp_device.c 1689 22 1449 579 2275 +0.0%
ncm_device.c 1514 28 1408 5830 7358 +0.0%
ohci.c 1942 0 0 2414 4356 +0.0%
rp2040_usb.c 172 75 718 4 969 +0.0%
rusb2_common.c 160 0 16 0 176 +0.0%
tusb_fifo.c 843 0 477 0 838 +0.0%
typec_stm32.c 820 8 2 12 842 +0.0%
usbc.c 420 2 20 166 608 +0.0%
usbd.c 3190 57 89 276 3531 +0.0%
usbd_control.c 523 0 474 78 600 +0.0%
usbtmc_device.c 2176 24 69 291 2500 +0.0%
vendor_device.c 624 0 530 464 1087 +0.0%
video_device.c 4391 5 1851 472 4855 +0.0%
TOTAL 107052 1087 16291 44523 153572 +0.0%

@ceedriic
Copy link
Copy Markdown
Contributor Author

Deleted in favour of #3490 (same final code)

@ceedriic ceedriic closed this Feb 11, 2026
@ceedriic ceedriic deleted the non-blocking-host branch February 23, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants