Fix remaining Si4684 ARG-offset bugs: seek, DAB services, RSQ/RDS/DIGRAD acks

writeCommand() had no way to send a non-zero ARG1, since it always
hardcoded ARG1=0x00 before the caller's payload. Every command whose real
ARG1 needed to carry a flag (STCACK, INTACK, SERTYPE, DIGRAD/EVENT ack) or
whose payload needed to start at ARG1 instead of ARG2 was silently broken:

- seekFm(): SEEKUP/WRAP never reached the chip (always ARG2=0x00), so
  hardware seek always searched down/no-wrap; masked by the existing
  100 kHz software-step fallback in Si4684Tuner.
- startDabService()/stopDabService(): SERVICE_ID/COMPONENT_ID shifted one
  byte right of their real ARG4-11 positions, with SERTYPE landing where
  the spec requires a fixed 0x00.
- readDabServiceData(): same shift, plus STATUS_ONLY was bit3 (0x08)
  instead of the correct bit4 (0x10).
- clearFmStc(), readFmRsq(), readFmRds(), fetchDabServiceList(),
  readDabDigRadStatus(), readDabEventStatus(): these AN649 commands have
  only ARG1 and no ARG2 at all, so the old two-argument writeCommand()
  could never carry their ack/status flags — clearFmStc()'s STCACK never
  fired in this driver's history (masked by FM_TUNE_FREQ/FM_SEEK_START
  auto-clearing STC per their own spec).

writeCommand() gains a fourth parameter, arg1 (default 0x00, preserving
every already-correct call site); each caller above now passes its flag
through arg1 instead of the payload array.

Confirmed live: first locked:true and first genuine hardware seek (not
software-fallback) in this driver's history — 87.5 -> 98.3 MHz, RSSI +12
dBuV, SNR +14 dB.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
This commit is contained in:
2026-08-16 00:33:46 +02:00
co-authored by Claude Sonnet 5
parent ef15e9839c
commit f5fe92f93e
3 changed files with 114 additions and 35 deletions
@@ -421,11 +421,75 @@ comments. The lesson: `writeCommand()`'s implicit ARG1 prepend is an easy
trap for future commands — any new caller must remember its array starts at
ARG2, not ARG1.
**Follow-up**: get a proper antenna connected and confirm an actual station
lock (`valid=1`) on both FM and DAB; audit other `writeCommand()` call sites
in `Si4684Driver.cpp` for the same off-by-one pattern (POWER_UP and
HOST_LOAD were checked and are correct; FM_SEEK, property writes, and RSQ/
DIGRAD status reads have not yet been re-verified against AN649 page text).
**Follow-up, completed same session**: audited every remaining
`writeCommand()` call site in `Si4684Driver.cpp` against the AN649 page text
(not driver comments) and found the identical bug pattern repeated in
several more places — `writeCommand()`'s implicit `ARG1=0x00` prepend was
either swallowing a real ARG1 value the caller needed, or shifting a whole
multi-byte struct one slot right:
- **`seekFm()` (FM_SEEK_START, 0x31)**: `SEEKUP`/`WRAP` (real ARG2) were
never sent — the chip always saw `ARG2=0x00`, so hardware seek always
searched down with no wrap regardless of what was requested. This is why
every seek in this report's earlier captures fell through to the
`hitech95`-inspired 100 kHz software-step fallback in `Si4684Tuner`
instead of using the chip's real seek.
- **`startDabService()`/`stopDabService()` (0x81/0x82)**: `SERVICE_ID` and
`COMPONENT_ID` (8 bytes, real ARG4-11) were shifted one byte right into
ARG5-12, with `SERTYPE` landing in ARG2 (spec: fixed `0x00`) instead of
ARG1. Playing a specific DAB service would have started the wrong
service/component or failed outright — not yet observed in practice only
because tuning itself never worked before this session.
- **`readDabServiceData()` (GET_DIGITAL_SERVICE_DATA, 0x84)**: same
ARG1-only shift as below, plus the `STATUS_ONLY` bit was coded as `0x08`
(bit 3) instead of the correct `0x10` (bit 4) per the AN649 bit table.
- **`clearFmStc()`, `readFmRsq()`, `readFmRds()`, `fetchDabServiceList()`,
`readDabDigRadStatus()`, `readDabEventStatus()`**: all six commands
(FM_RSQ_STATUS 0x32, FM_RDS_STATUS 0x34, GET_DIGITAL_SERVICE_LIST 0x80,
DAB_DIGRAD_STATUS 0xB2, DAB_GET_EVENT_STATUS 0xB3) have **only ARG1** in
the AN649 spec — no ARG2 exists at all. Passing anything through the old
`writeCommand(cmd, payload, length)` two-argument form for these could
only ever send a spurious extra byte while the intended ARG1 flag (STCACK,
INTACK, SERTYPE, DIGRAD ack, EVENT_ACK) silently landed nowhere, since
`writeCommand()` had no way to set ARG1 to anything but a hardcoded
`0x00`. `clearFmStc()`'s STCACK never fired in this driver's entire
history — masked because `FM_TUNE_FREQ`/`FM_SEEK_START` already
auto-clear STC per their own AN649 documentation.
Fixed by giving `writeCommand()` a fourth parameter, `std::uint8_t arg1 =
0x00U` (default preserves every already-correct call site), and updating
each caller above to either pass its flag byte through `arg1` with no
payload (for the ARG1-only commands) or drop the erroneous leading array
element (for the multi-arg commands whose ARG1 is legitimately always
`0x00`, e.g. `seekFm`'s default tune mode).
**Confirmed live after this round of fixes** — first `locked: true` and
first hardware (non-software-fallback) seek in this entire investigation:
```
POST /api/tuner/tune {"band":"fm","frequency_khz":87500}
POST /api/tuner/seek {"direction":"up"}
-> {"frequency_khz":98300}
GET /api/tuner/status
-> {"locked":true,"fm":{"frequency_khz":98300,"rssi_dbuv":12,"snr_db":14,"stereo":false}}
```
The seek jumped directly from 87.5 to 98.3 MHz in one hardware search (not
100 kHz software steps), landing on a real, locked station at a plausible
RSSI/SNR. `bt1035` also came back to `true` in `/api/health` during this
same session (cause not yet diagnosed — see the BT1035 section below;
unrelated to this fix, separate chip).
DAB was swept across 7 frequency-table indices (5, 10, 15, 20, 25, 30, 35)
after the fix — `fic_quality`/`cnr_db` stayed at 0 on all of them, no lock
yet. The DAB_TUNE_FREQ/DAB service-list/service-start fixes are verified
correct against the AN649 spec text the same way the FM fix was, but do not
yet have an empirical lock to point to, unlike FM. Not treated as a red
flag — no DAB antenna tuning has been attempted yet, and the default
European frequency table may not match active local multiplexes at these
particular indices. **Next step**: sweep the full DAB frequency table (not
just 7 samples) with a real antenna and confirm a lock the same way FM was
confirmed.
**Unrelated finding from the same session, logged for completeness**: BT1035
began failing boot deterministically (`no spontaneous UART bytes after