Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Library for configuring and listing serial ports.
- Handshake (none, hardware, software)
- Byte Size (5, 6, 7, 8)
- Flush serial port send/receive buffers
- Query number of bytes in serial port send/receive buffers
- List available serial ports
- API: supports Windows, Linux and Mac

Expand Down
109 changes: 88 additions & 21 deletions src/serial.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1015,12 +1015,72 @@ pub fn changeControlPins(port: std.fs.File, pins: ControlPins) !void {
return error.Unexpected;
},

.macos => {},

else => @compileError("changeControlPins not implemented for " ++ @tagName(builtin.os.tag)),
}
}

/// TODO: This struct should probably be added to the Zig standard library at std.os.windows
const COMSTAT = extern struct {
status: packed struct(std.os.windows.DWORD) {
fCtsHold: u1,
fDsrHold: u1,
fRlsdHold: u1,
fXoffHold: u1,
fXoffSent: u1,
fEof: u1,
fTxim: u1,
fReserved: u25,
},
cbInQue: std.os.windows.DWORD,
cbOutQue: std.os.windows.DWORD,
};

extern "kernel32" fn ClearCommError(hFile: std.os.windows.HANDLE, lpErrors: *std.os.windows.DWORD, lpStat: *COMSTAT) std.os.windows.BOOL;

/// Returns the number of bytes waiting in the receive buffer
pub fn receiveBufferCount(port: std.fs.File) !usize {
switch (builtin.os.tag) {
.windows => {
var ret_error: std.os.windows.DWORD = 0;
var ret_comstat: COMSTAT = std.mem.zeroes(COMSTAT);
if (ClearCommError(port.handle, &ret_error, &ret_comstat) == 0)
return error.Unexpected;
return @intCast(ret_comstat.cbInQue);
},
.linux => {
// from /usr/include/asm-generic/ioctls.h
const FIONREAD = 0x541B;
var bytes_avail: usize = 0;
if (std.os.linux.ioctl(port.handle, FIONREAD, @intFromPtr(&bytes_avail)) != 0)
return error.Unexpected;
return bytes_avail;
},
else => @compileError("receiveBufferCount not implemented for " ++ @tagName(builtin.os.tag)),
}
}

/// Returns the number of bytes waiting in the transmit buffer
pub fn transmitBufferCount(port: std.fs.File) !usize {
switch (builtin.os.tag) {
.windows => {
var ret_error: std.os.windows.DWORD = 0;
var ret_comstat: COMSTAT = std.mem.zeroes(COMSTAT);
if (ClearCommError(port.handle, &ret_error, &ret_comstat) == 0)
return error.Unexpected;
return @intCast(ret_comstat.cbOutQue);
},
.linux => {
// from /usr/include/asm-generic/ioctls.h
const TIOCOUTQ = 0x5411;
var bytes_avail: usize = 0;
if (std.os.linux.ioctl(port.handle, TIOCOUTQ, @intFromPtr(&bytes_avail)) != 0)
return error.Unexpected;
return bytes_avail;
},
else => @compileError("transmitBufferCount not implemented for " ++ @tagName(builtin.os.tag)),
}
}

const PURGE_RXABORT = 0x0002;
const PURGE_RXCLEAR = 0x0008;
const PURGE_TXABORT = 0x0001;
Expand Down Expand Up @@ -1145,6 +1205,16 @@ test "iterate ports" {
}
}

fn openTestSerialDeviceFile() !std.fs.File {
// if any, these will likely exist on a machine
return switch (builtin.os.tag) {
.windows => std.fs.cwd().openFile("\\\\.\\COM3", .{ .mode = .read_write }),
.linux => std.fs.cwd().openFile("/dev/ttyUSB0", .{ .mode = .read_write }),
.macos => std.fs.cwd().openFile("/dev/cu.usbmodem101", .{ .mode = .read_write }),
else => unreachable,
};
}

test "basic configuration test" {
const cfg = SerialConfig{
.handshake = .none,
Expand All @@ -1154,31 +1224,14 @@ test "basic configuration test" {
.stop_bits = .one,
};

var tty: []const u8 = undefined;

switch (builtin.os.tag) {
.windows => tty = "\\\\.\\COM3",
.linux => tty = "/dev/ttyUSB0",
.macos => tty = "/dev/cu.usbmodem101",
else => unreachable,
}

var port = try std.fs.cwd().openFile(tty, .{ .mode = .read_write });
var port = try openTestSerialDeviceFile();
defer port.close();

try configureSerialPort(port, cfg);
}

test "basic flush test" {
var tty: []const u8 = undefined;
// if any, these will likely exist on a machine
switch (builtin.os.tag) {
.windows => tty = "\\\\.\\COM3",
.linux => tty = "/dev/ttyUSB0",
.macos => tty = "/dev/cu.usbmodem101",
else => unreachable,
}
var port = try std.fs.cwd().openFile(tty, .{ .mode = .read_write });
var port = try openTestSerialDeviceFile();
defer port.close();

try flushSerialPort(port, .both);
Expand All @@ -1190,6 +1243,20 @@ test "change control pins" {
_ = changeControlPins;
}

test "receive buffer counts" {
var port = try openTestSerialDeviceFile();
defer port.close();
try flushSerialPort(port, .input);
try std.testing.expectEqual(0, try receiveBufferCount(port));
}

test "transmit buffer counts" {
var port = try openTestSerialDeviceFile();
defer port.close();
try flushSerialPort(port, .output);
try std.testing.expectEqual(0, try transmitBufferCount(port));
}

test "bufPrint tests" {
var buf: [32]u8 = undefined;

Expand Down