dotnet / iot

This repo includes .NET Core implementations for various IoT boards, chips, displays and PCBs.
MIT License
2.13k stars 574 forks source link

libgpiod v2 support #2179

Closed huesla closed 5 months ago

huesla commented 7 months ago

Preface In order not to make this post even more complicated than it already is I will try to simplify it a bit. It is mainly about libgpiod versions. Since these show an inconsistency I would like to clear this up first.

libgpiod has a

  1. documented version (see releases ): In this post I mark this with the 'v' prefix, e.g. 'v2.1'
  2. library version: I mark this with the ".so" prefix: e.g. '.so.3.1.0'

libgpiod versions became inconsistent from 'v1.1'. See the following table which lists the most important documented versions with library version:

v1.0.2 -> .so.1.0.2 (last .so.1.x library) v1.1 -> .so.2.0.0 (first occurrence of inconsistency, first .so.2.x library) v1.6.4 -> .so.2.2.2 (last .so.2.x library) v2.0 -> .so.3.0.0 (first .so.3.x library) v2.1 -> .so.3.1.0 (latest .so.3.x libray)

Checked by building each released libgpiod version, then compared to the built library version.

Hello all, I would like to use the LibGpiodDriver for libgpiod v2.1 on the Raspberry Pi5. I had a little look into the code and noticed a few things.

As you can see Interop.libgpiod.cs tries to bind against libgpiod.so.2 which can be resolved to the latest available minor version e.g. .so.2.2.2.

Since this is current master (591188c) I assume that v2 versions (.so.3) are not yet supported.

I would be interested to know if there are future plans for this. If this is not the case I would be willing to contribute, although it is not yet clear to me what the versioning strategy in this repo is to support binding native libraries.

What I mean is this: If you look at the code you can see that in the class Interop.libgpiod.cs it tries to bind the library libgpiodor if >= NET6 libgpiod.so.2. Before NET6 libgpiod would tried be resolved, which gives a different result depending on the installed libgpiod version, (huh?). With NET6 or higher, libgpiod.so.2 would tried to be resolved, i.e. all versions from .so.2.0.0 to .so.2.2.2.

Nevertheless: The bind functions in the class are the same. (No #if NET6_0_OR_GREATER pre processor directive)

Now you can find the method IsLibgpiodVersion1_5orHigher() inLibGpiodDriver.cswhich is used to provide an additional method (gpiod_line_bias) via 'if branching'. v1.5 or higher corresponds to .so.2.2.0 -> .so.2.2.2. This means that versions .so.2.0.0 -> .so.2.2.0 (aka v1.1 -> v.1.4.5) would not be supported at all, whatever these functions are (unfortunately I can't find any online documentation for v1.4.5). I do not really care about that, because I am not aiming to use such an old version anyways.

I am just a bit confused about which libgpiod version is supported by which dotnet/iot version, and I can't find any documentation on this.

Actually I would have expected to find code structure where the versioning is clearly visible. For example an Interop class that binds against v0.9, one against v1.5 or other major versions. The same with LibGpioDriver.

I would be interested to know what you think. Regards, huesla

raffaeler commented 7 months ago

Hello, the latest change we made on libgpiod binding was in this PR. At least in theory this should bind to the latest major version so that you don't need to apt install the dev package anymore.

Since you are using the RPi5, you may want to take a look to this conversation on libgpiod behavior on the RPi5 as well.

Please let us know if this answers or not to your question.

joperezr commented 7 months ago

Hello @huesla, thanks for logging the issue. I should probably start by answering the question about what is the intention of this repo. Our main intent, is to try hard to have devs using our library being able to run their code in the devices that they use. To do this, and specifically when talking about libgpiod, it means we should at least aim to be able to work with libgpiod versions that come installed in popular distros.

When we wrote the code originally, we were using 1.x versions and we then noticed that 2.x didn't introduce major breaking changes (or at least not on the APIs we were calling) so with minor changes we were able to re-target to 2.x. After doing so, we started getting some reports and requests saying that some distros still only come with 1.x installed, and their desire to be able to run the code without the additional package install. That's why we came up with the changes on the PR that @raffaeler pointed out above, which essentially were to directly tied down to 2.x, but if we can't find it we make an attempt to instead log 1.x. This, is only possible because the APIs we call didn't really take any breaking changes between 1.x and 2.x.

As for your question around 3.x, we should check to see if the same still applies and there are no major breaking changes between 2.x and 3.x, and if that's the case, we can change our fallback logic so that it tries to load that version too. I would say in that case we should choose the loading order based on popularity, so defaulting to the version that is more commonly found, and then follow the same pattern for the fallback.

If 3.x does require code changes, we can no longer use our fallback logic like that, and instead would need to create a runtime check that would see which version of the library is found, and depending on that it would use a different native PAL.

Hope my answer make sense, and if you are up for it, always happy to take a PR 😄. Let me know if you have any additional questions.

huesla commented 7 months ago

@raffaeler @joperezr thanks for the historic insights.

libgpiod v2 actually differs a lot to previous versions. It targets version 2 of the Kernel GPIO character device, that addresses some issues of v1 like being able to set the debounce period of GPIO edge events (see here for more information). libgpiod v2 uses the v2 char device and introduces different concepts and models (request object etc.) to control GPIO lines.

I will look into it more and how to bind C# against it.

Ellerbach commented 7 months ago

[Triage] Thank you @huesla for the detailed explanations. As you seem to understand the differences and how things are working between the different versions, please proceed to try to implement your idea of PAL. We will be happy to support and help.

huesla commented 7 months ago

I'm back from my cave with a first version of the libgpiodv2 C# binding! It is completely integrated into the existing GpioController/Driver architecture. It includes the binding to the libgpiodv2 API, abstracting objects (so-called proxies) for easier use of the API, and the new LibGpiodV2Driver which can be used by the existing GpioController. You can find more details in the PR I am curious what you think of it, and hope to integrate it as well as possible into the existing architecture.