Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion components/usb_device/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ for back-compatibility.
- **Sequential allocation** of interfaces / endpoints / strings with an
endpoint-budget check (error via `std::error_code` if exceeded).
- **Configurable identity**: VID, PID, manufacturer / product / serial / interface
strings.
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`.
- **Idiomatic espp**: no exceptions; `initialize()` reports failures via
`std::error_code`.
- **Safe marshaling**: the TinyUSB RX callbacks (TinyUSB task context) are drained
Expand All @@ -85,6 +87,11 @@ Composite CDC + vendor/WebUSB device (both interfaces carry the same raw stream)
espp::UsbDevice::Config cfg;
cfg.vid = 0x1209; // pid.codes VID (ODrive uses this)
cfg.pid = 0x0d32; // ODrive-like PID
// optional descriptor details (defaults: 0x0100, 100 mA, remote wakeup on);
// e.g. a Nintendo Switch expects a Pro Controller to report 0x0210 and 500 mA
cfg.bcd_device = 0x0100;
cfg.max_power_ma = 100; // clamped to 500, rounded up to a 2 mA unit
cfg.remote_wakeup = true;

espp::UsbDevice::CdcFunction cdc;
cdc.on_receive = [&](std::span<const uint8_t> data) { /* serial rx */ };
Expand Down
8 changes: 8 additions & 0 deletions components/usb_device/include/usb_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ class UsbDevice : public BaseComponent {
std::string manufacturer{"espp"}; /**< Manufacturer string descriptor. */
std::string product{"espp USB Device"}; /**< Product string descriptor. */
std::string serial_number{"000000000001"}; /**< Serial number string descriptor. */
// Descriptor details some hosts check (e.g. a Switch expects a Pro Controller
// to report bcdDevice 0x0210 and 500 mA).
uint16_t bcd_device{0x0100}; /**< bcdDevice (device release, BCD) in the device descriptor.
Ignored for an XInput-only device (which reports the Xbox
360 value). */
uint16_t max_power_ma{100}; /**< bMaxPower in the configuration descriptor, in mA; clamped
to 500 and rounded up to the next 2 mA unit. */
bool remote_wakeup{true}; /**< Advertise remote wakeup in the configuration attributes. */

std::optional<CdcFunction> cdc{}; /**< Enable a CDC-ACM function. */
std::optional<VendorFunction> vendor{}; /**< Enable a vendor-specific / WebUSB function. */
Expand Down
25 changes: 20 additions & 5 deletions components/usb_device/src/usb_device.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "usb_device.hpp"

#include <algorithm>
#include <array>
#include <atomic>
#include <cstdio>
Expand Down Expand Up @@ -888,7 +889,7 @@ bool UsbDevice::initialize(std::error_code &ec) {
impl_->device_desc.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE;
impl_->device_desc.idVendor = xinput_only ? config_.xinput->vid : config_.vid;
impl_->device_desc.idProduct = xinput_only ? config_.xinput->pid : config_.pid;
impl_->device_desc.bcdDevice = xinput_only ? espp::xinput::kDefaultBcdDevice : 0x0100;
impl_->device_desc.bcdDevice = xinput_only ? espp::xinput::kDefaultBcdDevice : config_.bcd_device;
impl_->device_desc.iManufacturer = 0x01;
impl_->device_desc.iProduct = 0x02;
impl_->device_desc.iSerialNumber = 0x03;
Expand Down Expand Up @@ -927,10 +928,24 @@ bool UsbDevice::initialize(std::error_code &ec) {
desc.clear();
auto append = [&](const uint8_t *p, size_t n) { desc.insert(desc.end(), p, p + n); };
{
const uint8_t hdr[] = {
// config number, interface count, string index, total length, attribute, power (mA)
TUD_CONFIG_DESCRIPTOR(1, itf_count, 0, total_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
100),
// The 9-byte configuration descriptor header, written out explicitly
// (TUD_CONFIG_DESCRIPTOR's arithmetic on runtime values is a narrowing
// conversion inside a braced initializer). bMaxPower is in 2 mA units:
// clamp to the USB 2.0 maximum (500 mA) and round UP so an odd request is
// never under-reported (1 mA -> 2 mA).
const uint16_t power_ma = std::min<uint16_t>(config_.max_power_ma, 500);
const uint8_t attributes = static_cast<uint8_t>(
TU_BIT(7) | (config_.remote_wakeup ? TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP : 0));
const uint8_t hdr[9] = {
9, // bLength
static_cast<uint8_t>(TUSB_DESC_CONFIGURATION), // bDescriptorType
static_cast<uint8_t>(total_len & 0xFF), // wTotalLength (LE)
static_cast<uint8_t>(total_len >> 8),
static_cast<uint8_t>(itf_count), // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
attributes, // bmAttributes
static_cast<uint8_t>((power_ma + 1) / 2), // bMaxPower (2 mA units)
};
append(hdr, sizeof(hdr));
}
Expand Down
10 changes: 9 additions & 1 deletion doc/en/buses/usb_cdc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ Features
- WebUSB: BOS descriptor + WebUSB URL descriptor + MS OS 2.0 descriptor for
driverless browser access, with a configurable landing-page URL
- Sequential interface / endpoint / string allocation with an endpoint-budget check
- Configurable VID, PID, and manufacturer / product / serial / interface strings
- Configurable VID, PID, and manufacturer / product / serial / interface 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``
- No exceptions; ``initialize()`` reports failures via ``std::error_code``
- Safely marshals the TinyUSB RX callbacks (TinyUSB task context) into per-function
user callbacks
Expand All @@ -75,6 +78,11 @@ stream:
espp::UsbDevice::Config cfg;
cfg.vid = 0x1209; // pid.codes VID (ODrive uses this)
cfg.pid = 0x0d32; // ODrive-like PID
// optional descriptor details (defaults: 0x0100, 100 mA, remote wakeup on);
// e.g. a Nintendo Switch expects a Pro Controller to report 0x0210 and 500 mA
cfg.bcd_device = 0x0100;
cfg.max_power_ma = 100; // clamped to 500, rounded up to a 2 mA unit
cfg.remote_wakeup = true;

espp::UsbDevice::CdcFunction cdc;
cdc.on_receive = [&](std::span<const uint8_t> data) { /* handle serial rx */ };
Expand Down
Loading