Skip to content

Conversation

@joperezr
Copy link
Member

@joperezr joperezr commented Jun 22, 2023

Fixes #1070

cc: @rafal-mz @Ellerbach @krwq @pgrawehr

Things that I need help with for testing this:

  • Ensure that libgpiod driver still works as expected when picking it manually
  • Ensure that libgpiod driver is now the default in boards that haven't installed the libgpiod-dev package
  • If possible, Ensure that this works in several boards, not just a Raspberry Pi.
Microsoft Reviewers: Open in CodeFlow

@ghost ghost added the area-System.Device.Gpio Contains types for using general-purpose I/O (GPIO) pins label Jun 22, 2023
@joperezr joperezr added this to the v3.0.0 milestone Jun 22, 2023
Copy link
Member

@Ellerbach Ellerbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

@pgrawehr
Copy link
Contributor

I tested the behavior (with the Board sample project).

  • Initially, I had the packages libgpiod-dev and libgpiod2 installed on my Pi4. As expected, the test showed that the libgpiod driver was used for interrupt handling.
  • Then I uninstalled both of these packages, and it no longer worked (sysfs was used)
  • Then I re-added libgpiod2 and it worked again.

I have not tested whether libgipod2 is installed by default on a recent Raspberry Pi OS image.

@joperezr
Copy link
Member Author

Thanks for testing @pgrawehr! Yeah I think the behavior you are seeing is expected, and indeed it is my believe that libgpiod2 comes installed by default in the RPI OS which is the main value that the PR is bringing. If someone would be able to validate that, it would be great and I would consider this ready to go in.

@raffaeler
Copy link
Contributor

Test successful on RPi4

  • both packages installed: uses libgpiod driver
  • libgpiod2 packages only: uses libgpiod driver
  • no packages: uses sysfs driver
    Note: after running the app with sysfs and re-installing the two packages, the app crashes because of this

@raffaeler
Copy link
Contributor

Test failed on RPi3 with Raspbian version 'stretch'.

compiled with .NET 6

The package libgpiod2 is not available on 'stretch' therefore it always use sysfs.
On 'stretch' it is only available libgpiod1 (even when installing libgpiod-dev)
Anyway the app did not crash.

compiled with .NET 7

Same app/binaries of the RPi4 test, compiled as self-contained:

Failed to load /home/pi/test/IotButton1/libcoreclr.so, error: /lib/arm-linux-gnueabihf/libm.so.6: version `GLIBC_2.27' not found (required by /home/pi/test/IotButton1/libcoreclr.so)
Segmentation fault

This error is new to me

@raffaeler
Copy link
Contributor

I have no other hardware boards to test on.

@krwq
Copy link
Member

krwq commented Jun 29, 2023

[Triage] We should figure out if we can make it still work with libgpiod 1

@raffaeler
Copy link
Contributor

raffaeler commented Jul 4, 2023

The available Raspbian versions are the following (Bullseye is the first available at 64 bit)

Version Codename Debian release date RPI OS release date
9 Stretch June 2017 August 2017
10 Buster July 2019 July 2019
11 Bullseye August 2021 November 2021

Source: https://raspberrytips.com/raspberry-pi-os-versions/

This is the summary of my tests:

Version Codename libgpiod version Test result
9 Stretch 1 failure
10 Buster 2 success
11 Bullseye 2 success

Given that the string "libgpiod.2" is a const, we have few options:

  1. bind to an arbitrary name and create a symbolic link (ln utility) on the first run
  2. parameterize the library name and dynamic load the library instead of pinvoke
  3. support only v1 and v2 by duplicating the class (with some compile trick to avoid duplicated code)
  4. drop support for Stretch

@joperezr I believe that before or later option 3 and 4 will fail again in case of a v3. What do you think?

@krwq
Copy link
Member

krwq commented Jul 6, 2023

[Triage] This will be merged post 3.0

Copy link
Contributor

@raffaeler raffaeler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review the comments

@ghost ghost added the Needs: Author Feedback We are waiting for author to react to feedback (action required) label Jul 6, 2023
@krwq krwq removed this from the v3.0.0 milestone Jul 6, 2023
@joperezr
Copy link
Member Author

joperezr commented Jul 7, 2023

Sorry, catching up with this thread.

I don't think there is anything wrong with us dropping stretch for newer versions of the packages. People using stretch, can always use packages from 2.x era, but for 3.x stuff we will require higher OS, and I think that is fine as long as it is well documented. Once libgpiod 3.x is available, we can then use bump to use that in our next major version release. I don't think this PR should be merged post 3.0 release, it should either be included in it, or just wait till 4.0 as it is a breaking change. We know it is breaking, and we always have, which is why we wanted to use the major version bump to take this break as we believe the overall benefits would be more than the setbacks.

@ghost ghost removed the Needs: Author Feedback We are waiting for author to react to feedback (action required) label Jul 7, 2023
@raffaeler
Copy link
Contributor

People using stretch, can always use packages from 2.x era

I tried to install libgpiod2 on stretch and the installation (apt i) failed.
I guess the same will happen at the time a libgpiod3 will come out, but this is just a guess.

@joperezr
Copy link
Member Author

joperezr commented Jul 7, 2023

Right, what I meant here is that people using stretch can still use our System.Device.Gpio and Iot.Device.Bindings 2.x packages, which does support stretch. What I'm saying is that now that we are shipping a new major version, we can decide to drop support for stretch with the libgpiod driver (meaning potentially losing some people there and would just endup using sysfs there) in order to have much more people using libgpiod driver in newer distros (which we know this driver behaves much better than sysfs).

@joperezr
Copy link
Member Author

joperezr commented Jul 7, 2023

As long as we communicate this correctly, in our release notes and package notes we can say which package is recommended depending on your base Linux distro.

@Tragetaschen
Copy link
Contributor

Tragetaschen commented Jul 12, 2023

Given that the string "libgpiod.2" is a const, we have few options:

  1. bind to an arbitrary name and create a symbolic link (ln utility) on the first run

This can also be done using NativeLibrary.SetDllImport. That is, chose a well known string constant and then in the resolver try to load libgpiod.so.2 and libgpiod.so.1 in order to be compatiple with both. This is what I did "from the outside" to load the major-versioned libgpiod on a system without the -dev dependency installed.

From the inadvertent tests in #1070, there doesn't seem to be an incompatibility in the used exports between 1 and 2 as both work fine.

@raffaeler
Copy link
Contributor

@joperezr I would definitely prefer to support both and not dropping the old OS just because of this.
The proposed solution from @Tragetaschen (which I also used in the past) works great in .NET (core) but not in netstandard.

@joperezr
Copy link
Member Author

My only concerns with that approach would be perf impact, which could be mitigated by making sure we somehow cache the result so we only have to find the right version once, as opposed to having to do it for every single PInvoke. If we are able to do that, then I'm fine with closing this PR and then doing this post 3.0 release (as we wouldn't be doing a breaking change anymore so no need to tie this to a major version bump)

@Tragetaschen
Copy link
Contributor

@joperezr The work of loading a native library is O(1) with the constant being 1 ;-)
The libgpiod.so.* is loaded exactly once and so the resolver registered with NativeLibrary is asked exactly once to intercept that load attempt.

@krwq
Copy link
Member

krwq commented Jul 13, 2023

We'd presumably call SetDllImportResolver in one of the static ctors?

@raffaeler
Copy link
Contributor

We'd presumably call SetDllImportResolver in one of the static ctors?

@krwq We can definitely use NativeLibrary for .NET, but we have to do something for netstandard as well.

@Tragetaschen
Copy link
Contributor

Tragetaschen commented Jul 13, 2023

We'd presumably call SetDllImportResolver in one of the static ctors?

Or use ModuleInitializerAttribute

@joperezr
Copy link
Member Author

Thanks for the suggestion @Tragetaschen I think that is a good idea for us to try. In favor of that, I'm closing this PR so that we instead can go with the NativeLibrary load approach. @Tragetaschen given you are familiar to how all of that works, would you be willing to contribute a PR to achieve this 😃?

@joperezr joperezr closed this Jul 13, 2023
@Tragetaschen
Copy link
Contributor

Under different circumstances, I would gladly go ahead, but I changed jobs a couple of years back and don't have any embedded platform to run and test code on.

@joperezr
Copy link
Member Author

joperezr commented Jul 13, 2023

Understood, appreciate the guidance and I hope you'd be okay if we tag you on the PR once we have it ready in case you have a chance to do a quick review.

FWIW, looks like runtime is attempting to do something super similar due to the exact same problem but with a different native library, and they have a PR open to fix this dotnet/runtime#88851 so we can use that as guidance.

@github-actions github-actions bot locked and limited conversation to collaborators Dec 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Device.Gpio Contains types for using general-purpose I/O (GPIO) pins

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Include the major version when referencing *.so libraries

7 participants