summaryrefslogtreecommitdiff
path: root/tools/serial.zig
diff options
context:
space:
mode:
Diffstat (limited to 'tools/serial.zig')
-rw-r--r--tools/serial.zig28
1 files changed, 27 insertions, 1 deletions
diff --git a/tools/serial.zig b/tools/serial.zig
index 3398a11..7a740cd 100644
--- a/tools/serial.zig
+++ b/tools/serial.zig
@@ -81,10 +81,17 @@ pub const Port = struct {
file: std.Io.File,
io: std.Io,
saved: Termios2,
+ /// The rate currently programmed, kept because the wire's capacity in bytes per second is
+ /// `rate/10` and anything measuring this link against its ceiling needs that number. The
+ /// kernel would answer a TCGETS2, but a syscall per sample to re-read a value only this file
+ /// ever changes is worse than a field.
+ baud: Baud,
// std exposes neither these ioctl numbers nor the TIOCM bits.
const TIOCEXCL = 0x540C;
const TCFLSH = 0x540B;
+ /// tcdrain, with a nonzero argument. Zero would transmit a break instead.
+ const TCSBRK = 0x5409;
const TCIFLUSH = 0;
const TIOCMGET = 0x5415;
const TIOCMSET = 0x5418;
@@ -132,7 +139,7 @@ pub const Port = struct {
// Drop anything the kernel captured at the previous line rate.
_ = linux.ioctl(fd, TCFLSH, TCIFLUSH);
- return .{ .file = file, .io = io, .saved = saved };
+ return .{ .file = file, .io = io, .saved = saved, .baud = baud };
}
/// Re-rate an already-open port, leaving the raw-mode flags and the exclusive claim alone.
@@ -153,6 +160,25 @@ pub const Port = struct {
t.ospeed = baud.rate();
if (@as(isize, @bitCast(linux.ioctl(p.file.handle, Termios2.TCSETSW2, @intFromPtr(&t)))) < 0)
return error.SetAttrFailed;
+ p.baud = baud;
+ }
+
+ /// Bytes per second the wire can carry: one 8N1 byte occupies ten bit times.
+ pub fn capacity(p: *const Port) u32 {
+ return p.baud.rate() / 10;
+ }
+
+ /// Block until every byte written has physically left the wire - `tcdrain`, spelled as the
+ /// ioctl because std exposes neither.
+ ///
+ /// Distinct from `drain` above in both direction and meaning, which is worth stating because
+ /// getting them the wrong way round silently invalidates a measurement: `drain` discards what
+ /// has ARRIVED, this waits for what is LEAVING. A `write` returns once the kernel has accepted
+ /// the bytes, so timing a transfer to the write measures a memcpy into a tty buffer - at 115200
+ /// that reported 202% of the wire's capacity, which is how the confusion was noticed.
+ pub fn flushOutput(p: *Port) void {
+ // TCSBRK with a nonzero argument is tcdrain on Linux; with zero it would send a break.
+ _ = linux.ioctl(p.file.handle, TCSBRK, 1);
}
pub fn close(p: *Port) void {