diff --git a/cmd/picomap/main.go b/cmd/picomap/main.go index 9dcc183..f95bc25 100644 --- a/cmd/picomap/main.go +++ b/cmd/picomap/main.go @@ -113,7 +113,7 @@ func closeTargets(targets []target) { func main() { if len(os.Args) < 2 { - fmt.Fprintf(os.Stderr, "usage: picomap [args...]\n\ncommands:\n info\n load\n log\n reboot\n picoboot\n test\n") + fmt.Fprintf(os.Stderr, "usage: picomap [args...]\n\ncommands:\n info\n flash-status\n load\n log\n reboot\n picoboot\n test\n") os.Exit(1) } cmd := os.Args[1] @@ -123,6 +123,8 @@ func main() { switch cmd { case "info": err = cmdInfo(args) + case "flash-status": + err = cmdFlashStatus(args) case "load": err = cmdLoad(args) case "log": @@ -134,7 +136,7 @@ func main() { case "test": err = cmdTestGroup(args) default: - fmt.Fprintf(os.Stderr, "usage: picomap [args...]\n\ncommands:\n info\n load\n log\n reboot\n picoboot\n test\n") + fmt.Fprintf(os.Stderr, "usage: picomap [args...]\n\ncommands:\n info\n flash-status\n load\n log\n reboot\n picoboot\n test\n") os.Exit(1) } if err != nil { @@ -150,7 +152,8 @@ func printInfo(via string, info *client.ResponseInfo) { "mac", net.HardwareAddr(info.MAC[:]).String(), "ip", net.IP(info.IP[:]).String(), "firmware", info.FirmwareName, - "boot", info.Boot.String()) + "boot", info.Boot.String(), + "build_epoch", info.BuildEpoch) } func cmdInfo(args []string) error { @@ -204,6 +207,31 @@ func cmdLog(args []string) error { return nil } +func cmdFlashStatus(args []string) error { + fs := flag.NewFlagSet("flash-status", flag.ExitOnError) + tf := addTargetFlags(fs) + fs.Parse(args) + + targets, err := tf.connect(500 * time.Millisecond) + if err != nil { + return err + } + defer closeTargets(targets) + + for _, t := range targets { + status, err := t.client.FlashStatus() + if err != nil { + slog.Error("flash-status error", "via", t.name, "err", err) + continue + } + slog.Info("flash-status", + "via", t.name, + "boot_partition", status.BootPartition, + "boot_type", status.BootType) + } + return nil +} + func cmdReboot(args []string) error { fs := flag.NewFlagSet("reboot", flag.ExitOnError) tf := addTargetFlags(fs) diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 0e74a9a..fa4b055 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -30,6 +30,11 @@ pico_generate_pio_header(picomap ${CMAKE_CURRENT_LIST_DIR}/w6300/qspi.pio) pico_enable_stdio_usb(picomap 0) pico_enable_stdio_uart(picomap 0) pico_set_binary_type(picomap copy_to_ram) +string(TIMESTAMP BUILD_EPOCH "%s" UTC) +math(EXPR VERSION_MAJOR "${BUILD_EPOCH} >> 16") +math(EXPR VERSION_MINOR "${BUILD_EPOCH} & 65535") +pico_set_binary_version(picomap MAJOR ${VERSION_MAJOR} MINOR ${VERSION_MINOR}) +target_compile_definitions(picomap PRIVATE BUILD_EPOCH=${BUILD_EPOCH}) pico_add_extra_outputs(picomap) target_link_libraries(picomap ${LIB_DEPS}) @@ -40,5 +45,7 @@ pico_generate_pio_header(picomap_test ${CMAKE_CURRENT_LIST_DIR}/w6300/qspi.pio) pico_enable_stdio_usb(picomap_test 0) pico_enable_stdio_uart(picomap_test 0) pico_set_binary_type(picomap_test copy_to_ram) +pico_set_binary_version(picomap_test MAJOR ${VERSION_MAJOR} MINOR ${VERSION_MINOR}) +target_compile_definitions(picomap_test PRIVATE BUILD_EPOCH=${BUILD_EPOCH}) pico_add_extra_outputs(picomap_test) target_link_libraries(picomap_test ${LIB_DEPS}) diff --git a/firmware/firmware.cpp b/firmware/firmware.cpp index a622e1b..8626bce 100644 --- a/firmware/firmware.cpp +++ b/firmware/firmware.cpp @@ -10,6 +10,7 @@ static constexpr handler_entry handlers[] = { {RequestFlashErase::ext_id, typed_handler}, {RequestFlashWrite::ext_id, typed_handler}, {RequestReboot::ext_id, typed_handler}, + {RequestFlashStatus::ext_id, typed_handler}, }; int main() { diff --git a/firmware/include/handlers.h b/firmware/include/handlers.h index 776fda3..949d11c 100644 --- a/firmware/include/handlers.h +++ b/firmware/include/handlers.h @@ -14,3 +14,4 @@ std::optional handle_log(const responder& resp, const RequestLog&); std::optional handle_flash_erase(const responder& resp, const RequestFlashErase&); std::optional handle_flash_write(const responder& resp, const RequestFlashWrite&); std::optional handle_reboot(const responder& resp, const RequestReboot&); +std::optional handle_flash_status(const responder& resp, const RequestFlashStatus&); diff --git a/firmware/include/wire.h b/firmware/include/wire.h index 5d58808..c1969f5 100644 --- a/firmware/include/wire.h +++ b/firmware/include/wire.h @@ -57,8 +57,9 @@ struct ResponseInfo { std::array ip; std::string firmware_name; boot_reason boot; - auto as_tuple() const { return std::tie(board_id, mac, ip, firmware_name, boot); } - auto as_tuple() { return std::tie(board_id, mac, ip, firmware_name, boot); } + uint32_t build_epoch; + auto as_tuple() const { return std::tie(board_id, mac, ip, firmware_name, boot, build_epoch); } + auto as_tuple() { return std::tie(board_id, mac, ip, firmware_name, boot, build_epoch); } }; struct RequestLog { @@ -121,6 +122,20 @@ struct ResponseReboot { auto as_tuple() { return std::tie(); } }; +struct RequestFlashStatus { + static constexpr int8_t ext_id = 14; + auto as_tuple() const { return std::tie(); } + auto as_tuple() { return std::tie(); } +}; + +struct ResponseFlashStatus { + static constexpr int8_t ext_id = 15; + int8_t boot_partition; + uint8_t boot_type; + auto as_tuple() const { return std::tie(boot_partition, boot_type); } + auto as_tuple() { return std::tie(boot_partition, boot_type); } +}; + struct RequestListTests { static constexpr int8_t ext_id = 125; auto as_tuple() const { return std::tie(); } diff --git a/firmware/lib/handlers.cpp b/firmware/lib/handlers.cpp index ea10442..dbec25d 100644 --- a/firmware/lib/handlers.cpp +++ b/firmware/lib/handlers.cpp @@ -45,6 +45,7 @@ std::optional handle_info(const responder&, const RequestInfo&) { resp.ip = ns.ip; resp.firmware_name = firmware_name; resp.boot = detected_boot_reason; + resp.build_epoch = BUILD_EPOCH; return resp; } @@ -87,6 +88,19 @@ std::optional handle_flash_write(const responder&, const Req return ResponseFlashWrite{}; } +std::optional handle_flash_status(const responder&, const RequestFlashStatus&) { + ResponseFlashStatus resp; + boot_info_t bi; + if (rom_get_boot_info(&bi)) { + resp.boot_partition = bi.partition; + resp.boot_type = bi.boot_type; + } else { + resp.boot_partition = -1; + resp.boot_type = 0; + } + return resp; +} + std::optional handle_reboot(const responder&, const RequestReboot&) { dispatch_schedule_ms(100, []{ watchdog_hw->scratch[0] = static_cast(boot_reason::request_reboot); diff --git a/firmware/test.cpp b/firmware/test.cpp index 9b576f8..990d299 100644 --- a/firmware/test.cpp +++ b/firmware/test.cpp @@ -20,6 +20,7 @@ static constexpr handler_entry handlers[] = { {RequestFlashErase::ext_id, typed_handler}, {RequestFlashWrite::ext_id, typed_handler}, {RequestReboot::ext_id, typed_handler}, + {RequestFlashStatus::ext_id, typed_handler}, {RequestListTests::ext_id, typed_handler}, {RequestTest::ext_id, typed_handler}, }; diff --git a/lib/client/client.go b/lib/client/client.go index ebcb2a3..8bbad34 100644 --- a/lib/client/client.go +++ b/lib/client/client.go @@ -132,6 +132,10 @@ func (c *Client) Reboot() error { return err } +func (c *Client) FlashStatus() (*ResponseFlashStatus, error) { + return first(roundTrip[ResponseFlashStatus](c, &RequestFlashStatus{})) +} + func (c *Client) ListTests() (*ResponseListTests, error) { return first(roundTrip[ResponseListTests](c, &RequestListTests{})) } diff --git a/lib/client/types.go b/lib/client/types.go index b564206..c7ccdcf 100644 --- a/lib/client/types.go +++ b/lib/client/types.go @@ -33,6 +33,7 @@ type ResponseInfo struct { IP [4]byte FirmwareName string Boot BootReason + BuildEpoch uint32 } type RequestLog struct{} @@ -61,6 +62,12 @@ type ResponseFlashWrite struct{} type RequestReboot struct{} type ResponseReboot struct{} +type RequestFlashStatus struct{} +type ResponseFlashStatus struct { + BootPartition int8 + BootType uint8 +} + type RequestListTests struct{} type ResponseListTests struct { Names []string @@ -105,6 +112,8 @@ func init() { msgpack.RegisterExt(11, (*ResponseFlashWrite)(nil)) msgpack.RegisterExt(12, (*RequestReboot)(nil)) msgpack.RegisterExt(13, (*ResponseReboot)(nil)) + msgpack.RegisterExt(14, (*RequestFlashStatus)(nil)) + msgpack.RegisterExt(15, (*ResponseFlashStatus)(nil)) msgpack.RegisterExt(125, (*RequestListTests)(nil)) msgpack.RegisterExt(124, (*ResponseListTests)(nil)) msgpack.RegisterExt(127, (*RequestTest)(nil))