diff options
Diffstat (limited to 'src/p4.zig')
| -rw-r--r-- | src/p4.zig | 26 |
1 files changed, 24 insertions, 2 deletions
@@ -93,7 +93,9 @@ fn panicImpl(msg: []const u8, _: ?usize) noreturn { // cheapest possible defence: the firmware calls it first and refuses to continue on a mismatch. /// Bumped whenever any signature below changes, including a type. -const abi_version: u32 = 1; +/// 2 added `GpioFn` to `pardes_p4_init`. A firmware built against 1 passes five arguments where six +/// are read, which is exactly the silent-corruption case this counter exists to turn into a message. +const abi_version: u32 = 2; export fn pardes_p4_abi_version() callconv(.c) u32 { return abi_version; @@ -115,6 +117,15 @@ pub const Allocator = extern struct { /// How finished runs of ANSI leave this object. pub const WriteFn = *const fn (ctx: ?*anyopaque, ptr: [*]const u8, len: usize) callconv(.c) void; +/// Flip one pad and report the level before and after; false if the firmware declines. OPTIONAL on +/// the wire, so a host with no pads (or one that has not implemented them yet) passes null and the +/// `Gpio` word answers "no pads" instead of the object having to know which firmwares exist. +/// +/// The board's side, not the editor's, because a correct toggle is the IO MUX, the GPIO matrix, the +/// pad's own bits and the output enable - four register files behind a per-pin table that the +/// firmware already has and checks against ESP-IDF. See `Host.VTable.pull_gpio_toggle`. +pub const GpioFn = *const fn (ctx: ?*anyopaque, pin: u16, was: *u8, now: *u8) callconv(.c) bool; + // ------------------------------------------------------------------- the allocator, rebuilt // One `std.mem.Allocator` whose vtable forwards to the four pointers above. The indirection is the // price of the seam and it is paid once per allocation, which on a first-fit heap is already the @@ -158,6 +169,7 @@ fn gpa() std.mem.Allocator { var out_write: WriteFn = undefined; var out_ctx: ?*anyopaque = null; +var host_gpio: ?GpioFn = null; var out_buf: [8192]u8 = undefined; var out: std.Io.Writer = undefined; @@ -247,12 +259,14 @@ fn vaxisSize() vaxis.Winsize { export fn pardes_p4_init( alloc: *const Allocator, write: WriteFn, + gpio: ?GpioFn, ctx: ?*anyopaque, cols: u16, rows: u16, ) callconv(.c) u32 { host_alloc = alloc.*; out_write = write; + host_gpio = gpio; out_ctx = ctx; out = .{ .vtable = &.{ .drain = drain }, .buffer = &out_buf }; @@ -594,7 +608,15 @@ export fn pardes_p4_quit() callconv(.c) bool { // ------------------------------------------------------------------------------------ the host -const pardes_host: pardes.Host.VTable = .{ .push_present = present }; +const pardes_host: pardes.Host.VTable = .{ .push_present = present, .pull_gpio_toggle = gpioToggle }; + +/// The `Gpio` word's one seam to the board. Nothing here knows what a pad is; it forwards, and +/// answers false when the firmware brought none, which is what puts "gpio: NoPads" on the message +/// row rather than a trap. +fn gpioToggle(_: ?*anyopaque, pin: u16, was: *u8, now: *u8) bool { + const f = host_gpio orelse return false; + return f(out_ctx, pin, was, now); +} /// The canonical surface -> the wire. Same shape as the tty shell's (`src/tty/tty.zig:1096`) minus /// the panel compositor and the kitty image path: neither has a reason to exist on a board with no |
