From 7673a08dbda334596e311a1a395bab95d131e5ed Mon Sep 17 00:00:00 2001 From: zackees Date: Thu, 9 Jul 2026 12:49:07 -0700 Subject: [PATCH] feat(usb): embed refreshed per-board VID:PID archive, drop hardcoded table (#722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-vendor VID:PID ingest sub-issues (#723–#740) are all closed, so the FastLED/boards pipeline now carries far richer per-board data than fbuild's stale 745-byte embedded proto. Regenerate `usb-vids.proto.zstd` from the current boards data (745 B → 10 KB, 36 VIDs / 1369 PID rows) so offline resolution names real boards/probes instead of `Device 0xPPPP` placeholders: 303A:1001 → WEMOS LOLIN S3 1FC9:0132 → NXP LPC-Link2 (CMSIS-DAP) 1FC9:0143 → NXP MCU-Link 16C0:0483 → Teensy 4.0 Curated on the boards `other` branch (fastled/boards#44): added the MCU-Link PID (1fc9:0143) and authoritative vendor overrides for shared UART-bridge VIDs that the board-attribution pipeline otherwise mislabels (10c4 Silicon Labs, 0403 FTDI, 1a86 QinHeng/WCH, 067b Prolific — previously Arduino/Digilent/OLIMEX). Delete the hardcoded `FRIENDLY_PRODUCTS` supplement table from `port_scan`: friendly product names are now owned by the boards VID:PID data and embedded at build time. A missing name is a boards-data gap, not an fbuild source edit. Verified offline on real hardware: `port scan --offline` shows `Silicon Labs / …`, `NXP LPC-Link2 …`, `PJRC (Teensy) / Teensy 4.0`, `Espressif Systems / WEMOS LOLIN S3` with no network and no hardcoded table. Co-Authored-By: Claude Opus 4.8 --- crates/fbuild-cli/src/cli/port_scan.rs | 85 ++++++++------------ crates/fbuild-core/data/usb-vids.proto.zstd | Bin 745 -> 10086 bytes crates/fbuild-core/src/usb/resolver.rs | 11 ++- 3 files changed, 39 insertions(+), 57 deletions(-) diff --git a/crates/fbuild-cli/src/cli/port_scan.rs b/crates/fbuild-cli/src/cli/port_scan.rs index 46a18950..58ff8e72 100644 --- a/crates/fbuild-cli/src/cli/port_scan.rs +++ b/crates/fbuild-cli/src/cli/port_scan.rs @@ -223,7 +223,7 @@ fn render_usb_port_with_kernel_class( "{name:<10}{vid:04X}:{pid:04X} {descriptor}{serial_field}{interface_field}", ); let info = fbuild_core::usb::resolve(vid, pid); - let friendly_product = friendly_product_name(vid, pid, &info.product, product); + let friendly_product = friendly_product_name(pid, &info.product, product); let _ = writeln!( out, " └─ {} / {} cdc={}", @@ -244,30 +244,24 @@ fn cdc_label(kernel_class: Option) - /// Pick the most "friendly" product label for the resolver row. /// /// Preference order: -/// 1. Resolver's product if it's a real name (tier-2 overlay hit) — -/// i.e. *not* the synthetic `Device 0xPPPP` placeholder. -/// 2. Small inline supplement table for common embedded CDC-ACM PIDs -/// that FastLED/boards' canonical `vidpid` table doesn't yet -/// cover (e.g. ESP32-S3 builtin USB-CDC at 303A:1001). Migrate -/// these upstream as the canonical DB picks them up. -/// 3. The OS-supplied descriptor when it carries chip-specific -/// detail (e.g. macOS / Linux often expose "CP2102 USB to UART -/// Bridge Controller") — skip if it's the generic "USB Serial -/// Device" Windows fallback. -/// 4. Synthetic `Device 0xPPPP` placeholder (tier-3 fallback). -fn friendly_product_name( - vid: u16, - pid: u16, - resolved_product: &str, - os_descriptor: Option<&str>, -) -> String { +/// 1. Resolver's product if it's a real name — i.e. *not* the synthetic +/// `Device 0xPPPP` placeholder. This comes from the embedded +/// FastLED/boards VID:PID archive (offline) or the online overlay. +/// 2. The OS-supplied descriptor when it carries chip-specific detail +/// (e.g. macOS / Linux often expose "CP2102 USB to UART Bridge +/// Controller") — skip if it's the generic "USB Serial Device" +/// Windows fallback. +/// 3. Synthetic `Device 0xPPPP` placeholder. +/// +/// There is intentionally NO hardcoded per-PID product table here: friendly +/// product names are owned by the FastLED/boards VID:PID data and embedded at +/// build time (FastLED/fbuild#722, #959). A missing name is a data gap to fix +/// on the boards `other` branch, not in fbuild source. +fn friendly_product_name(pid: u16, resolved_product: &str, os_descriptor: Option<&str>) -> String { let synthetic = format!("Device 0x{pid:04X}"); if resolved_product != synthetic { return resolved_product.to_string(); } - if let Some(name) = friendly_supplement(vid, pid) { - return name.to_string(); - } if let Some(d) = os_descriptor { let trimmed = d.trim(); if !trimmed.is_empty() && !is_generic_descriptor(trimmed) { @@ -288,30 +282,6 @@ fn is_generic_descriptor(d: &str) -> bool { ) } -/// Small inline supplement for common embedded VID:PIDs that the -/// canonical online overlay may not carry in offline/test paths. Keep it -/// short — anything that lands into the embedded product map should be -/// removed here. -const FRIENDLY_PRODUCTS: &[(u16, u16, &str)] = &[ - // Espressif Systems (VID 0x303A) — ESP32 series USB-CDC ACM. - (0x303A, 0x1001, "ESP32-S3 USB-CDC"), - (0x303A, 0x1002, "ESP32-C3 USB-CDC"), - (0x303A, 0x4001, "ESP32-S2 USB-CDC"), - (0x303A, 0x0002, "ESP32-S2 ROM-DL"), - (0x303A, 0x0003, "ESP32-S3 ROM-DL"), - (0x303A, 0x1000, "ESP32-S2 USB-CDC"), - // NXP Semiconductors (VID 0x1FC9) — LPC-Link2 / MCU-Link CMSIS-DAP. - (0x1FC9, 0x0132, "LPC-Link2 CMSIS-DAP"), - (0x1FC9, 0x0143, "MCU-Link CMSIS-DAP"), -]; - -fn friendly_supplement(vid: u16, pid: u16) -> Option<&'static str> { - FRIENDLY_PRODUCTS - .iter() - .find(|&&(v, p, _)| v == vid && p == pid) - .map(|&(_, _, name)| name) -} - fn render_non_usb(out: &mut String, name: &str, kind: &str) { use std::fmt::Write as _; let _ = writeln!(out, "{name:<10}[{kind}]"); @@ -649,10 +619,10 @@ mod tests { } #[test] - fn esp32_s3_cdc_pid_gets_friendly_supplement() { - // In the tier-1/offline path the embedded archive carries vendor - // names only; the inline supplement keeps this common PID friendly - // when the online product overlay is not installed. + fn common_esp32_cdc_pid_resolves_from_embedded_archive() { + // The common ESP32 USB-Serial-JTAG PID (303A:1001) resolves to a real + // product name from the embedded FastLED/boards archive — no hardcoded + // supplement table, and NOT the synthetic placeholder. FastLED/fbuild#722. let ports = vec![usb_port( "COM25", 0x303A, @@ -662,11 +632,20 @@ mod tests { )]; let out = render_scan(&ports); assert!( - out.contains("ESP32-S3 USB-CDC"), - "expected friendly supplement product, got: {out}" + out.to_lowercase().contains("espressif"), + "expected Espressif vendor from the archive, got: {out}" + ); + // A real archive product name, not the synthetic placeholder or the + // generic Windows descriptor. + assert!(!out.contains("Device 0x1001"), "leaked placeholder: {out}"); + let resolver_row = out + .lines() + .find(|l| l.contains("└─")) + .expect("resolver row"); + assert!( + !resolver_row.contains("USB Serial Device"), + "generic descriptor leaked into resolver row: {resolver_row}" ); - // And we do NOT fall through to the synthetic placeholder. - assert!(!out.contains("Device 0x1001")); } #[test] diff --git a/crates/fbuild-core/data/usb-vids.proto.zstd b/crates/fbuild-core/data/usb-vids.proto.zstd index 23202b3690b7f757453391358632e6e1232a363a..91ebe7889b64927a5e5431c629ca49579146d50e 100644 GIT binary patch literal 10086 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 literal 745 zcmV&9L+N(t& zTRlOi2@|r#+@*MZTM2Bj5&4z^rob|)ggP83d;op`fdDT5zF}A`|BYMobuPP0-;HfF zUFD&#r*YYo@MNZ2I{D+mDvN+Hapo~{yq@<;3h#7mYkU0#Web`d3%@;n65DUg%UExt#4_{;lx2UTo(cN<$*x=NR%-qEEbjN9u6Iksk_Z^Ec(9paP&gg}^hhN%ll*kR6W8=~kk zZkUk2gV~#3ce}bV?m=8a61|g>}MP-JU zt-YC+3i^Y}4!zc0`KR6T*q(kx`M*`;8c~km>>AS~^f#0%Vh#Ee$`NnNJM41u1L8~^ z&67cc`9WqVQ-HIvr2G}y4F*Pk-Fu@78kb!xKY0QaXX0p{-!ngDd65wFc;~%ItA*$d zEy>r9q?ux&TS*zi^>z;D;??B=M<{Sagph6n!5B4^qAAtFFa}67r(#Kd+=oA1Az;0B zHV@FUzmdk}7DG!Er*Ig8zez;NOz6q9iliAJTQp1CHm0^@GHRv41$!(jfpC^MGFOFH zuRMxw*d*2DijqUY%di7lZTdR5Vvqc%HpRm3wL6x6iSd6Ck?Co b91|};NQpm!!}zU~0yh^0zJK#GAhECq=^}7P diff --git a/crates/fbuild-core/src/usb/resolver.rs b/crates/fbuild-core/src/usb/resolver.rs index fa7670df..1aab4f2b 100644 --- a/crates/fbuild-core/src/usb/resolver.rs +++ b/crates/fbuild-core/src/usb/resolver.rs @@ -166,7 +166,7 @@ mod tests { // embedded FastLED/boards VID:PID archive — NOT a hardcoded table in // fbuild. Pin the round-trip for the PIDs a Teensy exposes as serial // ports. FastLED/fbuild#962. - for (pid, expect) in [(0x0483u16, "serial"), (0x0489, "midi")] { + for pid in [0x0483u16, 0x0489] { let info = try_resolve(0x16C0, pid).expect("Teensy PID in embedded archive"); assert!( info.vendor.to_lowercase().contains("pjrc") @@ -174,12 +174,15 @@ mod tests { "16C0:{pid:04X} vendor should be PJRC/Teensy, got {:?}", info.vendor ); + // Product name is archive-derived (a board or USB-mode label) and + // may change as the FastLED/boards data is refreshed — assert it is + // a real Teensy name, NOT the synthetic `Device 0xPPPP` placeholder. assert!( - info.product.to_lowercase().contains("teensy") - && info.product.to_lowercase().contains(expect), - "16C0:{pid:04X} product should name the Teensy {expect} mode, got {:?}", + info.product.to_lowercase().contains("teensy"), + "16C0:{pid:04X} product should name Teensy, got {:?}", info.product ); + assert_ne!(info.product, format!("Device 0x{pid:04X}")); } }