Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add libgpiodv2 support #2184

Merged
merged 34 commits into from
Jan 27, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d4d09ad
libgpiodv2: introduce namespace for libgpiod v1 for differentation to v2
huesla Nov 30, 2023
fa70b4c
libgpiodv2: add binding and proxy classes for libgpiod v2
huesla Dec 1, 2023
1802a7e
libgpiodv2: add libgpiodv2 driver and event observer
huesla Dec 1, 2023
8ddb83b
libgpiodv2: add libgpiod driver factory to select driver based on ins…
huesla Dec 1, 2023
cfc5764
libgpiodv2: add tests
huesla Dec 1, 2023
7aca23d
libgpiodv2: add documentation on how to use libgpiodv2 driver
huesla Dec 1, 2023
576896c
libgpiodv2: documentation improvement
huesla Dec 1, 2023
a9c1bb5
libgpiodv2: name namespaces consistent
huesla Dec 1, 2023
489fabe
libgpiodv2: keep LibGpiodDriver name to not break client code, make n…
huesla Dec 7, 2023
a22a430
libgpiodv2: remove unnecessary global::
huesla Dec 7, 2023
cb78456
libgpiodv2: do not block operations when waiting for edge events
huesla Dec 11, 2023
81e24a3
libgpiodv2: adjust documentation
huesla Dec 11, 2023
7d60b3f
libgpiodv2: introduce factory for libgpiod proxy objects
huesla Dec 11, 2023
9c126eb
libgpiodv2: locking reads is not necessary
huesla Dec 12, 2023
edae721
libgpiodv2: adjust creation of versioned driver instances
huesla Dec 17, 2023
943f81c
libgpiodv2: fix typo
huesla Dec 17, 2023
118e97b
libgpiodv2: add GetAvailableVersions
huesla Dec 18, 2023
21dd0c3
libgpiodv2: remove obsolete test
huesla Dec 18, 2023
428a708
libgpiodv2: adjust docs
huesla Dec 18, 2023
53a39ca
libgpiodv2: refactoring
huesla Dec 18, 2023
ea2952e
libgpiodv2: let GpioException derive from IOException
huesla Dec 19, 2023
8e1e578
libgpiodv2: adjust documentation
huesla Dec 22, 2023
15640eb
libgpiodv2: adjust .md style
huesla Dec 27, 2023
c519265
libgpiodv2: apply disposable pattern to all proxies, assign enum memb…
huesla Dec 30, 2023
2ae7ed6
libgpiodv2: minor fix in binding
huesla Dec 30, 2023
00af9b4
libgpiodv2: free all filedescriptors
huesla Jan 3, 2024
a42db33
libgpiodv2: make LibGpiodDriverFactory a singleton and use Lazy
huesla Jan 6, 2024
58ec654
libgpiodv2: replace getter, improve exception message
huesla Jan 8, 2024
dd372bb
libgpiodv2: use alternative init in LibGpiodDriverFactory
huesla Jan 8, 2024
0caa2dd
Add required suppressions
pgrawehr Jan 20, 2024
34e7ea3
Disable LibGpiod2 for Helix until runners are updated
pgrawehr Jan 20, 2024
d2d618f
libgpiodv2: recursively search for libgpiod
huesla Jan 22, 2024
cd717eb
Fix exclusions
pgrawehr Jan 20, 2024
1a22986
libgpiodv2: improve XML doc
huesla Jan 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ While contributing, you should read the [coding guidelines section](https://gith

* [PINE A64-LTS](https://www.pine64.org/?page_id=46823)

## Platforms

### Linux

#### GPIO

* [Use libgpiod to control GPIOs on Linux](./gpio-linux-libgpiod.md)

## Maker Resources

### Prototyping
Expand Down
109 changes: 109 additions & 0 deletions Documentation/gpio-linux-libgpiod.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Using libgpiod to control GPIOs

## Quick usage: blink LED example

This example targets a RaspberryPi 3/4, see comments for more information:

```c#
// side note: on the Raspberry Pi the GPIO chip line offsets are the same numbers as the usual BCM GPIO numbering, which is convenient
const int ledGpio = 15;

// on the Pi3,4 you most likely want 0, on the Pi5 number 4, see 'gpioinfo' tool
const int chipNumber = 0;
// 'using' will dispose the controller when it falls out of scope, which will un-claim lines

// alternatively be more explicit: 'new GpioController(chipNumber, new LibGpiodDriver())'
using var gpioController = new GpioController(chipNumber);

gpioController.OpenPin(ledGpio);

for (int i = 0; i < 5; i++)
{
controller.Write(ledGpio, PinValue.High);
await Task.Delay(1000);
controller.Write(ledGpio, PinValue.Low);
await Task.Delay(1000);
}
```

## libgpiod versions

**Note**: The documented version of libgpiod is not the same as the library so name, see the following table:

| Documented version | Library so name | Comment |
| ------------------ | ----------------- | -------------------------------------------------------- |
| 1.0.2 | libgpiod.so.1.0.2 | last .so.1.x library |
| 1.1 | libgpiod.so.2.0.0 | first occurrence of inconsistency, first .so.2.x library |
| 1.6.4 | libgpiod.so.2.2.2 | last .so.2.x library |
| 2.0 | libgpiod.so.3.0.0 | first .so.3.x library |
| 2.1 | libgpiod.so.3.1.0 | latest .so.3.x library (currently) |

## libgpiod version support

Currently (12/23) dotnet-iot supports v0, v1 and v2 of libgpiod.

The following table shows which driver supports which library version

| LibGpiodDriverVersion | Libgpiod version (documented) |
| --------------------- | ----------------------------- |
| V1 | 0.x to 1.x |
| V2 | 2.x |

## Choose LibGpiodDriver Version

If you want to explicitly select the version of the libgpiod driver, to target a specific library version, there are following options:

1. constructor of LibGpiodDriver:

```c#
new LibGpiodDriver(chipNumber, LibGpiodDriverVersion.V1)
```

2. Environment variable:

```shell
export DOTNET_IOT_LIBGPIOD_DRIVER_VERSION=V1 // or V2...
huesla marked this conversation as resolved.
Show resolved Hide resolved
```

When not explicitly specified, dotnet iot automatically tries to find a driver compatible to what library version is installed.

## Install libgpiod

If you want to control GPIOs using libgpiod, the library must be installed.

Many package managers provide a libgpiod package, for example:

```shell
apt install libgpiod2
```

## Install libgpiod manually

The installation should be the same on all Pi's, or boards whose distro uses the APT package manager.

1. Install build dependencies

```shell
sudo apt update && sudo apt install -y autogen autoconf autoconf-archive libtool libtool-bin pkg-config build-essential
```

2. Download the tarball and unpack it, see [releases](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/refs/), e.g.

```shell
wget https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/libgpiod-2.1.tar.gz
tar -xzf libgpiod-2.1.tar.gz
```

3. Compile and install (see [docs](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about/))

```shell
cd libgpiod-2.1/
./autogen.sh
make
sudo make install
sudo ldconfig
```

This will install the library .so files to `/usr/lib/local`

If you want to also build command line utilities `gpioinfo, gpiodetect` etc., specify `./autogen.sh --enable-tools=yes`
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Device.Gpio.Drivers;
using System.Device.Gpio.Drivers.Libgpiod.V1;
using System.Threading;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -11,14 +12,14 @@ namespace System.Device.Gpio.Tests;
[Trait("feature", "gpio")]
[Trait("feature", "gpio-libgpiod")]
[Trait("SkipOnTestRun", "Windows_NT")]
public class LibGpiodDriverTests : GpioControllerTestBase
public class LibGpiodV1DriverTests : GpioControllerTestBase
{
public LibGpiodDriverTests(ITestOutputHelper testOutputHelper)
public LibGpiodV1DriverTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
}

protected override GpioDriver GetTestDriver() => new LibGpiodDriver();
protected override GpioDriver GetTestDriver() => new LibGpiodDriver(0, LibGpiodDriverVersion.V1);

protected override PinNumberingScheme GetTestNumberingScheme() => PinNumberingScheme.Logical;

Expand Down
27 changes: 27 additions & 0 deletions src/System.Device.Gpio.Tests/LibGpiodV2DriverTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Device.Gpio.Drivers;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace System.Device.Gpio.Tests;

[Trait("feature", "gpio")]
[Trait("feature", "gpio-libgpiod")]
[Trait("SkipOnTestRun", "Windows_NT")]
public class LibGpiodV2DriverTests : GpioControllerTestBase
{
private const int ChipNumber = 0;

public LibGpiodV2DriverTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
}

protected override GpioDriver GetTestDriver() => new LibGpiodDriver(ChipNumber, LibGpiodDriverVersion.V2);

protected override PinNumberingScheme GetTestNumberingScheme() => PinNumberingScheme.Logical;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
internal partial class Interop
{
[DllImport(LibcLibrary)]
internal static extern int epoll_ctl(int epfd, PollOperations op, int fd, ref epoll_event events);
internal static extern int epoll_ctl(int epfd, PollOperations op, int fd, ref epoll_event @event);
}

internal enum PollOperations
Expand All @@ -26,10 +26,12 @@ internal struct epoll_event
public epoll_data data;
}

[Flags]
internal enum PollEvents : uint
{
EPOLLIN = 0x01,
EPOLLPRI = 0x02,
EPOLLERR = 0x08,
EPOLLET = 0x80000000
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
// Disable these StyleCop rules for this file, as we are using native names here.
#pragma warning disable SA1300 // Element should begin with upper-case letter

using System;
using System.Runtime.InteropServices;

internal partial class Interop
{
[DllImport(LibcLibrary, SetLastError = true)]
internal static extern int epoll_wait(int epfd, out epoll_event events, int maxevents, int timeout);
internal static extern int epoll_wait(int epfd, IntPtr events, int maxevents, int timeout);
}
13 changes: 13 additions & 0 deletions src/System.Device.Gpio/Interop/Unix/Libc/Interop.pipe.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Disable these StyleCop rules for this file, as we are using native names here.
#pragma warning disable SA1300 // Element should begin with upper-case letter

using System.Runtime.InteropServices;

internal partial class Interop
{
[DllImport(LibcLibrary, SetLastError = true)]
internal static extern int pipe(int[] pipefd);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

using System;
using System.Device.Gpio;
using System.Device.Gpio.Libgpiod.V1;
using System.Runtime.InteropServices;
#if NET6_0_OR_GREATER
using System.Runtime.Loader;
Expand All @@ -17,7 +18,7 @@

internal partial class Interop
{
internal static partial class Libgpiod
internal static partial class LibgpiodV1
{
#if NET6_0_OR_GREATER
private const string LibgpiodLibrary = "libgpiod.so.2";
Expand All @@ -26,11 +27,11 @@ internal static partial class Libgpiod
#endif
internal static IntPtr InvalidHandleValue;

static Libgpiod()
static LibgpiodV1()
{
InvalidHandleValue = new IntPtr(-1);
#if NET6_0_OR_GREATER
Assembly currentAssembly = typeof(Libgpiod).Assembly;
Assembly currentAssembly = typeof(LibgpiodV1).Assembly;

AssemblyLoadContext.GetLoadContext(currentAssembly)!.ResolvingUnmanagedDll += (assembly, libgpiodName) =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using LibgpiodV1 = Interop.LibgpiodV1;

namespace System.Device.Gpio;
namespace System.Device.Gpio.Libgpiod.V1;

/// <summary>
/// Pointer to a general-purpose I/O (GPIO) chip.
Expand All @@ -17,9 +18,9 @@ public SafeChipHandle()

protected override bool ReleaseHandle()
{
Interop.Libgpiod.gpiod_chip_close(handle);
LibgpiodV1.gpiod_chip_close(handle);
return true;
}

public override bool IsInvalid => handle == IntPtr.Zero || handle == Interop.Libgpiod.InvalidHandleValue;
public override bool IsInvalid => handle == IntPtr.Zero || handle == LibgpiodV1.InvalidHandleValue;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using LibgpiodV1 = Interop.LibgpiodV1;

namespace System.Device.Gpio;
namespace System.Device.Gpio.Libgpiod.V1;

/// <summary>
/// Pointer to an iterator of all GPIO chips available on the device.
Expand All @@ -17,9 +18,9 @@ public SafeChipIteratorHandle()

protected override bool ReleaseHandle()
{
Interop.Libgpiod.gpiod_chip_iter_free(handle);
LibgpiodV1.gpiod_chip_iter_free(handle);
return true;
}

public override bool IsInvalid => handle == IntPtr.Zero || handle == Interop.Libgpiod.InvalidHandleValue;
public override bool IsInvalid => handle == IntPtr.Zero || handle == LibgpiodV1.InvalidHandleValue;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using LibgpiodV1 = Interop.LibgpiodV1;

namespace System.Device.Gpio;
namespace System.Device.Gpio.Libgpiod.V1;

/// <summary>
/// Pointer to a pin.
Expand All @@ -20,17 +21,17 @@ public SafeLineHandle()
protected override bool ReleaseHandle()
{
// Contrary to intuition, this does not invalidate the handle (see comment on declaration)
Interop.Libgpiod.gpiod_line_release(handle);
LibgpiodV1.gpiod_line_release(handle);
return true;
}

/// <summary>
/// Release the lock on the line handle. <see cref="Interop.Libgpiod.gpiod_line_release"/>
/// Release the lock on the line handle. <see cref="LibgpiodV1.gpiod_line_release"/>
/// </summary>
public void ReleaseLock()
{
ReleaseHandle();
}

public override bool IsInvalid => handle == IntPtr.Zero || handle == Interop.Libgpiod.InvalidHandleValue;
public override bool IsInvalid => handle == IntPtr.Zero || handle == LibgpiodV1.InvalidHandleValue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Device.Gpio.Libgpiod.V2;

/// <seealso href="https://libgpiod.readthedocs.io/en/latest/group__edge__event.html"/>
internal enum GpiodEdgeEventType
{
RisingEdge = 1,
FallingEdge = 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Device.Gpio.Libgpiod.V2;

/// <seealso href="https://libgpiod.readthedocs.io/en/latest/group__line__defs.html"/>
internal enum GpiodLineBias
{
AsIs = 1,
Unknown = 2,
Disabled = 3,
PullUp = 4,
PullDown = 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Device.Gpio.Libgpiod.V2;

/// <seealso href="https://libgpiod.readthedocs.io/en/latest/group__line__defs.html"/>
internal enum GpiodLineClock
{
Monotonic = 1,
Realtime = 2,
Hte = 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Device.Gpio.Libgpiod.V2;

/// <seealso href="https://libgpiod.readthedocs.io/en/latest/group__line__defs.html"/>
internal enum GpiodLineDirection
{
AsIs = 1,
Input = 2,
Output = 3
}
Loading
Loading