diff --git a/Documentation/gpio-linux-libgpiod.md b/Documentation/gpio-linux-libgpiod.md index f7712ebfbf..ae4813391a 100644 --- a/Documentation/gpio-linux-libgpiod.md +++ b/Documentation/gpio-linux-libgpiod.md @@ -50,7 +50,7 @@ The following table shows which driver supports which library version | V2 | 2.x | NOTE: Due to a [breaking change in the values of enums in the libgpiod]( -https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/commit/?id=783ff2e3c70788cdd1c65cba9ee0398bda5ebcda), only libgpiod versions 1.1 and later can be expected to function reliably with the V1 driver. +https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/commit/?id=783ff2e3c70788cdd1c65cba9ee0398bda5ebcda), only libgpiod versions 1.1 and later can be expected to function reliably with the V1 driver. To check what libgpiod packages you have on a deb based system, use: ``` $apt show libgpiod* ``` ## Choose LibGpiodDriver Version diff --git a/src/System.Device.Gpio.Tests/GpioControllerTestBase.cs b/src/System.Device.Gpio.Tests/GpioControllerTestBase.cs index 23ff09efb7..76fc5128e0 100644 --- a/src/System.Device.Gpio.Tests/GpioControllerTestBase.cs +++ b/src/System.Device.Gpio.Tests/GpioControllerTestBase.cs @@ -320,18 +320,10 @@ void Callback(object sender, PinValueChangedEventArgs e) [Fact] public void AddCallbackRemoveAllCallbackTest() { - using GpioDriver testDriver = GetTestDriver(); - // Skipping the test for now when using the SysFsDriver or the RaspberryPi3Driver given that this test is flaky for those drivers. - // Issue tracking this problem is https://github.com/dotnet/iot/issues/629 - if (testDriver is SysFsDriver || testDriver is RaspberryPi3Driver) - { - return; - } - RetryHelper.Execute(() => { int risingEventOccurredCount = 0, fallingEventOccurredCount = 0; - using (GpioController controller = new GpioController(testDriver)) + using (GpioController controller = new GpioController(GetTestDriver())) { controller.OpenPin(InputPin, PinMode.Input); controller.OpenPin(OutputPin, PinMode.Output); @@ -454,6 +446,7 @@ public void WaitForEventRisingEdgeTest() using (GpioController controller = new GpioController(GetTestDriver())) { CancellationTokenSource tokenSource = new CancellationTokenSource(); + tokenSource.CancelAfter(TimeSpan.FromSeconds(20)); controller.OpenPin(InputPin, PinMode.Input); controller.OpenPin(OutputPin, PinMode.Output); controller.Write(OutputPin, PinValue.Low); @@ -479,6 +472,7 @@ public void WaitForEventFallingEdgeTest() using (GpioController controller = new GpioController(GetTestDriver())) { CancellationTokenSource tokenSource = new CancellationTokenSource(); + tokenSource.CancelAfter(TimeSpan.FromSeconds(30)); controller.OpenPin(InputPin, PinMode.Input); controller.OpenPin(OutputPin, PinMode.Output); controller.Write(OutputPin, PinValue.Low); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs index 9b41006d8f..22f949f380 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs @@ -9,7 +9,7 @@ using System.Diagnostics; using System.Linq; using System.Diagnostics.CodeAnalysis; - +using System.Globalization; using LibgpiodV1 = Interop.LibgpiodV1; namespace System.Device.Gpio.Drivers; @@ -114,7 +114,6 @@ public static IList GetAvailableChips() { List result = new List(); var iterator = new SafeChipIteratorHandle(LibgpiodV1.gpiod_chip_iter_new()); - int index = 0; while (true) { SafeChipHandle chip = new SafeChipHandle(LibgpiodV1.gpiod_chip_iter_next_noclose(iterator)); @@ -129,8 +128,26 @@ public static IList GetAvailableChips() if (!result.Any(x => x.Label == label && x.NumLines == numLines)) { // The iterator may find duplicates, but we skip them here - result.Add(new GpioChipInfo(index, name, label, numLines)); - index++; + // Need to find the number at the end of the name (e.g. 15 in gpiochip15) + int id = 0; + int numberOfDigitsAtEnd = 0; + for (var i = name.Length - 1; i >= 0; i--) + { + if (!char.IsDigit(name[i])) + { + break; + } + + numberOfDigitsAtEnd++; + } + + string theNumber = name[^numberOfDigitsAtEnd..]; + if (!Int32.TryParse(theNumber, CultureInfo.InvariantCulture, out id)) + { + id = 0; + } + + result.Add(new GpioChipInfo(id, name, label, numLines)); } chip.Dispose(); diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs index 2635f4c610..fdd035017e 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioDriver.cs @@ -1,218 +1,218 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; - -namespace System.Device.Gpio; - -/// -/// Base class for Gpio Drivers. -/// A Gpio driver provides methods to read from and write to digital I/O pins. -/// -public abstract class GpioDriver : IDisposable -{ - /// - /// Finalizer to clean up unmanaged resources - /// - ~GpioDriver() - { - Dispose(false); - } - - /// - /// The number of pins provided by the driver. - /// - protected internal abstract int PinCount { get; } - - /// - /// Converts a board pin number to the driver's logical numbering scheme. - /// - /// The board pin number to convert. - /// The pin number in the driver's logical numbering scheme. - protected internal abstract int ConvertPinNumberToLogicalNumberingScheme(int pinNumber); - - /// - /// Opens a pin in order for it to be ready to use. - /// The driver attempts to open the pin without changing its mode or value. - /// - /// The pin number in the driver's logical numbering scheme. - protected internal abstract void OpenPin(int pinNumber); - - /// - /// Closes an open pin. - /// - /// The pin number in the driver's logical numbering scheme. - protected internal abstract void ClosePin(int pinNumber); - - /// - /// Sets the mode to a pin. - /// - /// The pin number in the driver's logical numbering scheme. - /// The mode to be set. - protected internal abstract void SetPinMode(int pinNumber, PinMode mode); - - /// - /// Sets the mode to a pin and sets an initial value for an output pin. - /// - /// The pin number in the driver's logical numbering scheme. - /// The mode to be set. - /// The initial value if the is output. The driver will do it's best to prevent glitches to the other value when - /// changing from input to output. - protected internal virtual void SetPinMode(int pinNumber, PinMode mode, PinValue initialValue) - { - SetPinMode(pinNumber, mode); - if (mode == PinMode.Output) - { - Write(pinNumber, initialValue); - } - } - - /// - /// Gets the mode of a pin. - /// - /// The pin number in the driver's logical numbering scheme. - /// The mode of the pin. - protected internal abstract PinMode GetPinMode(int pinNumber); - - /// - /// Checks if a pin supports a specific mode. - /// - /// The pin number in the driver's logical numbering scheme. - /// The mode to check. - /// The status if the pin supports the mode. - protected internal abstract bool IsPinModeSupported(int pinNumber, PinMode mode); - - /// - /// Reads the current value of a pin. - /// - /// The pin number in the driver's logical numbering scheme. - /// The value of the pin. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Device.Gpio; + +/// +/// Base class for Gpio Drivers. +/// A Gpio driver provides methods to read from and write to digital I/O pins. +/// +public abstract class GpioDriver : IDisposable +{ + /// + /// Finalizer to clean up unmanaged resources + /// + ~GpioDriver() + { + Dispose(false); + } + + /// + /// The number of pins provided by the driver. + /// + protected internal abstract int PinCount { get; } + + /// + /// Converts a board pin number to the driver's logical numbering scheme. + /// + /// The board pin number to convert. + /// The pin number in the driver's logical numbering scheme. + protected internal abstract int ConvertPinNumberToLogicalNumberingScheme(int pinNumber); + + /// + /// Opens a pin in order for it to be ready to use. + /// The driver attempts to open the pin without changing its mode or value. + /// + /// The pin number in the driver's logical numbering scheme. + protected internal abstract void OpenPin(int pinNumber); + + /// + /// Closes an open pin. + /// + /// The pin number in the driver's logical numbering scheme. + protected internal abstract void ClosePin(int pinNumber); + + /// + /// Sets the mode to a pin. + /// + /// The pin number in the driver's logical numbering scheme. + /// The mode to be set. + protected internal abstract void SetPinMode(int pinNumber, PinMode mode); + + /// + /// Sets the mode to a pin and sets an initial value for an output pin. + /// + /// The pin number in the driver's logical numbering scheme. + /// The mode to be set. + /// The initial value if the is output. The driver will do it's best to prevent glitches to the other value when + /// changing from input to output. + protected internal virtual void SetPinMode(int pinNumber, PinMode mode, PinValue initialValue) + { + SetPinMode(pinNumber, mode); + if (mode == PinMode.Output) + { + Write(pinNumber, initialValue); + } + } + + /// + /// Gets the mode of a pin. + /// + /// The pin number in the driver's logical numbering scheme. + /// The mode of the pin. + protected internal abstract PinMode GetPinMode(int pinNumber); + + /// + /// Checks if a pin supports a specific mode. + /// + /// The pin number in the driver's logical numbering scheme. + /// The mode to check. + /// The status if the pin supports the mode. + protected internal abstract bool IsPinModeSupported(int pinNumber, PinMode mode); + + /// + /// Reads the current value of a pin. + /// + /// The pin number in the driver's logical numbering scheme. + /// The value of the pin. protected internal abstract PinValue Read(int pinNumber); - /// - /// Read the given pins with the given pin numbers. - /// - /// - /// The default implementation calls for each pin in the array. - /// where possible, drivers should override this method to provide a more efficient implementation. - /// - /// The pin/value pairs to read. - protected internal virtual void Read(Span pinValuePairs) - { - for (int i = 0; i < pinValuePairs.Length; i++) - { - int pin = pinValuePairs[i].PinNumber; - pinValuePairs[i] = new PinValuePair(pin, Read(pin)); - } - } - - /// - /// Toggle the current value of a pin. - /// - /// The pin number in the driver's logical numbering scheme. - protected internal virtual void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber)); - - /// - /// Writes a value to a pin. - /// - /// The pin number in the driver's logical numbering scheme. - /// The value to be written to the pin. + /// + /// Read the given pins with the given pin numbers. + /// + /// + /// The default implementation calls for each pin in the array. + /// where possible, drivers should override this method to provide a more efficient implementation. + /// + /// The pin/value pairs to read. + protected internal virtual void Read(Span pinValuePairs) + { + for (int i = 0; i < pinValuePairs.Length; i++) + { + int pin = pinValuePairs[i].PinNumber; + pinValuePairs[i] = new PinValuePair(pin, Read(pin)); + } + } + + /// + /// Toggle the current value of a pin. + /// + /// The pin number in the driver's logical numbering scheme. + protected internal virtual void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber)); + + /// + /// Writes a value to a pin. + /// + /// The pin number in the driver's logical numbering scheme. + /// The value to be written to the pin. protected internal abstract void Write(int pinNumber, PinValue value); - /// - /// Write the given pins with the given values. - /// - /// - /// The default implementation calls for each pin in the array. - /// where possible, drivers should override this method to provide a more efficient implementation. - /// - /// The pin/value pairs to write. - protected internal virtual void Write(ReadOnlySpan pinValuePairs) - { - for (int i = 0; i < pinValuePairs.Length; i++) - { - Write(pinValuePairs[i].PinNumber, pinValuePairs[i].PinValue); - } - } - - /// - /// Blocks execution until an event of type eventType is received or a cancellation is requested. - /// - /// The pin number in the driver's logical numbering scheme. - /// The event types to wait for. - /// The cancellation token of when the operation should stop waiting for an event. - /// A structure that contains the result of the waiting operation. - protected internal abstract WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken); - - /// - /// Async call until an event of type eventType is received or a cancellation is requested. - /// - /// The pin number in the driver's logical numbering scheme. - /// The event types to wait for. - /// The cancellation token of when the operation should stop waiting for an event. - /// A task representing the operation of getting the structure that contains the result of the waiting operation - protected internal virtual ValueTask WaitForEventAsync(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken) - { - return new ValueTask(Task.Run(() => WaitForEvent(pinNumber, eventTypes, cancellationToken))); - } - - /// - /// Adds a handler for a pin value changed event. - /// - /// The pin number in the driver's logical numbering scheme. - /// The event types to wait for. - /// Delegate that defines the structure for callbacks when a pin value changed event occurs. - protected internal abstract void AddCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback); - - /// - /// Removes a handler for a pin value changed event. - /// - /// The pin number in the driver's logical numbering scheme. - /// Delegate that defines the structure for callbacks when a pin value changed event occurs. - protected internal abstract void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback); - - /// - /// Disposes this instance, closing all open pins - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes this instance - /// - /// True if explicitly disposing, false if in finalizer - protected virtual void Dispose(bool disposing) - { - // Nothing to do in base class. - } - - /// - /// Query information about a component and its children. - /// - /// A tree of instances. - /// - /// The returned data structure (or rather, its string representation) can be used to diagnose problems with incorrect driver types or - /// other system configuration problems. - /// This method is currently reserved for debugging purposes. Its behavior its and signature are subject to change. - /// - public virtual ComponentInformation QueryComponentInformation() - { - return new ComponentInformation(this, "Gpio Driver"); - } - - /// - /// Gets information about the current chip - /// - /// An instance of the record - /// The current driver does not support this data - [Experimental(DiagnosticIds.SDGPIO0001, UrlFormat = DiagnosticIds.UrlFormat)] - public virtual GpioChipInfo GetChipInfo() - { - throw new NotSupportedException(); - } -} + /// + /// Write the given pins with the given values. + /// + /// + /// The default implementation calls for each pin in the array. + /// where possible, drivers should override this method to provide a more efficient implementation. + /// + /// The pin/value pairs to write. + protected internal virtual void Write(ReadOnlySpan pinValuePairs) + { + for (int i = 0; i < pinValuePairs.Length; i++) + { + Write(pinValuePairs[i].PinNumber, pinValuePairs[i].PinValue); + } + } + + /// + /// Blocks execution until an event of type eventType is received or a cancellation is requested. + /// + /// The pin number in the driver's logical numbering scheme. + /// The event types to wait for. + /// The cancellation token of when the operation should stop waiting for an event. + /// A structure that contains the result of the waiting operation. + protected internal abstract WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken); + + /// + /// Async call until an event of type eventType is received or a cancellation is requested. + /// + /// The pin number in the driver's logical numbering scheme. + /// The event types to wait for. + /// The cancellation token of when the operation should stop waiting for an event. + /// A task representing the operation of getting the structure that contains the result of the waiting operation + protected internal virtual ValueTask WaitForEventAsync(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken) + { + return new ValueTask(Task.Run(() => WaitForEvent(pinNumber, eventTypes, cancellationToken))); + } + + /// + /// Adds a handler for a pin value changed event. + /// + /// The pin number in the driver's logical numbering scheme. + /// The event types to wait for. + /// Delegate that defines the structure for callbacks when a pin value changed event occurs. + protected internal abstract void AddCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback); + + /// + /// Removes a handler for a pin value changed event. + /// + /// The pin number in the driver's logical numbering scheme. + /// Delegate that defines the structure for callbacks when a pin value changed event occurs. + protected internal abstract void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback); + + /// + /// Disposes this instance, closing all open pins + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes this instance + /// + /// True if explicitly disposing, false if in finalizer + protected virtual void Dispose(bool disposing) + { + // Nothing to do in base class. + } + + /// + /// Query information about a component and its children. + /// + /// A tree of instances. + /// + /// The returned data structure (or rather, its string representation) can be used to diagnose problems with incorrect driver types or + /// other system configuration problems. + /// This method is currently reserved for debugging purposes. Its behavior its and signature are subject to change. + /// + public virtual ComponentInformation QueryComponentInformation() + { + return new ComponentInformation(this, "Gpio Driver"); + } + + /// + /// Gets information about the current chip + /// + /// An instance of the record + /// The current driver does not support this data + [Experimental(DiagnosticIds.SDGPIO0001, UrlFormat = DiagnosticIds.UrlFormat)] + public virtual GpioChipInfo GetChipInfo() + { + throw new NotSupportedException(); + } +}