diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 012a0d0..1256810 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,3 +44,15 @@ jobs: - uses: actions/checkout@v4 - name: Check manual class sync run: python3 tools/check-manual-sync.py --src components --manual docs/manual + + si4684-blobs: + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: Software + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Check Si4684 blob policy + run: python3 tools/check_si4684_blobs.py diff --git a/README.md b/README.md index c5be1ba..3a05332 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Vertical slices landed on `main` (newest first): | **0.5–0.6** | ADAU1701 runtime EQ/mixer, Si4684 tuning & DAB service list, BT1035 driver | | **0.3–0.4** | Secure store, Wi-Fi provisioning, companion-chip boot, walking skeleton | -**Next up** ([`Software/docs/TODO.md`](Software/docs/TODO.md)): Si4684 blob procurement docs (T7), NVS/flash encryption (T8). +**Next up** ([`Software/docs/TODO.md`](Software/docs/TODO.md)): NVS/flash encryption (T8). --- diff --git a/Software/Firmware/README.md b/Software/Firmware/README.md index 74f6dab..aeee61a 100644 --- a/Software/Firmware/README.md +++ b/Software/Firmware/README.md @@ -9,10 +9,16 @@ Binary and SigmaStudio exports loaded by the ESP32 at every boot. ## Si4684 blobs -`rom_patch_016.bin` and `dab_firmware.bin` are extracted from the -[PE5PVB SI4684-DAB-Receiver](https://github.com/PE5PVB/SI4684-DAB-Receiver) -project. `fm_firmware.bin` comes from the Skyworks Si4684 eval firmware pack -(see `tools/fetch_si4684_firmware.py --si46xx-dir`). +Proprietary Skyworks images — **local only**, gitignored (`*.bin`). See +[`Si4684-Firmware/README.md`](Si4684-Firmware/README.md) for procurement and +legal notes. Populate with: + +```bash +cd Software +python3 tools/fetch_si4684_firmware.py --dab-only +python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware +python3 tools/check_si4684_blobs.py +``` ## ADAU1701 export diff --git a/Software/Firmware/Si4684-Firmware/README.md b/Software/Firmware/Si4684-Firmware/README.md index ac983a1..6c04158 100644 --- a/Software/Firmware/Si4684-Firmware/README.md +++ b/Software/Firmware/Si4684-Firmware/README.md @@ -1,43 +1,89 @@ -# Si4684 firmware images +# Si4684 firmware images (local only — not in git) + +Skyworks Si4684 application firmware is **proprietary**. This directory holds +the three binary blobs your build embeds via `HOST_LOAD`; they must **never** +be committed to the public repository. | File | Size (typ.) | Role | |------|-------------|------| -| `rom_patch_016.bin` | 5796 | ROM patch / bootloader helper (HOST_LOAD before main image) | +| `rom_patch_016.bin` | 5796 B | ROM patch / bootloader helper (HOST_LOAD before main image) | | `dab_firmware.bin` | ~517 KB | DAB+ application image (PE5PVB / Skyworks BIF) | | `fm_firmware.bin` | ~530 KB | FM application image (Skyworks `fm_radio_5_1_0.bin`, AN649 A10) | -Boot sequence: AN649 — POWER_UP → LOAD_INIT → HOST_LOAD(patch) → LOAD_INIT → -HOST_LOAD(image) → BOOT. DAB and FM use the same patch; only the application +Boot sequence (AN649): POWER_UP → LOAD_INIT → HOST_LOAD(patch) → LOAD_INIT → +HOST_LOAD(image) → BOOT. DAB and FM share the same patch; only the application image differs. -## Refresh blobs +## Legal / redistribution + +- **Do not** commit `*.bin` files here, attach them to GitHub releases, or + redistribute them with this open-source tree. +- DAB images extracted from [PE5PVB/SI4684-DAB-Receiver](https://github.com/PE5PVB/SI4684-DAB-Receiver) are community-sourced; FM images come from the + Skyworks evaluation package or equivalent local sources (see below). +- Keep blobs on your machine only. CI verifies they are gitignored and absent + from history (`python3 tools/check_si4684_blobs.py`). + +## Before you build + +From `Software/`: ```bash -# DAB + patch from PE5PVB GitHub (always safe to re-run): -python3 tools/fetch_si4684_firmware.py --dab-only - -# FM from Skyworks eval / dabpi si46xx_firmware folder: -python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware - -# FM extracted from uGreen DABBoard radio_cli (Files_v16.zip from ugreen.eu/downloads): -python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip - -# Patch + DAB + FM entirely from uGreen radio_cli (overrides PE5PVB DAB): -python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip --ugreen-all - -# FM extracted from a full SPI flash dump (TechniSat layout, dirb.me tech wiki): -python3 tools/fetch_si4684_firmware.py --flash-dump technisat_spi.bin +python3 tools/fetch_si4684_firmware.py --dab-only # patch + DAB +python3 tools/fetch_si4684_firmware.py --si46xx-dir /path # add FM (see below) +ls -la Firmware/Si4684-Firmware/*.bin # expect 3 files ``` -FM images are **not** redistributed with PE5PVB (DAB-only project). Obtain -`fm_radio_5_1_0.bin` from the Si4684 evaluation package (AN649 table 1), -community `si46xx_firmware/` folders used by [teknoid/dabpi](https://github.com/teknoid/dabpi), -or extract from a uGreen [DABBoard](https://ugreen.eu/downloads/) `radio_cli` -binary (proprietary — keep local, do not commit to public git). +`idf.py build` fails at link/embed time if blobs are missing. -Low-level conversion helper: +## Obtain images locally + +### Patch + DAB (automated, network) ```bash -python3 tools/extract_si4684_blob.py --copy /path/to/fm_radio_5_1_0.bin \\ +cd Software +python3 tools/fetch_si4684_firmware.py --dab-only +``` + +Clones PE5PVB/SI4684-DAB-Receiver and writes `rom_patch_016.bin` and +`dab_firmware.bin`. + +### FM image (manual source — pick one) + +FM is **not** shipped with PE5PVB. Supply one of: + +| Source | Command | +|--------|---------| +| Skyworks eval / dabpi `si46xx_firmware/` | `python3 tools/fetch_si4684_firmware.py --si46xx-dir /path/to/si46xx_firmware` | +| uGreen DABBoard `Files_v16.zip` | `python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip` | +| Full SPI flash dump (TechniSat layout) | `python3 tools/fetch_si4684_firmware.py --flash-dump technisat_spi.bin` | + +Candidate FM filenames (AN649 / community): `fm_radio_5_1_0.bin`, +`fm_radio_5_0_9.bin`, `fmhd_radio_5_1_0.bin`, `fmhd_radio_5_0_4.bin`. + +Manual copy helper: + +```bash +python3 tools/extract_si4684_blob.py --copy /path/to/fm_radio_5_1_0.bin \ --out Firmware/Si4684-Firmware/fm_firmware.bin ``` + +### All three from uGreen radio_cli + +```bash +python3 tools/fetch_si4684_firmware.py --from-ugreen-radio-cli ~/Downloads/Files_v16.zip --ugreen-all +``` + +## Verify your checkout + +```bash +cd Software +python3 tools/check_si4684_blobs.py # must exit 0 +git ls-files 'Firmware/Si4684-Firmware/*.bin' # must print nothing +``` + +## References + +- Skyworks **AN649** — Si4684 programming API and boot flow +- `tools/fetch_si4684_firmware.py` — fetch/extract orchestration +- `tools/extract_si4684_blob.py` — header/array → raw `.bin` +- `docs/manual/ch-si4684.tex` — driver boot integration in DigiRadio diff --git a/Software/README.md b/Software/README.md index fa147ad..e0da7f3 100644 --- a/Software/README.md +++ b/Software/README.md @@ -31,6 +31,7 @@ Documentation gates (must exit 0 before merging; also enforced in CI): ```bash doxygen Doxyfile python3 tools/check-manual-sync.py +python3 tools/check_si4684_blobs.py python3 tools/gzip-www.sh # after editing components/net/www/index.html ``` diff --git a/Software/docs/TODO.md b/Software/docs/TODO.md index 1c1ef44..1aa1ec0 100644 --- a/Software/docs/TODO.md +++ b/Software/docs/TODO.md @@ -69,18 +69,10 @@ embedded gzip blob. No debug routes in `SetupWebServer`. ## P3 — Procurement & hardening -### T7. Si4684 firmware blob strategy (legal) -**Why:** the Si4684 images are proprietary (Skyworks). Tools to fetch/ -extract exist under `tools/`, but the `.bin` images must **not** be -committed to the public repo. -**What:** -- Confirm `*.bin` (patch, FM, DAB images) are in `.gitignore` and absent - from git history. -- Document in `Software/Firmware/Si4684-Firmware/README.md` how a builder - obtains the images locally (tools + AN649 reference), without - redistributing proprietary binaries. -**Done when:** no proprietary blob is tracked; the procurement path is -documented and reproducible. +### T7. Si4684 firmware blob strategy (legal) — **DONE (fw 0.8.2)** +`Firmware/Si4684-Firmware/*.bin` gitignored; no blobs in git history. +Procurement documented in `Si4684-Firmware/README.md`; CI job +`si4684-blobs` runs `tools/check_si4684_blobs.py`. ### T8. Flash/NVS encryption enablement **Why:** secure storage holds Wi-Fi and user credentials; encryption at diff --git a/Software/tools/check_si4684_blobs.py b/Software/tools/check_si4684_blobs.py new file mode 100644 index 0000000..a65693b --- /dev/null +++ b/Software/tools/check_si4684_blobs.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""check_si4684_blobs.py — CI gate: no proprietary Si4684 blobs in git. + +DigiRadio firmware — https://github.com/manvalan/DigiRadio + +Copyright 2026 Michele Bigi +SPDX-License-Identifier: Apache-2.0 + +Skyworks / uGreen Si4684 application images must live only on the builder's +machine under Firmware/Si4684-Firmware/*.bin (gitignored). This script fails +if any such blob is tracked, appears in git history, or is missing from +.gitignore. +""" + +from __future__ import annotations + +import argparse +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +FW_DIR = ROOT / "Firmware" / "Si4684-Firmware" +GITIGNORE = ROOT / ".gitignore" +IGNORE_LINE = "Firmware/Si4684-Firmware/*.bin" + + +def git(*args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=ROOT.parent, + check=True, + capture_output=True, + text=True, + ) + return result.stdout + + +def check_gitignore() -> list[str]: + errors: list[str] = [] + if not GITIGNORE.is_file(): + errors.append(f"missing {GITIGNORE.relative_to(ROOT)}") + return errors + text = GITIGNORE.read_text(encoding="utf-8") + if IGNORE_LINE not in text: + errors.append( + f".gitignore must contain {IGNORE_LINE!r} " + "(proprietary Si4684 blobs)" + ) + return errors + + +def check_tracked_bins() -> list[str]: + out = git("ls-files", "--", "Software/Firmware/Si4684-Firmware/*.bin") + tracked = [line for line in out.splitlines() if line.strip()] + if not tracked: + return [] + return [ + "tracked Si4684 blob(s) in git index (remove and keep local only): " + + ", ".join(tracked) + ] + + +def check_history() -> list[str]: + out = git( + "log", + "--all", + "--oneline", + "--", + "Software/Firmware/Si4684-Firmware/*.bin", + ) + lines = [line for line in out.splitlines() if line.strip()] + if not lines: + return [] + return [ + "Si4684 .bin file(s) found in git history — " + "history rewrite required before publishing: " + + "; ".join(lines[:5]) + + (" …" if len(lines) > 5 else "") + ] + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Ensure Si4684 firmware blobs are not in git." + ) + parser.parse_args() + errors = check_gitignore() + check_tracked_bins() + check_history() + if errors: + for err in errors: + print(f"check_si4684_blobs: error: {err}", file=sys.stderr) + return 1 + print( + "Si4684 blob policy OK: " + f"{IGNORE_LINE} gitignored, no tracked blobs, clean history." + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main())