summaryrefslogtreecommitdiff
path: root/examples/device
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-08-25 09:27:48 +0200
committerHiFiPHile <[email protected]>2026-08-25 09:27:48 +0200
commitdfac26a272fa7bbbca2050fbe9f1ca09008e548e (patch)
treeefbc53f8f2c1e5d9c7f38e5fef6d774a20053cec /examples/device
parente590b45fcf51f9ddace73178074e4fe6d691e319 (diff)
parent5c0e31cdabaf37f14e1f5e988a020abfc1000495 (diff)
Merge master updates into the UAC1 host branch
Bring the audio work onto the current host core and build files before applying the remaining review fixes. Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'examples/device')
-rw-r--r--examples/device/99-tinyusb-examples.rules (renamed from examples/device/99-tinyusb.rules)3
-rw-r--r--examples/device/CMakeLists.txt1
-rw-r--r--examples/device/audio_4_channel_mic/src/usb_descriptors.c15
-rw-r--r--examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c11
-rw-r--r--examples/device/audio_test/src/usb_descriptors.c15
-rw-r--r--examples/device/audio_test_freertos/src/usb_descriptors.c11
-rw-r--r--examples/device/audio_test_multi_rate/src/usb_descriptors.c11
-rw-r--r--examples/device/cdc_dual_ports/src/usb_descriptors.c11
-rw-r--r--examples/device/cdc_msc/src/usb_descriptors.c11
-rw-r--r--examples/device/cdc_msc_freertos/src/usb_descriptors.c11
-rw-r--r--examples/device/cdc_msc_throughput/src/usb_descriptors.c13
-rw-r--r--examples/device/cdc_uac2/src/tusb_config.h8
-rw-r--r--examples/device/cdc_uac2/src/usb_descriptors.c21
-rw-r--r--examples/device/dfu/src/usb_descriptors.c10
-rw-r--r--examples/device/dfu_runtime/src/usb_descriptors.c10
-rw-r--r--examples/device/dynamic_configuration/src/usb_descriptors.c13
-rw-r--r--examples/device/hid_boot_interface/src/usb_descriptors.c10
-rw-r--r--examples/device/hid_composite/src/usb_descriptors.c11
-rw-r--r--examples/device/hid_composite_freertos/src/usb_descriptors.c11
-rw-r--r--examples/device/hid_generic_inout/src/usb_descriptors.c11
-rw-r--r--examples/device/hid_multiple_interface/src/usb_descriptors.c11
-rw-r--r--examples/device/midi2_device/src/main.c13
-rw-r--r--examples/device/midi_test/src/usb_descriptors.c11
-rw-r--r--examples/device/midi_test_freertos/src/usb_descriptors.c11
-rw-r--r--examples/device/msc_dual_lun/src/usb_descriptors.c11
-rw-r--r--examples/device/mtp/src/usb_descriptors.c11
-rw-r--r--examples/device/net_lwip_webserver/skip.txt2
-rw-r--r--examples/device/net_lwip_webserver/src/usb_descriptors.c12
-rw-r--r--examples/device/printer_to_cdc/src/usb_descriptors.c12
-rw-r--r--examples/device/uac2_headset/src/usb_descriptors.c23
-rw-r--r--examples/device/uac2_speaker_fb/src/usb_descriptors.c11
-rw-r--r--examples/device/usbtest/CMakeLists.txt30
-rw-r--r--examples/device/usbtest/CMakePresets.json6
-rw-r--r--examples/device/usbtest/Makefile11
-rw-r--r--examples/device/usbtest/README.md94
-rw-r--r--examples/device/usbtest/skip.txt13
-rw-r--r--examples/device/usbtest/src/CMakeLists.txt4
-rw-r--r--examples/device/usbtest/src/main.c344
-rw-r--r--examples/device/usbtest/src/tusb_config.h151
-rw-r--r--examples/device/usbtest/src/usb_descriptors.c295
-rw-r--r--examples/device/usbtest/src/usb_descriptors.h84
-rw-r--r--examples/device/usbtmc/src/usb_descriptors.c11
-rw-r--r--examples/device/video_capture/src/usb_descriptors.c15
-rw-r--r--examples/device/video_capture_2ch/src/usb_descriptors.c22
-rw-r--r--examples/device/webusb_serial/README.md2
-rw-r--r--examples/device/webusb_serial/src/main.c2
-rw-r--r--examples/device/webusb_serial/src/usb_descriptors.c11
47 files changed, 1178 insertions, 263 deletions
diff --git a/examples/device/99-tinyusb.rules b/examples/device/99-tinyusb-examples.rules
index d306bada5..e7a399345 100644
--- a/examples/device/99-tinyusb.rules
+++ b/examples/device/99-tinyusb-examples.rules
@@ -1,5 +1,6 @@
+# udev rules for running the TinyUSB device examples as a non-root user.
# Copy this file to the location of your distribution's udev rules, for example on Ubuntu:
-# sudo cp 99-tinyusb.rules /etc/udev/rules.d/
+# sudo cp 99-tinyusb-examples.rules /etc/udev/rules.d/
# Then reload udev configuration by executing:
# sudo udevadm control --reload-rules
# sudo udevadm trigger
diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt
index 1432b36bb..02fcc0b87 100644
--- a/examples/device/CMakeLists.txt
+++ b/examples/device/CMakeLists.txt
@@ -35,6 +35,7 @@ set(EXAMPLE_LIST
printer_to_cdc
uac2_headset
uac2_speaker_fb
+ usbtest
usbtmc
video_capture
video_capture_2ch
diff --git a/examples/device/audio_4_channel_mic/src/usb_descriptors.c b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
index 6b9a9bbae..020bc934e 100644
--- a/examples/device/audio_4_channel_mic/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4001
//--------------------------------------------------------------------+
// Device Descriptors
@@ -99,6 +92,10 @@ enum
// Only EP3 is available for ISO
#define EPNUM_AUDIO 0x03
+#elif CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101
+ // ERR050101 (see CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101): an isochronous IN endpoint needs a number
+ // no other device on the bus uses
+ #define EPNUM_AUDIO 0x07
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
index 216cd062a..0afb3df0a 100644
--- a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4002
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/audio_test/src/usb_descriptors.c b/examples/device/audio_test/src/usb_descriptors.c
index cea4eb8d1..d16526116 100644
--- a/examples/device/audio_test/src/usb_descriptors.c
+++ b/examples/device/audio_test/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4003
//--------------------------------------------------------------------+
// Device Descriptors
@@ -99,6 +92,10 @@ enum
// Only EP3 is available for ISO
#define EPNUM_AUDIO 0x03
+#elif CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101
+ // ERR050101 (see CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101): an isochronous IN endpoint needs a number
+ // no other device on the bus uses
+ #define EPNUM_AUDIO 0x07
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_test_freertos/src/usb_descriptors.c b/examples/device/audio_test_freertos/src/usb_descriptors.c
index 37ebf84d3..709425c49 100644
--- a/examples/device/audio_test_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_test_freertos/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4004
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/audio_test_multi_rate/src/usb_descriptors.c b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
index b1f60dd10..008c8cd76 100644
--- a/examples/device/audio_test_multi_rate/src/usb_descriptors.c
+++ b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
@@ -28,15 +28,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4005
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/cdc_dual_ports/src/usb_descriptors.c b/examples/device/cdc_dual_ports/src/usb_descriptors.c
index adfd8cf9d..779221c0c 100644
--- a/examples/device/cdc_dual_ports/src/usb_descriptors.c
+++ b/examples/device/cdc_dual_ports/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4006
#define USB_VID 0xCafe
#define USB_BCD 0x0200
diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c
index 5dc80dee3..140ef2140 100644
--- a/examples/device/cdc_msc/src/usb_descriptors.c
+++ b/examples/device/cdc_msc/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4007
#define USB_VID 0xCafe
#define USB_BCD 0x0200
diff --git a/examples/device/cdc_msc_freertos/src/usb_descriptors.c b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
index f5b015051..8398f0365 100644
--- a/examples/device/cdc_msc_freertos/src/usb_descriptors.c
+++ b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4008
#define USB_VID 0xCafe
#define USB_BCD 0x0200
diff --git a/examples/device/cdc_msc_throughput/src/usb_descriptors.c b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
index 3b0ff6e17..dca5a65cf 100644
--- a/examples/device/cdc_msc_throughput/src/usb_descriptors.c
+++ b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
@@ -26,7 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-#define USB_PID (0x4000 | ((CFG_TUD_CDC) ? (1 << 0) : 0) | ((CFG_TUD_MSC) ? (1 << 1) : 0))
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4009
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -64,7 +65,15 @@ enum {
};
// Place bulk endpoints on EP>=8 for MAX32690 class parts (bigger FIFO, DPB-capable).
-#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
+ // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+ // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+ #define EPNUM_MSC_OUT 0x05
+ #define EPNUM_MSC_IN 0x85
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
#if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
// Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/device/cdc_uac2/src/tusb_config.h b/examples/device/cdc_uac2/src/tusb_config.h
index 358ff6747..f54a9b606 100644
--- a/examples/device/cdc_uac2/src/tusb_config.h
+++ b/examples/device/cdc_uac2/src/tusb_config.h
@@ -109,8 +109,10 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 2
// Audio format type I specifications
-#if defined(__RX__)
-#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000 // 16bit/48kHz is the best quality for Renesas RX
+#if defined(__RX__) || (CFG_TUSB_MCU == OPT_MCU_PY32F0)
+// RX : 16bit/48kHz is the best quality for Renesas RX
+// PY32F0 : 48kHz/16bit keeps ISO packets <= 98 B so both directions fit the fixed EP FIFOs (EP1 512 B, EP2 128 B)
+#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 48000
#else
#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE 96000 // 24bit/96kHz is the best quality for full-speed, high-speed is needed beyond this
#endif
@@ -123,7 +125,7 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX 2
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX 16
-#if defined(__RX__)
+#if defined(__RX__) || (CFG_TUSB_MCU == OPT_MCU_PY32F0)
// 8bit in 8bit slots
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX 1
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_TX 8
diff --git a/examples/device/cdc_uac2/src/usb_descriptors.c b/examples/device/cdc_uac2/src/usb_descriptors.c
index fdffc761e..648c56e71 100644
--- a/examples/device/cdc_uac2/src/usb_descriptors.c
+++ b/examples/device/cdc_uac2/src/usb_descriptors.c
@@ -29,15 +29,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x400a
//--------------------------------------------------------------------+
// Device Descriptors
@@ -117,6 +110,16 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_CDC_IN 0x85
#endif
+#elif CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101
+ // ERR050101 (see CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101): an isochronous IN endpoint needs a number
+ // no other device on the bus uses
+ #define EPNUM_AUDIO_IN 0x07
+ #define EPNUM_AUDIO_OUT 0x01
+
+ #define EPNUM_CDC_NOTIF 0x83
+ #define EPNUM_CDC_OUT 0x04
+ #define EPNUM_CDC_IN 0x84
+
#else
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_AUDIO_OUT 0x01
diff --git a/examples/device/dfu/src/usb_descriptors.c b/examples/device/dfu/src/usb_descriptors.c
index e4291be2d..5bfb32b2e 100644
--- a/examples/device/dfu/src/usb_descriptors.c
+++ b/examples/device/dfu/src/usb_descriptors.c
@@ -26,14 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x400b
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/dfu_runtime/src/usb_descriptors.c b/examples/device/dfu_runtime/src/usb_descriptors.c
index 8fa078da2..273566414 100644
--- a/examples/device/dfu_runtime/src/usb_descriptors.c
+++ b/examples/device/dfu_runtime/src/usb_descriptors.c
@@ -26,14 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x400c
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/dynamic_configuration/src/usb_descriptors.c b/examples/device/dynamic_configuration/src/usb_descriptors.c
index c4049414f..838052ea1 100644
--- a/examples/device/dynamic_configuration/src/usb_descriptors.c
+++ b/examples/device/dynamic_configuration/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x400d
// Configuration mode
// 0 : enumerated as CDC/MIDI. Board button is not pressed when enumerating
@@ -79,7 +72,7 @@ tusb_desc_device_t const desc_device_1 =
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
- .idProduct = USB_PID + 11, // should be different PID than desc0
+ .idProduct = USB_PID + 0x0100, // must differ from desc0's PID and stay outside the 0x40xx per-example space
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
diff --git a/examples/device/hid_boot_interface/src/usb_descriptors.c b/examples/device/hid_boot_interface/src/usb_descriptors.c
index b5c31a94a..4d5caa835 100644
--- a/examples/device/hid_boot_interface/src/usb_descriptors.c
+++ b/examples/device/hid_boot_interface/src/usb_descriptors.c
@@ -27,14 +27,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4))
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x400e
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/hid_composite/src/usb_descriptors.c b/examples/device/hid_composite/src/usb_descriptors.c
index 46e4b63f9..7f5f74c70 100644
--- a/examples/device/hid_composite/src/usb_descriptors.c
+++ b/examples/device/hid_composite/src/usb_descriptors.c
@@ -27,15 +27,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x400f
#define USB_VID 0xCafe
#define USB_BCD 0x0200
diff --git a/examples/device/hid_composite_freertos/src/usb_descriptors.c b/examples/device/hid_composite_freertos/src/usb_descriptors.c
index a745c17b5..a0464afae 100644
--- a/examples/device/hid_composite_freertos/src/usb_descriptors.c
+++ b/examples/device/hid_composite_freertos/src/usb_descriptors.c
@@ -27,15 +27,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4011
#define USB_VID 0xCafe
#define USB_BCD 0x0200
diff --git a/examples/device/hid_generic_inout/src/usb_descriptors.c b/examples/device/hid_generic_inout/src/usb_descriptors.c
index 93e718461..f179b74f7 100644
--- a/examples/device/hid_generic_inout/src/usb_descriptors.c
+++ b/examples/device/hid_generic_inout/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4012
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/hid_multiple_interface/src/usb_descriptors.c b/examples/device/hid_multiple_interface/src/usb_descriptors.c
index cd2d93c44..90aef6dd7 100644
--- a/examples/device/hid_multiple_interface/src/usb_descriptors.c
+++ b/examples/device/hid_multiple_interface/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4013
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/midi2_device/src/main.c b/examples/device/midi2_device/src/main.c
index 2c77652dc..f0162ad04 100644
--- a/examples/device/midi2_device/src/main.c
+++ b/examples/device/midi2_device/src/main.c
@@ -565,6 +565,19 @@ const char* tud_midi2_fb_name_cb(uint8_t itf, uint8_t fb_idx) {
return (fb_idx == 0) ? "Synth Out" : "Keys In";
}
+// Sent when the host asks for a Device Identity Notification (Endpoint
+// Discovery 'd' filter bit). Same four fields as the MIDI 1.0 Device Inquiry
+// reply; 0x7D is the prototyping SysEx ID, placed in the first of the three
+// manufacturer bytes.
+bool tud_midi2_device_identity_cb(uint8_t itf, tud_midi2_device_identity_t* identity) {
+ (void)itf;
+ identity->manufacturer = 0x7D0000;
+ identity->family = 0x0001;
+ identity->model = 0x0001;
+ identity->sw_revision = 0x00010000;
+ return true;
+}
+
//--------------------------------------------------------------------+
// Initial Setup - Program Change, CC, Per-Note Management
//--------------------------------------------------------------------+
diff --git a/examples/device/midi_test/src/usb_descriptors.c b/examples/device/midi_test/src/usb_descriptors.c
index 99c798ce1..fc7228c35 100644
--- a/examples/device/midi_test/src/usb_descriptors.c
+++ b/examples/device/midi_test/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4014
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/midi_test_freertos/src/usb_descriptors.c b/examples/device/midi_test_freertos/src/usb_descriptors.c
index 99c798ce1..bfdbc555e 100644
--- a/examples/device/midi_test_freertos/src/usb_descriptors.c
+++ b/examples/device/midi_test_freertos/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4015
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/msc_dual_lun/src/usb_descriptors.c b/examples/device/msc_dual_lun/src/usb_descriptors.c
index b328cf17f..5e036a8c4 100644
--- a/examples/device/msc_dual_lun/src/usb_descriptors.c
+++ b/examples/device/msc_dual_lun/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4016
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c
index 4c840560e..fefa8a239 100644
--- a/examples/device/mtp/src/usb_descriptors.c
+++ b/examples/device/mtp/src/usb_descriptors.c
@@ -26,15 +26,8 @@
#include "bsp/board_api.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] MTP | VENDOR | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) | PID_MAP(MTP, 5))
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4017
#define USB_VID 0xCafe
#define USB_BCD 0x0200
diff --git a/examples/device/net_lwip_webserver/skip.txt b/examples/device/net_lwip_webserver/skip.txt
index 5e5562087..53836b581 100644
--- a/examples/device/net_lwip_webserver/skip.txt
+++ b/examples/device/net_lwip_webserver/skip.txt
@@ -1,3 +1,4 @@
+mcu:APM32F0XX
mcu:CH32V103
mcu:CH32V20X
mcu:LPC11UXX
@@ -10,6 +11,7 @@ mcu:SAMD11
mcu:STM32L0
mcu:STM32F0
mcu:KINETIS_KL
+mcu:PY32F0
mcu:STM32H7RS
mcu:STM32N6
family:broadcom_64bit
diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c
index 09090bb92..85a6a420c 100644
--- a/examples/device/net_lwip_webserver/src/usb_descriptors.c
+++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c
@@ -27,16 +27,8 @@
#include "class/net/net_device.h"
#include "tusb.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID \
- (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) | \
- PID_MAP(ECM_RNDIS, 5) | PID_MAP(NCM, 5))
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x4018
// String Descriptor Index
enum {
diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.c b/examples/device/printer_to_cdc/src/usb_descriptors.c
index db7bfe97a..92cd2b6be 100644
--- a/examples/device/printer_to_cdc/src/usb_descriptors.c
+++ b/examples/device/printer_to_cdc/src/usb_descriptors.c
@@ -29,7 +29,7 @@
#include "usb_descriptors.h"
#define USB_VID 0xCafe
-#define USB_PID 0x4005
+#define USB_PID 0x4019
#define USB_BCD 0x0200
//--------------------------------------------------------------------+
@@ -67,7 +67,15 @@ uint8_t const *tud_descriptor_device_cb(void) {
//--------------------------------------------------------------------+
// Endpoint numbers
-#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
+ // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+ // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+ #define EPNUM_PRINTER_OUT 0x05
+ #define EPNUM_PRINTER_IN 0x85
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
#if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
// Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/device/uac2_headset/src/usb_descriptors.c b/examples/device/uac2_headset/src/usb_descriptors.c
index b554e7195..d8cdd768e 100644
--- a/examples/device/uac2_headset/src/usb_descriptors.c
+++ b/examples/device/uac2_headset/src/usb_descriptors.c
@@ -28,15 +28,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x401a
//--------------------------------------------------------------------+
// Device Descriptors
@@ -105,12 +98,24 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_AUDIO_OUT 0x0A
#define EPNUM_AUDIO_IN 0x0B
#define EPNUM_AUDIO_INT 0x01
+ #elif TU_CHECK_MCU(OPT_MCU_PY32F0)
+ // Speaker OUT (196 B) only fits EP1 (512 B FIFO); mic IN (98 B) fits EP2 (128 B)
+ #define EPNUM_AUDIO_OUT 0x01
+ #define EPNUM_AUDIO_IN 0x02
+ #define EPNUM_AUDIO_INT 0x03
#else
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_AUDIO_OUT 0x02
#define EPNUM_AUDIO_INT 0x03
#endif
+#elif CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101
+ // ERR050101 (see CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101): an isochronous IN endpoint needs a number
+ // no other device on the bus uses
+ #define EPNUM_AUDIO_IN 0x07
+ #define EPNUM_AUDIO_OUT 0x01
+ #define EPNUM_AUDIO_INT 0x02
+
#else
#define EPNUM_AUDIO_IN 0x01
#define EPNUM_AUDIO_OUT 0x01
diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.c b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
index f0c780e38..40a36cbf4 100644
--- a/examples/device/uac2_speaker_fb/src/usb_descriptors.c
+++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
@@ -28,15 +28,8 @@
#include "usb_descriptors.h"
#include "common_types.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VENDOR, 5) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x401b
//--------------------------------------------------------------------+
// Device Descriptors
diff --git a/examples/device/usbtest/CMakeLists.txt b/examples/device/usbtest/CMakeLists.txt
new file mode 100644
index 000000000..10414ede7
--- /dev/null
+++ b/examples/device/usbtest/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(usbtest C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${PROJECT_NAME} noos)
diff --git a/examples/device/usbtest/CMakePresets.json b/examples/device/usbtest/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/device/usbtest/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/device/usbtest/Makefile b/examples/device/usbtest/Makefile
new file mode 100644
index 000000000..035e90308
--- /dev/null
+++ b/examples/device/usbtest/Makefile
@@ -0,0 +1,11 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/usbtest/README.md b/examples/device/usbtest/README.md
new file mode 100644
index 000000000..8fcc4ad36
--- /dev/null
+++ b/examples/device/usbtest/README.md
@@ -0,0 +1,94 @@
+# usbtest
+
+Device-side peer of the Linux kernel USB test pair:
+
+- `usbtest.ko` — host kernel module (`drivers/usb/misc/usbtest.c`) containing ~30 numbered
+ test cases over bulk/control/interrupt/isochronous transfers.
+- `testusb` — userspace dispatcher (`tools/usb/testusb.c`) that tells the module which case
+ to run via usbfs ioctl.
+
+This example implements the Gadget-Zero style *source/sink* protocol on a vendor-specific
+interface so the whole battery can exercise TinyUSB device controller drivers:
+
+- bulk IN = infinite source (usbtest pattern 0: all zeros)
+- bulk OUT = infinite sink (data discarded)
+
+## Tiers
+
+The firmware advertises its capability tier in `bcdDevice` (`0x01TT`); the host script picks
+the matching test battery automatically.
+
+| Tier | Capability | usbtest cases |
+|------|------------|---------------|
+| 1 | bulk source/sink | 0, 9, 10, 1–8, 11, 12, 24, 13, 29, 17–20, 27, 28 |
+| 2 | + vendor control `0x5b`/`0x5c` (ctrl_out) | + 14, 21 |
+| 3 | + interrupt source/sink | + 25, 26 |
+| 4 | + isochronous source/sink | + 15, 16, 22, 23 |
+
+This example implements all four tiers using the vendor class with the interrupt
+(`CFG_TUD_VENDOR_EP_INT_OUT/IN`) and isochronous (`CFG_TUD_VENDOR_EP_ISO_OUT/IN`)
+endpoint pairs and altsetting support (`CFG_TUD_VENDOR_ALT_SETTINGS`): alt 0
+carries no endpoints, alt 1 the full source/sink set, per USB 2.0 5.6.3 (the host
+usbtest driver selects alt 1 itself).
+
+## Test cases
+
+Directions are from the host's point of view: *write* = host→device (OUT endpoint, device
+sinks and discards), *read* = device→host (IN endpoint, device sources zeros and the host
+verifies every byte). All checking happens host-side in `usbtest.ko`; a case fails on a data
+mismatch, an unexpected short packet/STALL, or a timeout. What each case stresses on the
+device/DCD side:
+
+| # | Name | What it does / what it exercises |
+|---|------|----------------------------------|
+| 0 | NOP | ioctl round-trip sanity, no USB traffic — proves the interface bound with the right capability profile |
+| 9 | ch9 subset | chapter-9 standard control requests (GET_DESCRIPTOR, GET_STATUS, SET/CLEAR_FEATURE, SET_INTERFACE, …) — the EP0 state machine, incl. status stages and ZLPs |
+| 10 | queued control | many control URBs in flight at once — EP0 under sustained back-to-back SETUPs |
+| 1 / 2 | bulk write / read | plain OUT sink / IN source streams of whole max-size packets — FIFO handling, multi-packet transfers |
+| 3 / 4 | bulk write / read vary | same with transfer sizes varying per URB — short packets and packet-boundary edge cases |
+| 5–8 | bulk sg write/read (+vary) | scatter-gather queued URBs — continuous packet pressure with no inter-URB gap; classic overflow/babble catcher |
+| 11 / 12 | unlink reads / writes | URBs submitted then cancelled mid-flight — the device keeps streaming while the host aborts; DCD abort/cleanup paths |
+| 24 | unlink queued writes | unlink from a deep OUT queue — same, under queue pressure |
+| 13 | ep halt set/clear | SET_FEATURE(ENDPOINT_HALT), verify the endpoint really STALLs, then CLEAR_FEATURE and verify traffic resumes at DATA0 — stall must abort an armed transfer (and flush any loaded FIFO) |
+| 29 | toggle clear | CLEAR_FEATURE(HALT) on a **non-halted** endpoint mid-traffic, purely to reset the data toggle — the DCD must reset DATA0 *without* disarming the queued transfer (historically the most common per-DCD bug in this battery) |
+| 17 / 18 | bulk write / read unaligned | bulk streams from oddly-offset host buffers — host DMA-alignment path; the device sees normal traffic |
+| 19 / 20 | bulk write / read premapped | bulk streams using host pre-mapped DMA buffers — another host memory path |
+| 27 / 28 | bulk write / read perf | sustained maximum-throughput streams, reported in MB/s — real-time FIFO servicing under load |
+| 14 | ctrl_out write/read | vendor EP0 request `0x5b` stores wLength bytes, `0x5c` reads them back, sizes varying — multi-packet control-OUT data stages and buffer persistence across requests |
+| 21 | ctrl_out unaligned | same from odd host buffer offsets |
+| 25 / 26 | int write / read | interrupt OUT sink / IN source at the descriptor's polling interval — interrupt endpoint arming and completion |
+| 15 / 16 | iso write / read | isochronous OUT sink / IN source, one packet per (micro)frame with per-packet status — no handshake/retry, DATA0-only; the IN source must re-arm fast enough to make every frame deadline |
+| 22 / 23 | iso write / read unaligned | same from odd host buffer offsets |
+
+Per-case iteration counts and sizes are chosen by `test/hil/usbtest.py` for the negotiated
+speed (see its `PARAMS` table); the authoritative case implementations live in the kernel's
+`drivers/usb/misc/usbtest.c`.
+
+## Running
+
+Use the host script (handles driver binding, per-case parameters, result parsing):
+
+```bash
+python3 test/hil/usbtest.py --serial <board-uid>
+```
+
+Requirements on the host: `usbtest` kernel module (`CONFIG_USB_TEST`, `modprobe usbtest`),
+the `testusb` binary built from kernel `tools/usb/testusb.c`, and sudo (usbfs ioctls +
+driver bind/unbind).
+
+Manual runs are possible but beware `testusb` defaults: always pass explicit `-s`/`-v`
+values that are multiples of 512 — the device streams whole max-size packets, so a
+non-packet-aligned read length overflows (`-EOVERFLOW`), and never run bare `testusb -a`
+(the default parameter set includes cases with invalid parameters and hour-long runtimes
+at full speed).
+
+```bash
+# bind: MUST use the 5-field form referencing Gadget Zero (0525:a4a0) so the
+# dynamic id inherits its capability profile. A plain "cafe 4010" id leaves
+# driver_info NULL, which usbtest_probe() dereferences -> kernel oops.
+sudo modprobe usbtest
+echo "cafe 4010 0 0525 a4a0" | sudo tee /sys/bus/usb/drivers/usbtest/new_id
+# example: bulk write/read
+sudo testusb -D /dev/bus/usb/<BBB>/<DDD> -t 1 -c 128 -s 1024 -v 512
+sudo testusb -D /dev/bus/usb/<BBB>/<DDD> -t 2 -c 128 -s 1024 -v 512
+```
diff --git a/examples/device/usbtest/skip.txt b/examples/device/usbtest/skip.txt
new file mode 100644
index 000000000..792404fe4
--- /dev/null
+++ b/examples/device/usbtest/skip.txt
@@ -0,0 +1,13 @@
+mcu:MSP430x5xx
+mcu:NUC121
+mcu:SAMD11
+# DCD has no isochronous support (dcd_edpt_iso_alloc refuses), tier-4 cannot enumerate:
+mcu:CXD56
+mcu:FT90X
+mcu:NUC100
+mcu:NUC120
+mcu:NUC505
+mcu:PIC32MZ
+mcu:SAMG
+mcu:SAMX7X
+mcu:VALENTYUSB_EPTRI
diff --git a/examples/device/usbtest/src/CMakeLists.txt b/examples/device/usbtest/src/CMakeLists.txt
new file mode 100644
index 000000000..cef2b46ee
--- /dev/null
+++ b/examples/device/usbtest/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+# This file is for ESP-IDF only
+idf_component_register(SRCS "main.c" "usb_descriptors.c"
+ INCLUDE_DIRS "."
+ REQUIRES boards tinyusb_src)
diff --git a/examples/device/usbtest/src/main.c b/examples/device/usbtest/src/main.c
new file mode 100644
index 000000000..e57a90161
--- /dev/null
+++ b/examples/device/usbtest/src/main.c
@@ -0,0 +1,344 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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.
+ *
+ */
+
+/* Device-side peer of the Linux kernel host test driver drivers/usb/misc/usbtest.c
+ * (driven from userspace by tools/usb/testusb.c). Implements the Gadget-Zero
+ * style source/sink protocol on a vendor interface (alt 0 = no endpoints,
+ * alt 1 = full set, selected by the host usbtest driver):
+ * - bulk/interrupt/isochronous IN = infinite source (pattern 0: all zeros)
+ * - bulk/interrupt/isochronous OUT = infinite sink (data discarded)
+ * - EP0 0x5b/0x5c = control write then read-back (ctrl_out tests)
+ * See examples/device/usbtest/README.md and test/hil/usbtest.py for usage.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "usb_descriptors.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+/* Blink pattern
+ * - 250 ms : device not mounted
+ * - 1000 ms : device mounted
+ */
+enum {
+ BLINK_NOT_MOUNTED = 250,
+ BLINK_MOUNTED = 1000,
+};
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+
+// Source data, all zeros = usbtest pattern 0. Sizes are a multiple of the
+// endpoint max packet size: transfers are always whole packets, never an
+// unintended short packet or ZLP.
+static uint8_t const tx_chunk[CFG_TUD_VENDOR_TX_EPSIZE];
+static uint8_t const int_tx_chunk[USBTEST_INT_EP_MPS];
+#if USBTEST_TIER >= 4
+static uint8_t const iso_tx_chunk[USBTEST_ISO_EP_MPS];
+#endif
+
+// Interrupt/iso submit one packet per (micro)frame, sized to the NEGOTIATED speed's mps — a
+// high-speed build enumerated at full speed must submit the FS length, not the HS-capacity buffer
+// size (bulk is exempt: it streams multi-packet transfers). See usb_descriptors.h.
+static inline uint16_t usbtest_int_len(void) {
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? USBTEST_INT_EP_MPS_HS : USBTEST_INT_EP_MPS_FS;
+}
+#if USBTEST_TIER >= 4
+static inline uint16_t usbtest_iso_len(void) {
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? USBTEST_ISO_EP_MPS_HS : USBTEST_ISO_EP_MPS_FS;
+}
+#endif
+
+//------------- prototypes -------------//
+void led_blinking_task(void* param);
+void usbtest_task(void* param);
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+void freertos_init(void);
+#endif
+
+/*------------- MAIN -------------*/
+int main(void) {
+ board_init();
+
+ // If using FreeRTOS: create blinky, tinyusb device, and usbtest source/sink tasks
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ freertos_init();
+#else
+ // init device stack on configured roothub port
+ tusb_rhport_init_t dev_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_AUTO
+ };
+ tusb_init(BOARD_TUD_RHPORT, &dev_init);
+
+ board_init_after_tusb();
+
+ while (1) {
+ tud_task(); // tinyusb device task
+ usbtest_task(NULL);
+ led_blinking_task(NULL);
+ }
+#endif
+}
+
+//--------------------------------------------------------------------+
+// Source/sink pumps
+//--------------------------------------------------------------------+
+
+// Polling keeps all four endpoints armed and self-heals after endpoint halt
+// (set/clear feature tests): stall marks the endpoint busy so the calls fail
+// quietly until the host clears the halt, then the next tick re-arms.
+static void usbtest_pump(void) {
+ if (tud_vendor_mounted()) {
+ tud_vendor_read_xfer(); // bulk sink: arm/re-arm, quiet fail if armed or halted
+ if (tud_vendor_write_available()) { // 0 while bulk IN is busy or halted
+ tud_vendor_write(tx_chunk, sizeof(tx_chunk));
+ }
+
+ tud_vendor_int_read_xfer(); // interrupt sink
+ if (tud_vendor_int_write_available()) {
+ tud_vendor_int_write(int_tx_chunk, usbtest_int_len());
+ }
+
+#if USBTEST_TIER >= 4
+ tud_vendor_iso_read_xfer(); // isochronous sink
+ if (tud_vendor_iso_write_available()) {
+ tud_vendor_iso_write(iso_tx_chunk, usbtest_iso_len());
+ }
+#endif
+ }
+}
+
+void usbtest_task(void* param) {
+ (void) param;
+ #if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+ usbtest_pump();
+ vTaskDelay(1); // yield; tx/rx completion callbacks keep the pipes saturated between polls
+ }
+ #else
+ usbtest_pump(); // called from the main loop, one tick per call
+ #endif
+}
+
+// Invoked when received data from host: discard and immediately re-arm
+void tud_vendor_rx_cb(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
+ (void) idx;
+ (void) buffer;
+ (void) bufsize;
+ tud_vendor_read_xfer();
+}
+
+// Invoked when last bulk tx transfer finished: keep the source saturated
+void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) {
+ (void) idx;
+ (void) sent_bytes;
+ tud_vendor_write(tx_chunk, sizeof(tx_chunk));
+}
+
+// Interrupt pair: same discard/refill pumps as bulk
+void tud_vendor_int_rx_cb(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
+ (void) idx;
+ (void) buffer;
+ (void) bufsize;
+ tud_vendor_int_read_xfer();
+}
+
+void tud_vendor_int_tx_cb(uint8_t idx, uint32_t sent_bytes) {
+ (void) idx;
+ (void) sent_bytes;
+ tud_vendor_int_write(int_tx_chunk, usbtest_int_len());
+}
+
+// Isochronous pair: same discard/refill pumps; a completion may be a missed
+// frame, re-arm regardless
+#if USBTEST_TIER >= 4
+void tud_vendor_iso_rx_cb(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
+ (void) idx;
+ (void) buffer;
+ (void) bufsize;
+ tud_vendor_iso_read_xfer();
+}
+
+void tud_vendor_iso_tx_cb(uint8_t idx, uint32_t sent_bytes) {
+ (void) idx;
+ (void) sent_bytes;
+ tud_vendor_iso_write(iso_tx_chunk, usbtest_iso_len());
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Vendor control requests (EP0)
+//--------------------------------------------------------------------+
+
+// Control write/read-back for the ctrl_out tests (14/21), same protocol as
+// Gadget Zero: 0x5b stores the host's wLength bytes, 0x5c returns them.
+static uint8_t ctrl_buf[1024];
+
+// Invoked on vendor control transfers, and by usbd for forwarded standard
+// endpoint requests (halt set/clear) whose return value it ignores — return
+// false for anything that is not a supported vendor request.
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
+ if (request->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) {
+ return false;
+ }
+
+ switch (request->bRequest) {
+ case 0x5b: // control WRITE: receive wLength bytes into ctrl_buf
+ TU_VERIFY(request->bmRequestType_bit.direction == TUSB_DIR_OUT);
+ TU_VERIFY(request->wValue == 0 && request->wIndex == 0);
+ TU_VERIFY(request->wLength <= sizeof(ctrl_buf));
+ if (stage == CONTROL_STAGE_SETUP) {
+ return tud_control_xfer(rhport, request, ctrl_buf, request->wLength);
+ }
+ return true; // DATA/ACK: payload already landed in ctrl_buf
+
+ case 0x5c: // control READ: send back the previously written bytes
+ TU_VERIFY(request->bmRequestType_bit.direction == TUSB_DIR_IN);
+ TU_VERIFY(request->wValue == 0 && request->wIndex == 0);
+ TU_VERIFY(request->wLength <= sizeof(ctrl_buf));
+ if (stage == CONTROL_STAGE_SETUP) {
+ return tud_control_xfer(rhport, request, ctrl_buf, request->wLength);
+ }
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+//--------------------------------------------------------------------+
+// Device callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when device is mounted
+void tud_mount_cb(void) {
+ blink_interval_ms = BLINK_MOUNTED;
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void) {
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+}
+
+//--------------------------------------------------------------------+
+// BLINKING TASK
+//--------------------------------------------------------------------+
+void led_blinking_task(void* param) {
+ (void) param;
+ static uint32_t start_ms = 0;
+ static bool led_state = false;
+
+ while (1) {
+ #if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
+ #else
+ // Blink every interval ms
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
+ return; // not enough time
+ }
+ #endif
+
+ start_ms += blink_interval_ms;
+ board_led_write(led_state);
+ led_state = 1 - led_state; // toggle
+ }
+}
+
+//--------------------------------------------------------------------+
+// FreeRTOS
+//--------------------------------------------------------------------+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+
+#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
+#define USBTEST_STACK_SIZE (configMINIMAL_STACK_SIZE*2)
+
+#ifdef ESP_PLATFORM
+ #define USBD_STACK_SIZE 4096
+ int main(void);
+ void app_main(void) {
+ main();
+ }
+#else
+ // Increase stack size when debug log is enabled
+ #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+#endif
+
+// static task allocation
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t blinky_stack[BLINKY_STACK_SIZE];
+StaticTask_t blinky_taskdef;
+
+StackType_t usb_device_stack[USBD_STACK_SIZE];
+StaticTask_t usb_device_taskdef;
+
+StackType_t usbtest_stack[USBTEST_STACK_SIZE];
+StaticTask_t usbtest_taskdef;
+#endif
+
+// USB Device Driver task: processes all usb events and invokes callbacks
+void usb_device_task(void* param) {
+ (void) param;
+
+ // init device stack on configured roothub port. Must be called after the
+ // scheduler starts: the USB IRQ handler uses RTOS queue APIs.
+ tusb_rhport_init_t dev_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_AUTO
+ };
+ tusb_init(BOARD_TUD_RHPORT, &dev_init);
+
+ board_init_after_tusb();
+
+ // RTOS forever loop
+ while (1) {
+ tud_task(); // put thread to waiting state until there is a new event
+ }
+}
+
+void freertos_init(void) {
+ #if configSUPPORT_STATIC_ALLOCATION
+ xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
+ xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
+ xTaskCreateStatic(usbtest_task, "usbtest", USBTEST_STACK_SIZE, NULL, configMAX_PRIORITIES-2, usbtest_stack, &usbtest_taskdef);
+ #else
+ xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
+ xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, NULL);
+ xTaskCreate(usbtest_task, "usbtest", USBTEST_STACK_SIZE, NULL, configMAX_PRIORITIES-2, NULL);
+ #endif
+
+ // only start scheduler for non-espressif mcu (espressif starts it in startup code)
+ #ifndef ESP_PLATFORM
+ vTaskStartScheduler();
+ #endif
+}
+#endif
diff --git a/examples/device/usbtest/src/tusb_config.h b/examples/device/usbtest/src/tusb_config.h
new file mode 100644
index 000000000..dda131ae2
--- /dev/null
+++ b/examples/device/usbtest/src/tusb_config.h
@@ -0,0 +1,151 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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.
+ *
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_TUD_RHPORT
+#define BOARD_TUD_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUD_MAX_SPEED
+#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+// Espressif IDF requires "freertos/" prefix in include path
+#ifdef ESP_PLATFORM
+#define CFG_TUSB_OS_INC_PATH freertos/
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC 0
+#define CFG_TUD_MSC 0
+#define CFG_TUD_HID 0
+#define CFG_TUD_MIDI 0
+#define CFG_TUD_VENDOR 1
+
+// Non-buffered mode: every transfer is submitted with an exact length so the
+// host never sees an unexpected short packet or ZLP mid-transfer, which the
+// usbtest data-integrity cases treat as failure.
+#define CFG_TUD_VENDOR_RX_BUFSIZE 0
+#define CFG_TUD_VENDOR_TX_BUFSIZE 0
+
+// App re-arms RX itself: required to recover the sink after halt tests
+// (SET_FEATURE/CLEAR_FEATURE endpoint halt) where no completion ever fires.
+#define CFG_TUD_VENDOR_RX_MANUAL_XFER 1
+
+// Multi-packet IN transfers; must be a multiple of bulk MPS at both speeds (64/512).
+// LPC11/13 (ip3511 FS) keep endpoint buffers in a dedicated 2 KB USB RAM: a 2048 B bulk epbuf
+// overflows it once the int/iso buffers join, so those parts use 512 (= 8 FS packets).
+#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX)
+#define CFG_TUD_VENDOR_TX_EPSIZE 512
+#else
+#define CFG_TUD_VENDOR_TX_EPSIZE 2048
+#endif
+
+// Interrupt IN/OUT source/sink pair (usbtest cases 25/26). Buffer sizes track the
+// per-speed endpoint max packet size (see usb_descriptors.c) so full-speed builds
+// don't over-allocate the scarce USB DMA section.
+#define CFG_TUD_VENDOR_EP_INT_OUT 1
+#define CFG_TUD_VENDOR_EP_INT_IN 1
+#define CFG_TUD_VENDOR_EP_INT_OUT_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_VENDOR_EP_INT_IN_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// Isochronous IN/OUT source/sink pair (usbtest cases 15/16/22/23), placed in
+// altsetting 1: alt 0 has no endpoints so no iso bandwidth is claimed by default
+#define CFG_TUD_VENDOR_EP_ISO_OUT 1
+#define CFG_TUD_VENDOR_EP_ISO_IN 1
+#define CFG_TUD_VENDOR_EP_ISO_OUT_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
+#define CFG_TUD_VENDOR_EP_ISO_IN_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
+#define CFG_TUD_VENDOR_ALT_SETTINGS 1
+
+// CH32V20X fsdev port has only 512 B PMA and single-buffered iso can't keep the iso IN endpoint
+// fed under load. Double-buffer iso; the descriptor drops iso mps to 32 there so 2x32 = 64 B/ep
+// keeps the same PMA budget (see usb_descriptors.h). Other fsdev parts have room and stay single.
+#if CFG_TUSB_MCU == OPT_MCU_CH32V20X
+#define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 1
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/usbtest/src/usb_descriptors.c b/examples/device/usbtest/src/usb_descriptors.c
new file mode 100644
index 000000000..8453885c4
--- /dev/null
+++ b/examples/device/usbtest/src/usb_descriptors.c
@@ -0,0 +1,295 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "usb_descriptors.h"
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0x00, // per-interface class
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = 0xCafe,
+ .idProduct = 0x4010,
+ .bcdDevice = 0x0100 | USBTEST_TIER,
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01
+};
+
+// Invoked when received GET DEVICE DESCRIPTOR
+uint8_t const* tud_descriptor_device_cb(void) {
+ return (uint8_t const*) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+
+// Interface must be number 0: testusb -D issues its ioctls against interface 0
+enum {
+ ITF_NUM_VENDOR = 0,
+ ITF_NUM_TOTAL
+};
+
+// Vendor interface, Gadget-Zero style altsettings: alt 0 carries no endpoints (an
+// isochronous endpoint must not claim bandwidth in the default altsetting, USB 2.0
+// 5.6.3), alt 1 carries bulk + interrupt (+ isochronous IN/OUT at tier 4). The host
+// usbtest driver skips altsettings without pipes and selects alt 1 itself. No TUD_
+// macro covers this layout, hand-rolled.
+#if USBTEST_TIER >= 4
+ #define USBTEST_EP_COUNT 6
+ #define USBTEST_ISO_EPS(_isoout, _isoin, _iso_mps, _iso_interval) \
+ ,7, TUSB_DESC_ENDPOINT, _isoout, (uint8_t)(TUSB_XFER_ISOCHRONOUS | (uint8_t)(TUSB_ISO_EP_ATT_ASYNCHRONOUS)), U16_TO_U8S_LE(_iso_mps), _iso_interval,\
+ 7, TUSB_DESC_ENDPOINT, _isoin, (uint8_t)(TUSB_XFER_ISOCHRONOUS | (uint8_t)(TUSB_ISO_EP_ATT_ASYNCHRONOUS)), U16_TO_U8S_LE(_iso_mps), _iso_interval
+#else
+ #define USBTEST_EP_COUNT 4
+ #define USBTEST_ISO_EPS(_isoout, _isoin, _iso_mps, _iso_interval)
+#endif
+#define USBTEST_DESC_LEN (9 + 9 + USBTEST_EP_COUNT*7)
+#define USBTEST_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _bulk_mps, _intout, _intin, _int_mps, _int_interval, _isoout, _isoin, _iso_mps, _iso_interval) \
+ /* alt 0: zero bandwidth, no endpoints */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\
+ /* alt 1: full source/sink set */\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 1, USBTEST_EP_COUNT, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\
+ 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_mps), 0,\
+ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_mps), 0,\
+ 7, TUSB_DESC_ENDPOINT, _intout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_int_mps), _int_interval,\
+ 7, TUSB_DESC_ENDPOINT, _intin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_int_mps), _int_interval\
+ USBTEST_ISO_EPS(_isoout, _isoin, _iso_mps, _iso_interval)
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + USBTEST_DESC_LEN)
+
+#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
+ // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+ // 0 control, 1 Interrupt, 2 Bulk, 3 Iso, 4 Interrupt etc ...
+ #define EPNUM_BULK_OUT 0x02
+ #define EPNUM_BULK_IN 0x85
+ #define EPNUM_INT_OUT 0x01
+ #define EPNUM_INT_IN 0x84
+ #define EPNUM_ISO_OUT 0x03
+ #define EPNUM_ISO_IN 0x86
+
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ // MCUs that don't support a same endpoint number with different direction IN and OUT
+ // e.g EP1 OUT & EP1 IN cannot exist together
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_BULK_OUT 0x08
+ #define EPNUM_BULK_IN 0x89
+ #define EPNUM_INT_OUT 0x02
+ #define EPNUM_INT_IN 0x83
+ #define EPNUM_ISO_OUT 0x04
+ #define EPNUM_ISO_IN 0x85
+ #else
+ #define EPNUM_BULK_OUT 0x01
+ #define EPNUM_BULK_IN 0x82
+ #define EPNUM_INT_OUT 0x03
+ #define EPNUM_INT_IN 0x84
+ #define EPNUM_ISO_OUT 0x05
+ #define EPNUM_ISO_IN 0x86
+ #endif
+
+#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
+ // nRF5x: ISO endpoints are hardware-fixed to EP8 (ISOOUT/ISOIN)
+ #define EPNUM_BULK_OUT 0x01
+ #define EPNUM_BULK_IN 0x81
+ #define EPNUM_INT_OUT 0x02
+ #define EPNUM_INT_IN 0x82
+ #define EPNUM_ISO_OUT 0x08
+ #define EPNUM_ISO_IN 0x88
+
+#elif CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
+ #define EPNUM_BULK_OUT 0x01
+ #define EPNUM_BULK_IN 0x81
+ #define EPNUM_INT_OUT 0x02
+ #define EPNUM_INT_IN 0x82
+ #define EPNUM_ISO_OUT 0x03
+ // ERR050101 (see CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101): park the iso IN endpoint clear of the numbers
+ // other devices use. Override per board when several affected boards share a hub.
+ #ifndef EPNUM_ISO_IN
+ #if CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101
+ #define EPNUM_ISO_IN 0x87
+ #else
+ #define EPNUM_ISO_IN 0x83
+ #endif
+ #endif
+
+#else
+ #define EPNUM_BULK_OUT 0x01
+ #define EPNUM_BULK_IN 0x81
+ #define EPNUM_INT_OUT 0x02
+ #define EPNUM_INT_IN 0x82
+ #define EPNUM_ISO_OUT 0x03
+ #define EPNUM_ISO_IN 0x83
+#endif
+
+static uint8_t const desc_fs_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+ // Interface number, string index, bulk out/in + mps, int out/in + mps + interval, iso out/in + mps + interval
+ USBTEST_DESCRIPTOR(ITF_NUM_VENDOR, 4, EPNUM_BULK_OUT, 0x80 | EPNUM_BULK_IN, 64,
+ EPNUM_INT_OUT, 0x80 | EPNUM_INT_IN, USBTEST_INT_EP_MPS_FS, 1,
+ EPNUM_ISO_OUT, 0x80 | EPNUM_ISO_IN, USBTEST_ISO_EP_MPS_FS, 1)
+};
+
+#if TUD_OPT_HIGH_SPEED
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
+
+static uint8_t const desc_hs_configuration[] = {
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+ // Interface number, string index, bulk out/in + mps, int out/in + mps + interval, iso out/in + mps + interval (1 ms)
+ USBTEST_DESCRIPTOR(ITF_NUM_VENDOR, 4, EPNUM_BULK_OUT, 0x80 | EPNUM_BULK_IN, 512,
+ EPNUM_INT_OUT, 0x80 | EPNUM_INT_IN, USBTEST_INT_EP_MPS_HS, 4,
+ EPNUM_ISO_OUT, 0x80 | EPNUM_ISO_IN, USBTEST_ISO_EP_MPS_HS, 4)
+};
+
+// other speed configuration
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+
+// 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 = 0x0200,
+
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00
+};
+
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR 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
+// 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) {
+ (void) index; // for multiple configurations
+
+ // if link speed is high return fullspeed config, and vice versa
+ // Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
+ memcpy(desc_other_speed_config,
+ (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
+ CONFIG_TOTAL_LEN);
+
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+
+ return desc_other_speed_config;
+}
+#endif // highspeed
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index; // for multiple configurations
+
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
+#else
+ return desc_fs_configuration;
+#endif
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// String Descriptor Index
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER,
+ STRID_PRODUCT,
+ STRID_SERIAL,
+};
+
+// array of pointer to string descriptors
+static char const* string_desc_arr[] = {
+ (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
+ "TinyUSB", // 1: Manufacturer
+ "TinyUSB usbtest", // 2: Product
+ NULL, // 3: Serials will use unique ID if possible
+ "TinyUSB usbtest source/sink" // 4: Vendor Interface
+};
+
+static uint16_t _desc_str[32 + 1];
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void) langid;
+ size_t chr_count;
+
+ switch (index) {
+ case STRID_LANGID:
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ break;
+
+ case STRID_SERIAL:
+ chr_count = board_usb_get_serial(_desc_str + 1, 32);
+ break;
+
+ default:
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL;
+
+ const char* str = string_desc_arr[index];
+
+ // Cap at max char
+ chr_count = strlen(str);
+ size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+ if (chr_count > max_count) chr_count = max_count;
+
+ // Convert ASCII string into UTF-16
+ for (size_t i = 0; i < chr_count; i++) {
+ _desc_str[1 + i] = str[i];
+ }
+ break;
+ }
+
+ // first byte is length (including header), second byte is string type
+ _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+
+ return _desc_str;
+}
diff --git a/examples/device/usbtest/src/usb_descriptors.h b/examples/device/usbtest/src/usb_descriptors.h
new file mode 100644
index 000000000..bcf8b5ec4
--- /dev/null
+++ b/examples/device/usbtest/src/usb_descriptors.h
@@ -0,0 +1,84 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 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.
+ *
+ */
+
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
+
+// Device-capability tier advertised in bcdDevice low byte (0x01TT), read by the
+// host script to select which usbtest cases to run:
+// 1: bulk source/sink
+// 2: + vendor control 0x5b/0x5c (ctrl_out)
+// 3: + interrupt source/sink
+// 4: + isochronous source/sink
+// Default is the full tier 4; a board whose DCD cannot serve a tier lowers it here
+// (BOARD_<NAME> is defined by both build systems) and the host battery follows.
+#ifndef USBTEST_TIER
+ #if defined(BOARD_RA2A1_EK)
+ // RA2A1's RUSB2 instance has no isochronous pipe (other RA parts have pipes 1-2)
+ #define USBTEST_TIER 3
+ #else
+ #define USBTEST_TIER 4
+ #endif
+#endif
+
+// Interrupt/isochronous endpoint max packet sizes, must match the configuration descriptor.
+// TUD_OPT_HIGH_SPEED is a compile-time capability flag, NOT the live bus speed, so the full-speed
+// config descriptor (and the OTHER_SPEED descriptor served to a HS host) must use full-speed-legal
+// sizes regardless of it: interrupt <= 64 B, isochronous <= 1023 B (and both iso EPs must fit the
+// 1023 B/frame FS periodic budget). Hence separate _FS / _HS descriptor sizes; the plain macro
+// below is the compile-time capability maximum that sizes the source buffers (runtime write
+// lengths follow the negotiated speed via tud_speed_get(), see main.c).
+//
+// The CH32 USB IPs have tiny per-endpoint buffers so tier-4's six endpoints don't fit at the usual
+// FS sizes: usbfs gives 64 B/ep (iso must drop to 64), and the CH32V20X fsdev port shares one 512 B
+// PMA across every endpoint (needs iso 32 AND a small interrupt mps to fit alongside EP0+bulk+iso).
+#if CFG_TUSB_MCU == OPT_MCU_CH32V20X && defined(CFG_TUD_WCH_USBIP_FSDEV) && CFG_TUD_WCH_USBIP_FSDEV
+ #define USBTEST_INT_EP_MPS_FS 16
+ #define USBTEST_ISO_EP_MPS_FS 32 // double-buffered on fsdev: 2x32=64/ep, same 512 B PMA budget
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X, OPT_MCU_CH32V103, OPT_MCU_CH32F20X, OPT_MCU_CH32V307, OPT_MCU_CH583)
+ // WCH USBFS parts cap every endpoint (except EP3 IN) at 64 B. For the CH32V307 this applies to its
+ // full-speed (usbfs) port; its high-speed (usbhs) port uses the _HS sizes below via desc_hs.
+ #define USBTEST_INT_EP_MPS_FS 64
+ #define USBTEST_ISO_EP_MPS_FS 64
+#else
+ #define USBTEST_INT_EP_MPS_FS 64
+ #define USBTEST_ISO_EP_MPS_FS 128
+#endif
+// RUSB2 (Renesas RA) interrupt pipes 6-9 have a fixed 64-byte single buffer at any speed
+// (RA6M5 UM R01UH0891 sec 29.1: "Pipes 6 to 9: Interrupt transfer with 64-byte single buffer").
+#if TU_CHECK_MCU(OPT_MCU_RAXXX)
+ #define USBTEST_INT_EP_MPS_HS 64
+#else
+ #define USBTEST_INT_EP_MPS_HS 512
+#endif
+#define USBTEST_ISO_EP_MPS_HS 512
+
+// Compile-time capability maximum: sizes the source buffers / vendor epbufs for the largest
+// packet the build can negotiate. Runtime write lengths follow tud_speed_get() (see main.c) —
+// a high-speed build enumerated at full speed submits the _FS lengths.
+#define USBTEST_INT_EP_MPS (TUD_OPT_HIGH_SPEED ? USBTEST_INT_EP_MPS_HS : USBTEST_INT_EP_MPS_FS)
+#define USBTEST_ISO_EP_MPS (TUD_OPT_HIGH_SPEED ? USBTEST_ISO_EP_MPS_HS : USBTEST_ISO_EP_MPS_FS)
+
+#endif /* USB_DESCRIPTORS_H_ */
diff --git a/examples/device/usbtmc/src/usb_descriptors.c b/examples/device/usbtmc/src/usb_descriptors.c
index ecdcef834..5ba5d9367 100644
--- a/examples/device/usbtmc/src/usb_descriptors.c
+++ b/examples/device/usbtmc/src/usb_descriptors.c
@@ -28,15 +28,8 @@
#include "class/usbtmc/usbtmc.h"
#include "class/usbtmc/usbtmc_device.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x401c
#define USB_VID 0xcafe
#define USB_BCD 0x0200
diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c
index b3382c82d..11321a305 100644
--- a/examples/device/video_capture/src/usb_descriptors.c
+++ b/examples/device/video_capture/src/usb_descriptors.c
@@ -27,15 +27,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] VIDEO | AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VIDEO, 5) | PID_MAP(VENDOR, 6) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x401d
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -120,6 +113,10 @@ enum {
#define EPNUM_VIDEO_IN (CFG_TUD_VIDEO_STREAMING_BULK ? 0x81 : 0x88)
#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
#define EPNUM_VIDEO_IN 0x81
+#elif CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101
+ // ERR050101 (see CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101): an isochronous IN endpoint needs a number
+ // no other device on the bus uses
+ #define EPNUM_VIDEO_IN (CFG_TUD_VIDEO_STREAMING_BULK ? 0x81 : 0x87)
#else
#define EPNUM_VIDEO_IN 0x81
#endif
diff --git a/examples/device/video_capture_2ch/src/usb_descriptors.c b/examples/device/video_capture_2ch/src/usb_descriptors.c
index 8dc986da6..2630be84b 100644
--- a/examples/device/video_capture_2ch/src/usb_descriptors.c
+++ b/examples/device/video_capture_2ch/src/usb_descriptors.c
@@ -27,15 +27,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] VIDEO | AUDIO | MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(AUDIO, 4) | PID_MAP(VIDEO, 5) | PID_MAP(VENDOR, 6) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x401e
#define USB_VID 0xCafe
#define USB_BCD 0x0200
@@ -117,8 +110,15 @@ enum {
ITF_NUM_TOTAL
};
-#define EPNUM_VIDEO_IN_1 0x81
-#define EPNUM_VIDEO_IN_2 0x82
+#if CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101 && !CFG_TUD_VIDEO_STREAMING_BULK
+ // ERR050101 (see CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101): an isochronous IN endpoint needs a number
+ // no other device on the bus uses
+ #define EPNUM_VIDEO_IN_1 0x86
+ #define EPNUM_VIDEO_IN_2 0x87
+#else
+ #define EPNUM_VIDEO_IN_1 0x81
+ #define EPNUM_VIDEO_IN_2 0x82
+#endif
#if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
#define USE_MJPEG 1
diff --git a/examples/device/webusb_serial/README.md b/examples/device/webusb_serial/README.md
index 5ca70f909..15837e59e 100644
--- a/examples/device/webusb_serial/README.md
+++ b/examples/device/webusb_serial/README.md
@@ -51,4 +51,4 @@ make BOARD=raspberry_pi_pico all
After flashing, open the landing page (`https://example.tinyusb.org/webusb-serial/index.html`) in a WebUSB-capable browser such as Chrome, click **Connect**, and select the device — the on-board LED lights solid once connected. Characters typed in the web page are echoed back, and are also mirrored to the CDC serial port (e.g. `/dev/ttyACM0`) and vice versa.
-On Linux/macOS you may need to install the udev rules from `examples/device/99-tinyusb.rules` for the browser to access the device.
+On Linux/macOS you may need to install the udev rules from `examples/device/99-tinyusb-examples.rules` for the browser to access the device.
diff --git a/examples/device/webusb_serial/src/main.c b/examples/device/webusb_serial/src/main.c
index 4be5e4db4..e200c334c 100644
--- a/examples/device/webusb_serial/src/main.c
+++ b/examples/device/webusb_serial/src/main.c
@@ -39,7 +39,7 @@
* is done automatically by firmware.
*
* - On Linux/macOS, udev permission may need to be updated by
- * - copying '/examples/device/99-tinyusb.rules' file to /etc/udev/rules.d/ then
+ * - copying 'examples/device/99-tinyusb-examples.rules' file to /etc/udev/rules.d/ then
* - run 'sudo udevadm control --reload-rules && sudo udevadm trigger'
*/
diff --git a/examples/device/webusb_serial/src/usb_descriptors.c b/examples/device/webusb_serial/src/usb_descriptors.c
index 527837161..5986bffdf 100644
--- a/examples/device/webusb_serial/src/usb_descriptors.c
+++ b/examples/device/webusb_serial/src/usb_descriptors.c
@@ -27,15 +27,8 @@
#include "tusb.h"
#include "usb_descriptors.h"
-/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
- * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
- *
- * Auto ProductID layout's Bitmap:
- * [MSB] MIDI | HID | MSC | CDC [LSB]
- */
-#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
-#define USB_PID (0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | \
- PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) )
+// Unique PID per example: guarantees re-enumeration on re-flash and a fresh host driver match.
+#define USB_PID 0x401f
//--------------------------------------------------------------------+
// Device Descriptors