From 54ac2e007f4482907de01b5bfb5a17ded8722856 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin Date: Mon, 6 Apr 2026 21:34:42 +0300 Subject: [PATCH] Add agent version and capabilities to INFO response Extend RSP_INFO from 16 to 24 bytes with two new fields: - agent_version (uint32_t): protocol version, currently 2 - capabilities (uint32_t): bitfield of supported features - flash_stream, sector_bitmap, page_skip, set_baud, reboot, selfupdate, scan Host parses extended fields when present (len >= 24). CLI displays version and capability list in `defib agent info`. Co-Authored-By: Claude Opus 4.6 (1M context) --- agent/main.c | 21 +++++++++++++++++++-- src/defib/agent/client.py | 20 +++++++++++++++++++- src/defib/agent/protocol.py | 2 +- src/defib/cli/app.py | 14 ++++++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/agent/main.c b/agent/main.c index 0a31205..9191b35 100644 --- a/agent/main.c +++ b/agent/main.c @@ -105,8 +105,23 @@ static int addr_readable(uint32_t addr, uint32_t size) { return 0; } +/* Agent protocol version — increment on protocol changes */ +#define AGENT_VERSION 2 + +/* Capability flags — advertise supported features */ +#define CAP_FLASH_STREAM (1 << 0) /* CMD_FLASH_STREAM with double-buffer */ +#define CAP_SECTOR_BITMAP (1 << 1) /* 0xFF sector skip in FLASH_STREAM */ +#define CAP_PAGE_SKIP (1 << 2) /* 0xFF page skip in programming */ +#define CAP_SET_BAUD (1 << 3) /* CMD_SET_BAUD for high-speed UART */ +#define CAP_REBOOT (1 << 4) /* CMD_REBOOT */ +#define CAP_SELFUPDATE (1 << 5) /* CMD_SELFUPDATE */ +#define CAP_SCAN (1 << 6) /* CMD_SCAN */ + +#define AGENT_CAPS (CAP_FLASH_STREAM | CAP_SECTOR_BITMAP | CAP_PAGE_SKIP | \ + CAP_SET_BAUD | CAP_REBOOT | CAP_SELFUPDATE | CAP_SCAN) + static void handle_info(void) { - uint8_t resp[16]; + uint8_t resp[24]; /* JEDEC ID in first 4 bytes (3 bytes + padding) */ resp[0] = flash_info.jedec_id[0]; resp[1] = flash_info.jedec_id[1]; @@ -115,7 +130,9 @@ static void handle_info(void) { write_le32(&resp[4], flash_info.size); write_le32(&resp[8], RAM_BASE); write_le32(&resp[12], 0x10000); /* 64KB sector */ - proto_send(RSP_INFO, resp, 16); + write_le32(&resp[16], AGENT_VERSION); + write_le32(&resp[20], AGENT_CAPS); + proto_send(RSP_INFO, resp, 24); } static void handle_read(const uint8_t *data, uint32_t len) { diff --git a/src/defib/agent/client.py b/src/defib/agent/client.py index 2eec246..1b2c4a6 100644 --- a/src/defib/agent/client.py +++ b/src/defib/agent/client.py @@ -192,6 +192,15 @@ async def _restore_baud(self) -> None: if ok: self._current_baud = FALLBACK_BAUD + # Agent capability flags (must match agent/main.c) + CAP_FLASH_STREAM = 1 << 0 + CAP_SECTOR_BITMAP = 1 << 1 + CAP_PAGE_SKIP = 1 << 2 + CAP_SET_BAUD = 1 << 3 + CAP_REBOOT = 1 << 4 + CAP_SELFUPDATE = 1 << 5 + CAP_SCAN = 1 << 6 + async def get_info(self) -> dict[str, int | str]: """Request device info from the agent.""" self._clear_rx_buffers() @@ -209,13 +218,22 @@ async def get_info(self) -> dict[str, int | str]: self._ram_base = ram_base self._sector_size = sector_size - return { + result: dict[str, int | str] = { "jedec_id": f"{jedec[0]:02x}{jedec[1]:02x}{jedec[2]:02x}", "flash_size": flash_size, "ram_base": ram_base, "sector_size": sector_size, } + # Extended fields (agent version >= 2) + if len(data) >= 24: + version = struct.unpack(" None: console.print(f" Flash size: {int(info.get('flash_size', 0)) // 1024} KB") console.print(f" RAM base: 0x{int(info.get('ram_base', 0)):08x}") console.print(f" Sector size: {int(info.get('sector_size', 0)) // 1024} KB") + if "agent_version" in info: + caps = int(info.get("capabilities", 0)) + cap_names = [] + cap_map = [ + (1 << 0, "flash_stream"), (1 << 1, "sector_bitmap"), + (1 << 2, "page_skip"), (1 << 3, "set_baud"), + (1 << 4, "reboot"), (1 << 5, "selfupdate"), + (1 << 6, "scan"), + ] + for bit, name in cap_map: + if caps & bit: + cap_names.append(name) + console.print(f" Agent ver: {info['agent_version']}") + console.print(f" Capabilities: {', '.join(cap_names) or 'none'}") @agent_app.command("read")