Initial Commit
This commit is contained in:
commit
92f0d28999
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
zig-out/
|
||||||
|
zig-cache/
|
39
README.md
Normal file
39
README.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# zig-gpio
|
||||||
|
|
||||||
|
**zig-gpio** is a Zig library for controlling GPIO lines on Linux systems
|
||||||
|
|
||||||
|
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!
|
||||||
|
|
||||||
|
## 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`.
|
||||||
|
|
||||||
|
I plan to eventually write a Zig replacement for `gpiodetect` and `gpioinfo`.
|
||||||
|
|
||||||
|
## Try it yourself!
|
||||||
|
|
||||||
|
Here's an example of a really simple program that requests pin 22 from `gpiochip2` and makes it blink at a 1 second interval. That pin offset is the LED of a Milk-V Duo board, so if you're using a different board, make sure to change it.
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const std = @import("std");
|
||||||
|
const gpio = @import("gpio");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var chip = try gpio.getChip("/dev/gpiochip2");
|
||||||
|
defer chip.close();
|
||||||
|
std.debug.print("Chip Name: {s}\n", .{chip.name});
|
||||||
|
|
||||||
|
var line = try chip.requestLine(22, .{ .output = true });
|
||||||
|
defer line.close();
|
||||||
|
while (true) {
|
||||||
|
try line.setHigh();
|
||||||
|
std.time.sleep(std.time.ns_per_s);
|
||||||
|
try line.setLow();
|
||||||
|
std.time.sleep(std.time.ns_per_s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more examples, see the [_examples](_examples) directory. You can build all the examples using the `zig build examples` command.
|
19
_examples/blinky.zig
Normal file
19
_examples/blinky.zig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const gpio = @import("gpio");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var chip = try gpio.getChip("/dev/gpiochip2");
|
||||||
|
defer chip.close();
|
||||||
|
try chip.setConsumer("blinky");
|
||||||
|
|
||||||
|
std.debug.print("Chip Name: {s}\n", .{chip.name});
|
||||||
|
|
||||||
|
var line = try chip.requestLine(22, .{ .output = true });
|
||||||
|
defer line.close();
|
||||||
|
while (true) {
|
||||||
|
try line.setHigh();
|
||||||
|
std.time.sleep(std.time.ns_per_s);
|
||||||
|
try line.setLow();
|
||||||
|
std.time.sleep(std.time.ns_per_s);
|
||||||
|
}
|
||||||
|
}
|
27
_examples/multi.zig
Normal file
27
_examples/multi.zig
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const gpio = @import("gpio");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var chip = try gpio.getChip("/dev/gpiochip0");
|
||||||
|
defer chip.close();
|
||||||
|
try chip.setConsumer("multi");
|
||||||
|
|
||||||
|
std.debug.print("Chip Name: {s}\n", .{chip.name});
|
||||||
|
|
||||||
|
// Request the lines with offsets 26, 27, 28, and 29 as outputs.
|
||||||
|
var lines = try chip.requestLines(&.{ 26, 27, 28, 29 }, .{ .output = true });
|
||||||
|
defer lines.close();
|
||||||
|
// Alternate between lines 27/29 and 26/28 being high
|
||||||
|
while (true) {
|
||||||
|
// Set lines 27 and 29 as low (off)
|
||||||
|
try lines.setLow(&.{ 1, 3 });
|
||||||
|
// Set lines 26 and 28 as high (on)
|
||||||
|
try lines.setHigh(&.{ 0, 2 });
|
||||||
|
std.time.sleep(std.time.ns_per_s);
|
||||||
|
// Set lines 26 and 28 as low (off)
|
||||||
|
try lines.setLow(&.{ 0, 2 });
|
||||||
|
// Set lines 27 and 28 as high (on)
|
||||||
|
try lines.setHigh(&.{ 1, 3 });
|
||||||
|
std.time.sleep(std.time.ns_per_s);
|
||||||
|
}
|
||||||
|
}
|
31
build.zig
Normal file
31
build.zig
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
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" } });
|
||||||
|
try b.modules.put(b.dupe("gpio"), gpio_module);
|
||||||
|
|
||||||
|
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| {
|
||||||
|
const desc = try std.fmt.allocPrint(b.allocator, "build the {s} example", .{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.addModule("gpio", gpio_module);
|
||||||
|
|
||||||
|
const build_step = b.addInstallArtifact(exe, .{});
|
||||||
|
step.dependOn(&build_step.step);
|
||||||
|
examples_step.dependOn(&build_step.step);
|
||||||
|
}
|
||||||
|
}
|
5
build.zig.zon
Normal file
5
build.zig.zon
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.{
|
||||||
|
.name = "zig-gpio",
|
||||||
|
.version = "0.0.1",
|
||||||
|
.paths = .{""},
|
||||||
|
}
|
252
gpio.zig
Normal file
252
gpio.zig
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
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, .{});
|
||||||
|
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, .{});
|
||||||
|
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);
|
||||||
|
return Chip{
|
||||||
|
.name = info.name,
|
||||||
|
.label = info.label,
|
||||||
|
.handle = fd,
|
||||||
|
.lines = info.lines,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a single Linux `gpiochip` character device.
|
||||||
|
pub const Chip = struct {
|
||||||
|
/// The name of the `gpiochip` device.
|
||||||
|
name: [gpio.uapi.MAX_NAME_SIZE]u8,
|
||||||
|
/// The label of the `gpiochip` device
|
||||||
|
label: [gpio.uapi.MAX_NAME_SIZE]u8,
|
||||||
|
/// An optional consumer value to use when requesting lines.
|
||||||
|
/// Can be set using `set_consumer` or `set_consumer_z`.
|
||||||
|
/// 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,
|
||||||
|
// The amount of lines available under this device.
|
||||||
|
lines: u32,
|
||||||
|
closed: bool = false,
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
self.consumer = std.mem.zeroes([gpio.uapi.MAX_NAME_SIZE]u8);
|
||||||
|
std.mem.copyForwards(u8, &self.consumer.?, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as setConsumer but the `consumer` parameter is null-terminated.
|
||||||
|
pub fn setConsumerZ(self: *Chip, consumer: [*:0]const u8) !void {
|
||||||
|
self.consumer = std.mem.zeroes([gpio.uapi.MAX_NAME_SIZE]u8);
|
||||||
|
@memcpy(&self.consumer.?, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns information about the GPIO line at the given `offset`.
|
||||||
|
pub fn getLineInfo(self: Chip, offset: u32) !gpio.uapi.LineInfo {
|
||||||
|
if (self.closed) return error.ChipClosed;
|
||||||
|
return gpio.uapi.getLineInfo(self.handle, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
return Line{ .lines = l };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests control of a collection of lines on the chip. If granted, control is maintained until
|
||||||
|
/// the `lines` are closed.
|
||||||
|
pub fn requestLines(self: Chip, offsets: []const u32, flags: gpio.uapi.LineFlags) !Lines {
|
||||||
|
if (self.closed) return error.ChipClosed;
|
||||||
|
if (offsets.len > gpio.uapi.MAX_LINES) return error.TooManyLines;
|
||||||
|
|
||||||
|
var lr = std.mem.zeroes(gpio.uapi.LineRequest);
|
||||||
|
lr.num_lines = @truncate(offsets.len);
|
||||||
|
lr.config.flags = flags;
|
||||||
|
|
||||||
|
if (self.consumer != null) {
|
||||||
|
lr.consumer = self.consumer.?;
|
||||||
|
} else {
|
||||||
|
std.mem.copyForwards(u8, &lr.consumer, "zig-gpio");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0.., offsets) |i, offset| {
|
||||||
|
if (offset >= self.lines) return error.OffsetOutOfRange;
|
||||||
|
lr.offsets[i] = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const line_fd = try gpio.uapi.getLine(self.handle, lr);
|
||||||
|
return Lines{
|
||||||
|
.handle = line_fd,
|
||||||
|
.num_lines = lr.num_lines,
|
||||||
|
.offsets = offsets,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases all resources held by the `chip`.
|
||||||
|
pub fn close(self: *Chip) void {
|
||||||
|
if (self.closed) return;
|
||||||
|
self.closed = true;
|
||||||
|
std.os.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,
|
||||||
|
/// The amount of lines being controlled.
|
||||||
|
num_lines: u32,
|
||||||
|
/// The offsets of the lines being controlled.
|
||||||
|
offsets: []const u32,
|
||||||
|
closed: bool = false,
|
||||||
|
|
||||||
|
/// Sets the lines at the given indices as high (on).
|
||||||
|
///
|
||||||
|
/// 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 setHigh(self: Lines, indices: []const u32) !void {
|
||||||
|
if (self.closed) return error.LineClosed;
|
||||||
|
|
||||||
|
var vals = gpio.uapi.LineValues{};
|
||||||
|
for (indices) |index| {
|
||||||
|
if (index >= self.num_lines) return error.IndexOutOfRange;
|
||||||
|
vals.bits.set(index);
|
||||||
|
vals.mask.set(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
try gpio.uapi.setLineValues(self.handle, vals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the lines at the given indices as low (off).
|
||||||
|
///
|
||||||
|
/// 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 setLow(self: Lines, indices: []const u32) !void {
|
||||||
|
if (self.closed) return error.LineClosed;
|
||||||
|
|
||||||
|
var vals = gpio.uapi.LineValues{};
|
||||||
|
for (indices) |index| {
|
||||||
|
if (index >= self.num_lines) return error.IndexOutOfRange;
|
||||||
|
vals.mask.set(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
try gpio.uapi.setLineValues(self.handle, vals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the configuration flags 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 reconfigure(self: Lines, indices: []const u32, flags: gpio.uapi.LineFlags) !void {
|
||||||
|
var lc = std.mem.zeroes(gpio.uapi.LineConfig);
|
||||||
|
lc.attrs[0] = gpio.uapi.LineConfigAttribute{
|
||||||
|
.attr = .{
|
||||||
|
.id = .Flags,
|
||||||
|
.data = .{ .flags = flags },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (indices) |index| {
|
||||||
|
if (index >= self.num_lines) return error.IndexOutOfRange;
|
||||||
|
lc.attrs[0].mask.set(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
try gpio.uapi.setLineConfig(self.handle, lc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the debounce period 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 setDebouncePeriod(self: Lines, indices: []const u32, duration_us: u32) !void {
|
||||||
|
var lc = std.mem.zeroes(gpio.uapi.LineConfig);
|
||||||
|
lc.attrs[0] = gpio.uapi.LineConfigAttribute{
|
||||||
|
.attr = .{
|
||||||
|
.id = .Debounce,
|
||||||
|
.data = .{ .debounce_period_us = duration_us },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (indices) |index| {
|
||||||
|
if (index >= self.num_lines) return error.IndexOutOfRange;
|
||||||
|
lc.attrs[0].mask.set(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
try gpio.uapi.setLineConfig(self.handle, lc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents a single line requested from a `chip`.
|
||||||
|
pub const Line = struct {
|
||||||
|
/// The `Lines` value containing the line.
|
||||||
|
lines: Lines,
|
||||||
|
|
||||||
|
/// Sets the line as high (on).
|
||||||
|
pub fn setHigh(self: Line) !void {
|
||||||
|
try self.lines.setHigh(&.{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the line as low (off).
|
||||||
|
pub fn setLow(self: Line) !void {
|
||||||
|
try self.lines.setLow(&.{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the configuration flags of the line.
|
||||||
|
pub fn reconfigure(self: Line, flags: gpio.uapi.LineFlags) !void {
|
||||||
|
try self.lines.reconfigure(&.{0}, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the debounce period of the line.
|
||||||
|
pub fn setDebouncePeriod(self: Line, duration_us: u32) !void {
|
||||||
|
try self.lines.setDebouncePeriod(&.{0}, duration_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the current value of the line as a boolean.
|
||||||
|
pub fn getValue(self: Line) !bool {
|
||||||
|
const vals = try self.lines.getValues();
|
||||||
|
return vals.isSet(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases all the resources held by the `line`.
|
||||||
|
pub fn close(self: *Line) void {
|
||||||
|
self.lines.close();
|
||||||
|
}
|
||||||
|
};
|
7
index.zig
Normal file
7
index.zig
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub const uapi = @import("uapi.zig");
|
||||||
|
|
||||||
|
pub const getChip = @import("gpio.zig").getChip;
|
||||||
|
pub const getChipZ = @import("gpio.zig").getChipZ;
|
||||||
|
pub const Chip = @import("gpio.zig").Chip;
|
||||||
|
pub const Lines = @import("gpio.zig").Lines;
|
||||||
|
pub const Line = @import("gpio.zig").Line;
|
242
uapi.zig
Normal file
242
uapi.zig
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// The maximum size of name and label arrays
|
||||||
|
pub const MAX_NAME_SIZE = 32;
|
||||||
|
|
||||||
|
/// Information about a certain GPIO chip
|
||||||
|
pub const ChipInfo = extern struct {
|
||||||
|
/// The Linux kernel name of this GPIO chip
|
||||||
|
name: [MAX_NAME_SIZE]u8,
|
||||||
|
/// A functional name for this GPIO chip, such as a product
|
||||||
|
/// number, may be empty (i.e. `label[0] == '\0'`)
|
||||||
|
label: [MAX_NAME_SIZE]u8,
|
||||||
|
/// The number of GPIO lines on this chip
|
||||||
|
lines: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The maximum number of configuration attributes associated with a line request.
|
||||||
|
pub const MAX_LINE_NUM_ATTRS = 10;
|
||||||
|
|
||||||
|
/// Information about a certain GPIO line
|
||||||
|
pub const LineInfo = extern struct {
|
||||||
|
/// The name of this GPIO line, such as the output pin of the line on
|
||||||
|
/// the chip, a rail or a pin header name on a board, as specified by the
|
||||||
|
/// GPIO chip, may be empty (i.e. `name[0] == '\0'`)
|
||||||
|
name: [MAX_NAME_SIZE]u8,
|
||||||
|
/// a functional name for the consumer of this GPIO line as set
|
||||||
|
/// by whatever is using it, will be empty if there is no current user,
|
||||||
|
/// but may also be empty if the consumer doesn't set this up
|
||||||
|
consumer: [MAX_NAME_SIZE]u8,
|
||||||
|
/// The local offset on this GPIO chip, fill this in when
|
||||||
|
/// requesting the line information from the kernel
|
||||||
|
offset: u32,
|
||||||
|
/// The number of attributes in `attrs`
|
||||||
|
num_attrs: u32,
|
||||||
|
/// Configuration flags for this GPIO line
|
||||||
|
flags: LineFlags,
|
||||||
|
/// The configuration attributes associated with the line
|
||||||
|
attrs: [MAX_LINE_NUM_ATTRS]LineAttribute,
|
||||||
|
/// Reserved for future use
|
||||||
|
_padding: [4]u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// LineAttribute ID values
|
||||||
|
pub const LineAttributeId = enum(u32) {
|
||||||
|
/// Indicates that the line attribute contains flags
|
||||||
|
Flags = 1,
|
||||||
|
/// Indicates that the line attribute contains output values
|
||||||
|
OutputValues = 2,
|
||||||
|
/// Indicates that the line attribute contains a debounce period
|
||||||
|
Debounce = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A configurable attribute of a line
|
||||||
|
pub const LineAttribute = extern struct {
|
||||||
|
id: LineAttributeId,
|
||||||
|
_padding: u32 = 0,
|
||||||
|
data: extern union {
|
||||||
|
flags: LineFlags,
|
||||||
|
values: u64,
|
||||||
|
debounce_period_us: u32,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A configuration attribute
|
||||||
|
pub const LineConfigAttribute = extern struct {
|
||||||
|
attr: LineAttribute,
|
||||||
|
mask: LineValueBitset = .{ .mask = 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Maximum number of requested lines
|
||||||
|
pub const MAX_LINES = 64;
|
||||||
|
|
||||||
|
/// Information about a request for GPIO lines
|
||||||
|
pub const LineRequest = extern struct {
|
||||||
|
offsets: [MAX_LINES]u32,
|
||||||
|
consumer: [MAX_NAME_SIZE]u8,
|
||||||
|
config: LineConfig,
|
||||||
|
num_lines: u32,
|
||||||
|
event_buffer_size: u32,
|
||||||
|
_padding: [5]u32 = [5]u32{ 0, 0, 0, 0, 0 },
|
||||||
|
fd: i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Configuration flags for GPIO lines
|
||||||
|
pub const LineFlags = packed struct {
|
||||||
|
/// Line is not available for request
|
||||||
|
used: bool = false,
|
||||||
|
/// Line active state is physical low
|
||||||
|
active_low: bool = false,
|
||||||
|
/// Line is an input
|
||||||
|
input: bool = false,
|
||||||
|
/// Line is an output
|
||||||
|
output: bool = false,
|
||||||
|
/// Line detects rising (inactive to active) edges
|
||||||
|
edge_rising: bool = false,
|
||||||
|
/// Line detects falling (active to inactive) edges
|
||||||
|
edge_falling: bool = false,
|
||||||
|
/// Line is an open drain output
|
||||||
|
open_drain: bool = false,
|
||||||
|
/// Line is an open source output
|
||||||
|
open_source: bool = false,
|
||||||
|
/// Line has pull-up bias enabled
|
||||||
|
bias_pull_up: bool = false,
|
||||||
|
/// Line has pull-down bias enabled
|
||||||
|
bias_pull_down: bool = false,
|
||||||
|
/// Line has bias disabled
|
||||||
|
bias_disabled: bool = false,
|
||||||
|
/// Line events contain REALTIME timestamps
|
||||||
|
event_clock_real_time: bool = false,
|
||||||
|
/// Line events contain timestamps from hardware timestamp engine
|
||||||
|
event_clock_hte: bool = false,
|
||||||
|
/// Reserved for future use
|
||||||
|
_padding: u51 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Configuration for GPIO lines
|
||||||
|
pub const LineConfig = extern struct {
|
||||||
|
/// Configuration flags for the GPIO lines. This is the default for
|
||||||
|
/// all requested lines but may be overridden for particular lines
|
||||||
|
/// using `attrs`.
|
||||||
|
flags: LineFlags,
|
||||||
|
/// The number of attributes in `attrs`
|
||||||
|
num_attrs: u32,
|
||||||
|
/// Reserved for future use and must be zero-filled
|
||||||
|
_padding: [5]u32 = [5]u32{ 0, 0, 0, 0, 0 },
|
||||||
|
/// The configuration attributes associated with the requested lines
|
||||||
|
attrs: [MAX_LINE_NUM_ATTRS]LineConfigAttribute,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A bitset representing GPIO line values
|
||||||
|
pub const LineValueBitset = std.bit_set.IntegerBitSet(MAX_LINES);
|
||||||
|
|
||||||
|
/// Values of GPIO lines
|
||||||
|
pub const LineValues = extern struct {
|
||||||
|
/// A bitmap containing the value of the lines, set to 1 for active
|
||||||
|
/// and 0 for inactive.
|
||||||
|
bits: LineValueBitset = .{ .mask = 0 },
|
||||||
|
|
||||||
|
/// A bitmap identifying the lines to get or set, with each bit
|
||||||
|
/// number corresponding to the index in LineRequest.offsets
|
||||||
|
mask: LineValueBitset = .{ .mask = 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/// `LineInfoChanged.type` values
|
||||||
|
pub const ChangeType = enum(u32) {
|
||||||
|
/// Line has been requested
|
||||||
|
Requested = 1,
|
||||||
|
/// Line has been released
|
||||||
|
Released = 2,
|
||||||
|
/// Line has been reconfigured
|
||||||
|
Config = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Information about a change in status of a GPIO line
|
||||||
|
pub const LineInfoChanged = extern struct {
|
||||||
|
/// Updated line information
|
||||||
|
info: LineInfo,
|
||||||
|
/// Estimate of the time when the status change occurred, in nanoseconds
|
||||||
|
timestamp_ns: u64,
|
||||||
|
/// The type of change
|
||||||
|
type: ChangeType,
|
||||||
|
/// Reserved for future use
|
||||||
|
_padding: [5]u32 = [5]u32{ 0, 0, 0, 0, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns an error based on the given return code
|
||||||
|
fn handleErrno(ret: usize) !void {
|
||||||
|
if (ret == 0) return;
|
||||||
|
return switch (std.os.errno(ret)) {
|
||||||
|
.BUSY => error.DeviceIsBusy,
|
||||||
|
.INVAL => error.InvalidArgument,
|
||||||
|
.BADF => error.BadFileDescriptor,
|
||||||
|
.NOTTY => error.InappropriateIOCTLForDevice,
|
||||||
|
.FAULT => unreachable,
|
||||||
|
else => |err| return std.os.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 {
|
||||||
|
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)));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x05, LineInfo);
|
||||||
|
var info = std.mem.zeroes(LineInfo);
|
||||||
|
info.offset = offset;
|
||||||
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&info)));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x06, LineInfo);
|
||||||
|
var info = std.mem.zeroes(LineInfo);
|
||||||
|
info.offset = offset;
|
||||||
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&info)));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes `GPIO_GET_LINEINFO_UNWATCH_IOCTL` on the given fd
|
||||||
|
pub fn unwatchLineInfo(fd: std.os.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 {
|
||||||
|
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 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes `GPIO_V2_LINE_SET_VALUES_IOCTL` on the given fd
|
||||||
|
pub fn setLineValues(fd: std.os.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 {
|
||||||
|
const req = std.os.linux.IOCTL.IOWR(0xB4, 0x0D, LineConfig);
|
||||||
|
try handleErrno(std.os.linux.ioctl(fd, req, @intFromPtr(&lc)));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user