summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-05-08 13:13:01 +0200
committerHiFiPhile <[email protected]>2026-05-08 13:13:08 +0200
commit21fbee1e63e3f756bb991ccb6477ec2b69f53eb8 (patch)
tree858a9d204a570a0a883d3c29a037d089ad48a0d6
parent5951d07ad9de9d05127348f110024ac08bf5e676 (diff)
parentd21fdd98f34241f52a01832e3c4133cd03a94d0a (diff)
Merge remote-tracking branch 'tinyusb/master' into ncm_packet_filter
Signed-off-by: HiFiPhile <[email protected]>
-rw-r--r--.gitignore30
-rw-r--r--examples/device/net_lwip_webserver/src/usb_descriptors.c169
-rw-r--r--hw/bsp/at32f402_405/family.c2
-rw-r--r--hw/bsp/at32f403a_407/family.c2
-rw-r--r--hw/bsp/at32f413/family.c2
-rw-r--r--hw/bsp/at32f415/family.c2
-rw-r--r--hw/bsp/at32f423/family.c2
-rw-r--r--hw/bsp/at32f425/family.c2
-rw-r--r--hw/bsp/at32f435_437/family.c2
-rw-r--r--hw/bsp/at32f45x/family.c2
-rw-r--r--hw/bsp/board.c2
-rw-r--r--hw/bsp/ch32v20x/family.c2
-rw-r--r--hw/bsp/ft9xx/family.c5
-rw-r--r--hw/bsp/gd32vf103/family.c2
-rw-r--r--hw/bsp/kinetis_k/family.c2
-rw-r--r--hw/bsp/mm32/family.c2
-rw-r--r--hw/bsp/nrf/boards/nrf54h20dk/board.cmake3
-rw-r--r--hw/bsp/nrf/boards/nrf54h20dk/board.mk5
-rw-r--r--hw/bsp/nrf/boards/nrf54lm20dk/board.cmake5
-rw-r--r--hw/bsp/pic32mz/family.c4
-rw-r--r--hw/bsp/rp2040/family.c2
-rw-r--r--hw/bsp/rp2040/family.cmake1
-rw-r--r--hw/bsp/stm32c0/family.c2
-rw-r--r--hw/bsp/stm32f0/family.c2
-rw-r--r--hw/bsp/stm32f1/family.c2
-rw-r--r--hw/bsp/stm32f2/family.c2
-rw-r--r--hw/bsp/stm32f3/family.c2
-rw-r--r--hw/bsp/stm32f4/family.c2
-rw-r--r--hw/bsp/stm32f7/family.c2
-rw-r--r--hw/bsp/stm32g0/family.c2
-rw-r--r--hw/bsp/stm32g4/family.c2
-rw-r--r--hw/bsp/stm32h5/family.c2
-rw-r--r--hw/bsp/stm32h7/family.c2
-rw-r--r--hw/bsp/stm32h7rs/family.c2
-rw-r--r--hw/bsp/stm32l0/family.c2
-rw-r--r--hw/bsp/stm32l4/family.c2
-rw-r--r--hw/bsp/stm32n6/family.c2
-rw-r--r--hw/bsp/stm32u0/family.c2
-rw-r--r--hw/bsp/stm32u5/family.c2
-rw-r--r--hw/bsp/stm32wb/family.c2
-rw-r--r--hw/bsp/xmc4000/family.c2
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/class/hid/hid_host.c33
-rw-r--r--src/class/hid/hid_host.h2
-rw-r--r--src/class/midi/midi_host.h7
-rw-r--r--src/class/net/ecm_rndis_device.c9
-rw-r--r--src/class/net/ncm_device.c5
-rw-r--r--src/class/net/net_device.h3
-rw-r--r--src/class/printer/printer_device.c1
-rw-r--r--src/common/tusb_private.h19
-rw-r--r--src/device/usbd.c501
-rw-r--r--src/device/usbd.h6
-rw-r--r--src/device/usbd_control.c220
-rw-r--r--src/device/usbd_pvt.h4
-rw-r--r--src/host/usbh.c23
-rw-r--r--src/tinyusb.mk1
-rw-r--r--src/tusb.c15
-rw-r--r--test/fuzz/rules.mk1
-rwxr-xr-xtest/hil/hil_test.py16
-rw-r--r--test/hil/tinyusb.json5
-rw-r--r--test/unit-test/CMakeLists.txt4
61 files changed, 609 insertions, 555 deletions
diff --git a/.gitignore b/.gitignore
index e324916a4..c11e51bb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,3 +57,33 @@ BrowseInfo
README_processed.rst
.worktrees
cmake-metrics/
+# Directories fetched by tools/get_deps.py - not to be committed
+lib/CMSIS_5/
+lib/CMSIS_6/
+lib/FreeRTOS-Kernel/
+lib/fatfs/
+lib/lwip/
+lib/sct_neopixel/
+lib/threadx/
+tools/linkermap/
+tools/uf2/
+hw/mcu/allwinner/
+hw/mcu/analog/
+hw/mcu/artery/
+hw/mcu/broadcom/
+hw/mcu/bridgetek/ft9xx/ft90x-sdk/
+hw/mcu/gd/
+hw/mcu/hpmicro/
+hw/mcu/infineon/
+hw/mcu/microchip/
+hw/mcu/mindmotion/
+hw/mcu/nordic/nrfx/
+hw/mcu/nuvoton/
+hw/mcu/nxp/
+hw/mcu/raspberry_pi/
+hw/mcu/renesas/
+hw/mcu/silabs/
+hw/mcu/sony/cxd56/spresense-exported-sdk/
+hw/mcu/st/
+hw/mcu/ti/
+hw/mcu/wch/
diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c
index 33b14d758..09090bb92 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,
@@ -144,57 +146,184 @@ 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, 64),
+};
+
+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,
+ 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, CFG_TUD_NET_ENDPOINT_SIZE),
+ ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 512),
};
-static const uint8_t ecm_configuration[] = {
+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,
- CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
+ 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.
- 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, (uint8_t)((uint8_t)NCM_NETWORK_CAPS_ETH_FILTER | (uint8_t)NCM_NETWORK_CAPS_NTB_INPUT_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, NCM capabilities.
+ TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
+ 64, CFG_TUD_NET_MTU, 50, (uint8_t)((uint8_t)NCM_NETWORK_CAPS_ETH_FILTER | (uint8_t)NCM_NETWORK_CAPS_NTB_INPUT_SIZE)),
};
+#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, NCM capabilities.
+ 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, (uint8_t)((uint8_t)NCM_NETWORK_CAPS_ETH_FILTER | (uint8_t)NCM_NETWORK_CAPS_NTB_INPUT_SIZE)),
+};
+#endif // highspeed
+
#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_arr[CONFIG_ID_COUNT] = {
#if CFG_TUD_ECM_RNDIS
- [CONFIG_ID_RNDIS] = rndis_configuration,
- [CONFIG_ID_ECM] = ecm_configuration
+
+static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = rndis_fs_configuration,
+ [CONFIG_ID_ECM] = ecm_fs_configuration
+};
+
+#if TUD_OPT_HIGH_SPEED
+static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = rndis_hs_configuration,
+ [CONFIG_ID_ECM] = ecm_hs_configuration
+};
+
+// Size array for each configuration
+static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = {
+ [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
- [CONFIG_ID_NCM] = ncm_configuration
+
+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
+};
+
+// Scratch buffer for other speed configuration (sized to hold the largest config)
+#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
+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 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;
+
+ // 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
+
// 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/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/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/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..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) {
@@ -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/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()
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/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/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
}
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/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
diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h
index b9ab0130d..8fdfd8966 100644
--- a/src/class/midi/midi_host.h
+++ b/src/class/midi/midi_host.h
@@ -150,6 +150,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
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
index 2d398b57c..643bcfbcd 100644
--- a/src/class/net/ecm_rndis_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -48,9 +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;
@@ -183,6 +184,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
@@ -365,8 +369,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 % CFG_TUD_NET_ENDPOINT_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 e16b0523f..336d8ad3e 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
@@ -353,7 +354,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 % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
+ uint16_t const ep_size = ncm_interface.ep_size;
+ if (xferred_bytes == 0 || (xferred_bytes & (ep_size-1)) != 0) {
return false;
}
@@ -925,6 +927,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;
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index e0d235ebe..332df09b3 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/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 da0ffb4c6..0e58f70bf 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;
@@ -136,12 +149,16 @@ 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;
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,8 @@ 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 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);
@@ -419,12 +437,6 @@ 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);
-
//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
@@ -459,17 +471,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
//--------------------------------------------------------------------+
@@ -609,9 +610,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);
@@ -646,7 +645,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) {
@@ -713,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) {
@@ -725,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_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);
- 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;
@@ -748,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);
@@ -809,25 +807,273 @@ 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;
+}
+
+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) {
+ 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) {
+ 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) {
+ // _usbd_dev.ctrl_xfer fields are pre-initialized at process_setup_received entry
+ (void) 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) {
+ // _usbd_dev.ctrl_xfer.request and reset fields are pre-initialized at process_setup_received entry
+ (void) request;
+ 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) {
+ 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;
+}
+
+// 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);
+ 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) {
+ 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) {
+ // 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
//--------------------------------------------------------------------+
// 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);
}
+// 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:
+ // 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_control_request(uint8_t rhport, tusb_control_request_t const * p_request) {
- usbd_control_set_complete_callback(NULL);
+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.
+ 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;
+
+ 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
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);
}
@@ -860,115 +1106,7 @@ static bool process_control_request(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.
- usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API
- dcd_set_address(rhport, (uint8_t) p_request->wValue);
- // skip tud_control_status()
- _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);
-
- usbd_control_set_complete_callback(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: {
@@ -1004,7 +1142,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: {
@@ -1062,10 +1200,10 @@ 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) {
+ if (!(_usbd_dev.ep_status[0][TUSB_DIR_IN] & TU_EDPT_STATE_BUSY)) {
tud_control_status(rhport, p_request);
}
}
@@ -1144,8 +1282,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 );
@@ -1153,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));
}
}
@@ -1305,15 +1440,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);
}
}
}
@@ -1403,9 +1538,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) {
@@ -1413,9 +1546,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) {
@@ -1435,18 +1566,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;
@@ -1467,19 +1597,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;
@@ -1500,7 +1629,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) {
@@ -1512,8 +1641,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) {
@@ -1525,8 +1653,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) {
@@ -1535,7 +1662,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;
}
/**
@@ -1555,9 +1682,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;
@@ -1602,9 +1727,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/device/usbd.h b/src/device/usbd.h
index 5a21c7039..473e697ac 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -1027,8 +1027,8 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
#define TUD_CDC_NCM_DESC_LEN (8+9+5+5+13+6+7+9+9+7+7)
// CDC-NCM 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, capability.
-#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize, _capability) \
+// 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, capability.
+#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize, _ep_notif_interval, _capability) \
/* 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), _capability, \
/* 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/src/device/usbd_control.c b/src/device/usbd_control.c
deleted file mode 100644
index b14d08a9c..000000000
--- a/src/device/usbd_control.c
+++ /dev/null
@@ -1,220 +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.
- */
-
-#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
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
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/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/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);
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/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))
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
@@ -156,11 +156,6 @@
"device": false, "host": true, "dual": false,
"dev_attached": [
{
- "vid_pid": "1a86_55d4",
- "serial": "52D2023934",
- "is_cdc": true
- },
- {
"vid_pid": "2008_2018",
"serial": "O20070925A002746",
"is_msc": true,
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"
)