7个版本 (破坏性更新)

0.6.0 2023年9月12日
0.5.1 2021年11月22日
0.5.0 2021年9月22日
0.4.0 2020年8月25日
0.1.0 2018年9月29日

Unix API 中排名 #57

Download history 5199/week @ 2024-04-08 5982/week @ 2024-04-15 5979/week @ 2024-04-22 5695/week @ 2024-04-29 4123/week @ 2024-05-06 5305/week @ 2024-05-13 5757/week @ 2024-05-20 5103/week @ 2024-05-27 5130/week @ 2024-06-03 4682/week @ 2024-06-10 4917/week @ 2024-06-17 4678/week @ 2024-06-24 3496/week @ 2024-07-01 4702/week @ 2024-07-08 4892/week @ 2024-07-15 5932/week @ 2024-07-22

每月下载 19,187
15 个crates 使用

MIT/Apache 协议

56KB
767 代码行

gpio-cdev

Build Status Version License

rust-gpio-cdev 是一个Rust库/crate,提供了对 GPIO字符设备ABI 的访问。此API自Linux v4.4开始稳定,废弃了计划在2020年(即将到来)后从上游内核中删除的旧版sysfs接口。

如果不针对旧内核,建议使用此API而不是此crate的前驱者 sysfs_gpio。更多关于差异的信息,请参阅 Sysfs GPIO vs GPIO Character Device

安装

将以下内容添加到您的 Cargo.toml

[dependencies]
gpio-cdev = "0.4"

请注意,以下功能可用

  • async-tokio:为在tokio运行时中异步代码中消费GPIO事件添加Stream接口。

示例

示例目录 中有多个额外的示例。

读取状态

use gpio_cdev::{Chip, LineRequestFlags};

// Read the state of GPIO4 on a raspberry pi.  /dev/gpiochip0
// maps to the driver for the SoC (builtin) GPIO controller.
let mut chip = Chip::new("/dev/gpiochip0")?;
let handle = chip
    .get_line(4)?
    .request(LineRequestFlags::INPUT, 0, "read-input")?;
for _ in 1..4 {
    println!("Value: {:?}", handle.get_value()?);
}

镜像状态(读取/写入)

use gpio_cdev::{Chip, LineRequestFlags, EventRequestFlags, EventType};

// Lines are offset within gpiochip0; see docs for more info on chips/lines
//
// This function will synchronously follow the state of one line
// on gpiochip0 and mirror its state on another line.  With this you
// could, for instance, control the state of an LED with a button
// if hooked up to the right pins on a raspberry pi.
fn mirror_gpio(inputline: u32, outputline: u32) -> Result<(), gpio_cdev::Error> {
    let mut chip = Chip::new("/dev/gpiochip0")?;
    let input = chip.get_line(inputline)?;
    let output = chip.get_line(outputline)?;
    let output_handle = output.request(LineRequestFlags::OUTPUT, 0, "mirror-gpio")?;
    for event in input.events(
        LineRequestFlags::INPUT,
        EventRequestFlags::BOTH_EDGES,
        "mirror-gpio",
    )? {
        let evt = event?;
        println!("{:?}", evt);
        match evt.event_type() {
            EventType::RisingEdge => {
                output_handle.set_value(1)?;
            }
            EventType::FallingEdge => {
                output_handle.set_value(0)?;
            }
        }
    }

    Ok(())
}

异步使用

请注意,这需要添加 async-tokio 功能。

use futures::stream::StreamExt;
use gpio_cdev::{Chip, AsyncLineEventHandle};

async fn gpiomon(chip: String, line: u32) -> gpio_cdev::Result<()> {
    let mut chip = Chip::new(args.chip)?;
    let line = chip.get_line(args.line)?;
    let mut events = AsyncLineEventHandle::new(line.events(
        LineRequestFlags::INPUT,
        EventRequestFlags::BOTH_EDGES,
        "gpioevents",
    )?)?;

    while let Some(event) = events.next().await {
        let event = event?;
        println!("GPIO Event: {:?}", event);
    }

    Ok(())
}

Sysfs GPIO 与 GPIO 字符设备

与sysfs gpio接口(由sysfs_gpio crate提供)相比,字符设备有几个优点和关键设计差异(其中一些是内核中废弃的原因)。

由于许多人熟悉sysfs接口(通过shell中的基本命令可以轻松访问)而很少有人熟悉GPIO字符设备,因此探索这两个概念及其关键区别可能是有用的。

获取GPIO访问权限

在Linux内核中,单个GPIO通过在探针期间将自己注册为GPIO芯片的驱动程序来公开,这些驱动程序属于gpio子系统。每个芯片都提供对一组GPIO的访问。目前,当此芯片注册时,会分配一个全局基本编号给此驱动程序。Linux内核的注释gpio_chip_add_data在分配基本编号给GPIO芯片时很好地总结了情况。

/*
 * TODO: this allocates a Linux GPIO number base in the global
 * GPIO numberspace for this chip. In the long run we want to
 * get *rid* of this numberspace and use only descriptors, but
 * it may be a pipe dream. It will not happen before we get rid
 * of the sysfs interface anyways.
 */

GPIO的整个sysfs接口基于从分配给GPIO芯片的基本编号的偏移量。基本编号完全取决于芯片注册到子系统的顺序以及每个先前芯片注册的GPIO数量。这之所以能够使用,唯一的理由是大多数GPIO都是通过在引导过程中一致注册的SoC硬件访问的。这并不好;实际上,这甚至不好。

GPIO字符设备ABI通过总线设备/sys/bus/gpiochipN(或/dev/gpiochipN)提供对GPIO芯片拥有的GPIO的访问。在芯片内部,程序员仍然需要了解一些关于如何访问GPIO的细节,但通常情况是合理的。可以通过迭代所有现有的设备或设置适当的udev规则来确定所需的GPIO芯片。一个很好的例子是内核源代码中的lsgpio实用程序

在sysfs中,每个芯片内部的GPIO将被导出并单独使用。GPIO字符设备允许通过“linehandle”文件描述符(在请求时动态创建)读取、写入、配置和监视一个或多个GPIO(通过偏移量引用)。

“导出”GPIO

使用sysfs API,可以将全局GPIO编号写入“export”文件,以在文件系统上的新文件上执行进一步操作。使用gpiochip字符设备,可以通过使用GPIO_GET_LINEHANDLE_IOCTL创建的“linehandle”文件描述符获取对芯片内一个或多个GPIO偏移量的操作句柄。结果是,行将只记住其状态,直到文件描述符打开;一旦文件描述符关闭,行的状态将重置。

在请求linehandle时,还包括有关如何使用单个GPIO(输入、输出、原样、低电平有效、开漏、开源等)的附加信息。可以将在单个请求中组合多个行,但它们必须以相同的方式进行配置。请参阅struct gpioevent_request

读取/写入GPIO

通过sysfs,可以使用值文件读取/写入GPIO。对于GPIO字符设备,可以使用GPIOHANDLE_GET_LINE_VALUES_IOCTLGPIOHANDLE_SET_LINE_VALUES_IOCTL来获取/设置芯片内一个或多个偏移量的状态。

输入事件

通过sysfs,可以使用触发文件来设置,并通过在值文件上轮询来通知用户空间(基于设置的情况)单个事件。使用GPIO字符设备,可以设置一个gpio_eventrequest,它将在gpiochip的行内创建一个新的匿名文件(fd提供)用于事件通知。与sysfs gpio事件相反,事件文件将排队多个事件,并包括事件(尽力而为)纳秒级精度时间和事件类型的标识符。

有了这些信息,可以更合理地考虑使用在内核中捕获的带有时序信息的队列,从用户空间解释基本数字信号(具有上升和下降沿)。以前,需要快速处理事件通知,再次对值文件进行系统调用以查看状态等,这涉及到太多变量,无法被认为是可靠的。

最小支持的Rust版本(MSRV)

此crate保证在稳定Rust 1.65.0及更高版本上编译。它可能可以用较旧版本编译,但在任何新的补丁版本中可能会有所改变。

许可证

许可方式如下

任选其一。

贡献

除非您明确声明,否则任何有意提交以包含在您的工作中的贡献,根据Apache-2.0许可证的定义,将按上述方式双许可,不附加任何其他条款或条件。

行为准则

对this crate的贡献受Rust行为准则的约束,该crate的维护者、嵌入式Linux团队承诺将介入以维护该行为准则。

依赖项

~2–10MB
~100K SLoC