diff --git a/.github/workflows/update-data.yml b/.github/workflows/update-data.yml index c363cfe8..6f9c2449 100644 --- a/.github/workflows/update-data.yml +++ b/.github/workflows/update-data.yml @@ -1,10 +1,13 @@ -# Nightly refresh of the `online-data` branch's published datasets. +# Nightly refresh of the `online-data` and `www` branches. # -# Today the branch carries two datasets — USB VID:PID name resolution and -# the PlatformIO board catalog. The workflow file lives on `main` only -# because GitHub Actions requires `schedule` / `workflow_dispatch` to be -# defined on the default branch. All actual data + the merger scripts -# live on the orphan `online-data` branch (see `docs/online-data.md`). +# `online-data` (orphan) carries the merged JSON datasets — USB VID:PID name +# resolution and the PlatformIO board catalog — plus their merger scripts. +# `www` (orphan, GH Pages source) carries a day-rotated SQLite database +# (`.db`) built from the same JSON plus the static-site front-end +# that serves it via sql.js. See FastLED/fbuild#718 for the www design. +# +# This workflow file lives on `main` only because GitHub Actions requires +# `schedule` / `workflow_dispatch` to be defined on the default branch. # # At runtime the job: # @@ -62,6 +65,15 @@ env: ONLINE_WORKTREE: ${{ github.workspace }}/.online-data BRANCH_BASE_URL: https://raw.githubusercontent.com/${{ github.repository }}/online-data HISTORY_LIMIT: 200 + # www branch (GH Pages source): see FastLED/fbuild#718. + WWW_BRANCH: www + WWW_WORKTREE: ${{ github.workspace }}/.www + # sql.js is downloaded from a pinned release with an SRI check (below) + # and staged onto the www branch fresh on every run. + SQLJS_VERSION: "1.10.3" + SQLJS_BASE_URL: https://github.com/sql-js/sql.js/releases/download/v1.10.3/sqljs-wasm.zip + # Public site URL (overridden if GitHub Pages is configured elsewhere). + WEBSITE_URL: https://fastled.github.io/fbuild/ jobs: update: @@ -94,6 +106,14 @@ jobs: - uses: astral-sh/setup-uv@v3 + - name: Setup www worktree (orphan, GH Pages source) + # Wraps fetch / orphan-bootstrap logic in Python — unit-tested in + # online-data-tools/test_orchestrators.py. + run: | + uv run --no-project --script \ + "${{ github.workspace }}/online-data-tools/setup_www_worktree.py" \ + --worktree "${WWW_WORKTREE}" --branch "${WWW_BRANCH}" + - name: Setup soldr uses: zackees/setup-soldr@v0.9.62 with: @@ -216,6 +236,55 @@ jobs: --manifest-fragment /tmp/fragments/pio-boards.json \ --manifest-fragment-slim /tmp/fragments/vendor_boards.json + # ──────────────────────────────────────────────────────────────────── + # Tier-4 USB-VID source: inlined supplement curated from + # usb-ids.gowdy.us. The public text databases (Rust crate, + # linux-usb.org, hwdata) don't carry newer VIDs like 0x303A Espressif + # or 0x2E8A Raspberry Pi Foundation. The 253-entry overlay lives in + # online-data-tools/vendor_names_inlined.py — committed to main so + # the workflow is reproducible offline (no nightly live-scrape) and + # auditable (each entry traces back to ids.txt -> ids4.json). + # ──────────────────────────────────────────────────────────────────── + + - name: Emit inlined vendor-name supplement (USB-VID tier-4) + id: emit-inlined + continue-on-error: true + if: steps.merge-usb.outcome == 'success' + run: | + uv run --no-project --script \ + "${{ github.workspace }}/online-data-tools/vendor_names_inlined.py" \ + --out /tmp/inlined-supplement.json + + - name: Overlay inlined supplement onto usb-vid.json + id: overlay-inlined + continue-on-error: true + if: steps.emit-inlined.outcome == 'success' + # vendor-override: the curated inlined names WIN over the upstream + # text databases. Upstream products lists are preserved untouched — + # only the vendor name field gets replaced for VIDs present in both. + run: | + uv run --no-project --script \ + "${{ github.workspace }}/online-data-tools/overlay_usb_vid.py" \ + --upstream "${ONLINE_WORKTREE}/data/usb-vid.json" \ + --supplement /tmp/inlined-supplement.json \ + --out "${ONLINE_WORKTREE}/data/usb-vid.json" \ + --mode vendor-override + + - name: Package usb-vendors.tar.zst (embeddable into fbuild) + id: package-archive + continue-on-error: true + if: steps.overlay-inlined.outcome == 'success' + # Compact {vid: vendor} dict in tar.zst form. fbuild include_bytes!()s + # this at compile time so its USB-vendor lookup needs no runtime + # network access and no `usb-ids` Rust crate dependency. PID-level + # lookups live in the www-branch SQLite-over-HTTP DB. + run: | + uv run --no-project --script \ + "${{ github.workspace }}/online-data-tools/build_vendor_archive.py" \ + --upstream "${ONLINE_WORKTREE}/data/usb-vid.json" \ + --out "${ONLINE_WORKTREE}/data/usb-vendors.tar.zst" + ls -la "${ONLINE_WORKTREE}/data/usb-vendors.tar.zst" + - name: Assemble manifest.json id: build-manifest # We rebuild the manifest whenever at least one dataset succeeded, @@ -244,50 +313,58 @@ jobs: --out "${ONLINE_WORKTREE}/manifest.json" \ "${fragments[@]}" - - name: Commit + push if data actually changed - id: commit + # ──────────────────────────────────────────────────────────────────── + # www branch: build today's SQLite, refresh static assets, download + # sql.js, rotate old DBs, write www/manifest.json, annotate online + # manifest with the link-out. All seven sub-steps run inside one + # Python orchestrator (online-data-tools/update_www.py) and are + # exercised end-to-end in test_orchestrators.py. + # ──────────────────────────────────────────────────────────────────── + + - name: Refresh www (sqlite + static site + manifests) + id: build-sqlite + # Only attempt if at least one upstream merger produced fresh JSON — + # otherwise we'd be rebuilding yesterday's DB under a new filename, + # which the rotation step would then evict tomorrow. if: steps.build-manifest.outcome == 'success' - working-directory: ${{ env.ONLINE_WORKTREE }} run: | - set -euo pipefail - git add manifest.json data/ - if git diff --cached --quiet; then - echo "no changes to commit" - echo "changed=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - ts="$(date -u +%Y-%m-%d)" - # Include which datasets actually refreshed in the commit body. - parts=() - [ "${{ steps.merge-usb.outcome }}" = "success" ] && parts+=("usb-vid") - [ "${{ steps.merge-pio.outcome }}" = "success" ] && parts+=("pio-boards") - body="$(printf 'datasets: %s' "$(IFS=, ; echo "${parts[*]}")")" - git commit -m "chore(online-data): nightly refresh ${ts}" -m "${body}" - echo "changed=true" >> "$GITHUB_OUTPUT" + uv run --no-project --script \ + "${{ github.workspace }}/online-data-tools/update_www.py" \ + --workspace "${{ github.workspace }}" \ + --online-worktree "${ONLINE_WORKTREE}" \ + --www-worktree "${WWW_WORKTREE}" \ + --website-url "${WEBSITE_URL}" \ + --sqljs-zip-url "${SQLJS_BASE_URL}" - - name: Prune history to last ${{ env.HISTORY_LIMIT }} commits - if: steps.commit.outputs.changed == 'true' - working-directory: ${{ env.ONLINE_WORKTREE }} + # ──────────────────────────────────────────────────────────────────── + # Publish both branches via the same Python orchestrator. It handles + # `git add` / commit-if-changed / 200-commit history prune / + # first-push-falls-back-to-plain. End-to-end tested in + # test_orchestrators.py against a bare local remote. + # ──────────────────────────────────────────────────────────────────── + + - name: Publish online-data branch + id: commit + if: steps.build-manifest.outcome == 'success' run: | - set -euo pipefail - total="$(git rev-list --count HEAD)" - echo "current history length: ${total}" - if [ "${total}" -le "${HISTORY_LIMIT}" ]; then - echo "no prune needed (<= ${HISTORY_LIMIT} commits)" - exit 0 - fi - target="$(git rev-list --max-count="${HISTORY_LIMIT}" HEAD | tail -n 1)" - git replace --graft "${target}" - pip install --quiet git-filter-repo - git filter-repo --force --refs HEAD - git for-each-ref --format='delete %(refname)' refs/replace/ | \ - git update-ref --stdin + uv run --no-project --script \ + "${{ github.workspace }}/online-data-tools/publish_branch.py" \ + --worktree "${ONLINE_WORKTREE}" \ + --branch "${ONLINE_BRANCH}" \ + --message "chore(online-data): nightly refresh" \ + --history-limit "${HISTORY_LIMIT}" - - name: Push - if: steps.commit.outputs.changed == 'true' - working-directory: ${{ env.ONLINE_WORKTREE }} + - name: Publish www branch + id: commit-www + if: steps.build-sqlite.outcome == 'success' run: | - git push --force-with-lease origin "${ONLINE_BRANCH}" + uv run --no-project --script \ + "${{ github.workspace }}/online-data-tools/publish_branch.py" \ + --worktree "${WWW_WORKTREE}" \ + --branch "${WWW_BRANCH}" \ + --message "chore(www): nightly refresh" \ + --body "sqlite + static site rebuild from latest online-data" \ + --history-limit "${HISTORY_LIMIT}" - name: Summary if: always() @@ -302,7 +379,12 @@ jobs: echo "| usbids/usbids github | ${{ steps.fetch-github.outcome }} |" echo "| pio boards (platformio) | ${{ steps.dump-pio.outcome }} |" echo "| merge usb-vid | ${{ steps.merge-usb.outcome }} |" + echo "| emit inlined supplement | ${{ steps.emit-inlined.outcome }} |" + echo "| overlay inlined supplement | ${{ steps.overlay-inlined.outcome }} |" + echo "| package vendor archive | ${{ steps.package-archive.outcome }} |" echo "| merge pio-boards | ${{ steps.merge-pio.outcome }} |" echo "| build manifest | ${{ steps.build-manifest.outcome }} |" - echo "| committed | ${{ steps.commit.outputs.changed || 'n/a' }} |" + echo "| build sqlite (www) | ${{ steps.build-sqlite.outcome }} |" + echo "| committed (online-data) | ${{ steps.commit.outputs.changed || 'n/a' }} |" + echo "| committed (www) | ${{ steps.commit-www.outputs.changed || 'n/a' }} |" } >> "$GITHUB_STEP_SUMMARY" diff --git a/Cargo.lock b/Cargo.lock index dd2515a3..69c14eec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -970,11 +970,13 @@ dependencies = [ "serde", "serde_json", "sha2", + "tar", "tempfile", "thiserror 2.0.18", "tokio", "tracing", "usb-ids", + "zstd", ] [[package]] diff --git a/crates/fbuild-core/Cargo.toml b/crates/fbuild-core/Cargo.toml index ae451c04..f89393bb 100644 --- a/crates/fbuild-core/Cargo.toml +++ b/crates/fbuild-core/Cargo.toml @@ -12,8 +12,17 @@ tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } sha2 = { workspace = true } -# Tier-1 USB VID:PID resolver — see `crate::usb`. +# Aggregator backend for the `online-data` workflow only: `examples/dump_usb_ids.rs` +# uses this to feed tier-1 into `online-data/data/usb-vid.json`. The fbuild +# runtime resolver no longer touches this crate — it goes through the +# compile-time-embedded `usb-vendors.tar.zst` archive instead (see +# `crate::usb::embedded`). usb-ids = { workspace = true } +# Embedded USB-vendor archive decompression + extraction at first use. +# Pulled in as workspace deps so other crates can share the same zstd / tar +# wire format without per-crate version drift. +zstd = { workspace = true } +tar = { workspace = true } # Process containment primitive (Job Objects on Windows; process groups + # PR_SET_PDEATHSIG on Linux; process groups on macOS). The single global # `ContainedProcessGroup` owned by the daemon ensures every child process diff --git a/crates/fbuild-core/data/README.md b/crates/fbuild-core/data/README.md new file mode 100644 index 00000000..ff55bfb4 --- /dev/null +++ b/crates/fbuild-core/data/README.md @@ -0,0 +1,29 @@ +# fbuild-core embedded data + +Binary blobs `include_bytes!`'d into `fbuild-core` at compile time. + +| File | Purpose | +| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `usb-vendors.tar.zst` | USB Vendor-ID → vendor-name map. Produced by `online-data-tools/build_vendor_archive.py` from the merged `online-data/data/usb-vid.json` (which already incorporates the curated `vendor_names_inlined.py` overlay). See `crate::usb::embedded`. | + +## How to refresh the vendor archive + +The nightly `Update data` workflow on `main` produces a fresh +`usb-vendors.tar.zst` under `online-data/data/`. To bump the embedded +copy here (a deliberate manual step — see issue #718): + +```bash +# 1. Pull the latest from the online-data branch. +curl -sSLo crates/fbuild-core/data/usb-vendors.tar.zst \ + https://raw.githubusercontent.com/FastLED/fbuild/online-data/data/usb-vendors.tar.zst + +# 2. Run the fbuild-core tests to confirm the archive parses + the +# well-known entries still resolve. +soldr cargo test -p fbuild-core usb::embedded +``` + +`fbuild-core` will refuse to load the archive if its embedded +`manifest.json` reports a schema version newer than the consumer knows +about — bump `EMBEDDED_SCHEMA_VERSION` in `src/usb/embedded.rs` whenever +the archive format changes (in lock-step with +`online-data-tools/build_vendor_archive.py::SCHEMA_VERSION`). diff --git a/crates/fbuild-core/data/usb-vendors.tar.zst b/crates/fbuild-core/data/usb-vendors.tar.zst new file mode 100644 index 00000000..633ce94d Binary files /dev/null and b/crates/fbuild-core/data/usb-vendors.tar.zst differ diff --git a/crates/fbuild-core/src/usb/embedded.rs b/crates/fbuild-core/src/usb/embedded.rs new file mode 100644 index 00000000..01dff7d9 --- /dev/null +++ b/crates/fbuild-core/src/usb/embedded.rs @@ -0,0 +1,260 @@ +//! Compile-time-embedded USB VID → vendor-name map. +//! +//! Replaces the runtime dependency on the `usb-ids` Rust crate. The blob is +//! produced by `online-data-tools/build_vendor_archive.py` and lives at +//! `crates/fbuild-core/data/usb-vendors.tar.zst`. See that script + the +//! `data/README.md` for the refresh workflow. +//! +//! Compact format inside the tar (`usb-vendors.txt`): +//! ```text +//! vid:vendor,vid:vendor,... +//! ``` +//! where `vid` is 4-hex-digit lowercase and `vendor` has `,` and `%` +//! escaped per RFC 3986. See `parse_compact` for the inflater and +//! `online-data-tools/build_vendor_archive.py::pack_compact` for the +//! producer counterpart. +//! +//! Lookup is `O(1)` after the first call: the tar is decompressed + +//! parsed exactly once into a `HashMap` behind a `OnceLock`. +//! Decompression cost is paid lazily — callers that never touch USB +//! resolution don't pay it at all. + +use std::collections::HashMap; +use std::io::Read; +use std::sync::OnceLock; + +/// Lock-step with `build_vendor_archive.py::SCHEMA_VERSION`. Bump both +/// sides whenever the archive layout changes; the consumer refuses to +/// load an archive whose schema is newer than this constant. +pub const EMBEDDED_SCHEMA_VERSION: u64 = 2; + +const RAW_ARCHIVE: &[u8] = include_bytes!("../../data/usb-vendors.tar.zst"); + +static VENDOR_MAP: OnceLock> = OnceLock::new(); + +/// Look up the vendor name for a USB VID. Returns `None` if the embedded +/// archive doesn't carry that VID — callers should fall through to the +/// online overlay (`usb::data::lookup`) before reporting "unknown". +pub fn vendor_name(vid: u16) -> Option<&'static str> { + VENDOR_MAP + .get_or_init(load_or_panic) + .get(&vid) + .map(|s| s.as_str()) +} + +/// Number of vendor entries in the embedded archive. Mostly useful in +/// tests to detect accidental truncation. +pub fn embedded_vendor_count() -> usize { + VENDOR_MAP.get_or_init(load_or_panic).len() +} + +fn load_or_panic() -> HashMap { + match load() { + Ok(m) => m, + Err(e) => { + // A corrupt embedded archive is a build-config bug, not a + // runtime condition we can recover from. Panicking here surfaces + // it loudly the first time anything in fbuild touches a USB + // device rather than silently degrading to "unknown vendor". + panic!("fbuild-core: embedded usb-vendors.tar.zst is unusable: {e}"); + } + } +} + +#[derive(Debug)] +enum LoadError { + Zstd(String), + Tar(String), + MissingPayload, + SchemaTooNew { found: u64, max: u64 }, + BadManifest(String), +} + +impl std::fmt::Display for LoadError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Zstd(s) => write!(f, "zstd decompress failed: {s}"), + Self::Tar(s) => write!(f, "tar extract failed: {s}"), + Self::MissingPayload => f.write_str("archive missing usb-vendors.txt"), + Self::SchemaTooNew { found, max } => write!( + f, + "embedded archive schema_version={found} exceeds consumer max={max}; \ + bump EMBEDDED_SCHEMA_VERSION in fbuild-core::usb::embedded after \ + confirming the consumer supports the new format" + ), + Self::BadManifest(s) => write!(f, "manifest.json invalid: {s}"), + } + } +} + +fn load() -> Result, LoadError> { + let mut decoded = Vec::with_capacity(RAW_ARCHIVE.len() * 8); + zstd::stream::copy_decode(RAW_ARCHIVE, &mut decoded) + .map_err(|e| LoadError::Zstd(e.to_string()))?; + + let mut payload: Option = None; + let mut manifest: Option = None; + let mut archive = tar::Archive::new(decoded.as_slice()); + for entry in archive.entries().map_err(|e| LoadError::Tar(e.to_string()))? { + let mut entry = entry.map_err(|e| LoadError::Tar(e.to_string()))?; + let path = entry + .path() + .map_err(|e| LoadError::Tar(e.to_string()))? + .to_string_lossy() + .into_owned(); + let mut buf = String::new(); + entry + .read_to_string(&mut buf) + .map_err(|e| LoadError::Tar(e.to_string()))?; + match path.as_str() { + "usb-vendors.txt" => payload = Some(buf), + "manifest.json" => manifest = Some(buf), + _ => {} // forward-compat — ignore unknown extras + } + } + + if let Some(m) = manifest { + let parsed: serde_json::Value = + serde_json::from_str(&m).map_err(|e| LoadError::BadManifest(e.to_string()))?; + let v = parsed + .get("schema_version") + .and_then(|x| x.as_u64()) + .ok_or_else(|| LoadError::BadManifest("schema_version missing".into()))?; + if v > EMBEDDED_SCHEMA_VERSION { + return Err(LoadError::SchemaTooNew { + found: v, + max: EMBEDDED_SCHEMA_VERSION, + }); + } + } + + let payload = payload.ok_or(LoadError::MissingPayload)?; + Ok(parse_compact(&payload)) +} + +/// Parse the compact `vid:name,vid:name,...` format into a lookup table. +/// Mirror of `build_vendor_archive.py::parse_compact` — keep in sync. +fn parse_compact(s: &str) -> HashMap { + let mut out = HashMap::new(); + for chunk in s.split(',') { + if chunk.is_empty() { + continue; + } + let Some((vid_hex, name_esc)) = chunk.split_once(':') else { + continue; + }; + let Ok(vid) = u16::from_str_radix(vid_hex, 16) else { + continue; + }; + out.insert(vid, unescape(name_esc)); + } + out +} + +fn unescape(s: &str) -> String { + // Inverse of `_ESCAPE_RE` in build_vendor_archive.py. The producer only + // ever emits ASCII `%XX` escapes (for `,` and `%`); we intentionally do + // NOT decode multi-byte `%XX` runs here because that would require + // assembling UTF-8 byte sequences and the producer never generates + // them anyway — non-ASCII characters always pass through as raw UTF-8. + let mut out = String::with_capacity(s.len()); + let bytes = s.as_bytes(); + let mut i = 0; + while i < bytes.len() { + if bytes[i] == b'%' && i + 2 < bytes.len() { + if let (Some(hi), Some(lo)) = + (hex_nibble(bytes[i + 1]), hex_nibble(bytes[i + 2])) + { + let byte = hi * 16 + lo; + if byte < 0x80 { + out.push(byte as char); + i += 3; + continue; + } + // 0x80..=0xFF: leave the `%XX` as a literal — see comment. + } + } + // Step by one UTF-8 char so multi-byte sequences stay intact. + let ch_start = i; + let mut ch_end = i + 1; + while ch_end < bytes.len() && (bytes[ch_end] & 0xC0) == 0x80 { + ch_end += 1; + } + out.push_str(&s[ch_start..ch_end]); + i = ch_end; + } + out +} + +fn hex_nibble(b: u8) -> Option { + match b { + b'0'..=b'9' => Some(b - b'0'), + b'a'..=b'f' => Some(b - b'a' + 10), + b'A'..=b'F' => Some(b - b'A' + 10), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn embedded_archive_loads_and_parses() { + let n = embedded_vendor_count(); + assert!( + n > 500, + "embedded archive looks truncated: only {n} vendor entries" + ); + } + + #[test] + fn embedded_resolves_well_known_vids() { + // These are the headline VIDs the curated overlay was created to + // ensure — see issue FastLED/fbuild#718. If they vanish, the www + // page's headline "what board is this VID:PID?" query degrades. + // These need to be substrings the canonical upstream `usb.ids` + // text database actually emits (since vendor-override mode does + // not REPLACE names the upstream already has — see overlay + // mode semantics in online-data-tools/overlay_usb_vid.py). VIDs + // 0x303a and 0x2e8a are the ones the inlined supplement contributes. + for (vid, expected_substr) in [ + (0x303a_u16, "Espressif"), // inlined supplement only + (0x2e8a, "Raspberry Pi"), // inlined supplement only + (0x0403, "Future Technology"), // upstream + (0x10c4, "Silicon Lab"), // upstream may say "Cygnal" + (0x1a86, "QinHeng"), // upstream + (0x16c0, "Van Ooijen Technische"), // PJRC/Teensy via VOTI alloc + ] { + let name = vendor_name(vid).unwrap_or_else(|| { + panic!("embedded archive missing vendor for VID 0x{vid:04X}") + }); + assert!( + name.to_lowercase().contains(&expected_substr.to_lowercase()), + "VID 0x{vid:04X}: expected substring {expected_substr:?}, got {name:?}" + ); + } + } + + #[test] + fn unknown_vid_returns_none() { + // 0xBADD is in the unallocated portion of the USB-IF range as of + // the 2026 snapshot. If a future archive picks it up the test + // can move to another reserved range. + assert!(vendor_name(0xBADD).is_none(), + "0xBADD unexpectedly present: {:?}", vendor_name(0xBADD)); + } + + #[test] + fn parse_compact_handles_escapes_and_unicode() { + // The producer only ever escapes `,` and `%` (both ASCII). Non-ASCII + // text passes through as raw UTF-8 — we verify both round-trip. + let s = "0001:plain,0002:has%2Ccomma,0003:has%25percent,0004:em\u{2014}dash"; + let m = parse_compact(s); + assert_eq!(m.get(&1).map(|s| s.as_str()), Some("plain")); + assert_eq!(m.get(&2).map(|s| s.as_str()), Some("has,comma")); + assert_eq!(m.get(&3).map(|s| s.as_str()), Some("has%percent")); + let v = m.get(&4).expect("vid 4 missing"); + assert!(v.contains('—'), "missing em-dash: {v:?}"); + } +} diff --git a/crates/fbuild-core/src/usb/mod.rs b/crates/fbuild-core/src/usb/mod.rs index 6669d240..56733eae 100644 --- a/crates/fbuild-core/src/usb/mod.rs +++ b/crates/fbuild-core/src/usb/mod.rs @@ -2,15 +2,17 @@ //! //! Three resolution tiers, queried in order: //! -//! 1. **Bundled** — the [`usb-ids`](https://crates.io/crates/usb-ids) crate, -//! compiled in at build time as a `phf` perfect-hash table. Zero IO, zero -//! allocations for the lookup itself. Tracks the upstream -//! `linux-usb.org` snapshot the crate was published against. -//! 2. **Online overlay** — an optional `{ "VVVV:PPPP": {vendor, product} }` -//! JSON map loaded at runtime (typically from a daemon-managed cache file -//! that mirrors the `online-data` branch of this repo). The overlay -//! provides newly-assigned VID/PID pairs that the bundled snapshot -//! doesn't yet know about. +//! 1. **Online overlay** — an optional `{ "VVVV:PPPP": {vendor, product} }` +//! JSON map loaded at runtime (typically from a daemon-managed cache +//! file that mirrors the `online-data` branch of this repo). This is +//! the richest source — it has both vendor AND product names — and is +//! queried first. +//! 2. **Embedded vendor archive** — a 22 KB `tar.zst` blob compiled in +//! via `include_bytes!` (see [`embedded`]). Vendor names only — for +//! VIDs the overlay doesn't carry, we resolve the vendor offline and +//! synthesize `"Device 0xPPPP"` as the product placeholder. Per-PID +//! detail is intentionally not bundled — clients can hit the +//! SQLite-over-HTTP database on the `www` branch for that. //! 3. **Fallback** — synthetic `"Unknown vendor 0xVVVV"` placeholder so //! callers can always print something deterministic. //! @@ -25,7 +27,9 @@ //! branch — see [`MANIFEST_URL`] and [`USB_VID_JSON_URL`]. pub mod data; +pub mod embedded; pub mod resolver; pub use data::{install_online_cache, MANIFEST_URL, USB_VID_JSON_URL}; +pub use embedded::vendor_name as embedded_vendor_name; pub use resolver::{pretty, resolve, resolve_bundled, try_resolve, UsbInfo}; diff --git a/crates/fbuild-core/src/usb/resolver.rs b/crates/fbuild-core/src/usb/resolver.rs index 5c3d917d..f6ff7dd0 100644 --- a/crates/fbuild-core/src/usb/resolver.rs +++ b/crates/fbuild-core/src/usb/resolver.rs @@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize}; +use super::embedded; + /// Resolved USB device identity. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct UsbInfo { @@ -12,7 +14,8 @@ pub struct UsbInfo { /// Best-effort lookup. Never returns `None`: a synthetic /// `"Unknown vendor 0xVVVV"` / `"Unknown product 0xPPPP"` is produced -/// when both tier-1 (bundled) and tier-2 (online overlay) miss. +/// when both tier-1 (embedded vendor archive) and tier-2 (online overlay) +/// miss. pub fn resolve(vid: u16, pid: u16) -> UsbInfo { try_resolve(vid, pid).unwrap_or_else(|| UsbInfo { vendor: format!("Unknown vendor 0x{vid:04X}"), @@ -21,18 +24,31 @@ pub fn resolve(vid: u16, pid: u16) -> UsbInfo { } /// Tier-1 + tier-2 only. Returns `None` if neither knows this pair. +/// +/// Tier order is reversed from the old `usb-ids`-backed implementation: +/// the online overlay carries the full `{vendor, product}` aggregate +/// (it ingests the bundled Rust crate dump on the `online-data` branch +/// at workflow time), while the embedded vendor archive is intentionally +/// vendor-name-only. We consult the overlay first because it has more +/// information; we only fall through to the embedded archive when the +/// overlay misses the VID entirely. pub fn try_resolve(vid: u16, pid: u16) -> Option { - resolve_bundled(vid, pid).or_else(|| super::data::lookup(vid, pid)) + if let Some(info) = super::data::lookup(vid, pid) { + return Some(info); + } + resolve_bundled(vid, pid) } -/// Tier-1 only (the bundled `usb-ids` crate). Use when callers need to -/// distinguish "the offline snapshot knows this" from "we had to fall -/// through to the online overlay" — diagnostics, attribution, etc. +/// Tier-1 only (the compile-time-embedded vendor archive). The embedded +/// archive carries vendor names only — see +/// `crates/fbuild-core/data/usb-vendors.tar.zst`. For VIDs present in the +/// archive, the returned `UsbInfo.product` is a synthetic `"Device 0xPPPP"` +/// placeholder since per-PID resolution lives in the runtime overlay +/// (tier-2) and the www-branch SQLite-over-HTTP database. pub fn resolve_bundled(vid: u16, pid: u16) -> Option { - let device = usb_ids::Device::from_vid_pid(vid, pid)?; - Some(UsbInfo { - vendor: device.vendor().name().to_string(), - product: device.name().to_string(), + embedded::vendor_name(vid).map(|vendor| UsbInfo { + vendor: vendor.to_string(), + product: format!("Device 0x{pid:04X}"), }) } @@ -54,87 +70,78 @@ mod tests { static OVERLAY_LOCK: Mutex<()> = Mutex::new(()); #[test] - fn bundled_resolves_ftdi_ft232() { - let info = resolve_bundled(0x0403, 0x6001).expect("FTDI FT232 in bundled DB"); + fn embedded_resolves_ftdi_vendor() { + let info = resolve_bundled(0x0403, 0x6001).expect("FTDI VID in embedded archive"); assert!( - info.vendor.to_lowercase().contains("future technology"), + info.vendor.to_lowercase().contains("future technology") + || info.vendor.to_lowercase().contains("ftdi"), "vendor: {}", info.vendor ); - assert!( - info.product.to_lowercase().contains("ft232"), - "product: {}", - info.product - ); + // Tier-1 product is intentionally synthetic — the real product + // name lives in the runtime overlay (tier-2). + assert_eq!(info.product, "Device 0x6001"); } #[test] - fn bundled_resolves_silabs_cp210x() { - let info = resolve_bundled(0x10C4, 0xEA60).expect("Silicon Labs CP210x in bundled DB"); + fn embedded_resolves_silabs_vendor() { + let info = resolve_bundled(0x10C4, 0xEA60).expect("Silicon Labs VID in embedded archive"); assert!( - info.vendor.to_lowercase().contains("silicon labs") + info.vendor.to_lowercase().contains("silicon lab") || info.vendor.to_lowercase().contains("cygnal"), "vendor: {}", info.vendor ); - assert!( - info.product.to_lowercase().contains("cp210"), - "product: {}", - info.product - ); } #[test] - fn bundled_resolves_wch_ch340() { - let info = resolve_bundled(0x1A86, 0x7523).expect("WCH CH340 in bundled DB"); + fn embedded_resolves_espressif_via_inlined_supplement() { + // 0x303a is missing from every canonical text database we mirror + // — the curated inlined supplement (online-data-tools/ + // vendor_names_inlined.py) injects it during the workflow's + // merge step, and the resulting tar.zst is the embedded archive. + // This test pins the round-trip: curated overlay → embedded + // archive → fbuild runtime resolution. + let info = resolve_bundled(0x303A, 0x4002).expect("Espressif in embedded archive"); assert!( - info.vendor.to_lowercase().contains("qinheng") - || info.vendor.to_lowercase().contains("wch") - || info.vendor.to_lowercase().contains("nanjing"), + info.vendor.to_lowercase().contains("espressif"), "vendor: {}", info.vendor ); - assert!( - info.product.to_lowercase().contains("ch340") - || info.product.to_lowercase().contains("serial"), - "product: {}", - info.product - ); } #[test] fn unknown_pair_returns_synthetic_placeholder() { - // 0xFFFE:0xFFFE is reserved and will not be assigned by USB-IF; - // safe sentinel for "we expect tier-3 to fire." - let info = resolve(0xFFFE, 0xFFFE); - assert_eq!(info.vendor, "Unknown vendor 0xFFFE"); - assert_eq!(info.product, "Unknown product 0xFFFE"); + // 0xBADD:0xBADD is reserved and will not be assigned by USB-IF. + let info = resolve(0xBADD, 0xBADD); + assert_eq!(info.vendor, "Unknown vendor 0xBADD"); + assert_eq!(info.product, "Unknown product 0xBADD"); } #[test] fn pretty_format_uses_canonical_shape() { - // FTDI FT232 is one of the most stable VID:PIDs in the bundled DB - // (it's the de-facto USB-serial chip used in every Arduino clone). + // FTDI is in the embedded archive — vendor resolves, product is + // synthetic so the tail is deterministic. let s = pretty(0x0403, 0x6001); assert!(s.ends_with("(0403:6001)"), "tail format wrong: {s}"); assert!( - s.to_lowercase().contains("future technology"), + s.to_lowercase().contains("future technology") + || s.to_lowercase().contains("ftdi"), "missing vendor: {s}" ); - // Pretty also handles the unknown path deterministically. - let unknown = pretty(0xFFFE, 0xFFFE); + // Unknown path stays deterministic. + let unknown = pretty(0xBADD, 0xBADD); assert_eq!( unknown, - "Unknown vendor 0xFFFE Unknown product 0xFFFE (FFFE:FFFE)" + "Unknown vendor 0xBADD Unknown product 0xBADD (BADD:BADD)" ); } #[test] - fn online_overlay_resolves_when_bundled_misses() { + fn online_overlay_resolves_when_embedded_misses() { let _guard = OVERLAY_LOCK.lock().unwrap(); - // Use a VID:PID that the bundled `usb-ids` crate cannot resolve - // (0xFFFD:0xABCD is reserved). Install an overlay entry for it and - // confirm `resolve()` picks tier-2 instead of falling to tier-3. + // Pick a VID:PID that the embedded archive cannot resolve. + // 0xFFFD is reserved by USB-IF. assert!( resolve_bundled(0xFFFD, 0xABCD).is_none(), "test fixture assumed an unallocated VID:PID; pick a different one" @@ -153,7 +160,26 @@ mod tests { assert_eq!(info.vendor, "Acme Test Devices"); assert_eq!(info.product, "Test Widget 9000"); - // Reset so unrelated tests don't observe this entry. + super::super::data::clear_online_cache_for_tests(); + } + + #[test] + fn online_overlay_wins_over_embedded_for_same_vid() { + // Overlay has tier priority — if a VID is in BOTH the embedded + // archive and the overlay, the overlay's richer entry wins. + let _guard = OVERLAY_LOCK.lock().unwrap(); + let mut map = HashMap::new(); + map.insert( + super::super::data::pack(0x0403, 0x6001), + UsbInfo { + vendor: "FTDI Official".to_string(), + product: "FT232 Serial Converter".to_string(), + }, + ); + super::super::data::install_online_cache_map(map); + let info = resolve(0x0403, 0x6001); + assert_eq!(info.vendor, "FTDI Official"); + assert_eq!(info.product, "FT232 Serial Converter"); super::super::data::clear_online_cache_for_tests(); } } diff --git a/ids.json b/ids.json new file mode 100644 index 00000000..9e800a29 --- /dev/null +++ b/ids.json @@ -0,0 +1,255 @@ +{ + "0000": "Wrong vendor ID", + "0010": "TSI Incorporated", + "0017": "Meyer Instruments (MIS)", + "0024": "Numark Mixtrack", + "0028": "beyerdynamic GmbH & Co. KG beyerdynamic PRO X", + "00f9": "UWP WBDI Device", + "0154": "LW154 Wireless 150N Adapter", + "015c": "Tecno World", + "0280": "CAM(Dongle) [Freenet TV-Stick]", + "0284": "\"FX-USB-AW/-BD\" USB/RS482 Converters, Mitsubishi Electric Corp.", + "0b9a": "Namco Limited", + "0c7c": "TMS International BV", + "0cc7": "Kontron Medical AG", + "0ea3": "RION NL-52 Sound Level Meter", + "0fb4": "TiiTuii Co., Ltd.", + "1021": "Western Digital External HDD", + "103e": "Aim-TTi", + "10a4": "Gunding Cosmopolit 7 Web", + "1105": "Sigma Designs Inc.", + "1106": "VIA Technologies, Inc.", + "1180": "Ricoh Company, Ltd.", + "12c9": "Newmen Tech., Ltd.", + "1305": "Star Micronics", + "1354": "FACTS Engineering LLC", + "14b7": "In2Games Limited", + "14e4": "Broadcom Corp.", + "1556": "CERN", + "15d3": "Symmetric Research", + "1609": "Flash", + "1642": "DataTraveler 101 8GB", + "1662": "Quantum Mini", + "16bd": "Leica Geosystems AG", + "1747": "CML Microcircuits", + "1768": "Unify Software and Solutions GmbH & Co. KG OpenStage WL3 VoWLAN IP phone", + "1778": "IPEVO Inc.", + "1802": "TS5000 series", + "1825": "STAR-Dundee Ltd.", + "182d": "Sitecom Europe B.V.", + "1856": "PIXMA TS6250", + "1902": "Endoscope Camera HD", + "1912": "Renesas Technology Corp.", + "19d9": "Denso Ten Limited", + "1a17": "Oticon A/S", + "1a29": "ABOV Semiconductor Co., Ltd.", + "1a59": "000A RM01 [Haag Streit]", + "1a90": "Corsair Voyager GT 16GB", + "1ac2": "DESKO GmbH", + "1b17": "EXO S.A.", + "1b21": "ASMedia Technology Inc.", + "1b3d": "Matrix Orbital", + "1b4f": "SparkFun", + "1cd7": "GMC-Instruments GmbH", + "1d37": "Signal Processing Devices Sweden AB", + "1d6c": "AUKEY Technology Co., Ltd.", + "1d73": "Signal Processing Devices AB", + "1de7": "0113 [Duet Executive]", + "1e3a": "Continental Automotive Systems Inc.", + "1e9b": "NetCom Sicherheitstechnik GmbH", + "1f18": "TESEQ", + "1f29": "Analogix Semiconductor, Inc.", + "1f36": "ddm hopt+schuler", + "1f71": "Gadmei Electronic Technology Corporation", + "1f85": "Netronix, Inc.", + "1fb9": "Lake Shore Cryotronics, Inc.", + "1fd2": "MELFAS Co. Ltd.", + "2008": "Novanta Inc.", + "2017": "OSMC Remote Controller", + "201b": "Shenzhen Hui Mao Technology (Sinocan)", + "2020": "BroadMobi", + "2022": "Antec", + "20d6": "Bensussen Deutsch & Associates", + "20e7": "Atik CCD Camera", + "2164": "Witek System Inc.", + "2179": "Flex design tablet", + "217c": "TempTale, Sensitech", + "21b0": "Grace Industries", + "21c4": "Longsys Electronics (HK) Co., Ltd.", + "21e1": "CAEN S.p.A.", + "222d": "Leifheit - Soehnle", + "2239": "PEIKER acustic GmbH & Co., KG", + "223b": "Crystalfontz America, Inc.", + "2252": "HBGIC Technology Co., Ltd.", + "2257": "On-The-Go-Video", + "22f4": "Olive V-ME102 CDMA modem", + "230a": "DataLocker", + "2312": "LP320B Wireless Presenter [August International]", + "231d": "VKB-sim", + "2321": "iKingdom Corp. [iConnectivity]", + "2342": "NIKO", + "2358": "NuTesla Composite HID+CDC", + "23e3": "Christie Digital Systems", + "23e5": "Antelope Audio", + "23e8": "Propellerhead", + "2472": "TOP", + "247f": "Lynx", + "2541": "Chipsailing", + "2550": "Shenzhen EDUP Electronics Technology Co., Ltd.", + "256c": "HUION", + "258a": "AUKEY", + "25bc": "CETRTA POT", + "2622": "MISSION", + "263c": "SCHULTES", + "2660": "Test", + "2669": "M4S PSK Series Device [M4S PSK]", + "26ce": "ASRock", + "2752": "miniDSP", + "2763": "Primes GmbH", + "276d": "276d:1101", + "2853": "Ralston Instruments, LLC", + "28ba": "Materialise Motion NV", + "2909": "Game Golf Live", + "2914": "Kent Displays, Inc.", + "291a": "Anker Innovation Ltd", + "2947": "Kapelse", + "2982": "Ableton AG", + "2983": "Coyote System SAS", + "29cc": "Kodak Alaris, Inc", + "29df": "CAM(Dongle) [Freenet TV-Stick]", + "29f3": "Resonessence Labs", + "29fe": "Geo Semiconductor", + "2a52": "L Card, LLC", + "2a65": "FreeWave Technologies", + "2a94": "G2touch Co., LTD.", + "2afd": "McIntosh HD USB Audio [McIntosh DA1]", + "2b04": "Duo with WiFi and BLE", + "2b16": "Doccamera", + "2b71": "Flashforge [FlashForge Creator Pro 2 3D Printer]", + "2b73": "Pioneer DJ Corporation", + "2b89": "Unknown", + "2b98": "Glenair Inc.", + "2bd9": "Kubicam", + "2beb": "Gateworks Corporation", + "2c33": "Wizapply", + "2cc8": "Hewlett Packard Enterprise", + "2ce4": "ESMART CCID Device", + "2d01": "Guangdong Zike Technology Co., Ltd", + "2dbc": "Mikroelektronika d.o.o", + "2dee": "QUALCOMM MeigLink", + "2e3c": "Joy-IT", + "2e50": "beyerdynamic GmbH & Co. KG", + "2e8a": "Raspberry Pi Foundation", + "2ea1": "DASAN Electron Co", + "2eb9": "Realtek or Sabrent?", + "2efd": "Filco Co., Ltd.", + "2f68": "Hoksi Technology", + "2fd0": "C*Core Technology Co., Ltd.", + "2fe9": "Shenzhen Xintai Technology Co. Ltd", + "2fee": "Holitech", + "300c": "Gyrfalcon Technology Inc.", + "303a": "Espressif Systems", + "30b1": "Bitmain Technologies Inc.", + "30be": "Schiit Audio", + "30d6": "Chroma-Q", + "30de": "KIOXIA EXCERIA PLUS", + "30fa": "Manhattan", + "311f": "TrustKey Co., Ltd.", + "3131": "Jose Correa", + "3151": "Unknown", + "31b1": "SELPHY CP530", + "31b2": "KTMicro", + "31e3": "Wooting", + "31e9": "Solid State Logic, Ltd", + "320f": "Glorious LLC", + "3231": "Kneron, Inc.", + "3232": "CCTV Dome Camera", + "3274": "MicroArray", + "3285": "Nacon", + "3297": "ZSA Technology Labs Inc.", + "32a3": "GoTrust", + "32ac": "Framework Computer BV", + "32cd": "NECパーソナルコンピュータ株式会社", + "32e4": "ELP-USBFHD06H-BL36IR", + "32e6": "IcSpring Technology", + "332d": "Verbatim GmbH", + "3346": "Cvitek Co. Ltd.", + "335e": "Eight Amps", + "33be": "Syncopated Engineering, Inc.", + "33c4": "Tomahawk Robotics", + "33c8": "Seidl Technologies UG", + "33dd": "Zuki Inc", + "33f7": "Linux Automation GmbH", + "33f8": "Rolling Wireless S.a.r.l.", + "33ff": "nyantec GmbH", + "3434": "Keychron", + "344f": "SCX-3400 Series", + "3455": "Atomos Global Pty Ltd", + "345f": "MacroSilicon", + "3464": "Senscomm Semiconductor, Inc", + "346d": "VendorCo", + "346e": "Gudsen Technology (HK) Co., Ltd (MOZA)", + "349c": "Zhuhai Hongxin Technology Co., Ltd", + "349e": "Token2", + "3542": "Sonova Consumer Hearing", + "3544": "Rusoku technologijos UAB", + "3553": "PCsensor", + "359f": "Shenzhen Sipeed Technology Co., Ltd.", + "35b6": "Orqa d.o.o", + "35f0": "Bitcraze AB", + "35f1": "INFICON", + "369a": "HighSecLabs, Ltd", + "36da": "Record Sure Limited [Recordsure]", + "36e9": "ifanr Inc.", + "3760": "CIN-ergy B.V.", + "37c5": "OpenMV, LLC", + "3802": "LDA Technologies LTD", + "3817": "SleepImage", + "3842": "EVGA", + "386e": "XTX Markets", + "3876": "Fenice Power Co., Ltd", + "38c5": "JetHome LLC", + "3938": "MOSART Semiconductor", + "3c93": "QingDao Topscomm", + "413d": "RDing Tech Co aka PCsensor", + "4816": "Integrated Webcam", + "4c4a": "JieLi Technology", + "4e4c": "NieL™ TechSolution", + "5041": "Linksys (?)", + "5246": "bladeRF Software Defined Radio", + "5262": "X.Tips", + "5325": "Woolworth GmbH", + "573c": "Xreal Light Microcontroller", + "5888": "3TR EMU", + "6004": "ISD-V4 Tablet Pen", + "6005": "Hewlett-Packard", + "6495": "GoDEX International Co.", + "6964": "Idobo", + "7374": "DATA MODUL", + "7712": "2711 Temperature sensor HUB [SEIICHI]", + "7777": "SEIICHI Technology Co., Ltd.", + "8347": "VisTrend Co., Ltd.", + "8888": "inLight", + "9048": "NuTesla CDC Serial Emulator", + "9e8f": "Plug Computer Basic [SheevaPlug]", + "a69c": "AICSEMI", + "a8f8": "Bastard Keyboards", + "b2c3": "GNDHog", + "b711": "VuPlus", + "c069": "M500 Laser Mouse", + "c07c": "M-R0017 [G700s Rechargeable Gaming Mouse]", + "c0f4": "DualMiner", + "c580": "HID UNIKEYdongle [F-Response]", + "c5cb": "ARTECH (Artech Technology Design Co., Ltd.)", + "d13e": "Coldcard Wallet", + "dff0": "shapinb", + "e2b5": "JieLi Technology", + "e3b5": "JieLi Technology", + "e5b7": "JieLi Technology", + "eb57": "ZhuHai JieLi Technology", + "eba4": "Aoboco", + "feed": "DOIO Keyboard", + "ffd2": "ZHONG-HUI ELECTRONICS CORP.", + "fffe": "Inland (MicroCenter brand)", + "ffff": "Unknown Thickness Gage" +} \ No newline at end of file diff --git a/ids.txt b/ids.txt new file mode 100644 index 00000000..81cc49eb --- /dev/null +++ b/ids.txt @@ -0,0 +1,253 @@ +291a +2947 +2982 +2983 +29cc +29df +29f3 +29fe +2a52 +2a65 +2a94 +2afd +2b04 +2b16 +2b71 +2b73 +2b89 +2b98 +2bd9 +2beb +2c33 +2cc8 +2ce4 +2d01 +2dbc +2dee +2e3c +2e50 +2e8a +2ea1 +2eb9 +2efd +2f68 +2fd0 +2fe9 +2fee +300c +303a +30b1 +30be +30d6 +30de +30fa +311f +3131 +3151 +31b2 +31e3 +31e9 +320f +3231 +3232 +3274 +3285 +3297 +32a3 +32ac +32cd +32e4 +32e6 +332d +3346 +335e +33be +33c4 +33c8 +33dd +33f7 +33f8 +33ff +3434 +3455 +345f +3464 +346d +346e +349c +349e +3542 +3544 +3553 +359f +35b6 +35f0 +35f1 +369a +36da +36e9 +3760 +37c5 +3802 +3817 +3842 +386e +3876 +38c5 +3c93 +413d +4c4a +4e4c +5041 +5246 +5262 +5325 +573c +5888 +6005 +6495 +6964 +7374 +7712 +7777 +8347 +8888 +a69c +a8f8 +b2c3 +b711 +c07c +c0f4 +c580 +c5cb +d13e +dff0 +e2b5 +e3b5 +e5b7 +eb57 +eba4 +feed +ffd2 +fffe +ffff +1cd7 +1d37 +1d6c +1d73 +1de7 +1e3a +1e9b +1f18 +1f29 +1f36 +1f71 +1f85 +1fb9 +1fd2 +2008 +2017 +201b +2020 +2022 +20d6 +20e7 +2164 +2179 +217c +21b0 +21c4 +21e1 +222d +2239 +223b +2252 +2257 +22f4 +230a +2312 +231d +2321 +2342 +2358 +23e3 +23e5 +23e8 +2472 +247f +2541 +2550 +256c +258a +25bc +2622 +263c +2660 +2669 +26ce +2752 +2763 +276d +2853 +28ba +2909 +2914 +0c7c +0ea3 +0b9a +14b7 +1556 +15d3 +1609 +1662 +16bd +1747 +1768 +1778 +1802 +1825 +182d +1856 +1902 +1912 +19d9 +1a17 +1a29 +1a59 +1ac2 +1b17 +1b21 +1b3d +1b4f +0000 +0017 +0028 +00f9 +015c +0fb4 +1021 +103e +10a4 +1106 +12c9 +1305 +1354 +1105 +1180 +0cc7 +31b1 +344f +3938 +4816 +6004 +9048 +9e8f +c069 +0010 +0024 +0154 +0280 +0284 +14e4 +1642 +1a90 \ No newline at end of file diff --git a/ids2.json b/ids2.json new file mode 100644 index 00000000..818caa5f --- /dev/null +++ b/ids2.json @@ -0,0 +1,789 @@ +{ + "0000": [ + "Wrong vendor ID" + ], + "0010": [ + "TSI Incorporated", + "Test" + ], + "0017": [ + "Meyer Instruments (MIS)" + ], + "0024": [ + "Numark Mixtrack" + ], + "0028": [ + "beyerdynamic GmbH & Co. KG beyerdynamic PRO X" + ], + "00f9": [ + "UWP WBDI Device" + ], + "0154": [ + "LW154 Wireless 150N Adapter" + ], + "015c": [ + "Tecno World" + ], + "0280": [ + "CAM(Dongle) [Freenet TV-Stick]" + ], + "0284": [ + "\"FX-USB-AW/-BD\" USB/RS482 Converters, Mitsubishi Electric Corp." + ], + "0b9a": [ + "Namco Limited" + ], + "0c7c": [ + "TMS International BV" + ], + "0cc7": [ + "Kontron Medical AG" + ], + "0ea3": [ + "RION NL-52 Sound Level Meter" + ], + "0fb4": [ + "TiiTuii Co., Ltd." + ], + "1021": [ + "Western Digital External HDD" + ], + "103e": [ + "Aim-TTi" + ], + "10a4": [ + "Gunding Cosmopolit 7 Web" + ], + "1105": [ + "Sigma Designs Inc." + ], + "1106": [ + "VIA Technologies, Inc." + ], + "1180": [ + "Ricoh Company, Ltd." + ], + "12c9": [ + "Newmen Tech., Ltd." + ], + "1305": [ + "Star Micronics" + ], + "1354": [ + "FACTS Engineering LLC" + ], + "14b7": [ + "In2Games Limited" + ], + "14e4": [ + "Broadcom Corp." + ], + "1556": [ + "CERN" + ], + "15d3": [ + "Symmetric Research" + ], + "1609": [ + "Flash" + ], + "1642": [ + "DataTraveler 101 8GB" + ], + "1662": [ + "Quantum Mini" + ], + "16bd": [ + "Leica Geosystems AG" + ], + "1747": [ + "CML Microcircuits" + ], + "1768": [ + "Unify Software and Solutions GmbH & Co. KG OpenStage WL3 VoWLAN IP phone" + ], + "1778": [ + "IPEVO Inc." + ], + "1802": [ + "TS5000 series" + ], + "1825": [ + "STAR-Dundee Ltd." + ], + "182d": [ + "Sitecom Europe B.V." + ], + "1856": [ + "PIXMA TS6250" + ], + "1902": [ + "Endoscope Camera HD" + ], + "1912": [ + "Renesas Technology Corp." + ], + "19d9": [ + "Denso Ten Limited" + ], + "1a17": [ + "Oticon A/S" + ], + "1a29": [ + "ABOV Semiconductor Co., Ltd." + ], + "1a59": [ + "000A RM01 [Haag Streit]" + ], + "1a90": [ + "Corsair Voyager GT 16GB" + ], + "1ac2": [ + "DESKO GmbH" + ], + "1b17": [ + "EXO S.A." + ], + "1b21": [ + "ASMedia Technology Inc." + ], + "1b3d": [ + "Matrix Orbital" + ], + "1b4f": [ + "SparkFun", + "Arduino Leonardo ATmega32U4 USB IO Board [MakeyMakey]" + ], + "1cd7": [ + "GMC-Instruments GmbH" + ], + "1d37": [ + "Signal Processing Devices Sweden AB" + ], + "1d6c": [ + "AUKEY Technology Co., Ltd." + ], + "1d73": [ + "Signal Processing Devices AB", + "Signal Processing Devices Sweden AB" + ], + "1de7": [ + "0113 [Duet Executive]" + ], + "1e3a": [ + "Continental Automotive Systems Inc." + ], + "1e9b": [ + "NetCom Sicherheitstechnik GmbH" + ], + "1f18": [ + "TESEQ" + ], + "1f29": [ + "Analogix Semiconductor, Inc." + ], + "1f36": [ + "ddm hopt+schuler" + ], + "1f71": [ + "Gadmei Electronic Technology Corporation" + ], + "1f85": [ + "Netronix, Inc.", + "Netronix, Inc. / Obreey", + "Rakuten, Inc." + ], + "1fb9": [ + "Lake Shore Cryotronics, Inc." + ], + "1fd2": [ + "MELFAS Co. Ltd." + ], + "2008": [ + "Novanta Inc." + ], + "2017": [ + "OSMC Remote Controller", + "NAL Research Corporation" + ], + "201b": [ + "Shenzhen Hui Mao Technology (Sinocan)", + "UNI-TEC Electronics" + ], + "2020": [ + "BroadMobi" + ], + "2022": [ + "Antec" + ], + "20d6": [ + "Bensussen Deutsch & Associates" + ], + "20e7": [ + "Atik CCD Camera" + ], + "2164": [ + "Witek System Inc." + ], + "2179": [ + "Flex design tablet" + ], + "217c": [ + "TempTale, Sensitech" + ], + "21b0": [ + "Grace Industries" + ], + "21c4": [ + "Longsys Electronics (HK) Co., Ltd." + ], + "21e1": [ + "CAEN S.p.A." + ], + "222d": [ + "Leifheit - Soehnle" + ], + "2239": [ + "PEIKER acustic GmbH & Co., KG" + ], + "223b": [ + "Crystalfontz America, Inc." + ], + "2252": [ + "HBGIC Technology Co., Ltd." + ], + "2257": [ + "On-The-Go-Video" + ], + "22f4": [ + "Olive V-ME102 CDMA modem" + ], + "230a": [ + "DataLocker" + ], + "2312": [ + "LP320B Wireless Presenter [August International]" + ], + "231d": [ + "VKB-sim" + ], + "2321": [ + "iKingdom Corp. [iConnectivity]" + ], + "2342": [ + "NIKO" + ], + "2358": [ + "NuTesla Composite HID+CDC" + ], + "23e3": [ + "Christie Digital Systems" + ], + "23e5": [ + "Antelope Audio" + ], + "23e8": [ + "Propellerhead" + ], + "2472": [ + "TOP" + ], + "247f": [ + "Lynx" + ], + "2541": [ + "Chipsailing" + ], + "2550": [ + "Shenzhen EDUP Electronics Technology Co., Ltd." + ], + "256c": [ + "HUION", + "Unknown tablet device", + "Huion / Gaomon", + "Paul M" + ], + "258a": [ + "AUKEY", + " [USB chips by: Sino Wealth Electronic Ltd.]" + ], + "25bc": [ + "CETRTA POT" + ], + "2622": [ + "MISSION" + ], + "263c": [ + "SCHULTES" + ], + "2660": [ + "Test" + ], + "2669": [ + "M4S PSK Series Device [M4S PSK]" + ], + "26ce": [ + "ASRock" + ], + "2752": [ + "miniDSP" + ], + "2763": [ + "Primes GmbH" + ], + "276d": [ + "276d:1101" + ], + "2853": [ + "Ralston Instruments, LLC" + ], + "28ba": [ + "Materialise Motion NV" + ], + "2909": [ + "Game Golf Live" + ], + "2914": [ + "Kent Displays, Inc.", + "Improv Electronics" + ], + "291a": [ + "Anker Innovation Ltd" + ], + "2947": [ + "Kapelse" + ], + "2982": [ + "Ableton AG" + ], + "2983": [ + "Coyote System SAS" + ], + "29cc": [ + "Kodak Alaris, Inc" + ], + "29df": [ + "CAM(Dongle) [Freenet TV-Stick]" + ], + "29f3": [ + "Resonessence Labs" + ], + "29fe": [ + "Geo Semiconductor", + "GEO Semiconductor" + ], + "2a52": [ + "L Card, LLC" + ], + "2a65": [ + "FreeWave Technologies" + ], + "2a94": [ + "G2touch Co., LTD." + ], + "2afd": [ + "McIntosh HD USB Audio [McIntosh DA1]" + ], + "2b04": [ + "Duo with WiFi and BLE" + ], + "2b16": [ + "Doccamera" + ], + "2b71": [ + "Flashforge [FlashForge Creator Pro 2 3D Printer]" + ], + "2b73": [ + "Pioneer DJ Corporation" + ], + "2b89": [ + "Unknown" + ], + "2b98": [ + "Glenair Inc." + ], + "2bd9": [ + "Kubicam", + "Huddly" + ], + "2beb": [ + "Gateworks Corporation" + ], + "2c33": [ + "Wizapply" + ], + "2cc8": [ + "Hewlett Packard Enterprise" + ], + "2ce4": [ + "ESMART CCID Device", + "ESMART" + ], + "2d01": [ + "Guangdong Zike Technology Co., Ltd" + ], + "2dbc": [ + "Mikroelektronika d.o.o" + ], + "2dee": [ + "QUALCOMM MeigLink" + ], + "2e3c": [ + "Joy-IT" + ], + "2e50": [ + "beyerdynamic GmbH & Co. KG" + ], + "2e8a": [ + "Raspberry Pi Foundation" + ], + "2ea1": [ + "DASAN Electron Co" + ], + "2eb9": [ + "Realtek or Sabrent?" + ], + "2efd": [ + "Filco Co., Ltd." + ], + "2f68": [ + "Hoksi Technology" + ], + "2fd0": [ + "C*Core Technology Co., Ltd." + ], + "2fe9": [ + "Shenzhen Xintai Technology Co. Ltd" + ], + "2fee": [ + "Holitech" + ], + "300c": [ + "Gyrfalcon Technology Inc." + ], + "303a": [ + "Espressif Systems" + ], + "30b1": [ + "Bitmain Technologies Inc." + ], + "30be": [ + "Schiit Audio" + ], + "30d6": [ + "Chroma-Q" + ], + "30de": [ + "KIOXIA EXCERIA PLUS" + ], + "30fa": [ + "Manhattan", + "Wuxi Instant Microelectronics Co., Ltd." + ], + "311f": [ + "TrustKey Co., Ltd." + ], + "3131": [ + "Jose Correa", + "Authentik Systems" + ], + "3151": [ + "Unknown", + "Dailan Bon Auto Electronic Co., Ltd.", + "Yichip Microelectronics (Hangzhou) Co., Ltd" + ], + "31b1": [ + "SELPHY CP530", + "Shenzhen Jinduan Electronics Co., Ltd." + ], + "31b2": [ + "KTMicro" + ], + "31e3": [ + "Wooting" + ], + "31e9": [ + "Solid State Logic, Ltd" + ], + "320f": [ + "Glorious LLC" + ], + "3231": [ + "Kneron, Inc." + ], + "3232": [ + "CCTV Dome Camera", + "Shenzhen Trusda Industrial Co., Ltd." + ], + "3274": [ + "MicroArray" + ], + "3285": [ + "Nacon" + ], + "3297": [ + "ZSA Technology Labs Inc." + ], + "32a3": [ + "GoTrust" + ], + "32ac": [ + "Framework Computer BV" + ], + "32cd": [ + "NECパーソナルコンピュータ株式会社", + "NEC" + ], + "32e4": [ + "ELP-USBFHD06H-BL36IR" + ], + "32e6": [ + "IcSpring Technology" + ], + "332d": [ + "Verbatim GmbH" + ], + "3346": [ + "Cvitek Co. Ltd." + ], + "335e": [ + "Eight Amps" + ], + "33be": [ + "Syncopated Engineering, Inc." + ], + "33c4": [ + "Tomahawk Robotics" + ], + "33c8": [ + "Seidl Technologies UG", + "Seidl Technologies UG (haftungsbeschraenkt)" + ], + "33dd": [ + "Zuki Inc" + ], + "33f7": [ + "Linux Automation GmbH" + ], + "33f8": [ + "Rolling Wireless S.a.r.l." + ], + "33ff": [ + "nyantec GmbH" + ], + "3434": [ + "Keychron" + ], + "344f": [ + "SCX-3400 Series" + ], + "3455": [ + "Atomos Global Pty Ltd" + ], + "345f": [ + "MacroSilicon" + ], + "3464": [ + "Senscomm Semiconductor, Inc" + ], + "346d": [ + "VendorCo" + ], + "346e": [ + "Gudsen Technology (HK) Co., Ltd (MOZA)" + ], + "349c": [ + "Zhuhai Hongxin Technology Co., Ltd" + ], + "349e": [ + "Token2" + ], + "3542": [ + "Sonova Consumer Hearing" + ], + "3544": [ + "Rusoku technologijos UAB" + ], + "3553": [ + "PCsensor" + ], + "359f": [ + "Shenzhen Sipeed Technology Co., Ltd." + ], + "35b6": [ + "Orqa d.o.o" + ], + "35f0": [ + "Bitcraze AB" + ], + "35f1": [ + "INFICON" + ], + "369a": [ + "HighSecLabs, Ltd" + ], + "36da": [ + "Record Sure Limited [Recordsure]" + ], + "36e9": [ + "ifanr Inc." + ], + "3760": [ + "CIN-ergy B.V." + ], + "37c5": [ + "OpenMV, LLC" + ], + "3802": [ + "LDA Technologies LTD" + ], + "3817": [ + "SleepImage" + ], + "3842": [ + "EVGA" + ], + "386e": [ + "XTX Markets" + ], + "3876": [ + "Fenice Power Co., Ltd" + ], + "38c5": [ + "JetHome LLC" + ], + "3938": [ + "MOSART Semiconductor" + ], + "3c93": [ + "QingDao Topscomm" + ], + "413d": [ + "RDing Tech Co aka PCsensor", + "RDing Technology Ltd [PCsensor]" + ], + "4816": [ + "Integrated Webcam" + ], + "4c4a": [ + "JieLi Technology" + ], + "4e4c": [ + "NieL™ TechSolution" + ], + "5041": [ + "Linksys (?)" + ], + "5246": [ + "bladeRF Software Defined Radio" + ], + "5262": [ + "X.Tips" + ], + "5325": [ + "Woolworth GmbH" + ], + "573c": [ + "Xreal Light Microcontroller" + ], + "5888": [ + "3TR EMU", + "3Tronics MU30" + ], + "6004": [ + "ISD-V4 Tablet Pen" + ], + "6005": [ + "Hewlett-Packard" + ], + "6495": [ + "GoDEX International Co." + ], + "6964": [ + "Idobo" + ], + "7374": [ + "DATA MODUL" + ], + "7712": [ + "2711 Temperature sensor HUB [SEIICHI]" + ], + "7777": [ + "SEIICHI Technology Co., Ltd." + ], + "8347": [ + "VisTrend Co., Ltd." + ], + "8888": [ + "inLight" + ], + "9048": [ + "NuTesla CDC Serial Emulator" + ], + "9e8f": [ + "Plug Computer Basic [SheevaPlug]" + ], + "a69c": [ + "AICSEMI" + ], + "a8f8": [ + "Bastard Keyboards" + ], + "b2c3": [ + "GNDHog" + ], + "b711": [ + "VuPlus" + ], + "c069": [ + "M500 Laser Mouse" + ], + "c07c": [ + "M-R0017 [G700s Rechargeable Gaming Mouse]" + ], + "c0f4": [ + "DualMiner", + "Generic USB Keyboard" + ], + "c580": [ + "HID UNIKEYdongle [F-Response]" + ], + "c5cb": [ + "ARTECH (Artech Technology Design Co., Ltd.)" + ], + "d13e": [ + "Coldcard Wallet" + ], + "dff0": [ + "shapinb" + ], + "e2b5": [ + "JieLi Technology" + ], + "e3b5": [ + "JieLi Technology" + ], + "e5b7": [ + "JieLi Technology" + ], + "eb57": [ + "ZhuHai JieLi Technology" + ], + "eba4": [ + "Aoboco" + ], + "feed": [ + "DOIO Keyboard", + "Unknown" + ], + "ffd2": [ + "ZHONG-HUI ELECTRONICS CORP." + ], + "fffe": [ + "Inland (MicroCenter brand)" + ], + "ffff": [ + "Unknown Thickness Gage", + "Wrong vendor ID" + ] +} diff --git a/ids3.json b/ids3.json new file mode 100644 index 00000000..1b9b0c8a --- /dev/null +++ b/ids3.json @@ -0,0 +1,761 @@ +{ + "0000": [ + "Wrong vendor ID" + ], + "0010": [ + "TSI Incorporated" + ], + "0017": [ + "Meyer Instruments (MIS)" + ], + "0024": [ + "Numark Mixtrack" + ], + "0028": [ + "beyerdynamic GmbH & Co. KG beyerdynamic PRO X" + ], + "00f9": [ + "UWP WBDI Device" + ], + "0154": [ + "LW154 Wireless 150N Adapter" + ], + "015c": [ + "Tecno World" + ], + "0280": [ + "CAM(Dongle) [Freenet TV-Stick]" + ], + "0284": [ + "\"FX-USB-AW/-BD\" USB/RS482 Converters, Mitsubishi Electric Corp." + ], + "0b9a": [ + "Namco Limited" + ], + "0c7c": [ + "TMS International BV" + ], + "0cc7": [ + "Kontron Medical AG" + ], + "0ea3": [ + "RION NL-52 Sound Level Meter" + ], + "0fb4": [ + "TiiTuii Co., Ltd." + ], + "1021": [ + "Western Digital External HDD" + ], + "103e": [ + "Aim-TTi" + ], + "10a4": [ + "Gunding Cosmopolit 7 Web" + ], + "1105": [ + "Sigma Designs Inc." + ], + "1106": [ + "VIA Technologies, Inc." + ], + "1180": [ + "Ricoh Company, Ltd." + ], + "12c9": [ + "Newmen Tech., Ltd." + ], + "1305": [ + "Star Micronics" + ], + "1354": [ + "FACTS Engineering LLC" + ], + "14b7": [ + "In2Games Limited" + ], + "14e4": [ + "Broadcom Corp." + ], + "1556": [ + "CERN" + ], + "15d3": [ + "Symmetric Research" + ], + "1609": [ + "Flash" + ], + "1642": [ + "DataTraveler 101 8GB" + ], + "1662": [ + "Quantum Mini" + ], + "16bd": [ + "Leica Geosystems AG" + ], + "1747": [ + "CML Microcircuits" + ], + "1768": [ + "Unify Software and Solutions GmbH & Co. KG OpenStage WL3 VoWLAN IP phone" + ], + "1778": [ + "IPEVO Inc." + ], + "1802": [ + "TS5000 series" + ], + "1825": [ + "STAR-Dundee Ltd." + ], + "182d": [ + "Sitecom Europe B.V." + ], + "1856": [ + "PIXMA TS6250" + ], + "1902": [ + "Endoscope Camera HD" + ], + "1912": [ + "Renesas Technology Corp." + ], + "19d9": [ + "Denso Ten Limited" + ], + "1a17": [ + "Oticon A/S" + ], + "1a29": [ + "ABOV Semiconductor Co., Ltd." + ], + "1a59": [ + "000A RM01 [Haag Streit]" + ], + "1a90": [ + "Corsair Voyager GT 16GB" + ], + "1ac2": [ + "DESKO GmbH" + ], + "1b17": [ + "EXO S.A." + ], + "1b21": [ + "ASMedia Technology Inc." + ], + "1b3d": [ + "Matrix Orbital" + ], + "1b4f": [ + "SparkFun" + ], + "1cd7": [ + "GMC-Instruments GmbH" + ], + "1d37": [ + "Signal Processing Devices Sweden AB" + ], + "1d6c": [ + "AUKEY Technology Co., Ltd." + ], + "1d73": [ + "Signal Processing Devices Sweden AB" + ], + "1de7": [ + "0113 [Duet Executive]" + ], + "1e3a": [ + "Continental Automotive Systems Inc." + ], + "1e9b": [ + "NetCom Sicherheitstechnik GmbH" + ], + "1f18": [ + "TESEQ" + ], + "1f29": [ + "Analogix Semiconductor, Inc." + ], + "1f36": [ + "ddm hopt+schuler" + ], + "1f71": [ + "Gadmei Electronic Technology Corporation" + ], + "1f85": [ + "Netronix, Inc. / Obreey" + ], + "1fb9": [ + "Lake Shore Cryotronics, Inc." + ], + "1fd2": [ + "MELFAS Co. Ltd." + ], + "2008": [ + "Novanta Inc." + ], + "2017": [ + "NAL Research Corporation" + ], + "201b": [ + "UNI-TEC Electronics" + ], + "2020": [ + "BroadMobi" + ], + "2022": [ + "Antec" + ], + "20d6": [ + "Bensussen Deutsch & Associates" + ], + "20e7": [ + "Atik CCD Camera" + ], + "2164": [ + "Witek System Inc." + ], + "2179": [ + "Flex design tablet" + ], + "217c": [ + "TempTale, Sensitech" + ], + "21b0": [ + "Grace Industries" + ], + "21c4": [ + "Longsys Electronics (HK) Co., Ltd." + ], + "21e1": [ + "CAEN S.p.A." + ], + "222d": [ + "Leifheit - Soehnle" + ], + "2239": [ + "PEIKER acustic GmbH & Co., KG" + ], + "223b": [ + "Crystalfontz America, Inc." + ], + "2252": [ + "HBGIC Technology Co., Ltd." + ], + "2257": [ + "On-The-Go-Video" + ], + "22f4": [ + "Olive V-ME102 CDMA modem" + ], + "230a": [ + "DataLocker" + ], + "2312": [ + "LP320B Wireless Presenter [August International]" + ], + "231d": [ + "VKB-sim" + ], + "2321": [ + "iKingdom Corp. [iConnectivity]" + ], + "2342": [ + "NIKO" + ], + "2358": [ + "NuTesla Composite HID+CDC" + ], + "23e3": [ + "Christie Digital Systems" + ], + "23e5": [ + "Antelope Audio" + ], + "23e8": [ + "Propellerhead" + ], + "2472": [ + "TOP" + ], + "247f": [ + "Lynx" + ], + "2541": [ + "Chipsailing" + ], + "2550": [ + "Shenzhen EDUP Electronics Technology Co., Ltd." + ], + "256c": [ + "HUION" + ], + "258a": [ + " [USB chips by: Sino Wealth Electronic Ltd.]" + ], + "25bc": [ + "CETRTA POT" + ], + "2622": [ + "MISSION" + ], + "263c": [ + "SCHULTES" + ], + "2660": [ + "Test" + ], + "2669": [ + "M4S PSK Series Device [M4S PSK]" + ], + "26ce": [ + "ASRock" + ], + "2752": [ + "miniDSP" + ], + "2763": [ + "Primes GmbH" + ], + "276d": [ + "276d:1101" + ], + "2853": [ + "Ralston Instruments, LLC" + ], + "28ba": [ + "Materialise Motion NV" + ], + "2909": [ + "Game Golf Live" + ], + "2914": [ + "Kent Displays, Inc." + ], + "291a": [ + "Anker Innovation Ltd" + ], + "2947": [ + "Kapelse" + ], + "2982": [ + "Ableton AG" + ], + "2983": [ + "Coyote System SAS" + ], + "29cc": [ + "Kodak Alaris, Inc" + ], + "29df": [ + "CAM(Dongle) [Freenet TV-Stick]" + ], + "29f3": [ + "Resonessence Labs" + ], + "29fe": [ + "Geo Semiconductor" + ], + "2a52": [ + "L Card, LLC" + ], + "2a65": [ + "FreeWave Technologies" + ], + "2a94": [ + "G2touch Co., LTD." + ], + "2afd": [ + "McIntosh HD USB Audio [McIntosh DA1]" + ], + "2b04": [ + "Duo with WiFi and BLE" + ], + "2b16": [ + "Doccamera" + ], + "2b71": [ + "Flashforge [FlashForge Creator Pro 2 3D Printer]" + ], + "2b73": [ + "Pioneer DJ Corporation" + ], + "2b89": [ + "Unknown" + ], + "2b98": [ + "Glenair Inc." + ], + "2bd9": [ + "Huddly" + ], + "2beb": [ + "Gateworks Corporation" + ], + "2c33": [ + "Wizapply" + ], + "2cc8": [ + "Hewlett Packard Enterprise" + ], + "2ce4": [ + "ESMART" + ], + "2d01": [ + "Guangdong Zike Technology Co., Ltd" + ], + "2dbc": [ + "Mikroelektronika d.o.o" + ], + "2dee": [ + "QUALCOMM MeigLink" + ], + "2e3c": [ + "Joy-IT" + ], + "2e50": [ + "beyerdynamic GmbH & Co. KG" + ], + "2e8a": [ + "Raspberry Pi Foundation" + ], + "2ea1": [ + "DASAN Electron Co" + ], + "2eb9": [ + "Realtek or Sabrent?" + ], + "2efd": [ + "Filco Co., Ltd." + ], + "2f68": [ + "Hoksi Technology" + ], + "2fd0": [ + "C*Core Technology Co., Ltd." + ], + "2fe9": [ + "Shenzhen Xintai Technology Co. Ltd" + ], + "2fee": [ + "Holitech" + ], + "300c": [ + "Gyrfalcon Technology Inc." + ], + "303a": [ + "Espressif Systems" + ], + "30b1": [ + "Bitmain Technologies Inc." + ], + "30be": [ + "Schiit Audio" + ], + "30d6": [ + "Chroma-Q" + ], + "30de": [ + "KIOXIA EXCERIA PLUS" + ], + "30fa": [ + "Wuxi Instant Microelectronics Co., Ltd." + ], + "311f": [ + "TrustKey Co., Ltd." + ], + "3131": [ + "Authentik Systems" + ], + "3151": [ + "Yichip Microelectronics (Hangzhou) Co., Ltd" + ], + "31b1": [ + "Shenzhen Jinduan Electronics Co., Ltd." + ], + "31b2": [ + "KTMicro" + ], + "31e3": [ + "Wooting" + ], + "31e9": [ + "Solid State Logic, Ltd" + ], + "320f": [ + "Glorious LLC" + ], + "3231": [ + "Kneron, Inc." + ], + "3232": [ + "Shenzhen Trusda Industrial Co., Ltd." + ], + "3274": [ + "MicroArray" + ], + "3285": [ + "Nacon" + ], + "3297": [ + "ZSA Technology Labs Inc." + ], + "32a3": [ + "GoTrust" + ], + "32ac": [ + "Framework Computer BV" + ], + "32cd": [ + "NEC" + ], + "32e4": [ + "ELP-USBFHD06H-BL36IR" + ], + "32e6": [ + "IcSpring Technology" + ], + "332d": [ + "Verbatim GmbH" + ], + "3346": [ + "Cvitek Co. Ltd." + ], + "335e": [ + "Eight Amps" + ], + "33be": [ + "Syncopated Engineering, Inc." + ], + "33c4": [ + "Tomahawk Robotics" + ], + "33c8": [ + "Seidl Technologies UG" + ], + "33dd": [ + "Zuki Inc" + ], + "33f7": [ + "Linux Automation GmbH" + ], + "33f8": [ + "Rolling Wireless S.a.r.l." + ], + "33ff": [ + "nyantec GmbH" + ], + "3434": [ + "Keychron" + ], + "344f": [ + "SCX-3400 Series" + ], + "3455": [ + "Atomos Global Pty Ltd" + ], + "345f": [ + "MacroSilicon" + ], + "3464": [ + "Senscomm Semiconductor, Inc" + ], + "346d": [ + "VendorCo" + ], + "346e": [ + "Gudsen Technology (HK) Co., Ltd (MOZA)" + ], + "349c": [ + "Zhuhai Hongxin Technology Co., Ltd" + ], + "349e": [ + "Token2" + ], + "3542": [ + "Sonova Consumer Hearing" + ], + "3544": [ + "Rusoku technologijos UAB" + ], + "3553": [ + "PCsensor" + ], + "359f": [ + "Shenzhen Sipeed Technology Co., Ltd." + ], + "35b6": [ + "Orqa d.o.o" + ], + "35f0": [ + "Bitcraze AB" + ], + "35f1": [ + "INFICON" + ], + "369a": [ + "HighSecLabs, Ltd" + ], + "36da": [ + "Record Sure Limited [Recordsure]" + ], + "36e9": [ + "ifanr Inc." + ], + "3760": [ + "CIN-ergy B.V." + ], + "37c5": [ + "OpenMV, LLC" + ], + "3802": [ + "LDA Technologies LTD" + ], + "3817": [ + "SleepImage" + ], + "3842": [ + "EVGA" + ], + "386e": [ + "XTX Markets" + ], + "3876": [ + "Fenice Power Co., Ltd" + ], + "38c5": [ + "JetHome LLC" + ], + "3938": [ + "MOSART Semiconductor" + ], + "3c93": [ + "QingDao Topscomm" + ], + "413d": [ + "RDing Technology Ltd [PCsensor]" + ], + "4816": [ + "Integrated Webcam" + ], + "4c4a": [ + "JieLi Technology" + ], + "4e4c": [ + "NieLTM TechSolution" + ], + "5041": [ + "Linksys (?)" + ], + "5246": [ + "bladeRF Software Defined Radio" + ], + "5262": [ + "X.Tips" + ], + "5325": [ + "Woolworth GmbH" + ], + "573c": [ + "Xreal Light Microcontroller" + ], + "5888": [ + "3Tronics MU30" + ], + "6004": [ + "ISD-V4 Tablet Pen" + ], + "6005": [ + "Hewlett-Packard" + ], + "6495": [ + "GoDEX International Co." + ], + "6964": [ + "Idobo" + ], + "7374": [ + "DATA MODUL" + ], + "7712": [ + "2711 Temperature sensor HUB [SEIICHI]" + ], + "7777": [ + "SEIICHI Technology Co., Ltd." + ], + "8347": [ + "VisTrend Co., Ltd." + ], + "8888": [ + "inLight" + ], + "9048": [ + "NuTesla CDC Serial Emulator" + ], + "9e8f": [ + "Plug Computer Basic [SheevaPlug]" + ], + "a69c": [ + "AICSEMI" + ], + "a8f8": [ + "Bastard Keyboards" + ], + "b2c3": [ + "GNDHog" + ], + "b711": [ + "VuPlus" + ], + "c069": [ + "M500 Laser Mouse" + ], + "c07c": [ + "M-R0017 [G700s Rechargeable Gaming Mouse]" + ], + "c0f4": [ + "DualMiner" + ], + "c580": [ + "HID UNIKEYdongle [F-Response]" + ], + "c5cb": [ + "ARTECH (Artech Technology Design Co., Ltd.)" + ], + "d13e": [ + "Coldcard Wallet" + ], + "dff0": [ + "shapinb" + ], + "e2b5": [ + "JieLi Technology" + ], + "e3b5": [ + "JieLi Technology" + ], + "e5b7": [ + "JieLi Technology" + ], + "eb57": [ + "ZhuHai JieLi Technology" + ], + "eba4": [ + "Aoboco" + ], + "feed": [ + "DOIO Keyboard" + ], + "ffd2": [ + "ZHONG-HUI ELECTRONICS CORP." + ], + "fffe": [ + "Inland (MicroCenter brand)" + ], + "ffff": [ + "Wrong vendor ID" + ] +} diff --git a/ids4.json b/ids4.json new file mode 100644 index 00000000..d25b63d2 --- /dev/null +++ b/ids4.json @@ -0,0 +1,255 @@ +{ + "0000": "Wrong vendor ID", + "0010": "TSI Incorporated", + "0017": "Meyer Instruments (MIS)", + "0024": "Numark Mixtrack", + "0028": "beyerdynamic GmbH & Co. KG beyerdynamic PRO X", + "00f9": "UWP WBDI Device", + "0154": "LW154 Wireless 150N Adapter", + "015c": "Tecno World", + "0280": "CAM(Dongle) [Freenet TV-Stick]", + "0284": "\"FX-USB-AW/-BD\" USB/RS482 Converters, Mitsubishi Electric Corp.", + "0b9a": "Namco Limited", + "0c7c": "TMS International BV", + "0cc7": "Kontron Medical AG", + "0ea3": "RION NL-52 Sound Level Meter", + "0fb4": "TiiTuii Co., Ltd.", + "1021": "Western Digital External HDD", + "103e": "Aim-TTi", + "10a4": "Gunding Cosmopolit 7 Web", + "1105": "Sigma Designs Inc.", + "1106": "VIA Technologies, Inc.", + "1180": "Ricoh Company, Ltd.", + "12c9": "Newmen Tech., Ltd.", + "1305": "Star Micronics", + "1354": "FACTS Engineering LLC", + "14b7": "In2Games Limited", + "14e4": "Broadcom Corp.", + "1556": "CERN", + "15d3": "Symmetric Research", + "1609": "Flash", + "1642": "DataTraveler 101 8GB", + "1662": "Quantum Mini", + "16bd": "Leica Geosystems AG", + "1747": "CML Microcircuits", + "1768": "Unify Software and Solutions GmbH & Co. KG OpenStage WL3 VoWLAN IP phone", + "1778": "IPEVO Inc.", + "1802": "TS5000 series", + "1825": "STAR-Dundee Ltd.", + "182d": "Sitecom Europe B.V.", + "1856": "PIXMA TS6250", + "1902": "Endoscope Camera HD", + "1912": "Renesas Technology Corp.", + "19d9": "Denso Ten Limited", + "1a17": "Oticon A/S", + "1a29": "ABOV Semiconductor Co., Ltd.", + "1a59": "000A RM01 [Haag Streit]", + "1a90": "Corsair Voyager GT 16GB", + "1ac2": "DESKO GmbH", + "1b17": "EXO S.A.", + "1b21": "ASMedia Technology Inc.", + "1b3d": "Matrix Orbital", + "1b4f": "SparkFun", + "1cd7": "GMC-Instruments GmbH", + "1d37": "Signal Processing Devices Sweden AB", + "1d6c": "AUKEY Technology Co., Ltd.", + "1d73": "Signal Processing Devices Sweden AB", + "1de7": "0113 [Duet Executive]", + "1e3a": "Continental Automotive Systems Inc.", + "1e9b": "NetCom Sicherheitstechnik GmbH", + "1f18": "TESEQ", + "1f29": "Analogix Semiconductor, Inc.", + "1f36": "ddm hopt+schuler", + "1f71": "Gadmei Electronic Technology Corporation", + "1f85": "Netronix, Inc. / Obreey", + "1fb9": "Lake Shore Cryotronics, Inc.", + "1fd2": "MELFAS Co. Ltd.", + "2008": "Novanta Inc.", + "2017": "NAL Research Corporation", + "201b": "UNI-TEC Electronics", + "2020": "BroadMobi", + "2022": "Antec", + "20d6": "Bensussen Deutsch & Associates", + "20e7": "Atik CCD Camera", + "2164": "Witek System Inc.", + "2179": "Flex design tablet", + "217c": "TempTale, Sensitech", + "21b0": "Grace Industries", + "21c4": "Longsys Electronics (HK) Co., Ltd.", + "21e1": "CAEN S.p.A.", + "222d": "Leifheit - Soehnle", + "2239": "PEIKER acustic GmbH & Co., KG", + "223b": "Crystalfontz America, Inc.", + "2252": "HBGIC Technology Co., Ltd.", + "2257": "On-The-Go-Video", + "22f4": "Olive V-ME102 CDMA modem", + "230a": "DataLocker", + "2312": "LP320B Wireless Presenter [August International]", + "231d": "VKB-sim", + "2321": "iKingdom Corp. [iConnectivity]", + "2342": "NIKO", + "2358": "NuTesla Composite HID+CDC", + "23e3": "Christie Digital Systems", + "23e5": "Antelope Audio", + "23e8": "Propellerhead", + "2472": "TOP", + "247f": "Lynx", + "2541": "Chipsailing", + "2550": "Shenzhen EDUP Electronics Technology Co., Ltd.", + "256c": "HUION", + "258a": " [USB chips by: Sino Wealth Electronic Ltd.]", + "25bc": "CETRTA POT", + "2622": "MISSION", + "263c": "SCHULTES", + "2660": "Test", + "2669": "M4S PSK Series Device [M4S PSK]", + "26ce": "ASRock", + "2752": "miniDSP", + "2763": "Primes GmbH", + "276d": "276d:1101", + "2853": "Ralston Instruments, LLC", + "28ba": "Materialise Motion NV", + "2909": "Game Golf Live", + "2914": "Kent Displays, Inc.", + "291a": "Anker Innovation Ltd", + "2947": "Kapelse", + "2982": "Ableton AG", + "2983": "Coyote System SAS", + "29cc": "Kodak Alaris, Inc", + "29df": "CAM(Dongle) [Freenet TV-Stick]", + "29f3": "Resonessence Labs", + "29fe": "Geo Semiconductor", + "2a52": "L Card, LLC", + "2a65": "FreeWave Technologies", + "2a94": "G2touch Co., LTD.", + "2afd": "McIntosh HD USB Audio [McIntosh DA1]", + "2b04": "Duo with WiFi and BLE", + "2b16": "Doccamera", + "2b71": "Flashforge [FlashForge Creator Pro 2 3D Printer]", + "2b73": "Pioneer DJ Corporation", + "2b89": "Unknown", + "2b98": "Glenair Inc.", + "2bd9": "Huddly", + "2beb": "Gateworks Corporation", + "2c33": "Wizapply", + "2cc8": "Hewlett Packard Enterprise", + "2ce4": "ESMART", + "2d01": "Guangdong Zike Technology Co., Ltd", + "2dbc": "Mikroelektronika d.o.o", + "2dee": "QUALCOMM MeigLink", + "2e3c": "Joy-IT", + "2e50": "beyerdynamic GmbH & Co. KG", + "2e8a": "Raspberry Pi Foundation", + "2ea1": "DASAN Electron Co", + "2eb9": "Realtek or Sabrent?", + "2efd": "Filco Co., Ltd.", + "2f68": "Hoksi Technology", + "2fd0": "C*Core Technology Co., Ltd.", + "2fe9": "Shenzhen Xintai Technology Co. Ltd", + "2fee": "Holitech", + "300c": "Gyrfalcon Technology Inc.", + "303a": "Espressif Systems", + "30b1": "Bitmain Technologies Inc.", + "30be": "Schiit Audio", + "30d6": "Chroma-Q", + "30de": "KIOXIA EXCERIA PLUS", + "30fa": "Wuxi Instant Microelectronics Co., Ltd.", + "311f": "TrustKey Co., Ltd.", + "3131": "Authentik Systems", + "3151": "Yichip Microelectronics (Hangzhou) Co., Ltd", + "31b1": "Shenzhen Jinduan Electronics Co., Ltd.", + "31b2": "KTMicro", + "31e3": "Wooting", + "31e9": "Solid State Logic, Ltd", + "320f": "Glorious LLC", + "3231": "Kneron, Inc.", + "3232": "Shenzhen Trusda Industrial Co., Ltd.", + "3274": "MicroArray", + "3285": "Nacon", + "3297": "ZSA Technology Labs Inc.", + "32a3": "GoTrust", + "32ac": "Framework Computer BV", + "32cd": "NEC", + "32e4": "ELP-USBFHD06H-BL36IR", + "32e6": "IcSpring Technology", + "332d": "Verbatim GmbH", + "3346": "Cvitek Co. Ltd.", + "335e": "Eight Amps", + "33be": "Syncopated Engineering, Inc.", + "33c4": "Tomahawk Robotics", + "33c8": "Seidl Technologies UG", + "33dd": "Zuki Inc", + "33f7": "Linux Automation GmbH", + "33f8": "Rolling Wireless S.a.r.l.", + "33ff": "nyantec GmbH", + "3434": "Keychron", + "344f": "SCX-3400 Series", + "3455": "Atomos Global Pty Ltd", + "345f": "MacroSilicon", + "3464": "Senscomm Semiconductor, Inc", + "346d": "VendorCo", + "346e": "Gudsen Technology (HK) Co., Ltd (MOZA)", + "349c": "Zhuhai Hongxin Technology Co., Ltd", + "349e": "Token2", + "3542": "Sonova Consumer Hearing", + "3544": "Rusoku technologijos UAB", + "3553": "PCsensor", + "359f": "Shenzhen Sipeed Technology Co., Ltd.", + "35b6": "Orqa d.o.o", + "35f0": "Bitcraze AB", + "35f1": "INFICON", + "369a": "HighSecLabs, Ltd", + "36da": "Record Sure Limited [Recordsure]", + "36e9": "ifanr Inc.", + "3760": "CIN-ergy B.V.", + "37c5": "OpenMV, LLC", + "3802": "LDA Technologies LTD", + "3817": "SleepImage", + "3842": "EVGA", + "386e": "XTX Markets", + "3876": "Fenice Power Co., Ltd", + "38c5": "JetHome LLC", + "3938": "MOSART Semiconductor", + "3c93": "QingDao Topscomm", + "413d": "RDing Technology Ltd [PCsensor]", + "4816": "Integrated Webcam", + "4c4a": "JieLi Technology", + "4e4c": "NieLTM TechSolution", + "5041": "Linksys (?)", + "5246": "bladeRF Software Defined Radio", + "5262": "X.Tips", + "5325": "Woolworth GmbH", + "573c": "Xreal Light Microcontroller", + "5888": "3Tronics MU30", + "6004": "ISD-V4 Tablet Pen", + "6005": "Hewlett-Packard", + "6495": "GoDEX International Co.", + "6964": "Idobo", + "7374": "DATA MODUL", + "7712": "2711 Temperature sensor HUB [SEIICHI]", + "7777": "SEIICHI Technology Co., Ltd.", + "8347": "VisTrend Co., Ltd.", + "8888": "inLight", + "9048": "NuTesla CDC Serial Emulator", + "9e8f": "Plug Computer Basic [SheevaPlug]", + "a69c": "AICSEMI", + "a8f8": "Bastard Keyboards", + "b2c3": "GNDHog", + "b711": "VuPlus", + "c069": "M500 Laser Mouse", + "c07c": "M-R0017 [G700s Rechargeable Gaming Mouse]", + "c0f4": "DualMiner", + "c580": "HID UNIKEYdongle [F-Response]", + "c5cb": "ARTECH (Artech Technology Design Co., Ltd.)", + "d13e": "Coldcard Wallet", + "dff0": "shapinb", + "e2b5": "JieLi Technology", + "e3b5": "JieLi Technology", + "e5b7": "JieLi Technology", + "eb57": "ZhuHai JieLi Technology", + "eba4": "Aoboco", + "feed": "DOIO Keyboard", + "ffd2": "ZHONG-HUI ELECTRONICS CORP.", + "fffe": "Inland (MicroCenter brand)", + "ffff": "Wrong vendor ID" +} diff --git a/online-data-tools/README.md b/online-data-tools/README.md new file mode 100644 index 00000000..4ad8551d --- /dev/null +++ b/online-data-tools/README.md @@ -0,0 +1,27 @@ +# online-data-tools + +Build-time helpers invoked by `.github/workflows/update-data.yml` to produce +the SQLite databases hosted on the `www` orphan branch. + +Scripts here live on `main` (so they get unit-tested in CI), but their output +is committed to orphan branches: + +| Script | Reads from | Writes to | +| ------------------- | ------------------------------------------- | -------------------------------------- | +| `build_sqlite.py` | `online-data/data/*.json` | `www/.db` | +| `rotate_www_dbs.py` | `www/*.db` | `www/` (deletes >2-day-old `.db`s) | +| `build_www_manifest.py` | day-stable filenames | `www/manifest.json` | + +The merger scripts on the `online-data` orphan branch +(`merge_sources.py`, `merge_pio_boards.py`, `build_manifest.py`, +`dump_platformio.py`) are NOT moved here — they predate this directory and +the convention is documented in [issue #718](https://github.com/FastLED/fbuild/issues/718). + +## Tests + +```bash +uv run --no-project --with pytest pytest online-data-tools/test_build_sqlite.py -v +``` + +Each script declares its own PEP 723 dependencies and is runnable via +`uv run --no-project --script + + + diff --git a/online-data-tools/www_static/style.css b/online-data-tools/www_static/style.css new file mode 100644 index 00000000..4a52b435 --- /dev/null +++ b/online-data-tools/www_static/style.css @@ -0,0 +1,72 @@ +:root { + --fg: #1a1a1a; + --bg: #fafafa; + --accent: #0066cc; + --muted: #666; + --border: #ddd; + --table-stripe: #f2f2f2; +} +@media (prefers-color-scheme: dark) { + :root { + --fg: #e8e8e8; + --bg: #181818; + --accent: #6fb4ff; + --muted: #aaa; + --border: #333; + --table-stripe: #222; + } +} +* { box-sizing: border-box; } +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + color: var(--fg); + background: var(--bg); + margin: 0; + padding: 1rem; + max-width: 920px; + margin-left: auto; + margin-right: auto; + line-height: 1.5; +} +header h1 { margin-bottom: 0.25rem; } +header .sub { color: var(--muted); margin-top: 0; } +.meta { display: inline-block; margin-left: 0.5em; font-style: italic; } +section { margin: 1.25rem 0; padding-top: 0.5rem; border-top: 1px solid var(--border); } +section h2 { font-size: 1.05rem; margin-bottom: 0.5rem; } +form { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: end; } +label { display: flex; flex-direction: column; font-size: 0.85rem; color: var(--muted); } +input { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 1rem; + padding: 0.4rem 0.5rem; + border: 1px solid var(--border); + background: var(--bg); + color: var(--fg); + border-radius: 3px; + min-width: 7em; +} +button { + font-size: 0.95rem; + padding: 0.45rem 0.9rem; + background: var(--accent); + color: white; + border: 0; + border-radius: 3px; + cursor: pointer; +} +button:hover { filter: brightness(1.05); } +.status { color: var(--muted); font-size: 0.85rem; margin: 0.25rem 0; white-space: pre-wrap; } +table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin-top: 0.5rem; } +th, td { + text-align: left; + padding: 0.35rem 0.5rem; + border-bottom: 1px solid var(--border); + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + vertical-align: top; +} +tr:nth-child(even) td { background: var(--table-stripe); } +th { font-weight: 600; background: var(--table-stripe); } +a { color: var(--accent); } +footer { color: var(--muted); font-size: 0.8rem; margin-top: 2rem; } +.hint { color: var(--muted); font-style: italic; } +.error { color: #c33; }