diff options
Diffstat (limited to 'tools/serial.zig')
| -rw-r--r-- | tools/serial.zig | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/tools/serial.zig b/tools/serial.zig index c13942c..da225fd 100644 --- a/tools/serial.zig +++ b/tools/serial.zig @@ -43,6 +43,10 @@ const Termios2 = extern struct { /// _IOR('T', 0x2A, struct termios2) and _IOW('T', 0x2B, struct termios2) for a 44-byte struct. const TCGETS2: u32 = 0x802C542A; const TCSETS2: u32 = 0x402C542B; + /// _IOW('T', 0x2C, ...): TCSETS2's draining sibling. Changing the divisor while bytes are + /// still in the kernel's output queue sends the tail of the old line at the new rate, which on + /// a mid-session baud switch corrupts exactly the handshake line the switch was announced by. + const TCSETSW2: u32 = 0x402C542C; /// CBAUD escape meaning "take the rate from ispeed/ospeed" (asm-generic/termbits.h). const BOTHER: u32 = 0o010000; @@ -110,6 +114,26 @@ pub const Port = struct { return .{ .file = file, .io = io, .saved = saved }; } + /// Re-rate an already-open port, leaving the raw-mode flags and the exclusive claim alone. + /// + /// This exists because the console and the flasher want different rates on the same wire. The + /// ROM loader auto-detects the host's rate from SYNC's 0x55 pattern, so `open` can simply pick + /// one; a running application cannot, because its UART divider was programmed by the + /// second-stage bootloader and the only way to change it is for the firmware to reprogram its + /// own divider and for the host to follow. That makes the switch a two-sided handshake, and + /// `TCSETSW2` is the half that has to be ordered: the firmware announces the change on the old + /// rate, and the announcement must have physically left the wire before either side moves. + pub fn setBaud(p: *Port, baud: Baud) !void { + var t: Termios2 = undefined; + if (@as(isize, @bitCast(linux.ioctl(p.file.handle, Termios2.TCGETS2, @intFromPtr(&t)))) < 0) + return error.NotATerminal; + t.cflag = (t.cflag & ~Termios2.CBAUD) | Termios2.BOTHER; + t.ispeed = baud.rate(); + t.ospeed = baud.rate(); + if (@as(isize, @bitCast(linux.ioctl(p.file.handle, Termios2.TCSETSW2, @intFromPtr(&t)))) < 0) + return error.SetAttrFailed; + } + pub fn close(p: *Port) void { _ = linux.ioctl(p.file.handle, Termios2.TCSETS2, @intFromPtr(&p.saved)); p.file.close(p.io); |
