summaryrefslogtreecommitdiff
path: root/tools/serial.zig
diff options
context:
space:
mode:
Diffstat (limited to 'tools/serial.zig')
-rw-r--r--tools/serial.zig24
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);