In-app flash write, UF2 parser, remove picotool dependency, reboot command

This commit is contained in:
Ian Gulliver
2026-04-11 22:26:54 +09:00
parent a635aa04e0
commit e3d97f4946
11 changed files with 263 additions and 76 deletions

View File

@@ -117,6 +117,21 @@ func (c *Client) Log() (*ResponseLog, error) {
return first(roundTrip[ResponseLog](c, &RequestLog{}))
}
func (c *Client) FlashErase(addr, length uint32) error {
_, err := first(roundTrip[ResponseFlashErase](c, &RequestFlashErase{Addr: addr, Len: length}))
return err
}
func (c *Client) FlashWrite(addr uint32, data []byte) error {
_, err := first(roundTrip[ResponseFlashWrite](c, &RequestFlashWrite{Addr: addr, Data: data}))
return err
}
func (c *Client) Reboot() error {
_, err := first(roundTrip[ResponseReboot](c, &RequestReboot{}))
return err
}
func (c *Client) ListTests() (*ResponseListTests, error) {
return first(roundTrip[ResponseListTests](c, &RequestListTests{}))
}

View File

@@ -24,6 +24,21 @@ type ResponseLog struct {
Entries []LogEntry
}
type RequestFlashErase struct {
Addr uint32
Len uint32
}
type ResponseFlashErase struct{}
type RequestFlashWrite struct {
Addr uint32
Data []byte
}
type ResponseFlashWrite struct{}
type RequestReboot struct{}
type ResponseReboot struct{}
type RequestListTests struct{}
type ResponseListTests struct {
Names []string
@@ -62,6 +77,12 @@ func init() {
msgpack.RegisterExt(5, (*ResponseInfo)(nil))
msgpack.RegisterExt(6, (*RequestLog)(nil))
msgpack.RegisterExt(7, (*ResponseLog)(nil))
msgpack.RegisterExt(8, (*RequestFlashErase)(nil))
msgpack.RegisterExt(9, (*ResponseFlashErase)(nil))
msgpack.RegisterExt(10, (*RequestFlashWrite)(nil))
msgpack.RegisterExt(11, (*ResponseFlashWrite)(nil))
msgpack.RegisterExt(12, (*RequestReboot)(nil))
msgpack.RegisterExt(13, (*ResponseReboot)(nil))
msgpack.RegisterExt(125, (*RequestListTests)(nil))
msgpack.RegisterExt(124, (*ResponseListTests)(nil))
msgpack.RegisterExt(127, (*RequestTest)(nil))

View File

@@ -1,32 +0,0 @@
package picotool
import (
"fmt"
"os/exec"
"time"
)
func Load(uf2Path string, serial string, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
var out []byte
var err error
for {
cmd := exec.Command("picotool", "load", uf2Path, "-x", "--ser", serial)
out, err = cmd.CombinedOutput()
if err == nil {
return nil
}
if time.Now().After(deadline) {
return fmt.Errorf("picotool load: %w\n%s", err, out)
}
}
}
func Reboot(serial string) error {
cmd := exec.Command("picotool", "reboot", "--ser", serial)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("picotool reboot: %w\n%s", err, out)
}
return nil
}

71
lib/uf2/uf2.go Normal file
View File

@@ -0,0 +1,71 @@
package uf2
import (
"encoding/binary"
"fmt"
"os"
"sort"
)
const (
blockSize = 512
magic0 = 0x0A324655
magic1 = 0x9E5D5157
magicEnd = 0x0AB16F30
flagNotMainFlash = 0x00000001
flagFamilyIDPresent = 0x00002000
absoluteFamilyID = 0xe48bff57
)
type Block struct {
Addr uint32
Data []byte
}
func Parse(path string) ([]Block, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
if len(data)%blockSize != 0 {
return nil, fmt.Errorf("file size %d not multiple of %d", len(data), blockSize)
}
var blocks []Block
for i := 0; i < len(data); i += blockSize {
b := data[i : i+blockSize]
m0 := binary.LittleEndian.Uint32(b[0:4])
m1 := binary.LittleEndian.Uint32(b[4:8])
me := binary.LittleEndian.Uint32(b[508:512])
if m0 != magic0 || m1 != magic1 || me != magicEnd {
return nil, fmt.Errorf("block %d: bad magic", i/blockSize)
}
flags := binary.LittleEndian.Uint32(b[8:12])
if flags&flagNotMainFlash != 0 {
continue
}
if flags&flagFamilyIDPresent != 0 {
familyID := binary.LittleEndian.Uint32(b[28:32])
if familyID == absoluteFamilyID {
continue
}
}
addr := binary.LittleEndian.Uint32(b[12:16])
size := binary.LittleEndian.Uint32(b[16:20])
if size > 256 {
return nil, fmt.Errorf("block %d: data size %d > 256", i/blockSize, size)
}
blocks = append(blocks, Block{
Addr: addr,
Data: make([]byte, size),
})
copy(blocks[len(blocks)-1].Data, b[32:32+size])
}
sort.Slice(blocks, func(i, j int) bool {
return blocks[i].Addr < blocks[j].Addr
})
return blocks, nil
}