summaryrefslogtreecommitdiff
path: root/src/p4.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/p4.zig')
-rw-r--r--src/p4.zig26
1 files changed, 24 insertions, 2 deletions
diff --git a/src/p4.zig b/src/p4.zig
index 05610eca..8e46b2ff 100644
--- a/src/p4.zig
+++ b/src/p4.zig
@@ -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