diff --git a/README.md b/README.md index 957b76d..1b92911 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Library for configuring and listing serial ports. - Stop Bits (one, two) - Handshake (none, hardware, software) - Byte Size (5, 6, 7, 8) + - Receive timeout (optional) - Flush serial port send/receive buffers - List available serial ports - API: supports Windows, Linux and Mac diff --git a/src/serial.zig b/src/serial.zig index e020d78..6ab97cd 100644 --- a/src/serial.zig +++ b/src/serial.zig @@ -198,13 +198,14 @@ const WindowsInformationIterator = struct { self.device_info_set, &device_info_data, @ptrCast(&self.hw_id), - 255, + self.hw_id.len - 1, null, ) == std.os.windows.TRUE) { length = @as(u32, @truncate(std.mem.indexOfSentinel(u8, 0, &self.hw_id))); - info.hw_id = self.hw_id[0..length]; + const id = self.hw_id[0..length]; + info.hw_id = id; - length = parseSerialNumber(&self.hw_id, &self.serial_buffer) catch 0; + length = parseSerialNumber(id, &self.serial_buffer) catch 0; if (length == 0) { length = getParentSerialNumber(device_info_data.devInst, &self.hw_id, &self.serial_buffer) catch 0; } @@ -294,12 +295,14 @@ const WindowsInformationIterator = struct { var local_buffer: [256:0]u8 = std.mem.zeroes([256:0]u8); if (CM_Get_Parent(&parent_id, child_inst, 0) != 0) return error.WindowsError; - if (CM_Get_Device_IDA(parent_id, @ptrCast(&local_buffer), 256, 0) != 0) return error.WindowsError; + if (CM_Get_Device_IDA(parent_id, @ptrCast(&local_buffer), local_buffer.len, 0) != 0) return error.WindowsError; defer child_inst = parent_id; - if (!std.mem.containsAtLeast(u8, local_buffer[0..255], 1, vidpid_slice)) continue; + const hwidlen = std.mem.indexOfSentinel(u8, 0, &local_buffer); + const hwid = local_buffer[0..hwidlen]; + if (!std.mem.containsAtLeast(u8, hwid, 1, vidpid_slice)) continue; - const length = try parseSerialNumber(local_buffer[0..255], serial_number); + const length = try parseSerialNumber(hwid, serial_number); if (length > 0) return length; } } @@ -749,6 +752,9 @@ pub const SerialConfig = struct { /// Defines the handshake protocol used. handshake: Handshake = .none, + /// Timeout in milliseconds for read operations. + timeout_ms: ?u32 = null, + pub fn format(self: Self, writer: *Io.Writer) !void { return writer.print("{d}@{d}{c}{d}{s}", .{ self.baud_rate, @@ -768,11 +774,6 @@ const CBAUD = 0o000000010017; //Baud speed mask (not in POSIX). const CMSPAR = 0o010000000000; const CRTSCTS = 0o020000000000; -const VTIME = 5; -const VMIN = 6; -const VSTART = 8; -const VSTOP = 9; - /// This function configures a serial port with the given config. /// `port` is an already opened serial port, on windows these /// are either called `\\.\COMxx\` or `COMx`, on unixes the serial @@ -822,6 +823,13 @@ pub fn configureSerialPort(port: std.fs.File, config: SerialConfig) !void { if (SetCommState(port.handle, &dcb) == 0) return error.WindowsError; + + if (config.timeout_ms) |timeout| { + var timeouts = COMMTIMEOUTS{}; + timeouts.ReadTotalTimeoutConstant = timeout; + if (SetCommTimeouts(port.handle, &timeouts) == 0) + return error.WindowsError; + } }, .linux, .macos => |tag| { var settings = try std.posix.tcgetattr(port.handle); @@ -898,10 +906,19 @@ pub fn configureSerialPort(port: std.fs.File, config: SerialConfig) !void { settings.oflag = .{}; settings.lflag = .{}; - settings.cc[VMIN] = 1; - settings.cc[VSTOP] = 0x13; // XOFF - settings.cc[VSTART] = 0x11; // XON - settings.cc[VTIME] = 0; + settings.cc[c.VSTOP] = 0x13; // XOFF + settings.cc[c.VSTART] = 0x11; // XON + if (config.timeout_ms) |timeout| { + const tenths = timeout / 100; + if(tenths == 0) return error.UnsupportedTimeout; + settings.cc[c.VTIME] = if(tenths < 256) @intCast(tenths) else return error.UnsupportedTimeout; + settings.cc[c.VMIN] = 0; + } + else { + + settings.cc[c.VTIME] = 0; + settings.cc[c.VMIN] = 1; + } try std.posix.tcsetattr(port.handle, .NOW, settings); @@ -1137,6 +1154,15 @@ const DCB = extern struct { extern "kernel32" fn GetCommState(hFile: std.os.windows.HANDLE, lpDCB: *DCB) callconv(.winapi) std.os.windows.BOOL; extern "kernel32" fn SetCommState(hFile: std.os.windows.HANDLE, lpDCB: *DCB) callconv(.winapi) std.os.windows.BOOL; +const COMMTIMEOUTS = extern struct { + ReadIntervalTimeout: std.os.windows.DWORD = std.math.maxInt(std.os.windows.DWORD), + ReadTotalTimeoutMultiplier: std.os.windows.DWORD = 0, + ReadTotalTimeoutConstant: std.os.windows.DWORD = 0, + WriteTotalTimeoutMultiplier: std.os.windows.DWORD = 0, + WriteTotalTimeoutConstant: std.os.windows.DWORD = 0, +}; +extern "kernel32" fn SetCommTimeouts(in_hFile: std.os.windows.HANDLE, in_lpCommTimeouts: *COMMTIMEOUTS) callconv(.winapi) std.os.windows.BOOL; + test "iterate ports" { var it = try list(); while (try it.next()) |port| {