fermentord/vendor/github.com/warthog618/gpiod/README.md

735 lines
23 KiB
Markdown
Raw Normal View History

<!--
SPDX-FileCopyrightText: 2019 Kent Gibson <warthog618@gmail.com>
SPDX-License-Identifier: MIT
-->
# gpiod
[![Build Status](https://app.travis-ci.com/warthog618/gpiod.svg)](https://app.travis-ci.com/warthog618/gpiod)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/warthog618/gpiod)](https://pkg.go.dev/github.com/warthog618/gpiod)
[![Go Report Card](https://goreportcard.com/badge/github.com/warthog618/gpiod)](https://goreportcard.com/report/github.com/warthog618/gpiod)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/warthog618/gpiod/blob/master/LICENSE)
A native Go library for Linux GPIO.
**gpiod** is a library for accessing GPIO pins/lines on Linux platforms using
the GPIO character device.
The goal of this library is to provide the Go equivalent of the C
**[libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/)**
library. The intent is not to mirror the **libgpiod** API but to provide the
equivalent functionality.
:warning: v0.6.0 introduces a few API breaking changes. Refer to
the [release notes](#release-notes) if updating from an older version.
## Features
Supports the following functionality per line and for collections of lines:
- direction (input/output)<sup>**1**</sup>
- write (active/inactive)
- read (active/inactive)
- active high/low (defaults to high)
- output mode (push-pull/open-drain/open-source)
- pull up/down<sup>**2**</sup>
- watches and edge detection (rising/falling/both)
- chip and line labels
- debouncing input lines<sup>**3**</sup>
- different configurations for lines within a collection<sup>**3**</sup>
<sup>**1**</sup> Dynamically changing line direction without releasing the line
requires Linux v5.5 or later.
<sup>**2**</sup> Requires Linux v5.5 or later.
<sup>**3**</sup> Requires Linux v5.10 or later.
All library functions are safe to call from different goroutines.
## Quick Start
A simple piece of wire example that reads the value of an input line (pin 2) and
writes its value to an output line (pin 3):
```go
import "github.com/warthog618/gpiod"
...
in, _ := gpiod.RequestLine("gpiochip0", 2, gpiod.AsInput)
val, _ := in.Value()
out, _ := gpiod.RequestLine("gpiochip0", 3, gpiod.AsOutput(val))
...
```
Error handling and releasing of resources omitted for brevity.
## Usage
```go
import "github.com/warthog618/gpiod"
```
Error handling is omitted from the following examples for brevity.
### Line Requests
To read or alter the value of a
[line](https://pkg.go.dev/github.com/warthog618/gpiod#Line) it must first be
requested using [*gpiod.RequestLine*](https://pkg.go.dev/github.com/warthog618/gpiod#RequestLine):
```go
l, _ := gpiod.RequestLine("gpiochip0", 4) // in its existing state
```
or from the [*Chip*](#chip-initialization) object using
[*Chip.RequestLine*](https://pkg.go.dev/github.com/warthog618/gpiod#Chip.RequestLine):
```go
l, _ := c.RequestLine(4) // from a Chip object
```
The offset parameter identifies the line on the chip, and is specific to the
GPIO chip. To improve readability, convenience mappings can be provided for
specific devices, such as the Raspberry Pi:
```go
l, _ := c.RequestLine(rpi.J8p7) // using Raspberry Pi J8 mapping
```
The initial configuration of the line can be set by providing line
[configuration options](#configuration-options), as shown in this *AsOutput*
example:
```go
l, _ := gpiod.RequestLine("gpiochip0", 4, gpiod.AsOutput(1)) // as an output line
```
Multiple lines from the same chip may be requested as a collection of
[lines](https://pkg.go.dev/github.com/warthog618/gpiod#Lines) using
[*gpiod.RequestLines*](https://pkg.go.dev/github.com/warthog618/gpiod#RequestLines)
```go
ll, _ := gpiod.RequestLines("gpiochip0", []int{0, 1, 2, 3}, gpiod.AsOutput(0, 0, 1, 1))
```
or from a Chip object using
[*Chip.RequestLines*](https://pkg.go.dev/github.com/warthog618/gpiod#Chip.RequestLines):
```go
ll, _ := c.RequestLines([]int{0, 1, 2, 3}, gpiod.AsOutput(0, 0, 1, 1))
```
When no longer required, the line(s) should be closed to release resources:
```go
l.Close()
ll.Close()
```
### Line Values
Lines must be requsted using [*RequestLine*](#line-requests) before their
values can be accessed.
#### Read Input
The current line value can be read with the
[*Value*](https://pkg.go.dev/github.com/warthog618/gpiod#Line.Value)
method:
```go
r, _ := l.Value() // Read state from line (active / inactive)
```
For collections of lines, the level of all lines is read simultaneously using
the [*Values*](https://pkg.go.dev/github.com/warthog618/gpiod#Lines.SetValues)
method:
```go
rr := []int{0, 0, 0, 0} // buffer to read into...
ll.Values(rr) // Read the state of a collection of lines
```
#### Write Output
The current line value can be set with the
[*SetValue*](https://pkg.go.dev/github.com/warthog618/gpiod#Line.SetValue)
method:
```go
l.SetValue(1) // Set line active
l.SetValue(0) // Set line inactive
```
Also refer to the [blinker](example/blinker/blinker.go) example.
For collections of lines, all lines are set simultaneously using the
[*SetValues*](https://pkg.go.dev/github.com/warthog618/gpiod#Lines.SetValues)
method:
```go
ll.SetValues([]int{0, 1, 0, 1}) // Set a collection of lines
```
#### Edge Watches
The value of an input line can be watched and trigger calls to handler
functions.
The watch can be on rising or falling edges, or both.
The events are passed to a handler function provided using the
*WithEventHandler(eh)* option. The handler function is passed a
[*LineEvent*](https://pkg.go.dev/github.com/warthog618/gpiod#LineEvent), which
contains details of the edge event including the offset of the triggering line,
the time the edge was detected and the type of edge detected:
```go
func handler(evt gpiod.LineEvent) {
// handle edge event
}
l, _ = c.RequestLine(rpi.J8p7, gpiod.WithEventHandler(handler), gpiod.WithBothEdges)
```
To maintain event ordering, the event handler is called serially from a
goroutine that reads the events from the kernel. The event handler is expected
to be short lived, and so should hand off any potentially blocking operations to
a separate goroutine.
An edge watch can be removed by closing the line:
```go
l.Close()
```
or by reconfiguring the requested lines to disable edge detection:
```go
l.Reconfigure(gpiod.WithoutEdges)
```
Note that the *Close* waits for the event handler to return and so must not be
called from the event handler context - it should be called from a separate
goroutine.
Also see the [watcher](example/watcher/watcher.go) example.
### Line Configuration
Line configuration is set via [options](#configuration-options) to
*Chip.RequestLine(s)* and *Line.Reconfigure*. These override any default which
may be set in *NewChip*.
Note that configuration options applied to a collection of lines apply to all
lines in the collection, unless they are applied to a subset of the requested
lines using the *WithLines* option.
#### Reconfiguration
Requested lines may be reconfigured using the Reconfigure method:
```go
l.Reconfigure(gpiod.AsInput) // set direction to Input
ll.Reconfigure(gpiod.AsOutput(1, 0)) // set direction to Output (and values to active and inactive)
```
The *Line.Reconfigure* method accepts differential changes to the configuration
for the lines, so option categories not specified or overridden by the specified
changes will remain unchanged.
The *Line.Reconfigure* method requires Linux v5.5 or later.
#### Complex Configurations
It is sometimes necessary for the configuration of lines within a request to
have slightly different configurations. Line options may be applied to a subset
of requested lines using the *WithLines(offsets, options)* option.
The following example requests a set of output lines and sets some of the lines
in the request to active low:
```go
ll, _ = c.RequestLines([]int{0, 1, 2, 3}, gpiod.AsOutput(0, 0, 1, 1),
gpiod.WithLines([]int{0, 3}, gpiod.AsActiveLow),
gpiod.AsOpenDrain)
```
The configuration of the subset of lines inherits the configuration of the
request at the point the *WithLines* is invoked. Subsequent changes to the
request configuration do not alter the configuration of the subset - in the
example above, lines 0 and 3 will not be configured as open-drain.
Once a line's configuration has branched from the request configuration it can
only be altered with *WithLines* options:
```go
ll.Reconfigure(gpiod.WithLines([]int{0}, gpiod.AsActiveHigh))
```
or reset to the request configuration using the *Defaulted* option:
```go
ll.Reconfigure(gpiod.WithLines([]int{3}, gpiod.Defaulted))
```
Complex configurations require Linux v5.10 or later.
### Chip Initialization
The Chip object is used to discover details about avaialble lines and can be used
to request lines from a GPIO chip.
A Chip object is constructed using the
[*NewChip*](https://pkg.go.dev/github.com/warthog618/gpiod#NewChip) function.
```go
c, _ := gpiod.NewChip("gpiochip0")
```
The parameter is the chip name, which corresponds to the name of the device in
the **/dev** directory, so in this example **/dev/gpiochip0**.
The list of currently available GPIO chips is returned by the *Chips* function:
```go
cc := gpiod.Chips()
```
Default attributes for Lines requested from the Chip can be set via
[configuration options](#configuration-options) to
[*NewChip*](https://pkg.go.dev/github.com/warthog618/gpiod#NewChip).
```go
c, _ := gpiod.NewChip("gpiochip0", gpiod.WithConsumer("myapp"))
```
In this example the consumer label is defaulted to "myapp".
When no longer required, the chip should be closed to release resources:
```go
c.Close()
```
Closing a chip does not close or otherwise alter the state of any lines
requested from the chip.
### Line Info
[Info](https://pkg.go.dev/github.com/warthog618/gpiod#LineInfo) about a line can
be read at any time from the chip using the
[*LineInfo*](https://pkg.go.dev/github.com/warthog618/gpiod#Chip.LineInfo)
method:
```go
inf, _ := c.LineInfo(4)
inf, _ := c.LineInfo(rpi.J8p7) // Using Raspberry Pi J8 mapping
```
Note that the line info does not include the value. The line must be requested
from the chip to access the value.
Once requested, the line info can also be read from the line:
```go
inf, _ := l.Info()
infs, _ := ll.Info()
```
#### Info Watches
Changes to the line info can be monitored by adding an info watch for the line:
```go
func infoChangeHandler( evt gpiod.LineInfoChangeEvent) {
// handle change in line info
}
inf, _ := c.WatchLineInfo(4, infoChangeHandler)
```
Note that the info watch does not monitor the line value (active or inactive)
only its configuration. Refer to [Edge Watches](#edge-watches) for monitoring
line value.
An info watch can be cancelled by unwatching:
```go
c.UnwatchLineInfo(4)
```
or by closing the chip.
#### Categories
Most line configuration options belong to one of the following categories:
- Active Level
- Direction
- Bias
- Drive
- Debounce
- Edge Detection
- Event Clock
Only one option from each category may be applied. If multiple options from a
category are applied then all but the last are ignored.
##### Active Level
The values used throughout the API for line values are the logical value, which
is 0 for inactive and 1 for active. The physical value considered active can be
controlled using the *AsActiveHigh* and *AsActiveLow* options:
```go
l, _ := c.RequestLine(4, gpiod.AsActiveLow) // during request
l.Reconfigure(gpiod.AsActiveHigh) // once requested
```
Lines are typically active high by default.
##### Direction
The line direction can be controlled using the *AsInput* and *AsOutput* options:
```go
l, _ := c.RequestLine(4, gpiod.AsInput) // during request
l.Reconfigure(gpiod.AsInput) // set direction to Input
l.Reconfigure(gpiod.AsOutput(0)) // set direction to Output (and value to inactive)
```
##### Bias
The bias options control the pull up/down state of the line:
```go
l, _ = c.RequestLine(4, gpiod.WithPullUp) // during request
l.Reconfigure(gpiod.WithBiasDisabled) // once requested
```
The bias options require Linux v5.5 or later.
##### Drive
The drive options control how an output line is driven when active and inactive:
```go
l,_ := c.RequestLine(4, gpiod.AsOpenDrain) // during request
l.Reconfigure(gpiod.AsOpenSource) // once requested
```
The default drive for output lines is push-pull, which actively drives the line
in both directions.
##### Debounce
Input lines may be debounced using the *WithDebounce* option. The debouncing will
be performed by the underlying hardware, if supported, else by the Linux
kernel.
```go
period := 10 * time.Millisecond
l, _ = c.RequestLine(4, gpiod.WithDebounce(period))// during request
l.Reconfigure(gpiod.WithDebounce(period)) // once requested
```
The WithDebounce option requires Linux v5.10 or later.
##### Edge Detection
The edge options control which edges on input lines will generate edge events.
Edge events are passed to the event handler specified in the *WithEventHandler(eh)*
option.
By default edge detection is not enabled on requested lines.
Refer to [Edge Watches](#edge-watches) for examples of the edge detection options.
##### Event Clock
The event clock options control the source clock used to timestamp edge events.
This is only useful for Linux kernels v5.11 and later - prior to that the clock
source is fixed.
The event clock source used by the kernel has changed over time as follows:
Kernel Version | Clock source
--- | ---
pre-v5.7 | CLOCK_REALTIME
v5.7 - v5.10 | CLOCK_MONOTONIC
v5.11 and later | configurable
Determining which clock the edge event timestamps contain is currently left as
an exercise for the user.
#### Configuration Options
The available configuration options are:
Option | Category | Description
---|---|---
*WithConsumer*<sup>**1**</sup> | Info | Set the consumer label for the lines
*AsActiveLow* | Level | Treat a low physical line value as active
*AsActiveHigh* | Level | Treat a high physical line value as active (**default**)
*AsInput* | Direction | Request lines as input
*AsIs*<sup>**2**</sup> | Direction | Request lines in their current input/output state (**default**)
*AsOutput(\<values\>...)*<sup>**3**</sup> | Direction | Request lines as output with the provided values
*AsPushPull* | Drive | Request output lines drive both high and low (**default**)
*AsOpenDrain* | Drive | Request lines as open drain outputs
*AsOpenSource* | Drive | Request lines as open source outputs
*WithEventHandler(eh)<sup>**1**</sup>* | | Send edge events detected on requested lines to the provided handler
*WithEventBufferSize(num)<sup>**1**,**5**</sup>* | | Suggest the minimum number of events that can be stored in the kernel event buffer for the requested lines
*WithFallingEdge* | Edge Detection<sup>**3**</sup> | Request lines with falling edge detection
*WithRisingEdge* | Edge Detection<sup>**3**</sup> | Request lines with rising edge detection
*WithBothEdges* | Edge Detection<sup>**3**</sup> | Request lines with rising and falling edge detection
*WithoutEdges*<sup>**5**</sup> | Edge Detection<sup>**3**</sup> | Request lines with edge detection disabled (**default**)
*WithBiasAsIs* | Bias<sup>**4**</sup> | Request the lines have their bias setting left unaltered (**default**)
*WithBiasDisabled* | Bias<sup>**4**</sup> | Request the lines have internal bias disabled
*WithPullDown* | Bias<sup>**4**</sup> | Request the lines have internal pull-down enabled
*WithPullUp* | Bias<sup>**4**</sup> | Request the lines have internal pull-up enabled
*WithDebounce(period)*<sup>**5**</sup> | Debounce | Request the lines be debounced with the provided period
*WithMonotonicEventClock* | Event Clock | Request the timestamp in edge events use the monotonic clock (**default**)
*WithRealtimeEventClock*<sup>**6**</sup> | Event Clock | Request the timestamp in edge events use the realtime clock
*WithLines(offsets, options...)*<sup>**3**,**5**</sup> | | Specify configuration options for a subset of lines in a request
*Defaulted*<sup>**5**</sup> | | Reset the configuration for a request to the default configuration, or the configuration of a particular line in a request to the default for that request
The options described as **default** are generally not required, except to override other options earlier in a chain of configuration options.
<sup>**1**</sup> Can be applied to either *NewChip* or *Chip.RequestLine*, but
cannot be used with *Line.Reconfigure*.
<sup>**2**</sup> Can be applied to *Chip.RequestLine*, but cannot be used
with *NewChip* or *Line.Reconfigure*.
<sup>**3**</sup> Can be applied to either *Chip.RequestLine* or
*Line.Reconfigure*, but cannot be used with *NewChip*.
<sup>**4**</sup> Requires Linux v5.5 or later.
<sup>**5**</sup> Requires Linux v5.10 or later.
<sup>**6**</sup> Requires Linux v5.11 or later.
## Installation
On Linux:
```shell
go get github.com/warthog618/gpiod
```
For other platforms, where you intend to cross-compile for Linux, don't attempt to compile the package when it is installed:
```shell
go get -d github.com/warthog618/gpiod
```
## Tools
A command line utility, **gpiodctl**, can be found in the cmd directory and is
provided to allow manual or scripted manipulation of GPIO lines. This utility
combines the Go equivalent of all the **libgpiod** command line tools into a
single tool.
```shell
gpiodctl is a utility to control GPIO lines on Linux GPIO character devices
Usage:
gpiodctl [flags]
gpiodctl [command]
Available Commands:
detect Detect available GPIO chips
find Find a GPIO line by name
get Get the state of a line or lines
help Help about any command
info Info about chip lines
mon Monitor the state of a line or lines
set Set the state of a line or lines
version Display the version
watch Watch lines for changes to the line info
Flags:
-h, --help help for gpiodctl
Use "gpiodctl [command] --help" for more information about a command.
```
The Go equivalent of each of the **libgpiod** command line tools can also be
found in the cmd directory.
Those tools are:
Tool | Description
--- | ---
gpiodetect | Report all the gpiochips available on the system.
gpioinfo | Report the details of all the lines available on gpiochips.
gpiofind | Find the gpiochip and offset of a line by name.
gpioget | Get the value of a line or a set of lines on one gpiochip.
gpioset | Set of value of a line or a set of lines on one gpiochip.
gpiomon | Report edges detected on a line or set of lines on one gpiochip.
## Tests
The library is fully tested, other than some error cases and sanity checks that
are difficult to trigger.
The tests require a kernel release 5.1.0 or later to run. For all the tests to
pass a kernel 5.5.0 or later is required.
The test user must have access to the **/dev/gpiochip0** character device.
### Platforms
The tests can be run on either of two platforms:
- gpio-mockup (**default**)
- Raspberry Pi
#### gpio-mockup
The gpio-mockup platform is any Linux platform with a recent kernel that supports
the **gpio-mockup** loadable module. **gpio-mockup** must be built as a module
and the test user must have rights to load and unload the module.
The **gpio-mockup** is the default platform for tests and benchmarks as it does
not interact with physical hardware and so is always safe to run.
#### Raspberry Pi
On Raspberry Pi, the tests are intended to be run on a board with J8 pins 11 and
12 floating and with pins 15 and 16 tied together, possibly using a jumper
across the header.
:warning: The tests set J8 pins 11, 12 and 16 to outputs so **DO NOT**
run them on hardware where any of those pins is being externally driven.
The Raspberry Pi platform is selected by specifying the platform parameter on
the test command line:
```shell
go test -platform=rpi
```
Tests have been run successfully on Raspberry Pi Zero W and Pi 4B. The library
should also work on other Raspberry Pi variants, I just haven't gotten around to
testing them yet.
The tests can be cross-compiled from other platforms using:
```shell
GOOS=linux GOARCH=arm GOARM=6 go test -c
```
Later Pis can also use ARM7 (GOARM=7).
### Benchmarks
The tests include benchmarks on reads, writes, bulk reads and writes, and
interrupt latency.
These are the results from a Raspberry Pi Zero W running Linux v5.10 and built
with go1.15.6:
```shell
$ ./gpiod.test -platform=rpi -test.bench=.*
goos: linux
goarch: arm
pkg: github.com/warthog618/gpiod
BenchmarkChipNewClose 265 3949958 ns/op
BenchmarkLineInfo 28420 40192 ns/op
BenchmarkLineReconfigure 26079 46121 ns/op
BenchmarkLineValue 114961 10176 ns/op
BenchmarkLinesValues 66969 17367 ns/op
BenchmarkLineSetValue 92529 12531 ns/op
BenchmarkLinesSetValues 65965 17309 ns/op
BenchmarkInterruptLatency 1827 638202 ns/op
PASS
```
## Prerequisites
The library targets Linux with support for the GPIO character device API. That
generally means that **/dev/gpiochip0** exists.
The caller must have access to the character device - typically
**/dev/gpiochip0**. That is generally root unless you have changed the
permissions of that device.
The Bias line options and the Line.Reconfigure method both require Linux v5.5 or
later.
Debounce and other uAPI v2 features require Linux v5.10 or later.
The requirements for each [configuration option](#configuration-options) are
noted in that section.
## Release Notes
### v0.8.0
Add top level *RequestLine* and *RequestLines* functions to simplify common use cases.
**blinker** and **watcher** examples interwork with each other on a Raspberry Pi with a jumper across **J8-15** and **J8-16**.
Fix deadlock in **gpiodctl set** no-wait.
### v0.7.0
*LineEvent* exposes sequence numbers for uAPI v2 events.
Info tools (**gpiodctl info** and **gpioinfo**) report debounce-period.
**gpiodctl mon** and watcher example report event sequence numbers.
**gpiodctl mon** supports setting debounce period.
**gpiodctl detect** reports kernel uAPI version in use.
Watchers use Eventfd instead of pipes to reduce open file descriptors.
Start migrating to Go 1.17 go:build style build tags.
Make licensing [REUSE](https://reuse.software/) compliant.
### v0.6.0
*gpiod* now supports both the old GPIO uAPI (v1) and the newer (v2) introduced
in Linux v5.10. The library automatically detects the available uAPI versions
and makes use of the latest.
Applications written for uAPI v1 will continue to work with uAPI v2.
Applications that make use of v2 specific features will return errors when run
on Linux kernels prior to v5.10.
Breaking API changes:
1. The event handler parameter has been moved from edge options into the
*WithEventHandler(eh)* option to allow for reconfiguration of edge detection
which is supported in Linux v5.10.
Old edge options should be replaced with the *WithEventHandler* option and
the now parameterless edge option, e.g.:
```sed
s/gpiod\.WithBothEdges(/gpiod.WithBothEdges, gpiod.WithEventHandler(/g
```
2. *WithBiasDisable* is renamed *WithBiasDisabled*. This option is probably
rarely used and the renaming is trivial, so no backward compatibility is
provided.
3. *FindLine* has been dropped as line names are not guaranteed to be unique.
Iterating over the available chips and lines to search for line by name can
be easily done - the *Chips* function provides the list of available chips as
a starting point.
Refer to the *find* command in **gpiodctl** for example code.