Compare commits

...

14 Commits

13 changed files with 408 additions and 37 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
liberapay: Elara6331

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Elara6331
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -4,13 +4,17 @@
This library can be used to access GPIO on devices such as [Raspberry Pis](https://www.raspberrypi.com/) or the [Milk-V Duo](https://milkv.io/duo) (which is the board I created it for and tested it with).
This is my first public Zig project, so I'm open to any suggestions!
This is my first Zig project, so I'm open to any suggestions!
_There's a companion article available on my website: https://www.elara.ws/articles/milkv-duo._
## Compatibility
**zig-gpio** uses the v2 character device API, which means it will work on any Linux system running kernel 5.10 or above. All you need to do is find out which `gpiochip` device controls which pin and what the offsets are, which you can do by either finding documentation online, or using the `gpiodetect` and `gpioinfo` tools from `libgpiod`.
**zig-gpio** uses the v2 character device API, which means it will work on any Linux system running kernel 5.10 or above. All you need to do is find out which `gpiochip` device controls which pin and what the offsets are, which you can do by either finding documentation online, or using the `gpiodetect` and `gpioinfo` tools from this repo or from `libgpiod`.
I plan to eventually write a Zig replacement for `gpiodetect` and `gpioinfo`.
## Commands
**zig-gpio** provides replacements for some of the `libgpiod` tools, such as `gpiodetect` and `gpioinfo`. You can build all of them using `zig build commands` or specific ones using `zig build <command>` (for example: `zig build gpiodetect`).
## Try it yourself!
@ -36,4 +40,41 @@ pub fn main() !void {
}
```
For more examples, see the [_examples](_examples) directory. You can build all the examples using the `zig build examples` command.
For more examples, see the [_examples](_examples) directory. You can build all the examples using the `zig build examples` command.
## Using zig-gpio in your project
If you don't have a zig project already, you can create one by running `zig init-exe` in a new folder.
To add `zig-gpio` as a dependency, there are two steps:
1. Add `zig-gpio` to your `build.zig.zon` file
2. Add `zig-gpio` to your `build.zig` file
If you don't have a `build.zig.zon` file, create one. If you do, just add `zig-gpio` as a dependency. Here's what it should look like:
```zig
.{
.name = "my_project",
.version = "0.0.1",
.dependencies = .{
.gpio = .{
.url = "https://gitea.elara.ws/Elara6331/zig-gpio/archive/v0.0.2.tar.gz",
.hash = "1220e3af3194d1154217423d60124ae3a46537c2253dbfb8057e9b550526d2885df1",
}
}
}
```
Then, in your `build.zig` file, add the following before `b.installArtifact(exe)`:
```zig
const gpio = b.dependency("gpio", .{
.target = target,
.optimize = optimize,
});
exe.addModule("gpio", gpio.module("gpio"));
```
And that's it! You should now be able to use `zig-gpio` via `@import("gpio");`

View File

@ -1,18 +1,37 @@
const std = @import("std");
const Item = struct {
name: []const u8,
src: []const u8,
};
/// List of examples
const examples = [_]Item{
.{ .name = "blinky", .src = "src/examples/blinky.zig" },
.{ .name = "multi", .src = "src/examples/multi.zig" },
};
/// List of commands
const commands = [_]Item{
.{ .name = "gpiodetect", .src = "src/cmd/detect.zig" },
.{ .name = "gpioinfo", .src = "src/cmd/info.zig" },
.{ .name = "gpioget", .src = "src/cmd/get.zig" },
.{ .name = "gpioset", .src = "src/cmd/set.zig" },
};
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
var gpio_module = b.createModule(.{ .source_file = .{ .path = "index.zig" } });
// Add the gpio module so it can be used by the package manager
const gpio_module = b.createModule(.{ .root_source_file = .{ .path = "src/index.zig" } });
try b.modules.put(b.dupe("gpio"), gpio_module);
// Create a step to build all the examples
const examples_step = b.step("examples", "build all the examples");
inline for ([_]struct { name: []const u8, src: []const u8 }{
.{ .name = "blinky", .src = "_examples/blinky.zig" },
.{ .name = "multi", .src = "_examples/multi.zig" },
}) |cfg| {
// Add all the examples
inline for (examples) |cfg| {
const desc = try std.fmt.allocPrint(b.allocator, "build the {s} example", .{cfg.name});
const step = b.step(cfg.name, desc);
@ -22,10 +41,31 @@ pub fn build(b: *std.Build) !void {
.target = target,
.optimize = optimize,
});
exe.addModule("gpio", gpio_module);
exe.root_module.addImport("gpio", gpio_module);
const build_step = b.addInstallArtifact(exe, .{});
step.dependOn(&build_step.step);
examples_step.dependOn(&build_step.step);
}
// Create a step to build all the commands
const commands_step = b.step("commands", "build all the commands");
// Add all the commands
inline for (commands) |cfg| {
const desc = try std.fmt.allocPrint(b.allocator, "build the {s} command", .{cfg.name});
const step = b.step(cfg.name, desc);
const exe = b.addExecutable(.{
.name = cfg.name,
.root_source_file = .{ .path = cfg.src },
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("gpio", gpio_module);
const build_step = b.addInstallArtifact(exe, .{});
step.dependOn(&build_step.step);
commands_step.dependOn(&build_step.step);
}
}

25
src/cmd/detect.zig Normal file
View File

@ -0,0 +1,25 @@
const std = @import("std");
const gpio = @import("gpio");
pub fn main() !void {
var iter_dir = try std.fs.openDirAbsolute("/dev", .{ .iterate = true });
defer iter_dir.close();
const stdout = std.io.getStdOut().writer();
var iter = iter_dir.iterate();
while (try iter.next()) |entry| {
if (!hasPrefix(entry.name, "gpiochip")) continue;
const fl = try iter_dir.openFile(entry.name, .{});
var chip = try gpio.getChipByFd(fl.handle);
defer chip.close(); // This will close the fd
try stdout.print("{s} [{s}] ({d} lines)\n", .{ chip.nameSlice(), chip.labelSlice(), chip.lines });
}
}
fn hasPrefix(s: []const u8, prefix: []const u8) bool {
if (s.len < prefix.len) return false;
return (std.mem.eql(u8, s[0..prefix.len], prefix));
}

55
src/cmd/get.zig Normal file
View File

@ -0,0 +1,55 @@
const std = @import("std");
const gpio = @import("gpio");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() == .ok);
const alloc = gpa.allocator();
var args = try std.process.argsAlloc(alloc);
defer std.process.argsFree(alloc, args);
const stdout = std.io.getStdOut().writer();
if (args.len < 3) {
try stdout.print("Usage: {s} <gpiochip> <line...>\n\n", .{args[0]});
return error.InsufficientArguments;
}
const path: []const u8 = if (hasPrefix(args[1], "gpiochip"))
try std.mem.concat(alloc, u8, &.{ "/dev/", args[1] })
else
try std.mem.concat(alloc, u8, &.{ "/dev/gpiochip", args[1] });
defer alloc.free(path);
var chip = try gpio.getChip(path);
defer chip.close();
try chip.setConsumer("gpioget");
var offsets = std.ArrayList(u32).init(alloc);
defer offsets.deinit();
// Iterate over each argument starting from the second one
for (args[2..args.len]) |argument| {
// Parse each argument as an integer and add it to offsets
const offset = try std.fmt.parseUnsigned(u32, argument, 10);
try offsets.append(offset);
}
var lines = try chip.requestLines(offsets.items, .{ .input = true });
defer lines.close();
const vals = try lines.getValues();
var i: u32 = 0;
while (i < args.len - 2) : (i += 1) {
const value: u1 = if (vals.isSet(i)) 1 else 0;
try stdout.print("{d} ", .{value});
}
try stdout.writeByte('\n');
}
fn hasPrefix(s: []const u8, prefix: []const u8) bool {
if (s.len < prefix.len) return false;
return (std.mem.eql(u8, s[0..prefix.len], prefix));
}

72
src/cmd/info.zig Normal file
View File

@ -0,0 +1,72 @@
const std = @import("std");
const gpio = @import("gpio");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() == .ok);
const alloc = gpa.allocator();
var dir = try std.fs.openDirAbsolute("/dev", .{});
defer dir.close();
var args = std.process.args();
_ = args.skip(); // Skip the program name
// Iterate over each argument
while (args.next()) |argument| {
const hasGpiochip = hasPrefix(argument, "gpiochip");
// If the argument has the "gpiochip" prefix,
// just use it unchanged. Otherwise, add the prefix.
const filename: []const u8 = if (hasGpiochip)
argument
else
try std.mem.concat(alloc, u8, &.{ "gpiochip", argument });
// We only need to free if we actually allocated,
// which only happens if there was no prefix.
defer if (!hasGpiochip) alloc.free(filename);
const fl = try dir.openFile(filename, .{});
var chip = try gpio.getChipByFd(fl.handle);
defer chip.close(); // This will close the fd
const stdout = std.io.getStdOut().writer();
try stdout.print("{s} - {d} lines:\n", .{ chip.nameSlice(), chip.lines });
var offset: u32 = 0;
while (offset < chip.lines) : (offset += 1) {
const lineInfo = try chip.getLineInfo(offset);
// Create an arraylist to store all the flag strings
var flags = std.ArrayList([]const u8).init(alloc);
defer flags.deinit();
// Appand any relevant flag strings to the array list
if (lineInfo.flags.input) try flags.append("input");
if (lineInfo.flags.output) try flags.append("output");
if (lineInfo.flags.used) try flags.append("used");
if (lineInfo.flags.active_low) try flags.append("active_low");
if (lineInfo.flags.edge_rising) try flags.append("edge_rising");
if (lineInfo.flags.edge_falling) try flags.append("edge_falling");
if (lineInfo.flags.open_drain) try flags.append("open_drain");
if (lineInfo.flags.open_source) try flags.append("open_source");
if (lineInfo.flags.bias_pull_up) try flags.append("bias_pull_up");
if (lineInfo.flags.bias_pull_down) try flags.append("bias_pull_down");
// Join the array list into a string
const flagStr = try std.mem.join(alloc, ", ", flags.items);
defer alloc.free(flagStr);
const name = if (lineInfo.name[0] != 0) lineInfo.nameSlice() else "<unnamed>";
const consumer = if (lineInfo.flags.used) lineInfo.consumerSlice() else "<unused>";
try stdout.print(" line {d}: {s} {s} [{s}]\n", .{ offset, name, consumer, flagStr });
}
}
}
fn hasPrefix(s: []const u8, prefix: []const u8) bool {
if (s.len < prefix.len) return false;
return (std.mem.eql(u8, s[0..prefix.len], prefix));
}

53
src/cmd/set.zig Normal file
View File

@ -0,0 +1,53 @@
const std = @import("std");
const gpio = @import("gpio");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() == .ok);
const alloc = gpa.allocator();
var args = try std.process.argsAlloc(alloc);
defer std.process.argsFree(alloc, args);
const stdout = std.io.getStdOut().writer();
if (args.len < 3) {
try stdout.print("Usage: {s} <gpiochip> <line=value...>\n\n", .{args[0]});
return error.InsufficientArguments;
}
const path: []const u8 = if (hasPrefix(args[1], "gpiochip"))
try std.mem.concat(alloc, u8, &.{ "/dev/", args[1] })
else
try std.mem.concat(alloc, u8, &.{ "/dev/gpiochip", args[1] });
defer alloc.free(path);
var chip = try gpio.getChip(path);
defer chip.close();
try chip.setConsumer("gpioset");
var values = std.AutoArrayHashMap(u32, bool).init(alloc);
defer values.deinit();
// Iterate over each argument starting from the second one
for (args[2..args.len]) |argument| {
// Get the index of the equals sign in the argument
const eqIndex = std.mem.indexOf(u8, argument, "=") orelse return error.InvalidArgument;
// Parse each argument's offset and value, and add it to the values map
const offset = try std.fmt.parseUnsigned(u32, argument[0..eqIndex], 10);
const value = try std.fmt.parseUnsigned(u1, argument[eqIndex + 1 .. argument.len], 10);
try values.put(offset, value != 0);
}
var lines = try chip.requestLines(values.keys(), .{ .output = true });
defer lines.close();
var vals = gpio.uapi.LineValueBitset{ .mask = 0 };
for (0.., values.values()) |i, val| vals.setValue(i, val);
try lines.setAllValues(vals);
}
fn hasPrefix(s: []const u8, prefix: []const u8) bool {
if (s.len < prefix.len) return false;
return (std.mem.eql(u8, s[0..prefix.len], prefix));
}

View File

@ -3,19 +3,19 @@ const gpio = @import("index.zig");
/// Opens the file at path and uses the file descriptor to get the gpiochip.
pub fn getChip(path: []const u8) !Chip {
var fl = try std.fs.openFileAbsolute(path, .{});
const fl = try std.fs.openFileAbsolute(path, .{});
return try getChipByFd(fl.handle);
}
/// Same as `getChip` but the `path` parameter is null-terminated.
pub fn getChipZ(path: [*:0]const u8) !Chip {
var fl = try std.fs.openFileAbsoluteZ(path, .{});
const fl = try std.fs.openFileAbsoluteZ(path, .{});
return try getChipByFd(fl.handle);
}
/// Returns a `chip` with the given file descriptor.
pub fn getChipByFd(fd: std.os.fd_t) !Chip {
var info = try gpio.uapi.getChipInfo(fd);
pub fn getChipByFd(fd: std.posix.fd_t) !Chip {
const info = try gpio.uapi.getChipInfo(fd);
return Chip{
.name = info.name,
.label = info.label,
@ -35,11 +35,21 @@ pub const Chip = struct {
/// If it isn't set, "zig-gpio" will be used instead.
consumer: ?[gpio.uapi.MAX_NAME_SIZE]u8 = null,
/// The file descriptor of the `gpiochip` device.
handle: std.os.fd_t,
handle: std.posix.fd_t,
// The amount of lines available under this device.
lines: u32,
closed: bool = false,
/// Returns the chip's name as a slice without any null characters
pub fn nameSlice(self: *Chip) []const u8 {
return std.mem.sliceTo(&self.name, 0);
}
/// Returns the chip's label as a slice without any null characters
pub fn labelSlice(self: *Chip) []const u8 {
return std.mem.sliceTo(&self.label, 0);
}
/// Sets the chip's consumer value to `consumer`.
pub fn setConsumer(self: *Chip, consumer: []const u8) !void {
if (consumer.len > gpio.uapi.MAX_NAME_SIZE) return error.ConsumerTooLong;
@ -61,7 +71,7 @@ pub const Chip = struct {
/// Requests and returns a single line at the given `offset`, from the given `chip`.
pub fn requestLine(self: Chip, offset: u32, flags: gpio.uapi.LineFlags) !Line {
var l = try self.requestLines(&.{offset}, flags);
const l = try self.requestLines(&.{offset}, flags);
return Line{ .lines = l };
}
@ -98,14 +108,14 @@ pub const Chip = struct {
pub fn close(self: *Chip) void {
if (self.closed) return;
self.closed = true;
std.os.close(self.handle);
std.posix.close(self.handle);
}
};
/// Represents a collection of lines requested from a `chip`.
pub const Lines = struct {
/// The file descriptor of the lines.
handle: std.os.fd_t,
handle: std.posix.fd_t,
/// The amount of lines being controlled.
num_lines: u32,
/// The offsets of the lines being controlled.
@ -199,18 +209,52 @@ pub const Lines = struct {
try gpio.uapi.setLineConfig(self.handle, lc);
}
/// Returns the values of all the controlled lines as a bitset.
/// Sets the values of the lines at the given indices.
///
/// Note that this function takes indices and not offsets.
/// The indices correspond to the index of the offset in your request.
/// For example, if you requested `&.{22, 20, 23}`,
/// `22` will correspond to `0`, `20` will correspond to `1`,
/// and `23` will correspond to `2`.
pub fn setValues(self: Lines, indices: []const u32, vals: gpio.uapi.LineValueBitset) !void {
if (self.closed) return error.LineClosed;
var lv = gpio.uapi.LineValues{ .bits = vals };
for (indices) |index| {
if (index >= self.num_lines) return error.IndexOutOfRange;
lv.mask.set(index);
}
return try gpio.uapi.setLineValues(self.handle, lv);
}
/// Sets the values of all the controlled lines
pub fn setAllValues(self: Lines, vals: gpio.uapi.LineValueBitset) !void {
if (self.closed) return error.LineClosed;
var lv = gpio.uapi.LineValues{ .bits = vals };
// Add all the indices to the bitset of values to set
var i: u32 = 0;
while (i < self.num_lines) : (i += 1) lv.mask.set(i);
return try gpio.uapi.setLineValues(self.handle, lv);
}
/// Gets the values of all the controlled lines as a bitset
pub fn getValues(self: Lines) !gpio.uapi.LineValueBitset {
if (self.closed) return error.LineClosed;
const vals = try gpio.uapi.getLineValues(self.handle);
return vals.bits;
var vals = gpio.uapi.LineValueBitset{ .mask = 0 };
// Add all the indices to the bitset of values to get
var i: u32 = 0;
while (i < self.num_lines) : (i += 1) vals.set(i);
return try gpio.uapi.getLineValues(self.handle, vals);
}
/// Releases all the resources held by the requested `lines`.
pub fn close(self: *Lines) void {
if (self.closed) return;
self.closed = true;
std.os.close(self.handle);
std.posix.close(self.handle);
}
};
@ -229,6 +273,13 @@ pub const Line = struct {
try self.lines.setLow(&.{0});
}
// Sets the value of the line.
pub fn setValue(self: Line, value: bool) !void {
var vals = gpio.uapi.LineValueBitset{ .mask = 0 };
vals.setValue(0, value);
try self.lines.setValues(&.{0}, vals);
}
/// Sets the configuration flags of the line.
pub fn reconfigure(self: Line, flags: gpio.uapi.LineFlags) !void {
try self.lines.reconfigure(&.{0}, flags);

View File

@ -2,6 +2,7 @@ pub const uapi = @import("uapi.zig");
pub const getChip = @import("gpio.zig").getChip;
pub const getChipZ = @import("gpio.zig").getChipZ;
pub const getChipByFd = @import("gpio.zig").getChipByFd;
pub const Chip = @import("gpio.zig").Chip;
pub const Lines = @import("gpio.zig").Lines;
pub const Line = @import("gpio.zig").Line;

View File

@ -38,6 +38,16 @@ pub const LineInfo = extern struct {
attrs: [MAX_LINE_NUM_ATTRS]LineAttribute,
/// Reserved for future use
_padding: [4]u32,
/// Returns the line's name as a slice without any null characters
pub fn nameSlice(self: *const LineInfo) []const u8 {
return std.mem.sliceTo(&self.name, 0);
}
/// Returns the line's consumer as a slice without any null characters
pub fn consumerSlice(self: *const LineInfo) []const u8 {
return std.mem.sliceTo(&self.consumer, 0);
}
};
/// LineAttribute ID values
@ -166,19 +176,20 @@ pub const LineInfoChanged = extern struct {
/// Returns an error based on the given return code
fn handleErrno(ret: usize) !void {
if (ret == 0) return;
return switch (std.os.errno(ret)) {
return switch (std.posix.errno(ret)) {
.BUSY => error.DeviceIsBusy,
.INVAL => error.InvalidArgument,
.BADF => error.BadFileDescriptor,
.NOTTY => error.InappropriateIOCTLForDevice,
.IO => error.IOError,
.FAULT => unreachable,
else => |err| return std.os.unexpectedErrno(err),
else => |err| return std.posix.unexpectedErrno(err),
};
}
/// Executes `GPIO_GET_CHIPINFO_IOCTL` on the given fd and returns the resulting
/// `ChipInfo` value
pub fn getChipInfo(fd: std.os.fd_t) !ChipInfo {
pub fn getChipInfo(fd: std.posix.fd_t) !ChipInfo {
const req = std.os.linux.IOCTL.IOR(0xB4, 0x01, ChipInfo);
var info = std.mem.zeroes(ChipInfo);
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&info)));
@ -187,7 +198,7 @@ pub fn getChipInfo(fd: std.os.fd_t) !ChipInfo {
/// Executes `GPIO_V2_GET_LINEINFO_IOCTL` on the given fd and returns the resulting
/// `LineInfo` value
pub fn getLineInfo(fd: std.os.fd_t, offset: u32) !LineInfo {
pub fn getLineInfo(fd: std.posix.fd_t, offset: u32) !LineInfo {
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x05, LineInfo);
var info = std.mem.zeroes(LineInfo);
info.offset = offset;
@ -197,7 +208,7 @@ pub fn getLineInfo(fd: std.os.fd_t, offset: u32) !LineInfo {
/// Executes `GPIO_V2_GET_LINEINFO_WATCH_IOCTL` on the given fd and returns the resulting
/// `LineInfo` value
pub fn watchLineInfo(fd: std.os.fd_t, offset: u32) !LineInfo {
pub fn watchLineInfo(fd: std.posix.fd_t, offset: u32) !LineInfo {
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x06, LineInfo);
var info = std.mem.zeroes(LineInfo);
info.offset = offset;
@ -206,37 +217,37 @@ pub fn watchLineInfo(fd: std.os.fd_t, offset: u32) !LineInfo {
}
/// Executes `GPIO_GET_LINEINFO_UNWATCH_IOCTL` on the given fd
pub fn unwatchLineInfo(fd: std.os.fd_t, offset: u32) !void {
pub fn unwatchLineInfo(fd: std.posix.fd_t, offset: u32) !void {
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0C, u32);
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&offset)));
}
/// Executes `GPIO_V2_GET_LINE_IOCTL` on the given fd and returns the resulting
/// line descriptor
pub fn getLine(fd: std.os.fd_t, lr: LineRequest) !std.os.fd_t {
pub fn getLine(fd: std.posix.fd_t, lr: LineRequest) !std.posix.fd_t {
const lrp = &lr;
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x07, LineRequest);
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(lrp)));
return lrp.fd;
}
/// Executes `GPIO_V2_LINE_GET_VALUES_IOCTL` on the given fd and returns the resulting
/// `LineValues` value
pub fn getLineValues(fd: std.os.fd_t) !LineValues {
/// Executes `GPIO_V2_LINE_GET_VALUES_IOCTL` on the given fd with the given mask,
/// and returns a bitset representing all the line values.
pub fn getLineValues(fd: std.posix.fd_t, mask: LineValueBitset) !LineValueBitset {
var vals = LineValues{ .mask = mask };
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0E, LineValues);
var values = std.mem.zeroes(LineValues);
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&values)));
return values;
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&vals)));
return vals.bits;
}
/// Executes `GPIO_V2_LINE_SET_VALUES_IOCTL` on the given fd
pub fn setLineValues(fd: std.os.fd_t, lv: LineValues) !void {
pub fn setLineValues(fd: std.posix.fd_t, lv: LineValues) !void {
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0F, LineValues);
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&lv)));
}
/// Executes `GPIO_V2_LINE_SET_CONFIG_IOCTL` on the given fd
pub fn setLineConfig(fd: std.os.fd_t, lc: LineConfig) !void {
pub fn setLineConfig(fd: std.posix.fd_t, lc: LineConfig) !void {
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0D, LineConfig);
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&lc)));
}