From 4a93ede2e8d8cfe46292edaffb616e456fff1164 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Sat, 19 Sep 2026 23:59:43 -0500 Subject: [PATCH] feat(usb_device): Config::port to pick the USB-OTG port (ESP32-P4) esp_tinyusb's tinyusb_config_t.port selects the peripheral on targets with two USB-OTG controllers; TinyUSB defaults to the high-speed port on the ESP32-P4. Boards do not always route that port to a device-capable connector (the M5Stack Tab5 wires it to its USB-A host jack; its USB-C carries the full-speed port), so let the application choose: -1 keeps the TinyUSB default, otherwise the value is validated against TINYUSB_PORT_MAX and passed through. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01BAeyJSuzVyqvSe4mffGUZo --- components/usb_device/README.md | 4 ++++ components/usb_device/include/usb_device.hpp | 9 +++++++++ components/usb_device/src/usb_device.cpp | 9 +++++++++ 3 files changed, 22 insertions(+) diff --git a/components/usb_device/README.md b/components/usb_device/README.md index 034633ed54..1bed6274fd 100644 --- a/components/usb_device/README.md +++ b/components/usb_device/README.md @@ -79,6 +79,10 @@ for back-compatibility. strings, plus the descriptor details some hosts check: `bcd_device` (device release), `max_power_ma` (bMaxPower, clamped to 500 mA and rounded up to the next 2 mA unit) and `remote_wakeup`. +- **Port selection** (`Config::port`): on targets with two USB-OTG + controllers (ESP32-P4) pick the full-speed or the high-speed port; boards do + not always route the high-speed port to a device-capable connector (the + M5Stack Tab5's USB-C carries the full-speed port). - **Idiomatic espp**: no exceptions; `initialize()` reports failures via `std::error_code`. - **Safe marshaling**: the TinyUSB RX callbacks (TinyUSB task context) are drained diff --git a/components/usb_device/include/usb_device.hpp b/components/usb_device/include/usb_device.hpp index ae5889481f..185db98732 100644 --- a/components/usb_device/include/usb_device.hpp +++ b/components/usb_device/include/usb_device.hpp @@ -347,6 +347,15 @@ class UsbDevice : public BaseComponent { * false to stay invisible to the host until connect() -- e.g. to finish * application file I/O on an MSC medium before a host can take it. */ bool connect_on_initialize{true}; + /** USB peripheral port to use, as esp_tinyusb's `tinyusb_port_t` (0 = the + * USB-OTG 1.1 full-speed port, 1 = the USB-OTG 2.0 high-speed port on + * targets that have one). -1 = TinyUSB's default for the target: the + * high-speed port on the ESP32-P4, the full-speed port elsewhere. Boards + * do not always route the high-speed port to a device-capable connector + * (the M5Stack Tab5 wires it to its USB-A host jack; its USB-C carries the + * full-speed port, shared with the USB-Serial-JTAG console), so this lets + * the application pick the connector. */ + int port{-1}; std::optional cdc{}; /**< Enable a CDC-ACM function. */ std::optional vendor{}; /**< Enable a vendor-specific / WebUSB function. */ diff --git a/components/usb_device/src/usb_device.cpp b/components/usb_device/src/usb_device.cpp index 3e301353ab..d384cf4951 100644 --- a/components/usb_device/src/usb_device.cpp +++ b/components/usb_device/src/usb_device.cpp @@ -1496,6 +1496,15 @@ bool UsbDevice::initialize(std::error_code &ec) { // --- Install the TinyUSB driver with our descriptors --- tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); + if (config_.port >= 0) { + if (config_.port >= static_cast(TINYUSB_PORT_MAX)) { + logger_.error("Invalid USB port {} (this target has {} port(s))", config_.port, + static_cast(TINYUSB_PORT_MAX)); + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + tusb_cfg.port = static_cast(config_.port); + } tusb_cfg.descriptor.device = &impl_->device_desc; tusb_cfg.descriptor.string = impl_->strings.data(); tusb_cfg.descriptor.string_count = static_cast(impl_->strings.size());