mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-23 15:08:19 +00:00
feat(core/prodtest): add nrf update command & script
[no changelog]
This commit is contained in:
parent
351c6da4be
commit
d5d921d3d4
@ -322,6 +322,10 @@ nrf-version
|
||||
OK 0.1.2.3
|
||||
```
|
||||
|
||||
### nrf-update
|
||||
Updates the nRF firmware.
|
||||
|
||||
|
||||
### touch-draw
|
||||
Starts a drawing canvas, where user can draw with finger on pen. Canvas is exited by sending CTRL+C command.
|
||||
```
|
||||
|
@ -69,6 +69,107 @@ static void prodtest_nrf_version(cli_t* cli) {
|
||||
info.version_patch, info.version_tweak);
|
||||
}
|
||||
|
||||
#define NRF_UPDATE_MAXSIZE 0x50000
|
||||
__attribute__((section(".buf"))) static uint8_t nrf_buffer[NRF_UPDATE_MAXSIZE];
|
||||
|
||||
static void prodtest_nrf_update(cli_t* cli) {
|
||||
static size_t nrf_len = 0;
|
||||
static bool nrf_update_in_progress = false;
|
||||
|
||||
if (cli_arg_count(cli) < 1) {
|
||||
cli_error_arg_count(cli);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* phase = cli_arg(cli, "phase");
|
||||
if (phase == NULL) {
|
||||
cli_error_arg(cli, "Expecting phase (begin|chunk|end).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == strcmp(phase, "begin")) {
|
||||
if (cli_arg_count(cli) != 1) {
|
||||
cli_error_arg_count(cli);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Reset our state
|
||||
nrf_len = 0;
|
||||
nrf_update_in_progress = true;
|
||||
cli_trace(cli, "begin");
|
||||
cli_ok(cli, "");
|
||||
|
||||
} else if (0 == strcmp(phase, "chunk")) {
|
||||
if (cli_arg_count(cli) < 2) {
|
||||
cli_error_arg_count(cli);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!nrf_update_in_progress) {
|
||||
cli_error(cli, CLI_ERROR, "Update not started. Use 'begin' first.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Receive next piece of the image
|
||||
size_t chunk_len = 0;
|
||||
uint8_t chunk_buf[512]; // tune this if you like
|
||||
|
||||
if (!cli_arg_hex(cli, "hex-data", chunk_buf, sizeof(chunk_buf),
|
||||
&chunk_len)) {
|
||||
cli_error_arg(cli, "Expecting hex-data for chunk.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (nrf_len + chunk_len > NRF_UPDATE_MAXSIZE) {
|
||||
cli_error(cli, CLI_ERROR, "Buffer overflow (have %u, need %u)",
|
||||
(unsigned)nrf_len, (unsigned)chunk_len);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memcpy(&nrf_buffer[nrf_len], chunk_buf, chunk_len);
|
||||
nrf_len += chunk_len;
|
||||
|
||||
cli_ok(cli, "%u %u", (unsigned)chunk_len, (unsigned)nrf_len);
|
||||
|
||||
} else if (0 == strcmp(phase, "end")) {
|
||||
if (cli_arg_count(cli) != 1) {
|
||||
cli_error_arg_count(cli);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!nrf_update_in_progress) {
|
||||
cli_error(cli, CLI_ERROR, "Update not started. Use 'begin' first.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (nrf_len == 0) {
|
||||
cli_error(cli, CLI_ERROR, "No data received");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Hand off to your firmware update routine
|
||||
if (!nrf_update(nrf_buffer, nrf_len)) {
|
||||
cli_error(cli, CLI_ERROR, "Update failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Clear state so next begin is required
|
||||
nrf_len = 0;
|
||||
nrf_update_in_progress = false;
|
||||
|
||||
cli_trace(cli, "Update successful");
|
||||
cli_ok(cli, "");
|
||||
|
||||
} else {
|
||||
cli_error(cli, CLI_ERROR, "Unknown phase '%s' (begin|chunk|end)", phase);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
cleanup:
|
||||
nrf_update_in_progress = false;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
PRODTEST_CLI_CMD(
|
||||
@ -85,4 +186,11 @@ PRODTEST_CLI_CMD(
|
||||
.args = ""
|
||||
);
|
||||
|
||||
PRODTEST_CLI_CMD(
|
||||
.name = "nrf-update",
|
||||
.func = prodtest_nrf_update,
|
||||
.info = "Update nRF firmware",
|
||||
.args = "<phase> <hex-data>"
|
||||
);
|
||||
|
||||
#endif
|
||||
|
76
core/tools/nrf_update.py
Normal file
76
core/tools/nrf_update.py
Normal file
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import click
|
||||
import serial
|
||||
|
||||
def send_cmd(ser, cmd, expect_ok=True):
|
||||
"""Send a line, read response, and abort on non-OK."""
|
||||
ser.write((cmd + "\r\n").encode())
|
||||
# Give the device a moment to process
|
||||
time.sleep(0.05)
|
||||
resp = ser.readline().decode(errors="ignore").strip()
|
||||
# Skip comments or empty lines
|
||||
while resp.startswith("#") or len(resp) == 0:
|
||||
resp = ser.readline().decode(errors="ignore").strip()
|
||||
click.echo(f"> {cmd}")
|
||||
click.echo(f"< {resp}")
|
||||
if expect_ok and not resp.startswith("OK"):
|
||||
click.echo("Error from device, aborting.", err=True)
|
||||
sys.exit(1)
|
||||
return resp
|
||||
|
||||
def upload_nrf(port, bin_path, chunk_size):
|
||||
# Read binary file
|
||||
data = Path(bin_path).read_bytes()
|
||||
total = len(data)
|
||||
click.echo(f"Read {total} bytes from {bin_path!r}")
|
||||
|
||||
# Open USB-VCP port
|
||||
ser = serial.Serial(port, timeout=2)
|
||||
time.sleep(0.1)
|
||||
ser.reset_input_buffer()
|
||||
ser.reset_output_buffer()
|
||||
|
||||
# 1) Begin transfer
|
||||
send_cmd(ser, "nrf-update begin")
|
||||
|
||||
# 2) Stream chunks
|
||||
offset = 0
|
||||
while offset < total:
|
||||
chunk = data[offset:offset + chunk_size]
|
||||
hexstr = chunk.hex()
|
||||
send_cmd(ser, f"nrf-update chunk {hexstr}")
|
||||
offset += len(chunk)
|
||||
pct = offset * 100 // total
|
||||
click.echo(f" Uploaded {offset}/{total} bytes ({pct}%)")
|
||||
|
||||
# 3) Finish transfer
|
||||
send_cmd(ser, "nrf-update end")
|
||||
click.echo("nRF update complete.")
|
||||
|
||||
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
||||
@click.argument("port", metavar="<serial-port>")
|
||||
@click.argument("binary", metavar="<nrf-binary>", type=click.Path(exists=True, dir_okay=False))
|
||||
@click.option("--chunk-size", "-c", default=512, show_default=True,
|
||||
help="Max bytes per chunk")
|
||||
def main(port, binary, chunk_size):
|
||||
"""
|
||||
Upload an nRF firmware image via USB-VCP CLI.
|
||||
|
||||
<serial-port> e.g. /dev/ttyUSB0 or COM3
|
||||
<nrf-binary> path to the .bin file
|
||||
"""
|
||||
try:
|
||||
upload_nrf(port, binary, chunk_size)
|
||||
except serial.SerialException as e:
|
||||
click.echo(f"Serial error: {e}", err=True)
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
click.echo("Interrupted by user.", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user