Skip to content

Conversation

@ceedriic
Copy link
Contributor

These new function variants allows the main application loop to limit how much time is spend on these functions, to ensure that other critical code can run fast enough.

Describe the PR
An application main loop (without os) that usually tinyusb host and device stack look like:

      while(1) { // the mainloop
        application_code();
        tud_task(); // tinyusb device task
        tuh_task(); // tinyusb host task
      }

Unfortunately, without patching, tud_task() and tuh_task() can take arbitrarily long, if the CPU is slow and USB traffic fast.
the other day I got tud_task() running many seconds mounting a FATFS partition using a msc device on slow full speed USB.
Infinite loop can also happen in weird situations (bugs or stuff like usbh.c line 626).

This PR solves all of these by allowing the caller of these function to limit the number of events processed at once, ensuring the other stack and application_code run quickly. The leftover events will be processed on the next call.

These new variants allows the main application loop to limit how much time is spend on these functions, to ensure that other critical code can run often enough.
@HiFiPhile
Copy link
Collaborator

HiFiPhile commented Dec 26, 2025

I think this is just a poor man's solution, the number of events per cycle is not directly proportional to how long tud_task will run.

For your MSC case the long latency is due to disk IO in blocked callback functions. The right way is using RTOS + non-blocking asynchronous IO like cdc_msc_freertos example.

Without RTOS you can also use PendSV interrupt (or other free interrupts):

  • Put tud_task()/tuh_task() in PendSV interrupt handler
  • tud_event_hook_cb() will be called when there is a new event need to be processed, inside the callback you need to trigger PendSV interrupt
  • In tud_msc_write10_cb()/tud_msc_read10_cb() use a queue to post IO request to your main loop, return TUD_MSC_RET_ASYNC immediately
  • In the main loop process the IO request and call tud_msc_async_io_done()

@ceedriic
Copy link
Contributor Author

I think this is just a poor man's solution, the number of events per cycle is not directly proportional to how long tud_task will run.

It might be a poor man's solution, but it still is a solution to a real problem.

For your MSC case the long latency is due to disk IO in blocked callback functions. The right way is using RTOS + non-blocking asynconous IO like cdc_msc_freertos example.

Well, in my case, disk IO was pretty fast because it is a Ramdisk (I might be a poor man, but I'm not stupid enough to block on callbacks :)

I agree with you that RTOS is better for these, but I'm writing monitoring code on the M4 side (I'm using a rtos on the M7).

In any case TinyUSB claims to be working without RTOS - which is great - so I think this patch adresses a real problem.

Use uint32_t for 'events' variable
@github-actions
Copy link

github-actions bot commented Dec 26, 2025

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

No entries.

Changes <1% in size

file .text .rodata .data .bss size % diff
usbd.c 3155 ➙ 3187 (+32) 57 86 ➙ 87 (+1) 276 3492 ➙ 3524 (+32) +0.9%
usbh.c 4367 ➙ 4397 (+30) 60 60 954 5405 ➙ 5435 (+30) +0.6%
TOTAL 7522 ➙ 7584 (+62) 117 146 ➙ 147 (+1) 1230 8897 ➙ 8959 (+62) +0.7%
No changes
file .text .rodata .data .bss size % diff
audio_device.c 2860 0 1250 1688 4543 +0.0%
cdc_device.c 1333 16 20 692 2024 +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 4188 25 0 263 4474 +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 2228 0 0 160 2388 +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 736 20 580 1029 2365 +0.0%
dcd_rusb2.c 3076 0 0 156 3232 +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 2494 0 0 291 2785 +0.0%
dfu_device.c 744 28 712 184 929 +0.0%
dfu_rt_device.c 157 0 134 0 156 +0.0%
dwc2_common.c 781 29 0 0 797 +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 622 0 0 0 622 +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 4880 30 0 512 5422 +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 936 73 420 512 1941 +0.0%
hcd_rusb2.c 2965 0 0 246 3211 +0.0%
hcd_samd.c 2220 0 0 324 2544 +0.0%
hcd_stm32_fsdev.c 3258 0 1 420 3679 +0.0%
hid_device.c 1120 44 997 115 1235 +0.0%
hid_host.c 1206 0 0 1250 2456 +0.0%
hub.c 1235 8 8 29 1268 +0.0%
midi_device.c 1130 0 991 615 1743 +0.0%
midi_host.c 1353 7 7 3740 5097 +0.0%
msc_device.c 2506 108 2274 538 3044 +0.0%
msc_host.c 1589 0 0 394 1984 +0.0%
mtp_device.c 1622 22 1385 578 2208 +0.0%
ncm_device.c 1517 28 1408 5824 7354 +0.0%
ohci.c 1942 0 0 2414 4356 +0.0%
rp2040_usb.c 124 70 574 4 772 +0.0%
rusb2_common.c 0 0 16 0 16 +0.0%
tusb.c 420 0 361 3 421 +0.0%
tusb_fifo.c 890 0 525 0 884 +0.0%
typec_stm32.c 820 8 2 12 842 +0.0%
usbc.c 408 2 16 166 592 +0.0%
usbd_control.c 524 0 474 78 601 +0.0%
usbtmc_device.c 2178 24 70 294 2504 +0.0%
vendor_device.c 636 0 538 476 1111 +0.0%
video_device.c 4402 6 1851 471 4865 +0.0%
TOTAL 105779 1035 16399 44862 152402 +0.0%

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.

2 participants