From 3e5b79282a66a33a8cefc1f8584923819ff97eda Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:43:46 +0000 Subject: net: upgrade net_lwip_webserver to separate FS/HS descriptors and add bInterval to TUD_CDC_NCM_DESCRIPTOR Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/e212b526-e279-4a83-88bf-a742df293165 --- .../net_lwip_webserver/src/usb_descriptors.c | 128 ++++++++++++++++++--- hw/mcu/raspberry_pi/Pico-PIO-USB | 1 + hw/mcu/st/cmsis_device_f4 | 1 + hw/mcu/st/stm32f4xx_hal_driver | 1 + lib/CMSIS_5 | 1 + lib/FreeRTOS-Kernel | 1 + lib/lwip | 1 + lib/threadx | 1 + src/device/usbd.h | 8 +- tools/linkermap | 1 + tools/uf2 | 1 + 11 files changed, 124 insertions(+), 21 deletions(-) create mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB create mode 160000 hw/mcu/st/cmsis_device_f4 create mode 160000 hw/mcu/st/stm32f4xx_hal_driver create mode 160000 lib/CMSIS_5 create mode 160000 lib/FreeRTOS-Kernel create mode 160000 lib/lwip create mode 160000 lib/threadx create mode 160000 tools/linkermap create mode 160000 tools/uf2 diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c index c976cb62b..09a8a7548 100644 --- a/examples/device/net_lwip_webserver/src/usb_descriptors.c +++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c @@ -65,17 +65,19 @@ enum { CONFIG_ID_COUNT }; +#if CFG_TUD_NCM +#define USB_BCD 0x0201 +#else +#define USB_BCD 0x0200 +#endif + //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ static const tusb_desc_device_t desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, -#if CFG_TUD_NCM - .bcdUSB = 0x0201, -#else - .bcdUSB = 0x0200, -#endif + .bcdUSB = USB_BCD, // Use Interface Association Descriptor (IAD) device class .bDeviceClass = TUSB_CLASS_MISC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, @@ -136,57 +138,149 @@ const uint8_t *tud_descriptor_device_cb(void) { #if CFG_TUD_ECM_RNDIS -static uint8_t const rndis_configuration[] = { +// full speed configuration +static uint8_t const rndis_fs_configuration[] = { // Config number (index+1), interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100), // Interface number, string index, EP notification address and size, EP data address (out, in) and size. TUD_RNDIS_DESCRIPTOR( - ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE), + ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 64), }; -static const uint8_t ecm_configuration[] = { +static const uint8_t ecm_fs_configuration[] = { // Config number (index+1), interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100), // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. TUD_CDC_ECM_DESCRIPTOR( ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, - CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), + 64, CFG_TUD_NET_MTU), }; +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +// high speed configuration +static uint8_t const rndis_hs_configuration[] = { + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_RNDIS_DESCRIPTOR( + ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 512), +}; + +static const uint8_t ecm_hs_configuration[] = { + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_ECM_DESCRIPTOR( + ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, + 512, CFG_TUD_NET_MTU), +}; +#endif // highspeed + #else -static uint8_t const ncm_configuration[] = { +// full speed configuration +static uint8_t const ncm_fs_configuration[] = { // Config number (index+1), interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100), - // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval. TUD_CDC_NCM_DESCRIPTOR( ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, - CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), + 64, CFG_TUD_NET_MTU, 50), }; +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +// high speed configuration +// bInterval: FS=50 means 50ms; HS encodes as 2^(n-1) * 125us, so 9 = 2^8 * 125us = 32ms +static uint8_t const ncm_hs_configuration[] = { + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval. + TUD_CDC_NCM_DESCRIPTOR( + ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, + 512, CFG_TUD_NET_MTU, 9), +}; +#endif // highspeed + #endif // Configuration array: RNDIS and CDC-ECM // - Windows only works with RNDIS // - MacOS only works with CDC-ECM // - Linux will work on both -static const uint8_t *const configuration_arr[CONFIG_ID_COUNT] = { +static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = { +#if CFG_TUD_ECM_RNDIS + [CONFIG_ID_RNDIS] = rndis_fs_configuration, + [CONFIG_ID_ECM] = ecm_fs_configuration +#else + [CONFIG_ID_NCM] = ncm_fs_configuration +#endif +}; + +#if TUD_OPT_HIGH_SPEED +static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = { #if CFG_TUD_ECM_RNDIS - [CONFIG_ID_RNDIS] = rndis_configuration, - [CONFIG_ID_ECM] = ecm_configuration + [CONFIG_ID_RNDIS] = rndis_hs_configuration, + [CONFIG_ID_ECM] = ecm_hs_configuration #else - [CONFIG_ID_NCM] = ncm_configuration + [CONFIG_ID_NCM] = ncm_hs_configuration #endif }; +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +static tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = CONFIG_ID_COUNT, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + // if link speed is high return fullspeed config, and vice versa + const uint8_t *const *arr = (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_fs_arr : configuration_hs_arr; + return (index < CONFIG_ID_COUNT) ? arr[index] : NULL; +} + +#endif // highspeed + // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { - return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL; + if (index >= CONFIG_ID_COUNT) return NULL; +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_hs_arr[index] : configuration_fs_arr[index]; +#else + return configuration_fs_arr[index]; +#endif } #if CFG_TUD_NCM diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB new file mode 160000 index 000000000..675543bcc --- /dev/null +++ b/hw/mcu/raspberry_pi/Pico-PIO-USB @@ -0,0 +1 @@ +Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 new file mode 160000 index 000000000..3c77349ce --- /dev/null +++ b/hw/mcu/st/cmsis_device_f4 @@ -0,0 +1 @@ +Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver new file mode 160000 index 000000000..b6f0ed382 --- /dev/null +++ b/hw/mcu/st/stm32f4xx_hal_driver @@ -0,0 +1 @@ +Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 new file mode 160000 index 000000000..2b7495b85 --- /dev/null +++ b/lib/CMSIS_5 @@ -0,0 +1 @@ +Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel new file mode 160000 index 000000000..cc0e0707c --- /dev/null +++ b/lib/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip new file mode 160000 index 000000000..159e31b68 --- /dev/null +++ b/lib/lwip @@ -0,0 +1 @@ +Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx new file mode 160000 index 000000000..4b6e8100d --- /dev/null +++ b/lib/threadx @@ -0,0 +1 @@ +Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/src/device/usbd.h b/src/device/usbd.h index d3a6dccbb..93fb588df 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -1026,9 +1026,9 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ // Length of template descriptor #define TUD_CDC_NCM_DESC_LEN (8+9+5+5+13+6+7+9+9+7+7) -// CDC-ECM Descriptor Template -// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. -#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \ +// CDC-NCM Descriptor Template +// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), size, max segment size, EP notification bInterval. +#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize, _ep_notif_interval) \ /* Interface Association */\ 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, 0,\ /* CDC Control Interface */\ @@ -1042,7 +1042,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ /* CDC-NCM Functional Descriptor */\ 6, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_NCM, U16_TO_U8S_LE(0x0100), 0, \ /* Endpoint Notification */\ - 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 50,\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), _ep_notif_interval,\ /* CDC Data Interface (default inactive) */\ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\ /* CDC Data Interface (alternative active) */\ diff --git a/tools/linkermap b/tools/linkermap new file mode 160000 index 000000000..8e1f440fa --- /dev/null +++ b/tools/linkermap @@ -0,0 +1 @@ +Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 new file mode 160000 index 000000000..c594542b2 --- /dev/null +++ b/tools/uf2 @@ -0,0 +1 @@ +Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From cec6ea2223e9b6c814c6e3b17d88e54324d4f957 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:48:45 +0000 Subject: remove accidentally committed submodule entries --- hw/mcu/raspberry_pi/Pico-PIO-USB | 1 - hw/mcu/st/cmsis_device_f4 | 1 - hw/mcu/st/stm32f4xx_hal_driver | 1 - lib/CMSIS_5 | 1 - lib/FreeRTOS-Kernel | 1 - lib/lwip | 1 - lib/threadx | 1 - tools/linkermap | 1 - tools/uf2 | 1 - 9 files changed, 9 deletions(-) delete mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB delete mode 160000 hw/mcu/st/cmsis_device_f4 delete mode 160000 hw/mcu/st/stm32f4xx_hal_driver delete mode 160000 lib/CMSIS_5 delete mode 160000 lib/FreeRTOS-Kernel delete mode 160000 lib/lwip delete mode 160000 lib/threadx delete mode 160000 tools/linkermap delete mode 160000 tools/uf2 diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB deleted file mode 160000 index 675543bcc..000000000 --- a/hw/mcu/raspberry_pi/Pico-PIO-USB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 deleted file mode 160000 index 3c77349ce..000000000 --- a/hw/mcu/st/cmsis_device_f4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver deleted file mode 160000 index b6f0ed382..000000000 --- a/hw/mcu/st/stm32f4xx_hal_driver +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 deleted file mode 160000 index 2b7495b85..000000000 --- a/lib/CMSIS_5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel deleted file mode 160000 index cc0e0707c..000000000 --- a/lib/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip deleted file mode 160000 index 159e31b68..000000000 --- a/lib/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx deleted file mode 160000 index 4b6e8100d..000000000 --- a/lib/threadx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap deleted file mode 160000 index 8e1f440fa..000000000 --- a/tools/linkermap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 deleted file mode 160000 index c594542b2..000000000 --- a/tools/uf2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From ebeb495bd0cc3deb1758c6fcf79739ae5eac543d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:54:27 +0000 Subject: net: remove CFG_TUD_NET_ENDPOINT_SIZE, manage ZLP based on real speed in drivers Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/5d355974-2223-4071-8d14-b6d4ac1fd030 --- hw/mcu/st/cmsis_device_f4 | 1 + hw/mcu/st/stm32f4xx_hal_driver | 1 + lib/CMSIS_5 | 1 + lib/FreeRTOS-Kernel | 1 + lib/lwip | 1 + lib/threadx | 1 + src/class/net/ecm_rndis_device.c | 4 ++-- src/class/net/ncm_device.c | 2 +- src/class/net/net_device.h | 3 --- tools/linkermap | 1 + tools/uf2 | 1 + 11 files changed, 11 insertions(+), 6 deletions(-) create mode 160000 hw/mcu/st/cmsis_device_f4 create mode 160000 hw/mcu/st/stm32f4xx_hal_driver create mode 160000 lib/CMSIS_5 create mode 160000 lib/FreeRTOS-Kernel create mode 160000 lib/lwip create mode 160000 lib/threadx create mode 160000 tools/linkermap create mode 160000 tools/uf2 diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 new file mode 160000 index 000000000..3c77349ce --- /dev/null +++ b/hw/mcu/st/cmsis_device_f4 @@ -0,0 +1 @@ +Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver new file mode 160000 index 000000000..b6f0ed382 --- /dev/null +++ b/hw/mcu/st/stm32f4xx_hal_driver @@ -0,0 +1 @@ +Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 new file mode 160000 index 000000000..2b7495b85 --- /dev/null +++ b/lib/CMSIS_5 @@ -0,0 +1 @@ +Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel new file mode 160000 index 000000000..cc0e0707c --- /dev/null +++ b/lib/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip new file mode 160000 index 000000000..159e31b68 --- /dev/null +++ b/lib/lwip @@ -0,0 +1 @@ +Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx new file mode 160000 index 000000000..4b6e8100d --- /dev/null +++ b/lib/threadx @@ -0,0 +1 @@ +Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c index eaa82c187..773c495ed 100644 --- a/src/class/net/ecm_rndis_device.c +++ b/src/class/net/ecm_rndis_device.c @@ -356,8 +356,8 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ /* data transmission finished */ if (ep_addr == _netd_itf.ep_in) { /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */ - - if (xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE))) { + uint16_t const ep_size = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64; + if (xferred_bytes && (0 == (xferred_bytes % ep_size))) { do_in_xfer(NULL, 0); /* a ZLP is needed */ } else { /* we're finally finished */ diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 405e4467b..4f75dc478 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -340,7 +340,7 @@ static xmit_ntb_t *xmit_get_next_ready_ntb(void) { static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) { TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes); - if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) { + if (xferred_bytes == 0 || xferred_bytes % (tud_speed_get() == TUSB_SPEED_HIGH ? 512 : 64) != 0) { return false; } diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h index 96c03fd61..849f8a2f9 100644 --- a/src/class/net/net_device.h +++ b/src/class/net/net_device.h @@ -35,9 +35,6 @@ #error "Cannot enable both ECM_RNDIS and NCM network drivers" #endif -/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */ -#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) - /* Maximum Transmission Unit (in bytes) of the network, including Ethernet header */ #ifndef CFG_TUD_NET_MTU #define CFG_TUD_NET_MTU 1514 diff --git a/tools/linkermap b/tools/linkermap new file mode 160000 index 000000000..8e1f440fa --- /dev/null +++ b/tools/linkermap @@ -0,0 +1 @@ +Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 new file mode 160000 index 000000000..c594542b2 --- /dev/null +++ b/tools/uf2 @@ -0,0 +1 @@ +Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From 0f5335b55a7f8f086138a956e46d59d9f8bf9221 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:57:07 +0000 Subject: remove accidentally committed submodule entries --- hw/mcu/st/cmsis_device_f4 | 1 - hw/mcu/st/stm32f4xx_hal_driver | 1 - lib/CMSIS_5 | 1 - lib/FreeRTOS-Kernel | 1 - lib/lwip | 1 - lib/threadx | 1 - tools/linkermap | 1 - tools/uf2 | 1 - 8 files changed, 8 deletions(-) delete mode 160000 hw/mcu/st/cmsis_device_f4 delete mode 160000 hw/mcu/st/stm32f4xx_hal_driver delete mode 160000 lib/CMSIS_5 delete mode 160000 lib/FreeRTOS-Kernel delete mode 160000 lib/lwip delete mode 160000 lib/threadx delete mode 160000 tools/linkermap delete mode 160000 tools/uf2 diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 deleted file mode 160000 index 3c77349ce..000000000 --- a/hw/mcu/st/cmsis_device_f4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver deleted file mode 160000 index b6f0ed382..000000000 --- a/hw/mcu/st/stm32f4xx_hal_driver +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 deleted file mode 160000 index 2b7495b85..000000000 --- a/lib/CMSIS_5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel deleted file mode 160000 index cc0e0707c..000000000 --- a/lib/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip deleted file mode 160000 index 159e31b68..000000000 --- a/lib/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx deleted file mode 160000 index 4b6e8100d..000000000 --- a/lib/threadx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap deleted file mode 160000 index 8e1f440fa..000000000 --- a/tools/linkermap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 deleted file mode 160000 index c594542b2..000000000 --- a/tools/uf2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From c843e5e2a76302eabc2228c42ffecd2a242443cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 19:59:29 +0000 Subject: net: cache ep_size in local variable in ncm_device.c xmit_insert_required_zlp Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/5d355974-2223-4071-8d14-b6d4ac1fd030 --- src/class/net/ncm_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 4f75dc478..cf32514ba 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -340,7 +340,8 @@ static xmit_ntb_t *xmit_get_next_ready_ntb(void) { static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) { TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes); - if (xferred_bytes == 0 || xferred_bytes % (tud_speed_get() == TUSB_SPEED_HIGH ? 512 : 64) != 0) { + uint16_t const ep_size = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64; + if (xferred_bytes == 0 || xferred_bytes % ep_size != 0) { return false; } -- cgit v1.3.1 From f494d3517fe21ecd7259cd8a90d2c6709edd0e90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:06:52 +0000 Subject: net: save actual ep_size from descriptor in netd_open instead of hardcoding 64/512 Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/4c20b579-db28-4643-b062-81881adcf6c1 --- src/class/net/ecm_rndis_device.c | 7 +++++-- src/class/net/ncm_device.c | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c index 773c495ed..d4c2ebf9a 100644 --- a/src/class/net/ecm_rndis_device.c +++ b/src/class/net/ecm_rndis_device.c @@ -51,6 +51,7 @@ typedef struct { uint8_t ep_notif; uint8_t ep_in; uint8_t ep_out; + uint16_t ep_size; // bulk endpoint max packet size (IN and OUT assumed equal) bool ecm_mode; @@ -176,6 +177,9 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1 // Pair of endpoints TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0); + // Save the actual bulk endpoint size (IN and OUT assumed equal) + _netd_itf.ep_size = tu_edpt_packet_size((tusb_desc_endpoint_t const *) p_desc); + if (_netd_itf.ecm_mode) { // ECM by default is in-active, save the endpoint attribute // to open later when received setInterface @@ -356,8 +360,7 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ /* data transmission finished */ if (ep_addr == _netd_itf.ep_in) { /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */ - uint16_t const ep_size = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64; - if (xferred_bytes && (0 == (xferred_bytes % ep_size))) { + if (xferred_bytes && (0 == (xferred_bytes % _netd_itf.ep_size))) { do_in_xfer(NULL, 0); /* a ZLP is needed */ } else { /* we're finally finished */ diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index cf32514ba..fe33d0247 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -83,6 +83,7 @@ typedef struct { uint8_t itf_num; // interface number uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3) uint8_t rhport; // storage of \a rhport because some callbacks are done without it + uint16_t ep_size; // bulk endpoint max packet size (IN and OUT assumed equal) // recv handling recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs @@ -340,7 +341,7 @@ static xmit_ntb_t *xmit_get_next_ready_ntb(void) { static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) { TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes); - uint16_t const ep_size = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64; + uint16_t const ep_size = ncm_interface.ep_size; if (xferred_bytes == 0 || xferred_bytes % ep_size != 0) { return false; } @@ -906,6 +907,7 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16 // a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0); TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in)); + ncm_interface.ep_size = tu_edpt_packet_size((tusb_desc_endpoint_t const *) p_desc); drv_len += 2 * sizeof(tusb_desc_endpoint_t); return drv_len; -- cgit v1.3.1 From f6fd60a6a3ff92ca3ee40cf74f8070e3b1c50812 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:25:07 +0000 Subject: examples/net_lwip_webserver: fix other_speed_config to use TUSB_DESC_OTHER_SPEED_CONFIG and fix SEED typo Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3d21b8d3-b2df-4603-a426-2bcfdb408f04 --- .../net_lwip_webserver/src/usb_descriptors.c | 31 +++++++++++++++++++--- hw/mcu/raspberry_pi/Pico-PIO-USB | 1 + hw/mcu/st/cmsis_device_f4 | 1 + hw/mcu/st/stm32f4xx_hal_driver | 1 + lib/CMSIS_5 | 1 + lib/FreeRTOS-Kernel | 1 + lib/lwip | 1 + lib/threadx | 1 + tools/linkermap | 1 + tools/uf2 | 1 + 10 files changed, 37 insertions(+), 3 deletions(-) create mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB create mode 160000 hw/mcu/st/cmsis_device_f4 create mode 160000 hw/mcu/st/stm32f4xx_hal_driver create mode 160000 lib/CMSIS_5 create mode 160000 lib/FreeRTOS-Kernel create mode 160000 lib/lwip create mode 160000 lib/threadx create mode 160000 tools/linkermap create mode 160000 tools/uf2 diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c index 09a8a7548..80594fdeb 100644 --- a/examples/device/net_lwip_webserver/src/usb_descriptors.c +++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c @@ -236,7 +236,25 @@ static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = { #endif }; -// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +// Size array for each configuration +static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = { +#if CFG_TUD_ECM_RNDIS + [CONFIG_ID_RNDIS] = MAIN_CONFIG_TOTAL_LEN, + [CONFIG_ID_ECM] = ALT_CONFIG_TOTAL_LEN +#else + [CONFIG_ID_NCM] = NCM_CONFIG_TOTAL_LEN +#endif +}; + +// Scratch buffer for other speed configuration (sized to hold the largest config) +#if CFG_TUD_ECM_RNDIS + #define MAX_CONFIG_TOTAL_LEN TU_MAX(MAIN_CONFIG_TOTAL_LEN, ALT_CONFIG_TOTAL_LEN) +#else + #define MAX_CONFIG_TOTAL_LEN NCM_CONFIG_TOTAL_LEN +#endif +static uint8_t desc_other_speed_config[MAX_CONFIG_TOTAL_LEN]; + +// device qualifier: device descriptor fields that differ at other speed static tusb_desc_device_qualifier_t const desc_device_qualifier = { .bLength = sizeof(tusb_desc_device_qualifier_t), .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, @@ -259,13 +277,20 @@ uint8_t const *tud_descriptor_device_qualifier_cb(void) { return (uint8_t const *) &desc_device_qualifier; } -// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Invoked when received GET OTHER SPEED CONFIGURATION DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + if (index >= CONFIG_ID_COUNT) return NULL; + // if link speed is high return fullspeed config, and vice versa const uint8_t *const *arr = (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_fs_arr : configuration_hs_arr; - return (index < CONFIG_ID_COUNT) ? arr[index] : NULL; + + // Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, arr[index], configuration_sz_arr[index]); + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; } #endif // highspeed diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB new file mode 160000 index 000000000..675543bcc --- /dev/null +++ b/hw/mcu/raspberry_pi/Pico-PIO-USB @@ -0,0 +1 @@ +Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 new file mode 160000 index 000000000..3c77349ce --- /dev/null +++ b/hw/mcu/st/cmsis_device_f4 @@ -0,0 +1 @@ +Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver new file mode 160000 index 000000000..b6f0ed382 --- /dev/null +++ b/hw/mcu/st/stm32f4xx_hal_driver @@ -0,0 +1 @@ +Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 new file mode 160000 index 000000000..2b7495b85 --- /dev/null +++ b/lib/CMSIS_5 @@ -0,0 +1 @@ +Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel new file mode 160000 index 000000000..cc0e0707c --- /dev/null +++ b/lib/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip new file mode 160000 index 000000000..159e31b68 --- /dev/null +++ b/lib/lwip @@ -0,0 +1 @@ +Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx new file mode 160000 index 000000000..4b6e8100d --- /dev/null +++ b/lib/threadx @@ -0,0 +1 @@ +Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap new file mode 160000 index 000000000..8e1f440fa --- /dev/null +++ b/tools/linkermap @@ -0,0 +1 @@ +Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 new file mode 160000 index 000000000..c594542b2 --- /dev/null +++ b/tools/uf2 @@ -0,0 +1 @@ +Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From 4c873da62d63790914a0107a0804c7ba2a560099 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:26:22 +0000 Subject: remove accidentally committed submodule entries --- hw/mcu/raspberry_pi/Pico-PIO-USB | 1 - hw/mcu/st/cmsis_device_f4 | 1 - hw/mcu/st/stm32f4xx_hal_driver | 1 - lib/CMSIS_5 | 1 - lib/FreeRTOS-Kernel | 1 - lib/lwip | 1 - lib/threadx | 1 - tools/linkermap | 1 - tools/uf2 | 1 - 9 files changed, 9 deletions(-) delete mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB delete mode 160000 hw/mcu/st/cmsis_device_f4 delete mode 160000 hw/mcu/st/stm32f4xx_hal_driver delete mode 160000 lib/CMSIS_5 delete mode 160000 lib/FreeRTOS-Kernel delete mode 160000 lib/lwip delete mode 160000 lib/threadx delete mode 160000 tools/linkermap delete mode 160000 tools/uf2 diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB deleted file mode 160000 index 675543bcc..000000000 --- a/hw/mcu/raspberry_pi/Pico-PIO-USB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 deleted file mode 160000 index 3c77349ce..000000000 --- a/hw/mcu/st/cmsis_device_f4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver deleted file mode 160000 index b6f0ed382..000000000 --- a/hw/mcu/st/stm32f4xx_hal_driver +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 deleted file mode 160000 index 2b7495b85..000000000 --- a/lib/CMSIS_5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel deleted file mode 160000 index cc0e0707c..000000000 --- a/lib/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip deleted file mode 160000 index 159e31b68..000000000 --- a/lib/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx deleted file mode 160000 index 4b6e8100d..000000000 --- a/lib/threadx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap deleted file mode 160000 index 8e1f440fa..000000000 --- a/tools/linkermap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 deleted file mode 160000 index c594542b2..000000000 --- a/tools/uf2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From 46a0361f641dfd118f8f5e69889621f2d174cd96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:26:30 +0000 Subject: placeholder Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3d21b8d3-b2df-4603-a426-2bcfdb408f04 --- hw/mcu/raspberry_pi/Pico-PIO-USB | 1 + hw/mcu/st/cmsis_device_f4 | 1 + hw/mcu/st/stm32f4xx_hal_driver | 1 + lib/CMSIS_5 | 1 + lib/FreeRTOS-Kernel | 1 + lib/lwip | 1 + lib/threadx | 1 + tools/linkermap | 1 + tools/uf2 | 1 + 9 files changed, 9 insertions(+) create mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB create mode 160000 hw/mcu/st/cmsis_device_f4 create mode 160000 hw/mcu/st/stm32f4xx_hal_driver create mode 160000 lib/CMSIS_5 create mode 160000 lib/FreeRTOS-Kernel create mode 160000 lib/lwip create mode 160000 lib/threadx create mode 160000 tools/linkermap create mode 160000 tools/uf2 diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB new file mode 160000 index 000000000..675543bcc --- /dev/null +++ b/hw/mcu/raspberry_pi/Pico-PIO-USB @@ -0,0 +1 @@ +Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 new file mode 160000 index 000000000..3c77349ce --- /dev/null +++ b/hw/mcu/st/cmsis_device_f4 @@ -0,0 +1 @@ +Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver new file mode 160000 index 000000000..b6f0ed382 --- /dev/null +++ b/hw/mcu/st/stm32f4xx_hal_driver @@ -0,0 +1 @@ +Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 new file mode 160000 index 000000000..2b7495b85 --- /dev/null +++ b/lib/CMSIS_5 @@ -0,0 +1 @@ +Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel new file mode 160000 index 000000000..cc0e0707c --- /dev/null +++ b/lib/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip new file mode 160000 index 000000000..159e31b68 --- /dev/null +++ b/lib/lwip @@ -0,0 +1 @@ +Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx new file mode 160000 index 000000000..4b6e8100d --- /dev/null +++ b/lib/threadx @@ -0,0 +1 @@ +Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap new file mode 160000 index 000000000..8e1f440fa --- /dev/null +++ b/tools/linkermap @@ -0,0 +1 @@ +Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 new file mode 160000 index 000000000..c594542b2 --- /dev/null +++ b/tools/uf2 @@ -0,0 +1 @@ +Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From b46147a497bf68d680e9a8a898eeca2d255ce53b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:27:58 +0000 Subject: remove accidentally committed submodule entries --- .gitignore | 10 ++++++++++ hw/mcu/raspberry_pi/Pico-PIO-USB | 1 - hw/mcu/st/cmsis_device_f4 | 1 - hw/mcu/st/stm32f4xx_hal_driver | 1 - lib/CMSIS_5 | 1 - lib/FreeRTOS-Kernel | 1 - lib/lwip | 1 - lib/threadx | 1 - tools/linkermap | 1 - tools/uf2 | 1 - 10 files changed, 10 insertions(+), 9 deletions(-) delete mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB delete mode 160000 hw/mcu/st/cmsis_device_f4 delete mode 160000 hw/mcu/st/stm32f4xx_hal_driver delete mode 160000 lib/CMSIS_5 delete mode 160000 lib/FreeRTOS-Kernel delete mode 160000 lib/lwip delete mode 160000 lib/threadx delete mode 160000 tools/linkermap delete mode 160000 tools/uf2 diff --git a/.gitignore b/.gitignore index 162f9a019..f15f54d0a 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,13 @@ Release BrowseInfo .cmake_build README_processed.rst +# Dependencies fetched by tools/get_deps.py (should not be committed) +hw/mcu/raspberry_pi/Pico-PIO-USB/ +hw/mcu/st/cmsis_device_f4/ +hw/mcu/st/stm32f4xx_hal_driver/ +lib/CMSIS_5/ +lib/FreeRTOS-Kernel/ +lib/lwip/ +lib/threadx/ +tools/linkermap/ +tools/uf2/ diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB deleted file mode 160000 index 675543bcc..000000000 --- a/hw/mcu/raspberry_pi/Pico-PIO-USB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 deleted file mode 160000 index 3c77349ce..000000000 --- a/hw/mcu/st/cmsis_device_f4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver deleted file mode 160000 index b6f0ed382..000000000 --- a/hw/mcu/st/stm32f4xx_hal_driver +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 deleted file mode 160000 index 2b7495b85..000000000 --- a/lib/CMSIS_5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel deleted file mode 160000 index cc0e0707c..000000000 --- a/lib/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cc0e0707c0c748713485b870bb980852b210877f diff --git a/lib/lwip b/lib/lwip deleted file mode 160000 index 159e31b68..000000000 --- a/lib/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx deleted file mode 160000 index 4b6e8100d..000000000 --- a/lib/threadx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap deleted file mode 160000 index 8e1f440fa..000000000 --- a/tools/linkermap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 deleted file mode 160000 index c594542b2..000000000 --- a/tools/uf2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From 9c49c0eb215057885ae0aec46172778b9cf9b9a5 Mon Sep 17 00:00:00 2001 From: Hakan Lindestaf Date: Wed, 22 Apr 2026 17:48:07 -0500 Subject: midi host: raise default RX FIFO above EP size, document drain requirement Follow-up to #3239. tuh_midi_stream_read terminates on cable-number transitions, leaving residue in the FIFO. With the default RX FIFO sized equal to one bulk packet, the next bulk IN transfer fails to queue and the driver silently stops receiving. Raising the default to 2x bulk gives single-call apps a full packet of headroom and documents the drain-loop expectation. Reproduced with Akai LPD8 mk1 (VID 09E8 PID 0075) on STM32H753 DWC2 host; fixed with this patch. See #3613 for full repro + captures. --- src/class/midi/midi_host.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h index b9ab0130d..4eefed4ba 100644 --- a/src/class/midi/midi_host.h +++ b/src/class/midi/midi_host.h @@ -38,11 +38,16 @@ extern "C" { // Class Driver Configuration //--------------------------------------------------------------------+ #ifndef CFG_TUH_MIDI_RX_BUFSIZE - #define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MAX + // Default sized to 2x the bulk endpoint to absorb residue left in the FIFO + // when tuh_midi_stream_read() stops early on a cable-number transition. + // Sizing this equal to the endpoint packet size (the historical default) + // can cause the next bulk IN transfer to fail to queue silently, wedging + // the stream. See the drain-loop note on tuh_midi_stream_read() below. + #define CFG_TUH_MIDI_RX_BUFSIZE (2 * TUH_EPSIZE_BULK_MAX) #endif #ifndef CFG_TUH_MIDI_TX_BUFSIZE - #define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MAX + #define CFG_TUH_MIDI_TX_BUFSIZE (2 * TUH_EPSIZE_BULK_MAX) #endif #ifndef CFG_TUH_MIDI_EP_BUFSIZE @@ -150,6 +155,13 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, const uint8_t *p_ // Note that this function ignores the CIN field of the MIDI packet // because a number of commercial devices out there do not encode // it properly. +// +// NOTE: this function terminates when it encounters an event whose cable +// number differs from the one being returned. Applications should invoke +// it in a loop until it returns 0 (or until tuh_midi_read_available() +// returns 0) to guarantee the stream FIFO is fully drained per callback. +// Leaving bytes in the FIFO across callbacks can prevent subsequent bulk +// IN transfers from landing. uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize); #endif -- cgit v1.3.1 From d24cf89e4896ee959c034818bd4ecb6210714042 Mon Sep 17 00:00:00 2001 From: Hakan Lindestaf Date: Wed, 22 Apr 2026 23:32:05 +0000 Subject: Revert MIDI TX buffer size definition --- src/class/midi/midi_host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h index 4eefed4ba..000d815c4 100644 --- a/src/class/midi/midi_host.h +++ b/src/class/midi/midi_host.h @@ -47,7 +47,7 @@ extern "C" { #endif #ifndef CFG_TUH_MIDI_TX_BUFSIZE - #define CFG_TUH_MIDI_TX_BUFSIZE (2 * TUH_EPSIZE_BULK_MAX) + #define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MAX #endif #ifndef CFG_TUH_MIDI_EP_BUFSIZE -- cgit v1.3.1 From 9d3ad336bfad062fa1e6f6d63feb97a9851cc9e1 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 29 Apr 2026 17:27:59 +0700 Subject: deprecated `usbd_control.c` and merge its functionality into `usbd.c` --- hw/bsp/rp2040/family.cmake | 1 - src/CMakeLists.txt | 1 - src/device/usbd.c | 173 ++++++++++++++++++++++++++++++++++-- src/device/usbd_control.c | 199 ++---------------------------------------- src/tinyusb.mk | 1 - test/fuzz/rules.mk | 1 - test/unit-test/CMakeLists.txt | 4 +- 7 files changed, 172 insertions(+), 208 deletions(-) diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 6e88b9fa1..075582554 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -93,7 +93,6 @@ target_sources(tinyusb_device_base INTERFACE ${TOP}/src/portable/raspberrypi/rp2040/dcd_rp2040.c ${TOP}/src/portable/raspberrypi/rp2040/rp2040_usb.c ${TOP}/src/device/usbd.c - ${TOP}/src/device/usbd_control.c ${TOP}/src/class/audio/audio_device.c ${TOP}/src/class/cdc/cdc_device.c ${TOP}/src/class/dfu/dfu_device.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00f466007..c7a5184c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,6 @@ function(tinyusb_sources_get OUTPUT_VAR) ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/common/tusb_fifo.c # device ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd.c - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd_control.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/audio/audio_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_device.c diff --git a/src/device/usbd.c b/src/device/usbd.c index da0ffb4c6..acf808bf6 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -419,11 +419,10 @@ static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_req } #endif -// from usbd_control.c -void usbd_control_reset(void); -void usbd_control_set_request(tusb_control_request_t const *request); -void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp ); -bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +// Control Endpoint +static void usbd_control_reset(void); +static void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp); +static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available @@ -808,6 +807,157 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { } } +//--------------------------------------------------------------------+ +// Control Endpoint +//--------------------------------------------------------------------+ + +// Weak hook: invoked when the control transfer's status stage completes +TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) { + (void) rhport; + (void) request; +} + +typedef struct { + tusb_control_request_t request; + uint8_t* buffer; + uint16_t data_len; + uint16_t total_xferred; + usbd_control_xfer_cb_t complete_cb; +} usbd_control_xfer_t; + +static usbd_control_xfer_t _ctrl_xfer; + +CFG_TUD_MEM_SECTION static struct { + TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); +} _ctrl_epbuf; + +uint8_t* usbd_get_ctrl_buf(void) { + return _ctrl_epbuf.buf; +} + +// Endpoint used for the Status stage of a control transfer. +// Per USB 2.0 §9.3.1, when wLength == 0 the Direction bit is ignored and the Status +// stage is always IN. Otherwise the Status stage is opposite of the Data stage direction. +TU_ATTR_ALWAYS_INLINE static inline uint8_t status_stage_ep(const tusb_control_request_t* request) { + return (request->wLength != 0 && request->bmRequestType_bit.direction) ? TU_EP0_OUT : TU_EP0_IN; +} + +// Queue ZLP status transaction +TU_ATTR_ALWAYS_INLINE static inline bool status_stage_xact(uint8_t rhport, uint8_t ep_status) { + return usbd_edpt_xfer(rhport, ep_status, NULL, 0, false); +} + +// Queue a transaction in Data Stage. Each transaction has up to Endpoint0's max +// packet size. This function can also transfer a zero-length packet. +static bool data_stage_xact(uint8_t rhport) { + const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); + uint8_t ep_addr = TU_EP0_OUT; + + if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { + ep_addr = TU_EP0_IN; + if (0u != xact_len && _ctrl_xfer.buffer != _ctrl_epbuf.buf) { + TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); + } + } + + return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); +} + +// Status phase +bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) { + // _ctrl_xfer fields are pre-initialized at process_control_request entry + (void) request; + return status_stage_xact(rhport, status_stage_ep(&_ctrl_xfer.request)); +} + +// Transmit data to/from the control endpoint. If wLength is zero, a status packet is sent instead. +bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) { + // _ctrl_xfer.request and reset fields are pre-initialized at process_control_request entry + (void) request; + _ctrl_xfer.buffer = (uint8_t*) buffer; + _ctrl_xfer.data_len = tu_min16(len, _ctrl_xfer.request.wLength); + + if (_ctrl_xfer.request.wLength > 0U) { + if (_ctrl_xfer.data_len > 0U) { + TU_ASSERT(buffer); + } + TU_ASSERT(data_stage_xact(rhport)); + } else { + // wLength == 0: Status stage is always IN per USB 2.0 §9.3.1 + TU_ASSERT(status_stage_xact(rhport, TU_EP0_IN)); + } + + return true; +} + +static void usbd_control_reset(void) { + tu_varclr(&_ctrl_xfer); +} + +static void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) { + _ctrl_xfer.complete_cb = fp; +} + +// Callback when a transaction completes on the DATA stage or Status stage of EP0 +static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void) result; + + // Status Stage complete: ep_addr matches the resolved Status stage endpoint + uint8_t const ep_status = status_stage_ep(&_ctrl_xfer.request); + if (ep_addr == ep_status) { + TU_ASSERT(0 == xferred_bytes); + + // invoke optional dcd hook if available + dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); + + if (NULL != _ctrl_xfer.complete_cb) { + // TODO refactor with usbd_driver_print_control_complete_name + _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request); + } + + return true; + } + + // Data stage progress + if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) { + TU_VERIFY(_ctrl_xfer.buffer); + if (_ctrl_xfer.buffer != _ctrl_epbuf.buf) { + memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes); + } + TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2); + } + + _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; + _ctrl_xfer.buffer += xferred_bytes; + + // Data Stage complete when wLength reached or short packet (incl. ZLP) seen + if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || + (xferred_bytes < CFG_TUD_ENDPOINT0_BUFSIZE)) { + bool is_ok = true; + + if (NULL != _ctrl_xfer.complete_cb) { + #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); + #endif + // Callback can still stall control in status phase, e.g. OUT data doesn't make sense + is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request); + } + + if (is_ok) { + TU_ASSERT(status_stage_xact(rhport, ep_status)); + } else { + // Stall both IN and OUT control endpoint + dcd_edpt_stall(rhport, TU_EP0_OUT); + dcd_edpt_stall(rhport, TU_EP0_IN); + } + } else { + // More data to transfer + TU_ASSERT(data_stage_xact(rhport)); + } + + return true; +} + //--------------------------------------------------------------------+ // Control Request Parser & Handling //--------------------------------------------------------------------+ @@ -822,7 +972,14 @@ static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * dri // This handles the actual request and its response. // Returns false if unable to complete the request, causing caller to stall control endpoints. static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) { - usbd_control_set_complete_callback(NULL); + // Initialize control transfer state for this request. The request copy must be + // visible to usbd_control_xfer_cb when the (asynchronous) status ZLP completes, + // since the SETUP packet event has already gone out of scope by then. + _ctrl_xfer.request = *p_request; + _ctrl_xfer.buffer = NULL; + _ctrl_xfer.total_xferred = 0; + _ctrl_xfer.data_len = 0; + _ctrl_xfer.complete_cb = NULL; TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); // Vendor request @@ -865,9 +1022,9 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const // Depending on mcu, status phase could be sent either before or after changing device address, // or even require stack to not response with status at all // Therefore DCD must take full responsibility to response and include zlp status packet if needed. - usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API + // _ctrl_xfer.request was already populated at process_control_request() entry, so the + // status ZLP that the DCD queues will be recognized by usbd_control_xfer_cb(). dcd_set_address(rhport, (uint8_t) p_request->wValue); - // skip tud_control_status() _usbd_dev.addressed = 1; break; diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index b14d08a9c..38dcc6a82 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -24,197 +24,8 @@ * This file is part of the TinyUSB stack. */ -#include "tusb_option.h" - -#if CFG_TUD_ENABLED - -#include "dcd.h" -#include "tusb.h" -#include "device/usbd_pvt.h" - -//--------------------------------------------------------------------+ -// Callback weak stubs (called if application does not provide) -//--------------------------------------------------------------------+ -TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) { - (void) rhport; - (void) request; -} - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - - -typedef struct { - tusb_control_request_t request; - uint8_t* buffer; - uint16_t data_len; - uint16_t total_xferred; - usbd_control_xfer_cb_t complete_cb; -} usbd_control_xfer_t; - -static usbd_control_xfer_t _ctrl_xfer; - -CFG_TUD_MEM_SECTION static struct { - TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); -} _ctrl_epbuf; - -uint8_t* usbd_get_ctrl_buf(void) { - return _ctrl_epbuf.buf; -} - -//--------------------------------------------------------------------+ -// Application API -//--------------------------------------------------------------------+ - -// Endpoint used for the Status stage of a control transfer. -// Per USB 2.0 §9.3.1, when wLength == 0 the Direction bit is ignored and the Status stage -// is always IN. Otherwise the Status stage is opposite to the Data stage direction. -TU_ATTR_ALWAYS_INLINE static inline uint8_t status_stage_ep(const tusb_control_request_t* request) { - return (request->wLength != 0 && request->bmRequestType_bit.direction) ? TU_EP0_OUT : TU_EP0_IN; -} - -// Queue ZLP status transaction -TU_ATTR_ALWAYS_INLINE static inline bool status_stage_xact(uint8_t rhport, uint8_t ep_status) { - return usbd_edpt_xfer(rhport, ep_status, NULL, 0, false); -} - -// Status phase -bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) { - _ctrl_xfer.request = (*request); - _ctrl_xfer.buffer = NULL; - _ctrl_xfer.total_xferred = 0; - _ctrl_xfer.data_len = 0; - - return status_stage_xact(rhport, status_stage_ep(request)); -} - -// Queue a transaction in Data Stage -// Each transaction has up to Endpoint0's max packet size. -// This function can also transfer an zero-length packet -static bool data_stage_xact(uint8_t rhport) { - const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); - uint8_t ep_addr = TU_EP0_OUT; - - if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { - ep_addr = TU_EP0_IN; - if (0u != xact_len && _ctrl_xfer.buffer != _ctrl_epbuf.buf) { - TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); - } - } - - return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); -} - -// Transmit data to/from the control endpoint. -// If the request's wLength is zero, a status packet is sent instead. -bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) { - _ctrl_xfer.request = (*request); - _ctrl_xfer.buffer = (uint8_t*) buffer; - _ctrl_xfer.total_xferred = 0U; - _ctrl_xfer.data_len = tu_min16(len, request->wLength); - - if (request->wLength > 0U) { - if (_ctrl_xfer.data_len > 0U) { - TU_ASSERT(buffer); - } - TU_ASSERT(data_stage_xact(rhport)); - } else { - TU_ASSERT(status_stage_xact(rhport, TU_EP0_IN)); - } - - return true; -} - -//--------------------------------------------------------------------+ -// USBD API -//--------------------------------------------------------------------+ -void usbd_control_reset(void); -void usbd_control_set_request(const tusb_control_request_t* request); -void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp); -bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); - -void usbd_control_reset(void) { - tu_varclr(&_ctrl_xfer); -} - -// Set complete callback -void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) { - _ctrl_xfer.complete_cb = fp; -} - -// for dcd_set_address where DCD is responsible for status response -void usbd_control_set_request(const tusb_control_request_t* request) { - _ctrl_xfer.request = (*request); - _ctrl_xfer.buffer = NULL; - _ctrl_xfer.total_xferred = 0; - _ctrl_xfer.data_len = 0; -} - -// callback when a transaction complete on -// - DATA stage of control endpoint or -// - Status stage -bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { - (void) result; - - // Status Stage complete: endpoint matches the Status stage endpoint - uint8_t const ep_status = status_stage_ep(&_ctrl_xfer.request); - if (ep_addr == ep_status) { - TU_ASSERT(0 == xferred_bytes); - - // invoke optional dcd hook if available - dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); - - if (NULL != _ctrl_xfer.complete_cb) { - // TODO refactor with usbd_driver_print_control_complete_name - _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request); - } - - return true; - } - - // Data stage complete - if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) { - TU_VERIFY(_ctrl_xfer.buffer); - if (_ctrl_xfer.buffer != _ctrl_epbuf.buf) { - memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes); - } - TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2); - } - - _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; - _ctrl_xfer.buffer += xferred_bytes; - - // Data Stage is complete when all request's length are transferred or - // a short packet is sent including zero-length packet. - if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || - (xferred_bytes < CFG_TUD_ENDPOINT0_BUFSIZE)) { - // DATA stage is complete - bool is_ok = true; - - // invoke complete callback if set - // callback can still stall control in status phase e.g out data does not make sense - if (NULL != _ctrl_xfer.complete_cb) { - #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL - usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); - #endif - - is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request); - } - - if (is_ok) { - TU_ASSERT(status_stage_xact(rhport, ep_status)); - } else { - // Stall both IN and OUT control endpoint - dcd_edpt_stall(rhport, TU_EP0_OUT); - dcd_edpt_stall(rhport, TU_EP0_IN); - } - } else { - // More data to transfer - TU_ASSERT(data_stage_xact(rhport)); - } - - return true; -} - -#endif +// The usbd control function that used to live in this file has been merged +// into src/device/usbd.c. This translation unit is intentionally empty and is +// kept only so external/vendor build systems that still reference the path +// keep resolving. Drop usbd_control.c from your build to silence the warning. +#warning "src/device/usbd_control.c is deprecated and now empty; remove it from your build (its content lives in src/device/usbd.c)." diff --git a/src/tinyusb.mk b/src/tinyusb.mk index 169098016..e3ef35dcf 100644 --- a/src/tinyusb.mk +++ b/src/tinyusb.mk @@ -3,7 +3,6 @@ TINYUSB_SRC_C += \ src/tusb.c \ src/common/tusb_fifo.c \ src/device/usbd.c \ - src/device/usbd_control.c \ src/typec/usbc.c \ src/class/audio/audio_device.c \ src/class/cdc/cdc_device.c \ diff --git a/test/fuzz/rules.mk b/test/fuzz/rules.mk index 329dcce11..c14330312 100644 --- a/test/fuzz/rules.mk +++ b/test/fuzz/rules.mk @@ -23,7 +23,6 @@ SRC_C += \ src/tusb.c \ src/common/tusb_fifo.c \ src/device/usbd.c \ - src/device/usbd_control.c \ src/class/audio/audio_device.c \ src/class/cdc/cdc_device.c \ src/class/dfu/dfu_device.c \ diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index b44a91d57..a33af4563 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -117,14 +117,14 @@ add_ceedling_test( add_ceedling_test( test_usbd ${CEEDLING_WORKDIR}/test/device/usbd/test_usbd.c - "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/device/usbd_control.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" + "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" "${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_dcd.c;${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_msc_device.c" ) add_ceedling_test( test_msc_device ${CEEDLING_WORKDIR}/test/device/msc/test_msc_device.c - "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/device/usbd_control.c;${CEEDLING_WORKDIR}/../../src/class/msc/msc_device.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" + "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/class/msc/msc_device.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" "${CEEDLING_BUILD_DIR}/test/mocks/test_msc_device/mock_dcd.c" ) -- cgit v1.3.1 From 4722252558d4b2a5d77fff7a59ab9c9e51befe47 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 29 Apr 2026 22:00:05 +0200 Subject: ensure board_uart_write return -1 on default Signed-off-by: HiFiPhile --- hw/bsp/at32f402_405/family.c | 2 +- hw/bsp/at32f403a_407/family.c | 2 +- hw/bsp/at32f413/family.c | 2 +- hw/bsp/at32f415/family.c | 2 +- hw/bsp/at32f423/family.c | 2 +- hw/bsp/at32f425/family.c | 2 +- hw/bsp/at32f435_437/family.c | 2 +- hw/bsp/at32f45x/family.c | 2 +- hw/bsp/ch32v20x/family.c | 2 +- hw/bsp/ft9xx/family.c | 3 ++- hw/bsp/gd32vf103/family.c | 2 +- hw/bsp/kinetis_k/family.c | 2 +- hw/bsp/mm32/family.c | 2 +- hw/bsp/pic32mz/family.c | 4 +++- hw/bsp/rp2040/family.c | 2 +- hw/bsp/stm32c0/family.c | 2 +- hw/bsp/stm32f0/family.c | 2 +- hw/bsp/stm32f1/family.c | 2 +- hw/bsp/stm32f2/family.c | 2 +- hw/bsp/stm32f3/family.c | 2 +- hw/bsp/stm32f4/family.c | 2 +- hw/bsp/stm32f7/family.c | 2 +- hw/bsp/stm32g0/family.c | 2 +- hw/bsp/stm32g4/family.c | 2 +- hw/bsp/stm32h5/family.c | 2 +- hw/bsp/stm32h7/family.c | 2 +- hw/bsp/stm32h7rs/family.c | 2 +- hw/bsp/stm32l0/family.c | 2 +- hw/bsp/stm32l4/family.c | 2 +- hw/bsp/stm32n6/family.c | 2 +- hw/bsp/stm32u0/family.c | 2 +- hw/bsp/stm32u5/family.c | 2 +- hw/bsp/stm32wb/family.c | 2 +- hw/bsp/xmc4000/family.c | 2 +- 34 files changed, 37 insertions(+), 34 deletions(-) diff --git a/hw/bsp/at32f402_405/family.c b/hw/bsp/at32f402_405/family.c index 56d4a7bea..aa1a5b484 100644 --- a/hw/bsp/at32f402_405/family.c +++ b/hw/bsp/at32f402_405/family.c @@ -220,7 +220,7 @@ int board_uart_write(void const *buf, int len) #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/at32f403a_407/family.c b/hw/bsp/at32f403a_407/family.c index 942e15872..cf15ba83a 100644 --- a/hw/bsp/at32f403a_407/family.c +++ b/hw/bsp/at32f403a_407/family.c @@ -238,7 +238,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/at32f413/family.c b/hw/bsp/at32f413/family.c index d9af0ae4d..69591b2ba 100644 --- a/hw/bsp/at32f413/family.c +++ b/hw/bsp/at32f413/family.c @@ -238,7 +238,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/at32f415/family.c b/hw/bsp/at32f415/family.c index ca205d480..132e0db31 100644 --- a/hw/bsp/at32f415/family.c +++ b/hw/bsp/at32f415/family.c @@ -212,7 +212,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/at32f423/family.c b/hw/bsp/at32f423/family.c index 9f13dba07..79ac3fcca 100644 --- a/hw/bsp/at32f423/family.c +++ b/hw/bsp/at32f423/family.c @@ -239,7 +239,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/at32f425/family.c b/hw/bsp/at32f425/family.c index 1629ad7c0..75e53b1f7 100644 --- a/hw/bsp/at32f425/family.c +++ b/hw/bsp/at32f425/family.c @@ -220,7 +220,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/at32f435_437/family.c b/hw/bsp/at32f435_437/family.c index 59a4fe120..80e200363 100644 --- a/hw/bsp/at32f435_437/family.c +++ b/hw/bsp/at32f435_437/family.c @@ -265,7 +265,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/at32f45x/family.c b/hw/bsp/at32f45x/family.c index 27eae861f..42b688b87 100644 --- a/hw/bsp/at32f45x/family.c +++ b/hw/bsp/at32f45x/family.c @@ -216,7 +216,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/ch32v20x/family.c b/hw/bsp/ch32v20x/family.c index 221f62107..76024cfde 100644 --- a/hw/bsp/ch32v20x/family.c +++ b/hw/bsp/ch32v20x/family.c @@ -220,6 +220,6 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/ft9xx/family.c b/hw/bsp/ft9xx/family.c index 5ee134eb0..a1c50e1b7 100644 --- a/hw/bsp/ft9xx/family.c +++ b/hw/bsp/ft9xx/family.c @@ -232,10 +232,11 @@ int board_uart_write(void const *buf, int len) break; } } + return count; #else (void) buf; (void) len; + return -1; #endif - return count; } // Get current milliseconds diff --git a/hw/bsp/gd32vf103/family.c b/hw/bsp/gd32vf103/family.c index c1dc82bda..8f82b3ada 100644 --- a/hw/bsp/gd32vf103/family.c +++ b/hw/bsp/gd32vf103/family.c @@ -174,7 +174,7 @@ int board_uart_write(void const* buf, int len) { #else (void)buf; (void)len; - return 0; + return -1; #endif } diff --git a/hw/bsp/kinetis_k/family.c b/hw/bsp/kinetis_k/family.c index 8efab2762..a5af83931 100644 --- a/hw/bsp/kinetis_k/family.c +++ b/hw/bsp/kinetis_k/family.c @@ -140,7 +140,7 @@ int board_uart_write(void const *buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/mm32/family.c b/hw/bsp/mm32/family.c index 14a17f6c5..1538181dc 100644 --- a/hw/bsp/mm32/family.c +++ b/hw/bsp/mm32/family.c @@ -167,7 +167,7 @@ int board_uart_write(void const* buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/pic32mz/family.c b/hw/bsp/pic32mz/family.c index 5805e653f..98038b4f7 100644 --- a/hw/bsp/pic32mz/family.c +++ b/hw/bsp/pic32mz/family.c @@ -102,7 +102,9 @@ TU_ATTR_WEAK int board_uart_read(uint8_t * buf, int len) TU_ATTR_WEAK int board_uart_write(void const * buf, int len) { (void) buf; - return len; + (void) len; + + return -1; } #if CFG_TUSB_OS == OPT_OS_NONE diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c index a4642face..55feec159 100644 --- a/hw/bsp/rp2040/family.c +++ b/hw/bsp/rp2040/family.c @@ -282,7 +282,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32c0/family.c b/hw/bsp/stm32c0/family.c index 72af3ce7f..ac37045fb 100644 --- a/hw/bsp/stm32c0/family.c +++ b/hw/bsp/stm32c0/family.c @@ -193,7 +193,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32f0/family.c b/hw/bsp/stm32f0/family.c index f413163e5..c77ef4008 100644 --- a/hw/bsp/stm32f0/family.c +++ b/hw/bsp/stm32f0/family.c @@ -206,7 +206,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32f1/family.c b/hw/bsp/stm32f1/family.c index 74e0f53f2..abde44d21 100644 --- a/hw/bsp/stm32f1/family.c +++ b/hw/bsp/stm32f1/family.c @@ -246,7 +246,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32f2/family.c b/hw/bsp/stm32f2/family.c index 051a026c5..260e5e067 100644 --- a/hw/bsp/stm32f2/family.c +++ b/hw/bsp/stm32f2/family.c @@ -210,7 +210,7 @@ int board_uart_write(void const* buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32f3/family.c b/hw/bsp/stm32f3/family.c index 0864d0fad..35e1852e8 100644 --- a/hw/bsp/stm32f3/family.c +++ b/hw/bsp/stm32f3/family.c @@ -222,7 +222,7 @@ int board_uart_write(void const* buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32f4/family.c b/hw/bsp/stm32f4/family.c index f4ef99150..4eea5c7a8 100644 --- a/hw/bsp/stm32f4/family.c +++ b/hw/bsp/stm32f4/family.c @@ -295,7 +295,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32f7/family.c b/hw/bsp/stm32f7/family.c index 078e372d8..7a322591b 100644 --- a/hw/bsp/stm32f7/family.c +++ b/hw/bsp/stm32f7/family.c @@ -357,7 +357,7 @@ int board_uart_write(const void *buf, int len) { #else (void)buf; (void)len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32g0/family.c b/hw/bsp/stm32g0/family.c index d0ff8bac2..cb674ccc9 100644 --- a/hw/bsp/stm32g0/family.c +++ b/hw/bsp/stm32g0/family.c @@ -206,7 +206,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32g4/family.c b/hw/bsp/stm32g4/family.c index 98739ffd5..433f74e2a 100644 --- a/hw/bsp/stm32g4/family.c +++ b/hw/bsp/stm32g4/family.c @@ -236,7 +236,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32h5/family.c b/hw/bsp/stm32h5/family.c index 8298dfaab..f52f94f32 100644 --- a/hw/bsp/stm32h5/family.c +++ b/hw/bsp/stm32h5/family.c @@ -238,7 +238,7 @@ int board_uart_write(void const* buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32h7/family.c b/hw/bsp/stm32h7/family.c index b32f73754..5173c5401 100644 --- a/hw/bsp/stm32h7/family.c +++ b/hw/bsp/stm32h7/family.c @@ -328,7 +328,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32h7rs/family.c b/hw/bsp/stm32h7rs/family.c index 7ae9e5532..b0841c947 100644 --- a/hw/bsp/stm32h7rs/family.c +++ b/hw/bsp/stm32h7rs/family.c @@ -490,7 +490,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32l0/family.c b/hw/bsp/stm32l0/family.c index 930fa2d66..a4a0ebbfe 100644 --- a/hw/bsp/stm32l0/family.c +++ b/hw/bsp/stm32l0/family.c @@ -176,7 +176,7 @@ int board_uart_write(void const* buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32l4/family.c b/hw/bsp/stm32l4/family.c index 7a8acb3de..96de82bd5 100644 --- a/hw/bsp/stm32l4/family.c +++ b/hw/bsp/stm32l4/family.c @@ -264,7 +264,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32n6/family.c b/hw/bsp/stm32n6/family.c index 95578af04..80de20c6a 100644 --- a/hw/bsp/stm32n6/family.c +++ b/hw/bsp/stm32n6/family.c @@ -388,7 +388,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32u0/family.c b/hw/bsp/stm32u0/family.c index 5cf6e1eb2..7bd99fba6 100644 --- a/hw/bsp/stm32u0/family.c +++ b/hw/bsp/stm32u0/family.c @@ -199,7 +199,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32u5/family.c b/hw/bsp/stm32u5/family.c index 41e354351..7969b1c70 100644 --- a/hw/bsp/stm32u5/family.c +++ b/hw/bsp/stm32u5/family.c @@ -304,7 +304,7 @@ int board_uart_write(void const *buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/stm32wb/family.c b/hw/bsp/stm32wb/family.c index d97be2115..1f1da2271 100644 --- a/hw/bsp/stm32wb/family.c +++ b/hw/bsp/stm32wb/family.c @@ -196,7 +196,7 @@ int board_uart_write(void const* buf, int len) { return count; #else (void) buf; (void) len; - return 0; + return -1; #endif } diff --git a/hw/bsp/xmc4000/family.c b/hw/bsp/xmc4000/family.c index 1325b784b..7e224d092 100644 --- a/hw/bsp/xmc4000/family.c +++ b/hw/bsp/xmc4000/family.c @@ -150,7 +150,7 @@ int board_uart_write(void const* buf, int len) { #else (void) buf; (void) len; - return 0; + return -1; #endif } -- cgit v1.3.1 From 4e7db9c60d9ff77b3e0de14ecdfeb372813c46b7 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 29 Apr 2026 22:11:36 +0200 Subject: fixup Signed-off-by: HiFiPhile --- hw/bsp/board.c | 2 +- hw/bsp/ft9xx/family.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/bsp/board.c b/hw/bsp/board.c index 65b44e5f2..5093382d9 100644 --- a/hw/bsp/board.c +++ b/hw/bsp/board.c @@ -171,7 +171,7 @@ int board_getchar(void) { } int board_putchar(int c) { - if (board_uart_write((const char *)&c, 1)) { + if (board_uart_write((const char *)&c, 1) > 0) { return c; } else { return -1; diff --git a/hw/bsp/ft9xx/family.c b/hw/bsp/ft9xx/family.c index a1c50e1b7..0ca2663ac 100644 --- a/hw/bsp/ft9xx/family.c +++ b/hw/bsp/ft9xx/family.c @@ -221,8 +221,8 @@ int board_uart_read(uint8_t *buf, int len) // Send characters to UART int board_uart_write(void const *buf, int len) { - int count = 0; #ifdef BOARD_UART + int count = 0; uint8_t const *p = (uint8_t const *) buf; while (count < len) { if (BOARD_UART->LSR_ICR_XON2 & MASK_UART_LSR_THRE) { -- cgit v1.3.1 From eb66712196b54ff5dcf39df020fb0cad72e853d0 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 29 Apr 2026 22:23:32 +0700 Subject: refactor `usbd.c`: centralize control transfer state management into `_usbd_dev` structure and remove `usbd_control_reset` --- src/device/usbd.c | 146 +++++++++++++++++++--------------------------- src/device/usbd_control.c | 31 ---------- 2 files changed, 61 insertions(+), 116 deletions(-) delete mode 100644 src/device/usbd_control.c diff --git a/src/device/usbd.c b/src/device/usbd.c index acf808bf6..49c8851de 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -115,7 +115,20 @@ TU_ATTR_WEAK bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_si //--------------------------------------------------------------------+ // Device Data //--------------------------------------------------------------------+ + +// Per-control-transfer state: populated at process_setup_received() entry, +// consumed asynchronously by usbd_control_xfer_cb() when the EP0 transfer completes. +typedef struct { + tusb_control_request_t request; + uint8_t* buffer; + uint16_t data_len; + uint16_t total_xferred; + usbd_control_xfer_cb_t complete_cb; +} usbd_control_xfer_t; + typedef struct { + usbd_control_xfer_t ctrl_xfer; + // Note: these may share an enum state volatile uint8_t connected; volatile uint8_t addressed; @@ -142,6 +155,10 @@ typedef struct { static usbd_device_t _usbd_dev; static volatile uint8_t _usbd_queued_setup; +CFG_TUD_MEM_SECTION static struct { + TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); +} _ctrl_epbuf; + //--------------------------------------------------------------------+ // Class Driver //--------------------------------------------------------------------+ @@ -405,7 +422,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, //--------------------------------------------------------------------+ // Prototypes //--------------------------------------------------------------------+ -static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); +static bool process_setup_received(uint8_t rhport, tusb_control_request_t const * p_request); static bool process_set_config(uint8_t rhport, uint8_t cfg_num); static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); @@ -420,8 +437,6 @@ static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_req #endif // Control Endpoint -static void usbd_control_reset(void); -static void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp); static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); //--------------------------------------------------------------------+ @@ -458,17 +473,6 @@ static char const *const _usbd_event_str[DCD_EVENT_COUNT] = { "Func Call" }; -// for usbd_control to print the name of control complete driver -void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) { - for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { - usbd_class_driver_t const* driver = get_driver(i); - if (driver && driver->control_xfer_cb == callback) { - TU_LOG_USBD("%s control complete\r\n", driver->name); - return; - } - } -} - #endif //--------------------------------------------------------------------+ @@ -608,9 +612,7 @@ bool tud_deinit(uint8_t rhport) { } } - // Clear device data - tu_varclr(&_usbd_dev); - usbd_control_reset(); + tu_varclr(&_usbd_dev); // Clear device data // Deinit device queue & task osal_queue_delete(_usbd_q); @@ -645,7 +647,6 @@ static void configuration_reset(uint8_t rhport) { static void usbd_reset(uint8_t rhport) { configuration_reset(rhport); - usbd_control_reset(); } bool tud_task_event_ready(void) { @@ -731,7 +732,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { _usbd_dev.ep_status[0][TUSB_DIR_IN].claimed = 0; // Process control request - if (!process_control_request(event.rhport, &event.setup_received)) { + if (!process_setup_received(event.rhport, &event.setup_received)) { TU_LOG_USBD(" Stall EP0\r\n"); // Failed -> stall both control endpoint IN and OUT dcd_edpt_stall(event.rhport, 0); @@ -817,20 +818,6 @@ TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_r (void) request; } -typedef struct { - tusb_control_request_t request; - uint8_t* buffer; - uint16_t data_len; - uint16_t total_xferred; - usbd_control_xfer_cb_t complete_cb; -} usbd_control_xfer_t; - -static usbd_control_xfer_t _ctrl_xfer; - -CFG_TUD_MEM_SECTION static struct { - TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); -} _ctrl_epbuf; - uint8_t* usbd_get_ctrl_buf(void) { return _ctrl_epbuf.buf; } @@ -850,13 +837,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool status_stage_xact(uint8_t rhport, uint8 // Queue a transaction in Data Stage. Each transaction has up to Endpoint0's max // packet size. This function can also transfer a zero-length packet. static bool data_stage_xact(uint8_t rhport) { - const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); + usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; + const uint16_t xact_len = tu_min16(ctrl_xfer->data_len - ctrl_xfer->total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); uint8_t ep_addr = TU_EP0_OUT; - if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { + if (ctrl_xfer->request.bmRequestType_bit.direction == TUSB_DIR_IN) { ep_addr = TU_EP0_IN; - if (0u != xact_len && _ctrl_xfer.buffer != _ctrl_epbuf.buf) { - TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); + if (0u != xact_len && ctrl_xfer->buffer != _ctrl_epbuf.buf) { + TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, ctrl_xfer->buffer, xact_len)); } } @@ -865,20 +853,21 @@ static bool data_stage_xact(uint8_t rhport) { // Status phase bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) { - // _ctrl_xfer fields are pre-initialized at process_control_request entry + // _usbd_dev.ctrl_xfer fields are pre-initialized at process_setup_received entry (void) request; - return status_stage_xact(rhport, status_stage_ep(&_ctrl_xfer.request)); + return status_stage_xact(rhport, status_stage_ep(&_usbd_dev.ctrl_xfer.request)); } // Transmit data to/from the control endpoint. If wLength is zero, a status packet is sent instead. bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) { - // _ctrl_xfer.request and reset fields are pre-initialized at process_control_request entry + // _usbd_dev.ctrl_xfer.request and reset fields are pre-initialized at process_setup_received entry (void) request; - _ctrl_xfer.buffer = (uint8_t*) buffer; - _ctrl_xfer.data_len = tu_min16(len, _ctrl_xfer.request.wLength); + usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; + ctrl_xfer->buffer = (uint8_t*) buffer; + ctrl_xfer->data_len = tu_min16(len, ctrl_xfer->request.wLength); - if (_ctrl_xfer.request.wLength > 0U) { - if (_ctrl_xfer.data_len > 0U) { + if (ctrl_xfer->request.wLength > 0U) { + if (ctrl_xfer->data_len > 0U) { TU_ASSERT(buffer); } TU_ASSERT(data_stage_xact(rhport)); @@ -890,57 +879,46 @@ bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, voi return true; } -static void usbd_control_reset(void) { - tu_varclr(&_ctrl_xfer); -} - -static void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) { - _ctrl_xfer.complete_cb = fp; -} - // Callback when a transaction completes on the DATA stage or Status stage of EP0 static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { (void) result; + usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; // Status Stage complete: ep_addr matches the resolved Status stage endpoint - uint8_t const ep_status = status_stage_ep(&_ctrl_xfer.request); + uint8_t const ep_status = status_stage_ep(&ctrl_xfer->request); if (ep_addr == ep_status) { TU_ASSERT(0 == xferred_bytes); // invoke optional dcd hook if available - dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); + dcd_edpt0_status_complete(rhport, &ctrl_xfer->request); - if (NULL != _ctrl_xfer.complete_cb) { - // TODO refactor with usbd_driver_print_control_complete_name - _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request); + if (NULL != ctrl_xfer->complete_cb) { + ctrl_xfer->complete_cb(rhport, CONTROL_STAGE_ACK, &ctrl_xfer->request); } return true; } // Data stage progress - if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) { - TU_VERIFY(_ctrl_xfer.buffer); - if (_ctrl_xfer.buffer != _ctrl_epbuf.buf) { - memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes); + if (ctrl_xfer->request.bmRequestType_bit.direction == TUSB_DIR_OUT) { + TU_VERIFY(ctrl_xfer->buffer); + if (ctrl_xfer->buffer != _ctrl_epbuf.buf) { + memcpy(ctrl_xfer->buffer, _ctrl_epbuf.buf, xferred_bytes); } - TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2); + TU_LOG_MEM(CFG_TUD_LOG_LEVEL, ctrl_xfer->buffer, xferred_bytes, 2); } - _ctrl_xfer.total_xferred += (uint16_t) xferred_bytes; - _ctrl_xfer.buffer += xferred_bytes; + ctrl_xfer->total_xferred += (uint16_t) xferred_bytes; + ctrl_xfer->buffer += xferred_bytes; // Data Stage complete when wLength reached or short packet (incl. ZLP) seen - if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || + if ((ctrl_xfer->request.wLength == ctrl_xfer->total_xferred) || (xferred_bytes < CFG_TUD_ENDPOINT0_BUFSIZE)) { bool is_ok = true; - if (NULL != _ctrl_xfer.complete_cb) { - #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL - usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb); - #endif + if (NULL != ctrl_xfer->complete_cb) { // Callback can still stall control in status phase, e.g. OUT data doesn't make sense - is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request); + is_ok = ctrl_xfer->complete_cb(rhport, CONTROL_STAGE_DATA, &ctrl_xfer->request); } if (is_ok) { @@ -964,27 +942,28 @@ static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t // Helper to invoke class driver control request handler static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) { - usbd_control_set_complete_callback(driver->control_xfer_cb); + _usbd_dev.ctrl_xfer.complete_cb = driver->control_xfer_cb; TU_LOG_USBD(" %s control request\r\n", driver->name); return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); } // This handles the actual request and its response. // Returns false if unable to complete the request, causing caller to stall control endpoints. -static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) { +static bool process_setup_received(uint8_t rhport, tusb_control_request_t const * p_request) { // Initialize control transfer state for this request. The request copy must be // visible to usbd_control_xfer_cb when the (asynchronous) status ZLP completes, // since the SETUP packet event has already gone out of scope by then. - _ctrl_xfer.request = *p_request; - _ctrl_xfer.buffer = NULL; - _ctrl_xfer.total_xferred = 0; - _ctrl_xfer.data_len = 0; - _ctrl_xfer.complete_cb = NULL; + usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; + ctrl_xfer->request = *p_request; + ctrl_xfer->buffer = NULL; + ctrl_xfer->total_xferred = 0; + ctrl_xfer->data_len = 0; + ctrl_xfer->complete_cb = NULL; TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); // Vendor request if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) { - usbd_control_set_complete_callback(tud_vendor_control_xfer_cb); + ctrl_xfer->complete_cb = tud_vendor_control_xfer_cb; return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request); } @@ -1022,8 +1001,6 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const // Depending on mcu, status phase could be sent either before or after changing device address, // or even require stack to not response with status at all // Therefore DCD must take full responsibility to response and include zlp status packet if needed. - // _ctrl_xfer.request was already populated at process_control_request() entry, so the - // status ZLP that the DCD queues will be recognized by usbd_control_xfer_cb(). dcd_set_address(rhport, (uint8_t) p_request->wValue); _usbd_dev.addressed = 1; break; @@ -1092,7 +1069,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const uint8_t const selector = tu_u16_high(p_request->wIndex); TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE); - usbd_control_set_complete_callback(process_test_mode_cb); + ctrl_xfer->complete_cb = process_test_mode_cb; tud_control_status(rhport, p_request); break; } @@ -1161,7 +1138,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type); // Clear complete callback if driver set since it can also stall the request. - usbd_control_set_complete_callback(NULL); + ctrl_xfer->complete_cb = NULL; switch (p_request->bRequest) { //-V2520 case TUSB_REQ_GET_INTERFACE: { @@ -1219,7 +1196,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const // STD request must always be ACKed regardless of driver returned value // Also clear complete callback if driver set since it can also stall the request. (void) invoke_class_control(rhport, driver, p_request); - usbd_control_set_complete_callback(NULL); + ctrl_xfer->complete_cb = NULL; // skip ZLP status if driver already did that if (!_usbd_dev.ep_status[0][TUSB_DIR_IN].busy) { @@ -1301,8 +1278,7 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num) { } // return descriptor's buffer and update desc_len -static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) -{ +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) { tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); uint8_t const desc_index = tu_u16_low( p_request->wValue ); diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c deleted file mode 100644 index 38dcc6a82..000000000 --- a/src/device/usbd_control.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * This file is part of the TinyUSB stack. - */ - -// The usbd control function that used to live in this file has been merged -// into src/device/usbd.c. This translation unit is intentionally empty and is -// kept only so external/vendor build systems that still reference the path -// keep resolving. Drop usbd_control.c from your build to silence the warning. -#warning "src/device/usbd_control.c is deprecated and now empty; remove it from your build (its content lives in src/device/usbd.c)." -- cgit v1.3.1 From 25ddb7ff0cfd361b733dadd3bb5307fdb94b6dc1 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 09:22:11 +0700 Subject: minor clean up --- .../net_lwip_webserver/src/usb_descriptors.c | 42 ++++++++++++++-------- src/class/net/ecm_rndis_device.c | 4 +-- src/class/net/ncm_device.c | 2 +- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c index 1aa223eb9..a7e48c79b 100644 --- a/examples/device/net_lwip_webserver/src/usb_descriptors.c +++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c @@ -221,45 +221,57 @@ static uint8_t const ncm_hs_configuration[] = { #endif -// Configuration array: RNDIS and CDC-ECM +// NCM work with all latest OS i.e macos 10.10+, windows 10+, and Linux. +// For older system Configuration array of RNDIS and CDC-ECM may be needed for better compatibility. // - Windows only works with RNDIS // - MacOS only works with CDC-ECM // - Linux will work on both -static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = { #if CFG_TUD_ECM_RNDIS + +static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = { [CONFIG_ID_RNDIS] = rndis_fs_configuration, [CONFIG_ID_ECM] = ecm_fs_configuration -#else - [CONFIG_ID_NCM] = ncm_fs_configuration -#endif }; #if TUD_OPT_HIGH_SPEED static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = { -#if CFG_TUD_ECM_RNDIS [CONFIG_ID_RNDIS] = rndis_hs_configuration, [CONFIG_ID_ECM] = ecm_hs_configuration -#else - [CONFIG_ID_NCM] = ncm_hs_configuration -#endif }; // Size array for each configuration static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = { -#if CFG_TUD_ECM_RNDIS [CONFIG_ID_RNDIS] = MAIN_CONFIG_TOTAL_LEN, [CONFIG_ID_ECM] = ALT_CONFIG_TOTAL_LEN +}; + +// Scratch buffer for other speed configuration (sized to hold the largest config) +#define MAX_CONFIG_TOTAL_LEN TU_MAX(MAIN_CONFIG_TOTAL_LEN, ALT_CONFIG_TOTAL_LEN) +#endif + #else + +static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = { + [CONFIG_ID_NCM] = ncm_fs_configuration +}; + +#if TUD_OPT_HIGH_SPEED +static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = { + [CONFIG_ID_NCM] = ncm_hs_configuration +}; + +// Size array for each configuration +static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = { [CONFIG_ID_NCM] = NCM_CONFIG_TOTAL_LEN -#endif }; // Scratch buffer for other speed configuration (sized to hold the largest config) -#if CFG_TUD_ECM_RNDIS - #define MAX_CONFIG_TOTAL_LEN TU_MAX(MAIN_CONFIG_TOTAL_LEN, ALT_CONFIG_TOTAL_LEN) -#else - #define MAX_CONFIG_TOTAL_LEN NCM_CONFIG_TOTAL_LEN +#define MAX_CONFIG_TOTAL_LEN NCM_CONFIG_TOTAL_LEN #endif + +#endif + +#if TUD_OPT_HIGH_SPEED static uint8_t desc_other_speed_config[MAX_CONFIG_TOTAL_LEN]; // device qualifier: device descriptor fields that differ at other speed diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c index 52c4f7f87..b27cac3ea 100644 --- a/src/class/net/ecm_rndis_device.c +++ b/src/class/net/ecm_rndis_device.c @@ -48,10 +48,10 @@ typedef struct { uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active - uint8_t ep_notif; uint8_t ep_in; uint8_t ep_out; uint16_t ep_size; // bulk endpoint max packet size (IN and OUT assumed equal) + uint8_t ep_notif; bool ecm_mode; @@ -361,7 +361,7 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ /* data transmission finished */ if (ep_addr == _netd_itf.ep_in) { /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */ - if (xferred_bytes && (0 == (xferred_bytes % _netd_itf.ep_size))) { + if (xferred_bytes > 0 && 0 == (xferred_bytes & (_netd_itf.ep_size-1))) { do_in_xfer(NULL, 0); /* a ZLP is needed */ } else { /* we're finally finished */ diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index fe33d0247..1327dbaf2 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -342,7 +342,7 @@ static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) { TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes); uint16_t const ep_size = ncm_interface.ep_size; - if (xferred_bytes == 0 || xferred_bytes % ep_size != 0) { + if (xferred_bytes == 0 || (xferred_bytes & (ep_size-1)) != 0) { return false; } -- cgit v1.3.1 From a5e9ce5fbb300702d04c47c7c06436396912d3d6 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 10:29:29 +0700 Subject: refactor `usbd.c`: extract `process_std_device_request` for clarity --- src/device/usbd.c | 223 +++++++++++++++++++++++++++--------------------------- 1 file changed, 113 insertions(+), 110 deletions(-) diff --git a/src/device/usbd.c b/src/device/usbd.c index 49c8851de..291319709 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -422,6 +422,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, //--------------------------------------------------------------------+ // Prototypes //--------------------------------------------------------------------+ +static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); static bool process_setup_received(uint8_t rhport, tusb_control_request_t const * p_request); static bool process_set_config(uint8_t rhport, uint8_t cfg_num); static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); @@ -436,9 +437,6 @@ static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_req } #endif -// Control Endpoint -static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); - //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ @@ -947,6 +945,117 @@ static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * dri return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); } +// Process a standard request to the device recipient (extracted from +// process_setup_received for readability; GCC chooses to inline it). +static bool process_std_device_request(uint8_t rhport, tusb_control_request_t const * p_request) { + switch (p_request->bRequest) { //-V2520 + case TUSB_REQ_SET_ADDRESS: + // Depending on mcu, status phase could be sent either before or after changing device address, + // or even require stack to not response with status at all + // Therefore DCD must take full responsibility to response and include zlp status packet if needed. + dcd_set_address(rhport, (uint8_t) p_request->wValue); + _usbd_dev.addressed = 1; + return true; + + case TUSB_REQ_GET_CONFIGURATION: { + uint8_t cfg_num = _usbd_dev.cfg_num; + tud_control_xfer(rhport, p_request, &cfg_num, 1); + return true; + } + + case TUSB_REQ_SET_CONFIGURATION: { + uint8_t const cfg_num = (uint8_t) p_request->wValue; + + // Only process if new configure is different + if (_usbd_dev.cfg_num != cfg_num) { + if (_usbd_dev.cfg_num != 0) { + // already configured: need to clear all endpoints and driver first + TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num); + + dcd_sof_enable(rhport, false); + dcd_edpt_close_all(rhport); + + // close all drivers and current configured state except bus speed + const uint8_t speed = _usbd_dev.speed; + configuration_reset(rhport); + + _usbd_dev.speed = speed; // restore speed + } + + _usbd_dev.cfg_num = cfg_num; + + // Handle the new configuration + if (cfg_num == 0) { + tud_umount_cb(); + } else { + if (!process_set_config(rhport, cfg_num)) { + _usbd_dev.cfg_num = 0; + TU_ASSERT(false); + } + tud_mount_cb(); + } + } + + tud_control_status(rhport, p_request); + return true; + } + + case TUSB_REQ_GET_DESCRIPTOR: + return process_get_descriptor(rhport, p_request); + + case TUSB_REQ_SET_FEATURE: + switch (p_request->wValue) { //-V2520 + case TUSB_REQ_FEATURE_REMOTE_WAKEUP: + TU_LOG_USBD(" Enable Remote Wakeup\r\n"); + // Host may enable remote wake up before suspending especially HID device + _usbd_dev.remote_wakeup_en = 1; + tud_control_status(rhport, p_request); + return true; + + #if CFG_TUD_TEST_MODE + case TUSB_REQ_FEATURE_TEST_MODE: { + // Only handle the test mode if supported and valid + TU_VERIFY(0 == tu_u16_low(p_request->wIndex)); + + uint8_t const selector = tu_u16_high(p_request->wIndex); + TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE); + + _usbd_dev.ctrl_xfer.complete_cb = process_test_mode_cb; + tud_control_status(rhport, p_request); + return true; + } + #endif + + // Stall unsupported feature selector + default: return false; + } + + case TUSB_REQ_CLEAR_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + TU_LOG_USBD(" Disable Remote Wakeup\r\n"); + + // Host may disable remote wake up after resuming + _usbd_dev.remote_wakeup_en = 0; + tud_control_status(rhport, p_request); + return true; + + case TUSB_REQ_GET_STATUS: { + // Device status bit mask + // - Bit 0: Self Powered TODO must invoke callback to get actual status + // - Bit 1: Remote Wakeup enabled + uint16_t status = (uint16_t) _usbd_dev.dev_state_bm; + tud_control_xfer(rhport, p_request, &status, 2); + return true; + } + + default: + TU_BREAKPOINT(); + return false; + } +} + + // This handles the actual request and its response. // Returns false if unable to complete the request, causing caller to stall control endpoints. static bool process_setup_received(uint8_t rhport, tusb_control_request_t const * p_request) { @@ -996,113 +1105,7 @@ static bool process_setup_received(uint8_t rhport, tusb_control_request_t const return false; } - switch (p_request->bRequest) { //-V2520 - case TUSB_REQ_SET_ADDRESS: - // Depending on mcu, status phase could be sent either before or after changing device address, - // or even require stack to not response with status at all - // Therefore DCD must take full responsibility to response and include zlp status packet if needed. - dcd_set_address(rhport, (uint8_t) p_request->wValue); - _usbd_dev.addressed = 1; - break; - - case TUSB_REQ_GET_CONFIGURATION: { - uint8_t cfg_num = _usbd_dev.cfg_num; - tud_control_xfer(rhport, p_request, &cfg_num, 1); - } - break; - - case TUSB_REQ_SET_CONFIGURATION: { - uint8_t const cfg_num = (uint8_t) p_request->wValue; - - // Only process if new configure is different - if (_usbd_dev.cfg_num != cfg_num) { - if (_usbd_dev.cfg_num != 0) { - // already configured: need to clear all endpoints and driver first - TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num); - - dcd_sof_enable(rhport, false); - dcd_edpt_close_all(rhport); - - // close all drivers and current configured state except bus speed - const uint8_t speed = _usbd_dev.speed; - configuration_reset(rhport); - - _usbd_dev.speed = speed; // restore speed - } - - _usbd_dev.cfg_num = cfg_num; - - // Handle the new configuration - if (cfg_num == 0) { - tud_umount_cb(); - } else { - if (!process_set_config(rhport, cfg_num)) { - _usbd_dev.cfg_num = 0; - TU_ASSERT(false); - } - tud_mount_cb(); - } - } - - tud_control_status(rhport, p_request); - } - break; - - case TUSB_REQ_GET_DESCRIPTOR: - TU_VERIFY(process_get_descriptor(rhport, p_request)); - break; - - case TUSB_REQ_SET_FEATURE: - switch(p_request->wValue) { //-V2520 - case TUSB_REQ_FEATURE_REMOTE_WAKEUP: - TU_LOG_USBD(" Enable Remote Wakeup\r\n"); - // Host may enable remote wake up before suspending especially HID device - _usbd_dev.remote_wakeup_en = 1; - tud_control_status(rhport, p_request); - break; - - #if CFG_TUD_TEST_MODE - case TUSB_REQ_FEATURE_TEST_MODE: { - // Only handle the test mode if supported and valid - TU_VERIFY(0 == tu_u16_low(p_request->wIndex)); - - uint8_t const selector = tu_u16_high(p_request->wIndex); - TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE); - - ctrl_xfer->complete_cb = process_test_mode_cb; - tud_control_status(rhport, p_request); - break; - } - #endif - - // Stall unsupported feature selector - default: return false; - } - break; - - case TUSB_REQ_CLEAR_FEATURE: - // Only support remote wakeup for device feature - TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); - TU_LOG_USBD(" Disable Remote Wakeup\r\n"); - - // Host may disable remote wake up after resuming - _usbd_dev.remote_wakeup_en = 0; - tud_control_status(rhport, p_request); - break; - - case TUSB_REQ_GET_STATUS: { - // Device status bit mask - // - Bit 0: Self Powered TODO must invoke callback to get actual status - // - Bit 1: Remote Wakeup enabled - uint16_t status = (uint16_t)_usbd_dev.dev_state_bm; - tud_control_xfer(rhport, p_request, &status, 2); - break; - } - - // Unknown/Unsupported request - default: TU_BREAKPOINT(); return false; - } - break; + return process_std_device_request(rhport, p_request); //------------- Class/Interface Specific Request -------------// case TUSB_REQ_RCPT_INTERFACE: { -- cgit v1.3.1 From 9d68ed65f7200f155f01416b332c5e9f2340a8b2 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 11:19:26 +0700 Subject: refactor: replace `tu_edpt_state_t` struct with `uint8_t` and update all endpoint state handling methods and accesses --- src/class/printer/printer_device.c | 1 - src/common/tusb_private.h | 19 ++++------ src/device/usbd.c | 72 ++++++++++++++++---------------------- src/host/usbh.c | 23 ++++++------ src/tusb.c | 15 ++++---- 5 files changed, 54 insertions(+), 76 deletions(-) diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c index d2dc9b163..158455fc9 100644 --- a/src/class/printer/printer_device.c +++ b/src/class/printer/printer_device.c @@ -41,7 +41,6 @@ typedef struct { uint8_t itf_num; /*------------- From this point, data is not cleared by bus reset -------------*/ - tu_edpt_stream_t rx_stream; tu_edpt_stream_t tx_stream; diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index 91d213755..a31bf7b03 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -46,17 +46,10 @@ extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM]; // Endpoint //--------------------------------------------------------------------+ -enum { - TU_EDPT_STATE_BUSY = 0x01, - TU_EDPT_STATE_STALLED = 0x02, - TU_EDPT_STATE_CLAIMED = 0x04, -}; - -typedef struct TU_ATTR_PACKED { - volatile uint8_t busy : 1; - volatile uint8_t stalled : 1; - volatile uint8_t claimed : 1; -} tu_edpt_state_t; +// Endpoint state bits — manipulate the bare uint8_t with these masks. +#define TU_EDPT_STATE_BUSY 0x01u +#define TU_EDPT_STATE_STALLED 0x02u +#define TU_EDPT_STATE_CLAIMED 0x04u typedef struct { uint8_t hwid; // device: rhport, host: daddr @@ -92,10 +85,10 @@ bool tu_bind_driver_to_ep_itf(uint8_t driver_id, uint8_t ep2drv[][2], uint8_t it const uint8_t *p_desc, uint16_t desc_len); // Claim an endpoint with provided mutex -bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex); +bool tu_edpt_claim(volatile uint8_t* ep_state, osal_mutex_t mutex); // Release an endpoint with provided mutex -bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex); +bool tu_edpt_release(volatile uint8_t* ep_state, osal_mutex_t mutex); //--------------------------------------------------------------------+ // Endpoint Stream diff --git a/src/device/usbd.c b/src/device/usbd.c index 291319709..f8ec51762 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -149,7 +149,7 @@ typedef struct { uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each - tu_edpt_state_t ep_status[CFG_TUD_ENDPPOINT_MAX][2]; + volatile uint8_t ep_status[CFG_TUD_ENDPPOINT_MAX][2]; } usbd_device_t; static usbd_device_t _usbd_dev; @@ -711,7 +711,9 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { break; case DCD_EVENT_SETUP_RECEIVED: - TU_ASSERT(_usbd_queued_setup > 0,); + if (_usbd_queued_setup == 0) { + break; + } _usbd_queued_setup--; TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8); if (_usbd_queued_setup != 0) { @@ -723,18 +725,16 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { // But it is easier to set it every time instead of wasting time to check then set _usbd_dev.connected = 1; - // mark both in & out control as free - _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = 0; - _usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0; - _usbd_dev.ep_status[0][TUSB_DIR_IN].busy = 0; - _usbd_dev.ep_status[0][TUSB_DIR_IN].claimed = 0; + // reset ep state + _usbd_dev.ep_status[0][TUSB_DIR_OUT] = 0; + _usbd_dev.ep_status[0][TUSB_DIR_IN] = 0; // Process control request if (!process_setup_received(event.rhport, &event.setup_received)) { TU_LOG_USBD(" Stall EP0\r\n"); // Failed -> stall both control endpoint IN and OUT - dcd_edpt_stall(event.rhport, 0); - dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); + dcd_edpt_stall(event.rhport, TU_EP0_OUT); + dcd_edpt_stall(event.rhport, TU_EP0_IN); } break; @@ -746,8 +746,8 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) { TU_LOG_USBD("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); - _usbd_dev.ep_status[epnum][ep_dir].busy = 0; - _usbd_dev.ep_status[epnum][ep_dir].claimed = 0; + // Clear busy + claimed + _usbd_dev.ep_status[epnum][ep_dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); if (0 == epnum) { usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len); @@ -1202,7 +1202,7 @@ static bool process_setup_received(uint8_t rhport, tusb_control_request_t const ctrl_xfer->complete_cb = NULL; // skip ZLP status if driver already did that - if (!_usbd_dev.ep_status[0][TUSB_DIR_IN].busy) { + if (!(_usbd_dev.ep_status[0][TUSB_DIR_IN] & TU_EDPT_STATE_BUSY)) { tud_control_status(rhport, p_request); } } @@ -1441,15 +1441,15 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]); if (driver && driver->xfer_isr) { - _usbd_dev.ep_status[epnum][ep_dir].busy = 0; - _usbd_dev.ep_status[epnum][ep_dir].claimed = 0; + // Clear busy + claimed + _usbd_dev.ep_status[epnum][ep_dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); send = !driver->xfer_isr(event->rhport, ep_addr, (xfer_result_t) event->xfer_complete.result, event->xfer_complete.len); // xfer_isr() is deferred to xfer_cb(), revert busy/claimed status if (send) { - _usbd_dev.ep_status[epnum][ep_dir].busy = 1; - _usbd_dev.ep_status[epnum][ep_dir].claimed = 1; + // set busy + claimed + _usbd_dev.ep_status[epnum][ep_dir] |= (TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); } } } @@ -1539,9 +1539,7 @@ bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) { uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; - - return tu_edpt_claim(ep_state, _usbd_mutex); + return tu_edpt_claim(&_usbd_dev.ep_status[epnum][dir], _usbd_mutex); } bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) { @@ -1549,9 +1547,7 @@ bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) { uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir]; - - return tu_edpt_release(ep_state, _usbd_mutex); + return tu_edpt_release(&_usbd_dev.ep_status[epnum][dir], _usbd_mutex); } bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes, bool is_isr) { @@ -1571,18 +1567,17 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t #endif // Attempt to transfer on a busy endpoint, sound like an race condition ! - TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + TU_ASSERT((_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_BUSY) == 0); // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() // could return and USBD task can preempt and clear the busy - _usbd_dev.ep_status[epnum][dir].busy = 1; + _usbd_dev.ep_status[epnum][dir] |= TU_EDPT_STATE_BUSY; if (dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes, is_isr)) { return true; } else { // DCD error, mark endpoint as ready to allow next transfer - _usbd_dev.ep_status[epnum][dir].busy = 0; - _usbd_dev.ep_status[epnum][dir].claimed = 0; + _usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); TU_LOG_USBD("FAILED\r\n"); TU_BREAKPOINT(); return false; @@ -1603,19 +1598,18 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_ TU_LOG_USBD(" Queue FIFO EP %02X with %u bytes ... ", ep_addr, total_bytes); // Attempt to transfer on a busy endpoint, sound like a race condition ! - TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0); + TU_ASSERT((_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_BUSY) == 0); // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return // and usbd task can preempt and clear the busy - _usbd_dev.ep_status[epnum][dir].busy = 1; + _usbd_dev.ep_status[epnum][dir] |= TU_EDPT_STATE_BUSY; if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes, is_isr)) { TU_LOG_USBD("OK\r\n"); return true; } else { // DCD error, mark endpoint as ready to allow next transfer - _usbd_dev.ep_status[epnum][dir].busy = 0; - _usbd_dev.ep_status[epnum][dir].claimed = 0; + _usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); TU_LOG_USBD("failed\r\n"); TU_BREAKPOINT(); return false; @@ -1636,7 +1630,7 @@ bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) { uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - return _usbd_dev.ep_status[epnum][dir].busy; + return (_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_BUSY) != 0; } void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { @@ -1648,8 +1642,7 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { // only stalled if currently cleared TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr); dcd_edpt_stall(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = 1; - _usbd_dev.ep_status[epnum][dir].busy = 1; + _usbd_dev.ep_status[epnum][dir] |= (TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY); } void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { @@ -1661,8 +1654,7 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { // only clear if currently stalled TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr); dcd_edpt_clear_stall(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = 0; - _usbd_dev.ep_status[epnum][dir].busy = 0; + _usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~(TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY); } bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) { @@ -1671,7 +1663,7 @@ bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) { uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - return _usbd_dev.ep_status[epnum][dir].stalled; + return (_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_STALLED) != 0; } /** @@ -1691,9 +1683,7 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { uint8_t const dir = tu_edpt_dir(ep_addr); dcd_edpt_close(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = 0; - _usbd_dev.ep_status[epnum][dir].busy = 0; - _usbd_dev.ep_status[epnum][dir].claimed = 0; + _usbd_dev.ep_status[epnum][dir] = 0; #endif return; @@ -1738,9 +1728,7 @@ bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX); TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t)_usbd_dev.speed)); - _usbd_dev.ep_status[epnum][dir].stalled = 0; - _usbd_dev.ep_status[epnum][dir].busy = 0; - _usbd_dev.ep_status[epnum][dir].claimed = 0; + _usbd_dev.ep_status[epnum][dir] = 0; return dcd_edpt_iso_activate(rhport, desc_ep); #else (void) rhport; (void) desc_ep; diff --git a/src/host/usbh.c b/src/host/usbh.c index 8f80800e9..490724b02 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -140,7 +140,7 @@ typedef struct { uint8_t itf2drv[CFG_TUH_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) uint8_t ep2drv[CFG_TUH_ENDPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each - tu_edpt_state_t ep_status[CFG_TUH_ENDPOINT_MAX][2]; + volatile uint8_t ep_status[CFG_TUH_ENDPOINT_MAX][2]; #if CFG_TUH_API_EDPT_XFER // TODO array can be CFG_TUH_ENDPOINT_MAX-1 @@ -744,8 +744,8 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) { usbh_device_t* dev = get_device(event.dev_addr); TU_VERIFY(dev && dev->connected,); - dev->ep_status[epnum][ep_dir].busy = 0; - dev->ep_status[epnum][ep_dir].claimed = 0; + // clear busy and claimed + dev->ep_status[epnum][ep_dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); if (0 == epnum) { usbh_control_xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len); @@ -1016,10 +1016,10 @@ bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) { usbh_device_t* dev = get_device(daddr); TU_VERIFY(dev); - TU_VERIFY(dev->ep_status[epnum][dir].busy); // non-control skip if not busy + TU_VERIFY(dev->ep_status[epnum][dir] & TU_EDPT_STATE_BUSY); // non-control skip if not busy // abort then mark as ready and release endpoint hcd_edpt_abort_xfer(dev->bus_info.rhport, daddr, ep_addr); - dev->ep_status[epnum][dir].busy = false; + dev->ep_status[epnum][dir] &= (uint8_t) ~TU_EDPT_STATE_BUSY; // clear busy tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex); } @@ -1110,16 +1110,16 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t* bu uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir]; + volatile uint8_t* ep_state = &dev->ep_status[epnum][dir]; TU_LOG_USBH(" Queue EP %02X with %u bytes ... \r\n", ep_addr, total_bytes); // Attempt to transfer on a busy endpoint, sound like an race condition ! - TU_ASSERT(ep_state->busy == 0); + TU_ASSERT((*ep_state & TU_EDPT_STATE_BUSY) == 0); // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() // could return and USBH task can preempt and clear the busy - ep_state->busy = 1; + *ep_state |= TU_EDPT_STATE_BUSY; #if CFG_TUH_API_EDPT_XFER dev->ep_callback[epnum][dir].complete_cb = complete_cb; @@ -1130,9 +1130,8 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t* bu TU_LOG_USBH("OK\r\n"); return true; } else { - // HCD error, mark endpoint as ready to allow next transfer - ep_state->busy = 0; - ep_state->claimed = 0; + // HCD error, clear busy and claimed to allow next transfer + *ep_state &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); TU_LOG1("Failed\r\n"); // TU_BREAKPOINT(); return false; @@ -1178,7 +1177,7 @@ bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) { uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - return dev->ep_status[epnum][dir].busy; + return (dev->ep_status[epnum][dir] & TU_EDPT_STATE_BUSY) != 0; } //--------------------------------------------------------------------+ diff --git a/src/tusb.c b/src/tusb.c index 5e4422e41..5d656fb8c 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -224,32 +224,31 @@ uint8_t const* tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t by // Endpoint Helper for both Host and Device stack //--------------------------------------------------------------------+ -bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { +bool tu_edpt_claim(volatile uint8_t* ep_state, osal_mutex_t mutex) { (void) mutex; // pre-check to help reducing mutex lock - TU_VERIFY(ep_state->busy == 0); - TU_VERIFY(ep_state->claimed == 0); + TU_VERIFY((*ep_state & (TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED)) == 0); (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); // can only claim the endpoint if it is not busy and not claimed yet. - bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0); + bool const available = (*ep_state & (TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED)) == 0; if (available) { - ep_state->claimed = 1; + *ep_state |= TU_EDPT_STATE_CLAIMED; } (void) osal_mutex_unlock(mutex); return available; } -bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) { +bool tu_edpt_release(volatile uint8_t* ep_state, osal_mutex_t mutex) { (void) mutex; (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); // can only release the endpoint if it is claimed and not busy - bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0); + bool const ret = (*ep_state & (TU_EDPT_STATE_CLAIMED | TU_EDPT_STATE_BUSY)) == TU_EDPT_STATE_CLAIMED; if (ret) { - ep_state->claimed = 0; + *ep_state &= (uint8_t) ~TU_EDPT_STATE_CLAIMED; } (void) osal_mutex_unlock(mutex); -- cgit v1.3.1 From 2a8e659bb9422655236accd84e4d1c5e39383adc Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 12:12:45 +0700 Subject: hil_test: use non-blocking writes with timeout for printer tests --- test/hil/hil_test.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index e98bd5da7..f96d0d90a 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -30,6 +30,7 @@ import argparse import os import random import re +import select import sys import time import warnings @@ -923,18 +924,27 @@ def test_device_printer_to_cdc(board): ser.reset_input_buffer() # Test 1: Printer -> CDC with multiple sizes, write in random 1-64 byte chunks + LP_WRITE_TIMEOUT = 5.0 # seconds; firmware may stall draining the printer OUT endpoint for size in sizes: test_data = rand_ascii(size) ser.reset_input_buffer() rd = b'' offset = 0 - with open(lp_dev, 'wb') as lp: + lp_fd = os.open(lp_dev, os.O_WRONLY | os.O_NONBLOCK) + try: while offset < size: chunk_size = min(random.randint(1, 64), size - offset) - lp.write(test_data[offset:offset + chunk_size]) - lp.flush() + buf = test_data[offset:offset + chunk_size] + written = 0 + while written < len(buf): + _, wr, _ = select.select([], [lp_fd], [], LP_WRITE_TIMEOUT) + assert wr, f'Printer write timeout after {LP_WRITE_TIMEOUT}s (firmware not draining OUT endpoint)' + n = os.write(lp_fd, buf[written:]) + written += n rd += ser.read(chunk_size) offset += chunk_size + finally: + os.close(lp_fd) # read any remaining bytes (fullspeed devices may need extra time) while len(rd) < size: remaining = ser.read(size - len(rd)) -- cgit v1.3.1 From e954302c103b4fc6870c6914945e6da1f62f6423 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 13:39:39 +0700 Subject: fix usbd control to support wLength hack --- src/device/usbd.c | 17 ++++++++--------- src/device/usbd_pvt.h | 4 ---- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/device/usbd.c b/src/device/usbd.c index f8ec51762..0e58f70bf 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -945,8 +945,7 @@ static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * dri return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); } -// Process a standard request to the device recipient (extracted from -// process_setup_received for readability; GCC chooses to inline it). +// Process a standard request to the device recipient. static bool process_std_device_request(uint8_t rhport, tusb_control_request_t const * p_request) { switch (p_request->bRequest) { //-V2520 case TUSB_REQ_SET_ADDRESS: @@ -1068,6 +1067,8 @@ static bool process_setup_received(uint8_t rhport, tusb_control_request_t const ctrl_xfer->total_xferred = 0; ctrl_xfer->data_len = 0; ctrl_xfer->complete_cb = NULL; + + p_request = &ctrl_xfer->request; // re-direct request pointer to internal copy (modifiable for hacking) TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); // Vendor request @@ -1289,20 +1290,18 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const case TUSB_DESC_DEVICE: { TU_LOG_USBD(" Device\r\n"); - void* desc_device = (void*) (uintptr_t) tud_descriptor_device_cb(); + void *desc_device = (void *)(uintptr_t)tud_descriptor_device_cb(); TU_ASSERT(desc_device); // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. // This only happens with the very first get device descriptor and EP0 size = 8 or 16. if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && - ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) { + p_request->wLength > sizeof(tusb_desc_device_t)) { // Hack here: we modify the request length to prevent usbd_control response with zlp // since we are responding with 1 packet & less data than wLength. - tusb_control_request_t mod_request = *p_request; - mod_request.wLength = CFG_TUD_ENDPOINT0_SIZE; - - return tud_control_xfer(rhport, &mod_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); - }else { + ((tusb_control_request_t *)(uintptr_t)p_request)->wLength = CFG_TUD_ENDPOINT0_SIZE; + return tud_control_xfer(rhport, p_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); + } else { return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); } } diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h index 5f11ea481..be778f9af 100644 --- a/src/device/usbd_pvt.h +++ b/src/device/usbd_pvt.h @@ -130,10 +130,6 @@ void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en); bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in); void usbd_defer_func(osal_task_func_t func, void *param, bool in_isr); -#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL -void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback); -#endif - #ifdef __cplusplus } #endif -- cgit v1.3.1 From fc0747c2a7c04b25b640bfb5acce711fa39f8443 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 19:39:46 +0700 Subject: hil remove hub from pico native host test for now --- test/hil/tinyusb.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json index a3f7ff8bf..aed711f80 100644 --- a/test/hil/tinyusb.json +++ b/test/hil/tinyusb.json @@ -155,11 +155,6 @@ "tests": { "device": false, "host": true, "dual": false, "dev_attached": [ - { - "vid_pid": "1a86_55d4", - "serial": "52D2023934", - "is_cdc": true - }, { "vid_pid": "2008_2018", "serial": "O20070925A002746", -- cgit v1.3.1 From 77258a35ef2dcddef4f62bc51d31b9beef3b46d2 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 19:42:28 +0700 Subject: add default implementation for tuh_hid_report_received_cb() --- src/class/hid/hid_host.c | 33 ++++++++++----------------------- src/class/hid/hid_host.h | 2 +- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index 7935b84d3..fc7704258 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -74,44 +74,31 @@ static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT; // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ TU_ATTR_WEAK void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len) { - (void) dev_addr; - (void) idx; - (void) report_desc; - (void) desc_len; + (void) dev_addr; (void) idx; (void) report_desc; (void) desc_len; } TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx) { - (void) dev_addr; - (void) idx; + (void) dev_addr; (void) idx; +} + +TU_ATTR_WEAK void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report, uint16_t len) { + (void) dev_addr; (void) idx; (void) report; (void) len; } TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len) { - (void) dev_addr; - (void) idx; - (void) report; - (void) len; + (void) dev_addr; (void) idx; (void) report; (void) len; } TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) { - (void) dev_addr; - (void) idx; - (void) report_id; - (void) report_type; - (void) len; + (void) dev_addr; (void) idx; (void) report_id; (void) report_type; (void) len; } TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) { - (void) dev_addr; - (void) idx; - (void) report_id; - (void) report_type; - (void) len; + (void) dev_addr; (void) idx; (void) report_id; (void) report_type; (void) len; } TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) { - (void) dev_addr; - (void) idx; - (void) protocol; + (void) dev_addr; (void) idx; (void) protocol; } //--------------------------------------------------------------------+ diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h index 922848fc2..95ba859ad 100644 --- a/src/class/hid/hid_host.h +++ b/src/class/hid/hid_host.h @@ -140,7 +140,7 @@ bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx); bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void *report, uint16_t len); //--------------------------------------------------------------------+ -// Callbacks (Weak is optional) +// Callbacks (optional) //--------------------------------------------------------------------+ // Invoked when device with hid interface is mounted -- cgit v1.3.1 From 076fd79b4c16b7435060585c0337bc69133e073f Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 May 2026 22:13:14 +0700 Subject: Adjust RAM configuration for nrf54h20 to optimize memory usage in video examples --- hw/bsp/nrf/boards/nrf54h20dk/board.cmake | 3 ++- hw/bsp/nrf/boards/nrf54h20dk/board.mk | 5 +++++ hw/bsp/nrf/boards/nrf54lm20dk/board.cmake | 5 ++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/bsp/nrf/boards/nrf54h20dk/board.cmake b/hw/bsp/nrf/boards/nrf54h20dk/board.cmake index fca7a56d2..8095b59fe 100644 --- a/hw/bsp/nrf/boards/nrf54h20dk/board.cmake +++ b/hw/bsp/nrf/boards/nrf54h20dk/board.cmake @@ -1,7 +1,8 @@ set(MCU_VARIANT nrf54h20) function(update_board TARGET) - # temporarily, 54h20 has multiple sram sections + # 32 KB primary RAM is too tight for memory-heavy examples (e.g. video YUY2 + # framebuf). TODO: route static .bss to RAM00 (512 KB) and drop this. target_compile_definitions(${TARGET} PUBLIC CFG_EXAMPLE_VIDEO_READONLY ) diff --git a/hw/bsp/nrf/boards/nrf54h20dk/board.mk b/hw/bsp/nrf/boards/nrf54h20dk/board.mk index c49b605e8..3333a76f1 100644 --- a/hw/bsp/nrf/boards/nrf54h20dk/board.mk +++ b/hw/bsp/nrf/boards/nrf54h20dk/board.mk @@ -1,6 +1,11 @@ MCU_VARIANT = nrf54h20 CFLAGS += -DNRF54H20_XXAA +# 32 KB primary RAM is too tight for memory-heavy examples (e.g. video YUY2 +# framebuf). Match the CMake build (board.cmake) — TODO: route static .bss to +# RAM00 (512 KB) and drop this. +CFLAGS += -DCFG_EXAMPLE_VIDEO_READONLY + # enable max3421 host driver for this board MAX3421_HOST = 1 diff --git a/hw/bsp/nrf/boards/nrf54lm20dk/board.cmake b/hw/bsp/nrf/boards/nrf54lm20dk/board.cmake index 8c2b83346..e97b8822c 100644 --- a/hw/bsp/nrf/boards/nrf54lm20dk/board.cmake +++ b/hw/bsp/nrf/boards/nrf54lm20dk/board.cmake @@ -2,7 +2,6 @@ set(MCU_VARIANT nrf54lm20a_enga) set(JLINK_DEVICE NRF54LM20A_M33) function(update_board TARGET) - target_compile_definitions(${TARGET} PUBLIC - CFG_EXAMPLE_VIDEO_READONLY - ) + # No board-specific overrides needed — primary 256 KB RAM is plenty for + # memory-heavy examples (video YUY2 framebuf etc.). endfunction() -- cgit v1.3.1 From e557f94c721871246ee7ac0761d74e5f8794300f Mon Sep 17 00:00:00 2001 From: Zixun LI Date: Thu, 7 May 2026 11:17:57 +0200 Subject: revert RX buffer size as it's not related to issue Even if CFG_TUH_MIDI_RX_BUFSIZE=100*TUH_EPSIZE_BULK_MAX, calling tuh_midi_stream_read without a loop can return only one 4-byte packet and preventing subsequent transfer. Signed-off-by: Zixun LI --- src/class/midi/midi_host.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h index 000d815c4..8fdfd8966 100644 --- a/src/class/midi/midi_host.h +++ b/src/class/midi/midi_host.h @@ -38,12 +38,7 @@ extern "C" { // Class Driver Configuration //--------------------------------------------------------------------+ #ifndef CFG_TUH_MIDI_RX_BUFSIZE - // Default sized to 2x the bulk endpoint to absorb residue left in the FIFO - // when tuh_midi_stream_read() stops early on a cable-number transition. - // Sizing this equal to the endpoint packet size (the historical default) - // can cause the next bulk IN transfer to fail to queue silently, wedging - // the stream. See the drain-loop note on tuh_midi_stream_read() below. - #define CFG_TUH_MIDI_RX_BUFSIZE (2 * TUH_EPSIZE_BULK_MAX) + #define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MAX #endif #ifndef CFG_TUH_MIDI_TX_BUFSIZE -- cgit v1.3.1