diff --git a/.github/workflows/update-data.yml b/.github/workflows/update-data.yml deleted file mode 100644 index 59afef44..00000000 --- a/.github/workflows/update-data.yml +++ /dev/null @@ -1,692 +0,0 @@ -# Nightly refresh of the `online-data` and `www` branches. -# -# `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: -# -# 1. checks out `main` (default) so it can build the `dump_usb_ids` -# example from `crates/fbuild-core/examples/dump_usb_ids.rs`; -# 2. fetches + worktrees the `online-data` branch into a sibling dir so -# the merger scripts live at `online-data/tools/{merge_sources, -# merge_pio_boards,build_manifest}.py`; -# 3. **in parallel** produces: -# - `usb-ids` Rust crate dump (tier-1 USB-VID source) -# - two upstream `usb.ids` text mirror fetches -# - official vendor PID registry supplements -# - the full PlatformIO board catalog (`pio boards --json-output`) -# Each source has its own step + `continue-on-error: true` so any -# single failure is non-fatal — the merger downstream sees only the -# sources that actually arrived intact; -# 4. runs the USB-VID merger → sorted `usb-vid.json` + conflict log + -# per-dataset manifest fragment + compact runtime `usb-vids.proto.zstd`; -# 5. runs the PlatformIO board merger → `pio-boards.json` (deep-union -# with the previously committed copy so transient field drops in -# `pio boards` don't lose data) + per-dataset manifest fragment; -# 6. assembles the future-forward `manifest.json` from both fragments; -# 7. commits + pushes only if any data file actually changed, with -# history pruned to 200 commits. -# -# Fault tolerance summary: -# - Any single source failure → workflow continues with the rest. -# - USB-VID merger refuses to write below 1000 entries. -# - PIO merger refuses to write below 1500 boards AND deep-unions with -# the previously committed data so a feature drop upstream is repaired -# from history. -# - All-sources-fail → no commit happens; the existing online-data -# branch keeps its last good snapshot. -# - History is pruned to the most recent 200 commits per the design. -# -# Manual trigger: Actions tab → "Update data" → Run workflow. - -name: Update data - -on: - schedule: - # 04:17 UTC daily — off-peak, avoids the top-of-hour stampede on shared - # GitHub-hosted runners. - - cron: "17 4 * * *" - workflow_dispatch: - -permissions: - contents: write - -concurrency: - group: update-data - cancel-in-progress: false - -env: - ONLINE_BRANCH: online-data - 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: - name: Update online-data datasets - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout main (default branch) - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Configure git identity for the commit - run: | - git config user.name "fbuild-bot[nightly]" - git config user.email "fbuild-bot+nightly@users.noreply.github.com" - - - name: Fetch + worktree the online-data branch - timeout-minutes: 5 - env: - GIT_HTTP_LOW_SPEED_TIME: "30" - GIT_HTTP_LOW_SPEED_LIMIT: "1000" - run: | - set -euo pipefail - if git ls-remote --heads origin "${ONLINE_BRANCH}" | grep -q .; then - git fetch origin "${ONLINE_BRANCH}:${ONLINE_BRANCH}" - git worktree add "${ONLINE_WORKTREE}" "${ONLINE_BRANCH}" - else - echo "::warning::online-data branch missing on remote; bootstrapping empty orphan worktree" - git worktree add --detach "${ONLINE_WORKTREE}" - (cd "${ONLINE_WORKTREE}" && git checkout --orphan "${ONLINE_BRANCH}" && git rm -rf . 2>/dev/null || true) - fi - mkdir -p "${ONLINE_WORKTREE}/data" - ls -la "${ONLINE_WORKTREE}" - - - 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 - with: - version: 0.8.8 - cache: true - build-cache: true - target-cache: true - prebuild-deps: none - linker: platform-default - - # ──────────────────────────────────────────────────────────────────── - # Parallel data-source acquisition. Each fetch is its own step so - # `steps..outcome` cleanly attributes blame; the merge step - # downstream consumes only sources that succeeded. The Rust build - # is the longest step (~1–2 min cold, seconds warm); the pio dump - # and curl fetches are each <90 s — the wall-time cost is bounded by - # the slowest single source. - # ──────────────────────────────────────────────────────────────────── - - - name: Build dump_usb_ids example (USB-VID tier-1) - id: build-dump - continue-on-error: true - run: soldr cargo build --release --example dump_usb_ids -p fbuild-core - - - name: Run dump_usb_ids → /tmp/usb-ids-rs.json - id: run-dump - continue-on-error: true - if: steps.build-dump.outcome == 'success' - run: | - set -euo pipefail - ./target/release/examples/dump_usb_ids > /tmp/usb-ids-rs.json - wc -l /tmp/usb-ids-rs.json - - - name: Fetch linux-usb.org/usb.ids (USB-VID tier-2) - id: fetch-linux-usb - continue-on-error: true - run: | - # HTTP only — the linux-usb.org HTTPS endpoint has a SAN mismatch. - curl --silent --show-error --retry 5 --retry-delay 10 --fail \ - --max-time 90 \ - -o /tmp/linux-usb.txt \ - "http://www.linux-usb.org/usb.ids" - wc -l /tmp/linux-usb.txt - - - name: Fetch usbids/usbids GitHub mirror (USB-VID tier-3) - id: fetch-github - continue-on-error: true - run: | - curl --silent --show-error --retry 5 --retry-delay 10 --fail \ - --max-time 90 \ - -o /tmp/usbids-github.txt \ - "https://raw.githubusercontent.com/usbids/usbids/master/usb.ids" - wc -l /tmp/usbids-github.txt - - - name: Fetch Espressif USB PID registry (USB-VID tier-1 supplement) - id: fetch-espressif-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_espressif_usb_pids.py" \ - --out /tmp/espressif-usb-pids.json - wc -l /tmp/espressif-usb-pids.json - - - name: Fetch Raspberry Pi USB PID registry (USB-VID tier-1 supplement) - id: fetch-raspberrypi-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_raspberrypi_usb_pids.py" \ - --out /tmp/raspberrypi-usb-pids.json - wc -l /tmp/raspberrypi-usb-pids.json - - - name: Fetch Nordic USB PID supplement (USB-VID tier-1 supplement) - id: fetch-nordic-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_nordic_usb_pids.py" \ - --out /tmp/nordic-usb-pids.json - wc -l /tmp/nordic-usb-pids.json - - - name: Fetch Microchip USB PID supplement (USB-VID tier-1 supplement) - id: fetch-microchip-first-party-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_microchip_usb_pids.py" \ - --tier first-party \ - --out /tmp/microchip-first-party-usb-pids.json - wc -l /tmp/microchip-first-party-usb-pids.json - - - name: Fetch Arduino USB PID supplement (USB-VID tier-1 supplement) - id: fetch-arduino-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_arduino_usb_pids.py" \ - --out /tmp/arduino-usb-pids.json - wc -l /tmp/arduino-usb-pids.json - - - name: Fetch Adafruit USB PID supplement (USB-VID tier-1 supplement) - id: fetch-adafruit-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_adafruit_usb_pids.py" \ - --out /tmp/adafruit-usb-pids.json - wc -l /tmp/adafruit-usb-pids.json - - - name: Fetch SparkFun USB PID supplement (USB-VID tier-1 supplement) - id: fetch-sparkfun-first-party-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_sparkfun_usb_pids.py" \ - --tier first-party \ - --out /tmp/sparkfun-first-party-usb-pids.json - wc -l /tmp/sparkfun-first-party-usb-pids.json - - - name: Fetch Seeed USB PID supplement (USB-VID tier-1 supplement) - id: fetch-seeed-first-party-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_seeed_usb_pids.py" \ - --tier first-party \ - --out /tmp/seeed-first-party-usb-pids.json - wc -l /tmp/seeed-first-party-usb-pids.json - - - name: Fetch FTDI USB PID supplement (USB-VID tier-3 supplement) - id: fetch-ftdi-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_ftdi_usb_pids.py" \ - --out /tmp/ftdi-usb-pids.json - wc -l /tmp/ftdi-usb-pids.json - - - name: Fetch WCH USB PID supplement (USB-VID tier-3 supplement) - id: fetch-wch-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_wch_usb_pids.py" \ - --out /tmp/wch-usb-pids.json - wc -l /tmp/wch-usb-pids.json - - - name: Fetch Teensy USB PID supplement (USB-VID tier-3 supplement) - id: fetch-teensy-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_teensy_usb_pids.py" \ - --out /tmp/teensy-usb-pids.json - wc -l /tmp/teensy-usb-pids.json - - - name: Fetch STM USB PID supplement (USB-VID tier-3 supplement) - id: fetch-stm-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_stm_usb_pids.py" \ - --out /tmp/stm-usb-pids.json - wc -l /tmp/stm-usb-pids.json - - - name: Fetch NXP USB PID supplement (USB-VID tier-3 supplement) - id: fetch-nxp-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_nxp_usb_pids.py" \ - --out /tmp/nxp-usb-pids.json - wc -l /tmp/nxp-usb-pids.json - - - name: Fetch Silicon Labs USB PID supplement (USB-VID tier-3 supplement) - id: fetch-silabs-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_silabs_usb_pids.py" \ - --out /tmp/silabs-usb-pids.json - wc -l /tmp/silabs-usb-pids.json - - - name: Fetch Renesas RA Arduino USB PID rows (USB-VID tier-4 supplement) - id: fetch-renesas-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_renesas_usb_pids.py" \ - --out /tmp/renesas-usb-pids.json - wc -l /tmp/renesas-usb-pids.json - - - name: Fetch Microchip supplemental USB PID rows (USB-VID tier-4 supplement) - id: fetch-microchip-supplemental-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_microchip_usb_pids.py" \ - --tier supplemental \ - --out /tmp/microchip-supplemental-usb-pids.json - wc -l /tmp/microchip-supplemental-usb-pids.json - - - name: Fetch SparkFun supplemental USB PID rows (USB-VID tier-4 supplement) - id: fetch-sparkfun-supplemental-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_sparkfun_usb_pids.py" \ - --tier supplemental \ - --out /tmp/sparkfun-supplemental-usb-pids.json - wc -l /tmp/sparkfun-supplemental-usb-pids.json - - - name: Fetch Seeed supplemental USB PID rows (USB-VID tier-4 supplement) - id: fetch-seeed-supplemental-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/fetch_seeed_usb_pids.py" \ - --tier supplemental \ - --out /tmp/seeed-supplemental-usb-pids.json - wc -l /tmp/seeed-supplemental-usb-pids.json - - - name: Extract FastLED board USB PID rows (USB-VID repo-board supplement) - id: extract-fastled-board-pids - continue-on-error: true - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/extract_fastled_board_usb_pids.py" \ - --boards-dir "${{ github.workspace }}/crates/fbuild-config/assets/boards/json" \ - --out /tmp/fastled-board-usb-pids.json - wc -l /tmp/fastled-board-usb-pids.json - - - name: Dump PlatformIO board catalog → /tmp/all_boards.json - id: dump-pio - continue-on-error: true - run: | - # `dump_platformio.py` declares `platformio` as an inline - # dependency so `uv run --no-project --script` materializes it - # in an ephemeral env. No global pio install needed. - uv run --no-project --script \ - "${ONLINE_WORKTREE}/tools/dump_platformio.py" \ - /tmp/all_boards.json - # jq isn't on minimal runners — use python for the sanity print. - uv run --no-project --script - "/tmp/all_boards.json" <<'PY' - # /// script - # requires-python = ">=3.10" - # /// - import json, sys - data = json.loads(open(sys.argv[1], encoding="utf-8").read()) - print(f"pio boards: {len(data)} entries") - PY - - # ──────────────────────────────────────────────────────────────────── - # Per-dataset merge steps. Each writes its own data file + a - # manifest fragment. The fragments are then consumed by - # build_manifest.py to assemble the unified manifest.json. - # ──────────────────────────────────────────────────────────────────── - - - name: Merge USB-VID sources - id: merge-usb - continue-on-error: true - run: | - set -euo pipefail - args=() - if [ "${{ steps.fetch-espressif-pids.outcome }}" = "success" ] && [ -s /tmp/espressif-usb-pids.json ]; then - args+=(--json "espressif-usb-pids=/tmp/espressif-usb-pids.json") - fi - if [ "${{ steps.fetch-raspberrypi-pids.outcome }}" = "success" ] && [ -s /tmp/raspberrypi-usb-pids.json ]; then - args+=(--json "raspberrypi-usb-pids=/tmp/raspberrypi-usb-pids.json") - fi - if [ "${{ steps.fetch-nordic-pids.outcome }}" = "success" ] && [ -s /tmp/nordic-usb-pids.json ]; then - args+=(--json "nordic-usb-pids=/tmp/nordic-usb-pids.json") - fi - if [ "${{ steps.fetch-microchip-first-party-pids.outcome }}" = "success" ] && [ -s /tmp/microchip-first-party-usb-pids.json ]; then - args+=(--json "microchip-first-party-usb-pids=/tmp/microchip-first-party-usb-pids.json") - fi - if [ "${{ steps.fetch-arduino-pids.outcome }}" = "success" ] && [ -s /tmp/arduino-usb-pids.json ]; then - args+=(--json "arduino-usb-pids=/tmp/arduino-usb-pids.json") - fi - if [ "${{ steps.fetch-adafruit-pids.outcome }}" = "success" ] && [ -s /tmp/adafruit-usb-pids.json ]; then - args+=(--json "adafruit-usb-pids=/tmp/adafruit-usb-pids.json") - fi - if [ "${{ steps.fetch-sparkfun-first-party-pids.outcome }}" = "success" ] && [ -s /tmp/sparkfun-first-party-usb-pids.json ]; then - args+=(--json "sparkfun-first-party-usb-pids=/tmp/sparkfun-first-party-usb-pids.json") - fi - if [ "${{ steps.fetch-seeed-first-party-pids.outcome }}" = "success" ] && [ -s /tmp/seeed-first-party-usb-pids.json ]; then - args+=(--json "seeed-first-party-usb-pids=/tmp/seeed-first-party-usb-pids.json") - fi - if [ "${{ steps.run-dump.outcome }}" = "success" ] && [ -s /tmp/usb-ids-rs.json ]; then - args+=(--json "usb-ids-rs=/tmp/usb-ids-rs.json") - fi - if [ "${{ steps.fetch-linux-usb.outcome }}" = "success" ] && [ -s /tmp/linux-usb.txt ]; then - args+=(--txt "linux-usb.org=/tmp/linux-usb.txt") - fi - if [ "${{ steps.fetch-github.outcome }}" = "success" ] && [ -s /tmp/usbids-github.txt ]; then - args+=(--txt "usbids-github=/tmp/usbids-github.txt") - fi - if [ "${{ steps.fetch-ftdi-pids.outcome }}" = "success" ] && [ -s /tmp/ftdi-usb-pids.json ]; then - # FTDI is intentionally after generic sources so mature usb.ids - # product names win for common bridge PIDs; the supplement fills - # newer original-FTDI rows that generic sources often lack. - args+=(--json "ftdi-usb-pids=/tmp/ftdi-usb-pids.json") - fi - if [ "${{ steps.fetch-wch-pids.outcome }}" = "success" ] && [ -s /tmp/wch-usb-pids.json ]; then - # WCH bridge-chip rows are after generic sources for the same - # reason as FTDI: fill newer chip IDs without replacing mature - # usb.ids names for older CH34x entries. - args+=(--json "wch-usb-pids=/tmp/wch-usb-pids.json") - fi - if [ "${{ steps.fetch-teensy-pids.outcome }}" = "success" ] && [ -s /tmp/teensy-usb-pids.json ]; then - # Teensy rows are after generic VOTI/PJRC USB-ID data so legacy - # names remain stable; this source fills modern Teensyduino PIDs - # declared in PJRC's current core headers. - args+=(--json "teensy-usb-pids=/tmp/teensy-usb-pids.json") - fi - if [ "${{ steps.fetch-stm-pids.outcome }}" = "success" ] && [ -s /tmp/stm-usb-pids.json ]; then - # ST rows are after generic sources so existing usb.ids names - # stay stable while newer ST-LINK products fill in. - args+=(--json "stm-usb-pids=/tmp/stm-usb-pids.json") - fi - if [ "${{ steps.fetch-nxp-pids.outcome }}" = "success" ] && [ -s /tmp/nxp-usb-pids.json ]; then - # NXP rows are after generic sources so existing usb.ids names - # stay stable while newer mfgtools ROM-loader PIDs fill in. - args+=(--json "nxp-usb-pids=/tmp/nxp-usb-pids.json") - fi - if [ "${{ steps.fetch-silabs-pids.outcome }}" = "success" ] && [ -s /tmp/silabs-usb-pids.json ]; then - # Silicon Labs rows are after generic sources so mature usb.ids - # names for common CP210x bridge PIDs stay stable while the - # Energy Micro / Silicon Labs debug-interface row fills in. - args+=(--json "silabs-usb-pids=/tmp/silabs-usb-pids.json") - fi - if [ "${{ steps.extract-fastled-board-pids.outcome }}" = "success" ] && [ -s /tmp/fastled-board-usb-pids.json ]; then - # Local board JSON rows are repo-scope supplements: they fill - # missing product names for boards fbuild actually carries, but - # do not replace stronger vendor/generic USB-ID source rows. - args+=(--json "fastled-board-usb-pids=/tmp/fastled-board-usb-pids.json") - fi - if [ "${{ steps.fetch-renesas-pids.outcome }}" = "success" ] && [ -s /tmp/renesas-usb-pids.json ]; then - # ArduinoCore-renesas board-package rows are deliberately weak: - # they fill missing Arduino RA board product names after generic - # USB-ID sources and vendor-owned supplements have won. - args+=(--json "renesas-usb-pids=/tmp/renesas-usb-pids.json") - fi - if [ "${{ steps.fetch-microchip-supplemental-pids.outcome }}" = "success" ] && [ -s /tmp/microchip-supplemental-usb-pids.json ]; then - # Third-party Microchip/Atmel rows are deliberately weak: they - # fill gaps only after first-party and generic USB-ID sources. - args+=(--json "microchip-supplemental-usb-pids=/tmp/microchip-supplemental-usb-pids.json") - fi - if [ "${{ steps.fetch-sparkfun-supplemental-pids.outcome }}" = "success" ] && [ -s /tmp/sparkfun-supplemental-usb-pids.json ]; then - # Third-party SparkFun board-package rows are deliberately weak: - # they fill gaps only after first-party and generic USB-ID sources. - args+=(--json "sparkfun-supplemental-usb-pids=/tmp/sparkfun-supplemental-usb-pids.json") - fi - if [ "${{ steps.fetch-seeed-supplemental-pids.outcome }}" = "success" ] && [ -s /tmp/seeed-supplemental-usb-pids.json ]; then - # Third-party Seeed board-package rows are deliberately weak: - # they fill gaps only after first-party and generic USB-ID sources. - args+=(--json "seeed-supplemental-usb-pids=/tmp/seeed-supplemental-usb-pids.json") - fi - if [ "${#args[@]}" -eq 0 ]; then - echo "::warning::all USB-VID sources failed; preserving previously committed data" - exit 1 - fi - mkdir -p /tmp/fragments - uv run --no-project --script \ - "${ONLINE_WORKTREE}/tools/merge_sources.py" \ - "${args[@]}" \ - --out-dir "${ONLINE_WORKTREE}/data" \ - --branch-base-url "${BRANCH_BASE_URL}" \ - --manifest-fragment /tmp/fragments/usb-vid.json - - - name: Merge PlatformIO board dump (full + slim vendor view) - id: merge-pio - continue-on-error: true - if: steps.dump-pio.outcome == 'success' - run: | - set -euo pipefail - mkdir -p /tmp/fragments - uv run --no-project --script \ - "${ONLINE_WORKTREE}/tools/merge_pio_boards.py" \ - --new /tmp/all_boards.json \ - --old "${ONLINE_WORKTREE}/data/pio-boards.json" \ - --out "${ONLINE_WORKTREE}/data/pio-boards.json" \ - --out-slim "${ONLINE_WORKTREE}/data/vendor_boards.json" \ - --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: Package usb-vids.proto.zstd (runtime USB VID:PID overlay) - id: package-usb-proto - if: steps.merge-usb.outcome == 'success' - # Full product-level runtime cache for fbuild CLI/device scans. This - # is derived after source priority has already been resolved into - # usb-vid.json; weak third-party rows only fill gaps left by - # first-party/vendor/generic sources and never override their winners. - run: | - uv run --no-project --script \ - "${{ github.workspace }}/online-data-tools/build_usb_vid_proto.py" \ - --upstream "${ONLINE_WORKTREE}/data/usb-vid.json" \ - --out "${ONLINE_WORKTREE}/data/usb-vids.proto.zstd" - ls -la "${ONLINE_WORKTREE}/data/usb-vids.proto.zstd" - - - name: Assemble manifest.json - id: build-manifest - # We rebuild the manifest whenever at least one dataset succeeded, - # so generated_at moves even on a no-op data day (heartbeat). - # Datasets that didn't merge this run get marked status=missing in - # the manifest but keep their committed data file untouched. - if: | - steps.merge-usb.outcome == 'success' || - steps.merge-pio.outcome == 'success' - run: | - set -euo pipefail - fragments=() - if [ -f /tmp/fragments/usb-vid.json ]; then - fragments+=(--fragment "usb-vid=/tmp/fragments/usb-vid.json") - fi - if [ -f /tmp/fragments/pio-boards.json ]; then - fragments+=(--fragment "pio-boards=/tmp/fragments/pio-boards.json") - fi - if [ -f /tmp/fragments/vendor_boards.json ]; then - fragments+=(--fragment "vendor_boards=/tmp/fragments/vendor_boards.json") - fi - uv run --no-project --script \ - "${ONLINE_WORKTREE}/tools/build_manifest.py" \ - --branch-base-url "${BRANCH_BASE_URL}" \ - --data-dir "${ONLINE_WORKTREE}/data" \ - --out "${ONLINE_WORKTREE}/manifest.json" \ - "${fragments[@]}" - - # ──────────────────────────────────────────────────────────────────── - # 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' - run: | - 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}" - - # ──────────────────────────────────────────────────────────────────── - # 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: | - 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: Publish www branch - id: commit-www - if: steps.build-sqlite.outcome == 'success' - run: | - 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() - run: | - { - echo "## Update online-data" - echo "" - echo "| source / step | outcome |" - echo "|---|---|" - echo "| usb-ids-rs (dump example) | ${{ steps.run-dump.outcome }} |" - echo "| linux-usb.org | ${{ steps.fetch-linux-usb.outcome }} |" - echo "| usbids/usbids github | ${{ steps.fetch-github.outcome }} |" - echo "| espressif usb-pids | ${{ steps.fetch-espressif-pids.outcome }} |" - echo "| raspberrypi usb-pid | ${{ steps.fetch-raspberrypi-pids.outcome }} |" - echo "| nordic usb-pids | ${{ steps.fetch-nordic-pids.outcome }} |" - echo "| microchip first-party pids | ${{ steps.fetch-microchip-first-party-pids.outcome }} |" - echo "| arduino usb-pids | ${{ steps.fetch-arduino-pids.outcome }} |" - echo "| adafruit usb-pids | ${{ steps.fetch-adafruit-pids.outcome }} |" - echo "| sparkfun first-party pids | ${{ steps.fetch-sparkfun-first-party-pids.outcome }} |" - echo "| seeed first-party pids | ${{ steps.fetch-seeed-first-party-pids.outcome }} |" - echo "| ftdi usb-pids | ${{ steps.fetch-ftdi-pids.outcome }} |" - echo "| wch usb-pids | ${{ steps.fetch-wch-pids.outcome }} |" - echo "| teensy usb-pids | ${{ steps.fetch-teensy-pids.outcome }} |" - echo "| stm usb-pids | ${{ steps.fetch-stm-pids.outcome }} |" - echo "| nxp usb-pids | ${{ steps.fetch-nxp-pids.outcome }} |" - echo "| silabs usb-pids | ${{ steps.fetch-silabs-pids.outcome }} |" - echo "| renesas weak pids | ${{ steps.fetch-renesas-pids.outcome }} |" - echo "| microchip weak pids | ${{ steps.fetch-microchip-supplemental-pids.outcome }} |" - echo "| sparkfun weak pids | ${{ steps.fetch-sparkfun-supplemental-pids.outcome }} |" - echo "| seeed weak pids | ${{ steps.fetch-seeed-supplemental-pids.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 "| 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 f309f493..5e59fb26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1351,7 +1351,6 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tracing", - "usb-ids", "zstd", ] @@ -2978,44 +2977,6 @@ dependencies = [ "indexmap", ] -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" version = "1.1.13" @@ -4148,12 +4109,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "siphasher" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" - [[package]] name = "slab" version = "0.4.12" @@ -4980,19 +4935,6 @@ dependencies = [ "serde", ] -[[package]] -name = "usb-ids" -version = "1.2025.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f464d03993287ba27fae1c81bfa368df4493983de7e340429fc10e470043383" -dependencies = [ - "nom", - "phf", - "phf_codegen", - "proc-macro2", - "quote", -] - [[package]] name = "utf-8" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index bc414fa0..5bb035ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,13 +121,8 @@ object = { version = "0.36", default-features = false, features = ["read", "std" rusqlite = { version = "0.31", features = ["bundled"] } shell-words = "1" bincode = "1" -# USB VID:PID -> {vendor, product} name lookup. Bundles the linux-usb.org -# `usb.ids` snapshot at compile time into a `phf` perfect-hash table. -# Pure Rust, no libusb / no udev. Versioning scheme `1.YYYY.N` tracks -# upstream snapshots — `cargo update` pulls in new silicon. Tier-1 for -# `fbuild_core::usb::resolve()`; the tier-2 online overlay is fetched -# from the repo's `online-data` branch. -usb-ids = "1.2025" +# USB identity data is fetched from FastLED/boards at runtime. Do not add a +# catalogue dependency here; dependency checks run through `soldr cargo`. # prost: hand-derived `#[derive(prost::Message)]` payload structs for the # fbuild v1 broker request/response lane (no .proto/build.rs needed). Pinned # to the version running-process 4.3.0 re-exports so the wire types stay diff --git a/ci/check_usb_vidpid_literals.py b/ci/check_usb_vidpid_literals.py index 354930c2..8ef2ec03 100644 --- a/ci/check_usb_vidpid_literals.py +++ b/ci/check_usb_vidpid_literals.py @@ -1,97 +1,259 @@ -"""Guard the preparatory USB VID/PID diff surface. +"""Reject production USB VID/PID identities outside FastLED/boards. -This check currently covers only added same-line pairs in ``crates/`` and -``python/``, plus separate ``vid``/``pid`` fields in the board JSON snapshot. -The broader deny-all cleanup is deferred until FastLED/boards#47 lands. +The scan covers the complete tracked tree. Explicit test paths and Rust items +guarded by ``#[cfg(test)]`` are permitted because production builds cannot +reach them. Documentation and this guard's own fixtures are non-runtime input. """ from __future__ import annotations -import argparse import re import subprocess import sys +from dataclasses import dataclass +from pathlib import Path -PAIR_RE = re.compile(r"0x([0-9a-fA-F]{4})\s*[:,/]\s*0x([0-9a-fA-F]{4})") -PAIR_STRING_RE = re.compile(r"(?i)\b([0-9a-f]{4}):([0-9a-f]{4})\b") +TEXT_SUFFIXES = { + ".c", + ".cc", + ".cpp", + ".h", + ".hpp", + ".ini", + ".js", + ".json", + ".md", + ".ps1", + ".py", + ".rs", + ".sh", + ".toml", + ".ts", + ".txt", + ".yaml", + ".yml", +} + +EXACT_EXCLUSIONS = { + "ci/check_usb_vidpid_literals.py", + "ci/test_check_usb_vidpid_literals.py", +} +TEST_PATH_PREFIXES = ( + "ci/docker-test-serial/", + "crates/fbuild-core/data/", +) + +IDENTITY_STRING_RE = re.compile( + r"(?i)\b(?:0x)?([0-9a-f]{4})\s*:\s*(?:0x)?([0-9a-f]{4})\b" +) +TUPLE_PAIR_RE = re.compile( + r"(?i)[\[(]\s*0x([0-9a-f]{4})\s*,\s*0x([0-9a-f]{4})\s*[\])]" +) +NAMED_LITERAL_RE = re.compile( + r"(?i)\b(?:vid|pid|[a-z0-9]+_(?:vid|pid))\b\s*" + r"(?::\s*[a-z_][a-z0-9_:<>]*)?\s*(?:==|!=|=|:)\s*" + r"(?:Some\(\s*)?\b(0x[0-9a-f]{4}|[1-9][0-9]{0,4})\b" +) JSON_FIELD_RE = re.compile( - r'"(?Pvid|pid)"\s*:\s*"?(?P0x[0-9a-fA-F]{4}|[0-9a-fA-F]{4})"?', - re.IGNORECASE, + r'(?i)"(vid|pid)"\s*:\s*"?(0x[0-9a-f]{4}|[0-9a-f]{4}|[0-9]{1,5})"?' +) +EMBED_RE = re.compile( + r'(?i)include_(?:bytes|str)!\s*\([^\n)]*(?:usb[^\n)]*(?:ids|vid|pid)|(?:ids|vid|pid)[^\n)]*usb)' ) -EXCLUDED_PARTS = ( - "/tests/", - "\\tests\\", - "/test_", - "\\test_", - "/docs/", - "\\docs\\", - "online-data-tools/", - "online-data-tools\\", +CATALOGUE_RE = re.compile( + r"(?i)\b(?:board_fingerprints|environment_to_vcom|mcu_to_vid|seed_mcu|usb_vid_pid_catalog)\b" ) +CFG_TEST_RE = re.compile(r"#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]") + +@dataclass(frozen=True) +class Finding: + path: str + line: int + reason: str + excerpt: str -def production_path(path: str) -> bool: + +def test_only_path(path: str) -> bool: normalized = path.replace("\\", "/") - if any(part in normalized for part in EXCLUDED_PARTS): - return False - return normalized.startswith(("crates/", "python/")) + if normalized in EXACT_EXCLUSIONS or Path(normalized).suffix.lower() == ".md": + return True + if normalized.startswith(TEST_PATH_PREFIXES): + return True + parts = normalized.split("/") + name = parts[-1] + return ( + "tests" in parts + or name.startswith(("test_", "tests_")) + or name in {"test.rs", "tests.rs"} + or name.endswith("_test.rs") + ) -def added_production_pairs(diff: str) -> list[tuple[str, str, str]]: - path = "" - findings: list[tuple[str, str, str]] = [] - for line in diff.splitlines(): - if line.startswith("+++ b/"): - path = line[6:] +def _code_braces( + line: str, in_block_comment: bool, raw_hashes: int | None +) -> tuple[int, bool, int | None]: + """Count braces outside strings/comments on one Rust line.""" + delta = 0 + i = 0 + quote: str | None = None + escaped = False + while i < len(line): + if raw_hashes is not None: + delimiter = '"' + ('#' * raw_hashes) + end = line.find(delimiter, i) + if end < 0: + return delta, in_block_comment, raw_hashes + i = end + len(delimiter) + raw_hashes = None continue - if not line.startswith("+") or line.startswith("+++") or not production_path(path): + if in_block_comment: + end = line.find("*/", i) + if end < 0: + return delta, True, raw_hashes + i = end + 2 + in_block_comment = False continue - normalized = path.replace("\\", "/") - matches = (*PAIR_RE.finditer(line), *PAIR_STRING_RE.finditer(line)) - if normalized.startswith("crates/fbuild-config/assets/boards/json/"): - matches = (*matches, *JSON_FIELD_RE.finditer(line)) - for match in matches: - if len(match.groups()) == 2 and match.groupdict().get("field"): - findings.append((path, match.group("field").upper(), match.group("value"))) + if quote is not None: + char = line[i] + if escaped: + escaped = False + elif char == "\\": + escaped = True + elif char == quote: + quote = None + i += 1 + continue + if line.startswith("//", i): + break + if line.startswith("/*", i): + in_block_comment = True + i += 2 + continue + raw_match = re.match(r'r(#{0,16})"', line[i:]) + if raw_match: + raw_hashes = len(raw_match.group(1)) + i += len(raw_match.group(0)) + continue + if line[i] == '"': + quote = line[i] + elif line[i] == "{": + delta += 1 + elif line[i] == "}": + delta -= 1 + i += 1 + return delta, in_block_comment, raw_hashes + + +def strip_cfg_test_items(source: str) -> str: + """Blank Rust items selected by a standalone ``#[cfg(test)]`` attribute.""" + lines = source.splitlines(keepends=True) + output = list(lines) + pending = False + skipping = False + saw_body = False + depth = 0 + in_block_comment = False + raw_hashes: int | None = None + + for index, line in enumerate(lines): + if not pending and not skipping and CFG_TEST_RE.search(line): + output[index] = "\n" if line.endswith("\n") else "" + pending = True + remainder = CFG_TEST_RE.sub("", line).strip() + if not remainder: continue - if len(match.groups()) == 1: - findings.append((path, match.group(1).upper(), "????")) + line = remainder + + if pending or skipping: + output[index] = "\n" if lines[index].endswith("\n") else "" + stripped = line.strip() + if pending and (not stripped or stripped.startswith("#[")): continue - findings.append((path, match.group(1).upper(), match.group(2).upper())) + pending = False + skipping = True + delta, in_block_comment, raw_hashes = _code_braces( + line, in_block_comment, raw_hashes + ) + depth += delta + saw_body = saw_body or delta > 0 or "{" in line + if (saw_body and depth <= 0) or (not saw_body and ";" in line): + skipping = False + saw_body = False + depth = 0 + in_block_comment = False + raw_hashes = None + + return "".join(output) + + +def scan_text(path: str, source: str) -> list[Finding]: + if test_only_path(path): + return [] + if path.endswith(".rs"): + source = strip_cfg_test_items(source) + + findings: list[Finding] = [] + seen: set[tuple[int, str]] = set() + for line_number, line in enumerate(source.splitlines(), 1): + stripped = line.strip() + if not stripped or stripped.startswith(("//", "//!", "///", "# ")): + continue + checks = ( + (JSON_FIELD_RE, "VID/PID field"), + (NAMED_LITERAL_RE, "named VID/PID literal"), + (TUPLE_PAIR_RE, "USB-shaped numeric pair"), + (IDENTITY_STRING_RE, "USB-shaped identity string"), + (EMBED_RE, "embedded USB identity asset"), + (CATALOGUE_RE, "legacy USB catalogue symbol"), + ) + for pattern, reason in checks: + if not pattern.search(line): + continue + key = (line_number, reason) + if key not in seen: + findings.append(Finding(path, line_number, reason, stripped[:160])) + seen.add(key) return findings -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument("--base", default="origin/main") - args = parser.parse_args() - diff = subprocess.run( - ["git", "diff", "--unified=0", f"{args.base}...HEAD", "--"], - check=True, - capture_output=True, - text=True, +def tracked_paths() -> list[str]: + result = subprocess.run( + ["git", "ls-files", "-z"], check=True, capture_output=True ).stdout - findings = added_production_pairs(diff) + return [item.decode("utf-8") for item in result.split(b"\0") if item] + + +def scan_tree(root: Path = Path(".")) -> list[Finding]: + findings: list[Finding] = [] + for path in tracked_paths(): + file_path = root / path + if not file_path.is_file() or file_path.suffix.lower() not in TEXT_SUFFIXES: + continue + try: + source = file_path.read_text(encoding="utf-8") + except UnicodeDecodeError: + continue + findings.extend(scan_text(path, source)) + return findings + + +def main() -> int: + findings = scan_tree() if findings: print( - "new same-line crates/python or board-JSON USB VID/PID literals " - "are not allowed:", - file=sys.stderr, - ) - for path, vid, pid in findings: - print(f" {path}: {vid}:{pid}", file=sys.stderr) - print( - "Publish the identity through FastLED/boards and consume its artifact. " - "This preparatory guard does not cover separate Rust constants, " - "decimal/symbolic forms, non-board JSON, CI/setup/workflow/root paths, " - "or row-level board provenance.", + "production USB VID/PID data is forbidden; publish it through " + "FastLED/boards and consume the verified artifact:", file=sys.stderr, ) + for finding in findings: + print( + f" {finding.path}:{finding.line}: {finding.reason}: " + f"{finding.excerpt}", + file=sys.stderr, + ) return 1 - print( - "USB VID/PID diff guard passed for same-line crates/python pairs and " - "board JSON vid/pid fields." - ) + print("Full-tree USB VID/PID source guard passed.") return 0 diff --git a/ci/test_check_usb_vidpid_literals.py b/ci/test_check_usb_vidpid_literals.py index 9c0cebb6..3d8ae5ee 100644 --- a/ci/test_check_usb_vidpid_literals.py +++ b/ci/test_check_usb_vidpid_literals.py @@ -1,50 +1,83 @@ -"""Unit tests for the production USB VID/PID diff guard.""" +"""Unit tests for the full-tree production USB VID/PID guard.""" import importlib.util +import sys import unittest spec = importlib.util.spec_from_file_location("guard", "ci/check_usb_vidpid_literals.py") guard = importlib.util.module_from_spec(spec) assert spec.loader is not None +sys.modules[spec.name] = guard spec.loader.exec_module(guard) class GuardTests(unittest.TestCase): - def test_catalogue_hex_pair_is_rejected(self): - diff = "+++ b/crates/fbuild-serial/src/boards.rs\n+ (0x1234, 0x5678)," - self.assertEqual( - guard.added_production_pairs(diff), - [("crates/fbuild-serial/src/boards.rs", "1234", "5678")], - ) + def reasons(self, path: str, source: str) -> list[str]: + return [finding.reason for finding in guard.scan_text(path, source)] + + def test_production_pair_is_rejected(self): + reasons = self.reasons("crates/demo/src/boards.rs", "const ID: (u16, u16) = (0x1234, 0x5678);") + self.assertIn("USB-shaped numeric pair", reasons) + + def test_named_single_literal_is_rejected(self): + reasons = self.reasons("crates/demo/src/device.rs", "if vid == 0x1234 { return true; }") + self.assertIn("named VID/PID literal", reasons) + + def test_named_decimal_literal_is_rejected(self): + reasons = self.reasons("crates/demo/src/device.rs", "const DEVICE_VID: u16 = 4660;") + self.assertIn("named VID/PID literal", reasons) def test_board_json_separate_fields_are_rejected(self): - diff = ( - "+++ b/crates/fbuild-config/assets/boards/json/demo.json\n" - '+ "vid": "0x1234",\n' - '+ "pid": "5678",\n' + findings = guard.scan_text( + "crates/fbuild-config/assets/boards/json/demo.json", + '{\n "vid": "0x1234",\n "pid": "5678"\n}', ) self.assertEqual( - guard.added_production_pairs(diff), - [ - ("crates/fbuild-config/assets/boards/json/demo.json", "VID", "0x1234"), - ("crates/fbuild-config/assets/boards/json/demo.json", "PID", "5678"), - ], + [finding.reason for finding in findings], + ["VID/PID field", "VID/PID field"], ) - def test_fixture_marker_cannot_bypass_production_guard(self): - diff = ( - "+++ b/crates/fbuild-serial/src/boards.rs\n" - "+ // USB_VIDPID_ALLOW (0x1234, 0x5678)\n" - ) - self.assertEqual( - guard.added_production_pairs(diff), - [("crates/fbuild-serial/src/boards.rs", "1234", "5678")], + def test_embedded_catalogue_is_rejected(self): + reasons = self.reasons( + "crates/demo/src/data.rs", + 'const IDS: &[u8] = include_bytes!("data/usb-ids.bin");', ) + self.assertIn("embedded USB identity asset", reasons) + + def test_cfg_test_module_is_allowed_and_following_code_is_scanned(self): + source = """ +#[cfg(test)] +mod tests { + const ID: (u16, u16) = (0x1234, 0x5678); +} +const VID: u16 = 0x9999; +""" + findings = guard.scan_text("crates/demo/src/lib.rs", source) + self.assertEqual(len(findings), 1) + self.assertEqual(findings[0].line, 6) + + def test_cfg_test_single_item_is_allowed(self): + source = """ +#[cfg(test)] +const ID: (u16, u16) = (0x1234, 0x5678); +""" + self.assertEqual(guard.scan_text("crates/demo/src/lib.rs", source), []) + + def test_cfg_test_raw_string_braces_do_not_end_module_early(self): + source = ''' +#[cfg(test)] +mod tests { + const JSON: &str = r#"{"vid":"1234"}"#; + const ID: (u16, u16) = (0x1234, 0x5678); +} +''' + self.assertEqual(guard.scan_text("crates/demo/src/lib.rs", source), []) - def test_test_fixture_is_out_of_production_scope(self): - diff = "+++ b/crates/fbuild-serial/tests/fixture.rs\n+ (0x1234, 0x5678)" - self.assertEqual(guard.added_production_pairs(diff), []) + def test_test_path_and_frozen_fixture_are_allowed(self): + source = "const ID: (u16, u16) = (0x1234, 0x5678);" + self.assertEqual(guard.scan_text("crates/demo/tests/fixture.rs", source), []) + self.assertEqual(guard.scan_text("crates/fbuild-core/data/fixture.txt", source), []) if __name__ == "__main__": diff --git a/crates/CLAUDE.md b/crates/CLAUDE.md index 93f7cb07..154186d0 100644 --- a/crates/CLAUDE.md +++ b/crates/CLAUDE.md @@ -57,7 +57,7 @@ fbuild-test-support (test utilities) ────────────── ## Crate Responsibilities -- **fbuild-core** — `FbuildError`/`Result`, `BuildProfile`, `Platform`, `SizeInfo`, `DaemonState`. Also ships the `dump_usb_ids` example (`examples/dump_usb_ids.rs`) — **tier-1 source** for the nightly `online-data` USB-VID merge (FastLED/fbuild#720). Do not delete; the `usb-ids` workspace dep exists *only* for that example, so removing it drops the aggregator from 4 → 3 independent sources. +- **fbuild-core** — `FbuildError`/`Result`, `BuildProfile`, `Platform`, `SizeInfo`, `DaemonState`. USB identity catalogues are fetched from FastLED/boards and must never be generated or embedded here outside test fixtures. - **fbuild-config** — `PlatformIOConfig` (INI parser with `extends` inheritance), `BoardConfig`, `McuSpec` - **fbuild-paths** — Dev/prod path isolation (`~/.fbuild/{dev|prod}/`), version+identity-keyed daemon endpoint (`daemon_endpoint_key`/`default_daemon_port`, dynamic range 49152–65535; FastLED/fbuild#1009), cache dirs - **fbuild-packages** — URL-based package downloads, toolchain resolution, library manager, parallel pipeline diff --git a/crates/fbuild-cli/src/cli/bringup.rs b/crates/fbuild-cli/src/cli/bringup.rs index 6fb60d28..85d702ab 100644 --- a/crates/fbuild-cli/src/cli/bringup.rs +++ b/crates/fbuild-cli/src/cli/bringup.rs @@ -269,6 +269,68 @@ fn elapsed_ms(started: Instant) -> u64 { #[cfg(test)] mod tests { use super::*; + use sha2::{Digest, Sha256}; + use std::sync::Once; + + static USB_PROFILE_FIXTURE: Once = Once::new(); + + fn install_usb_profile_fixture() { + USB_PROFILE_FIXTURE.call_once(|| { + let artifact = serde_json::json!({ + "schema_version": 1, + "metadata": {}, + "identities": { + "16c0:0483": [{ + "match": {"vid": "16c0", "pid": "0483", "pid_mask": null}, + "purpose": "runtime", + "role": "runtime_cdc", + "transport": "usb", + "reset": "none", + "handoff": "none", + "platform": "nxplpc", + "family": "lpc11u35-vcom", + "generation": null, + "interface": "cdc", + "provenance": { + "source_url": "test://fbuild-cli/bringup", + "source_revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "source_class": "test" + }, + "priority": 100, + "allow_ambiguous": false + }] + }, + "boards": { + "lpc845brk": { + "identities": { + "bootloader": [], + "compile": [], + "probe": [], + "runtime": ["16c0:0483"] + }, + "aliases": ["lpc845", "lpc804", "lpcxpresso845max", "lpcxpresso804"] + } + } + }); + let artifact_bytes = serde_json::to_vec(&artifact).unwrap(); + let digest = Sha256::digest(&artifact_bytes) + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::(); + let metadata = serde_json::json!({ + "usb_profiles": "usb-profiles.json", + "usb_profiles_schema_version": 1, + "usb_profiles_sha256": digest + }); + let tmp = tempfile::tempdir().unwrap(); + let meta_path = tmp.path().join("_meta.json"); + let profiles_path = tmp.path().join("usb-profiles.json"); + std::fs::write(&meta_path, serde_json::to_vec(&metadata).unwrap()).unwrap(); + std::fs::write(&profiles_path, artifact_bytes).unwrap(); + fbuild_core::usb::profiles::try_install_verified_cache(&meta_path, &profiles_path) + .unwrap(); + }); + } #[test] fn default_config_is_echo_4242() { @@ -337,6 +399,7 @@ mod tests { /// surfaces all four. #[test] fn dry_run_reports_lpc845brk_resolved_state() { + install_usb_profile_fixture(); let args = BringupArgs { env: "lpc845brk".to_string(), rpc_method: None, @@ -383,6 +446,7 @@ mod tests { #[test] fn non_dry_run_returns_stubbed_result_with_resolved_state() { + install_usb_profile_fixture(); let args = BringupArgs { env: "lpc845brk".to_string(), rpc_method: None, diff --git a/crates/fbuild-cli/src/cli/port_scan.rs b/crates/fbuild-cli/src/cli/port_scan.rs index 58ff8e72..35b2573d 100644 --- a/crates/fbuild-cli/src/cli/port_scan.rs +++ b/crates/fbuild-cli/src/cli/port_scan.rs @@ -9,16 +9,9 @@ //! └─ Espressif Systems / ESP32-S3 //! ``` //! -//! Different from [`super::serial_probe::SerialAction::Probe`]'s `list` -//! action (FastLED/fbuild#686) which annotates from a tiny hardcoded -//! `BOARD_FINGERPRINTS` table — `port scan` consults the fbuild online-data -//! VID:PID overlay via the tiered resolver, so an unrecognized -//! device shows the actual vendor + product name instead of a blank -//! hint. -//! -//! The canonical runtime data source is the `fastled/fbuild` `online-data` -//! branch. The resolver in `fbuild_core::usb` is wired to consume it via the -//! tier-2 overlay; this command takes whatever the resolver returns. +//! The resolver consumes the published FastLED/boards display-name artifact. +//! Unknown devices receive deterministic fallback labels rather than a copied +//! catalogue in fbuild. use clap::Subcommand; use fbuild_core::{FbuildError, Result}; @@ -31,11 +24,8 @@ pub enum PortAction { /// the OS-visible identity + a `└─ vendor / product` second row /// resolved via [`fbuild_core::usb::resolve`]. Scan { - /// Skip the network fetch of the fbuild online-data overlay - /// (tier-2 of the resolver). Useful for offline runs — the - /// embedded vendor archive (tier-1) still provides vendor - /// names; product columns fall through to the synthetic - /// `Device 0xPPPP` placeholder. + /// Skip refreshing the FastLED/boards display-name cache. Useful for + /// offline runs; uncached identities receive `Unknown` labels. #[arg(long)] offline: bool, }, @@ -50,11 +40,8 @@ pub fn run_port(action: PortAction) -> Result<()> { fn run_scan(offline: bool) -> Result<()> { if !offline { - // Best-effort: populate the tier-2 online overlay so the - // resolver returns real product names (not just vendor + - // synthetic placeholder) for VID:PIDs the overlay carries. - // Errors are swallowed — the resolver always degrades to - // tier-1 + tier-3 if the overlay can't load. + // Best-effort: refresh the FastLED/boards display-name cache. Errors + // are swallowed because enumeration can still render unknown labels. populate_online_overlay(); } // Use fbuild-serial's blessed enumerator, not `serialport::available_ports()` @@ -69,12 +56,12 @@ fn run_scan(offline: bool) -> Result<()> { Ok(()) } -/// Fetch the fbuild online-data `usb-vids.proto.zstd` tier-2 overlay backing +/// Fetch the FastLED/boards display-name artifact backing /// [`fbuild_core::usb::resolve`] into the local cache root, then install it. /// /// Best-effort: any I/O / network / parse failure is swallowed and the -/// resolver degrades to tier-1 (embedded vendor archive). The cache is -/// kept fresh on a 7-day cadence — older copies are refetched. +/// resolver degrades to deterministic unknown labels. The cache is kept fresh +/// on a 7-day cadence — older copies are refetched. fn populate_online_overlay() { populate_online_overlay_from_urls( fbuild_core::usb::USB_VIDS_PROTO_ZSTD_URL, @@ -83,10 +70,19 @@ fn populate_online_overlay() { } fn populate_online_overlay_from_urls(proto_url: &str, json_url: &str) { - let Some(proto_cache_path) = overlay_cache_path() else { + let root = fbuild_paths::get_cache_root(); + populate_online_overlay_from_urls_in(proto_url, json_url, &root); +} + +fn populate_online_overlay_from_urls_in( + proto_url: &str, + json_url: &str, + root: &std::path::Path, +) { + let Some(proto_cache_path) = overlay_cache_path_in(root) else { return; }; - let Some(json_cache_path) = overlay_json_cache_path() else { + let Some(json_cache_path) = overlay_json_cache_path_in(root) else { return; }; if !fbuild_core::usb::populate_online_cache_from_paths_and_urls( @@ -95,19 +91,17 @@ fn populate_online_overlay_from_urls(proto_url: &str, json_url: &str) { proto_url, json_url, ) { - tracing::debug!("port scan: overlay unavailable; degrading to tier-1 only"); + tracing::debug!("port scan: FastLED/boards display-name cache unavailable"); } } -fn overlay_cache_path() -> Option { - let root = fbuild_paths::get_cache_root(); +fn overlay_cache_path_in(root: &std::path::Path) -> Option { let dir = root.join("usb"); std::fs::create_dir_all(&dir).ok()?; Some(dir.join("usb-vids.proto.zstd")) } -fn overlay_json_cache_path() -> Option { - let root = fbuild_paths::get_cache_root(); +fn overlay_json_cache_path_in(root: &std::path::Path) -> Option { let dir = root.join("usb"); std::fs::create_dir_all(&dir).ok()?; Some(dir.join("usb-vid.json")) @@ -244,9 +238,8 @@ 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 — i.e. *not* the synthetic -/// `Device 0xPPPP` placeholder. This comes from the embedded -/// FastLED/boards VID:PID archive (offline) or the online overlay. +/// 1. Resolver's product if it is a real FastLED/boards name rather than a +/// deterministic `Device` or `Unknown product` placeholder. /// 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" @@ -254,12 +247,13 @@ fn cdc_label(kernel_class: Option) - /// 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. +/// product names are owned by FastLED/boards and fetched at runtime +/// (FastLED/fbuild#722, #959). A missing name is a data gap to fix there, 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 { + let unknown = format!("Unknown product 0x{pid:04X}"); + if resolved_product != synthetic && resolved_product != unknown { return resolved_product.to_string(); } if let Some(d) = os_descriptor { @@ -296,31 +290,27 @@ mod tests { use serialport::{SerialPortInfo, SerialPortType, UsbPortInfo}; use std::io::{Read, Write}; use std::net::TcpListener; - use std::sync::Mutex; + use std::sync::{Mutex, MutexGuard}; use std::time::Duration; - static ENV_LOCK: Mutex<()> = Mutex::new(()); + static USB_CACHE_LOCK: Mutex<()> = Mutex::new(()); - struct EnvVarGuard { - name: &'static str, - previous: Option, - } - - impl EnvVarGuard { - fn set(name: &'static str, value: impl AsRef) -> Self { - let previous = std::env::var_os(name); - std::env::set_var(name, value); - Self { name, previous } - } - } - - impl Drop for EnvVarGuard { - fn drop(&mut self) { - match self.previous.take() { - Some(value) => std::env::set_var(self.name, value), - None => std::env::remove_var(self.name), - } - } + fn install_name_fixture() -> MutexGuard<'static, ()> { + let guard = USB_CACHE_LOCK.lock().unwrap(); + let tmp = tempfile::tempdir().unwrap(); + let path = tmp.path().join("usb-ids.json"); + std::fs::write( + &path, + r#"{ + "303a":{"vendor":"Espressif Systems","products":[["1001","USB JTAG/serial debug unit"]]}, + "0403":{"vendor":"Future Technology Devices International","products":[["6001","FT232 Serial UART"]]}, + "10c4":{"vendor":"Silicon Labs","products":[["ea60","CP210x UART Bridge"]]}, + "16c0":{"vendor":"PJRC","products":[["0483","Teensy USB Serial"]]} + }"#, + ) + .unwrap(); + assert!(fbuild_core::usb::try_install_online_cache(&path)); + guard } fn usb_port( @@ -344,13 +334,11 @@ mod tests { } #[test] - fn overlay_cache_path_uses_fbuild_cache_dir() { - let _env = ENV_LOCK.lock().unwrap(); + fn overlay_cache_paths_use_supplied_cache_root() { let tmp = tempfile::tempdir().unwrap(); - let _guard = EnvVarGuard::set("FBUILD_CACHE_DIR", tmp.path()); - let path = overlay_cache_path().expect("cache path"); - let json_path = overlay_json_cache_path().expect("json cache path"); + let path = overlay_cache_path_in(tmp.path()).expect("cache path"); + let json_path = overlay_json_cache_path_in(tmp.path()).expect("json cache path"); assert_eq!(path, tmp.path().join("usb").join("usb-vids.proto.zstd")); assert_eq!(json_path, tmp.path().join("usb").join("usb-vid.json")); @@ -359,9 +347,8 @@ mod tests { #[test] fn populate_overlay_falls_back_to_json_when_proto_is_missing() { - let _env = ENV_LOCK.lock().unwrap(); + let _usb = USB_CACHE_LOCK.lock().unwrap(); let tmp = tempfile::tempdir().unwrap(); - let _guard = EnvVarGuard::set("FBUILD_CACHE_DIR", tmp.path()); let listener = TcpListener::bind("127.0.0.1:0").unwrap(); listener.set_nonblocking(true).unwrap(); @@ -405,9 +392,10 @@ mod tests { request_count }); - populate_online_overlay_from_urls( + populate_online_overlay_from_urls_in( &format!("http://{addr}/usb-vids.proto.zstd"), &format!("http://{addr}/usb-vid.json"), + tmp.path(), ); assert_eq!(handle.join().unwrap(), 2); @@ -427,6 +415,7 @@ mod tests { #[test] fn single_usb_port_renders_two_rows_and_summary() { + let _usb = install_name_fixture(); let ports = vec![usb_port( "COM25", 0x303A, @@ -441,8 +430,7 @@ mod tests { assert!(out.contains("USB Serial Device (COM25)")); assert!(out.contains("ser=80:F1:B2:D1:DF:B1")); // Row 2: the `└─` continuation prefix + vendor/product from the - // tiered resolver. Espressif is in the embedded archive via - // the inlined supplement. + // explicitly installed runtime fixture. assert!(out.contains("└─")); assert!( out.to_lowercase().contains("espressif"), @@ -471,10 +459,11 @@ mod tests { #[test] fn missing_product_descriptor_falls_back_to_default_text() { + let _usb = install_name_fixture(); let ports = vec![usb_port("COM7", 0x0403, 0x6001, None, None)]; let out = render_scan(&ports); assert!(out.contains("USB Serial Device")); - // FTDI VID lives in the embedded archive. + // The explicit runtime fixture supplies the FTDI name. assert!( out.to_lowercase().contains("future technology") || out.to_lowercase().contains("ftdi") ); @@ -482,6 +471,7 @@ mod tests { #[test] fn multiple_ports_render_in_order_with_blank_separators() { + let _usb = install_name_fixture(); let ports = vec![ usb_port("COM1", 0x303A, 0x1001, None, None), usb_port("COM2", 0x10C4, 0xEA60, None, None), @@ -588,11 +578,12 @@ mod tests { } #[test] - fn teensy_16c0_port_resolves_pjrc_from_embedded_archive() { + fn teensy_16c0_port_resolves_pjrc_from_runtime_fixture() { + let _usb = install_name_fixture(); // The enumeration fix (fbuild-serial's Windows walk) is what makes a // Teensy show up at all; here we pin that once it IS enumerated, the // scan resolves VID 16C0 to PJRC. The vendor + product names come from - // the embedded FastLED/boards VID:PID archive, NOT a hardcoded table. + // an explicit runtime-cache fixture, not production constants. // FastLED/fbuild#962. let ports = vec![usb_port( "COM20", @@ -605,7 +596,7 @@ mod tests { assert!(out.contains("16C0:0483"), "got: {out}"); assert!( out.to_lowercase().contains("pjrc") || out.to_lowercase().contains("teensy"), - "expected PJRC/Teensy from the embedded archive, got: {out}" + "expected PJRC/Teensy from the runtime fixture, got: {out}" ); } @@ -619,10 +610,11 @@ mod tests { } #[test] - fn common_esp32_cdc_pid_resolves_from_embedded_archive() { + fn common_esp32_cdc_pid_resolves_from_runtime_fixture() { + let _usb = install_name_fixture(); // 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. + // product name from an explicit runtime-cache fixture, not a production + // fallback table. FastLED/fbuild#722. let ports = vec![usb_port( "COM25", 0x303A, @@ -633,7 +625,7 @@ mod tests { let out = render_scan(&ports); assert!( out.to_lowercase().contains("espressif"), - "expected Espressif vendor from the archive, got: {out}" + "expected Espressif vendor from the runtime fixture, got: {out}" ); // A real archive product name, not the synthetic placeholder or the // generic Windows descriptor. @@ -667,8 +659,7 @@ mod tests { #[test] fn generic_windows_descriptor_does_not_override_synthetic() { // The bare "USB Serial Device (COM25)" Windows fallback is not - // chip-specific, so we keep the synthetic placeholder when no - // supplement applies. + // chip-specific, so we keep the deterministic unknown placeholder. let ports = vec![usb_port( "COM7", 0x303A, @@ -684,8 +675,8 @@ mod tests { .find(|l| l.contains("└─")) .expect("expected a resolver row"); assert!( - resolver_row.contains("Device 0xFEED"), - "expected synthetic placeholder on the resolver row, got: {resolver_row}" + resolver_row.contains("Unknown product 0xFEED"), + "expected unknown placeholder on the resolver row, got: {resolver_row}" ); assert!( !resolver_row.contains("USB Serial Device"), diff --git a/crates/fbuild-cli/src/cli/serial_probe.rs b/crates/fbuild-cli/src/cli/serial_probe.rs index b11e1c79..5df8d74c 100644 --- a/crates/fbuild-cli/src/cli/serial_probe.rs +++ b/crates/fbuild-cli/src/cli/serial_probe.rs @@ -202,7 +202,7 @@ fn parse_vid_pid(s: &str) -> Result<(u16, u16)> { .or_else(|| raw.split_once(',')) .ok_or_else(|| { FbuildError::SerialError(format!( - "expected `VID:PID` (e.g. `16C0:0483`), got `{raw}`" + "expected a hexadecimal `VID:PID`, got `{raw}`" )) })?; let vid = u16::from_str_radix(vid_s.trim(), 16) diff --git a/crates/fbuild-cli/src/daemon_client/types.rs b/crates/fbuild-cli/src/daemon_client/types.rs index ecc30d37..c8c21643 100644 --- a/crates/fbuild-cli/src/daemon_client/types.rs +++ b/crates/fbuild-cli/src/daemon_client/types.rs @@ -403,8 +403,8 @@ pub struct DeviceInfoResponse { pub device_id: Option, pub vid: Option, pub pid: Option, - /// Pretty USB vendor name resolved by the daemon (tier-1 bundled - /// `usb-ids` + tier-2 online overlay). `None` for non-USB ports. + /// Pretty USB vendor name resolved by the daemon from FastLED/boards. + /// `None` for non-USB ports. #[serde(default)] pub vendor_name: Option, /// Pretty USB product name (same provenance as `vendor_name`). diff --git a/crates/fbuild-core/Cargo.toml b/crates/fbuild-core/Cargo.toml index 0579d25d..4ec75837 100644 --- a/crates/fbuild-core/Cargo.toml +++ b/crates/fbuild-core/Cargo.toml @@ -15,12 +15,6 @@ tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } sha2 = { workspace = true } -# 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 } # Runtime decompression for the downloaded FastLED/boards cache. zstd = { workspace = true } prost = { workspace = true } diff --git a/crates/fbuild-core/data/README.md b/crates/fbuild-core/data/README.md index a57be496..b507cad1 100644 --- a/crates/fbuild-core/data/README.md +++ b/crates/fbuild-core/data/README.md @@ -6,7 +6,7 @@ the published FastLED/boards artifacts during the build/cache phase. | File | Purpose | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `usb-vendors.tar.zst` | USB Vendor-ID → vendor-name map (long-tail fallback, ~2.2k VIDs). Produced by `online-data-tools/build_vendor_archive.py`. See `crate::usb::embedded`. | +| `usb-vendors.tar.zst` | Frozen test-only USB Vendor-ID fixture used by `crate::usb::embedded` under `cfg(test)`. It is never a production fallback. | | `usb-vids.proto.zstd` | Test fixture for the compact VID:PID overlay produced by **FastLED/boards**. It must never be used as a production built-in catalogue. Production ingestion fetches/consumes the published boards artifact. | ## How to refresh the VID:PID overlay (`usb-vids.proto.zstd`) @@ -29,24 +29,7 @@ hardcoded table or embedded runtime blob in fbuild — and re-run the boards pipeline. The published artifact then carries it through to fbuild's `usb::resolve` on the next ingestion. -## 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`). +The vendor archive is intentionally frozen. New identities and corrections +must be published through FastLED/boards; never refresh this fixture to solve a +production lookup or deployment problem. Validate it only with `soldr cargo` +tests. diff --git a/crates/fbuild-core/examples/README.md b/crates/fbuild-core/examples/README.md index eea72ba6..d0d90cc1 100644 --- a/crates/fbuild-core/examples/README.md +++ b/crates/fbuild-core/examples/README.md @@ -1,5 +1,5 @@ # `fbuild-core` examples -Standalone runnable examples exercising public `fbuild_core` APIs. - -- `dump_usb_ids.rs` — dumps the bundled `usb-ids` database as a sorted JSON object to stdout. Consumed by the `online-data` branch's nightly workflow as one input source for the merged `usb-vid.json`. Run with: `soldr cargo run --release --example dump_usb_ids -p fbuild-core > usb-ids-rs.json`. +This directory is reserved for standalone examples of public `fbuild_core` +APIs. USB identity publication belongs to FastLED/boards; do not add catalogue +dump or generation tools here. diff --git a/crates/fbuild-core/examples/dump_usb_ids.rs b/crates/fbuild-core/examples/dump_usb_ids.rs deleted file mode 100644 index b8736183..00000000 --- a/crates/fbuild-core/examples/dump_usb_ids.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Dump the bundled `usb-ids` database as a JSON object to stdout. -//! -//! # DO NOT REMOVE — anti-removal policy (FastLED/fbuild#720) -//! -//! This example is **tier-1** of the USB-VID name merge. It is invoked -//! by the `Update data` workflow (`.github/workflows/update-data.yml`) -//! to export the bundled [`usb-ids`](https://crates.io/crates/usb-ids) -//! crate snapshot as JSON; `tools/merge_sources.py` then unions that -//! with the linux-usb.org mirror, the usbids GitHub mirror, and the -//! curated `vendor_names_inlined.py` overlay to produce the published -//! `online-data/data/usb-vid.json`. -//! -//! The fbuild **runtime** USB resolver (`fbuild-core::usb::resolver`) -//! does not depend on this example — it uses the compile-time-embedded -//! `usb-vendors.tar.zst` (`fbuild-core::usb::embedded`). The `usb-ids` -//! workspace dependency is now pulled in *only* by this example, so -//! removing the example would also remove the dependency, dropping the -//! aggregator from 4 → 3 independent sources of truth. Decoupling -//! recorded in #719; anti-removal policy in #720. -//! -//! If your LLM-driven cleanup pass flagged this file as "dead code," -//! it is not — please read FastLED/fbuild#720 before deleting. -//! -//! Used by the `online-data` branch's nightly workflow (see -//! `.github/workflows/update-data.yml`) as one of the input sources -//! for the merged `usb-vid.json`. Running this example via -//! `soldr cargo run --release --example dump_usb_ids -p fbuild-core` -//! captures the exact data the bundled `usb-ids` crate version we depend -//! on actually knows about, so the online overlay can be cross-checked -//! against tier-1. -//! -//! Output schema (alphabetically sorted by key): -//! ```json -//! { -//! "0403:6001": {"vendor": "Future Technology Devices ...", "product": "FT232 ..."}, -//! ... -//! } -//! ``` -//! -//! No CLI arguments, no IO beyond stdout — kept intentionally tiny so the -//! nightly workflow can pipe it into a file with no risk of partial output. - -use std::collections::BTreeMap; - -fn main() { - // BTreeMap → keys are emitted in sorted order by `serde_json`. - let mut out: BTreeMap = BTreeMap::new(); - - for vendor in usb_ids::Vendors::iter() { - let vendor_name = vendor.name().to_string(); - for device in vendor.devices() { - let key = format!("{:04x}:{:04x}", vendor.id(), device.id()); - out.insert( - key, - Entry { - vendor: vendor_name.clone(), - product: device.name().to_string(), - }, - ); - } - } - - // pretty-print so diffs on the `online-data` branch are reviewable. - serde_json::to_writer_pretty(std::io::stdout().lock(), &out).expect("write JSON to stdout"); - println!(); -} - -#[derive(serde::Serialize)] -struct Entry { - vendor: String, - product: String, -} diff --git a/crates/fbuild-core/src/usb/README.md b/crates/fbuild-core/src/usb/README.md index d0d85b86..fef674c9 100644 --- a/crates/fbuild-core/src/usb/README.md +++ b/crates/fbuild-core/src/usb/README.md @@ -4,4 +4,5 @@ USB VID:PID → human-readable `(vendor, product)` resolution. - `mod.rs` — public API surface (`resolve`, `try_resolve`, `pretty`, `install_online_cache`). - `resolver.rs` — tiered lookup implementation + unit tests covering FTDI, CP210x, CH340, Espressif, and the synthetic fallback. -- `data.rs` — optional runtime overlay loaded from a JSON file. Used to pick up newly-assigned VID/PID pairs that the bundled `usb-ids` crate doesn't yet know about. Powered by the repo's `online-data` branch and its nightly refresh workflow. +- `data.rs` — verified runtime catalogue cache populated from the published FastLED/boards artifacts. +- `profiles.rs` — typed board, runtime, bootloader, probe, and compile identity profiles from FastLED/boards. diff --git a/crates/fbuild-core/src/usb/data.rs b/crates/fbuild-core/src/usb/data.rs index 08d6ed00..de90eed2 100644 --- a/crates/fbuild-core/src/usb/data.rs +++ b/crates/fbuild-core/src/usb/data.rs @@ -1,8 +1,7 @@ -//! Tier-2 online overlay: an optional per-VID protobuf map loaded from disk -//! at runtime. +//! FastLED/boards display-name cache loaded from disk at runtime. //! -//! Current on-disk schema is `usb-vids.proto.zstd`, published by the -//! `fastled/fbuild` `online-data` branch: +//! The compact on-disk schema is `usb-vids.proto.zstd`, published by +//! FastLED/boards: //! //! ```protobuf //! message UsbVidDatabase { @@ -45,14 +44,14 @@ //! shape on disk just avoids duplicating the vendor name for every //! product entry under a VID (significantly smaller payload). //! -//! The CLI downloads the zstd-compressed protobuf from `online-data`, -//! writes it to the global fbuild cache root, and calls +//! The CLI downloads the zstd-compressed protobuf from FastLED/boards, writes +//! it to the global fbuild cache root, and calls //! [`install_online_cache_proto_zstd`] to plug it into the resolver. //! Replacing the cache is supported (`RwLock`, not `OnceLock`) so the //! daemon/CLI can refresh during a long-running session without a restart. //! -//! All errors here are swallowed by design — if the overlay can't load, the -//! resolver simply degrades to tier-1 + tier-3. +//! Display-cache errors are swallowed by design; the resolver degrades to +//! deterministic unknown labels. use super::UsbInfo; use prost::Message; @@ -71,25 +70,17 @@ pub const MANIFEST_URL: &str = "https://fastled.github.io/boards/_meta.json"; /// tests; new code should use [`USB_VIDS_PROTO_ZSTD_URL`]. pub const USB_VID_JSON_URL: &str = "https://fastled.github.io/boards/usb-ids.json"; -/// Current compact USB VID:PID overlay published by the `online-data` branch. +/// Compact USB VID:PID display-name artifact published by FastLED/boards. pub const USB_VIDS_PROTO_ZSTD_URL: &str = "https://fastled.github.io/boards/usb-vids.proto.zstd"; static ONLINE_MAP: RwLock>> = RwLock::new(None); -/// Compile-time-embedded VID:PID → {vendor, product} overlay — the same -/// compact `usb-vids.proto.zstd` the online path fetches, but baked into the -/// binary so full VID:PID resolution works OFFLINE and needs no hardcoded -/// per-board tables. Produced by the FastLED/boards data pipeline -/// (`builders/build_usb_ids.py` over the `platformio`/`arduino`/`vendors`/ -/// `other` branches) and vendored here. Refresh workflow: see -/// `crates/fbuild-core/data/README.md`. -// Production never ships a built-in VID/PID catalogue. The canonical -// FastLED/boards artifact is fetched/ingested by the build/runtime cache -// path. Keep the historical blob available only to unit tests as a fixture. +/// Frozen historical fixture used only by this module's unit tests. +/// Production never includes it or falls back to it. #[cfg(test)] const EMBEDDED_PROTO: &[u8] = include_bytes!("../../data/usb-vids.proto.zstd"); -/// Both projections of the embedded proto. The compact `usb-vids.proto.zstd` +/// Both projections of the frozen fixture. The compact protobuf /// carries a `Vendor{vid, name, [Product{pid, name}]}` tree, so a single /// artifact yields BOTH a VID→vendor map AND a VID:PID→{vendor, product} /// map — no separate per-VID blob or hardcoded table needed. Parsed exactly @@ -114,8 +105,7 @@ fn embedded() -> &'static EmbeddedOverlay { } /// Inflate + parse the embedded proto into both projections. Errors bubble -/// up to `unwrap_or_default()` (empty overlay) so a bad blob degrades to -/// tier-1 vendor resolution rather than crashing. +/// up to `unwrap_or_default()` so a bad fixture yields an empty test map. #[cfg(test)] fn decode_embedded_overlay(raw: &[u8]) -> Result { let mut decoded = Vec::with_capacity(raw.len() * 4); @@ -164,9 +154,7 @@ pub fn embedded_vendor_count() -> usize { embedded().vendors.len() } -/// 7-day cache TTL. The `online-data` branch refreshes nightly; a weekly -/// local refresh gives useful freshness without adding network cost to every -/// serial-port operation. +/// A weekly local refresh balances freshness with serial-port scan latency. pub const ONLINE_CACHE_TTL_SECS: u64 = 7 * 24 * 60 * 60; #[derive(Clone, PartialEq, Message)] @@ -294,8 +282,7 @@ pub fn try_install_online_cache(path: &Path) -> bool { /// Install the overlay from the current `usb-vids.proto.zstd` cache file. /// Silently no-ops on any IO, zstd, or protobuf decode error so USB -/// resolution always degrades to the embedded vendor archive instead of -/// failing port enumeration. +/// resolution degrades to unknown labels instead of failing port enumeration. pub fn install_online_cache_proto_zstd(path: &Path) { let _ = try_install_online_cache_proto_zstd(path); } @@ -477,16 +464,13 @@ pub(crate) fn online_lookup(vid: u16, pid: u16) -> Option { guard.as_ref()?.get(&key).cloned() } -/// Compile-time embedded overlay only (FastLED/boards curated device map). +/// Frozen fixture lookup available only to this crate's tests. #[cfg(test)] pub(crate) fn embedded_lookup(vid: u16, pid: u16) -> Option { embedded().vidpid.get(&pack(vid, pid)).cloned() } -/// Combined tier-2 lookup (online overlay, then embedded overlay). Retained -/// for the test suite; production resolution goes through the layered -/// [`super::resolver::try_resolve`], which consults `online_lookup` and -/// `embedded_lookup` separately so it can source the vendor authoritatively. +/// Runtime-cache lookup followed by the frozen fixture, for tests only. #[cfg(test)] pub(crate) fn lookup(vid: u16, pid: u16) -> Option { online_lookup(vid, pid).or_else(|| embedded_lookup(vid, pid)) @@ -519,8 +503,7 @@ mod tests { let _guard = OVERLAY_LOCK.lock().unwrap(); let tmp = tempfile::tempdir().unwrap(); let path = tmp.path().join("usb-vid.json"); - // Nested per-VID shape (the format published on the - // `online-data` branch starting with the multi-dataset rev). + // Nested per-VID shape used by the FastLED/boards JSON artifact. let json = r#"{ "feed": { "vendor": "Feedface Inc", diff --git a/crates/fbuild-core/src/usb/embedded.rs b/crates/fbuild-core/src/usb/embedded.rs index ad2672ea..ff7eb991 100644 --- a/crates/fbuild-core/src/usb/embedded.rs +++ b/crates/fbuild-core/src/usb/embedded.rs @@ -1,18 +1,14 @@ -//! Compile-time-embedded USB VID → vendor-name map. +//! Frozen USB VID → vendor-name fixture for tests. //! -//! 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. +//! This module is reachable only through `#[cfg(test)]`. Production builds use +//! the verified FastLED/boards runtime cache and must never include this blob. //! //! 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. +//! escaped per RFC 3986. See `parse_compact` for the inflater. //! //! Lookup is `O(1)` after the first call: the tar is decompressed + //! parsed exactly once into a `HashMap` behind a `OnceLock`. @@ -23,9 +19,7 @@ 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. +/// Maximum schema understood by the frozen test-fixture parser. pub const EMBEDDED_SCHEMA_VERSION: u64 = 2; const RAW_ARCHIVE: &[u8] = include_bytes!("../../data/usb-vendors.tar.zst"); @@ -33,8 +27,7 @@ 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". +/// archive doesn't carry that VID. pub fn vendor_name(vid: u16) -> Option<&'static str> { VENDOR_MAP .get_or_init(load_or_panic) @@ -211,14 +204,8 @@ mod tests { #[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. + // Pin representative rows in the frozen historical fixture. This + // validates the parser only; runtime identity comes from boards. for (vid, expected_substr) in [ (0x303a_u16, "Espressif"), // inlined supplement only (0x2e8a, "Raspberry Pi"), // inlined supplement only @@ -239,12 +226,11 @@ mod tests { #[test] fn embedded_resolves_every_issue_740_vendor() { - // FastLED/fbuild#740 hand-verified 19 vendor VIDs at each - // `online-data` publish. Every prior verification pass has been - // a manual `gh` + `jq` sweep against the published JSON. + // FastLED/fbuild#740 hand-verified these vendor rows in the retired + // publisher. Keep the frozen fixture coverage without reviving it. // // Codify the entire table here so CI catches any regression the - // moment the embedded vendor archive is rebuilt without one of + // moment the frozen vendor fixture loses one of // the headline VIDs — the exact class of drift that previously // required manually re-running the verification each cycle. // @@ -254,10 +240,8 @@ mod tests { // // ## Overlay vs embedded drift (documented, not aspirational) // - // The #740 issue body's "Vendor-resolution results" table was - // taken from the PUBLISHED `online-data/data/usb-vid.json`, - // which has `vendor_names_inlined.py` applied via - // `overlay_usb_vid.py --mode vendor-override`. Three of the 19 + // The #740 issue body's "Vendor-resolution results" table was taken + // from a historical generated dataset. Three of the 19 // VIDs in that table were vendor-name-overridden by that // overlay pass (so the "Actual" column reads the overlay // value): diff --git a/crates/fbuild-core/src/usb/mod.rs b/crates/fbuild-core/src/usb/mod.rs index bf8deb34..c4629b23 100644 --- a/crates/fbuild-core/src/usb/mod.rs +++ b/crates/fbuild-core/src/usb/mod.rs @@ -30,4 +30,6 @@ pub use data::{ }; #[cfg(test)] pub use embedded::vendor_name as embedded_vendor_name; -pub use resolver::{pretty, resolve, resolve_bundled, try_resolve, UsbInfo}; +#[cfg(test)] +pub use resolver::resolve_bundled; +pub use resolver::{pretty, resolve, try_resolve, UsbInfo}; diff --git a/crates/fbuild-core/src/usb/resolver.rs b/crates/fbuild-core/src/usb/resolver.rs index afd2815e..524e1f23 100644 --- a/crates/fbuild-core/src/usb/resolver.rs +++ b/crates/fbuild-core/src/usb/resolver.rs @@ -15,8 +15,7 @@ pub struct UsbInfo { /// Best-effort lookup. Never returns `None`: a synthetic /// `"Unknown vendor 0xVVVV"` / `"Unknown product 0xPPPP"` is produced -/// when both tier-1 (embedded vendor archive) and tier-2 (online overlay) -/// miss. +/// when the verified FastLED/boards runtime cache misses. pub fn resolve(vid: u16, pid: u16) -> UsbInfo { try_resolve(vid, pid).unwrap_or_else(|| UsbInfo { vendor: format!("Unknown vendor 0x{vid:04X}"), @@ -24,15 +23,9 @@ 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. +/// Returns `None` when the verified FastLED/boards cache does not know this +/// pair. Test builds may fall through to frozen fixtures below; production +/// builds never do. pub fn try_resolve(vid: u16, pid: u16) -> Option { if let Some(info) = super::data::online_lookup(vid, pid) { return Some(info); @@ -75,37 +68,14 @@ pub fn try_resolve(vid: u16, pid: u16) -> Option { } } -/// Vendor-name-only tier (no per-PID product). Two compile-time-embedded -/// sources, in priority order: -/// -/// 1. The comprehensive `usb-vendors.tar.zst` archive (~2.2k VIDs) — the -/// authoritative USB-IF vendor names (e.g. `10C4` → "Silicon Labs"). -/// 2. The `usb-vids.proto.zstd` VID→vendor map (FastLED/boards pipeline) as -/// a fallback for VIDs not in the archive. -/// -/// The archive is preferred because the boards pipeline's VID→vendor column -/// is board-attributed (it can label a shared bridge VID with whichever -/// board first claimed it); the per-VID:PID curated names still take effect -/// through the tier-2 [`try_resolve`] path, which consults the proto's -/// VID:PID map first. -/// -/// For VIDs present in either, `UsbInfo.product` is a synthetic -/// `"Device 0xPPPP"` placeholder since per-PID resolution lives in the -/// VID:PID overlay (tier-2). +/// Resolve against frozen vendor-name fixtures in unit tests. +#[cfg(test)] pub fn resolve_bundled(vid: u16, pid: u16) -> Option { - #[cfg(not(test))] - { - let _ = (vid, pid); - None - } - #[cfg(test)] - { let vendor = embedded::vendor_name(vid).or_else(|| super::data::embedded_vendor(vid))?; Some(UsbInfo { vendor: vendor.to_string(), product: format!("Device 0x{pid:04X}"), }) - } } /// `"vendor product (VVVV:PPPP)"` — the canonical display format used by @@ -231,12 +201,8 @@ mod tests { #[test] 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. + // The frozen historical fixture carries this vendor. This test pins + // fixture parsing only; production resolves it from FastLED/boards. let info = resolve_bundled(0x303A, 0x4002).expect("Espressif in embedded archive"); assert!( info.vendor.to_lowercase().contains("espressif"), @@ -255,7 +221,7 @@ mod tests { #[test] fn pretty_format_uses_canonical_shape() { - // FTDI VID is in the embedded vendor archive, and this PID is NOT in + // FTDI VID is in the frozen vendor fixture, and this PID is NOT in // the VID:PID proto overlay, so resolution falls to the vendor // archive: vendor resolves, product is the synthetic placeholder so // the tail is deterministic. diff --git a/crates/fbuild-daemon/src/device_manager.rs b/crates/fbuild-daemon/src/device_manager.rs index 81d6a852..f4d46ccb 100644 --- a/crates/fbuild-daemon/src/device_manager.rs +++ b/crates/fbuild-daemon/src/device_manager.rs @@ -257,9 +257,8 @@ impl DeviceManager { serialport::SerialPortType::UsbPort(_) => detect_is_cdc(&port_info.port_name), _ => None, }; - // Resolve VID:PID → pretty (vendor, product) via the bundled - // `usb-ids` snapshot + any online overlay the daemon has - // installed. When both are present, the resolver-derived + // Resolve VID:PID → pretty (vendor, product) via the verified + // FastLED/boards runtime cache. The resolver-derived // description wins over the (often blank or generic) string // returned by the OS-level enumerator. Bluetooth / PCI / // unknown ports keep their static fallback descriptor. diff --git a/crates/fbuild-daemon/src/main.rs b/crates/fbuild-daemon/src/main.rs index 9f8bcc67..e02708ea 100644 --- a/crates/fbuild-daemon/src/main.rs +++ b/crates/fbuild-daemon/src/main.rs @@ -98,12 +98,10 @@ async fn main() { let listener = bind_listener_with_retry(&addr).await; tracing::info!("listening on {}", addr); - // Populate the tier-2 USB VID:PID overlay so `device list/status/deploy` - // return full vendor + product names instead of the tier-1 vendor-only - // + synthetic `Device 0xPPPP` placeholder. Best-effort: the resolver - // silently degrades to the embedded vendor archive if the fetch or - // decode fails. Runs on a blocking thread so a slow network doesn't - // stall daemon bootstrap. + // Populate the FastLED/boards USB caches used by device discovery and + // deployment. Best-effort display-name failure degrades to deterministic + // unknown labels; identity-dependent behavior fails closed. Runs on a + // blocking thread so a slow network doesn't stall daemon bootstrap. tokio::task::spawn_blocking(populate_usb_overlay_best_effort); // FastLED/fbuild#800 (Phase 4 stage 2 of #789): start the embedded @@ -457,9 +455,9 @@ async fn main() { /// /// The daemon's `/api/devices/*` handlers call `fbuild_core::usb::resolve` /// to render vendor/product names. Without this best-effort startup step -/// the resolver only sees the compile-time embedded vendor archive -/// (tier-1), so unknown PIDs render as `Device 0xPPPP`. Any I/O, network, -/// or decode failure is swallowed — the resolver falls back to tier-1. +/// unknown devices render with deterministic fallback labels. Any display-name +/// I/O, network, or decode failure is swallowed; typed profile verification +/// still fails closed for identity-dependent behavior. /// /// The `/usb/` directory is created lazily inside the shared /// `fbuild_core::usb::populate_online_cache_from_paths` helper (its fetch @@ -472,9 +470,9 @@ fn populate_usb_overlay_best_effort() { let profiles_meta_path = dir.join("_meta.json"); let profiles_path = dir.join("usb-profiles.json"); if fbuild_core::usb::populate_online_cache_from_paths(&proto_path, &json_path) { - tracing::info!("usb overlay: tier-2 VID:PID map installed"); + tracing::info!("usb overlay: FastLED/boards VID:PID map installed"); } else { - tracing::warn!("usb overlay: tier-2 unavailable; falling back to embedded vendor archive"); + tracing::warn!("usb overlay: FastLED/boards map unavailable; using unknown-device labels"); } if fbuild_core::usb::profiles::populate_profiles_from_paths(&profiles_meta_path, &profiles_path) { diff --git a/crates/fbuild-serial/src/port_class.rs b/crates/fbuild-serial/src/port_class.rs index 80a52fcd..5033f0f3 100644 --- a/crates/fbuild-serial/src/port_class.rs +++ b/crates/fbuild-serial/src/port_class.rs @@ -29,9 +29,7 @@ //! - `serialport-rs` — uses libudev on Linux internally but doesn't //! surface class info; would need an upstream patch. //! - `udev` crate — clean wrapper but requires libudev at runtime. -//! fbuild deliberately avoids that runtime dep (see Cargo.toml -//! comment on `usb-ids = "1.2025"` line: "Pure Rust, no libusb / -//! no udev"). +//! fbuild deliberately avoids that runtime dependency. //! - `nusb` / `rusb` — enumerate USB devices but can't link a //! `/dev/ttyACM0` path back to its USB device; the linking step is //! itself OS-specific. diff --git a/docs/online-data.md b/docs/online-data.md index 074354d0..37ff350f 100644 --- a/docs/online-data.md +++ b/docs/online-data.md @@ -1,164 +1,42 @@ -# `online-data` branch + nightly refresh +# USB identity data -The repo carries a long-lived orphan branch called `online-data` that holds -periodically-refreshed reference datasets fbuild reads at runtime. Datasets -currently published: +FastLED/boards is the only production source for USB VID/PID identities used +by fbuild. fbuild consumes the published, verified artifacts at runtime; it +does not collect, generate, embed, or publish a second catalogue. -| Dataset | Path | Description | -|---|---|---| -| `usb-vid` | `data/usb-vid.json` | USB VID:PID → `{vendor, product}` (union of multiple sources) | -| `usb-vids.proto.zstd` | `data/usb-vids.proto.zstd` | Compact protobuf/zstd form of `usb-vid.json` used by fbuild runtime scans | -| `usb-vid-conflicts` | `data/usb-vid-conflicts.json` | Per-key disagreements between USB-VID sources (observability) | -| `pio-boards` | `data/pio-boards.json` | Full PlatformIO board catalog (vendor, mcu, frameworks, debug tools, etc.) | -| `vendor_boards` | `data/vendor_boards.json` | Slim view of `pio-boards` — only `{vendor, name, mcu}` per board id, for cheap "what board is plugged in?" lookups | +## Published artifacts -The format is **future-forward** — new datasets are added by writing a new -JSON file under `data/`; `tools/build_manifest.py` auto-discovers them on -the next workflow run. No client breakage when datasets are added. -Binary companion files such as `usb-vids.proto.zstd` are published under -`data/` too, but are consumed through explicit runtime constants rather than -manifest auto-discovery. +- Metadata and hashes: `https://fastled.github.io/boards/_meta.json` +- Typed board profiles: `https://fastled.github.io/boards/usb-profiles.json` +- Display-name catalogue: `https://fastled.github.io/boards/usb-ids.json` +- Compact display-name catalogue: + `https://fastled.github.io/boards/usb-vids.proto.zstd` -The companion in-process USB resolver lives at `fbuild_core::usb` — see -`crates/fbuild-core/src/usb/`. The branch is the **tier-2 fallback** when -the bundled `usb-ids` crate doesn't know a VID:PID. +`fbuild_core::usb::profiles` verifies the typed profile artifact against the +metadata document before installing it. `fbuild_core::usb::data` manages the +display-name cache. Failed downloads or validation never authorize a bundled +production fallback: identity-dependent behavior fails closed, while display +labels degrade to deterministic `Unknown` text. -VID/PID product rows are USB-name metadata, not board-support proof. Board -existence remains governed by `crates/fbuild-config/assets/boards`; if a board -is absent there, it may not be an fbuild-supported board. Local FastLED board -VID/PID rows fill product-name gaps for checked-in boards after stronger USB -owner and generic sources have won. Third-party SDK or board-package rows are -weaker supplements after that. +The files cached beneath fbuild's shared `usb/` cache directory are disposable +copies of these published artifacts. They are not an additional source of +truth and must not be edited or checked into this repository. -## URLs +## Ownership rule -Always start from the manifest — direct dataset URLs may change in the -future, but the manifest's `datasets..url` field is the contract. +New boards, aliases, bootloader identities, runtime identities, compile-time +identities, transports, and VID/PID corrections belong in +[FastLED/boards](https://github.com/FastLED/boards). fbuild changes should +consume the resulting schema rather than adding device constants. -- Manifest (entry point — clients fetch this first): - `https://raw.githubusercontent.com/fastled/fbuild/online-data/manifest.json` -- USB VID:PID dataset: - `https://raw.githubusercontent.com/fastled/fbuild/online-data/data/usb-vid.json` -- Compact USB VID:PID protobuf/zstd runtime overlay: - `https://raw.githubusercontent.com/fastled/fbuild/online-data/data/usb-vids.proto.zstd` -- USB-VID source-conflict log: - `https://raw.githubusercontent.com/fastled/fbuild/online-data/data/usb-vid-conflicts.json` -- PlatformIO full board catalog: - `https://raw.githubusercontent.com/fastled/fbuild/online-data/data/pio-boards.json` -- PlatformIO slim vendor-name lookup (small, ~200 KB): - `https://raw.githubusercontent.com/fastled/fbuild/online-data/data/vendor_boards.json` +Concrete identities may exist only in explicit tests and fixtures. The frozen +archives under `crates/fbuild-core/data/` are test inputs and are excluded from +release/runtime builds. `ci/check_usb_vidpid_literals.py` enforces this boundary +over the full tracked tree. -The matching constants in code: `fbuild_core::usb::MANIFEST_URL`, -`fbuild_core::usb::USB_VID_JSON_URL`, and -`fbuild_core::usb::USB_VIDS_PROTO_ZSTD_URL`. +## Retired publisher -## Branch shape - -``` -online-data (orphan, NEVER merged into main) -├── README.md -├── manifest.json -├── data/ -│ ├── usb-vid.json # alphabetically sorted, lowercase hex keys -│ ├── usb-vids.proto.zstd # compact runtime overlay consumed by fbuild -│ └── usb-vid-conflicts.json # only keys where sources disagreed -└── tools/ - ├── README.md - └── merge_sources.py # union + sort + manifest emit -``` - -There is **no `Cargo.toml`, no `src/`, no workspace member** on -`online-data` — the dump-side tooling for the bundled `usb-ids` crate -lives on `main` as an example (`crates/fbuild-core/examples/dump_usb_ids.rs`) -so we don't have to add a new crate. The nightly workflow checks out main -to build that example, then checks out `online-data` in a worktree to run -the merger script and commit results. - -## How a refresh happens - -`.github/workflows/update-data.yml` is the only workflow that touches -`online-data`. It lives on `main` because GitHub Actions requires `schedule` -and `workflow_dispatch` triggers to be defined on the default branch. - -Per run: - -1. Checkout `main` (workflow + dump example live here). -2. `git worktree add` the `online-data` branch into a sibling directory. -3. Install uv + soldr. -4. `soldr cargo build --release --example dump_usb_ids -p fbuild-core` - then run it → `/tmp/usb-ids-rs.json` (one input source). -5. `curl --retry 5` two upstream `usb.ids` text mirrors: - `http://www.linux-usb.org/usb.ids` and - `https://raw.githubusercontent.com/usbids/usbids/master/usb.ids` - (independently fault-tolerant — one mirror going down does not break - the run). -6. `uv run --no-project --script .online-data/tools/merge_sources.py …` - over whichever sources arrived intact. The merger: - - takes the union in workflow argument order; first-party/vendor-owned - sources are ordered before generic USB-ID feeds, local FastLED board rows - fill checked-in board gaps after those sources, and third-party SDK / - board-package supplements come last so they only fill remaining rows; - - sorts keys alphabetically (lowercase `vvvv:pppp`); - - writes `data/usb-vid.json`, `data/usb-vid-conflicts.json`, - `data/usb-vids.proto.zstd`, and the freshly-stamped `manifest.json`; - - **refuses to write** if the union has fewer than 1000 entries so a - truncated upstream cannot blow away a healthy committed dataset. -7. If files actually changed, commit on `online-data`. -8. Prune history: if `git rev-list --count HEAD > 200`, graft the - 200-th-most-recent commit as the new root and `git filter-repo`. -9. `git push --force-with-lease origin online-data` (the force is needed - only when history was pruned). - -Manual trigger: Actions → "Update data" → Run workflow. - -## Fault tolerance contract - -- **`usb-ids` build / dump fails** → workflow continues with text sources. -- **One upstream mirror unreachable** → merger still runs against the - remaining sources. -- **All upstream sources fail** → merger refuses to write → workflow - finishes with no commit; existing committed data is preserved. -- **Merger writes too-small output** → same as above (sanity floor). -- **Compact protobuf generation fails after a successful USB merge** → - workflow stops before publishing a mismatched `online-data` commit. -- **Workflow itself fails before commit** → previous commit on - `online-data` remains the live data. - -In every failure mode the *previously committed* data on `online-data` -stays as the live truth — fbuild keeps working against the last good -snapshot. - -## Why orphan + force-push? - -- Orphan: `online-data` shares no history with `main`. We never want - data churn rebasing into the source tree. -- Force-push: only after the history-prune step rewrites the chain to - cap at 200 commits. A non-pruning run produces a normal fast-forward. - -## Manifest schema (future-forward) - -```json -{ - "schema_version": "1.0", - "generated_at": "2026-06-20T04:17:00Z", - "datasets": { - "usb-vid": { - "description": "USB VID:PID → {vendor, product} ...", - "url": "https://raw.githubusercontent.com/fastled/fbuild/online-data/data/usb-vid.json", - "conflicts_url": "https://raw.githubusercontent.com/fastled/fbuild/online-data/data/usb-vid-conflicts.json", - "format": "json-object", - "key_format": "vvvv:pppp", - "entries": 20536, - "sources": [ - {"name": "usb-ids-rs", "kind": "json", "entries": "20480"}, - {"name": "linux-usb.org", "kind": "usb.ids-text", "entries": "20536"}, - {"name": "usbids-github", "kind": "usb.ids-text", "entries": "20536"} - ] - } - } -} -``` - -Adding a new dataset (`pci-vid`, `board-features`, …) means appending -another entry under `datasets` and shipping a parser in the consuming -crate — no schema break. +The former fbuild-owned `online-data` branch, refresh workflow, root `ids*.json` +files, and `online-data-tools/` pipeline are retired. They must not be restored +or used as runtime fallbacks. All future collection and publishing work belongs +to FastLED/boards. diff --git a/docs/usb-vidpid-audit.md b/docs/usb-vidpid-audit.md index 7fe46181..eda146ce 100644 --- a/docs/usb-vidpid-audit.md +++ b/docs/usb-vidpid-audit.md @@ -30,29 +30,19 @@ Rust modules, `tests/` trees, CI hardware fixtures, and the archives under `crates/fbuild-core/data/` that are included only in test builds. Production modules must not import or enable these fixtures. -## Remaining legacy publication surface +## Retired legacy publication surface -The following fbuild-owned data-publishing infrastructure is not used as a -production runtime fallback, but still duplicates USB identity collection and -publication that now belongs in FastLED/boards: - -- `.github/workflows/update-data.yml`; -- `online-data-tools/**`, including `seed_mcu_to_vid.json`; -- root `ids.json`, `ids2.json`, `ids3.json`, and `ids4.json`; -- the `dump_usb_ids` maintenance example and old online-data documentation. - -It must be removed after confirming that no release, cache, or documentation -consumer still points at the retired fbuild branches. FastLED/boards owns all -future source collection and publication. +The fbuild-owned refresh workflow, `online-data-tools/**`, root `ids*.json` +files, maintenance dump example, and catalogue dependency have been removed. +The old branch is not a runtime fallback. FastLED/boards owns all future source +collection and publication. ## Guard status -`ci/check_usb_vidpid_literals.py` currently prevents new same-line production -pairs and bundled-board `vid`/`pid` fields. Final #1047 work must replace this -preparatory diff check with a full-tree deny rule that understands Rust -`cfg(test)` boundaries, permits explicit test fixtures, and rejects production -catalogues, generated tables, board snapshots, and workflow-owned identity -sources. +`ci/check_usb_vidpid_literals.py` scans the full tracked tree. It understands +Rust `cfg(test)` boundaries, permits explicit test fixtures, and rejects +production literals, catalogues, generated tables, board snapshots, and +embedded identity assets. Non-USB hexadecimal values such as memory addresses, protocol magic numbers, Windows flags, and UUID fields are outside this policy. diff --git a/ids.json b/ids.json deleted file mode 100644 index 9e800a29..00000000 --- a/ids.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "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/ids2.json b/ids2.json deleted file mode 100644 index 818caa5f..00000000 --- a/ids2.json +++ /dev/null @@ -1,789 +0,0 @@ -{ - "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 deleted file mode 100644 index 1b9b0c8a..00000000 --- a/ids3.json +++ /dev/null @@ -1,761 +0,0 @@ -{ - "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 deleted file mode 100644 index d25b63d2..00000000 --- a/ids4.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "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 deleted file mode 100644 index a78b9318..00000000 --- a/online-data-tools/README.md +++ /dev/null @@ -1,226 +0,0 @@ -# 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` | -| `build_usb_vid_proto.py` | `online-data/data/usb-vid.json` | `online-data/data/usb-vids.proto.zstd` | -| `rotate_www_dbs.py` | `www/*.db` | `www/` (deletes >2-day-old `.db`s) | -| `build_www_manifest.py` | day-stable filenames | `www/manifest.json` | -| `fetch_espressif_usb_pids.py` | `espressif/usb-pids` official PID registry | merge-compatible `/tmp/espressif-usb-pids.json` | -| `fetch_raspberrypi_usb_pids.py` | `raspberrypi/usb-pid` official PID registry | merge-compatible `/tmp/raspberrypi-usb-pids.json` | -| `fetch_nordic_usb_pids.py` | Nordic nRF Connect Programmer / DFU sources | merge-compatible `/tmp/nordic-usb-pids.json` | -| `fetch_microchip_usb_pids.py` | Microchip pyedbglib/pykitinfo plus weak AVRDUDE/board-package supplements | merge-compatible `/tmp/microchip-*-usb-pids.json` | -| `fetch_arduino_usb_pids.py` | Official ArduinoCore `boards.txt` files | merge-compatible `/tmp/arduino-usb-pids.json` | -| `fetch_adafruit_usb_pids.py` | Adafruit Arduino cores + TinyUF2 + CircuitPython descriptors | merge-compatible `/tmp/adafruit-usb-pids.json` | -| `fetch_sparkfun_usb_pids.py` | SparkFun board packages plus weak PlatformIO/CircuitPython supplements | merge-compatible `/tmp/sparkfun-*-usb-pids.json` | -| `fetch_seeed_usb_pids.py` | Seeed package archives/platform JSON plus weak third-party board packages | merge-compatible `/tmp/seeed-*-usb-pids.json` | -| `fetch_ftdi_usb_pids.py` | Linux `ftdi_sio_ids.h` original-FTDI PID section | merge-compatible `/tmp/ftdi-usb-pids.json` | -| `fetch_wch_usb_pids.py` | WCH CH343 Linux driver + udev rules | merge-compatible `/tmp/wch-usb-pids.json` | -| `fetch_teensy_usb_pids.py` | PJRC Teensy core headers + loader CLI | merge-compatible `/tmp/teensy-usb-pids.json` | -| `fetch_stm_usb_pids.py` | ST/OpenOCD ST-LINK sources | merge-compatible `/tmp/stm-usb-pids.json` | -| `fetch_nxp_usb_pids.py` | NXP mfgtools/UUU config table | merge-compatible `/tmp/nxp-usb-pids.json` | -| `fetch_silabs_usb_pids.py` | Linux CP210x driver + SiliconLabsSoftware OpenOCD udev rule | merge-compatible `/tmp/silabs-usb-pids.json` | -| `fetch_renesas_usb_pids.py` | ArduinoCore-renesas `boards.txt` weak supplement | merge-compatible `/tmp/renesas-usb-pids.json` | -| `extract_fastled_board_usb_pids.py` | Local `crates/fbuild-config/assets/boards/json` board VID/PID metadata | merge-compatible `/tmp/fastled-board-usb-pids.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). - -## USB VID:PID Supplements - -Source authority is intentional. First-party vendor registries are strongest, -generic USB-ID feeds provide broad baseline names, and local FastLED board data -is a repo-scope supplement that fills product-name gaps for boards under -`crates/fbuild-config/assets/boards/json`. Third-party SDK or board-package -rows are weaker supplements that merge after those sources and only fill gaps. -A USB VID/PID row improves product-name resolution, but if a board is not -present under `crates/fbuild-config/assets/boards`, it may not be an -fbuild-supported board. - -The Espressif supplement ingests the official `espressif/usb-pids` registry: - -- `allocated-pids.txt` for customer/product allocations under VID `0x303a` -- `allocated-pids-espressif-devboards.txt` for Espressif-built devboard PIDs - -It emits the flat JSON shape consumed by `online-data/tools/merge_sources.py` -so product-level names such as `303a:8001` (`Unexpected Maker TinyS2 - Arduino`) -land in `usb-vid.json` and the www SQLite `vidpid` table. Board existence is -still governed by the board catalogs (`pio-boards` / FastLED board data); a PID -registry entry does not by itself prove that fbuild supports a board. - -The Raspberry Pi supplement ingests the official `raspberrypi/usb-pid` -allocation table for VID `0x2e8a`. It emits product names from the upstream -`Product Description` column while keeping the VID owner as -`Raspberry Pi Foundation`; the per-row `Company` cell is allocation context, -not the USB vendor name. Blank placeholders, ranges, and reserved rows are -skipped. - -The Nordic supplement ingests Nordic-maintained nRF Connect Programmer -USB-product arrays and the `pc-nrf-dfu-js` README. It emits current Nordic -DFU / MCUboot rows for VID `0x1915`, including the specific -`1915:521f` PCA10059 nRF52840 dongle SDFU bootloader label, while keeping -application-dependent IDs such as `cafe` generic rather than assigning them to -a single firmware role. - -The Microchip supplement has explicit source tiers. `--tier first-party` -parses Microchip-maintained `pyedbglib` and `pykitinfo` rows for Atmel VID -`0x03eb` CMSIS-DAP tools and Microchip VID `0x04d8` MPLAB tools; the workflow -orders this before generic USB-ID sources so first-party tool names win. -`--tier supplemental` parses AVRDUDE plus selected Arduino/LowPowerLab board -package rows only after first-party and generic sources. The weak tier fills -gaps such as LowPowerLab SAMD VID/PIDs, but it must not override Microchip -first-party data. LowPowerLab's current package maps CurrentRanger to -`04d8:ee44`/`ee48`/`ee4c` and Moteino M0 to `04d8:eee4`/`eee5`/`eee8`; -local FastLED board JSON currently carries `current_ranger` as `04d8:eee5`, -so that local mismatch is noted rather than used as source truth. - -The Arduino supplement parses official ArduinoCore `boards.txt` files from -Arduino-maintained AVR, SAM, SAMD, megaAVR, mbed, Renesas, and Zephyr cores. -Arduino does not publish a single public PID allocation registry; the core -board definitions are the authoritative public table for Arduino VID `0x2341` -and historical Arduino VID `0x2a03`. The workflow orders this source before -generic USB-ID feeds so current Arduino product names win. As with other -board-package data, a row from an ArduinoCore source improves VID/PID -resolution but does not prove the board exists under -`crates/fbuild-config/assets/boards`. - -The Adafruit supplement parses Adafruit-maintained Arduino core `boards.txt` -files for SAMD, nRF52, AVR/32u4, and WICED rows, then fills newer gaps from -Adafruit TinyUF2 bootloader `board.h` descriptors and CircuitPython -`mpconfigboard.mk` descriptors for Adafruit-prefixed boards. These sources -cover VID `0x239a` products across SAMD, nRF52, ESP32-S2/S3, and RP2040 -families. The workflow orders the supplement before generic USB-ID feeds so -Adafruit first-party product names win, but entries still describe USB -products rather than fbuild board support; support is governed by -`crates/fbuild-config/assets/boards`. - -The FastLED board supplement extracts `build.vid` / `build.pid` from the -checked-in board JSON files under `crates/fbuild-config/assets/boards/json`. -It is intentionally ordered after vendor-owned and generic USB-ID sources: -local board names fill gaps for boards fbuild actually carries, but board JSON -`vendor` fields are not always the USB VID owner and should not replace -stronger USB product tables. Boards outside this repo are not inferred here. - -The SparkFun supplement has explicit source tiers for VID `0x1b4f`. -`--tier first-party` parses SparkFun-maintained Arduino board package files, -product-repo board files, and SparkFun product descriptors such as UF2 -`board_config.h` and CircuitPython `mpconfigboard.mk` files. The workflow -orders this first-party tier before generic USB-ID feeds so SparkFun-owned -product names win. `--tier supplemental` parses third-party PlatformIO board -JSON `build.hwids` and Adafruit CircuitPython descriptors for SparkFun-named -boards only after first-party and generic USB-ID sources. Those weak rows fill -gaps such as newer SparkFun ESP32/RP/Teensy MicroMod products but must not -override first-party rows. SparkFun's Apollo3 package currently has no -`vid.N`/`pid.N` rows, so Artemis/Apollo3 board discovery remains a weak CH340 -bridge hint rather than a SparkFun PID table. A SparkFun VID/PID row is USB -product metadata, not proof of fbuild board support; support still depends on -the board existing under `crates/fbuild-config/assets/boards`. - -The Seeed supplement has explicit source tiers for Seeed VID `0x2886`. -`--tier first-party` reads the current Seeed Boards Manager package index, -downloads the newest Seeed-hosted archives for SAMD, nRF52, mbed nRF52, -Renesas RA, and i.MX RT packages, then parses each archive's `boards.txt`. -It also parses Seeed's own `platform-seeedboards` JSON files as a first-party -gap filler without replacing package-archive names. `--tier supplemental` -parses third-party Espressif, Silicon Labs, Arduino-Pico, PlatformIO, -CircuitPython, and TinyUF2 rows only after first-party and generic USB-ID -sources. The Seeed platform C6 row that reuses `2886:0046` is skipped because -that PID is already used by XIAO ESP32C3; the weak PlatformIO C6 rows -`2886:0048`/`8048` are allowed to fill the gap. XIAO RP2040 remains under -Raspberry Pi VID `0x2e8a` from first-party Seeed platform data, so the -CircuitPython `2886:0042` row is not used to remap it. Rows for boards absent -from `crates/fbuild-config/assets/boards` improve USB name resolution only; -they do not prove fbuild board support. - -The FTDI supplement parses the upstream Linux `ftdi_sio_ids.h` header but only -the original FTDI PID section before the third-party marker. It emits a small -allowlist of missing FTDI-owned bridge/product rows such as `0403:6040` and -`0403:fbfa`; the workflow orders this source after generic USB-ID sources so -existing mature product names win for common bridge PIDs. - -The WCH supplement parses OpenWCH's CH343 Linux driver and its udev rules for -newer VID `0x1a86` USB serial chips. Udev comments provide names for CH342, -CH343, CH344, CH347T, and CH9101-CH9104 rows; newer driver-only IDs such as -CH339, CH346, CH9105, CH911x, and CH9433 are named from the driver's -PID-to-chip switch cases. It does not infer board names from bridge-chip IDs. - -The Teensy supplement parses PJRC's `teensy3/usb_desc.h` and -`teensy4/usb_desc.h` product descriptors plus `teensy_loader_cli` rebootor and -HalfKay bootloader references. Teensy PIDs identify USB personalities, not a -single board model; duplicate PIDs that differ by USB BCD version are collapsed -to conservative family labels such as `Teensyduino MIDI + Serial`. - -The STM supplement parses ST's OpenOCD ST-LINK driver and `stlink.cfg` PID -list, then adds common ST DFU and virtual COM rows. These entries identify -debugger or USB function products (`STLINK-V3P`, `STM Device in DFU Mode`, -`Virtual COM Port`); they do not imply a specific Nucleo or Discovery board. - -The NXP supplement parses NXP's mfgtools/UUU `config.cpp` table for VID -`0x1fc9` ROM downloader and fastboot protocol rows. Entries such as -`1fc9:0135` are labeled as NXP i.MX/i.MX RT serial downloader modes rather -than as a specific downstream board. - -The Silicon Labs supplement parses Linux's CP210x driver for Silicon -Labs-owned bridge defaults under VID `0x10c4`, then parses a first-party -SiliconLabsSoftware Arduino example udev rule for Energy Micro VID -`0x2544`, PID `0001`. Silicon Labs does not publish a public PID registry, -and its current Arduino core only declares board-package rows under Arduino -and Seeed VIDs (`2341:0072`, `2886:0062`, `2886:8062`), so this supplement -does not infer Silicon Labs board names from bridge-chip or debug-interface -IDs. - -Ambiq/Apollo3 currently has no wired PID supplement. The public USB-ID -sources already identify VID `0x2aec` as Ambiq Micro with PID `6011` -(`Converter`), while VID `0x1cbe` belongs to Luminary Micro/TI rather than -Ambiq. SparkFun's `Arduino_Apollo3` board package has no `vid.N`/`pid.N` -rows to ingest, and the Apollo3 boards present under -`crates/fbuild-config/assets/boards` are SparkFun Artemis boards uploaded via -serial loader rather than a documented Ambiq PID table. The `AM_APOLLO3` -MCU-to-VID seed therefore uses the weak CH340 bridge VID `0x1a86` only as a -board-search hint for those SparkFun boards; it does not add Ambiq product -PIDs without a first-party source. Third-party SDK or board-package rows may -be added later as supplemental data, but they should merge after first-party -and generic USB-ID sources so they fill gaps only. - -The Renesas RA supplement parses Arduino's `ArduinoCore-renesas` `boards.txt` -for Arduino-owned VID rows used by UNO R4, Nano R4, Portenta C33, and related -RA-family board-package entries. Renesas-owned VID `0x045b` remains sourced -from the generic USB-ID feeds unless a first-party Renesas PID registry is -found. Because the parsed source is an Arduino board package rather than a -Renesas allocation table, the workflow merges it after generic USB-ID sources -and vendor-owned supplements. Rows from ArduinoCore-renesas may describe -boards that are not present under `crates/fbuild-config/assets/boards`; those -rows improve VID/PID resolution but do not prove fbuild board support. - -## Tests - -```bash -uv run --no-project --with pytest pytest online-data-tools/test_build_sqlite.py -v -uv run --no-project --with pytest --with zstandard pytest online-data-tools/test_usb_vid_proto.py -v -uv run --no-project --with pytest pytest online-data-tools/test_espressif_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_raspberrypi_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_nordic_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_microchip_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_arduino_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_adafruit_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_sparkfun_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_seeed_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_ftdi_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_wch_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_teensy_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_stm_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_nxp_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_silabs_usb_pids.py -v -uv run --no-project --with pytest pytest online-data-tools/test_renesas_usb_pids.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 deleted file mode 100644 index 4a52b435..00000000 --- a/online-data-tools/www_static/style.css +++ /dev/null @@ -1,72 +0,0 @@ -: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; } diff --git a/tasks/todo.md b/tasks/todo.md deleted file mode 100644 index 62304df2..00000000 --- a/tasks/todo.md +++ /dev/null @@ -1,227 +0,0 @@ -# TODO — USB VID:PID resolver + `online-data` branch (FastLED/fbuild) - -## Goal - -Two-part design so fbuild can always translate a USB `VID:PID` to a -human-readable `vendor / product` name: - -1. **First line of defense** — bundled `usb-ids` Rust crate (offline, MIT, - `phf` perfect-hash table, zero allocations, zero network). -2. **Backup / live updates** — orphan `online-data` branch in this same repo, - refreshed once per day by a GitHub Actions workflow. Branch carries a - `manifest.json` (future-forward index) pointing at `data/usb-vid.json` - (sorted alphabetical union of multiple upstream sources). fbuild reads the - JSON when the bundled crate cannot resolve a VID:PID. Conflicts between - sources go into `data/usb-vid-conflicts.json` for observability. - -The acceptance bar (verbatim from the user): -- PR is merged. -- The nightly workflow is run manually via `workflow_dispatch`. -- The manifest URL `https://raw.githubusercontent.com/fastled/fbuild/online-data/manifest.json` actually resolves. -- A fbuild unit test demonstrates the `resolve(vid, pid)` API works end-to-end. -- When fbuild **connects to** or **scans** a device, the printed output includes - the pretty `vendor product (VVVV:PPPP)` string — not just raw hex. - -## Sources we will merge - -| Source | URL | Format | Notes | -|---|---|---|---| -| `usb-ids` crate (built via soldr) | bundled at compile time | Rust → JSON dump | Tracks upstream snapshots; primary truth | -| linux-usb.org (canonical) | `http://www.linux-usb.org/usb.ids` | plain text | HTTPS is broken (SAN mismatch) — `http://` is intentional | -| usbids/usbids GitHub mirror | `https://raw.githubusercontent.com/usbids/usbids/master/usb.ids` | plain text | CDN-backed, tracks upstream same-day | - -Priority order on conflict: `usb-ids-rs` > `linux-usb.org` > `usbids/usbids`. -All conflicts are still recorded in `usb-vid-conflicts.json`. - -## File layout (on `main` — minimal: resolver + workflow trigger only) - -``` -crates/fbuild-core/ - Cargo.toml # +usb-ids = "1.2025" - src/lib.rs # +pub mod usb; - src/usb/ - mod.rs # re-exports - resolver.rs # resolve(vid, pid), tiers - data.rs # online JSON load + OnceLock cache - tests.rs # FTDI / CP210x / CH340 / unknown - -crates/fbuild-daemon/ - src/device_manager.rs # enrich description via resolver - src/models.rs # +vendor_name/product_name on DeviceInfo, etc. - src/handlers/devices.rs # populate the new fields - -crates/fbuild-cli/ - src/cli/device.rs # display "vendor product (VVVV:PPPP)" in list/status - -.github/workflows/ - nightly-usb-ids.yml # cron + workflow_dispatch — checks out - # online-data into a worktree, builds the - # tools FROM THERE, commits data back. - # No tooling lives on main. - -docs/ - online-data.md # documents the branch + workflow + schema -``` - -## File layout (on `online-data` orphan branch — tools + data) - -``` -README.md # explains: orphan, do-not-merge, structure -manifest.json # future-forward index of datasets -data/usb-vid.json # {} initially, populated by workflow -data/usb-vid-conflicts.json # {} initially -.gitignore # bury *.bak / *.tmp - -tools/usb-ids-dump/ # standalone, NOT a workspace crate (and - Cargo.toml # not on main at all → no impact on the - src/main.rs # main-branch crate-gate / monocrate policy) - README.md -tools/merge_sources.py # union + sort + manifest emit -tools/README.md # how the nightly workflow uses these -``` - -> The workflow YAML still lives on `main` (GitHub requires `schedule` and -> `workflow_dispatch` triggers to be defined on the default branch). The -> workflow itself does nothing except: `git worktree add` the `online-data` -> branch, run the tools from there, and commit data files back. - -## API shape — `fbuild_core::usb` - -```rust -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct UsbInfo { - pub vendor: String, - pub product: String, -} - -/// Best-effort resolve; never returns None. If unknown, returns -/// `UsbInfo { vendor: "Unknown vendor 0xVVVV", product: "Unknown product 0xPPPP" }`. -pub fn resolve(vid: u16, pid: u16) -> UsbInfo; - -/// Only returns Some if either the bundled crate or the online cache resolved. -pub fn try_resolve(vid: u16, pid: u16) -> Option; - -/// Tier-1 only (bundled `usb-ids`). -pub fn resolve_bundled(vid: u16, pid: u16) -> Option; - -/// Install an override map. Called by the daemon at startup with the -/// path to `~/.fbuild/cache/usb-vid.json` (or wherever the CLI cached -/// the last download). -pub fn install_online_cache(path: &std::path::Path); - -/// Convenience pretty formatter: -/// "vendor product (VVVV:PPPP)" -/// Used by the CLI's device list / connect / scan log lines. -pub fn pretty(vid: u16, pid: u16) -> String; -``` - -Internals: -- Static `OnceLock>` for the online overlay. -- Key packing: `(vid as u32) << 16 | pid as u32`. -- `install_online_cache` reads file once, parses serde_json into the map. Silent on IO error (the resolver still works via tier 1 + fallback formatter). - -## Daemon / CLI wiring - -- `device_manager::refresh_devices` — when building each `DiscoveredDevice`, if `vid` and `pid` are present, call `fbuild_core::usb::resolve(vid, pid)` and stash both `vendor_name` + `product_name` on the device record. The free-form `description` becomes `"{vendor} {product}"` (overriding the bland `usb.product` string from `serialport`). -- `DeviceState`, `DeviceInfo`, `DeviceStatusResponse` — gain `Option vendor_name` and `Option product_name`. -- `fbuild-cli` — `device list` and `device status` print the new fields; deploy / monitor log lines that mention a port now use `fbuild_core::usb::pretty(vid, pid)` so the user always sees the friendly name. - -## Workflow design — `.github/workflows/nightly-usb-ids.yml` - -- Trigger: `schedule: cron '17 4 * * *'` (daily) + `workflow_dispatch`. -- Runner: ubuntu-latest. -- Permissions: `contents: write` (must push to `online-data`). -- Concurrency: `cancel-in-progress: false`, group `nightly-usb-ids` (don't trample a running update). - -Steps (high-level): -1. `actions/checkout@v6` (default — `main`, for the dump binary + script). -2. `setup-uv` + `setup-soldr` (same versions as `check-ubuntu.yml`). -3. `soldr cargo build --release --manifest-path ci/usb_ids/Cargo.toml` → produces `ci/usb_ids/target/release/dump-usb-ids`. **If this step fails, the workflow continues with the existing data — fault tolerance #1.** We do this by `continue-on-error: true` + a step output flag. -4. Run the dump: redirect stdout to `/tmp/source-usb-ids-rs.json`. Sanity-check entry count (>= 5000) — else mark as failed-but-non-fatal. -5. Download external sources with `curl --retry 5 --retry-delay 10 --fail` into `/tmp/`. Each download is independently fault-tolerant: a failure flags the source as missing for this run but does NOT abort the workflow. -6. `uv run python ci/usb_ids/merge_sources.py --rs … --txt … --out-dir /tmp/merged`. The script: - - reads only the sources that arrived intact this run, - - falls back to the previously-committed `data/usb-vid.json` from `online-data` for any missing tier, - - refuses to emit a `usb-vid.json` with fewer than 1000 entries (sanity floor), - - sorts keys alphabetically (`json.dumps(..., sort_keys=True, indent=2)` with stable encoding), - - writes `usb-vid.json`, `usb-vid-conflicts.json`, `manifest.json`. -7. Fetch + worktree `online-data` (we keep the branch in a separate `git worktree`, so we never touch the workflow checkout). -8. Replace the data files in the worktree only with files the merger actually emitted. If the merger emitted nothing for a given file, leave the existing committed copy in place — fault tolerance #2. -9. `git add manifest.json data/`. If `git diff --cached --quiet`, skip the commit (no churn commits). -10. Otherwise commit with a message like `chore(usb-ids): nightly refresh 2026-06-20 (sources: rs, linux-usb, github)` listing which sources contributed. -11. Prune history to last 200 commits: count via `git rev-list --count HEAD`; if > 200, find the boundary commit, `git replace --graft` it as a new root, run `git filter-repo` (or `filter-branch` if filter-repo isn't on the runner) to rewrite, clean the replace refs. -12. `git push --force-with-lease origin online-data` (force is needed only when history was pruned; otherwise a plain push). - -No build artifacts saved. The merger writes to `/tmp/`; the workflow only commits `manifest.json` + `data/*.json` + the existing `README.md` in `online-data`. - -## `manifest.json` schema (future-forward) - -```json -{ - "schema_version": "1.0", - "generated_at": "2026-06-20T04:17:00Z", - "datasets": { - "usb-vid": { - "description": "USB VID:PID → {vendor, product} (union of multiple sources)", - "url": "https://raw.githubusercontent.com/fastled/fbuild/online-data/data/usb-vid.json", - "conflicts_url": "https://raw.githubusercontent.com/fastled/fbuild/online-data/data/usb-vid-conflicts.json", - "format": "json-object", - "key_format": "vvvv:pppp (lowercase hex, colon-separated)", - "entries": 24536, - "sources": [ - {"name": "usb-ids-rs", "version": "1.2025.2"}, - {"name": "linux-usb.org", "fetched_at": "2026-06-20T04:17:11Z"}, - {"name": "usbids/usbids", "fetched_at": "2026-06-20T04:17:13Z"} - ] - } - } -} -``` - -Adding a future dataset (say `pci-vid`) means appending another entry under -`datasets` — no clients break. - -## `usb-vid.json` schema - -```json -{ - "0403:6001": {"vendor": "Future Technology Devices International, Ltd", "product": "FT232 Serial (UART) IC"}, - "10c4:ea60": {"vendor": "Silicon Labs", "product": "CP210x UART Bridge"}, - ... -} -``` - -Alphabetical sort by key (`json.dumps(sort_keys=True)`); 2-space indent; trailing newline. - -## `usb-vid-conflicts.json` schema - -```json -{ - "0403:6001": [ - {"source": "usb-ids-rs", "vendor": "...", "product": "..."}, - {"source": "linux-usb.org","vendor": "...", "product": "..."} - ] -} -``` - -Only entries that actually had disagreement appear here; the chosen winner is -the one in `usb-vid.json` (priority order above). - -## Acceptance plan (executable) - -1. `soldr cargo test -p fbuild-core usb::` — unit tests for `resolve()` pass for FTDI / CP210x / CH340 / Espressif / unknown. -2. `soldr cargo test -p fbuild-daemon` — DeviceManager tests still pass; new test confirms enriched description. -3. `soldr cargo clippy --workspace --all-targets -- -D warnings` clean. -4. PR open on a feature branch; `crate-gate.yml` passes (we did not add a workspace crate). -5. Push `online-data` orphan branch with seed contents — verify `https://raw.githubusercontent.com/fastled/fbuild/online-data/manifest.json` returns the seed manifest. -6. Merge PR. -7. From the Actions tab, manually run `nightly-usb-ids` via `workflow_dispatch`. -8. After the run succeeds, refetch `manifest.json` and confirm: - - `entries >= 20000` - - `sources` lists `usb-ids-rs` + the two text sources - - the `url` field actually serves a JSON object with `>= 20000` keys -9. Goal hook should auto-clear once all of the above are demonstrated. - -## Review (filled in at the end) - -(left blank for now — to be appended once everything is merged and verified)