From 13eb350bab62ed6b76a041a67e9680bc2a9ebe3e Mon Sep 17 00:00:00 2001 From: Henrik Olsson Date: Thu, 6 Aug 2026 14:50:50 +0000 Subject: [PATCH] feat(protocol): carry HW revision + FW identity in the MSG_OK_READY ack CAP-02. The operation-setup ack previously carried only the CAP-01 2-byte buffer size. It now carries everything the host needs to gate compatibility at connect time: [buffer_size u16 BE][effective_hw_revision u8][ver_len u8][ver bytes] Motivation: the host must refuse to program chips that route VPP to bus line 11 (socket pin 21 on the DIP24_2716 / DIP24_2532 pinouts) unless the attached shield is Rev 2.2 or later -- those parts need the 3-position JP4 header, and driving them on an earlier board is a chip-damage path. The revision was already computed at boot but only reachable via a separate CMD_HW_VERSION command or a FLAG_VERBOSE-gated INFO frame. Folding it into MSG_OK_READY lets the host retire its dedicated CMD_FW_VERSION pre-probe, removing one full command exchange per connect. Notes: - MSG_OK_READY is declared `params = [{ type = "bytes" }]` in the message catalog, so this needs NO catalog edit and NO codegen regen. - The revision byte is always emitted; builds without HARDWARE_REVISION send 0xFE (the REVISION_UNKNOWN value -- the symbol itself lives inside that same #ifdef). The ack's shape therefore never varies by build config, and "no detection compiled in" reaches the host as a rejectable value rather than as an absent field. - Hosts predating CAP-02 test `len(params) == 2`, miss, and fall back to their 512-byte chunk floor: degraded throughput on Leonardo, never a misparse. Cost: +34 bytes flash on uno (23954 -> 23988), no change to static RAM. The 36-byte pack buffer is stack-local to init_programmer_framed. Co-Authored-By: Claude Opus 5 (1M context) --- src/firestarter.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/firestarter.cpp b/src/firestarter.cpp index 851bbb9..09cf801 100644 --- a/src/firestarter.cpp +++ b/src/firestarter.cpp @@ -154,7 +154,43 @@ bool init_programmer_framed(firestarter_handle_t* handle) { LOG_INFO_ID_U8(MSG_INFO_HW, (uint8_t)rurp_get_hardware_revision()); #endif LOG_INFO_ID_U8(MSG_INFO_CMD, (uint8_t)handle->cmd); - LOG_OK_ID_U16(MSG_OK_READY, (uint16_t)DATA_BUFFER_SIZE); + // CAP-02: the operation-setup ack carries every identity value the host + // needs to gate compatibility BEFORE any hardware moves. configure_memory + // has already run at this point, but every configure_* handler is pure + // (function-pointer assignment only) and the VPP regulator is not engaged + // until firestarter_operation_init, which sits behind op_wait_for_ack() — + // so a host that refuses here stops the sequence with the rail still down. + // + // Wire layout, extending the CAP-01 2-byte buffer-size region: + // [buffer_size u16 BE][effective_hw_revision u8][ver_len u8][ver bytes] + // + // MSG_OK_READY is declared `params = [{ type = "bytes" }]` in the message + // catalog, so this needs no catalog edit and no codegen regen. Hosts + // predating CAP-02 test `len(params) == 2`, miss, and fall back to their + // 512-byte floor — degraded throughput, never a misparse. + // + // The revision byte is ALWAYS emitted: on builds without HARDWARE_REVISION + // it is 0xFE (the REVISION_UNKNOWN value — the symbol itself is inside that + // same #ifdef, so it cannot be named here). Keeping the byte unconditional + // means the ack's shape never varies by build configuration, only by the + // length of the version string, and "no detection compiled in" reaches the + // host as a value it can reject rather than as a missing field. + { + const char* _ver = FW_VERSION; + uint8_t _vlen = (uint8_t)strlen(_ver); + if (_vlen > 32) _vlen = 32; + uint8_t _ready[4 + 32]; + _ready[0] = (uint8_t)(((uint16_t)DATA_BUFFER_SIZE >> 8) & 0xFF); + _ready[1] = (uint8_t)((uint16_t)DATA_BUFFER_SIZE & 0xFF); +#ifdef HARDWARE_REVISION + _ready[2] = (uint8_t)rurp_get_hardware_revision(); +#else + _ready[2] = 0xFE; +#endif + _ready[3] = _vlen; + memcpy(_ready + 4, _ver, _vlen); + LOG_OK_ID_BYTES(MSG_OK_READY, _ready, (uint8_t)(4 + _vlen)); + } op_reset_timeout(); return true; }