Compare commits
No commits in common. "master" and "v0.0.1" have entirely different histories.
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +0,0 @@
|
|||||||
liberapay: Elara6331
|
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
|||||||
zig-out/
|
zig-out/
|
||||||
.zig-cache/
|
zig-cache/
|
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
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.
|
|
47
README.md
47
README.md
@ -4,17 +4,13 @@
|
|||||||
|
|
||||||
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 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 Zig project, so I'm open to any suggestions!
|
This is my first public 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
|
## 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 this repo or 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 `libgpiod`.
|
||||||
|
|
||||||
## Commands
|
I plan to eventually write a Zig replacement for `gpiodetect` and `gpioinfo`.
|
||||||
|
|
||||||
**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!
|
## Try it yourself!
|
||||||
|
|
||||||
@ -41,40 +37,3 @@ 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.root_module.addImport("gpio", gpio.module("gpio"));
|
|
||||||
```
|
|
||||||
|
|
||||||
And that's it! You should now be able to use `zig-gpio` via `@import("gpio");`
|
|
||||||
|
54
build.zig
54
build.zig
@ -1,71 +1,31 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const Item = struct {
|
|
||||||
name: []const u8,
|
|
||||||
src: []const u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// List of examples
|
|
||||||
const examples = [_]Item{
|
|
||||||
.{ .name = "blinky", .src = "examples/blinky.zig" },
|
|
||||||
.{ .name = "multi", .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 {
|
pub fn build(b: *std.Build) !void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
// Add the gpio module so it can be used by the package manager
|
var gpio_module = b.createModule(.{ .source_file = .{ .path = "index.zig" } });
|
||||||
const gpio_module = b.createModule(.{ .root_source_file = b.path("src/index.zig") });
|
|
||||||
try b.modules.put(b.dupe("gpio"), gpio_module);
|
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");
|
const examples_step = b.step("examples", "build all the examples");
|
||||||
|
|
||||||
// Add all the examples
|
inline for ([_]struct { name: []const u8, src: []const u8 }{
|
||||||
inline for (examples) |cfg| {
|
.{ .name = "blinky", .src = "_examples/blinky.zig" },
|
||||||
|
.{ .name = "multi", .src = "_examples/multi.zig" },
|
||||||
|
}) |cfg| {
|
||||||
const desc = try std.fmt.allocPrint(b.allocator, "build the {s} example", .{cfg.name});
|
const desc = try std.fmt.allocPrint(b.allocator, "build the {s} example", .{cfg.name});
|
||||||
const step = b.step(cfg.name, desc);
|
const step = b.step(cfg.name, desc);
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = cfg.name,
|
.name = cfg.name,
|
||||||
.root_source_file = b.path(cfg.src),
|
.root_source_file = .{ .path = cfg.src },
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
exe.root_module.addImport("gpio", gpio_module);
|
exe.addModule("gpio", gpio_module);
|
||||||
|
|
||||||
const build_step = b.addInstallArtifact(exe, .{});
|
const build_step = b.addInstallArtifact(exe, .{});
|
||||||
step.dependOn(&build_step.step);
|
step.dependOn(&build_step.step);
|
||||||
examples_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 = b.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,19 +3,19 @@ const gpio = @import("index.zig");
|
|||||||
|
|
||||||
/// Opens the file at path and uses the file descriptor to get the gpiochip.
|
/// Opens the file at path and uses the file descriptor to get the gpiochip.
|
||||||
pub fn getChip(path: []const u8) !Chip {
|
pub fn getChip(path: []const u8) !Chip {
|
||||||
const fl = try std.fs.openFileAbsolute(path, .{});
|
var fl = try std.fs.openFileAbsolute(path, .{});
|
||||||
return try getChipByFd(fl.handle);
|
return try getChipByFd(fl.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `getChip` but the `path` parameter is null-terminated.
|
/// Same as `getChip` but the `path` parameter is null-terminated.
|
||||||
pub fn getChipZ(path: [*:0]const u8) !Chip {
|
pub fn getChipZ(path: [*:0]const u8) !Chip {
|
||||||
const fl = try std.fs.openFileAbsoluteZ(path, .{});
|
var fl = try std.fs.openFileAbsoluteZ(path, .{});
|
||||||
return try getChipByFd(fl.handle);
|
return try getChipByFd(fl.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `chip` with the given file descriptor.
|
/// Returns a `chip` with the given file descriptor.
|
||||||
pub fn getChipByFd(fd: std.posix.fd_t) !Chip {
|
pub fn getChipByFd(fd: std.os.fd_t) !Chip {
|
||||||
const info = try gpio.uapi.getChipInfo(fd);
|
var info = try gpio.uapi.getChipInfo(fd);
|
||||||
return Chip{
|
return Chip{
|
||||||
.name = info.name,
|
.name = info.name,
|
||||||
.label = info.label,
|
.label = info.label,
|
||||||
@ -35,21 +35,11 @@ pub const Chip = struct {
|
|||||||
/// If it isn't set, "zig-gpio" will be used instead.
|
/// If it isn't set, "zig-gpio" will be used instead.
|
||||||
consumer: ?[gpio.uapi.MAX_NAME_SIZE]u8 = null,
|
consumer: ?[gpio.uapi.MAX_NAME_SIZE]u8 = null,
|
||||||
/// The file descriptor of the `gpiochip` device.
|
/// The file descriptor of the `gpiochip` device.
|
||||||
handle: std.posix.fd_t,
|
handle: std.os.fd_t,
|
||||||
// The amount of lines available under this device.
|
// The amount of lines available under this device.
|
||||||
lines: u32,
|
lines: u32,
|
||||||
closed: bool = false,
|
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`.
|
/// Sets the chip's consumer value to `consumer`.
|
||||||
pub fn setConsumer(self: *Chip, consumer: []const u8) !void {
|
pub fn setConsumer(self: *Chip, consumer: []const u8) !void {
|
||||||
if (consumer.len > gpio.uapi.MAX_NAME_SIZE) return error.ConsumerTooLong;
|
if (consumer.len > gpio.uapi.MAX_NAME_SIZE) return error.ConsumerTooLong;
|
||||||
@ -71,7 +61,7 @@ pub const Chip = struct {
|
|||||||
|
|
||||||
/// Requests and returns a single line at the given `offset`, from the given `chip`.
|
/// 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 {
|
pub fn requestLine(self: Chip, offset: u32, flags: gpio.uapi.LineFlags) !Line {
|
||||||
const l = try self.requestLines(&.{offset}, flags);
|
var l = try self.requestLines(&.{offset}, flags);
|
||||||
return Line{ .lines = l };
|
return Line{ .lines = l };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,14 +98,14 @@ pub const Chip = struct {
|
|||||||
pub fn close(self: *Chip) void {
|
pub fn close(self: *Chip) void {
|
||||||
if (self.closed) return;
|
if (self.closed) return;
|
||||||
self.closed = true;
|
self.closed = true;
|
||||||
std.posix.close(self.handle);
|
std.os.close(self.handle);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents a collection of lines requested from a `chip`.
|
/// Represents a collection of lines requested from a `chip`.
|
||||||
pub const Lines = struct {
|
pub const Lines = struct {
|
||||||
/// The file descriptor of the lines.
|
/// The file descriptor of the lines.
|
||||||
handle: std.posix.fd_t,
|
handle: std.os.fd_t,
|
||||||
/// The amount of lines being controlled.
|
/// The amount of lines being controlled.
|
||||||
num_lines: u32,
|
num_lines: u32,
|
||||||
/// The offsets of the lines being controlled.
|
/// The offsets of the lines being controlled.
|
||||||
@ -209,52 +199,18 @@ pub const Lines = struct {
|
|||||||
try gpio.uapi.setLineConfig(self.handle, lc);
|
try gpio.uapi.setLineConfig(self.handle, lc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the values of the lines at the given indices.
|
/// Returns the values of all the controlled lines as a bitset.
|
||||||
///
|
|
||||||
/// 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 {
|
pub fn getValues(self: Lines) !gpio.uapi.LineValueBitset {
|
||||||
if (self.closed) return error.LineClosed;
|
if (self.closed) return error.LineClosed;
|
||||||
var vals = gpio.uapi.LineValueBitset{ .mask = 0 };
|
const vals = try gpio.uapi.getLineValues(self.handle);
|
||||||
|
return vals.bits;
|
||||||
// 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`.
|
/// Releases all the resources held by the requested `lines`.
|
||||||
pub fn close(self: *Lines) void {
|
pub fn close(self: *Lines) void {
|
||||||
if (self.closed) return;
|
if (self.closed) return;
|
||||||
self.closed = true;
|
self.closed = true;
|
||||||
std.posix.close(self.handle);
|
std.os.close(self.handle);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -273,13 +229,6 @@ pub const Line = struct {
|
|||||||
try self.lines.setLow(&.{0});
|
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.
|
/// Sets the configuration flags of the line.
|
||||||
pub fn reconfigure(self: Line, flags: gpio.uapi.LineFlags) !void {
|
pub fn reconfigure(self: Line, flags: gpio.uapi.LineFlags) !void {
|
||||||
try self.lines.reconfigure(&.{0}, flags);
|
try self.lines.reconfigure(&.{0}, flags);
|
@ -2,7 +2,6 @@ pub const uapi = @import("uapi.zig");
|
|||||||
|
|
||||||
pub const getChip = @import("gpio.zig").getChip;
|
pub const getChip = @import("gpio.zig").getChip;
|
||||||
pub const getChipZ = @import("gpio.zig").getChipZ;
|
pub const getChipZ = @import("gpio.zig").getChipZ;
|
||||||
pub const getChipByFd = @import("gpio.zig").getChipByFd;
|
|
||||||
pub const Chip = @import("gpio.zig").Chip;
|
pub const Chip = @import("gpio.zig").Chip;
|
||||||
pub const Lines = @import("gpio.zig").Lines;
|
pub const Lines = @import("gpio.zig").Lines;
|
||||||
pub const Line = @import("gpio.zig").Line;
|
pub const Line = @import("gpio.zig").Line;
|
@ -1,25 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
@ -38,16 +38,6 @@ pub const LineInfo = extern struct {
|
|||||||
attrs: [MAX_LINE_NUM_ATTRS]LineAttribute,
|
attrs: [MAX_LINE_NUM_ATTRS]LineAttribute,
|
||||||
/// Reserved for future use
|
/// Reserved for future use
|
||||||
_padding: [4]u32,
|
_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
|
/// LineAttribute ID values
|
||||||
@ -176,20 +166,19 @@ pub const LineInfoChanged = extern struct {
|
|||||||
/// Returns an error based on the given return code
|
/// Returns an error based on the given return code
|
||||||
fn handleErrno(ret: usize) !void {
|
fn handleErrno(ret: usize) !void {
|
||||||
if (ret == 0) return;
|
if (ret == 0) return;
|
||||||
return switch (std.posix.errno(ret)) {
|
return switch (std.os.errno(ret)) {
|
||||||
.BUSY => error.DeviceIsBusy,
|
.BUSY => error.DeviceIsBusy,
|
||||||
.INVAL => error.InvalidArgument,
|
.INVAL => error.InvalidArgument,
|
||||||
.BADF => error.BadFileDescriptor,
|
.BADF => error.BadFileDescriptor,
|
||||||
.NOTTY => error.InappropriateIOCTLForDevice,
|
.NOTTY => error.InappropriateIOCTLForDevice,
|
||||||
.IO => error.IOError,
|
|
||||||
.FAULT => unreachable,
|
.FAULT => unreachable,
|
||||||
else => |err| return std.posix.unexpectedErrno(err),
|
else => |err| return std.os.unexpectedErrno(err),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `GPIO_GET_CHIPINFO_IOCTL` on the given fd and returns the resulting
|
/// Executes `GPIO_GET_CHIPINFO_IOCTL` on the given fd and returns the resulting
|
||||||
/// `ChipInfo` value
|
/// `ChipInfo` value
|
||||||
pub fn getChipInfo(fd: std.posix.fd_t) !ChipInfo {
|
pub fn getChipInfo(fd: std.os.fd_t) !ChipInfo {
|
||||||
const req = std.os.linux.IOCTL.IOR(0xB4, 0x01, ChipInfo);
|
const req = std.os.linux.IOCTL.IOR(0xB4, 0x01, ChipInfo);
|
||||||
var info = std.mem.zeroes(ChipInfo);
|
var info = std.mem.zeroes(ChipInfo);
|
||||||
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&info)));
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&info)));
|
||||||
@ -198,7 +187,7 @@ pub fn getChipInfo(fd: std.posix.fd_t) !ChipInfo {
|
|||||||
|
|
||||||
/// Executes `GPIO_V2_GET_LINEINFO_IOCTL` on the given fd and returns the resulting
|
/// Executes `GPIO_V2_GET_LINEINFO_IOCTL` on the given fd and returns the resulting
|
||||||
/// `LineInfo` value
|
/// `LineInfo` value
|
||||||
pub fn getLineInfo(fd: std.posix.fd_t, offset: u32) !LineInfo {
|
pub fn getLineInfo(fd: std.os.fd_t, offset: u32) !LineInfo {
|
||||||
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x05, LineInfo);
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x05, LineInfo);
|
||||||
var info = std.mem.zeroes(LineInfo);
|
var info = std.mem.zeroes(LineInfo);
|
||||||
info.offset = offset;
|
info.offset = offset;
|
||||||
@ -208,7 +197,7 @@ pub fn getLineInfo(fd: std.posix.fd_t, offset: u32) !LineInfo {
|
|||||||
|
|
||||||
/// Executes `GPIO_V2_GET_LINEINFO_WATCH_IOCTL` on the given fd and returns the resulting
|
/// Executes `GPIO_V2_GET_LINEINFO_WATCH_IOCTL` on the given fd and returns the resulting
|
||||||
/// `LineInfo` value
|
/// `LineInfo` value
|
||||||
pub fn watchLineInfo(fd: std.posix.fd_t, offset: u32) !LineInfo {
|
pub fn watchLineInfo(fd: std.os.fd_t, offset: u32) !LineInfo {
|
||||||
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x06, LineInfo);
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x06, LineInfo);
|
||||||
var info = std.mem.zeroes(LineInfo);
|
var info = std.mem.zeroes(LineInfo);
|
||||||
info.offset = offset;
|
info.offset = offset;
|
||||||
@ -217,37 +206,37 @@ pub fn watchLineInfo(fd: std.posix.fd_t, offset: u32) !LineInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `GPIO_GET_LINEINFO_UNWATCH_IOCTL` on the given fd
|
/// Executes `GPIO_GET_LINEINFO_UNWATCH_IOCTL` on the given fd
|
||||||
pub fn unwatchLineInfo(fd: std.posix.fd_t, offset: u32) !void {
|
pub fn unwatchLineInfo(fd: std.os.fd_t, offset: u32) !void {
|
||||||
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0C, u32);
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0C, u32);
|
||||||
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&offset)));
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&offset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `GPIO_V2_GET_LINE_IOCTL` on the given fd and returns the resulting
|
/// Executes `GPIO_V2_GET_LINE_IOCTL` on the given fd and returns the resulting
|
||||||
/// line descriptor
|
/// line descriptor
|
||||||
pub fn getLine(fd: std.posix.fd_t, lr: LineRequest) !std.posix.fd_t {
|
pub fn getLine(fd: std.os.fd_t, lr: LineRequest) !std.os.fd_t {
|
||||||
const lrp = &lr;
|
const lrp = &lr;
|
||||||
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x07, LineRequest);
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x07, LineRequest);
|
||||||
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(lrp)));
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(lrp)));
|
||||||
return lrp.fd;
|
return lrp.fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `GPIO_V2_LINE_GET_VALUES_IOCTL` on the given fd with the given mask,
|
/// Executes `GPIO_V2_LINE_GET_VALUES_IOCTL` on the given fd and returns the resulting
|
||||||
/// and returns a bitset representing all the line values.
|
/// `LineValues` value
|
||||||
pub fn getLineValues(fd: std.posix.fd_t, mask: LineValueBitset) !LineValueBitset {
|
pub fn getLineValues(fd: std.os.fd_t) !LineValues {
|
||||||
var vals = LineValues{ .mask = mask };
|
|
||||||
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0E, LineValues);
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0E, LineValues);
|
||||||
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&vals)));
|
var values = std.mem.zeroes(LineValues);
|
||||||
return vals.bits;
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&values)));
|
||||||
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `GPIO_V2_LINE_SET_VALUES_IOCTL` on the given fd
|
/// Executes `GPIO_V2_LINE_SET_VALUES_IOCTL` on the given fd
|
||||||
pub fn setLineValues(fd: std.posix.fd_t, lv: LineValues) !void {
|
pub fn setLineValues(fd: std.os.fd_t, lv: LineValues) !void {
|
||||||
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0F, LineValues);
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0F, LineValues);
|
||||||
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&lv)));
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&lv)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes `GPIO_V2_LINE_SET_CONFIG_IOCTL` on the given fd
|
/// Executes `GPIO_V2_LINE_SET_CONFIG_IOCTL` on the given fd
|
||||||
pub fn setLineConfig(fd: std.posix.fd_t, lc: LineConfig) !void {
|
pub fn setLineConfig(fd: std.os.fd_t, lc: LineConfig) !void {
|
||||||
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0D, LineConfig);
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0D, LineConfig);
|
||||||
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&lc)));
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&lc)));
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user