28 Commits

Author SHA1 Message Date
Ian Gulliver 712110aace Remove unused interrupt code: enable_interrupt_pin, CHECK markers, dead comments 2026-04-10 20:54:07 +09:00
Ian Gulliver 0c11cbb1d1 WFI with interrupt-driven wakeup for USB and W6300 2026-04-10 12:55:04 +09:00
Ian Gulliver 3d20bf4c33 Per-packet source tracking in transport, via/from in info output 2026-04-07 22:30:26 +09:00
Ian Gulliver f96ed20aa0 Fix broadcast collection: UDP reader blocks until deadline, 500ms timeout everywhere 2026-04-07 22:22:19 +09:00
Ian Gulliver e301c672a9 Broadcast discovery with InfoAll, interface broadcast detection, clean output 2026-04-07 22:12:20 +09:00
Ian Gulliver 7034391d4d UDP transport with broadcast support, -udp and -iface flags on info 2026-04-07 21:44:35 +09:00
Ian Gulliver 9989d8c66a Add UDP transport with picomap port 28781, info -udp flag 2026-04-07 21:36:50 +09:00
Ian Gulliver 3d749add7d Compose eth header into ipv4_header, use icmp_echo struct, protocol switch in handle_ipv4 2026-04-07 12:31:27 +09:00
Ian Gulliver f161dda60a Rewrite net.cpp to use packed structs for frame encoding/decoding 2026-04-07 12:21:41 +09:00
Ian Gulliver b0294fada3 Fix broadcast ping reply source IP, accept broadcast destinations 2026-04-07 12:18:07 +09:00
Ian Gulliver 31b2c16b07 Debug log with dlog_if_slow, MACRAW ping working, wfi disabled 2026-04-07 12:09:18 +09:00
Ian Gulliver d215ddc6f2 Add debug log ring buffer with timestamps, log CLI subcommand 2026-04-07 09:18:43 +09:00
Ian Gulliver ffcbaf0665 Merge info/load/test CLIs into unified picomap subcommand CLI 2026-04-07 08:37:34 +09:00
Ian Gulliver a7381ca435 MACRAW net stack, slog, load -target flag, LED blink, net_poll disabled pending SPI fix 2026-04-07 08:34:29 +09:00
Ian Gulliver a9193d51e4 Switch commands to slog, disable net_poll pending fix 2026-04-07 07:43:16 +09:00
Ian Gulliver 46db2fd966 MACRAW IP stack with ARP/ICMP, stable device ordering, LED blink on test 2026-04-07 07:34:24 +09:00
Ian Gulliver 642e2ff318 Remove leading underscores and mixed case from w6300 internal symbols 2026-04-07 07:15:24 +09:00
Ian Gulliver 3c320cf466 Rename screaming case symbols to snake_case in w6300 public API 2026-04-07 07:09:11 +09:00
Ian Gulliver 843a286631 Remove multicast socket polling, fix USB responsiveness 2026-04-07 07:04:53 +09:00
Ian Gulliver e60479bad8 Switch to IPv4 zeroconf, add test framework with discovery test, fix serial enumeration 2026-04-07 06:58:39 +09:00
Ian Gulliver b8c0e6be66 Handlers return multiple responses, dispatch polls sockets for UDP commands 2026-04-06 20:22:40 +09:00
Ian Gulliver f837937cb7 Add firmware_name to info response 2026-04-06 20:09:30 +09:00
Ian Gulliver 49bbe1b29c Split dispatch init/run, join IPv6 multicast discovery group in firmware 2026-04-06 20:01:22 +09:00
Ian Gulliver 00b960d81d Add dispatch loop, copy_to_ram, retry picotool load without sleep 2026-04-06 17:36:41 +09:00
Ian Gulliver b197f0bfa7 Extract picoboot and info handlers into lib/handlers 2026-04-06 17:24:34 +09:00
Ian Gulliver 00ab432a72 Add IPv6 link-local address to net_init and info response, use WaitGroup.Go 2026-04-06 17:20:13 +09:00
Ian Gulliver 1fa1b2076c Poll for BOOTSEL readiness instead of fixed sleep, load with -x 2026-04-06 09:19:26 +09:00
Ian Gulliver ee8563ab69 Remove DHCP client, preserved on dhcp-wip branch 2026-04-06 09:10:50 +09:00
27 changed files with 1605 additions and 658 deletions
+5 -3
View File
@@ -4,8 +4,10 @@ description: Build firmware, load it onto the Pico, and reboot. Use when the use
user-invocable: true user-invocable: true
--- ---
Run `go run ./cmd/load/` from the project root. This builds both firmware targets, loads picomap onto the first device and picomap_test onto the second, and reboots both. Run `go run ./cmd/picomap/ load` from the project root. This builds both firmware targets, loads picomap onto the first device and picomap_test onto the second, and reboots both.
After loading, run `go run ./cmd/info/` to verify both devices are responding. To load a single target: `go run ./cmd/picomap/ load picomap` or `go run ./cmd/picomap/ load picomap_test`.
After modifying the load command itself (cmd/load/, lib/wire/, lib/picoserial/), run it twice: once to load the firmware, once to verify the load process still works end-to-end. After loading, run `go run ./cmd/picomap/ info` to verify devices are responding.
After modifying the CLI itself (cmd/picomap/, lib/), run load twice: once to load the firmware, once to verify the load process still works end-to-end.
-69
View File
@@ -1,69 +0,0 @@
package main
import (
"fmt"
"os"
"sync"
"time"
"github.com/theater/picomap/lib/client"
)
type deviceResult struct {
dev string
info *client.ResponseInfo
err error
}
func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
func run() error {
devs, err := client.ListSerial()
if err != nil {
return err
}
if len(devs) == 0 {
return fmt.Errorf("no devices found")
}
results := make([]deviceResult, len(devs))
var wg sync.WaitGroup
for i, dev := range devs {
wg.Add(1)
go func() {
defer wg.Done()
results[i].dev = dev
c, err := client.NewSerial(dev, 2*time.Second)
if err != nil {
results[i].err = err
return
}
info, err := c.Info()
c.Close()
results[i].info = info
results[i].err = err
}()
}
wg.Wait()
for _, r := range results {
fmt.Printf("Device: %s\n", r.dev)
if r.err != nil {
fmt.Fprintf(os.Stderr, " error: %v\n", r.err)
continue
}
fmt.Printf(" Board ID: %02X%02X%02X%02X%02X%02X%02X%02X\n",
r.info.BoardID[0], r.info.BoardID[1], r.info.BoardID[2], r.info.BoardID[3],
r.info.BoardID[4], r.info.BoardID[5], r.info.BoardID[6], r.info.BoardID[7])
fmt.Printf(" MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
r.info.MAC[0], r.info.MAC[1], r.info.MAC[2],
r.info.MAC[3], r.info.MAC[4], r.info.MAC[5])
}
return nil
}
-158
View File
@@ -1,158 +0,0 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"sync"
"time"
"github.com/theater/picomap/lib/client"
"github.com/theater/picomap/lib/picotool"
)
func main() {
wd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
buildDir := filepath.Join(wd, "firmware", "build")
if err := run(buildDir); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
func build(buildDir string) error {
fmt.Println("Configuring...")
cmake := exec.Command("cmake", "-S", filepath.Join(filepath.Dir(buildDir)), "-B", buildDir)
cmake.Stdout = os.Stdout
cmake.Stderr = os.Stderr
if err := cmake.Run(); err != nil {
return fmt.Errorf("cmake failed: %w", err)
}
fmt.Println("Building...")
cmd := exec.Command("make", "-C", buildDir)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("build failed: %w", err)
}
return nil
}
func boardSerial(id [8]byte) string {
return fmt.Sprintf("%02X%02X%02X%02X%02X%02X%02X%02X",
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7])
}
func run(buildDir string) error {
if err := build(buildDir); err != nil {
return err
}
devs, err := client.ListSerial()
if err != nil {
return err
}
if len(devs) < 2 {
return fmt.Errorf("expected 2 devices, found %d", len(devs))
}
serials := make([]string, 2)
errs := make([]error, 2)
var wg sync.WaitGroup
for i := range 2 {
wg.Add(1)
go func() {
defer wg.Done()
c, err := client.NewSerial(devs[i], 2*time.Second)
if err != nil {
errs[i] = err
return
}
info, err := c.Info()
c.Close()
if err != nil {
errs[i] = err
return
}
serials[i] = boardSerial(info.BoardID)
}()
}
wg.Wait()
for i, err := range errs {
if err != nil {
return fmt.Errorf("info %s: %w", devs[i], err)
}
}
fmt.Println("Sending PICOBOOT requests...")
for i := range 2 {
wg.Add(1)
go func() {
defer wg.Done()
c, err := client.NewSerial(devs[i], 2*time.Second)
if err != nil {
errs[i] = err
return
}
err = c.PICOBOOT()
c.Close()
if err != nil {
errs[i] = fmt.Errorf("PICOBOOT %s: %w", devs[i], err)
}
}()
}
wg.Wait()
for _, err := range errs {
if err != nil {
return err
}
}
time.Sleep(2 * time.Second)
uf2s := []string{
filepath.Join(buildDir, "picomap.uf2"),
filepath.Join(buildDir, "picomap_test.uf2"),
}
fmt.Println("Loading firmware...")
for i := range 2 {
wg.Add(1)
go func() {
defer wg.Done()
errs[i] = picotool.Load(uf2s[i], serials[i])
}()
}
wg.Wait()
for i, err := range errs {
if err != nil {
return fmt.Errorf("load %s: %w", serials[i], err)
}
}
fmt.Println("Rebooting...")
for i := range 2 {
wg.Add(1)
go func() {
defer wg.Done()
errs[i] = picotool.Reboot(serials[i])
}()
}
wg.Wait()
for i, err := range errs {
if err != nil {
return fmt.Errorf("reboot %s: %w", serials[i], err)
}
}
fmt.Println("Done.")
return nil
}
+392
View File
@@ -0,0 +1,392 @@
package main
import (
"encoding/hex"
"flag"
"fmt"
"log/slog"
"net"
"os"
"os/exec"
"path/filepath"
"sync"
"time"
"github.com/theater/picomap/lib/client"
"github.com/theater/picomap/lib/picotool"
)
func main() {
if len(os.Args) < 2 {
slog.Error("usage: picomap <info|load|test> [args...]")
os.Exit(1)
}
cmd := os.Args[1]
args := os.Args[2:]
var err error
switch cmd {
case "info":
err = cmdInfo(args)
case "load":
err = cmdLoad(args)
case "log":
err = cmdLog(args)
case "test":
err = cmdTest(args)
default:
slog.Error("usage: picomap <info|load|log|test> [args...]")
os.Exit(1)
}
if err != nil {
slog.Error("fatal", "err", err)
os.Exit(1)
}
}
type deviceResult struct {
dev string
info *client.ResponseInfo
err error
}
func printInfo(via string, from string, info *client.ResponseInfo) {
slog.Info("device",
"via", via,
"from", from,
"board_id", hex.EncodeToString(info.BoardID[:]),
"mac", net.HardwareAddr(info.MAC[:]).String(),
"ip", net.IP(info.IP[:]).String(),
"firmware", info.FirmwareName)
}
func cmdInfo(args []string) error {
fs := flag.NewFlagSet("info", flag.ExitOnError)
udpAddr := fs.String("udp", "", "connect via UDP to this IP address")
iface := fs.String("iface", "", "bind to this network interface (for broadcast)")
fs.Parse(args)
if *udpAddr == "" && *iface != "" {
bcast, err := client.InterfaceBroadcast(*iface)
if err != nil {
return err
}
*udpAddr = bcast
}
if *udpAddr != "" {
c, err := client.NewUDP(*udpAddr, *iface, 500*time.Millisecond)
if err != nil {
return err
}
defer c.Close()
infos, err := c.InfoAll()
if err != nil {
return err
}
if len(infos) == 0 {
return fmt.Errorf("no devices responded")
}
for _, r := range infos {
printInfo(*udpAddr, r.From, r.Value)
}
return nil
}
devs, err := client.ListSerial()
if err != nil {
return err
}
if len(devs) == 0 {
return fmt.Errorf("no devices found")
}
results := make([]deviceResult, len(devs))
var wg sync.WaitGroup
for i, dev := range devs {
results[i].dev = dev
wg.Go(func() {
c, err := client.NewSerial(dev, 500*time.Millisecond)
if err != nil {
results[i].err = err
return
}
info, err := c.Info()
c.Close()
if err != nil {
results[i].err = err
return
}
results[i].info = info
})
}
wg.Wait()
for _, r := range results {
if r.err != nil {
slog.Error("device error", "dev", r.dev, "err", r.err)
continue
}
printInfo(r.dev, r.dev, r.info)
}
return nil
}
func boardSerial(id [8]byte) string {
return fmt.Sprintf("%02X%02X%02X%02X%02X%02X%02X%02X",
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7])
}
type deviceInfo struct {
dev string
serial string
uf2 string
name string
}
func buildFirmware(buildDir string) error {
slog.Info("configuring")
cmake := exec.Command("cmake", "-S", filepath.Join(filepath.Dir(buildDir)), "-B", buildDir)
cmake.Stdout = os.Stdout
cmake.Stderr = os.Stderr
if err := cmake.Run(); err != nil {
return fmt.Errorf("cmake failed: %w", err)
}
slog.Info("building")
cmd := exec.Command("make", "-C", buildDir)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("build failed: %w", err)
}
return nil
}
func cmdLog(_ []string) error {
devs, err := client.ListSerial()
if err != nil {
return err
}
if len(devs) == 0 {
return fmt.Errorf("no devices found")
}
for _, dev := range devs {
log := slog.With("dev", dev)
c, err := client.NewSerial(dev, 500*time.Millisecond)
if err != nil {
log.Error("connect error", "err", err)
continue
}
resp, err := c.Log()
c.Close()
if err != nil {
log.Error("log error", "err", err)
continue
}
if len(resp.Entries) == 0 {
log.Info("no debug messages")
continue
}
for _, e := range resp.Entries {
log.Info("dlog", "t_us", e.TimestampUS, "msg", e.Message)
}
}
return nil
}
func cmdLoad(args []string) error {
target := "all"
if len(args) > 0 {
target = args[0]
}
wd, err := os.Getwd()
if err != nil {
return err
}
buildDir := filepath.Join(wd, "firmware", "build")
if err := buildFirmware(buildDir); err != nil {
return err
}
devs, err := client.ListSerial()
if err != nil {
return err
}
allTargets := []struct {
name string
uf2 string
}{
{"picomap", filepath.Join(buildDir, "picomap.uf2")},
{"picomap_test", filepath.Join(buildDir, "picomap_test.uf2")},
}
var targets []struct {
name string
uf2 string
}
switch target {
case "all":
targets = allTargets
case "picomap":
targets = allTargets[:1]
case "picomap_test":
targets = allTargets[1:]
default:
return fmt.Errorf("unknown target %q", target)
}
if len(devs) < len(targets) {
return fmt.Errorf("need %d device(s), found %d", len(targets), len(devs))
}
devices := make([]deviceInfo, len(targets))
errs := make([]error, len(targets))
var wg sync.WaitGroup
for i := range targets {
log := slog.With("dev", devs[i])
wg.Go(func() {
c, err := client.NewSerial(devs[i], 500*time.Millisecond)
if err != nil {
errs[i] = err
return
}
info, err := c.Info()
c.Close()
if err != nil {
errs[i] = err
return
}
devices[i] = deviceInfo{
dev: devs[i],
serial: boardSerial(info.BoardID),
uf2: targets[i].uf2,
name: targets[i].name,
}
log.Info("got info", "serial", devices[i].serial, "firmware", info.FirmwareName)
})
}
wg.Wait()
for i, err := range errs {
if err != nil {
return fmt.Errorf("[%s] info: %w", devs[i], err)
}
}
for i := range devices {
log := slog.With("serial", devices[i].serial)
wg.Go(func() {
log.Info("sending PICOBOOT")
c, err := client.NewSerial(devices[i].dev, 500*time.Millisecond)
if err != nil {
errs[i] = err
return
}
err = c.PICOBOOT()
c.Close()
if err != nil {
errs[i] = fmt.Errorf("PICOBOOT %s: %w", devices[i].serial, err)
return
}
log.Info("PICOBOOT sent")
})
}
wg.Wait()
for i, err := range errs {
if err != nil {
return fmt.Errorf("[%s] %w", devices[i].serial, err)
}
}
uf2s := make([]string, len(targets))
for i := range targets {
uf2s[i] = targets[i].uf2
}
for i := range devices {
log := slog.With("serial", devices[i].serial)
wg.Go(func() {
log.Info("loading", "uf2", devices[i].name)
errs[i] = picotool.Load(devices[i].uf2, devices[i].serial, 10*time.Second)
if errs[i] == nil {
log.Info("loaded", "uf2", devices[i].name)
}
})
}
wg.Wait()
for i, err := range errs {
if err != nil {
return fmt.Errorf("[%s] load: %w", devices[i].serial, err)
}
}
slog.Info("done")
return nil
}
func cmdTest(args []string) error {
if len(args) < 1 {
return fmt.Errorf("usage: picomap test <name>")
}
name := args[0]
devs, err := client.ListSerial()
if err != nil {
return err
}
var testDev string
for _, dev := range devs {
log := slog.With("dev", dev)
c, err := client.NewSerial(dev, 500*time.Millisecond)
if err != nil {
log.Warn("connect error", "err", err)
continue
}
info, err := c.Info()
c.Close()
if err != nil {
log.Warn("info error", "err", err)
continue
}
log.Info("got info", "firmware", info.FirmwareName)
if info.FirmwareName == "picomap_test" {
testDev = dev
break
}
}
if testDev == "" {
return fmt.Errorf("no picomap_test device found")
}
log := slog.With("dev", testDev)
log.Info("running test", "name", name)
c, err := client.NewSerial(testDev, 10*time.Second)
if err != nil {
return err
}
defer c.Close()
result, err := c.Test(name)
if err != nil {
return fmt.Errorf("remote: %w", err)
}
for _, msg := range result.Messages {
log.Info("remote", "msg", msg)
}
if result.Pass {
log.Info("PASS")
} else {
log.Error("FAIL")
os.Exit(1)
}
return nil
}
+5 -2
View File
@@ -10,13 +10,14 @@ set(CMAKE_CXX_STANDARD 23)
pico_sdk_init() pico_sdk_init()
set(LIB_SOURCES set(LIB_SOURCES
lib/dhcp.cpp lib/dispatch.cpp
lib/handlers.cpp
lib/net.cpp lib/net.cpp
lib/tusb_config.cpp lib/tusb_config.cpp
w6300/w6300.cpp w6300/w6300.cpp
) )
set(LIB_DEPS pico_stdlib pico_rand tinyusb_device tinyusb_board hardware_pio hardware_spi hardware_dma hardware_clocks) set(LIB_DEPS pico_stdlib tinyusb_device tinyusb_board hardware_pio hardware_spi hardware_dma hardware_clocks)
add_executable(picomap firmware.cpp ${LIB_SOURCES}) add_executable(picomap firmware.cpp ${LIB_SOURCES})
target_include_directories(picomap PRIVATE include w6300) target_include_directories(picomap PRIVATE include w6300)
@@ -24,6 +25,7 @@ target_compile_options(picomap PRIVATE -Wall -Wextra -Wno-unused-parameter)
pico_generate_pio_header(picomap ${CMAKE_CURRENT_LIST_DIR}/w6300/qspi.pio) pico_generate_pio_header(picomap ${CMAKE_CURRENT_LIST_DIR}/w6300/qspi.pio)
pico_enable_stdio_usb(picomap 0) pico_enable_stdio_usb(picomap 0)
pico_enable_stdio_uart(picomap 0) pico_enable_stdio_uart(picomap 0)
pico_set_binary_type(picomap copy_to_ram)
pico_add_extra_outputs(picomap) pico_add_extra_outputs(picomap)
target_link_libraries(picomap ${LIB_DEPS}) target_link_libraries(picomap ${LIB_DEPS})
@@ -33,5 +35,6 @@ target_compile_options(picomap_test PRIVATE -Wall -Wextra -Wno-unused-parameter)
pico_generate_pio_header(picomap_test ${CMAKE_CURRENT_LIST_DIR}/w6300/qspi.pio) pico_generate_pio_header(picomap_test ${CMAKE_CURRENT_LIST_DIR}/w6300/qspi.pio)
pico_enable_stdio_usb(picomap_test 0) pico_enable_stdio_usb(picomap_test 0)
pico_enable_stdio_uart(picomap_test 0) pico_enable_stdio_uart(picomap_test 0)
pico_set_binary_type(picomap_test copy_to_ram)
pico_add_extra_outputs(picomap_test) pico_add_extra_outputs(picomap_test)
target_link_libraries(picomap_test ${LIB_DEPS}) target_link_libraries(picomap_test ${LIB_DEPS})
+11 -62
View File
@@ -1,66 +1,15 @@
#include "pico/stdlib.h" #include "dispatch.h"
#include "pico/bootrom.h" #include "handlers.h"
#include "pico/unique_id.h"
#include "tusb.h"
#include "wire.h"
#include "usb_cdc.h"
#include "timer_queue.h"
#include "dhcp.h"
#include "net.h"
#include "w6300.h"
static usb_cdc usb; std::string_view firmware_name = "picomap";
static timer_queue timers;
static constexpr handler_entry handlers[] = {
{RequestPICOBOOT::ext_id, handle_picoboot},
{RequestInfo::ext_id, handle_info},
{RequestLog::ext_id, handle_log},
};
int main() { int main() {
tusb_init(); dispatch_init();
dispatch_run(handlers);
net_init();
auto ninfo = w6300::get_net_info();
dhcp_start(timers, ninfo.mac);
static static_vector<uint8_t, 256> rx_buf;
while (true) {
tud_task();
usb.drain();
timers.run();
while (tud_cdc_available()) {
uint8_t byte;
if (tud_cdc_read(&byte, 1) != 1) break;
rx_buf.push_back(byte);
auto msg = try_decode(rx_buf);
if (!msg) {
if (rx_buf.full()) rx_buf.clear();
continue;
}
rx_buf.clear();
switch (msg->type_id) {
case RequestPICOBOOT::ext_id:
usb.send(encode_response(msg->message_id, ResponsePICOBOOT{}));
sleep_ms(100);
reset_usb_boot(0, 1);
break;
case RequestInfo::ext_id: {
ResponseInfo resp;
pico_unique_board_id_t uid;
pico_get_unique_board_id(&uid);
std::copy(uid.id, uid.id + 8, resp.board_id.begin());
auto ninfo = w6300::get_net_info();
resp.mac = ninfo.mac;
usb.send(encode_response(msg->message_id, resp));
break;
}
}
}
__wfi();
}
} }
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include "pico/time.h"
#include "ring_buffer.h"
struct log_entry {
uint32_t timestamp_us;
std::string message;
};
inline ring_buffer<log_entry, 32> g_debug_log;
inline void dlog(std::string_view msg) {
g_debug_log.push_overwrite(log_entry{static_cast<uint32_t>(time_us_32()), std::string(msg)});
}
inline void dlog_if_slow(std::string_view label, uint32_t threshold_us, std::function<void()> fn) {
uint32_t t0 = time_us_32();
fn();
uint32_t elapsed = time_us_32() - t0;
if (elapsed > threshold_us)
dlog(std::string(label) + " " + std::to_string(elapsed) + "us");
}
inline std::vector<log_entry> dlog_drain() {
std::vector<log_entry> result;
uint16_t n = g_debug_log.used();
result.reserve(n);
for (uint16_t i = 0; i < n; i++) {
log_entry e;
g_debug_log.peek(std::span{&e, 1});
result.push_back(std::move(e));
g_debug_log.consume(1);
}
return result;
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include <array>
#include "timer_queue.h"
void dhcp_start(timer_queue& timers, const std::array<uint8_t, 6>& mac);
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include <functional>
#include <span>
#include <vector>
#include "wire.h"
using handler_fn = std::vector<std::vector<uint8_t>> (*)(uint32_t message_id, std::span<const uint8_t> payload);
struct handler_entry {
int8_t type_id;
handler_fn handle;
};
template <typename Req, auto Fn>
std::vector<std::vector<uint8_t>> typed_handler(uint32_t message_id, std::span<const uint8_t> payload) {
msgpack::parser p(payload.data(), static_cast<int>(payload.size()));
Req req;
auto tup = req.as_tuple();
auto r = msgpack::unpack(p, tup);
if (!r) {
return {encode_response(message_id, DeviceError{1, "decode request ext_id=" +
std::to_string(Req::ext_id) + ": msgpack error " + std::to_string(static_cast<int>(r.error()))})};
}
return Fn(message_id, req);
}
void dispatch_init();
void dispatch_schedule_ms(uint32_t ms, std::function<void()> fn);
[[noreturn]] void dispatch_run(std::span<const handler_entry> handlers);
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
#include <span>
#include <string_view>
#include <vector>
#include "wire.h"
extern std::string_view firmware_name;
std::vector<std::vector<uint8_t>> handle_picoboot(uint32_t message_id, std::span<const uint8_t> payload);
std::vector<std::vector<uint8_t>> handle_info(uint32_t message_id, std::span<const uint8_t> payload);
std::vector<std::vector<uint8_t>> handle_log(uint32_t message_id, std::span<const uint8_t> payload);
+27
View File
@@ -367,6 +367,18 @@ public:
pack_result pack(const std::vector<uint8_t> &v) { return pack_bin(v); } pack_result pack(const std::vector<uint8_t> &v) { return pack_bin(v); }
template <typename T>
requires (!std::is_same_v<T, uint8_t>)
pack_result pack(const std::vector<T> &v) {
auto r = pack_array(v.size());
if (!r) return r;
for (auto& elem : v) {
r = r->get().pack(elem);
if (!r) return r;
}
return r;
}
template <size_t N> template <size_t N>
pack_result pack(const std::array<uint8_t, N> &v) { return pack_bin(v); } pack_result pack(const std::array<uint8_t, N> &v) { return pack_bin(v); }
@@ -744,6 +756,21 @@ inline result<parser> unpack(const parser &p, std::vector<uint8_t> &out) {
return p.next(); return p.next();
} }
template <typename T>
requires (!std::is_same_v<T, uint8_t>)
result<parser> unpack(const parser &p, std::vector<T> &out) {
auto cnt = p.count();
if (!cnt) return std::unexpected(cnt.error());
out.resize(*cnt);
result<parser> cur = p.first_item();
for (size_t i = 0; i < *cnt; i++) {
if (!cur) return cur;
cur = unpack(*cur, out[i]);
}
if (!cur) return cur;
return p.next();
}
template <typename... Ts, size_t... Is> template <typename... Ts, size_t... Is>
result<parser> unpack_tuple_elements(const parser &p, std::tuple<Ts...> &t, std::index_sequence<Is...>) { result<parser> unpack_tuple_elements(const parser &p, std::tuple<Ts...> &t, std::index_sequence<Is...>) {
result<parser> cur = p.first_item(); result<parser> cur = p.first_item();
+15
View File
@@ -1,3 +1,18 @@
#pragma once #pragma once
#include <array>
#include <cstdint>
#include <functional>
#include <span>
#include <vector>
struct net_state {
std::array<uint8_t, 6> mac;
std::array<uint8_t, 4> ip;
};
using net_handler = std::function<std::vector<std::vector<uint8_t>>(std::span<const uint8_t> payload)>;
bool net_init(); bool net_init();
const net_state& net_get_state();
void net_set_handler(net_handler handler);
void net_poll();
+17 -7
View File
@@ -3,9 +3,9 @@
#include <cstdint> #include <cstdint>
#include <span> #include <span>
template <uint16_t N> template <typename T, uint16_t N>
struct ring_buffer { struct ring_buffer {
std::array<uint8_t, N> data = {}; std::array<T, N> data = {};
uint16_t head = 0; uint16_t head = 0;
uint16_t tail = 0; uint16_t tail = 0;
@@ -13,13 +13,23 @@ struct ring_buffer {
uint16_t free() const { return N - used(); } uint16_t free() const { return N - used(); }
bool empty() const { return head == tail; } bool empty() const { return head == tail; }
void push(std::span<const uint8_t> src) { void push(std::span<const T> src) {
if (src.size() > free()) return; if (src.size() > free()) return;
for (auto b : src) for (auto& v : src)
data[(tail++) % N] = b; data[(tail++) % N] = v;
} }
uint16_t peek(std::span<uint8_t> dst) const { void push(const T& v) {
if (free() == 0) return;
data[(tail++) % N] = v;
}
void push_overwrite(const T& v) {
if (free() == 0) head++;
data[(tail++) % N] = v;
}
uint16_t peek(std::span<T> dst) const {
uint16_t len = dst.size() < used() ? dst.size() : used(); uint16_t len = dst.size() < used() ? dst.size() : used();
for (uint16_t i = 0; i < len; i++) for (uint16_t i = 0; i < len; i++)
dst[i] = data[(head + i) % N]; dst[i] = data[(head + i) % N];
@@ -34,7 +44,7 @@ struct ring_buffer {
} }
} }
std::span<const uint8_t> read_contiguous() const { std::span<const T> read_contiguous() const {
uint16_t offset = head % N; uint16_t offset = head % N;
uint16_t contig = N - offset; uint16_t contig = N - offset;
uint16_t pending = used(); uint16_t pending = used();
+1 -1
View File
@@ -6,7 +6,7 @@
#include "ring_buffer.h" #include "ring_buffer.h"
struct usb_cdc { struct usb_cdc {
ring_buffer<512> tx; ring_buffer<uint8_t, 8192> tx;
void send(std::span<const uint8_t> data) { void send(std::span<const uint8_t> data) {
tx.push(data); tx.push(data);
+68 -3
View File
@@ -47,8 +47,45 @@ struct ResponseInfo {
static constexpr int8_t ext_id = 5; static constexpr int8_t ext_id = 5;
std::array<uint8_t, 8> board_id; std::array<uint8_t, 8> board_id;
std::array<uint8_t, 6> mac; std::array<uint8_t, 6> mac;
auto as_tuple() const { return std::tie(board_id, mac); } std::array<uint8_t, 4> ip;
auto as_tuple() { return std::tie(board_id, mac); } std::string firmware_name;
auto as_tuple() const { return std::tie(board_id, mac, ip, firmware_name); }
auto as_tuple() { return std::tie(board_id, mac, ip, firmware_name); }
};
struct RequestLog {
static constexpr int8_t ext_id = 6;
auto as_tuple() const { return std::tie(); }
auto as_tuple() { return std::tie(); }
};
struct LogEntry {
uint32_t timestamp_us;
std::string message;
auto as_tuple() const { return std::tie(timestamp_us, message); }
auto as_tuple() { return std::tie(timestamp_us, message); }
};
struct ResponseLog {
static constexpr int8_t ext_id = 7;
std::vector<LogEntry> entries;
auto as_tuple() const { return std::tie(entries); }
auto as_tuple() { return std::tie(entries); }
};
struct RequestTest {
static constexpr int8_t ext_id = 127;
std::string name;
auto as_tuple() const { return std::tie(name); }
auto as_tuple() { return std::tie(name); }
};
struct ResponseTest {
static constexpr int8_t ext_id = 126;
bool pass;
std::vector<std::string> messages;
auto as_tuple() const { return std::tie(pass, messages); }
auto as_tuple() { return std::tie(pass, messages); }
}; };
static constexpr uint8_t hash_key[8] = {}; static constexpr uint8_t hash_key[8] = {};
@@ -56,6 +93,7 @@ static constexpr uint8_t hash_key[8] = {};
struct DecodedMessage { struct DecodedMessage {
uint32_t message_id; uint32_t message_id;
int8_t type_id; int8_t type_id;
std::vector<uint8_t> payload;
}; };
inline std::vector<uint8_t> pack_envelope(uint32_t message_id, const std::vector<uint8_t> &payload) { inline std::vector<uint8_t> pack_envelope(uint32_t message_id, const std::vector<uint8_t> &payload) {
@@ -87,10 +125,37 @@ inline msgpack::result<DecodedMessage> try_decode(const uint8_t *data, size_t le
auto ext = inner.get_ext(); auto ext = inner.get_ext();
if (!ext) return std::unexpected(ext.error()); if (!ext) return std::unexpected(ext.error());
return DecodedMessage{env.message_id, std::get<0>(*ext)}; auto& [type_id, ext_data] = *ext;
return DecodedMessage{env.message_id, type_id,
std::vector<uint8_t>(reinterpret_cast<const uint8_t*>(ext_data.data()),
reinterpret_cast<const uint8_t*>(ext_data.data()) + ext_data.size())};
} }
template <size_t N> template <size_t N>
inline msgpack::result<DecodedMessage> try_decode(const static_vector<uint8_t, N> &buf) { inline msgpack::result<DecodedMessage> try_decode(const static_vector<uint8_t, N> &buf) {
return try_decode(buf.data(), buf.size()); return try_decode(buf.data(), buf.size());
} }
template <typename T>
inline msgpack::result<T> decode_response(const uint8_t *data, size_t len) {
msgpack::parser p(data, static_cast<int>(len));
Envelope env;
auto r = msgpack::unpack(p, env);
if (!r) return std::unexpected(r.error());
uint32_t expected = halfsiphash::hash32(env.payload.data(), env.payload.size(), hash_key);
if (env.checksum != expected) return std::unexpected(msgpack::error_code::invalid);
msgpack::parser inner(env.payload.data(), static_cast<int>(env.payload.size()));
T out;
auto r2 = msgpack::unpack(inner, out);
if (!r2) return std::unexpected(r2.error());
return out;
}
inline std::vector<uint8_t> encode_request(uint32_t message_id, const auto &msg) {
msgpack::packer inner;
inner.pack(msg);
return pack_envelope(message_id, inner.get_payload());
}
-55
View File
@@ -1,55 +0,0 @@
#include "dhcp.h"
#include <span>
#include "pico/rand.h"
#include "w6300.h"
namespace dhcp_opt {
constexpr uint8_t message_type = 53;
constexpr uint8_t param_request = 55;
constexpr uint8_t end = 255;
constexpr uint8_t discover = 1;
constexpr uint8_t subnet_mask = 1;
}
struct __attribute__((packed)) dhcp_discover {
uint8_t op = 1;
uint8_t htype = 1;
uint8_t hlen = 6;
uint8_t hops = 0;
uint32_t xid = __builtin_bswap32(static_cast<uint32_t>(get_rand_64()));
uint16_t secs = 0;
uint16_t flags = __builtin_bswap16(0x8000);
std::array<uint8_t, 4> ciaddr = {};
std::array<uint8_t, 4> yiaddr = {};
std::array<uint8_t, 4> siaddr = {};
std::array<uint8_t, 4> giaddr = {};
std::array<uint8_t, 16> chaddr = {};
std::array<uint8_t, 64> sname = {};
std::array<uint8_t, 128> file = {};
std::array<uint8_t, 4> magic = {99, 130, 83, 99};
uint8_t opt_msg_type[3] = {dhcp_opt::message_type, 1, dhcp_opt::discover};
uint8_t opt_params[3] = {dhcp_opt::param_request, 1, dhcp_opt::subnet_mask};
uint8_t opt_end = dhcp_opt::end;
};
static_assert(sizeof(dhcp_discover) == 247);
static void send_discover(timer_queue& timers, const std::array<uint8_t, 6>& mac) {
auto sn = w6300::socket_id{0};
w6300::open_socket(sn, w6300::protocol::udp, w6300::port_num{68}, w6300::sock_flag::none);
dhcp_discover pkt;
std::copy(mac.begin(), mac.end(), pkt.chaddr.begin());
w6300::ip_address broadcast = {};
broadcast.ip = {255, 255, 255, 255};
broadcast.len = 4;
w6300::sendto(sn, std::span{reinterpret_cast<uint8_t*>(&pkt), sizeof(pkt)}, broadcast, w6300::port_num{67});
timers.schedule_ms(5000, [&timers, mac]() { send_discover(timers, mac); });
}
void dhcp_start(timer_queue& timers, const std::array<uint8_t, 6>& mac) {
send_discover(timers, mac);
}
+85
View File
@@ -0,0 +1,85 @@
#include "dispatch.h"
#include <unordered_map>
#include "pico/stdlib.h"
#include "pico/bootrom.h"
#include "tusb.h"
#include "wire.h"
#include "usb_cdc.h"
#include "timer_queue.h"
#include "net.h"
#include "debug_log.h"
#include "hardware/sync.h"
static timer_queue timers;
void dispatch_init() {
tusb_init();
net_init();
dlog("dispatch_init complete");
}
void dispatch_schedule_ms(uint32_t ms, std::function<void()> fn) {
timers.schedule_ms(ms, std::move(fn));
}
[[noreturn]] void dispatch_run(std::span<const handler_entry> handlers) {
std::unordered_map<int8_t, std::vector<std::vector<uint8_t>> (*)(uint32_t, std::span<const uint8_t>)> handler_map;
for (auto& entry : handlers) {
handler_map[entry.type_id] = entry.handle;
}
static usb_cdc usb;
static static_vector<uint8_t, 256> usb_rx_buf;
net_set_handler([&](std::span<const uint8_t> payload) -> std::vector<std::vector<uint8_t>> {
auto msg = try_decode(payload.data(), payload.size());
if (!msg) return {};
auto it = handler_map.find(msg->type_id);
if (it == handler_map.end()) return {};
return it->second(msg->message_id, msg->payload);
});
while (true) {
uint32_t save = save_and_disable_interrupts();
dlog_if_slow("tud_task", 1000, [&]{ tud_task(); });
dlog_if_slow("drain", 1000, [&]{ usb.drain(); });
dlog_if_slow("timers", 1000, [&]{ timers.run(); });
dlog_if_slow("net_poll", 1000, [&]{ net_poll(); });
while (tud_cdc_available()) {
uint8_t byte;
if (tud_cdc_read(&byte, 1) != 1) break;
usb_rx_buf.push_back(byte);
auto msg = try_decode(usb_rx_buf);
if (!msg) {
if (usb_rx_buf.full()) usb_rx_buf.clear();
continue;
}
usb_rx_buf.clear();
auto it = handler_map.find(msg->type_id);
if (it != handler_map.end()) {
for (auto& response : it->second(msg->message_id, msg->payload)) {
if (response.size() > usb.tx.free()) {
auto err = encode_response(msg->message_id,
DeviceError{2, "response too large: " + std::to_string(response.size())});
usb.send(err);
} else {
usb.send(response);
}
}
if (msg->type_id == RequestPICOBOOT::ext_id) {
sleep_ms(100);
reset_usb_boot(0, 1);
}
}
}
__wfi();
restore_interrupts(save);
}
}
+27
View File
@@ -0,0 +1,27 @@
#include "handlers.h"
#include "pico/unique_id.h"
#include "net.h"
#include "debug_log.h"
std::vector<std::vector<uint8_t>> handle_picoboot(uint32_t message_id, std::span<const uint8_t>) {
return {encode_response(message_id, ResponsePICOBOOT{})};
}
std::vector<std::vector<uint8_t>> handle_info(uint32_t message_id, std::span<const uint8_t>) {
ResponseInfo resp;
pico_unique_board_id_t uid;
pico_get_unique_board_id(&uid);
std::copy(uid.id, uid.id + 8, resp.board_id.begin());
auto& ns = net_get_state();
resp.mac = ns.mac;
resp.ip = ns.ip;
resp.firmware_name = firmware_name;
return {encode_response(message_id, resp)};
}
std::vector<std::vector<uint8_t>> handle_log(uint32_t message_id, std::span<const uint8_t>) {
ResponseLog resp;
for (auto& e : dlog_drain())
resp.entries.push_back(LogEntry{e.timestamp_us, std::move(e.message)});
return {encode_response(message_id, resp)};
}
+292 -8
View File
@@ -1,6 +1,264 @@
#include "net.h" #include "net.h"
#include <cstring>
#include "pico/unique_id.h" #include "pico/unique_id.h"
#include "pico/time.h"
#include "w6300.h" #include "w6300.h"
#include "debug_log.h"
using mac_addr = std::array<uint8_t, 6>;
using ip4_addr = std::array<uint8_t, 4>;
struct __attribute__((packed)) eth_header {
mac_addr dst;
mac_addr src;
uint16_t ethertype;
};
static_assert(sizeof(eth_header) == 14);
struct __attribute__((packed)) arp_packet {
eth_header eth;
uint16_t htype;
uint16_t ptype;
uint8_t hlen;
uint8_t plen;
uint16_t oper;
mac_addr sha;
ip4_addr spa;
mac_addr tha;
ip4_addr tpa;
};
static_assert(sizeof(arp_packet) == 42);
struct __attribute__((packed)) ipv4_header {
eth_header eth;
uint8_t ver_ihl;
uint8_t dscp_ecn;
uint16_t total_len;
uint16_t identification;
uint16_t flags_frag;
uint8_t ttl;
uint8_t protocol;
uint16_t checksum;
ip4_addr src;
ip4_addr dst;
size_t ip_header_len() const { return (ver_ihl & 0x0F) * 4; }
size_t ip_total_len() const { return __builtin_bswap16(total_len); }
const uint8_t* ip_start() const { return reinterpret_cast<const uint8_t*>(&ver_ihl); }
uint8_t* ip_start() { return reinterpret_cast<uint8_t*>(&ver_ihl); }
};
static_assert(sizeof(ipv4_header) == 34);
struct __attribute__((packed)) udp_header {
ipv4_header ip;
uint16_t src_port;
uint16_t dst_port;
uint16_t length;
uint16_t checksum;
};
static_assert(sizeof(udp_header) == 42);
struct __attribute__((packed)) icmp_echo {
uint8_t type;
uint8_t code;
uint16_t checksum;
uint16_t id;
uint16_t seq;
};
static_assert(sizeof(icmp_echo) == 8);
static constexpr uint16_t ETH_ARP = __builtin_bswap16(0x0806);
static constexpr uint16_t ETH_IPV4 = __builtin_bswap16(0x0800);
static constexpr uint16_t ARP_HTYPE_ETH = __builtin_bswap16(1);
static constexpr uint16_t ARP_PTYPE_IPV4 = __builtin_bswap16(0x0800);
static constexpr uint16_t ARP_OP_REQUEST = __builtin_bswap16(1);
static constexpr uint16_t ARP_OP_REPLY = __builtin_bswap16(2);
static constexpr uint16_t PICOMAP_PORT = __builtin_bswap16(28781);
static constexpr mac_addr MAC_BROADCAST = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
static constexpr ip4_addr IP_BROADCAST_ALL = {255, 255, 255, 255};
static constexpr ip4_addr IP_BROADCAST_SUBNET = {169, 254, 255, 255};
static net_state state;
static w6300::socket_id raw_socket{0};
static net_handler msg_handler;
static uint16_t ip_checksum(const void* data, size_t len) {
auto p = static_cast<const uint8_t*>(data);
uint32_t sum = 0;
for (size_t i = 0; i < len - 1; i += 2)
sum += (p[i] << 8) | p[i + 1];
if (len & 1)
sum += p[len - 1] << 8;
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
return __builtin_bswap16(~sum);
}
static bool mac_match(const mac_addr& dst) {
return dst == state.mac || dst == MAC_BROADCAST;
}
static bool ip_match(const ip4_addr& dst) {
return dst == state.ip;
}
static bool ip_match_or_broadcast(const ip4_addr& dst) {
return ip_match(dst) || dst == IP_BROADCAST_ALL || dst == IP_BROADCAST_SUBNET;
}
static void send_raw(const void* data, size_t len) {
dlog_if_slow("send_raw", 1000, [&]{
w6300::ip_address dummy = {};
w6300::sendto(raw_socket, std::span<const uint8_t>{static_cast<const uint8_t*>(data), len},
dummy, w6300::port_num{0});
});
}
static void handle_arp(const uint8_t* frame, size_t len) {
if (len < sizeof(arp_packet)) return;
auto& pkt = *reinterpret_cast<const arp_packet*>(frame);
if (pkt.htype != ARP_HTYPE_ETH) return;
if (pkt.ptype != ARP_PTYPE_IPV4) return;
if (pkt.hlen != 6 || pkt.plen != 4) return;
if (pkt.oper != ARP_OP_REQUEST) return;
if (!ip_match(pkt.tpa)) return;
arp_packet reply = {};
reply.eth.dst = pkt.eth.src;
reply.eth.src = state.mac;
reply.eth.ethertype = ETH_ARP;
reply.htype = ARP_HTYPE_ETH;
reply.ptype = ARP_PTYPE_IPV4;
reply.hlen = 6;
reply.plen = 4;
reply.oper = ARP_OP_REPLY;
reply.sha = state.mac;
reply.spa = state.ip;
reply.tha = pkt.sha;
reply.tpa = pkt.spa;
send_raw(&reply, sizeof(reply));
}
static void handle_udp(const uint8_t* frame, size_t len) {
if (len < sizeof(udp_header)) return;
auto& pkt = *reinterpret_cast<const udp_header*>(frame);
if (pkt.dst_port != PICOMAP_PORT) return;
if (!ip_match_or_broadcast(pkt.ip.dst)) return;
if (!msg_handler) return;
size_t udp_len = __builtin_bswap16(pkt.length);
if (udp_len < 8) return;
if (sizeof(eth_header) + pkt.ip.ip_total_len() < sizeof(udp_header) + udp_len - 8) return;
auto* payload = frame + sizeof(udp_header);
size_t payload_len = udp_len - 8;
auto responses = msg_handler(std::span<const uint8_t>{payload, payload_len});
for (auto& resp : responses) {
uint8_t reply_buf[1514];
size_t udp_data_len = resp.size();
size_t ip_total = 20 + 8 + udp_data_len;
size_t reply_len = sizeof(eth_header) + ip_total;
if (reply_len > sizeof(reply_buf)) continue;
auto& rip = *reinterpret_cast<ipv4_header*>(reply_buf);
rip.eth.dst = pkt.ip.eth.src;
rip.eth.src = state.mac;
rip.eth.ethertype = ETH_IPV4;
rip.ver_ihl = 0x45;
rip.dscp_ecn = 0;
rip.total_len = __builtin_bswap16(ip_total);
rip.identification = 0;
rip.flags_frag = 0;
rip.ttl = 64;
rip.protocol = 17;
rip.checksum = 0;
rip.src = state.ip;
rip.dst = pkt.ip.src;
rip.checksum = ip_checksum(rip.ip_start(), 20);
auto& rudp = *reinterpret_cast<udp_header*>(reply_buf);
rudp.src_port = PICOMAP_PORT;
rudp.dst_port = pkt.src_port;
rudp.length = __builtin_bswap16(8 + udp_data_len);
rudp.checksum = 0;
memcpy(reply_buf + sizeof(udp_header), resp.data(), udp_data_len);
send_raw(reply_buf, reply_len);
}
}
static void handle_icmp(const uint8_t* frame, size_t len) {
auto& ip = *reinterpret_cast<const ipv4_header*>(frame);
size_t ip_hdr_len = ip.ip_header_len();
size_t ip_total = ip.ip_total_len();
if (sizeof(eth_header) + ip_total > len) return;
if (ip.protocol != 1) return;
if (!ip_match_or_broadcast(ip.dst)) return;
auto& icmp = *reinterpret_cast<const icmp_echo*>(frame + sizeof(eth_header) + ip_hdr_len);
size_t icmp_len = ip_total - ip_hdr_len;
if (icmp_len < sizeof(icmp_echo)) return;
if (icmp.type != 8) return;
uint8_t reply_buf[1514];
size_t reply_len = sizeof(eth_header) + ip_total;
if (reply_len > sizeof(reply_buf)) return;
memcpy(reply_buf, frame, reply_len);
auto& rip = *reinterpret_cast<ipv4_header*>(reply_buf);
rip.eth.dst = ip.eth.src;
rip.eth.src = state.mac;
rip.src = state.ip;
rip.dst = ip.src;
rip.ttl = 64;
rip.checksum = 0;
rip.checksum = ip_checksum(rip.ip_start(), ip_hdr_len);
auto& ricmp = *reinterpret_cast<icmp_echo*>(reply_buf + sizeof(eth_header) + ip_hdr_len);
ricmp.type = 0;
ricmp.checksum = 0;
ricmp.checksum = ip_checksum(&ricmp, icmp_len);
send_raw(reply_buf, reply_len);
}
static void handle_ipv4(const uint8_t* frame, size_t len) {
if (len < sizeof(ipv4_header)) return;
auto& ip = *reinterpret_cast<const ipv4_header*>(frame);
if ((ip.ver_ihl >> 4) != 4) return;
switch (ip.protocol) {
case 1:
handle_icmp(frame, len);
break;
case 17:
handle_udp(frame, len);
break;
}
}
static void process_frame(const uint8_t* frame, size_t len) {
if (len < sizeof(eth_header)) return;
auto& eth = *reinterpret_cast<const eth_header*>(frame);
if (!mac_match(eth.dst)) return;
switch (eth.ethertype) {
case ETH_ARP:
handle_arp(frame, len);
break;
case ETH_IPV4:
handle_ipv4(frame, len);
break;
}
}
bool net_init() { bool net_init() {
w6300::init_spi(); w6300::init_spi();
@@ -11,14 +269,40 @@ bool net_init() {
pico_unique_board_id_t uid; pico_unique_board_id_t uid;
pico_get_unique_board_id(&uid); pico_get_unique_board_id(&uid);
w6300::net_info info = {}; state.mac[0] = (uid.id[0] & 0xFC) | 0x02;
info.mac[0] = (uid.id[0] & 0xFC) | 0x02; state.mac[1] = uid.id[1];
info.mac[1] = uid.id[1]; state.mac[2] = uid.id[2];
info.mac[2] = uid.id[2]; state.mac[3] = uid.id[3];
info.mac[3] = uid.id[3]; state.mac[4] = uid.id[4];
info.mac[4] = uid.id[4]; state.mac[5] = uid.id[5];
info.mac[5] = uid.id[5];
w6300::init_net(info); state.ip[0] = 169;
state.ip[1] = 254;
state.ip[2] = state.mac[4];
state.ip[3] = state.mac[5];
w6300::open_socket(raw_socket, w6300::protocol::macraw, w6300::port_num{0}, w6300::sock_flag::none);
w6300::set_socket_io_mode(raw_socket, w6300::sock_io_mode::nonblock);
w6300::set_interrupt_mask(w6300::ik_sock_0);
return true; return true;
} }
const net_state& net_get_state() {
return state;
}
void net_set_handler(net_handler handler) {
msg_handler = std::move(handler);
}
void net_poll() {
w6300::clear_interrupt(w6300::ik_int_all);
if (w6300::get_socket_recv_buf(raw_socket) == 0) return;
static uint8_t rx_buf[1518];
w6300::ip_address dummy_addr = {};
w6300::port_num dummy_port{0};
auto result = w6300::recvfrom(raw_socket, std::span{rx_buf}, dummy_addr, dummy_port);
if (!result) return;
process_frame(rx_buf, *result);
}
+124 -50
View File
@@ -1,58 +1,132 @@
#include <unordered_map>
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "pico/bootrom.h" #include "pico/time.h"
#include "pico/unique_id.h" #include "hardware/gpio.h"
#include "tusb.h" #include "dispatch.h"
#include "wire.h" #include "handlers.h"
#include "usb_cdc.h"
#include "net.h"
#include "w6300.h" #include "w6300.h"
static usb_cdc usb; static constexpr uint8_t LED_PIN = 25;
static void led_toggle() {
gpio_xor_mask(1 << LED_PIN);
dispatch_schedule_ms(1000, led_toggle);
}
std::string_view firmware_name = "picomap_test";
static constexpr uint16_t PICOMAP_DISCOVERY_PORT = 28777;
static constexpr std::array<uint8_t, 4> picomap_discovery_ip = {239, 0, 112, 109};
static constexpr std::array<uint8_t, 6> picomap_discovery_mac = {
0x01, 0x00, 0x5e, 0x00, 0x70, 0x6d,
};
static w6300::socket_id test_socket{1};
static ResponseTest test_discovery() {
ResponseTest resp;
resp.pass = true;
w6300::ip_address dest = {};
std::copy(picomap_discovery_ip.begin(), picomap_discovery_ip.end(), dest.ip.begin());
dest.len = 4;
w6300::set_socket_dest_mac(test_socket, picomap_discovery_mac);
auto req = encode_request(0, RequestInfo{});
auto send_result = w6300::sendto(test_socket, std::span<const uint8_t>{req}, dest,
w6300::port_num{PICOMAP_DISCOVERY_PORT});
if (!send_result) {
resp.pass = false;
resp.messages.push_back("sendto: error " + std::to_string(static_cast<int>(send_result.error())));
return resp;
}
uint8_t rx_buf[512];
w6300::ip_address src_addr = {};
w6300::port_num src_port{0};
auto deadline = make_timeout_time_ms(5000);
std::expected<uint16_t, w6300::sock_error> recv_result = std::unexpected(w6300::sock_error::busy);
while (get_absolute_time() < deadline) {
recv_result = w6300::recvfrom(test_socket, std::span{rx_buf}, src_addr, src_port);
if (recv_result || recv_result.error() != w6300::sock_error::busy) break;
}
if (!recv_result) {
resp.pass = false;
if (recv_result.error() == w6300::sock_error::busy) {
resp.messages.push_back("recvfrom: timed out after 5s");
} else {
resp.messages.push_back("recvfrom: error " + std::to_string(static_cast<int>(recv_result.error())));
}
return resp;
}
resp.messages.push_back("received " + std::to_string(*recv_result) + " bytes from port " +
std::to_string(static_cast<uint16_t>(src_port)));
auto info = decode_response<ResponseInfo>(rx_buf, *recv_result);
if (!info) {
resp.pass = false;
resp.messages.push_back("decode: msgpack error " + std::to_string(static_cast<int>(info.error())));
return resp;
}
if (info->firmware_name.empty()) {
resp.pass = false;
resp.messages.push_back("firmware_name is empty");
} else {
resp.messages.push_back("firmware_name: " + info->firmware_name);
}
bool mac_zero = true;
for (auto b : info->mac) { if (b != 0) { mac_zero = false; break; } }
if (mac_zero) {
resp.pass = false;
resp.messages.push_back("mac is all zeros");
}
bool ip_zero = true;
for (auto b : info->ip) { if (b != 0) { ip_zero = false; break; } }
if (ip_zero) {
resp.pass = false;
resp.messages.push_back("ip is all zeros");
}
return resp;
}
using test_fn = ResponseTest (*)();
static const std::unordered_map<std::string_view, test_fn> tests = {
{"discovery", test_discovery},
};
static std::vector<std::vector<uint8_t>> handle_test(uint32_t message_id, const RequestTest& req) {
auto it = tests.find(req.name);
if (it == tests.end()) {
return {encode_response(message_id, ResponseTest{false, {"unknown test: " + req.name}})};
}
return {encode_response(message_id, it->second())};
}
static constexpr handler_entry handlers[] = {
{RequestPICOBOOT::ext_id, handle_picoboot},
{RequestInfo::ext_id, handle_info},
{RequestLog::ext_id, handle_log},
{RequestTest::ext_id, typed_handler<RequestTest, handle_test>},
};
int main() { int main() {
tusb_init(); dispatch_init();
net_init();
static static_vector<uint8_t, 256> rx_buf; gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
dispatch_schedule_ms(1000, led_toggle);
while (true) { dispatch_run(handlers);
tud_task();
usb.drain();
while (tud_cdc_available()) {
uint8_t byte;
if (tud_cdc_read(&byte, 1) != 1) break;
rx_buf.push_back(byte);
auto msg = try_decode(rx_buf);
if (!msg) {
if (rx_buf.full()) rx_buf.clear();
continue;
}
rx_buf.clear();
switch (msg->type_id) {
case RequestPICOBOOT::ext_id:
usb.send(encode_response(msg->message_id, ResponsePICOBOOT{}));
sleep_ms(100);
reset_usb_boot(0, 1);
break;
case RequestInfo::ext_id: {
ResponseInfo resp;
pico_unique_board_id_t uid;
pico_get_unique_board_id(&uid);
std::copy(uid.id, uid.id + 8, resp.board_id.begin());
auto ninfo = w6300::get_net_info();
resp.mac = ninfo.mac;
usb.send(encode_response(msg->message_id, resp));
break;
}
}
}
__wfi();
}
} }
+171 -166
View File
@@ -75,7 +75,9 @@ void pio_init() {
gpio_put(PIN_CS, true); gpio_put(PIN_CS, true);
gpio_init(PIN_INT); gpio_init(PIN_INT);
gpio_set_dir(PIN_INT, GPIO_IN); gpio_set_dir(PIN_INT, GPIO_IN);
gpio_set_pulls(PIN_INT, false, false); gpio_pull_up(PIN_INT);
gpio_set_irq_enabled_with_callback(PIN_INT, GPIO_IRQ_EDGE_FALL, true,
[](uint, uint32_t){});
pio_hw_t *pios[2] = {pio0, pio1}; pio_hw_t *pios[2] = {pio0, pio1};
uint pio_index = 1; uint pio_index = 1;
@@ -260,7 +262,7 @@ constexpr uint8_t PACK_COMPLETED = static_cast<uint8_t>(pack_info::completed);
constexpr uint8_t PACK_IPv6 = static_cast<uint8_t>(pack_info::ipv6); constexpr uint8_t PACK_IPv6 = static_cast<uint8_t>(pack_info::ipv6);
enum sockint_kind { SIK_CONNECTED = 1, SIK_DISCONNECTED = 2, SIK_RECEIVED = 4, SIK_TIMEOUT = 8, SIK_SENT = 16, SIK_ALL = 0x1F }; enum sockint_kind { sik_connected = 1, sik_disconnected = 2, sik_received = 4, sik_timeout = 8, sik_sent = 16, sik_all = 0x1F };
constexpr uint8_t TCPSOCK_MODE = static_cast<uint8_t>(tcp_sock_info::mode); constexpr uint8_t TCPSOCK_MODE = static_cast<uint8_t>(tcp_sock_info::mode);
@@ -352,9 +354,9 @@ constexpr uint32_t REG_SLHOPR = (0x420F << 8) + CREG_BLOCK;
constexpr uint32_t REG_SN_MR(uint8_t n) { return (0x0000 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_MR(uint8_t n) { return (0x0000 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_PSR(uint8_t n) { return (0x0004 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_PSR(uint8_t n) { return (0x0004 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_CR(uint8_t n) { return (0x0010 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_CR(uint8_t n) { return (0x0010 << 8) + SREG_BLOCK(n); }
constexpr uint32_t _SnREG_IR(uint8_t n) { return (0x0020 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_IR(uint8_t n) { return (0x0020 << 8) + SREG_BLOCK(n); }
constexpr uint32_t _SnREG_IMR(uint8_t n) { return (0x0024 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_IMR(uint8_t n) { return (0x0024 << 8) + SREG_BLOCK(n); }
constexpr uint32_t _SnREG_IRCLR(uint8_t n) { return (0x0028 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_IRCLR(uint8_t n) { return (0x0028 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_SR(uint8_t n) { return (0x0030 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_SR(uint8_t n) { return (0x0030 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_ESR(uint8_t n) { return (0x0031 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_ESR(uint8_t n) { return (0x0031 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_PNR(uint8_t n) { return (0x0100 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_PNR(uint8_t n) { return (0x0100 << 8) + SREG_BLOCK(n); }
@@ -371,8 +373,8 @@ constexpr uint32_t REG_SN_DIP4R(uint8_t n) { return REG_SN_DIPR(n); }
constexpr uint32_t REG_SN_DIP6R(uint8_t n) { return (0x0130 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_DIP6R(uint8_t n) { return (0x0130 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_DPORTR(uint8_t n) { return (0x0140 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_DPORTR(uint8_t n) { return (0x0140 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_MR2(uint8_t n) { return (0x0144 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_MR2(uint8_t n) { return (0x0144 << 8) + SREG_BLOCK(n); }
constexpr uint32_t _SnREG_RTR(uint8_t n) { return (0x0180 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_RTR(uint8_t n) { return (0x0180 << 8) + SREG_BLOCK(n); }
constexpr uint32_t _SnREG_RCR(uint8_t n) { return (0x0184 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_RCR(uint8_t n) { return (0x0184 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_KPALVTR(uint8_t n) { return (0x0188 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_KPALVTR(uint8_t n) { return (0x0188 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_TX_BSR(uint8_t n) { return (0x0200 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_TX_BSR(uint8_t n) { return (0x0200 << 8) + SREG_BLOCK(n); }
constexpr uint32_t REG_SN_TX_FSR(uint8_t n) { return (0x0204 << 8) + SREG_BLOCK(n); } constexpr uint32_t REG_SN_TX_FSR(uint8_t n) { return (0x0204 << 8) + SREG_BLOCK(n); }
@@ -448,10 +450,10 @@ constexpr uint8_t PHYCR0_10H = 0x07;
constexpr uint8_t PHYCR1_PWDN = 1 << 5; constexpr uint8_t PHYCR1_PWDN = 1 << 5;
constexpr uint8_t PHYCR1_TE = 1 << 3; constexpr uint8_t PHYCR1_TE = 1 << 3;
constexpr uint8_t PHYCR1_RST = 1 << 0; constexpr uint8_t PHYCR1_RST = 1 << 0;
constexpr uint8_t NETxMR_UNRB = 1 << 3; constexpr uint8_t NETX_MR_UNRB = 1 << 3;
constexpr uint8_t NETxMR_PARP = 1 << 2; constexpr uint8_t NETX_MR_PARP = 1 << 2;
constexpr uint8_t NETxMR_RSTB = 1 << 1; constexpr uint8_t NETX_MR_RSTB = 1 << 1;
constexpr uint8_t NETxMR_PB = 1 << 0; constexpr uint8_t NETX_MR_PB = 1 << 0;
constexpr uint8_t NETMR_ANB = 1 << 5; constexpr uint8_t NETMR_ANB = 1 << 5;
constexpr uint8_t NETMR_M6B = 1 << 4; constexpr uint8_t NETMR_M6B = 1 << 4;
constexpr uint8_t NETMR_WOL = 1 << 2; constexpr uint8_t NETMR_WOL = 1 << 2;
@@ -460,51 +462,51 @@ constexpr uint8_t NETMR_IP4B = 1 << 0;
constexpr uint8_t NETMR2_DHAS = 1 << 7; constexpr uint8_t NETMR2_DHAS = 1 << 7;
constexpr uint8_t NETMR2_DHAS_ARP = 1 << 7; constexpr uint8_t NETMR2_DHAS_ARP = 1 << 7;
constexpr uint8_t NETMR2_DHAS_ETH = 0 << 7; constexpr uint8_t NETMR2_DHAS_ETH = 0 << 7;
constexpr uint8_t NETMR2_PPPoE = 1 << 0; constexpr uint8_t NETMR2_PPPOE = 1 << 0;
constexpr uint8_t ICMP6BLKR_PING6 = 1 << 4; constexpr uint8_t ICMP6BLKR_PING6 = 1 << 4;
constexpr uint8_t ICMP6BLKR_MLD = 1 << 3; constexpr uint8_t ICMP6BLKR_MLD = 1 << 3;
constexpr uint8_t ICMP6BLKR_RA = 1 << 2; constexpr uint8_t ICMP6BLKR_RA = 1 << 2;
constexpr uint8_t ICMP6BLKR_NA = 1 << 1; constexpr uint8_t ICMP6BLKR_NA = 1 << 1;
constexpr uint8_t ICMP6BLKR_NS = 1 << 0; constexpr uint8_t ICMP6BLKR_NS = 1 << 0;
constexpr uint8_t Sn_MR_MULTI = 1 << 7; constexpr uint8_t SN_MR_MULTI = 1 << 7;
constexpr uint8_t Sn_MR_MF = 1 << 7; constexpr uint8_t SN_MR_MF = 1 << 7;
constexpr uint8_t Sn_MR_BRDB = 1 << 6; constexpr uint8_t SN_MR_BRDB = 1 << 6;
constexpr uint8_t Sn_MR_FPSH = 1 << 6; constexpr uint8_t SN_MR_FPSH = 1 << 6;
constexpr uint8_t Sn_MR_ND = 1 << 5; constexpr uint8_t SN_MR_ND = 1 << 5;
constexpr uint8_t Sn_MR_MC = 1 << 5; constexpr uint8_t SN_MR_MC = 1 << 5;
constexpr uint8_t Sn_MR_SMB = 1 << 5; constexpr uint8_t SN_MR_SMB = 1 << 5;
constexpr uint8_t Sn_MR_MMB = 1 << 5; constexpr uint8_t SN_MR_MMB = 1 << 5;
constexpr uint8_t Sn_MR_MMB4 = Sn_MR_MMB; constexpr uint8_t SN_MR_MMB4 = SN_MR_MMB;
constexpr uint8_t Sn_MR_UNIB = 1 << 4; constexpr uint8_t SN_MR_UNIB = 1 << 4;
constexpr uint8_t Sn_MR_MMB6 = 1 << 4; constexpr uint8_t SN_MR_MMB6 = 1 << 4;
constexpr uint8_t Sn_MR_CLOSE = 0x00; constexpr uint8_t SN_MR_CLOSE = 0x00;
constexpr uint8_t Sn_MR_TCP = 0x01; constexpr uint8_t SN_MR_TCP = 0x01;
constexpr uint8_t Sn_MR_TCP4 = Sn_MR_TCP; constexpr uint8_t SN_MR_TCP4 = SN_MR_TCP;
constexpr uint8_t Sn_MR_UDP = 0x02; constexpr uint8_t SN_MR_UDP = 0x02;
constexpr uint8_t Sn_MR_UDP4 = Sn_MR_UDP; constexpr uint8_t SN_MR_UDP4 = SN_MR_UDP;
constexpr uint8_t Sn_MR_IPRAW = 0x03; constexpr uint8_t SN_MR_IPRAW = 0x03;
constexpr uint8_t Sn_MR_IPRAW4 = Sn_MR_IPRAW; constexpr uint8_t SN_MR_IPRAW4 = SN_MR_IPRAW;
constexpr uint8_t Sn_MR_MACRAW = 0x07; constexpr uint8_t SN_MR_MACRAW = 0x07;
constexpr uint8_t Sn_MR_TCP6 = 0x09; constexpr uint8_t SN_MR_TCP6 = 0x09;
constexpr uint8_t Sn_MR_UDP6 = 0x0A; constexpr uint8_t SN_MR_UDP6 = 0x0A;
constexpr uint8_t Sn_MR_IPRAW6 = 0x0B; constexpr uint8_t SN_MR_IPRAW6 = 0x0B;
constexpr uint8_t Sn_MR_TCPD = 0x0D; constexpr uint8_t SN_MR_TCPD = 0x0D;
constexpr uint8_t Sn_MR_UDPD = 0x0E; constexpr uint8_t SN_MR_UDPD = 0x0E;
constexpr uint8_t Sn_CR_OPEN = 0x01; constexpr uint8_t SN_CR_OPEN = 0x01;
constexpr uint8_t Sn_CR_LISTEN = 0x02; constexpr uint8_t SN_CR_LISTEN = 0x02;
constexpr uint8_t Sn_CR_CONNECT = 0x04; constexpr uint8_t SN_CR_CONNECT = 0x04;
constexpr uint8_t Sn_CR_CONNECT6 = 0x84; constexpr uint8_t SN_CR_CONNECT6 = 0x84;
constexpr uint8_t Sn_CR_DISCON = 0x08; constexpr uint8_t SN_CR_DISCON = 0x08;
constexpr uint8_t Sn_CR_CLOSE = 0x10; constexpr uint8_t SN_CR_CLOSE = 0x10;
constexpr uint8_t Sn_CR_SEND = 0x20; constexpr uint8_t SN_CR_SEND = 0x20;
constexpr uint8_t Sn_CR_SEND6 = 0xA0; constexpr uint8_t SN_CR_SEND6 = 0xA0;
constexpr uint8_t Sn_CR_SEND_KEEP = 0x22; constexpr uint8_t SN_CR_SEND_KEEP = 0x22;
constexpr uint8_t Sn_CR_RECV = 0x40; constexpr uint8_t SN_CR_RECV = 0x40;
constexpr uint8_t SnREG_IRSENDOK = 0x10; constexpr uint8_t SN_IR_SENDOK = 0x10;
constexpr uint8_t SnREG_IRTIMEOUT = 0x08; constexpr uint8_t SN_IR_TIMEOUT = 0x08;
constexpr uint8_t SnREG_IRRECV = 0x04; constexpr uint8_t SN_IR_RECV = 0x04;
constexpr uint8_t SnREG_IRDISCON = 0x02; constexpr uint8_t SN_IR_DISCON = 0x02;
constexpr uint8_t SnREG_IRCON = 0x01; constexpr uint8_t SN_IR_CON = 0x01;
constexpr uint8_t SOCK_CLOSED = 0x00; constexpr uint8_t SOCK_CLOSED = 0x00;
constexpr uint8_t SOCK_INIT = 0x13; constexpr uint8_t SOCK_INIT = 0x13;
constexpr uint8_t SOCK_LISTEN = 0x14; constexpr uint8_t SOCK_LISTEN = 0x14;
@@ -520,19 +522,19 @@ constexpr uint8_t SOCK_IPRAW4 = 0x32;
constexpr uint8_t SOCK_IPRAW = SOCK_IPRAW4; constexpr uint8_t SOCK_IPRAW = SOCK_IPRAW4;
constexpr uint8_t SOCK_IPRAW6 = 0x33; constexpr uint8_t SOCK_IPRAW6 = 0x33;
constexpr uint8_t SOCK_MACRAW = 0x42; constexpr uint8_t SOCK_MACRAW = 0x42;
constexpr uint8_t Sn_ESR_TCPM = 1 << 2; constexpr uint8_t SN_ESR_TCPM = 1 << 2;
constexpr uint8_t Sn_ESR_TCPM_IPV4 = 0 << 2; constexpr uint8_t SN_ESR_TCPM_IPV4 = 0 << 2;
constexpr uint8_t Sn_ESR_TCPM_IPV6 = 1 << 2; constexpr uint8_t SN_ESR_TCPM_IPV6 = 1 << 2;
constexpr uint8_t Sn_ESR_TCPOP = 1 << 1; constexpr uint8_t SN_ESR_TCPOP = 1 << 1;
constexpr uint8_t Sn_ESR_TCPOP_SVR = 0 << 1; constexpr uint8_t SN_ESR_TCPOP_SVR = 0 << 1;
constexpr uint8_t Sn_ESR_TCPOP_CLT = 1 << 1; constexpr uint8_t SN_ESR_TCPOP_CLT = 1 << 1;
constexpr uint8_t Sn_ESR_IP6T = 1 << 0; constexpr uint8_t SN_ESR_IP6T = 1 << 0;
constexpr uint8_t Sn_ESR_IP6T_LLA = 0 << 0; constexpr uint8_t SN_ESR_IP6T_LLA = 0 << 0;
constexpr uint8_t Sn_ESR_IP6T_GUA = 1 << 0; constexpr uint8_t SN_ESR_IP6T_GUA = 1 << 0;
constexpr uint8_t Sn_MR2_DHAM = 1 << 1; constexpr uint8_t SN_MR2_DHAM = 1 << 1;
constexpr uint8_t Sn_MR2_DHAM_AUTO = 0 << 1; constexpr uint8_t SN_MR2_DHAM_AUTO = 0 << 1;
constexpr uint8_t Sn_MR2_DHAM_MANUAL = 1 << 1; constexpr uint8_t SN_MR2_DHAM_MANUAL = 1 << 1;
constexpr uint8_t Sn_MR2_FARP = 1 << 0; constexpr uint8_t SN_MR2_FARP = 1 << 0;
constexpr uint8_t PHYRAR_BMCR = 0x00; constexpr uint8_t PHYRAR_BMCR = 0x00;
constexpr uint8_t PHYRAR_BMSR = 0x01; constexpr uint8_t PHYRAR_BMSR = 0x01;
constexpr uint16_t BMCR_RST = 1 << 15; constexpr uint16_t BMCR_RST = 1 << 15;
@@ -560,10 +562,10 @@ constexpr uint16_t BMSR_EXT_CAPA = 1 << 0;
void cris_enter(); void cris_enter();
void cris_exit(); void cris_exit();
uint8_t reg_read(uint32_t AddrSel); uint8_t reg_read(uint32_t addr_sel);
void reg_write(uint32_t AddrSel, uint8_t wb); void reg_write(uint32_t addr_sel, uint8_t wb);
void reg_read_buf(uint32_t AddrSel, uint8_t* pBuf, datasize_t len); void reg_read_buf(uint32_t addr_sel, uint8_t* buf, datasize_t len);
void reg_write_buf(uint32_t AddrSel, uint8_t* pBuf, datasize_t len); void reg_write_buf(uint32_t addr_sel, uint8_t* buf, datasize_t len);
uint16_t get_sn_tx_fsr(uint8_t sn); uint16_t get_sn_tx_fsr(uint8_t sn);
uint16_t get_sn_rx_rsr(uint8_t sn); uint16_t get_sn_rx_rsr(uint8_t sn);
@@ -733,10 +735,10 @@ inline void set_sn_psr(uint8_t sn, uint8_t v) { reg_write(REG_SN_PSR(sn), v); }
inline uint8_t get_sn_psr(uint8_t sn) { return reg_read(REG_SN_PSR(sn)); } inline uint8_t get_sn_psr(uint8_t sn) { return reg_read(REG_SN_PSR(sn)); }
inline void set_sn_cr(uint8_t sn, uint8_t v) { reg_write(REG_SN_CR(sn), v); } inline void set_sn_cr(uint8_t sn, uint8_t v) { reg_write(REG_SN_CR(sn), v); }
inline uint8_t get_sn_cr(uint8_t sn) { return reg_read(REG_SN_CR(sn)); } inline uint8_t get_sn_cr(uint8_t sn) { return reg_read(REG_SN_CR(sn)); }
inline uint8_t get_sn_ir(uint8_t sn) { return reg_read(_SnREG_IR(sn)); } inline uint8_t get_sn_ir(uint8_t sn) { return reg_read(REG_SN_IR(sn)); }
inline void set_sn_imr(uint8_t sn, uint8_t v) { reg_write(_SnREG_IMR(sn), v); } inline void set_sn_imr(uint8_t sn, uint8_t v) { reg_write(REG_SN_IMR(sn), v); }
inline uint8_t get_sn_imr(uint8_t sn) { return reg_read(_SnREG_IMR(sn)); } inline uint8_t get_sn_imr(uint8_t sn) { return reg_read(REG_SN_IMR(sn)); }
inline void set_sn_irclr(uint8_t sn, uint8_t v) { reg_write(_SnREG_IRCLR(sn), v); } inline void set_sn_irclr(uint8_t sn, uint8_t v) { reg_write(REG_SN_IRCLR(sn), v); }
inline void set_sn_ir(uint8_t sn, uint8_t v) { set_sn_irclr(sn, v); } inline void set_sn_ir(uint8_t sn, uint8_t v) { set_sn_irclr(sn, v); }
inline uint8_t get_sn_sr(uint8_t sn) { return reg_read(REG_SN_SR(sn)); } inline uint8_t get_sn_sr(uint8_t sn) { return reg_read(REG_SN_SR(sn)); }
inline uint8_t get_sn_esr(uint8_t sn) { return reg_read(REG_SN_ESR(sn)); } inline uint8_t get_sn_esr(uint8_t sn) { return reg_read(REG_SN_ESR(sn)); }
@@ -787,12 +789,12 @@ inline void set_sn_dport(uint8_t sn, uint16_t v) { set_sn_dportr(sn, v); }
inline void set_sn_mr2(uint8_t sn, uint8_t v) { reg_write(REG_SN_MR2(sn), v); } inline void set_sn_mr2(uint8_t sn, uint8_t v) { reg_write(REG_SN_MR2(sn), v); }
inline uint8_t get_sn_mr2(uint8_t sn) { return reg_read(REG_SN_MR2(sn)); } inline uint8_t get_sn_mr2(uint8_t sn) { return reg_read(REG_SN_MR2(sn)); }
inline void set_sn_rtr(uint8_t sn, uint16_t v) { inline void set_sn_rtr(uint8_t sn, uint16_t v) {
reg_write(_SnREG_RTR(sn), (uint8_t)(v >> 8)); reg_write(REG_SN_RTR(sn), (uint8_t)(v >> 8));
reg_write(offset_inc(_SnREG_RTR(sn), 1), (uint8_t)v); reg_write(offset_inc(REG_SN_RTR(sn), 1), (uint8_t)v);
} }
inline uint16_t get_sn_rtr(uint8_t sn) { return (((uint16_t)reg_read(_SnREG_RTR(sn))) << 8) + reg_read(offset_inc(_SnREG_RTR(sn), 1)); } inline uint16_t get_sn_rtr(uint8_t sn) { return (((uint16_t)reg_read(REG_SN_RTR(sn))) << 8) + reg_read(offset_inc(REG_SN_RTR(sn), 1)); }
inline void set_sn_rcr(uint8_t sn, uint8_t v) { reg_write(_SnREG_RCR(sn), v); } inline void set_sn_rcr(uint8_t sn, uint8_t v) { reg_write(REG_SN_RCR(sn), v); }
inline uint8_t get_sn_rcr(uint8_t sn) { return reg_read(_SnREG_RCR(sn)); } inline uint8_t get_sn_rcr(uint8_t sn) { return reg_read(REG_SN_RCR(sn)); }
inline void set_sn_kpalvtr(uint8_t sn, uint8_t v) { reg_write(REG_SN_KPALVTR(sn), v); } inline void set_sn_kpalvtr(uint8_t sn, uint8_t v) { reg_write(REG_SN_KPALVTR(sn), v); }
inline uint8_t get_sn_kpalvtr(uint8_t sn) { return reg_read(REG_SN_KPALVTR(sn)); } inline uint8_t get_sn_kpalvtr(uint8_t sn) { return reg_read(REG_SN_KPALVTR(sn)); }
inline void set_sn_tx_bsr(uint8_t sn, uint8_t v) { reg_write(REG_SN_TX_BSR(sn), v); } inline void set_sn_tx_bsr(uint8_t sn, uint8_t v) { reg_write(REG_SN_TX_BSR(sn), v); }
@@ -822,11 +824,9 @@ inline uint16_t get_sn_rx_wr(uint8_t sn) { return ((uint16_t)reg_read(REG_SN_RX_
static critical_section_t g_cris_sec; static critical_section_t g_cris_sec;
void cris_enter() { void cris_enter() {
critical_section_enter_blocking(&g_cris_sec);
} }
void cris_exit() { void cris_exit() {
critical_section_exit(&g_cris_sec);
} }
static uint8_t make_opcode(uint32_t addr, uint8_t rw) { static uint8_t make_opcode(uint32_t addr, uint8_t rw) {
@@ -837,36 +837,36 @@ static uint16_t make_addr(uint32_t addr) {
return static_cast<uint16_t>((addr & 0x00FFFF00) >> 8); return static_cast<uint16_t>((addr & 0x00FFFF00) >> 8);
} }
void reg_write(uint32_t AddrSel, uint8_t wb) { void reg_write(uint32_t addr_sel, uint8_t wb) {
cris_enter(); cris_enter();
pio_frame_start(); pio_frame_start();
pio_write(make_opcode(AddrSel, SPI_WRITE), make_addr(AddrSel), &wb, 1); pio_write(make_opcode(addr_sel, SPI_WRITE), make_addr(addr_sel), &wb, 1);
pio_frame_end(); pio_frame_end();
cris_exit(); cris_exit();
} }
uint8_t reg_read(uint32_t AddrSel) { uint8_t reg_read(uint32_t addr_sel) {
uint8_t ret[2] = {0}; uint8_t ret[2] = {0};
cris_enter(); cris_enter();
pio_frame_start(); pio_frame_start();
pio_read(make_opcode(AddrSel, SPI_READ), make_addr(AddrSel), ret, 1); pio_read(make_opcode(addr_sel, SPI_READ), make_addr(addr_sel), ret, 1);
pio_frame_end(); pio_frame_end();
cris_exit(); cris_exit();
return ret[0]; return ret[0];
} }
void reg_write_buf(uint32_t AddrSel, uint8_t* pBuf, datasize_t len) { void reg_write_buf(uint32_t addr_sel, uint8_t* buf, datasize_t len) {
cris_enter(); cris_enter();
pio_frame_start(); pio_frame_start();
pio_write(make_opcode(AddrSel, SPI_WRITE), make_addr(AddrSel), pBuf, len); pio_write(make_opcode(addr_sel, SPI_WRITE), make_addr(addr_sel), buf, len);
pio_frame_end(); pio_frame_end();
cris_exit(); cris_exit();
} }
void reg_read_buf(uint32_t AddrSel, uint8_t* pBuf, datasize_t len) { void reg_read_buf(uint32_t addr_sel, uint8_t* buf, datasize_t len) {
cris_enter(); cris_enter();
pio_frame_start(); pio_frame_start();
pio_read(make_opcode(AddrSel, SPI_READ), make_addr(AddrSel), pBuf, len); pio_read(make_opcode(addr_sel, SPI_READ), make_addr(addr_sel), buf, len);
pio_frame_end(); pio_frame_end();
cris_exit(); cris_exit();
} }
@@ -959,19 +959,19 @@ int8_t init_buffers(std::span<const uint8_t> txsize, std::span<const uint8_t> rx
soft_reset(); soft_reset();
if (!txsize.empty()) { if (!txsize.empty()) {
int8_t tmp = 0; int8_t tmp = 0;
for (int i = 0; i < SOCK_COUNT; i++) { for (int i = 0; i < sock_count; i++) {
tmp += txsize[i]; tmp += txsize[i];
if (tmp > 32) return -1; if (tmp > 32) return -1;
} }
for (int i = 0; i < SOCK_COUNT; i++) set_sn_txbuf_size(i, txsize[i]); for (int i = 0; i < sock_count; i++) set_sn_txbuf_size(i, txsize[i]);
} }
if (!rxsize.empty()) { if (!rxsize.empty()) {
int8_t tmp = 0; int8_t tmp = 0;
for (int i = 0; i < SOCK_COUNT; i++) { for (int i = 0; i < sock_count; i++) {
tmp += rxsize[i]; tmp += rxsize[i];
if (tmp > 32) return -1; if (tmp > 32) return -1;
} }
for (int i = 0; i < SOCK_COUNT; i++) set_sn_rxbuf_size(i, rxsize[i]); for (int i = 0; i < sock_count; i++) set_sn_rxbuf_size(i, rxsize[i]);
} }
return 0; return 0;
} }
@@ -979,7 +979,7 @@ int8_t init_buffers(std::span<const uint8_t> txsize, std::span<const uint8_t> rx
void clear_interrupt(intr_kind intr) { void clear_interrupt(intr_kind intr) {
set_irclr((uint8_t)intr); set_irclr((uint8_t)intr);
uint8_t sir = (uint8_t)((uint16_t)intr >> 8); uint8_t sir = (uint8_t)((uint16_t)intr >> 8);
for (int i = 0; i < SOCK_COUNT; i++) for (int i = 0; i < sock_count; i++)
if (sir & (1 << i)) set_sn_irclr(i, 0xFF); if (sir & (1 << i)) set_sn_irclr(i, 0xFF);
set_slirclr((uint8_t)((uint32_t)intr >> 16)); set_slirclr((uint8_t)((uint32_t)intr >> 16));
} }
@@ -1179,16 +1179,16 @@ constexpr uint16_t SOCK_ANY_PORT_NUM = 0xC000;
static uint16_t sock_any_port = SOCK_ANY_PORT_NUM; static uint16_t sock_any_port = SOCK_ANY_PORT_NUM;
static uint16_t sock_io_mode_bits = 0; static uint16_t sock_io_mode_bits = 0;
static uint16_t sock_is_sending = 0; static uint16_t sock_is_sending = 0;
static uint16_t sock_remained_size[SOCK_COUNT] = {0,}; static uint16_t sock_remained_size[sock_count] = {0,};
uint8_t sock_pack_info[SOCK_COUNT] = {0,}; uint8_t sock_pack_info[sock_count] = {0,};
#define FAIL(e) return std::unexpected(sock_error::e) #define FAIL(e) return std::unexpected(sock_error::e)
#define CHECK_SOCKNUM() do { if(sn >= SOCK_COUNT) FAIL(sock_num); } while(0) #define CHECK_SOCKNUM() do { if(sn >= sock_count) FAIL(sock_num); } while(0)
#define CHECK_SOCKMODE(mode) do { if((get_sn_mr(sn) & 0x0F) != mode) FAIL(sock_mode); } while(0) #define CHECK_SOCKMODE(mode) do { if((get_sn_mr(sn) & 0x0F) != mode) FAIL(sock_mode); } while(0)
#define CHECK_TCPMODE() do { if((get_sn_mr(sn) & 0x03) != 0x01) FAIL(sock_mode); } while(0) #define CHECK_TCPMODE() do { if((get_sn_mr(sn) & 0x03) != 0x01) FAIL(sock_mode); } while(0)
#define CHECK_UDPMODE() do { if((get_sn_mr(sn) & 0x03) != 0x02) FAIL(sock_mode); } while(0) #define CHECK_UDPMODE() do { if((get_sn_mr(sn) & 0x03) != 0x02) FAIL(sock_mode); } while(0)
#define CHECK_IPMODE() do { if((get_sn_mr(sn) & 0x07) != 0x03) FAIL(sock_mode); } while(0) #define CHECK_IPMODE() do { if((get_sn_mr(sn) & 0x07) != 0x03) FAIL(sock_mode); } while(0)
#define CHECK_DGRAMMODE() do { if(get_sn_mr(sn) == Sn_MR_CLOSED) FAIL(sock_mode); if((get_sn_mr(sn) & 0x03) == 0x01) FAIL(sock_mode); } while(0) #define CHECK_DGRAMMODE() do { if(get_sn_mr(sn) == SN_MR_CLOSED) FAIL(sock_mode); if((get_sn_mr(sn) & 0x03) == 0x01) FAIL(sock_mode); } while(0)
#define CHECK_SOCKINIT() do { if((get_sn_sr(sn) != SOCK_INIT)) FAIL(sock_init); } while(0) #define CHECK_SOCKINIT() do { if((get_sn_sr(sn) != SOCK_INIT)) FAIL(sock_init); } while(0)
#define CHECK_SOCKDATA() do { if(len == 0) FAIL(data_len); } while(0) #define CHECK_SOCKDATA() do { if(len == 0) FAIL(data_len); } while(0)
#define CHECK_IPZERO(addr, addrlen) do { uint16_t ipzero=0; for(uint8_t i=0; i<addrlen; i++) ipzero += (uint16_t)addr[i]; if(ipzero == 0) FAIL(ip_invalid); } while(0) #define CHECK_IPZERO(addr, addrlen) do { uint16_t ipzero=0; for(uint8_t i=0; i<addrlen; i++) ipzero += (uint16_t)addr[i]; if(ipzero == 0) FAIL(ip_invalid); } while(0)
@@ -1201,26 +1201,26 @@ std::expected<socket_id, sock_error> open_socket(socket_id sid, protocol proto,
uint8_t taddr[16]; uint8_t taddr[16];
CHECK_SOCKNUM(); CHECK_SOCKNUM();
switch (pr & 0x0F) { switch (pr & 0x0F) {
case Sn_MR_TCP4: case SN_MR_TCP4:
get_sipr(taddr); get_sipr(taddr);
CHECK_IPZERO(taddr, 4); CHECK_IPZERO(taddr, 4);
break; break;
case Sn_MR_TCP6: case SN_MR_TCP6:
get_llar(taddr); get_llar(taddr);
CHECK_IPZERO(taddr, 16); CHECK_IPZERO(taddr, 16);
break; break;
case Sn_MR_TCPD: case SN_MR_TCPD:
get_sipr(taddr); get_sipr(taddr);
CHECK_IPZERO(taddr, 4); CHECK_IPZERO(taddr, 4);
get_llar(taddr); get_llar(taddr);
CHECK_IPZERO(taddr, 16); CHECK_IPZERO(taddr, 16);
break; break;
case Sn_MR_UDP: case SN_MR_UDP:
case Sn_MR_UDP6: case SN_MR_UDP6:
case Sn_MR_UDPD: case SN_MR_UDPD:
case Sn_MR_MACRAW: case SN_MR_MACRAW:
case Sn_MR_IPRAW4: case SN_MR_IPRAW4:
case Sn_MR_IPRAW6: case SN_MR_IPRAW6:
break; break;
default: default:
FAIL(sock_mode); FAIL(sock_mode);
@@ -1229,16 +1229,16 @@ std::expected<socket_id, sock_error> open_socket(socket_id sid, protocol proto,
if (fl != 0) { if (fl != 0) {
switch (pr) { switch (pr) {
case Sn_MR_MACRAW: case SN_MR_MACRAW:
if ((fl & (Sn_MR2_DHAM | Sn_MR2_FARP)) != 0) FAIL(sock_flag); if ((fl & (SN_MR2_DHAM | SN_MR2_FARP)) != 0) FAIL(sock_flag);
break; break;
case Sn_MR_TCP4: case SN_MR_TCP4:
case Sn_MR_TCP6: case SN_MR_TCP6:
case Sn_MR_TCPD: case SN_MR_TCPD:
if ((fl & (Sn_MR_MULTI | Sn_MR_UNIB)) != 0) FAIL(sock_flag); if ((fl & (SN_MR_MULTI | SN_MR_UNIB)) != 0) FAIL(sock_flag);
break; break;
case Sn_MR_IPRAW4: case SN_MR_IPRAW4:
case Sn_MR_IPRAW6: case SN_MR_IPRAW6:
if (fl != 0) FAIL(sock_flag); if (fl != 0) FAIL(sock_flag);
break; break;
default: default:
@@ -1253,7 +1253,7 @@ std::expected<socket_id, sock_error> open_socket(socket_id sid, protocol proto,
if (sock_any_port == 0xFFF0) sock_any_port = SOCK_ANY_PORT_NUM; if (sock_any_port == 0xFFF0) sock_any_port = SOCK_ANY_PORT_NUM;
} }
set_sn_portr(sn, p); set_sn_portr(sn, p);
set_sn_cr(sn, Sn_CR_OPEN); set_sn_cr(sn, SN_CR_OPEN);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
sock_io_mode_bits &= ~(1 << sn); sock_io_mode_bits &= ~(1 << sn);
sock_io_mode_bits |= ((fl & (static_cast<uint8_t>(sock_flag::io_nonblock) >> 3)) << sn); sock_io_mode_bits |= ((fl & (static_cast<uint8_t>(sock_flag::io_nonblock) >> 3)) << sn);
@@ -1267,7 +1267,7 @@ std::expected<socket_id, sock_error> open_socket(socket_id sid, protocol proto,
std::expected<void, sock_error> close(socket_id sid) { std::expected<void, sock_error> close(socket_id sid) {
uint8_t sn = static_cast<uint8_t>(sid); uint8_t sn = static_cast<uint8_t>(sid);
CHECK_SOCKNUM(); CHECK_SOCKNUM();
set_sn_cr(sn, Sn_CR_CLOSE); set_sn_cr(sn, SN_CR_CLOSE);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
set_sn_ir(sn, 0xFF); set_sn_ir(sn, 0xFF);
sock_io_mode_bits &= ~(1 << sn); sock_io_mode_bits &= ~(1 << sn);
@@ -1283,7 +1283,7 @@ std::expected<void, sock_error> listen(socket_id sid) {
CHECK_SOCKNUM(); CHECK_SOCKNUM();
CHECK_TCPMODE(); CHECK_TCPMODE();
CHECK_SOCKINIT(); CHECK_SOCKINIT();
set_sn_cr(sn, Sn_CR_LISTEN); set_sn_cr(sn, SN_CR_LISTEN);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
while (get_sn_sr(sn) != SOCK_LISTEN) { while (get_sn_sr(sn) != SOCK_LISTEN) {
close(sid); close(sid);
@@ -1306,20 +1306,20 @@ std::expected<void, sock_error> connect(socket_id sid, const ip_address& addr, p
if (addrlen == 16) { if (addrlen == 16) {
if (get_sn_mr(sn) & 0x08) { if (get_sn_mr(sn) & 0x08) {
set_sn_dip6r(sn, const_cast<uint8_t*>(addr.ip.data())); set_sn_dip6r(sn, const_cast<uint8_t*>(addr.ip.data()));
set_sn_cr(sn, Sn_CR_CONNECT6); set_sn_cr(sn, SN_CR_CONNECT6);
} else { } else {
FAIL(sock_mode); FAIL(sock_mode);
} }
} else { } else {
if (get_sn_mr(sn) == Sn_MR_TCP6) FAIL(sock_mode); if (get_sn_mr(sn) == SN_MR_TCP6) FAIL(sock_mode);
set_sn_dipr(sn, const_cast<uint8_t*>(addr.ip.data())); set_sn_dipr(sn, const_cast<uint8_t*>(addr.ip.data()));
set_sn_cr(sn, Sn_CR_CONNECT); set_sn_cr(sn, SN_CR_CONNECT);
} }
while (get_sn_cr(sn)); while (get_sn_cr(sn));
if (sock_io_mode_bits & (1 << sn)) FAIL(busy); if (sock_io_mode_bits & (1 << sn)) FAIL(busy);
while (get_sn_sr(sn) != SOCK_ESTABLISHED) { while (get_sn_sr(sn) != SOCK_ESTABLISHED) {
if (get_sn_ir(sn) & SnREG_IRTIMEOUT) { if (get_sn_ir(sn) & SN_IR_TIMEOUT) {
set_sn_ir(sn, SnREG_IRTIMEOUT); set_sn_ir(sn, SN_IR_TIMEOUT);
FAIL(timeout); FAIL(timeout);
} }
if (get_sn_sr(sn) == SOCK_CLOSED) FAIL(sock_closed); if (get_sn_sr(sn) == SOCK_CLOSED) FAIL(sock_closed);
@@ -1332,12 +1332,12 @@ std::expected<void, sock_error> disconnect(socket_id sid) {
CHECK_SOCKNUM(); CHECK_SOCKNUM();
CHECK_TCPMODE(); CHECK_TCPMODE();
if (get_sn_sr(sn) != SOCK_CLOSED) { if (get_sn_sr(sn) != SOCK_CLOSED) {
set_sn_cr(sn, Sn_CR_DISCON); set_sn_cr(sn, SN_CR_DISCON);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
sock_is_sending &= ~(1 << sn); sock_is_sending &= ~(1 << sn);
if (sock_io_mode_bits & (1 << sn)) FAIL(busy); if (sock_io_mode_bits & (1 << sn)) FAIL(busy);
while (get_sn_sr(sn) != SOCK_CLOSED) { while (get_sn_sr(sn) != SOCK_CLOSED) {
if (get_sn_ir(sn) & SnREG_IRTIMEOUT) { if (get_sn_ir(sn) & SN_IR_TIMEOUT) {
close(sid); close(sid);
FAIL(timeout); FAIL(timeout);
} }
@@ -1366,17 +1366,17 @@ std::expected<uint16_t, sock_error> send(socket_id sid, std::span<const uint8_t>
} }
send_data(sn, const_cast<uint8_t*>(buf.data()), len); send_data(sn, const_cast<uint8_t*>(buf.data()), len);
if (sock_is_sending & (1 << sn)) { if (sock_is_sending & (1 << sn)) {
while (!(get_sn_ir(sn) & SnREG_IRSENDOK)) { while (!(get_sn_ir(sn) & SN_IR_SENDOK)) {
tmp = get_sn_sr(sn); tmp = get_sn_sr(sn);
if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT)) { if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT)) {
if ((tmp == SOCK_CLOSED) || (get_sn_ir(sn) & SnREG_IRTIMEOUT)) close(sid); if ((tmp == SOCK_CLOSED) || (get_sn_ir(sn) & SN_IR_TIMEOUT)) close(sid);
FAIL(sock_status); FAIL(sock_status);
} }
if (sock_io_mode_bits & (1 << sn)) FAIL(busy); if (sock_io_mode_bits & (1 << sn)) FAIL(busy);
} }
set_sn_ir(sn, SnREG_IRSENDOK); set_sn_ir(sn, SN_IR_SENDOK);
} }
set_sn_cr(sn, Sn_CR_SEND); set_sn_cr(sn, SN_CR_SEND);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
sock_is_sending |= (1 << sn); sock_is_sending |= (1 << sn);
return len; return len;
@@ -1389,7 +1389,7 @@ std::expected<uint16_t, sock_error> recv(socket_id sid, std::span<uint8_t> buf)
uint16_t recvsize = 0; uint16_t recvsize = 0;
CHECK_SOCKNUM(); CHECK_SOCKNUM();
CHECK_SOCKMODE(Sn_MR_TCP); CHECK_SOCKMODE(SN_MR_TCP);
CHECK_SOCKDATA(); CHECK_SOCKDATA();
recvsize = get_sn_rx_max(sn); recvsize = get_sn_rx_max(sn);
@@ -1416,7 +1416,7 @@ std::expected<uint16_t, sock_error> recv(socket_id sid, std::span<uint8_t> buf)
if (recvsize < len) len = recvsize; if (recvsize < len) len = recvsize;
recv_data(sn, buf.data(), len); recv_data(sn, buf.data(), len);
set_sn_cr(sn, Sn_CR_RECV); set_sn_cr(sn, SN_CR_RECV);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
return len; return len;
} }
@@ -1427,32 +1427,32 @@ std::expected<uint16_t, sock_error> sendto(socket_id sid, std::span<const uint8_
uint16_t len = buf.size(); uint16_t len = buf.size();
uint8_t addrlen = addr.len; uint8_t addrlen = addr.len;
uint8_t tmp = 0; uint8_t tmp = 0;
uint8_t tcmd = Sn_CR_SEND; uint8_t tcmd = SN_CR_SEND;
uint16_t freesize = 0; uint16_t freesize = 0;
CHECK_SOCKNUM(); CHECK_SOCKNUM();
switch (get_sn_mr(sn) & 0x0F) { switch (get_sn_mr(sn) & 0x0F) {
case Sn_MR_UDP: case SN_MR_UDP:
case Sn_MR_MACRAW: case SN_MR_MACRAW:
case Sn_MR_IPRAW: case SN_MR_IPRAW:
case Sn_MR_IPRAW6: case SN_MR_IPRAW6:
break; break;
default: default:
FAIL(sock_mode); FAIL(sock_mode);
} }
tmp = get_sn_mr(sn); tmp = get_sn_mr(sn);
if (tmp != Sn_MR_MACRAW) { if (tmp != SN_MR_MACRAW) {
if (addrlen == 16) { if (addrlen == 16) {
if (tmp & 0x08) { if (tmp & 0x08) {
set_sn_dip6r(sn, const_cast<uint8_t*>(addr.ip.data())); set_sn_dip6r(sn, const_cast<uint8_t*>(addr.ip.data()));
tcmd = Sn_CR_SEND6; tcmd = SN_CR_SEND6;
} else { } else {
FAIL(sock_mode); FAIL(sock_mode);
} }
} else if (addrlen == 4) { } else if (addrlen == 4) {
if (tmp == Sn_MR_UDP6 || tmp == Sn_MR_IPRAW6) FAIL(sock_mode); if (tmp == SN_MR_UDP6 || tmp == SN_MR_IPRAW6) FAIL(sock_mode);
set_sn_dipr(sn, const_cast<uint8_t*>(addr.ip.data())); set_sn_dipr(sn, const_cast<uint8_t*>(addr.ip.data()));
tcmd = Sn_CR_SEND; tcmd = SN_CR_SEND;
} else { } else {
FAIL(ip_invalid); FAIL(ip_invalid);
} }
@@ -1478,11 +1478,11 @@ std::expected<uint16_t, sock_error> sendto(socket_id sid, std::span<const uint8_
while (get_sn_cr(sn)); while (get_sn_cr(sn));
while (1) { while (1) {
tmp = get_sn_ir(sn); tmp = get_sn_ir(sn);
if (tmp & SnREG_IRSENDOK) { if (tmp & SN_IR_SENDOK) {
set_sn_ir(sn, SnREG_IRSENDOK); set_sn_ir(sn, SN_IR_SENDOK);
break; break;
} else if (tmp & SnREG_IRTIMEOUT) { } else if (tmp & SN_IR_TIMEOUT) {
set_sn_ir(sn, SnREG_IRTIMEOUT); set_sn_ir(sn, SN_IR_TIMEOUT);
FAIL(timeout); FAIL(timeout);
} }
} }
@@ -1500,10 +1500,10 @@ std::expected<uint16_t, sock_error> recvfrom(socket_id sid, std::span<uint8_t> b
CHECK_SOCKDATA(); CHECK_SOCKDATA();
switch ((mr = get_sn_mr(sn)) & 0x0F) { switch ((mr = get_sn_mr(sn)) & 0x0F) {
case Sn_MR_UDP: case SN_MR_UDP:
case Sn_MR_IPRAW: case SN_MR_IPRAW:
case Sn_MR_IPRAW6: case SN_MR_IPRAW6:
case Sn_MR_MACRAW: case SN_MR_MACRAW:
break; break;
default: default:
FAIL(sock_mode); FAIL(sock_mode);
@@ -1522,23 +1522,23 @@ std::expected<uint16_t, sock_error> recvfrom(socket_id sid, std::span<uint8_t> b
} }
recv_data(sn, head, 2); recv_data(sn, head, 2);
set_sn_cr(sn, Sn_CR_RECV); set_sn_cr(sn, SN_CR_RECV);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
pack_len = head[0] & 0x07; pack_len = head[0] & 0x07;
pack_len = (pack_len << 8) + head[1]; pack_len = (pack_len << 8) + head[1];
switch (mr & 0x07) { switch (mr & 0x07) {
case Sn_MR_UDP4: case SN_MR_UDP4:
case Sn_MR_UDP6: case SN_MR_UDP6:
case Sn_MR_UDPD: case SN_MR_UDPD:
sock_pack_info[sn] = head[0] & 0xF8; sock_pack_info[sn] = head[0] & 0xF8;
if (sock_pack_info[sn] & PACK_IPv6) addr.len = 16; if (sock_pack_info[sn] & PACK_IPv6) addr.len = 16;
else addr.len = 4; else addr.len = 4;
recv_data(sn, addr.ip.data(), addr.len); recv_data(sn, addr.ip.data(), addr.len);
set_sn_cr(sn, Sn_CR_RECV); set_sn_cr(sn, SN_CR_RECV);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
break; break;
case Sn_MR_MACRAW: case SN_MR_MACRAW:
if (sock_remained_size[sn] == 0) { if (sock_remained_size[sn] == 0) {
sock_remained_size[sn] = head[0]; sock_remained_size[sn] = head[0];
sock_remained_size[sn] = (sock_remained_size[sn] << 8) + head[1] - 2; sock_remained_size[sn] = (sock_remained_size[sn] << 8) + head[1] - 2;
@@ -1552,14 +1552,14 @@ std::expected<uint16_t, sock_error> recvfrom(socket_id sid, std::span<uint8_t> b
else pack_len = sock_remained_size[sn]; else pack_len = sock_remained_size[sn];
recv_data(sn, buf.data(), pack_len); recv_data(sn, buf.data(), pack_len);
break; break;
case Sn_MR_IPRAW6: case SN_MR_IPRAW6:
case Sn_MR_IPRAW4: case SN_MR_IPRAW4:
if (sock_remained_size[sn] == 0) { if (sock_remained_size[sn] == 0) {
sock_pack_info[sn] = head[0] & 0xF8; sock_pack_info[sn] = head[0] & 0xF8;
if (sock_pack_info[sn] & PACK_IPv6) addr.len = 16; if (sock_pack_info[sn] & PACK_IPv6) addr.len = 16;
else addr.len = 4; else addr.len = 4;
recv_data(sn, addr.ip.data(), addr.len); recv_data(sn, addr.ip.data(), addr.len);
set_sn_cr(sn, Sn_CR_RECV); set_sn_cr(sn, SN_CR_RECV);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
} }
break; break;
@@ -1574,14 +1574,14 @@ std::expected<uint16_t, sock_error> recvfrom(socket_id sid, std::span<uint8_t> b
if ((get_sn_mr(sn) & 0x03) == 0x02) { if ((get_sn_mr(sn) & 0x03) == 0x02) {
recv_data(sn, head, 2); recv_data(sn, head, 2);
port = static_cast<port_num>((((uint16_t)head[0]) << 8) + head[1]); port = static_cast<port_num>((((uint16_t)head[0]) << 8) + head[1]);
set_sn_cr(sn, Sn_CR_RECV); set_sn_cr(sn, SN_CR_RECV);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
} }
if (len < sock_remained_size[sn]) pack_len = len; if (len < sock_remained_size[sn]) pack_len = len;
else pack_len = sock_remained_size[sn]; else pack_len = sock_remained_size[sn];
recv_data(sn, buf.data(), pack_len); recv_data(sn, buf.data(), pack_len);
set_sn_cr(sn, Sn_CR_RECV); set_sn_cr(sn, SN_CR_RECV);
while (get_sn_cr(sn)); while (get_sn_cr(sn));
sock_remained_size[sn] -= pack_len; sock_remained_size[sn] -= pack_len;
@@ -1647,9 +1647,10 @@ void init_net(const net_info& info) {
set_net_info(info); set_net_info(info);
} }
std::expected<void, sock_error> set_socket_io_mode(socket_id sid, sock_io_mode mode) { std::expected<void, sock_error> set_socket_io_mode(socket_id sid, sock_io_mode mode) {
uint8_t sn = static_cast<uint8_t>(sid); uint8_t sn = static_cast<uint8_t>(sid);
if (sn >= SOCK_COUNT) FAIL(sock_num); if (sn >= sock_count) FAIL(sock_num);
if (mode == sock_io_mode::nonblock) sock_io_mode_bits |= (1 << sn); if (mode == sock_io_mode::nonblock) sock_io_mode_bits |= (1 << sn);
else if (mode == sock_io_mode::block) sock_io_mode_bits &= ~(1 << sn); else if (mode == sock_io_mode::block) sock_io_mode_bits &= ~(1 << sn);
else FAIL(arg); else FAIL(arg);
@@ -1671,8 +1672,8 @@ uint16_t get_socket_max_rx(socket_id sid) {
std::expected<void, sock_error> clear_socket_interrupt(socket_id sid, uint8_t flags) { std::expected<void, sock_error> clear_socket_interrupt(socket_id sid, uint8_t flags) {
uint8_t sn = static_cast<uint8_t>(sid); uint8_t sn = static_cast<uint8_t>(sid);
if (sn >= SOCK_COUNT) FAIL(sock_num); if (sn >= sock_count) FAIL(sock_num);
if (flags > SIK_ALL) FAIL(arg); if (flags > sik_all) FAIL(arg);
set_sn_ir(sn, flags); set_sn_ir(sn, flags);
return {}; return {};
} }
@@ -1683,8 +1684,8 @@ uint8_t get_socket_interrupt(socket_id sid) {
std::expected<void, sock_error> set_socket_interrupt_mask(socket_id sid, uint8_t mask) { std::expected<void, sock_error> set_socket_interrupt_mask(socket_id sid, uint8_t mask) {
uint8_t sn = static_cast<uint8_t>(sid); uint8_t sn = static_cast<uint8_t>(sid);
if (sn >= SOCK_COUNT) FAIL(sock_num); if (sn >= sock_count) FAIL(sock_num);
if (mask > SIK_ALL) FAIL(arg); if (mask > sik_all) FAIL(arg);
set_sn_imr(sn, mask); set_sn_imr(sn, mask);
return {}; return {};
} }
@@ -1695,7 +1696,7 @@ uint8_t get_socket_interrupt_mask(socket_id sid) {
std::expected<void, sock_error> set_socket_prefer(socket_id sid, srcv6_prefer pref) { std::expected<void, sock_error> set_socket_prefer(socket_id sid, srcv6_prefer pref) {
uint8_t sn = static_cast<uint8_t>(sid); uint8_t sn = static_cast<uint8_t>(sid);
if (sn >= SOCK_COUNT) FAIL(sock_num); if (sn >= sock_count) FAIL(sock_num);
uint8_t v = static_cast<uint8_t>(pref); uint8_t v = static_cast<uint8_t>(pref);
if ((v & 0x03) == 0x01) FAIL(arg); if ((v & 0x03) == 0x01) FAIL(arg);
set_sn_psr(sn, v); set_sn_psr(sn, v);
@@ -1730,6 +1731,10 @@ uint16_t get_socket_mss(socket_id sid) {
return get_sn_mssr(static_cast<uint8_t>(sid)); return get_sn_mssr(static_cast<uint8_t>(sid));
} }
void set_socket_dest_mac(socket_id sid, const std::array<uint8_t, 6>& mac) {
set_sn_dhar(static_cast<uint8_t>(sid), const_cast<uint8_t*>(mac.data()));
}
void set_socket_dest_ip(socket_id sid, const ip_address& addr) { void set_socket_dest_ip(socket_id sid, const ip_address& addr) {
uint8_t sn = static_cast<uint8_t>(sid); uint8_t sn = static_cast<uint8_t>(sid);
if (addr.len == 16) set_sn_dip6r(sn, const_cast<uint8_t*>(addr.ip.data())); if (addr.len == 16) set_sn_dip6r(sn, const_cast<uint8_t*>(addr.ip.data()));
@@ -1761,10 +1766,10 @@ std::expected<void, sock_error> send_keepalive(socket_id sid) {
uint8_t sn = static_cast<uint8_t>(sid); uint8_t sn = static_cast<uint8_t>(sid);
if ((get_sn_mr(sn) & 0x03) != 0x01) FAIL(sock_mode); if ((get_sn_mr(sn) & 0x03) != 0x01) FAIL(sock_mode);
if (get_sn_kpalvtr(sn) != 0) FAIL(sock_opt); if (get_sn_kpalvtr(sn) != 0) FAIL(sock_opt);
set_sn_cr(sn, Sn_CR_SEND_KEEP); set_sn_cr(sn, SN_CR_SEND_KEEP);
while (get_sn_cr(sn) != 0) { while (get_sn_cr(sn) != 0) {
if (get_sn_ir(sn) & SnREG_IRTIMEOUT) { if (get_sn_ir(sn) & SN_IR_TIMEOUT) {
set_sn_ir(sn, SnREG_IRTIMEOUT); set_sn_ir(sn, SN_IR_TIMEOUT);
FAIL(timeout); FAIL(timeout);
} }
} }
+23 -22
View File
@@ -7,7 +7,7 @@
namespace w6300 { namespace w6300 {
constexpr int SOCK_COUNT = 8; constexpr int sock_count = 8;
enum class socket_id : uint8_t {}; enum class socket_id : uint8_t {};
enum class port_num : uint16_t {}; enum class port_num : uint16_t {};
@@ -100,15 +100,15 @@ enum class sock_io_mode : uint8_t {
}; };
enum intr_kind : uint32_t { enum intr_kind : uint32_t {
IK_PPPOE_TERMINATED = (1 << 0), IK_DEST_UNREACH = (1 << 1), IK_IP_CONFLICT = (1 << 2), ik_pppoe_terminated = (1 << 0), ik_dest_unreach = (1 << 1), ik_ip_conflict = (1 << 2),
IK_DEST_UNREACH6 = (1 << 4), IK_WOL = (1 << 7), IK_NET_ALL = 0x97, ik_dest_unreach6 = (1 << 4), ik_wol = (1 << 7), ik_net_all = 0x97,
IK_SOCK_0 = (1 << 8), IK_SOCK_1 = (1 << 9), IK_SOCK_2 = (1 << 10), IK_SOCK_3 = (1 << 11), ik_sock_0 = (1 << 8), ik_sock_1 = (1 << 9), ik_sock_2 = (1 << 10), ik_sock_3 = (1 << 11),
IK_SOCK_4 = (1 << 12), IK_SOCK_5 = (1 << 13), IK_SOCK_6 = (1 << 14), IK_SOCK_7 = (1 << 15), ik_sock_4 = (1 << 12), ik_sock_5 = (1 << 13), ik_sock_6 = (1 << 14), ik_sock_7 = (1 << 15),
IK_SOCK_ALL = (0xFF << 8), ik_sock_all = (0xFF << 8),
IK_SOCKL_TOUT = (1 << 16), IK_SOCKL_ARP4 = (1 << 17), IK_SOCKL_PING4 = (1 << 18), ik_sockl_tout = (1 << 16), ik_sockl_arp4 = (1 << 17), ik_sockl_ping4 = (1 << 18),
IK_SOCKL_ARP6 = (1 << 19), IK_SOCKL_PING6 = (1 << 20), IK_SOCKL_NS = (1 << 21), ik_sockl_arp6 = (1 << 19), ik_sockl_ping6 = (1 << 20), ik_sockl_ns = (1 << 21),
IK_SOCKL_RS = (1 << 22), IK_SOCKL_RA = (1 << 23), IK_SOCKL_ALL = (0xFF << 16), ik_sockl_rs = (1 << 22), ik_sockl_ra = (1 << 23), ik_sockl_all = (0xFF << 16),
IK_INT_ALL = 0x00FFFF97 ik_int_all = 0x00FFFF97
}; };
struct phy_conf { struct phy_conf {
@@ -119,12 +119,12 @@ struct phy_conf {
}; };
enum ipconf_mode : uint8_t { enum ipconf_mode : uint8_t {
NETINFO_NONE = 0x00, NETINFO_STATIC_V4 = 0x01, NETINFO_STATIC_V6 = 0x02, ipconf_none = 0x00, ipconf_static_v4 = 0x01, ipconf_static_v6 = 0x02,
NETINFO_STATIC_ALL = 0x03, NETINFO_SLAAC_V6 = 0x04, ipconf_static_all = 0x03, ipconf_slaac_v6 = 0x04,
NETINFO_DHCP_V4 = 0x10, NETINFO_DHCP_V6 = 0x20, NETINFO_DHCP_ALL = 0x30 ipconf_dhcp_v4 = 0x10, ipconf_dhcp_v6 = 0x20, ipconf_dhcp_all = 0x30
}; };
enum dhcp_mode : uint8_t { NETINFO_STATIC = 1, NETINFO_DHCP }; enum dhcp_mode : uint8_t { dhcp_static = 1, dhcp_dynamic };
struct net_info { struct net_info {
std::array<uint8_t, 6> mac; std::array<uint8_t, 6> mac;
@@ -142,14 +142,14 @@ struct net_info {
}; };
enum netmode_type : uint32_t { enum netmode_type : uint32_t {
NM_IPB_V4 = (1 << 0), NM_IPB_V6 = (1 << 1), NM_WOL = (1 << 2), nm_ipb_v4 = (1 << 0), nm_ipb_v6 = (1 << 1), nm_wol = (1 << 2),
NM_PB6_MULTI = (1 << 4), NM_PB6_ALLNODE = (1 << 5), NM_MR_MASK = 0x37, nm_pb6_multi = (1 << 4), nm_pb6_allnode = (1 << 5), nm_mr_mask = 0x37,
NM_PPPoE = (1 << 8), NM_DHA_SELECT = (1 << 15), NM_MR2_MASK = (0x09 << 8), nm_pppoe = (1 << 8), nm_dha_select = (1 << 15), nm_mr2_mask = (0x09 << 8),
NM_PB4_ALL = (1 << 16), NM_TRSTB_V4 = (1 << 17), NM_PARP_V4 = (1 << 18), nm_pb4_all = (1 << 16), nm_trstb_v4 = (1 << 17), nm_parp_v4 = (1 << 18),
NM_UNRB_V4 = (1 << 19), NM_NET4_MASK = (0x0F << 16), nm_unrb_v4 = (1 << 19), nm_net4_mask = (0x0F << 16),
NM_PB6_ALL = (1 << 24), NM_TRSTB_V6 = (1 << 25), NM_PARP_V6 = (1 << 26), nm_pb6_all = (1 << 24), nm_trstb_v6 = (1 << 25), nm_parp_v6 = (1 << 26),
NM_UNRB_V6 = (1 << 27), NM_NET6_MASK = (0x0F << 24), nm_unrb_v6 = (1 << 27), nm_net6_mask = (0x0F << 24),
NM_MASK_ALL = 0x0F0F0937 nm_mask_all = 0x0F0F0937
}; };
struct net_timeout { struct net_timeout {
@@ -247,6 +247,7 @@ void set_socket_tos(socket_id sn, uint8_t tos);
uint8_t get_socket_tos(socket_id sn); uint8_t get_socket_tos(socket_id sn);
void set_socket_mss(socket_id sn, uint16_t mss); void set_socket_mss(socket_id sn, uint16_t mss);
uint16_t get_socket_mss(socket_id sn); uint16_t get_socket_mss(socket_id sn);
void set_socket_dest_mac(socket_id sn, const std::array<uint8_t, 6>& mac);
void set_socket_dest_ip(socket_id sn, const ip_address& addr); void set_socket_dest_ip(socket_id sn, const ip_address& addr);
ip_address get_socket_dest_ip(socket_id sn); ip_address get_socket_dest_ip(socket_id sn);
void set_socket_dest_port(socket_id sn, port_num port); void set_socket_dest_port(socket_id sn, port_num port);
+59 -31
View File
@@ -7,7 +7,6 @@ import (
"github.com/theater/picomap/lib/halfsiphash" "github.com/theater/picomap/lib/halfsiphash"
"github.com/theater/picomap/lib/msgpack" "github.com/theater/picomap/lib/msgpack"
"io"
) )
var HashKey = [8]byte{} var HashKey = [8]byte{}
@@ -15,7 +14,8 @@ var HashKey = [8]byte{}
type transport interface { type transport interface {
Send(data []byte) error Send(data []byte) error
SetReadTimeout(timeout time.Duration) SetReadTimeout(timeout time.Duration)
Reader() io.Reader Recv() (data []byte, from string, err error)
Broadcast() bool
Close() error Close() error
} }
@@ -44,51 +44,79 @@ func (c *Client) send(msg any) (uint32, error) {
return id, c.transport.Send(data) return id, c.transport.Send(data)
} }
func (c *Client) receive(expectedID uint32) (any, error) { type Response[T any] struct {
c.transport.SetReadTimeout(c.timeout) From string
dec := msgpack.NewDecoder(c.transport.Reader()) Value *T
var env Envelope
if err := dec.Decode(&env); err != nil {
return nil, fmt.Errorf("decode envelope: %w", err)
}
if env.MessageID != expectedID {
return nil, fmt.Errorf("message id mismatch: got %d, want %d", env.MessageID, expectedID)
}
expected := halfsiphash.Sum32(env.Payload, HashKey)
if env.Checksum != expected {
return nil, fmt.Errorf("checksum mismatch: got %08x, want %08x", env.Checksum, expected)
}
var inner any
if err := msgpack.Unmarshal(env.Payload, &inner); err != nil {
return nil, fmt.Errorf("decode inner: %w", err)
}
if devErr, ok := inner.(*DeviceError); ok {
return nil, devErr
}
return inner, nil
} }
func roundTrip[T any](c *Client, req any) (*T, error) { func roundTrip[T any](c *Client, req any) ([]Response[T], error) {
id, err := c.send(req) id, err := c.send(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp, err := c.receive(id) c.transport.SetReadTimeout(c.timeout)
broadcast := c.transport.Broadcast()
var results []Response[T]
for {
data, from, err := c.transport.Recv()
if err != nil {
break
}
var env Envelope
if err := msgpack.Unmarshal(data, &env); err != nil {
continue
}
if env.MessageID != id {
continue
}
expected := halfsiphash.Sum32(env.Payload, HashKey)
if env.Checksum != expected {
continue
}
var inner any
if err := msgpack.Unmarshal(env.Payload, &inner); err != nil {
continue
}
if devErr, ok := inner.(*DeviceError); ok {
return nil, devErr
}
if typed, ok := inner.(*T); ok {
results = append(results, Response[T]{From: from, Value: typed})
if !broadcast {
break
}
}
}
return results, nil
}
func first[T any](results []Response[T], err error) (*T, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
typed, ok := resp.(*T) if len(results) == 0 {
if !ok { return nil, fmt.Errorf("no response")
return nil, fmt.Errorf("unexpected response: %T", resp)
} }
return typed, nil return results[0].Value, nil
} }
func (c *Client) PICOBOOT() error { func (c *Client) PICOBOOT() error {
_, err := roundTrip[ResponsePICOBOOT](c, &RequestPICOBOOT{}) _, err := first(roundTrip[ResponsePICOBOOT](c, &RequestPICOBOOT{}))
return err return err
} }
func (c *Client) Info() (*ResponseInfo, error) { func (c *Client) Info() (*ResponseInfo, error) {
return first(roundTrip[ResponseInfo](c, &RequestInfo{}))
}
func (c *Client) InfoAll() ([]Response[ResponseInfo], error) {
return roundTrip[ResponseInfo](c, &RequestInfo{}) return roundTrip[ResponseInfo](c, &RequestInfo{})
} }
func (c *Client) Log() (*ResponseLog, error) {
return first(roundTrip[ResponseLog](c, &RequestLog{}))
}
func (c *Client) Test(name string) (*ResponseTest, error) {
return first(roundTrip[ResponseTest](c, &RequestTest{Name: name}))
}
+30 -8
View File
@@ -2,9 +2,11 @@ package client
import ( import (
"fmt" "fmt"
"io" "slices"
"strings"
"time" "time"
"github.com/theater/picomap/lib/msgpack"
"go.bug.st/serial" "go.bug.st/serial"
"go.bug.st/serial/enumerator" "go.bug.st/serial/enumerator"
) )
@@ -14,17 +16,30 @@ func ListSerial() ([]string, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("enumerating ports: %w", err) return nil, fmt.Errorf("enumerating ports: %w", err)
} }
var result []string type entry struct {
name string
serial string
}
var entries []entry
for _, p := range ports { for _, p := range ports {
if p.IsUSB { if p.IsUSB && p.VID == "2E8A" && strings.HasPrefix(p.Name, "/dev/cu.") {
result = append(result, p.Name) entries = append(entries, entry{p.Name, p.SerialNumber})
} }
} }
slices.SortFunc(entries, func(a, b entry) int {
return strings.Compare(a.serial, b.serial)
})
var result []string
for _, e := range entries {
result = append(result, e.name)
}
return result, nil return result, nil
} }
type serialTransport struct { type serialTransport struct {
port serial.Port port serial.Port
portName string
dec *msgpack.Decoder
} }
func NewSerial(portName string, timeout time.Duration) (*Client, error) { func NewSerial(portName string, timeout time.Duration) (*Client, error) {
@@ -32,7 +47,8 @@ func NewSerial(portName string, timeout time.Duration) (*Client, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("opening %s: %w", portName, err) return nil, fmt.Errorf("opening %s: %w", portName, err)
} }
return &Client{transport: &serialTransport{port: port}, timeout: timeout}, nil t := &serialTransport{port: port, portName: portName, dec: msgpack.NewDecoder(port)}
return &Client{transport: t, timeout: timeout}, nil
} }
func (t *serialTransport) Send(data []byte) error { func (t *serialTransport) Send(data []byte) error {
@@ -44,10 +60,16 @@ func (t *serialTransport) SetReadTimeout(timeout time.Duration) {
t.port.SetReadTimeout(timeout) t.port.SetReadTimeout(timeout)
} }
func (t *serialTransport) Reader() io.Reader { func (t *serialTransport) Recv() ([]byte, string, error) {
return t.port var raw msgpack.RawMessage
if err := t.dec.Decode(&raw); err != nil {
return nil, "", err
}
return []byte(raw), t.portName, nil
} }
func (t *serialTransport) Broadcast() bool { return false }
func (t *serialTransport) Close() error { func (t *serialTransport) Close() error {
return t.port.Close() return t.port.Close()
} }
+28 -2
View File
@@ -7,8 +7,30 @@ type ResponsePICOBOOT struct{}
type RequestInfo struct{} type RequestInfo struct{}
type ResponseInfo struct { type ResponseInfo struct {
BoardID [8]byte BoardID [8]byte
MAC [6]byte MAC [6]byte
IP [4]byte
FirmwareName string
}
type RequestLog struct{}
type LogEntry struct {
TimestampUS uint32
Message string
}
type ResponseLog struct {
Entries []LogEntry
}
type RequestTest struct {
Name string
}
type ResponseTest struct {
Pass bool
Messages []string
} }
type DeviceError struct { type DeviceError struct {
@@ -33,4 +55,8 @@ func init() {
msgpack.RegisterExt(3, (*ResponsePICOBOOT)(nil)) msgpack.RegisterExt(3, (*ResponsePICOBOOT)(nil))
msgpack.RegisterExt(4, (*RequestInfo)(nil)) msgpack.RegisterExt(4, (*RequestInfo)(nil))
msgpack.RegisterExt(5, (*ResponseInfo)(nil)) msgpack.RegisterExt(5, (*ResponseInfo)(nil))
msgpack.RegisterExt(6, (*RequestLog)(nil))
msgpack.RegisterExt(7, (*ResponseLog)(nil))
msgpack.RegisterExt(127, (*RequestTest)(nil))
msgpack.RegisterExt(126, (*ResponseTest)(nil))
} }
+130
View File
@@ -0,0 +1,130 @@
package client
import (
"fmt"
"net"
"syscall"
"time"
"golang.org/x/sys/unix"
)
const PicomapPort = 28781
type udpTransport struct {
conn *net.UDPConn
addr *net.UDPAddr
broadcast bool
}
func interfaceIPv4Net(name string) (net.IP, *net.IPNet, error) {
ifi, err := net.InterfaceByName(name)
if err != nil {
return nil, nil, fmt.Errorf("interface %s: %w", name, err)
}
addrs, err := ifi.Addrs()
if err != nil {
return nil, nil, fmt.Errorf("interface %s addrs: %w", name, err)
}
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok {
if ip4 := ipnet.IP.To4(); ip4 != nil {
return ip4, ipnet, nil
}
}
}
return nil, nil, fmt.Errorf("interface %s has no IPv4 address", name)
}
func broadcastAddr(ip net.IP, mask net.IPMask) net.IP {
bcast := make(net.IP, 4)
ip4 := ip.To4()
for i := range 4 {
bcast[i] = ip4[i] | ^mask[i]
}
return bcast
}
func InterfaceBroadcast(name string) (string, error) {
ip, ipnet, err := interfaceIPv4Net(name)
if err != nil {
return "", err
}
return broadcastAddr(ip, ipnet.Mask).String(), nil
}
func enableBroadcast(conn *net.UDPConn) error {
raw, err := conn.SyscallConn()
if err != nil {
return fmt.Errorf("syscall conn: %w", err)
}
var serr error
raw.Control(func(fd uintptr) {
serr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_BROADCAST, 1)
})
return serr
}
func isInterfaceBroadcast(iface string, ip net.IP) bool {
if iface == "" {
return false
}
localIP, ipnet, err := interfaceIPv4Net(iface)
if err != nil {
return false
}
return ip.Equal(broadcastAddr(localIP, ipnet.Mask)) || ip.Equal(net.IPv4bcast)
}
func NewUDP(addr string, iface string, timeout time.Duration) (*Client, error) {
raddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", addr, PicomapPort))
if err != nil {
return nil, fmt.Errorf("resolve %s: %w", addr, err)
}
var laddr *net.UDPAddr
if iface != "" {
ip, _, err := interfaceIPv4Net(iface)
if err != nil {
return nil, err
}
laddr = &net.UDPAddr{IP: ip, Port: 0}
}
conn, err := net.ListenUDP("udp4", laddr)
if err != nil {
return nil, fmt.Errorf("listen: %w", err)
}
if err := enableBroadcast(conn); err != nil {
conn.Close()
return nil, fmt.Errorf("SO_BROADCAST: %w", err)
}
bcast := isInterfaceBroadcast(iface, raddr.IP)
return &Client{transport: &udpTransport{conn: conn, addr: raddr, broadcast: bcast}, timeout: timeout}, nil
}
func (t *udpTransport) Send(data []byte) error {
_, err := t.conn.WriteToUDP(data, t.addr)
return err
}
func (t *udpTransport) SetReadTimeout(timeout time.Duration) {
t.conn.SetReadDeadline(time.Now().Add(timeout))
}
func (t *udpTransport) Recv() ([]byte, string, error) {
buf := make([]byte, 1500)
n, addr, err := t.conn.ReadFromUDP(buf)
if err != nil {
return nil, "", err
}
return buf[:n], addr.IP.String(), nil
}
func (t *udpTransport) Broadcast() bool { return t.broadcast }
func (t *udpTransport) Close() error {
return t.conn.Close()
}
+14 -6
View File
@@ -3,15 +3,23 @@ package picotool
import ( import (
"fmt" "fmt"
"os/exec" "os/exec"
"time"
) )
func Load(uf2Path string, serial string) error { func Load(uf2Path string, serial string, timeout time.Duration) error {
cmd := exec.Command("picotool", "load", uf2Path, "--ser", serial) deadline := time.Now().Add(timeout)
out, err := cmd.CombinedOutput() var out []byte
if err != nil { var err error
return fmt.Errorf("picotool load: %w\n%s", err, out) 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)
}
} }
return nil
} }
func Reboot(serial string) error { func Reboot(serial string) error {