Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions agent/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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) {
Expand Down
20 changes: 19 additions & 1 deletion src/defib/agent/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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("<I", data[16:20])[0]
caps = struct.unpack("<I", data[20:24])[0]
result["agent_version"] = version
result["capabilities"] = caps

return result

async def read_memory(
self,
addr: int,
Expand Down
2 changes: 1 addition & 1 deletion src/defib/agent/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
0x09 SCAN — Scan flash health

Responses (device → host):
0x81 INFO_RSP — chip_id(4B) + flash_size(4B) + ram_base(4B)
0x81 INFO_RSP — chip_id(4B) + flash_size(4B) + ram_base(4B) + sector_size(4B) + version(4B) + caps(4B)
0x82 DATA — seq(2B LE) + data(up to 1024B)
0x83 ACK — status(1B): 0=OK, 1=CRC error, 2=flash error
0x84 CRC32_RSP— crc32(4B LE)
Expand Down
14 changes: 14 additions & 0 deletions src/defib/cli/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,20 @@ async def _agent_info_async(port: str, output: str) -> 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")
Expand Down
Loading