Add Slice 4 tuner stack with AGENTS-compliant core types and HTTP API.
Introduces Si4684/ADAU1701 drivers, TunerService, FrequencyKHz, SeekDirection, /api/tuner routes without file-scope globals, host tests, and firmware blob tooling (binaries remain gitignored). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
"""extract_si4684_blob.py — convert Si4684 C arrays or copies to .bin files.
|
||||
|
||||
DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
|
||||
Copyright 2026 Michele Bigi
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Usage:
|
||||
# From a SigmaStudio-style C header (PE5PVB firmware.h, Si468xROM.h):
|
||||
tools/extract_si4684_blob.py --header src/firmware.h --array firmware \\
|
||||
--out Firmware/Si4684-Firmware/dab_firmware.bin
|
||||
|
||||
# Copy an existing Skyworks .bin from an eval-board folder:
|
||||
tools/extract_si4684_blob.py --copy ~/si46xx_firmware/fm_radio_5_1_0.bin \\
|
||||
--out Firmware/Si4684-Firmware/fm_firmware.bin
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def extract_c_array(header: Path, array_name: str) -> bytes:
|
||||
text = header.read_text(encoding="utf-8", errors="replace")
|
||||
pattern = rf"{re.escape(array_name)}\s*\[\s*\]\s*=\s*\{{(.*?)\}};"
|
||||
match = re.search(pattern, text, re.S)
|
||||
if not match:
|
||||
raise SystemExit(f"array {array_name!r} not found in {header}")
|
||||
values = re.findall(r"0x[0-9a-fA-F]+", match.group(1))
|
||||
if not values:
|
||||
raise SystemExit(f"array {array_name!r} is empty in {header}")
|
||||
return bytes(int(v, 16) for v in values)
|
||||
|
||||
|
||||
def extract_flash_region(dump: Path, offset: int, size: int) -> bytes:
|
||||
data = dump.read_bytes()
|
||||
end = offset + size
|
||||
if end > len(data):
|
||||
raise SystemExit(
|
||||
f"flash dump too small: need {end} bytes, have {len(data)}"
|
||||
)
|
||||
return data[offset:end]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description=__doc__)
|
||||
ap.add_argument("--out", type=Path, required=True, help="output .bin path")
|
||||
src = ap.add_mutually_exclusive_group(required=True)
|
||||
src.add_argument("--header", type=Path, help="C header with byte array")
|
||||
src.add_argument("--copy", type=Path, help="existing firmware .bin to copy")
|
||||
src.add_argument("--flash-dump", type=Path, help="full SPI flash dump")
|
||||
ap.add_argument("--array", help="array name when using --header")
|
||||
ap.add_argument(
|
||||
"--offset",
|
||||
type=lambda s: int(s, 0),
|
||||
default=0x106000,
|
||||
help="byte offset for --flash-dump (default: FM region from dirb.tech)",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--size",
|
||||
type=lambda s: int(s, 0),
|
||||
default=0x81721,
|
||||
help="byte length for --flash-dump (default: FM V5.1.0 size)",
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
if args.header is not None:
|
||||
if not args.array:
|
||||
ap.error("--array is required with --header")
|
||||
payload = extract_c_array(args.header, args.array)
|
||||
elif args.copy is not None:
|
||||
payload = args.copy.read_bytes()
|
||||
else:
|
||||
payload = extract_flash_region(args.flash_dump, args.offset, args.size)
|
||||
|
||||
args.out.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.out.write_bytes(payload)
|
||||
print(f"wrote {args.out} ({len(payload)} bytes)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user