Skip to content

Commit

Permalink
refactor: Config clarifications, Linux configure
Browse files Browse the repository at this point in the history
  • Loading branch information
mochalins committed Sep 23, 2024
1 parent 3fb545d commit 1b850ad
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 43 deletions.
113 changes: 97 additions & 16 deletions src/backend/linux.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ const std = @import("std");
const serialport = @import("../serialport.zig");
const linux = std.os.linux;

pub const BaudRate = b: {
const ti = @typeInfo(std.os.linux.speed_t).@"enum";
const baud_rate_ti: std.builtin.Type.Enum = .{
.tag_type = ti.tag_type,
.fields = ti.fields,
.decls = &.{},
.is_exhaustive = false,
};
break :b @Type(std.builtin.Type{ .@"enum" = baud_rate_ti });
};

pub const PortImpl = std.fs.File;

pub const ReadError = std.fs.File.ReadError;
Expand All @@ -21,29 +32,99 @@ pub fn close(port: PortImpl) void {
port.close();
}

fn configureParity(
termios: *std.os.linux.termios,
parity: serialport.Config.Parity,
) void {
termios.cflag.PARENB = parity != .none;
termios.cflag.PARODD = parity == .odd or parity == .mark;
termios.cflag.CMSPAR = parity == .mark or parity == .space;

termios.iflag.INPCK = parity != .none;
termios.iflag.IGNPAR = parity == .none;
}

fn configureFlowControl(
termios: *std.os.linux.termios,
flow_control: serialport.Config.FlowControl,
) void {
termios.cflag.CLOCAL = flow_control == .none;
termios.cflag.CRTSCTS = flow_control == .hardware;

termios.iflag.IXANY = flow_control == .software;
termios.iflag.IXON = flow_control == .software;
termios.iflag.IXOFF = flow_control == .software;
}

pub fn configure(port: PortImpl, config: serialport.Config) !void {
const CBAUD: u32 = switch (comptime builtin.target.cpu.arch) {
.powerpc, .powerpcle, .powerpc64, .powerpc64le => 0x000000FF,
else => 0x0000100F,
};
const CIBAUD: u32 = switch (comptime builtin.target.cpu.arch) {
.powerpc, .powerpcle, .powerpc64, .powerpc64le => 0x00FF0000,
else => 0x100F0000,
};
const BOTHER = switch (comptime builtin.target.cpu.arch) {
.powerpc, .powerpcle, .powerpc64, .powerpc64le => 0x0000001F,
else => 0x00001000,
};
const IBSHIFT = 16;

const custom_output_baud: bool = std.enums.tagName(
serialport.Config.BaudRate,
config.baud_rate,
) == null;
const custom_input_baud: bool = if (config.input_baud_rate) |ibr|
std.enums.tagName(serialport.Config.BaudRate, ibr) == null
else
custom_output_baud;

var settings = try std.posix.tcgetattr(port.handle);

settings.iflag = .{};
settings.iflag.INPCK = config.parity != .none;
settings.iflag.IXON = config.handshake == .software;
settings.iflag.IXOFF = config.handshake == .software;
// `cfmakeraw`
settings.iflag.IGNBRK = false;
settings.iflag.BRKINT = false;
settings.iflag.PARMRK = false;
settings.iflag.ISTRIP = false;
settings.iflag.INLCR = false;
settings.iflag.IGNCR = false;
settings.iflag.ICRNL = false;
settings.iflag.IXON = false;

settings.oflag.OPOST = false;

settings.lflag.ECHO = false;
settings.lflag.ECHONL = false;
settings.lflag.ICANON = false;
settings.lflag.ISIG = false;
settings.lflag.IEXTEN = false;

var cflag: u32 = @bitCast(settings.cflag);

// Set CBAUD and CIBAUD in cflag.
const baud_bits: u32 = @bitCast(@intFromEnum(config.baud_rate));
cflag &= ~CBAUD;
cflag &= ~CIBAUD;
cflag |= if (custom_output_baud) BOTHER else baud_bits;
if (config.input_baud_rate) |ibr| {
const input_baud_bits: u32 = @bitCast(@intFromEnum(ibr));
cflag |=
(if (custom_input_baud) input_baud_bits else BOTHER) << IBSHIFT;
}

settings.cflag = @bitCast(@intFromEnum(config.baud_rate));
settings.cflag = @bitCast(cflag);
settings.cflag.CREAD = true;
settings.cflag.CLOCAL = config.handshake == .none;
settings.cflag.CSTOPB = config.stop_bits == .two;
settings.cflag.CSIZE = @enumFromInt(@intFromEnum(config.word_size));
settings.cflag.CRTSCTS = config.handshake == .hardware;
settings.cflag.CSIZE = @enumFromInt(@intFromEnum(config.data_bits));

settings.cflag.PARENB = config.parity != .none;
settings.cflag.PARODD = config.parity == .odd or config.parity == .mark;
settings.cflag.CMSPAR = config.parity == .mark or config.parity == .space;
configureParity(&settings, config.parity);
configureFlowControl(&settings, config.flow_control);

settings.oflag = .{};
settings.lflag = .{};
settings.ispeed = config.baud_rate;
settings.ospeed = config.baud_rate;
const ispeed: *serialport.Config.BaudRate = @ptrCast(&settings.ispeed);
ispeed.* = if (config.input_baud_rate) |ibr| ibr else config.baud_rate;
const ospeed: *serialport.Config.BaudRate = @ptrCast(&settings.ospeed);
ospeed.* = config.baud_rate;

// Minimum arrived bytes before read returns.
settings.cc[@intFromEnum(linux.V.MIN)] = 0;
Expand Down Expand Up @@ -192,7 +273,7 @@ test {

const config: serialport.Config = .{
.baud_rate = .B230400,
.handshake = .software,
.flow_control = .software,
};

try configure(master, config);
Expand Down
10 changes: 6 additions & 4 deletions src/backend/macos.zig
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,18 @@ pub fn close(port: PortImpl) void {
pub fn configure(port: PortImpl, config: serialport.Config) !void {
var settings = try std.posix.tcgetattr(port.handle);

if (config.input_baud_rate != null) return error.InputBaudRateUnsupported;

settings.iflag = .{};
settings.iflag.INPCK = config.parity != .none;
settings.iflag.IXON = config.handshake == .software;
settings.iflag.IXOFF = config.handshake == .software;
settings.iflag.IXON = config.flow_control == .software;
settings.iflag.IXOFF = config.flow_control == .software;

settings.cflag = @bitCast(@intFromEnum(config.baud_rate));
settings.cflag.CREAD = true;
settings.cflag.CSTOPB = config.stop_bits == .two;
settings.cflag.CSIZE = @enumFromInt(@intFromEnum(config.word_size));
if (config.handshake == .hardware) {
settings.cflag.CSIZE = @enumFromInt(@intFromEnum(config.data_bits));
if (config.flow_control == .hardware) {
settings.cflag.CCTS_OFLOW = true;
settings.cflag.CRTS_IFLOW = true;
} else {
Expand Down
12 changes: 7 additions & 5 deletions src/backend/windows.zig
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,20 @@ pub fn configure(port: *const PortImpl, config: serialport.Config) !void {
var dcb: DCB = std.mem.zeroes(DCB);
dcb.DCBlength = @sizeOf(DCB);

if (config.input_baud_rate != null) return error.InputBaudRateUnsupported;

if (GetCommState(port.file.handle, &dcb) == 0)
return windows.unexpectedError(windows.GetLastError());

dcb.BaudRate = config.baud_rate;
dcb.flags = .{
.Parity = config.parity != .none,
.OutxCtsFlow = config.handshake == .hardware,
.OutX = config.handshake == .software,
.InX = config.handshake == .software,
.RtsControl = config.handshake == .hardware,
.OutxCtsFlow = config.flow_control == .hardware,
.OutX = config.flow_control == .software,
.InX = config.flow_control == .software,
.RtsControl = config.flow_control == .hardware,
};
dcb.ByteSize = 5 + @as(windows.BYTE, @intFromEnum(config.word_size));
dcb.ByteSize = 5 + @as(windows.BYTE, @intFromEnum(config.data_bits));
dcb.Parity = @intFromEnum(config.parity);
dcb.StopBits = if (config.stop_bits == .two) 2 else 0;
dcb.XonChar = 0x11;
Expand Down
47 changes: 29 additions & 18 deletions src/serialport.zig
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,21 @@ pub const Port = struct {
};

pub const Config = struct {
/// Baud rate. Used as both output and input baud rate, unless an input
/// baud is separately provided.
baud_rate: BaudRate,
/// Input-specific baud rate. Use only when a custom input baud rate
/// different than the output baud rate must be specified.
input_baud_rate: ?BaudRate = null,
/// Per-character parity bit use. Data bits must be less than eight to use
/// parity bit (eighth bit is used as parity bit).
parity: Parity = .none,
/// Number of bits used to signal end of character. Appended after all data
/// and parity bits.
stop_bits: StopBits = .one,
word_size: WordSize = .eight,
handshake: Handshake = .none,
/// Number of data bits to use per character.
data_bits: DataBits = .eight,
flow_control: FlowControl = .none,

pub const BaudRate = if (@hasDecl(backend, "BaudRate"))
backend.BaudRate
Expand All @@ -127,42 +137,43 @@ pub const Config = struct {
@compileError("unsupported backend/OS");

pub const Parity = enum(u3) {
/// No parity bit is used.
/// Do not create or check for parity bit per character.
none,
/// Parity bit is `0` when an odd number of bits is set in the data.
/// Parity bit set to `0` when data has odd number of `1` bits.
odd,
/// Parity bit is `0` when an even number of bits is set in the data.
/// Parity bit set to `0` when data has even number of `1` bits.
even,
/// Parity bit is always `1`.
/// Parity bit always set to `1`.
mark,
/// Parity bit is always `0`.
/// Parity bit always set to `0`. A.k.a. bit filling.
space,
};

pub const StopBits = enum(u1) {
/// Length of stop bits is one bit.
/// One bit to signal end of character.
one,
/// Length of stop bits is two bits.
/// Two bits to signal end of character.
two,
};

pub const WordSize = enum(u2) {
/// There are five data bits per word.
pub const DataBits = enum(u2) {
/// Five data bits per character.
five,
/// There are six data bits per word.
/// Six data bits per character.
six,
/// There are seven data bits per word.
/// Seven data bits per character.
seven,
/// There are eight data bits per word.
/// Eight data bits per character.
eight,
};

pub const Handshake = enum(u2) {
/// No handshake is used.
pub const FlowControl = enum(u2) {
/// No flow control is used.
none,
/// XON-XOFF software handshake is used.
/// XON-XOFF software flow control is used.
software,
/// Hardware handshake with RTS (RFR) / CTS is used.
/// Hardware flow control with RTS (RFR) / CTS is used. A.k.a. hardware
/// handshaking, pacing.
hardware,
};
};
Expand Down

0 comments on commit 1b850ad

Please sign in to comment.