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 10 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
99 changes: 99 additions & 0 deletions Documentation/raspi-libgpiodv2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Use libgpiodv2 on a Raspberry Pi
huesla marked this conversation as resolved.
Show resolved Hide resolved

If you want to control GPIOs using libgpiod version 2 the library must be installed.
huesla marked this conversation as resolved.
Show resolved Hide resolved

As of now (12/2023) only a few distros ship version 2 with their package manager, which requires manual installation.

## Install libgpiod v2.x

The installation should be the same on all Pi's, or other boards.

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: Go to [releases](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/refs/) and copy the download URL of a libgpiod version

``````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
``````

or any other version

3. Compile libgpiod and install (see [BUILDING](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`



# GPIO blink example using libgpiodv2

If not already done, install dotnet, see "Install dotnet" below.

`dotnet iot` will search common library paths on Linux to find `libgpiod.so.3` so no step is required here.

Simply use the `GpioController` like this:

``````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 inputPin = 14;
const int outputPin = 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
using var gpioController = new GpioController(chipNumber);

gpioController.OpenPin(inputPin);
gpioController.OpenPin(outputPin);

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

Or register a callback for edge events:

``````c#
...
controller.RegisterCallbackForPinValueChangedEvent(inputPin, PinEventTypes.Falling | PinEventTypes.Rising, (sender, eventArgs) =>
{
int pinNr = eventArgs.PinNumber;
PinEventTypes changeType = eventArgs.ChangeType;
Console.WriteLine($"Line '{pinNr}' changed to '{changeType}'");
});
...
``````



# Install dotnet

Currently `dotnet iot` targets .NET 6 (might change in the future)

``````shell
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 6.0
echo 'export DOTNET_ROOT=$HOME/.dotnet' >> ~/.bashrc
echo 'export PATH=$PATH:$HOME/.dotnet' >> ~/.bashrc
source ~/.bashrc
``````

See [docs](https://learn.microsoft.com/de-de/dotnet/iot/deployment)
4 changes: 2 additions & 2 deletions src/System.Device.Gpio.Tests/LibGpiodDriverTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 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.Device.Gpio.Drivers.Libgpiod.V1;
using System.Threading;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -18,7 +18,7 @@ public LibGpiodDriverTests(ITestOutputHelper testOutputHelper)
{
}

protected override GpioDriver GetTestDriver() => new LibGpiodDriver();
protected override GpioDriver GetTestDriver() => new LibGpiodV1Driver();

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

Expand Down
28 changes: 28 additions & 0 deletions src/System.Device.Gpio.Tests/LibGpiodV2DriverTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.Libgpiod.V2;
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
{
/// <summary>
/// Set to a low value to not delay tests too much, see constructor of <see cref="LibGpiodV2Driver"/>
/// </summary>
private static readonly TimeSpan s_eventReadTimeout = TimeSpan.FromMilliseconds(1);

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

protected override GpioDriver GetTestDriver() => new LibGpiodV2Driver(0, waitForEventsTimeout: s_eventReadTimeout);

protected override PinNumberingScheme GetTestNumberingScheme() => PinNumberingScheme.Logical;
}
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
}
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,
huesla marked this conversation as resolved.
Show resolved Hide resolved
Disabled,
PullUp,
PullDown
}
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,
Hte
}
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,
Output
}
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 GpiodLineDrive
{
PushPull = 1,
OpenDrain,
OpenSource
}
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.

namespace System.Device.Gpio.Libgpiod.V2;

/// <seealso href="https://libgpiod.readthedocs.io/en/latest/group__line__defs.html"/>
internal enum GpiodLineEdge
{
None = 1,
Rising,
Falling,
Both
}
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__watch.html"/>
internal enum GpiodLineInfoEventType
{
LineRequested = 1,
LineReleased,
LineConfigChanged
}
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 GpiodLineValue
{
Error = -1,
Inactive = 0,
Active = 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace System.Device.Gpio.Libgpiod.V2;

internal class ChipInfoSafeHandle : SafeHandle
{
public ChipInfoSafeHandle()
: base(IntPtr.Zero, true)
{
}

protected override bool ReleaseHandle()
{
LibgpiodV2.gpiod_chip_info_free(handle);
return true;
}

public override bool IsInvalid => handle == IntPtr.Zero;
}
Loading