From c954c8c4c70df616e429d8e7ae1c382d3043acea Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 May 2026 18:20:22 +0700 Subject: remove sync() in tinyusb callback since it cause issue with RTOS when usbh task is blocking --- examples/dual/dynamic_switch/src/main.c | 166 ++++++++++++++++---------------- examples/host/bare_api/src/main.c | 43 ++++++--- examples/host/cdc_msc_hid/src/cdc_app.c | 6 -- examples/host/device_info/src/main.c | 71 +++++++++++--- 4 files changed, 173 insertions(+), 113 deletions(-) (limited to 'examples') diff --git a/examples/dual/dynamic_switch/src/main.c b/examples/dual/dynamic_switch/src/main.c index c9ad2a835..e8e0deb53 100644 --- a/examples/dual/dynamic_switch/src/main.c +++ b/examples/dual/dynamic_switch/src/main.c @@ -79,6 +79,9 @@ StaticTask_t usb_taskdef; StackType_t cdc_stack[CDC_STACK_SIZE]; StaticTask_t cdc_taskdef; + +StackType_t devinfo_stack[USBH_STACK_SIZE]; +StaticTask_t devinfo_taskdef; #endif #endif @@ -87,14 +90,13 @@ static tusb_role_t current_role = TUSB_ROLE_DEVICE; #if CFG_TUSB_OS == OPT_OS_FREERTOS static void usb_task(void *param); -void led_blinking_task(void *param); -void cdc_task(void *params); -#else -void led_blinking_task(void); -void cdc_task(void); #endif +void led_blinking_task(void *param); +void cdc_task(void *param); +void print_devinfo_task(void *param); + void usb_mode_switch(void); -static void print_device_info(uint8_t daddr); +static void print_one_device(uint8_t daddr); static void print_utf16(uint16_t* temp_buf, size_t buf_len); // Declare buffer for USB transfer @@ -124,11 +126,13 @@ int main(void) { xTaskCreateStatic(usb_task, "usb", USBD_STACK_SIZE > USBH_STACK_SIZE ? USBD_STACK_SIZE : USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_stack, &usb_taskdef); xTaskCreateStatic(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, cdc_stack, &cdc_taskdef); + xTaskCreateStatic(print_devinfo_task, "devinfo", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, devinfo_stack, &devinfo_taskdef); #else xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); xTaskCreate(usb_task, "usb", USBD_STACK_SIZE > USBH_STACK_SIZE ? USBD_STACK_SIZE : USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); xTaskCreate(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL); + xTaskCreate(print_devinfo_task, "devinfo", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL); #endif #ifndef ESP_PLATFORM @@ -162,12 +166,13 @@ int main(void) { // Process USB tasks based on current mode if (current_role == TUSB_ROLE_DEVICE) { tud_task(); - cdc_task(); + cdc_task(NULL); } else { tuh_task(); + print_devinfo_task(NULL); } - led_blinking_task(); + led_blinking_task(NULL); } #endif } @@ -227,21 +232,28 @@ static void usb_task(void *param) { void usb_mode_switch(void) { printf("\r\n--- Switching USB mode ---\r\n"); - // Deinitialize current mode - if (current_role == TUSB_ROLE_DEVICE) { + // Snapshot then clear current_role BEFORE tusb_deinit() so concurrent + // tasks (cdc_task / print_devinfo_task on RTOS) see the role-change + // boundary and exit cleanly instead of calling host/device APIs against + // a deinitialised stack. + const tusb_role_t prev_role = current_role; + current_role = TUSB_ROLE_INVALID; + + if (prev_role == TUSB_ROLE_DEVICE) { printf("Stopping DEVICE mode...\r\n"); - tusb_deinit(BOARD_RHPORT); } else { printf("Stopping HOST mode...\r\n"); - tusb_deinit(BOARD_RHPORT); } + tusb_deinit(BOARD_RHPORT); #if CFG_TUSB_OS == OPT_OS_FREERTOS vTaskDelay(pdMS_TO_TICKS(100)); // Small delay for clean transition #else - tusb_time_delay_ms_api(100); // Small delay for clean transition -#endif // Switch to the other mode - if (current_role == TUSB_ROLE_DEVICE) { + tusb_time_delay_ms_api(100); +#endif + + // Switch to the other mode + if (prev_role == TUSB_ROLE_DEVICE) { printf("Starting HOST mode...\r\n"); tusb_rhport_init_t host_init = { .role = TUSB_ROLE_HOST, @@ -267,61 +279,30 @@ void usb_mode_switch(void) { // Device Mode: CDC Task //--------------------------------------------------------------------+ -#if CFG_TUSB_OS == OPT_OS_FREERTOS -void cdc_task(void *params) { - (void) params; - - // RTOS forever loop +void cdc_task(void *param) { + (void) param; while (1) { - // Only process CDC when in device mode + // Only touch device-CDC APIs while we're in device mode. After + // usb_mode_switch() sets current_role to INVALID and tusb_deinit() runs, + // calling tud_cdc_write_flush() here would hit a deinit'd device stack. if (current_role == TUSB_ROLE_DEVICE) { - // Connected and there are data available - while (tud_cdc_available()) { + if (tud_cdc_available()) { uint8_t buf[64]; - - // Read data - uint32_t count = tud_cdc_read(buf, sizeof(buf)); - - // Echo back - tud_cdc_write(buf, count); - - // Add newline for carriage return - for (uint32_t i = 0; i < count; i++) { - if (buf[i] == '\r') { - tud_cdc_write_char('\n'); - break; - } + const uint32_t count = tud_cdc_read(buf, sizeof(buf)); + if (count) { + tud_cdc_write(buf, count); } } - tud_cdc_write_flush(); } +#if CFG_TUSB_OS == OPT_OS_FREERTOS vTaskDelay(pdMS_TO_TICKS(10)); - } -} #else -void cdc_task(void) { - // Connected and there are data available - if (tud_cdc_available()) { - uint8_t buf[64]; - - // Read data - uint32_t count = tud_cdc_read(buf, sizeof(buf)); - - // Echo back - for (uint32_t i = 0; i < count; i++) { - tud_cdc_write_char(buf[i]); - - if (buf[i] == '\r') { - tud_cdc_write_char('\n'); - } - } - - tud_cdc_write_flush(); + return; // main loop will call us again +#endif } } -#endif //--------------------------------------------------------------------+ // Device Callbacks @@ -356,24 +337,57 @@ void tud_resume_cb(void) { // Host Callbacks //--------------------------------------------------------------------+ -// Invoked when device is mounted (configured) +// One flag per possible device address — set by tuh_mount_cb (host task) and +// cleared by print_devinfo_task once the device's descriptors are printed. +static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1]; + +// Invoked when device is mounted (configured). Runs in the host task — keep +// minimal; descriptor fetching happens in print_devinfo_task (different +// context so sync helpers are safe). void tuh_mount_cb(uint8_t daddr) { printf("[HOST] Device attached, address = %d\r\n", daddr); blink_interval_ms = BLINK_MOUNTED; - print_device_info(daddr); + if (daddr < TU_ARRAY_SIZE(need_devinfo)) { + need_devinfo[daddr] = true; + } } // Invoked when device is unmounted (unplugged) void tuh_umount_cb(uint8_t daddr) { printf("[HOST] Device removed, address = %d\r\n", daddr); blink_interval_ms = BLINK_NOT_MOUNTED; + if (daddr < TU_ARRAY_SIZE(need_devinfo)) { + need_devinfo[daddr] = false; + } } //--------------------------------------------------------------------+ -// Host Device Info +// Host Device Info — serialises descriptor fetching across all mounted +// devices using sync helpers. Safe to call from main loop (OS_NONE) or a +// dedicated task (FreeRTOS) — but NOT from a host-stack callback. //--------------------------------------------------------------------+ -static void print_device_info(uint8_t daddr) { +void print_devinfo_task(void *param) { + (void) param; + while (1) { + if (current_role == TUSB_ROLE_HOST) { + for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) { + if (need_devinfo[daddr]) { + need_devinfo[daddr] = false; + print_one_device(daddr); + } + } + } + +#if CFG_TUSB_OS == OPT_OS_FREERTOS + vTaskDelay(pdMS_TO_TICKS(10)); +#else + return; +#endif + } +} + +static void print_one_device(uint8_t daddr) { // Get Device Descriptor uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc.device, 18); if (XFER_RESULT_SUCCESS != xfer_result) { @@ -467,30 +481,20 @@ static void print_utf16(uint16_t* temp_buf, size_t buf_len) { // Blinking Task //--------------------------------------------------------------------+ -#if CFG_TUSB_OS == OPT_OS_FREERTOS void led_blinking_task(void *param) { (void) param; + static uint32_t start_ms = 0; static bool led_state = false; - - // RTOS forever loop while (1) { - board_led_write(led_state); - led_state = 1 - led_state; // toggle +#if CFG_TUSB_OS == OPT_OS_FREERTOS vTaskDelay(pdMS_TO_TICKS(blink_interval_ms)); - } -} #else -void led_blinking_task(void) { - static uint32_t start_ms = 0; - static bool led_state = false; - - // Blink every interval ms - if (tusb_time_millis_api() - start_ms < blink_interval_ms) { - return; // not enough time + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { + return; // not enough time + } +#endif + start_ms += blink_interval_ms; + board_led_write(led_state); + led_state = 1 - led_state; // toggle } - start_ms += blink_interval_ms; - - board_led_write(led_state); - led_state = 1 - led_state; // toggle } -#endif diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index 544f38102..679ce6f43 100644 --- a/examples/host/bare_api/src/main.c +++ b/examples/host/bare_api/src/main.c @@ -48,14 +48,19 @@ CFG_TUH_MEM_SECTION uint16_t temp_buf[128]; // temp buffer for string descriptor // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ void led_blinking_task(void); +void print_devinfo_task(void); static void print_utf16(uint16_t *temp_buf, size_t buf_len); -void print_device_descriptor(tuh_xfer_t* xfer); +static void print_one_device(uint8_t daddr); void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); uint8_t* get_hid_buf(uint8_t daddr); void free_hid_buf(uint8_t daddr); +// One flag per possible device address — set in tuh_mount_cb (host task) and +// cleared by print_devinfo_task (main loop) once the descriptors are printed. +static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1]; + /*------------- MAIN -------------*/ int main(void) { board_init(); @@ -74,38 +79,53 @@ int main(void) { while (1) { // tinyusb host task tuh_task(); + print_devinfo_task(); led_blinking_task(); } } /*------------- TinyUSB Callbacks -------------*/ -// Invoked when device is mounted (configured) +// Invoked when device is mounted (configured). Runs in the host task — keep +// it minimal. The descriptor fetching/printing happens in print_devinfo_task() +// below where the sync helpers are safe (different context). void tuh_mount_cb(uint8_t daddr) { printf("Device attached, address = %d\r\n", daddr); - - // Get Device Descriptor - // TODO: invoking control transfer now has issue with mounting hub with multiple devices attached, fix later - tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0); + if (daddr < TU_ARRAY_SIZE(need_devinfo)) { + need_devinfo[daddr] = true; + } } /// Invoked when device is unmounted (bus reset/unplugged) void tuh_umount_cb(uint8_t daddr) { printf("Device removed, address = %d\r\n", daddr); + if (daddr < TU_ARRAY_SIZE(need_devinfo)) { + need_devinfo[daddr] = false; + } free_hid_buf(daddr); } //--------------------------------------------------------------------+ -// Device Descriptor +// Print device info task — serialises descriptor fetching across all +// mounted devices via sync helpers. Sync calls are safe here because this +// runs in the main loop (outside any host-stack callback context). //--------------------------------------------------------------------+ -void print_device_descriptor(tuh_xfer_t *xfer) { - if (XFER_RESULT_SUCCESS != xfer->result) { +void print_devinfo_task(void) { + for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) { + if (need_devinfo[daddr]) { + need_devinfo[daddr] = false; + print_one_device(daddr); + } + } +} + +static void print_one_device(uint8_t daddr) { + // Get Device Descriptor + if (XFER_RESULT_SUCCESS != tuh_descriptor_get_device_sync(daddr, &desc_device, 18)) { printf("Failed to get device descriptor\r\n"); return; } - uint8_t const daddr = xfer->daddr; - printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct); printf("Device Descriptor:\r\n"); printf(" bLength %u\r\n" , desc_device.bLength); @@ -119,7 +139,6 @@ void print_device_descriptor(tuh_xfer_t *xfer) { printf(" idProduct 0x%04x\r\n" , desc_device.idProduct); printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice); - // Get String descriptor using Sync API printf(" iManufacturer %u ", desc_device.iManufacturer); if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf))) { print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf)); diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c index 20033981e..e6c190715 100644 --- a/examples/host/cdc_msc_hid/src/cdc_app.c +++ b/examples/host/cdc_msc_hid/src/cdc_app.c @@ -95,7 +95,6 @@ void tuh_cdc_mount_cb(uint8_t idx) { printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.desc.bInterfaceNumber); -#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM // If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack // while eneumerating new cdc device cdc_line_coding_t line_coding = {0}; @@ -103,11 +102,6 @@ void tuh_cdc_mount_cb(uint8_t idx) { printf(" Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits); printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits); } -#else - // Set Line Coding upon mounted - cdc_line_coding_t new_line_coding = { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }; - tuh_cdc_set_line_coding(idx, &new_line_coding, NULL, 0); -#endif } // Invoked when a device with CDC interface is unmounted diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c index b0e38dd6b..f32ed1a3e 100644 --- a/examples/host/device_info/src/main.c +++ b/examples/host/device_info/src/main.c @@ -72,8 +72,14 @@ CFG_TUH_MEM_SECTION struct { } desc; void led_blinking_task(void* param); +void print_devinfo_task(void* param); static void print_utf16(uint16_t* temp_buf, size_t buf_len); +// One flag per possible device address — set by tuh_mount_cb (host task) and +// cleared by print_devinfo_task (separate task / main loop) once the device's +// descriptor info has been printed. +static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1]; + #if CFG_TUSB_OS == OPT_OS_FREERTOS void init_freertos_task(void); #endif @@ -103,6 +109,7 @@ int main(void) { init_tinyusb(); while (1) { tuh_task(); // tinyusb host task + print_devinfo_task(NULL); led_blinking_task(NULL); } #endif @@ -110,10 +117,34 @@ int main(void) { /*------------- TinyUSB Callbacks -------------*/ -// Invoked when device is mounted (configured) +// Invoked when device is mounted (configured). Runs in the host task — keep +// it minimal. The actual descriptor fetching/printing happens in +// print_devinfo_task() below, which runs in a different context (main loop +// on OS_NONE / dedicated task on RTOS) where the sync helpers are safe. void tuh_mount_cb(uint8_t daddr) { blink_interval_ms = BLINK_MOUNTED; + if (daddr < TU_ARRAY_SIZE(need_devinfo)) { + need_devinfo[daddr] = true; + } +} +// Invoked when device is unmounted (bus reset/unplugged) +void tuh_umount_cb(uint8_t daddr) { + blink_interval_ms = BLINK_NOT_MOUNTED; + if (daddr < TU_ARRAY_SIZE(need_devinfo)) { + need_devinfo[daddr] = false; + } + printf("Device removed, address = %d\r\n", daddr); +} + +//--------------------------------------------------------------------+ +// Print device info task — serialises descriptor fetching across all +// mounted devices using the sync helpers. Sync calls are safe here because +// this task runs outside the host-task callback context (main loop on +// OS_NONE / dedicated FreeRTOS task on RTOS). +//--------------------------------------------------------------------+ + +static void print_one_device(uint8_t daddr) { // Get Device Descriptor uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc.device, 18); if (XFER_RESULT_SUCCESS != xfer_result) { @@ -129,9 +160,8 @@ void tuh_mount_cb(uint8_t daddr) { } if (XFER_RESULT_SUCCESS != xfer_result) { uint16_t* serial = (uint16_t*)(uintptr_t) desc.serial; - serial[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * 1 + 2)); - serial[1] = '0'; // simply 0 + serial[1] = '0'; serial[2] = 0; } print_utf16((uint16_t*)(uintptr_t) desc.serial, sizeof(desc.serial)/2); @@ -149,12 +179,9 @@ void tuh_mount_cb(uint8_t daddr) { printf(" idProduct 0x%04x\r\n", desc.device.idProduct); printf(" bcdDevice %04x\r\n", desc.device.bcdDevice); - // Get String descriptor using Sync API - printf(" iManufacturer %u ", desc.device.iManufacturer); if (desc.device.iManufacturer != 0) { - xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf)); - if (XFER_RESULT_SUCCESS == xfer_result) { + if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) { print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2); } } @@ -162,22 +189,33 @@ void tuh_mount_cb(uint8_t daddr) { printf(" iProduct %u ", desc.device.iProduct); if (desc.device.iProduct != 0) { - xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf)); - if (XFER_RESULT_SUCCESS == xfer_result) { + if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) { print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2); } } printf("\r\n"); printf(" iSerialNumber %u ", desc.device.iSerialNumber); - printf("%s\r\n", (char*)desc.serial); // serial is already to UTF-8 + printf("%s\r\n", (char*)desc.serial); // serial is already UTF-8 printf(" bNumConfigurations %u\r\n", desc.device.bNumConfigurations); } -// Invoked when device is unmounted (bus reset/unplugged) -void tuh_umount_cb(uint8_t daddr) { - blink_interval_ms = BLINK_NOT_MOUNTED; - printf("Device removed, address = %d\r\n", daddr); +void print_devinfo_task(void* param) { + (void) param; + +#if CFG_TUSB_OS == OPT_OS_FREERTOS + while (1) { +#endif + for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) { + if (need_devinfo[daddr]) { + need_devinfo[daddr] = false; + print_one_device(daddr); + } + } +#if CFG_TUSB_OS == OPT_OS_FREERTOS + vTaskDelay(pdMS_TO_TICKS(10)); + } +#endif } //--------------------------------------------------------------------+ @@ -278,6 +316,9 @@ StaticTask_t blinky_taskdef; StackType_t usb_stack[USB_STACK_SIZE]; StaticTask_t usb_taskdef; + +StackType_t devinfo_stack[USB_STACK_SIZE]; +StaticTask_t devinfo_taskdef; #endif #ifdef ESP_PLATFORM @@ -299,9 +340,11 @@ void init_freertos_task(void) { #if configSUPPORT_STATIC_ALLOCATION xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); xTaskCreateStatic(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_stack, &usb_taskdef); + xTaskCreateStatic(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-2, devinfo_stack, &devinfo_taskdef); #else xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); xTaskCreate(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); + xTaskCreate(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL); #endif // only start scheduler for non-espressif mcu -- cgit v1.3.1 From c5676382c7d5cad7b6cfe5dc185d9891b89241f2 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 May 2026 18:23:46 +0700 Subject: abstract OS logic with `CFG_TUSB_OS_HAS_SCHEDULER` to simplify conditional checks --- examples/device/msc_dual_lun/src/main.c | 2 +- examples/dual/host_info_to_device_cdc/src/main.c | 6 +++--- src/tusb_option.h | 12 ++++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/device/msc_dual_lun/src/main.c b/examples/device/msc_dual_lun/src/main.c index a4ade6f9b..1d764f12c 100644 --- a/examples/device/msc_dual_lun/src/main.c +++ b/examples/device/msc_dual_lun/src/main.c @@ -71,7 +71,7 @@ static void usb_device_init(void) { board_init_after_tusb(); } -#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO +#if CFG_TUSB_OS_HAS_SCHEDULER static void usb_device_task(RTOS_PARAM param) { (void) param; usb_device_init(); diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c index cf3430464..5186f91dc 100644 --- a/examples/dual/host_info_to_device_cdc/src/main.c +++ b/examples/dual/host_info_to_device_cdc/src/main.c @@ -130,7 +130,7 @@ static void main_task(void* param) { led_blinking_task(); // preempted RTOS run device/host stack in its own task -#if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO +#if CFG_TUSB_OS_HAS_SCHEDULER == 0 tud_task(); // tinyusb device task tuh_task(); // tinyusb host task #endif @@ -140,7 +140,7 @@ static void main_task(void* param) { int main(void) { board_init(); -#if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO +#if CFG_TUSB_OS_HAS_SCHEDULER == 0 printf("TinyUSB Host Information -> Device CDC Example\r\n"); usb_device_init(); @@ -156,7 +156,7 @@ int main(void) { return 0; } -#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO +#if CFG_TUSB_OS_HAS_SCHEDULER // USB Device Driver task for RTOS static void usb_device_task(void *param) { (void) param; diff --git a/src/tusb_option.h b/src/tusb_option.h index 74eb8cc06..dcf0646cf 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -535,6 +535,18 @@ #define CFG_TUSB_OS OPT_OS_NONE #endif +// 1 when CFG_TUSB_OS provides a preemptive scheduler with distinct tasks +// (FreeRTOS, Zephyr, ThreadX, etc.); 0 when the application is single-context +// (bare-metal OS_NONE or Pico SDK). Sync host control xfers from the host +// task are forbidden when this is 1. +#ifndef CFG_TUSB_OS_HAS_SCHEDULER + #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO + #define CFG_TUSB_OS_HAS_SCHEDULER 0 + #else + #define CFG_TUSB_OS_HAS_SCHEDULER 1 + #endif +#endif + #ifndef CFG_TUSB_OS_INC_PATH #ifndef CFG_TUSB_OS_INC_PATH_DEFAULT #define CFG_TUSB_OS_INC_PATH_DEFAULT -- cgit v1.3.1 From 24700ea8ef09d1a990fd3aff3345064c2a54088b Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 29 May 2026 23:59:15 +0700 Subject: midi2_device: gate -Wno-type-limits to GCC/Clang for IAR build iccarm rejects -Wno-type-limits, breaking the hil-hfp-iar CI matrix (stm32l412nucleo, stm32f746disco, lpcxpresso43s67). Apply the same CMAKE_C_COMPILER_ID guard used in hw/bsp/family_support.cmake so IAR builds skip the flag without losing the GCC warning suppression. Co-Authored-By: Claude Opus 4.7 --- examples/device/midi2_device/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/device/midi2_device/CMakeLists.txt b/examples/device/midi2_device/CMakeLists.txt index 295af6550..f1fe09db2 100644 --- a/examples/device/midi2_device/CMakeLists.txt +++ b/examples/device/midi2_device/CMakeLists.txt @@ -30,4 +30,6 @@ target_include_directories(${PROJECT_NAME} PUBLIC family_configure_device_example(${PROJECT_NAME} noos) # Suppress pre-existing warning in usbd.c (uint8_t comparison always true/false) -target_compile_options(${PROJECT_NAME} PRIVATE -Wno-type-limits) +if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + target_compile_options(${PROJECT_NAME} PRIVATE -Wno-type-limits) +endif() -- cgit v1.3.1 From 28beb0fe4db608c1a9001452d099cb5ea095377e Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 30 May 2026 00:11:19 +0700 Subject: ci: fix IAR Pe111 and stm32h7s3 flash overflow - examples/device/midi2_device/src/main.c: drop the unreachable `return 0;` after the `while(1)` superloop. IAR with --warnings_are_errors rejects Pe111 (statement is unreachable); C99 lets `int main` fall off the end, matching midi_test. - examples/host/msc_file_explorer_freertos/skip.txt: skip stm32h7s3nucleo. The board has only 64 KB on-chip FLASH and the FreeRTOS + FatFS host MSC explorer now overflows by ~248 bytes after the async control queue refactor. Co-Authored-By: Claude Opus 4.7 --- examples/device/midi2_device/src/main.c | 2 -- examples/host/msc_file_explorer_freertos/skip.txt | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/device/midi2_device/src/main.c b/examples/device/midi2_device/src/main.c index 62741ac41..ce052a20b 100644 --- a/examples/device/midi2_device/src/main.c +++ b/examples/device/midi2_device/src/main.c @@ -715,6 +715,4 @@ int main(void) { } } } - - return 0; } diff --git a/examples/host/msc_file_explorer_freertos/skip.txt b/examples/host/msc_file_explorer_freertos/skip.txt index f0be07d25..a8c9bea2a 100644 --- a/examples/host/msc_file_explorer_freertos/skip.txt +++ b/examples/host/msc_file_explorer_freertos/skip.txt @@ -1,3 +1,4 @@ mcu:CH32F20X board:lpcxpresso54114 mcu:FT90X +board:stm32h7s3nucleo -- cgit v1.3.1 From 0761df7420df3e62041f17e0c3e0f49d98b0667c Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 30 May 2026 00:42:14 +0700 Subject: midi2_host: drop unreachable return 0 for IAR Pe111 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same fix as midi2_device — IAR rejects the unreachable statement after the while(1) superloop. Let int main fall off the end. Co-Authored-By: Claude Opus 4.7 --- examples/host/midi2_host/src/main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'examples') diff --git a/examples/host/midi2_host/src/main.c b/examples/host/midi2_host/src/main.c index 63b08318c..3d5938a2d 100644 --- a/examples/host/midi2_host/src/main.c +++ b/examples/host/midi2_host/src/main.c @@ -160,6 +160,4 @@ int main(void) { while (1) { tuh_task(); } - - return 0; } -- cgit v1.3.1 From fc933e341df29e2ab6fa62508d45a92c65a621fb Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 30 May 2026 00:46:16 +0700 Subject: midi2_host: silence IAR Pe550 for midi2_idx The variable is set in mount/umount callbacks but not read elsewhere in the example (rx_cb already receives idx as a parameter). IAR treats Pe550 as an error under --warnings_are_errors. Tag it TU_ATTR_UNUSED so the example still shows the pattern of tracking the device index without erroring on unused-set. Co-Authored-By: Claude Opus 4.7 --- examples/host/midi2_host/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/host/midi2_host/src/main.c b/examples/host/midi2_host/src/main.c index 3d5938a2d..d81e77a33 100644 --- a/examples/host/midi2_host/src/main.c +++ b/examples/host/midi2_host/src/main.c @@ -35,7 +35,7 @@ // State //--------------------------------------------------------------------+ -static uint8_t midi2_idx = 0xFF; +TU_ATTR_UNUSED static uint8_t midi2_idx = 0xFF; //--------------------------------------------------------------------+ // UMP printer - shows MT and word(s) in hex; decodes Channel Voice -- cgit v1.3.1 From a64eb336c4a8d3f9c160aa30b81033e3ad7227d8 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 30 May 2026 00:52:22 +0700 Subject: dynamic_switch: hoist while(1) out for OS_NONE Sonar flagged the loop body as executing only once on OS_NONE because the OS_NONE branch returns inside the first iteration (main() drives the task again). Make the while(1) conditional on RTOS so the OS_NONE build is a straight-line function with no misleading loop. Co-Authored-By: Claude Opus 4.7 --- examples/dual/dynamic_switch/src/main.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/dual/dynamic_switch/src/main.c b/examples/dual/dynamic_switch/src/main.c index e8e0deb53..ac5126e56 100644 --- a/examples/dual/dynamic_switch/src/main.c +++ b/examples/dual/dynamic_switch/src/main.c @@ -281,7 +281,9 @@ void usb_mode_switch(void) { void cdc_task(void *param) { (void) param; +#if CFG_TUSB_OS == OPT_OS_FREERTOS while (1) { +#endif // Only touch device-CDC APIs while we're in device mode. After // usb_mode_switch() sets current_role to INVALID and tusb_deinit() runs, // calling tud_cdc_write_flush() here would hit a deinit'd device stack. @@ -295,13 +297,10 @@ void cdc_task(void *param) { } tud_cdc_write_flush(); } - #if CFG_TUSB_OS == OPT_OS_FREERTOS vTaskDelay(pdMS_TO_TICKS(10)); -#else - return; // main loop will call us again -#endif } +#endif } //--------------------------------------------------------------------+ @@ -369,7 +368,9 @@ void tuh_umount_cb(uint8_t daddr) { void print_devinfo_task(void *param) { (void) param; +#if CFG_TUSB_OS == OPT_OS_FREERTOS while (1) { +#endif if (current_role == TUSB_ROLE_HOST) { for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) { if (need_devinfo[daddr]) { @@ -378,13 +379,10 @@ void print_devinfo_task(void *param) { } } } - #if CFG_TUSB_OS == OPT_OS_FREERTOS vTaskDelay(pdMS_TO_TICKS(10)); -#else - return; -#endif } +#endif } static void print_one_device(uint8_t daddr) { -- cgit v1.3.1 From 7e0fcaa41ee9330274808d88e5211bdbe37511a4 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 1 Jun 2026 10:05:17 +0700 Subject: ultrareview nits: keep xfer_result table in sync, hoist blinky loop - src/tusb.c: extend tu_str_xfer_result[] with "ABORTED" and "INVALID" to match the new enum size. Not reachable today (no HCD posts those values through hcd_event_xfer_complete), but keeps the enum/table invariant intact so future HCDs that surface ABORTED don't index OOB. - examples/dual/dynamic_switch/src/main.c: apply the same while(1) hoist already done for cdc_task / print_devinfo_task to led_blinking_task. On OS_NONE the loop returned mid-iteration, which on first call could fire multiple back-to-back toggles while start_ms (initially 0) caught up to uptime. Co-Authored-By: Claude Opus 4.7 --- examples/dual/dynamic_switch/src/main.c | 17 ++++++++++------- src/tusb.c | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/dual/dynamic_switch/src/main.c b/examples/dual/dynamic_switch/src/main.c index ac5126e56..f67cd885c 100644 --- a/examples/dual/dynamic_switch/src/main.c +++ b/examples/dual/dynamic_switch/src/main.c @@ -483,16 +483,19 @@ void led_blinking_task(void *param) { (void) param; static uint32_t start_ms = 0; static bool led_state = false; - while (1) { #if CFG_TUSB_OS == OPT_OS_FREERTOS + while (1) { vTaskDelay(pdMS_TO_TICKS(blink_interval_ms)); -#else - if (tusb_time_millis_api() - start_ms < blink_interval_ms) { - return; // not enough time - } -#endif start_ms += blink_interval_ms; board_led_write(led_state); - led_state = 1 - led_state; // toggle + led_state = 1 - led_state; + } +#else + if (tusb_time_millis_api() - start_ms < blink_interval_ms) { + return; // not enough time } + start_ms += blink_interval_ms; + board_led_write(led_state); + led_state = 1 - led_state; +#endif } diff --git a/src/tusb.c b/src/tusb.c index 5d656fb8c..634cbc10b 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -497,7 +497,7 @@ char const* const tu_str_std_request[] = { }; char const* const tu_str_xfer_result[] = { - "OK", "FAILED", "STALLED", "TIMEOUT" + "OK", "FAILED", "STALLED", "TIMEOUT", "ABORTED", "INVALID" }; #endif -- cgit v1.3.1