#linux #gpio #library #embedded #async

gpiocdev

A library to access GPIO lines on Linux using the GPIO character device

7 unstable releases (3 breaking)

0.4.3 May 27, 2023
0.4.2 Dec 18, 2022
0.4.1 Nov 30, 2022
0.3.0 Oct 31, 2022
0.1.0 Oct 11, 2022

#137 in Unix APIs

Download history 37/week @ 2023-02-10 123/week @ 2023-02-17 141/week @ 2023-02-24 100/week @ 2023-03-03 89/week @ 2023-03-10 129/week @ 2023-03-17 183/week @ 2023-03-24 66/week @ 2023-03-31 58/week @ 2023-04-07 149/week @ 2023-04-14 188/week @ 2023-04-21 97/week @ 2023-04-28 97/week @ 2023-05-05 31/week @ 2023-05-12 68/week @ 2023-05-19 150/week @ 2023-05-26

363 downloads per month
Used in 2 crates

Apache-2.0 OR MIT

315KB
6K SLoC

gpiocdev

Build Status github crate LICENSE

A Rust library for accessing GPIO lines on Linux platforms using the GPIO character device.

This is the equivalent of libgpiod but in pure Rust.

The gpiocdev crate provides an API to access GPIOs from Rust applications.

ABI Compatibility

The library is compatible with the Linux GPIO uAPI, both v1 and v2, including receiving line edge events.

The gpiocdev API provides a unified abstraction for both uAPI versions, but will return an error if v2 features are attempted to be used on a v1-only system.

uAPI v2 specific features include:

  • lines with different configurations in one request
  • debouncing edge detection input lines
  • edge detection on multiple lines in one request
  • sequence numbers on edge events
  • reconfiguring edge detection without releasing the request
  • selection of source clock for edge events

Compatibility with either uAPI version can be selected via features, with the default being uAPI v2. If built with both, the library can automatically detect and use the most current available version, so defaulting to v2 and falling back to v1 if that is unavailable.

The library makes no use of the deprecated sysfs GPIO API.

Async Compatibility

The majority of the GPIO uAPI is synchronous. The exceptions are waiting for edge events from Requests, and info change events from Chips. Support for asynchronous wrappers around these are provided through features for the following reactors:

Reactor Feature Module
tokio async_tokio gpiocdev::async::tokio
async-io async_io gpiocdev::async::async_io

Additionally, Chips and Requests also expose their underlying file descriptor, which may be used directly with an async reactor. An example of this is the gpiocdev-cli edges command, which can asynchronously wait on multiple lines spread across multiple chips using the mio reactor.

With respect to the synchronous uAPI functions, those can generally be considered non-blocking unless the GPIO line is provided by an expander connected to the host processor via a bus such as I2C or SPI. In such cases calls to synchronous functions should be made from a separate thread so as not to stall the reactor. The uAPI does not provide a definitive indication as to whether lines are hosted locally or on an expander, so it is up to the application to determine if that may be the case. Even if the uAPI did provide an indicator, determining the approach most appropriate to interwork the reactor thread with the uAPI thread(s), and possibly other threads, is best left to the application.

Example Usage

Requesting a line by name:

    let led0 = gpiocdev::find_named_line("LED0").unwrap();
    let req = Request::builder()
        .with_found_line(&led0)
        .as_output(Value::Active)
        .request()?;

    // change value later
    req.set_value(led0.offset, Value::Inactive)

Getting a line value:

    // request the line
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_line(23)
        .as_input()
        .request()?;
    // get the value
    let value = req.value(23)?;

Setting a line:

    // request the line and set its value
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_line(22)
        .as_output(Value::Active)
        .request()?;

    // do something...

    // change value later
    req.set_value(22, Value::Inactive)

Waiting for events on a line:

    // request the line
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_line(23)
        .with_edge_detection(EdgeDetection::BothEdges)
        .request()?;

    // wait for line edge events
    for event in req.edge_events() {
        println!("{:?}", event?);
    }

Multiple lines may be selected in a single request, and then be operated on as a unit.

Getting multiple lines:

    // request multiple input lines
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_lines(&[18,23])
        .as_input()
        .request()?;
    // get multiple line values at once
    let mut values = Values::default();
    req.values(&mut values)?;

Setting multiple lines:

    // request multiple output lines
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_lines(&[17,22])
        .as_output(Value::Active)
        .request()?;
    // set multiple line values at once
    let mut values = Values::default();
    values.set(17, Value::Inactive);
    values.set(12, Value::Active);
    req.set_values(&values)?;

All line attributes available via the kernel GPIO interface, such as pull-ups and debounce etc, can also be set:

    // request the line
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_consumer("myapp")
        .with_line(23)
        .as_input()
        .as_active_low()
        .with_bias(Bias::PullUp)
        .request()?;
    // get the value
    let value = req.value(23)?;

A good starting point to learn more is the gpiocdev::request::Builder.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~2.7–9.5MB
~173K SLoC