From 2531b88ba7ddcbb07ce1b558d1cedb2fe53595e9 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Sat, 25 Nov 2023 11:54:02 +0000 Subject: [PATCH 1/8] Adding virtual gpio controller basic --- .../System/Device/Gpio/GpioPin.cs | 8 + .../Gpio/Drivers/VirtualGpioController.cs | 281 ++++++++++++++++++ src/devices/Gpio/Drivers/VirtualGpioPin.cs | 58 ++++ src/devices/Gpio/README.md | 22 +- 4 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 src/devices/Gpio/Drivers/VirtualGpioController.cs create mode 100644 src/devices/Gpio/Drivers/VirtualGpioPin.cs diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index 2bef40424c..c7f1ef4458 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -17,6 +17,14 @@ public class Gpio​Pin _pinNumber = pinNumber; } + /// + /// Only for compatibility reason and heritage. Do not use in normal usage. + /// + protected GpioPin() + { + _driver = null!; + } + /// /// Gets the pin number of the general-purpose I/O (GPIO) pin. /// diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs new file mode 100644 index 0000000000..ac8b08c72c --- /dev/null +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -0,0 +1,281 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using System.Threading; +using System.Collections.Generic; +using System.Linq; +using System.Collections.Concurrent; +using System.Device.Gpio; +using System; +using System.Device; + +namespace Iot.Device.Gpio +{ + /// + /// An implementation of a Virtual . + /// + public class VirtualGpioController : GpioController + { + private readonly ConcurrentDictionary _pins = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _openPins = new ConcurrentDictionary(); + + /// + /// Initializes a new instance of the class. + /// + public VirtualGpioController() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The numbering scheme used to represent pins provided by the controller. + public VirtualGpioController(PinNumberingScheme numberingScheme) + { + // Nothing on purpose, as only logical is suported + } + + /// + /// Initializes a new instance of the class. + /// + /// The numbering scheme used to represent pins provided by the controller. + /// The driver that manages all of the pin operations for the controller. + public VirtualGpioController(PinNumberingScheme numberingScheme, GpioDriver driver) + { + throw new NotImplementedException(); + } + + /// + /// Initializes a new instance of the class. + /// + public VirtualGpioController(Dictionary pins) + { + foreach (var pin in pins) + { + _pins.TryAdd(pin.Key, pin.Value); + } + } + + /// + /// Initializes a new instance of the class. + /// + public VirtualGpioController(IEnumerable pins) + { + int inc = 0; + foreach (var pin in pins) + { + _pins.TryAdd(inc++, pin); + } + } + + /// + /// Adds a new pin number with an associated . + /// + /// The pin number. + /// The to add. + public void Add(int pinNumber, GpioPin gpioPin) => _pins.TryAdd(pinNumber, gpioPin); + + /// + /// Adds a new . The number is done automatically by adding after the last element. + /// + /// The to add. + public void Add(GpioPin gpioPin) + { + // Find the last number of the pin and assign the new number to it + int newPin = _pins.Count > 0 ? _pins.Keys.Max() + 1 : 0; + _pins.TryAdd(newPin, gpioPin); + } + + /// + public override int PinCount => _pins.Count; + + /// + /// Closes an open pin. + /// If allowed by the driver, the state of the pin is not changed. + /// + /// The pin number in the controller's numbering scheme. + public new void ClosePin(int pinNumber) + { + if (!IsPinOpen(pinNumber)) + { + throw new InvalidOperationException($"Can not close pin {pinNumber} because it is not open."); + } + + _openPins.TryRemove(pinNumber, out _); + } + + /// + /// Disposes this instance and closes all open pins associated with this controller. + /// + public new void Dispose() + { + foreach (int pin in _openPins.Keys) + { + // The list contains the pin in the current NumberingScheme + ClosePin(pin); + } + + // We're just emptying the lists + _pins.Clear(); + _openPins.Clear(); + } + + /// + public override PinMode GetPinMode(int pinNumber) + { + if (!IsPinOpen(pinNumber)) + { + throw new InvalidOperationException($"Can not set a mode to pin {pinNumber} because it is not open."); + } + + return _pins[pinNumber].GetPinMode(); + } + + /// + public override bool IsPinModeSupported(int pinNumber, PinMode mode) + { + return _pins[pinNumber].IsPinModeSupported(mode); + } + + /// + /// Checks if a specific pin is open. + /// + /// The pin number in the controller's numbering scheme. + /// The status if the pin is open or closed. + public new bool IsPinOpen(int pinNumber) + { + return _openPins.ContainsKey(pinNumber); + } + + /// + /// Opens a pin and sets it to a specific mode. + /// + /// The pin number in the controller's numbering scheme. + /// The mode to be set. + public new GpioPin OpenPin(int pinNumber, PinMode mode) + { + var pin = OpenPin(pinNumber); + _pins[pinNumber].SetPinMode(mode); + return pin; + } + + /// + /// Opens a pin and sets it to a specific mode and value. + /// + /// The pin number in the controller's numbering scheme. + /// The mode to be set. + /// The initial value to be set if the mode is output. The driver will attempt to set the mode without causing glitches to the other value. + /// (if is , the pin should not glitch to low during open) + public new GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) + { + var pin = OpenPin(pinNumber, mode); + _pins[pinNumber].Write(initialValue); + return pin; + } + + /// + /// 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 controller's numbering scheme. + public new GpioPin OpenPin(int pinNumber) + { + if (IsPinOpen(pinNumber)) + { + return _openPins[pinNumber]; + } + + _openPins.TryAdd(pinNumber, new VirtualGpioPin(_pins[pinNumber], pinNumber)); + return _openPins[pinNumber]; + } + + /// + public override ComponentInformation QueryComponentInformation() + { + ComponentInformation self = new ComponentInformation(this, "Virtual GPIO Controller"); + + // PinCount is not added on purpose, because the property throws NotSupportedException on some hardware + self.Properties["OpenPins"] = string.Join(", ", _openPins.Select(x => x.Key)); + + return self; + } + + /// + public override PinValue Read(int pinNumber) + { + if (!IsPinOpen(pinNumber)) + { + throw new InvalidOperationException($"Can not read from pin {pinNumber} because it is not open."); + } + + return _pins[pinNumber].Read(); + } + + /// + /// Sets the mode to a pin. + /// + /// The pin number in the controller's numbering scheme. + /// The mode to be set. + public override void SetPinMode(int pinNumber, PinMode mode) + { + if (!IsPinOpen(pinNumber)) + { + throw new InvalidOperationException($"Can not set a mode to pin {pinNumber} because it is not open."); + } + + if (!IsPinModeSupported(pinNumber, mode)) + { + throw new InvalidOperationException($"Pin {pinNumber} does not support mode {mode}."); + } + + _pins[pinNumber].SetPinMode(mode); + } + + /// + public override void Write(int pinNumber, PinValue value) + { + if (!IsPinOpen(pinNumber)) + { + throw new InvalidOperationException($"Can not write to pin {pinNumber} because it is not open."); + } + + _pins[pinNumber].Write(value); + } + + /// + public override void Toggle(int pinNumber) + { + if (!IsPinOpen(pinNumber)) + { + throw new InvalidOperationException($"Can not read from pin {pinNumber} because it is not open."); + } + + _pins[pinNumber].Toggle(); + } + + /// + public override void RegisterCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback) + { + // Todo + } + + /// + public override void UnregisterCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) + { + // Todo + } + + /// + public override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + /// + public override ValueTask WaitForEventAsync(int pinNumber, PinEventTypes eventTypes, CancellationToken token) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/devices/Gpio/Drivers/VirtualGpioPin.cs b/src/devices/Gpio/Drivers/VirtualGpioPin.cs new file mode 100644 index 0000000000..0f202fa248 --- /dev/null +++ b/src/devices/Gpio/Drivers/VirtualGpioPin.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Device.Gpio; + +namespace Iot.Device.Gpio +{ + /// + /// Represents a general-purpose I/O (GPIO) pin. + /// + public class VirtualGpioPin : GpioPin + { + private int _virtualPinNumber; + private GpioPin _pin; + + /// + public override event PinChangeEventHandler? ValueChanged; + + /// + public override int PinNumber => _virtualPinNumber; + + /// + /// Initializes a new instance of the class. + /// + /// A valid . + /// The new virutal pin number. + public VirtualGpioPin(GpioPin gpioPin, int virtualPinNumber) + { + _virtualPinNumber = virtualPinNumber; + _pin = gpioPin ?? throw new ArgumentNullException(nameof(gpioPin)); + _pin.ValueChanged += PinValueChanged; + } + + private void PinValueChanged(object sender, PinValueChangedEventArgs pinValueChangedEventArgs) + { + ValueChanged?.Invoke(this, pinValueChangedEventArgs); + } + + /// + public override PinMode GetPinMode() => _pin.GetPinMode(); + + /// + public override bool IsPinModeSupported(PinMode pinMode) => _pin.IsPinModeSupported(pinMode); + + /// + public override void SetPinMode(PinMode value) => _pin.SetPinMode(value); + + /// + public override PinValue Read() => _pin.Read(); + + /// + public override void Write(PinValue value) => _pin.Write(value); + + /// + public override void Toggle() => _pin.Toggle(); + } +} diff --git a/src/devices/Gpio/README.md b/src/devices/Gpio/README.md index 9e701716c6..53a347fe61 100644 --- a/src/devices/Gpio/README.md +++ b/src/devices/Gpio/README.md @@ -1,4 +1,4 @@ -# GpioDriver for other boards +# GpioDriver for other boards and virtual GpioController and virtual GpioPin This project contains some **full function(PULL-UP, PULL-DOWN)** generic GPIO drivers, and it can provide faster GPIO access. @@ -7,6 +7,26 @@ This project contains some **full function(PULL-UP, PULL-DOWN)** generic GPIO dr * For Allwinner SoCs: [SunxiDriver](Drivers/Sunxi/README.md) * For Rockchip SoCs: [RockchipDriver](Drivers/Rockchip/README.md) +## Usage of vivirtual GpioController and virtual GpioPin + +When you have multiple GpioControllers and want to use them together in a specific binding, the `VirtualGpioController` allows to create a GpioController from GpioPin already open from different controllers. USage is straight forward and classes fully compatible with a normal `GpioController`. + +```csharp +// Create pins from 2 different GpioControllers +var pinFromA = gpioControllerA.Open(42, PinMode.Output); +var pinFromB = gpioControllerB.Open(24, PinMode.Input); +var pinFromC = gpioControllerC.Open(12, PinMode.Input); +VirtualGpioController gpio = new(); +// pin number will be 0 in this case +gpio.Add(pinFromA); +// pin number will be 12 in this case +gpio.Add(12, pinFromB); +// the pin number will be 13 in this case +gpio.Add(pinFromC); +``` + +When adding an existing GpioPin, by default, the new pin allocation will directly follow the higher pin number already existing. You can also use this method to change the numbering of your board. Note that the VirtualGpioController is only operating in the logical numbering. + ## Board specific drivers | Board | Driver | From fcae355782657c35312f33f33842f43d773bdb84 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Sat, 25 Nov 2023 17:55:15 +0000 Subject: [PATCH 2/8] add basic eventing --- .../Gpio/Drivers/VirtualGpioController.cs | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs index ac8b08c72c..3eab85db73 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioController.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -19,6 +19,9 @@ public class VirtualGpioController : GpioController { private readonly ConcurrentDictionary _pins = new ConcurrentDictionary(); private readonly ConcurrentDictionary _openPins = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _callbackEvents = new ConcurrentDictionary(); + + private record PinEvent(PinEventTypes PinEventTypes, PinChangeEventHandler? Callbacks); /// /// Initializes a new instance of the class. @@ -257,13 +260,47 @@ public override void Toggle(int pinNumber) /// public override void RegisterCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback) { - // Todo + if (_callbackEvents.Count == 0) + { + _pins[pinNumber].ValueChanged += PinValueChanged; + } + + _callbackEvents.TryAdd(pinNumber, new PinEvent(eventTypes, callback)); + } + + private void PinValueChanged(object sender, PinValueChangedEventArgs pinValueChangedEventArgs) + { + // Get GpioPin associated with the real life pin + // Note that if we have multiple pins with the same pin number, we will only take the first one in the list + var pins = _pins.Where(m => m.Value.PinNumber == pinValueChangedEventArgs.PinNumber); + foreach (var pin in pins) + { + if (_callbackEvents.ContainsKey(pin.Key)) + { + var pinEvent = _callbackEvents[pin.Key]; + if (pinEvent == null) + { + return; + } + + if (pinEvent.PinEventTypes == pinValueChangedEventArgs.ChangeType) + { + pinEvent.Callbacks?.Invoke(this, new PinValueChangedEventArgs(pinValueChangedEventArgs.ChangeType, pin.Key)); + } + + break; + } + } } /// public override void UnregisterCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) { - // Todo + _callbackEvents.TryRemove(pinNumber, out _); + if (_callbackEvents.Count == 0) + { + _pins[pinNumber].ValueChanged -= PinValueChanged; + } } /// From 2c2dda03dc32fdc72f3af6441faea6510019402c Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Mon, 11 Dec 2023 10:13:45 +0100 Subject: [PATCH 3/8] Adjusting GpioPin internals to take a GpioPin --- .../System.Device.Gpio.csproj | 2 +- .../System/Device/Gpio/GpioPin.cs | 45 ++++++++------ .../Gpio/Drivers/VirtualGpioController.cs | 13 ++++- src/devices/Gpio/Drivers/VirtualGpioPin.cs | 58 ------------------- src/devices/Gpio/README.md | 2 +- 5 files changed, 40 insertions(+), 80 deletions(-) delete mode 100644 src/devices/Gpio/Drivers/VirtualGpioPin.cs diff --git a/src/System.Device.Gpio/System.Device.Gpio.csproj b/src/System.Device.Gpio/System.Device.Gpio.csproj index 8cfbcd95b7..991f6c0d4b 100644 --- a/src/System.Device.Gpio/System.Device.Gpio.csproj +++ b/src/System.Device.Gpio/System.Device.Gpio.csproj @@ -70,5 +70,5 @@ - + diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index c7f1ef4458..8232484e26 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -8,21 +8,30 @@ namespace System.Device.Gpio /// public class Gpio​Pin { - private readonly int _pinNumber; - private readonly GpioDriver _driver; + private readonly int _externalPinNumber; + + /// + /// Gets or sets the internal pin number used by the driver. + /// + internal int DriverPinNumber { get; set; } + + /// + /// Gets or sets the . + /// + internal GpioDriver Driver { get; set; } internal Gpio​Pin(int pinNumber, GpioDriver driver) { - _driver = driver; - _pinNumber = pinNumber; + Driver = driver; + DriverPinNumber = pinNumber; + _externalPinNumber = -1; } - /// - /// Only for compatibility reason and heritage. Do not use in normal usage. - /// - protected GpioPin() + internal GpioPin(GpioPin gpioPin, int pinNumber) { - _driver = null!; + Driver = gpioPin.Driver; + DriverPinNumber = gpioPin.DriverPinNumber; + _externalPinNumber = pinNumber; } /// @@ -31,14 +40,14 @@ protected GpioPin() /// /// The pin number of the GPIO pin. /// - public virtual int PinNumber => _pinNumber; + public virtual int PinNumber => _externalPinNumber >= 0 ? _externalPinNumber : DriverPinNumber; /// /// Gets the current pin mode for the general-purpose I/O (GPIO) pin. The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. /// /// An enumeration value that indicates the current pin mode for the GPIO pin. /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. - public virtual PinMode GetPinMode() => _driver.GetPinMode(_pinNumber); + public virtual PinMode GetPinMode() => Driver.GetPinMode(DriverPinNumber); /// /// Gets whether the general-purpose I/O (GPIO) pin supports the specified pin mode. @@ -48,7 +57,7 @@ protected GpioPin() /// if the GPIO pin supports the pin mode that pinMode specifies; otherwise false. /// If you specify a pin mode for which this method returns when you call , generates an exception. /// - public virtual bool IsPinModeSupported(PinMode pinMode) => _driver.IsPinModeSupported(_pinNumber, pinMode); + public virtual bool IsPinModeSupported(PinMode pinMode) => Driver.IsPinModeSupported(DriverPinNumber, pinMode); /// /// Sets the pin mode of the general-purpose I/O (GPIO) pin. @@ -57,13 +66,13 @@ protected GpioPin() /// An enumeration value that specifies pin mode to use for the GPIO pin. /// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin. /// The GPIO pin does not support the specified pin mode. - public virtual void SetPinMode(PinMode value) => _driver.SetPinMode(_pinNumber, value); + public virtual void SetPinMode(PinMode value) => Driver.SetPinMode(DriverPinNumber, value); /// /// Reads the current value of the general-purpose I/O (GPIO) pin. /// /// The current value of the GPIO pin. If the pin is configured as an output, this value is the last value written to the pin. - public virtual PinValue Read() => _driver.Read(_pinNumber); + public virtual PinValue Read() => Driver.Read(DriverPinNumber); /// /// Drives the specified value onto the general purpose I/O (GPIO) pin according to the current pin mode for the pin @@ -74,7 +83,7 @@ protected GpioPin() /// If the GPIO pin is configured as an input, the method updates the latched output value for the pin. The latched output value is driven onto the pin when the configuration for the pin changes to output. /// /// This exception will be thrown on an attempt to write to a pin that hasn't been opened or is not configured as output. - public virtual void Write(PinValue value) => _driver.Write(_pinNumber, value); + public virtual void Write(PinValue value) => Driver.Write(DriverPinNumber, value); /// /// Occurs when the value of the general-purpose I/O (GPIO) pin changes, either because of an external stimulus when the pin is configured as an input, or when a value is written to the pin when the pin in configured as an output. @@ -83,18 +92,18 @@ public virtual event PinChangeEventHandler ValueChanged { add { - _driver.AddCallbackForPinValueChangedEvent(_pinNumber, PinEventTypes.Falling | PinEventTypes.Rising, value); + Driver.AddCallbackForPinValueChangedEvent(DriverPinNumber, PinEventTypes.Falling | PinEventTypes.Rising, value); } remove { - _driver.RemoveCallbackForPinValueChangedEvent(_pinNumber, value); + Driver.RemoveCallbackForPinValueChangedEvent(DriverPinNumber, value); } } /// /// Toggles the output of the general purpose I/O (GPIO) pin if the pin is configured as an output. /// - public virtual void Toggle() => _driver.Toggle(_pinNumber); + public virtual void Toggle() => Driver.Toggle(DriverPinNumber); } } diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs index 3eab85db73..d29813c24b 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioController.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -9,6 +9,7 @@ using System.Device.Gpio; using System; using System.Device; +using System.Reflection; namespace Iot.Device.Gpio { @@ -18,7 +19,7 @@ namespace Iot.Device.Gpio public class VirtualGpioController : GpioController { private readonly ConcurrentDictionary _pins = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _openPins = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _openPins = new ConcurrentDictionary(); private readonly ConcurrentDictionary _callbackEvents = new ConcurrentDictionary(); private record PinEvent(PinEventTypes PinEventTypes, PinChangeEventHandler? Callbacks); @@ -189,7 +190,15 @@ public override bool IsPinModeSupported(int pinNumber, PinMode mode) return _openPins[pinNumber]; } - _openPins.TryAdd(pinNumber, new VirtualGpioPin(_pins[pinNumber], pinNumber)); + // We're going to call the internal constructor taking a GpioPin + var ctor = typeof(GpioPin).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic). + FirstOrDefault(c => c.GetParameters().Where(m => m.ParameterType == typeof(GpioPin)).Any()); + if (ctor == null) + { + throw new ArgumentException("Invalid version of System.Device.Gpio"); + } + + _openPins.TryAdd(pinNumber, (GpioPin)ctor.Invoke(new object[2] { _pins[pinNumber], pinNumber })); return _openPins[pinNumber]; } diff --git a/src/devices/Gpio/Drivers/VirtualGpioPin.cs b/src/devices/Gpio/Drivers/VirtualGpioPin.cs deleted file mode 100644 index 0f202fa248..0000000000 --- a/src/devices/Gpio/Drivers/VirtualGpioPin.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Device.Gpio; - -namespace Iot.Device.Gpio -{ - /// - /// Represents a general-purpose I/O (GPIO) pin. - /// - public class VirtualGpioPin : GpioPin - { - private int _virtualPinNumber; - private GpioPin _pin; - - /// - public override event PinChangeEventHandler? ValueChanged; - - /// - public override int PinNumber => _virtualPinNumber; - - /// - /// Initializes a new instance of the class. - /// - /// A valid . - /// The new virutal pin number. - public VirtualGpioPin(GpioPin gpioPin, int virtualPinNumber) - { - _virtualPinNumber = virtualPinNumber; - _pin = gpioPin ?? throw new ArgumentNullException(nameof(gpioPin)); - _pin.ValueChanged += PinValueChanged; - } - - private void PinValueChanged(object sender, PinValueChangedEventArgs pinValueChangedEventArgs) - { - ValueChanged?.Invoke(this, pinValueChangedEventArgs); - } - - /// - public override PinMode GetPinMode() => _pin.GetPinMode(); - - /// - public override bool IsPinModeSupported(PinMode pinMode) => _pin.IsPinModeSupported(pinMode); - - /// - public override void SetPinMode(PinMode value) => _pin.SetPinMode(value); - - /// - public override PinValue Read() => _pin.Read(); - - /// - public override void Write(PinValue value) => _pin.Write(value); - - /// - public override void Toggle() => _pin.Toggle(); - } -} diff --git a/src/devices/Gpio/README.md b/src/devices/Gpio/README.md index 53a347fe61..1997ed2432 100644 --- a/src/devices/Gpio/README.md +++ b/src/devices/Gpio/README.md @@ -7,7 +7,7 @@ This project contains some **full function(PULL-UP, PULL-DOWN)** generic GPIO dr * For Allwinner SoCs: [SunxiDriver](Drivers/Sunxi/README.md) * For Rockchip SoCs: [RockchipDriver](Drivers/Rockchip/README.md) -## Usage of vivirtual GpioController and virtual GpioPin +## Usage of virtual GpioController and virtual GpioPin When you have multiple GpioControllers and want to use them together in a specific binding, the `VirtualGpioController` allows to create a GpioController from GpioPin already open from different controllers. USage is straight forward and classes fully compatible with a normal `GpioController`. From ac5ac45f696e10e6494708b97bddd5a28b8fa41a Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Mon, 8 Apr 2024 10:00:30 +0200 Subject: [PATCH 4/8] Adjusting based on discussions --- .../System/Device/Gpio/GpioPin.cs | 7 ++++- .../Gpio/Drivers/VirtualGpioController.cs | 11 ++----- src/devices/Gpio/Drivers/VirtualGpioPin.cs | 18 +++++++++++ src/devices/Gpio/Gpio.sln | 30 +++++++++---------- src/devices/Gpio/samples/Program.cs | 2 +- 5 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 src/devices/Gpio/Drivers/VirtualGpioPin.cs diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index 8232484e26..91d6bebc67 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -27,7 +27,12 @@ public class Gpio​Pin _externalPinNumber = -1; } - internal GpioPin(GpioPin gpioPin, int pinNumber) + /// + /// Creates an instance of GPIO Pin with a new pin number based on an existing GpioPin. + /// + /// The GPIO Pin class. + /// The new pin number to associate. + protected internal GpioPin(GpioPin gpioPin, int pinNumber) { Driver = gpioPin.Driver; DriverPinNumber = gpioPin.DriverPinNumber; diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs index d29813c24b..c9acb30b9b 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioController.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -10,6 +10,7 @@ using System; using System.Device; using System.Reflection; +using Iot.Device.Gpio.Drivers; namespace Iot.Device.Gpio { @@ -190,15 +191,9 @@ public override bool IsPinModeSupported(int pinNumber, PinMode mode) return _openPins[pinNumber]; } - // We're going to call the internal constructor taking a GpioPin - var ctor = typeof(GpioPin).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic). - FirstOrDefault(c => c.GetParameters().Where(m => m.ParameterType == typeof(GpioPin)).Any()); - if (ctor == null) - { - throw new ArgumentException("Invalid version of System.Device.Gpio"); - } + VirtualGpioPin pin = new VirtualGpioPin(_pins[pinNumber], pinNumber); - _openPins.TryAdd(pinNumber, (GpioPin)ctor.Invoke(new object[2] { _pins[pinNumber], pinNumber })); + _openPins.TryAdd(pinNumber, pin); return _openPins[pinNumber]; } diff --git a/src/devices/Gpio/Drivers/VirtualGpioPin.cs b/src/devices/Gpio/Drivers/VirtualGpioPin.cs new file mode 100644 index 0000000000..22d841d32e --- /dev/null +++ b/src/devices/Gpio/Drivers/VirtualGpioPin.cs @@ -0,0 +1,18 @@ +// 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; + +namespace Iot.Device.Gpio.Drivers +{ + /// + /// A virtual GpioPin to allow creating a new GpioPin and a new pin number + /// + internal class VirtualGpioPin : GpioPin + { + protected internal VirtualGpioPin(GpioPin gpioPin, int pinNumber) + : base(gpioPin, pinNumber) + { + } + } +} diff --git a/src/devices/Gpio/Gpio.sln b/src/devices/Gpio/Gpio.sln index a0409a1280..534463f238 100644 --- a/src/devices/Gpio/Gpio.sln +++ b/src/devices/Gpio/Gpio.sln @@ -5,12 +5,12 @@ VisualStudioVersion = 17.0.32002.185 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{45EC0518-E2FF-4278-ABDB-E0A2D79E71BE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iot.Device.Gpio.Samples", "Iot.Device.Gpio.Samples.csproj", "{356BCA51-E8A9-4190-A814-70A6F87E33E1}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iot.Device.Gpio", "Iot.Device.Gpio.csproj", "{B9CCBDB6-FAE3-49BF-B654-30698EF8131D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "..\Common\Common.csproj", "{52F56388-FBD6-4658-ABFC-161FBD79A5EA}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iot.Device.Gpio.Samples", "samples\Iot.Device.Gpio.Samples.csproj", "{455A62CA-C3D1-40AA-BD70-985E14129A92}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,18 +21,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|x64.ActiveCfg = Debug|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|x64.Build.0 = Debug|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|x86.ActiveCfg = Debug|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|x86.Build.0 = Debug|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Release|Any CPU.Build.0 = Release|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Release|x64.ActiveCfg = Release|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Release|x64.Build.0 = Release|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Release|x86.ActiveCfg = Release|Any CPU - {356BCA51-E8A9-4190-A814-70A6F87E33E1}.Release|x86.Build.0 = Release|Any CPU {B9CCBDB6-FAE3-49BF-B654-30698EF8131D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B9CCBDB6-FAE3-49BF-B654-30698EF8131D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B9CCBDB6-FAE3-49BF-B654-30698EF8131D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -57,12 +45,24 @@ Global {52F56388-FBD6-4658-ABFC-161FBD79A5EA}.Release|x64.Build.0 = Release|Any CPU {52F56388-FBD6-4658-ABFC-161FBD79A5EA}.Release|x86.ActiveCfg = Release|Any CPU {52F56388-FBD6-4658-ABFC-161FBD79A5EA}.Release|x86.Build.0 = Release|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Debug|x64.ActiveCfg = Debug|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Debug|x64.Build.0 = Debug|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Debug|x86.ActiveCfg = Debug|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Debug|x86.Build.0 = Debug|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Release|Any CPU.Build.0 = Release|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Release|x64.ActiveCfg = Release|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Release|x64.Build.0 = Release|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Release|x86.ActiveCfg = Release|Any CPU + {455A62CA-C3D1-40AA-BD70-985E14129A92}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {356BCA51-E8A9-4190-A814-70A6F87E33E1} = {45EC0518-E2FF-4278-ABDB-E0A2D79E71BE} + {455A62CA-C3D1-40AA-BD70-985E14129A92} = {45EC0518-E2FF-4278-ABDB-E0A2D79E71BE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3D905E48-D204-4FA4-AEF3-4B7AFC8047BC} diff --git a/src/devices/Gpio/samples/Program.cs b/src/devices/Gpio/samples/Program.cs index 88400c9648..4813f75bd4 100644 --- a/src/devices/Gpio/samples/Program.cs +++ b/src/devices/Gpio/samples/Program.cs @@ -68,4 +68,4 @@ bool Debounce() } } } -} \ No newline at end of file +} From 14b991b9bf8546caecbdec39027b172427836b2b Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Mon, 8 Apr 2024 11:11:24 +0200 Subject: [PATCH 5/8] Adjusting based on PR feedback --- .../System/Device/Gpio/GpioController.cs | 16 ++++++------ .../System/Device/Gpio/GpioPin.cs | 11 +++++--- .../Gpio/Drivers/VirtualGpioController.cs | 25 +++++++------------ src/devices/Gpio/README.md | 2 +- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs index fd1ee6a960..0536c98760 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs @@ -103,7 +103,7 @@ protected virtual int GetLogicalPinNumber(int pinNumber) /// The driver attempts to open the pin without changing its mode or value. /// /// The pin number in the controller's numbering scheme. - public GpioPin OpenPin(int pinNumber) + public virtual GpioPin OpenPin(int pinNumber) { if (IsPinOpen(pinNumber)) { @@ -131,7 +131,7 @@ protected virtual void OpenPinCore(int pinNumber) /// /// The pin number in the controller's numbering scheme. /// The mode to be set. - public GpioPin OpenPin(int pinNumber, PinMode mode) + public virtual GpioPin OpenPin(int pinNumber, PinMode mode) { var pin = OpenPin(pinNumber); SetPinMode(pinNumber, mode); @@ -145,7 +145,7 @@ public GpioPin OpenPin(int pinNumber, PinMode mode) /// The mode to be set. /// The initial value to be set if the mode is output. The driver will attempt to set the mode without causing glitches to the other value. /// (if is , the pin should not glitch to low during open) - public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) + public virtual GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) { var pin = OpenPin(pinNumber); // Set the desired initial value @@ -160,7 +160,7 @@ public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) /// If allowed by the driver, the state of the pin is not changed. /// /// The pin number in the controller's numbering scheme. - public void ClosePin(int pinNumber) + public virtual void ClosePin(int pinNumber) { if (!IsPinOpen(pinNumber)) { @@ -231,7 +231,7 @@ public virtual PinMode GetPinMode(int pinNumber) /// /// The pin number in the controller's numbering scheme. /// The status if the pin is open or closed. - public bool IsPinOpen(int pinNumber) + public virtual bool IsPinOpen(int pinNumber) { return _openPins.ContainsKey(pinNumber); } @@ -416,7 +416,7 @@ protected virtual void Dispose(bool disposing) } /// - public void Dispose() + public virtual void Dispose() { Dispose(true); } @@ -425,7 +425,7 @@ public void Dispose() /// Write the given pins with the given values. /// /// The pin/value pairs to write. - public void Write(ReadOnlySpan pinValuePairs) + public virtual void Write(ReadOnlySpan pinValuePairs) { for (int i = 0; i < pinValuePairs.Length; i++) { @@ -437,7 +437,7 @@ public void Write(ReadOnlySpan pinValuePairs) /// Read the given pins with the given pin numbers. /// /// The pin/value pairs to read. - public void Read(Span pinValuePairs) + public virtual void Read(Span pinValuePairs) { for (int i = 0; i < pinValuePairs.Length; i++) { diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index 91d6bebc67..278e180419 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -20,7 +20,12 @@ public class Gpio​Pin /// internal GpioDriver Driver { get; set; } - internal Gpio​Pin(int pinNumber, GpioDriver driver) + /// + /// Creates an instance of GPIO Pin with a new pin number based on an existing . + /// + /// The GPIO pin number. + /// The GpioDriver. + protected internal Gpio​Pin(int pinNumber, GpioDriver driver) { Driver = driver; DriverPinNumber = pinNumber; @@ -28,9 +33,9 @@ public class Gpio​Pin } /// - /// Creates an instance of GPIO Pin with a new pin number based on an existing GpioPin. + /// Creates an instance of GPIO Pin with a new pin number based on an existing . /// - /// The GPIO Pin class. + /// The GpioPin. /// The new pin number to associate. protected internal GpioPin(GpioPin gpioPin, int pinNumber) { diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs index c9acb30b9b..9f51bafa06 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioController.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -39,16 +39,10 @@ public VirtualGpioController() public VirtualGpioController(PinNumberingScheme numberingScheme) { // Nothing on purpose, as only logical is suported - } - - /// - /// Initializes a new instance of the class. - /// - /// The numbering scheme used to represent pins provided by the controller. - /// The driver that manages all of the pin operations for the controller. - public VirtualGpioController(PinNumberingScheme numberingScheme, GpioDriver driver) - { - throw new NotImplementedException(); + if (numberingScheme != PinNumberingScheme.Logical) + { + throw new ArgumentException("Only PinNumberingScheme Logical is supported."); + } } /// @@ -100,7 +94,7 @@ public void Add(GpioPin gpioPin) /// If allowed by the driver, the state of the pin is not changed. /// /// The pin number in the controller's numbering scheme. - public new void ClosePin(int pinNumber) + public override void ClosePin(int pinNumber) { if (!IsPinOpen(pinNumber)) { @@ -113,7 +107,7 @@ public void Add(GpioPin gpioPin) /// /// Disposes this instance and closes all open pins associated with this controller. /// - public new void Dispose() + public override void Dispose() { foreach (int pin in _openPins.Keys) { @@ -148,7 +142,7 @@ public override bool IsPinModeSupported(int pinNumber, PinMode mode) /// /// The pin number in the controller's numbering scheme. /// The status if the pin is open or closed. - public new bool IsPinOpen(int pinNumber) + public override bool IsPinOpen(int pinNumber) { return _openPins.ContainsKey(pinNumber); } @@ -158,7 +152,7 @@ public override bool IsPinModeSupported(int pinNumber, PinMode mode) /// /// The pin number in the controller's numbering scheme. /// The mode to be set. - public new GpioPin OpenPin(int pinNumber, PinMode mode) + public override GpioPin OpenPin(int pinNumber, PinMode mode) { var pin = OpenPin(pinNumber); _pins[pinNumber].SetPinMode(mode); @@ -172,7 +166,7 @@ public override bool IsPinModeSupported(int pinNumber, PinMode mode) /// The mode to be set. /// The initial value to be set if the mode is output. The driver will attempt to set the mode without causing glitches to the other value. /// (if is , the pin should not glitch to low during open) - public new GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) + public override GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) { var pin = OpenPin(pinNumber, mode); _pins[pinNumber].Write(initialValue); @@ -202,7 +196,6 @@ public override ComponentInformation QueryComponentInformation() { ComponentInformation self = new ComponentInformation(this, "Virtual GPIO Controller"); - // PinCount is not added on purpose, because the property throws NotSupportedException on some hardware self.Properties["OpenPins"] = string.Join(", ", _openPins.Select(x => x.Key)); return self; diff --git a/src/devices/Gpio/README.md b/src/devices/Gpio/README.md index 1997ed2432..2623ab96fa 100644 --- a/src/devices/Gpio/README.md +++ b/src/devices/Gpio/README.md @@ -9,7 +9,7 @@ This project contains some **full function(PULL-UP, PULL-DOWN)** generic GPIO dr ## Usage of virtual GpioController and virtual GpioPin -When you have multiple GpioControllers and want to use them together in a specific binding, the `VirtualGpioController` allows to create a GpioController from GpioPin already open from different controllers. USage is straight forward and classes fully compatible with a normal `GpioController`. +When you have multiple GpioControllers and want to use them together in a specific binding, the `VirtualGpioController` allows to create a GpioController from a set of `GpioPin`s already open from different controllers. Usage is straight forward and the class is fully compatible with a normal `GpioController`. ```csharp // Create pins from 2 different GpioControllers From 652895a1f816dfe3c3eb29527d4eafabceb75bd9 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Thu, 13 Jun 2024 16:25:30 +0200 Subject: [PATCH 6/8] adjusting logic and removing some complexity --- .../System/Device/Gpio/GpioController.cs | 18 +++--- .../Gpio/Drivers/VirtualGpioController.cs | 56 ++++--------------- 2 files changed, 21 insertions(+), 53 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs index 0536c98760..8fedcc58bf 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs @@ -33,7 +33,11 @@ public class GpioController : IDisposable /// If a pin element exists, that pin is open. Uses current controller's numbering scheme /// private readonly ConcurrentDictionary _openPins; - private readonly ConcurrentDictionary _gpioPins; + + /// + /// The GPIO pins open in the GpioController. + /// + protected readonly ConcurrentDictionary _gpioPins; private GpioDriver _driver; /// @@ -131,7 +135,7 @@ protected virtual void OpenPinCore(int pinNumber) /// /// The pin number in the controller's numbering scheme. /// The mode to be set. - public virtual GpioPin OpenPin(int pinNumber, PinMode mode) + public GpioPin OpenPin(int pinNumber, PinMode mode) { var pin = OpenPin(pinNumber); SetPinMode(pinNumber, mode); @@ -145,7 +149,7 @@ public virtual GpioPin OpenPin(int pinNumber, PinMode mode) /// The mode to be set. /// The initial value to be set if the mode is output. The driver will attempt to set the mode without causing glitches to the other value. /// (if is , the pin should not glitch to low during open) - public virtual GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) + public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) { var pin = OpenPin(pinNumber); // Set the desired initial value @@ -231,7 +235,7 @@ public virtual PinMode GetPinMode(int pinNumber) /// /// The pin number in the controller's numbering scheme. /// The status if the pin is open or closed. - public virtual bool IsPinOpen(int pinNumber) + public bool IsPinOpen(int pinNumber) { return _openPins.ContainsKey(pinNumber); } @@ -416,7 +420,7 @@ protected virtual void Dispose(bool disposing) } /// - public virtual void Dispose() + public void Dispose() { Dispose(true); } @@ -425,7 +429,7 @@ public virtual void Dispose() /// Write the given pins with the given values. /// /// The pin/value pairs to write. - public virtual void Write(ReadOnlySpan pinValuePairs) + public void Write(ReadOnlySpan pinValuePairs) { for (int i = 0; i < pinValuePairs.Length; i++) { @@ -437,7 +441,7 @@ public virtual void Write(ReadOnlySpan pinValuePairs) /// Read the given pins with the given pin numbers. /// /// The pin/value pairs to read. - public virtual void Read(Span pinValuePairs) + public void Read(Span pinValuePairs) { for (int i = 0; i < pinValuePairs.Length; i++) { diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs index 9f51bafa06..c43460197b 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioController.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -20,7 +20,6 @@ namespace Iot.Device.Gpio public class VirtualGpioController : GpioController { private readonly ConcurrentDictionary _pins = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _openPins = new ConcurrentDictionary(); private readonly ConcurrentDictionary _callbackEvents = new ConcurrentDictionary(); private record PinEvent(PinEventTypes PinEventTypes, PinChangeEventHandler? Callbacks); @@ -101,15 +100,16 @@ public override void ClosePin(int pinNumber) throw new InvalidOperationException($"Can not close pin {pinNumber} because it is not open."); } - _openPins.TryRemove(pinNumber, out _); + _gpioPins.TryRemove(pinNumber, out _); } /// /// Disposes this instance and closes all open pins associated with this controller. /// - public override void Dispose() + /// True to dispose all instances, false to dispose only unmanaged resources + protected override void Dispose(bool disposing) { - foreach (int pin in _openPins.Keys) + foreach (int pin in _gpioPins.Keys) { // The list contains the pin in the current NumberingScheme ClosePin(pin); @@ -117,7 +117,7 @@ public override void Dispose() // We're just emptying the lists _pins.Clear(); - _openPins.Clear(); + _gpioPins.Clear(); } /// @@ -137,58 +137,22 @@ public override bool IsPinModeSupported(int pinNumber, PinMode mode) return _pins[pinNumber].IsPinModeSupported(mode); } - /// - /// Checks if a specific pin is open. - /// - /// The pin number in the controller's numbering scheme. - /// The status if the pin is open or closed. - public override bool IsPinOpen(int pinNumber) - { - return _openPins.ContainsKey(pinNumber); - } - - /// - /// Opens a pin and sets it to a specific mode. - /// - /// The pin number in the controller's numbering scheme. - /// The mode to be set. - public override GpioPin OpenPin(int pinNumber, PinMode mode) - { - var pin = OpenPin(pinNumber); - _pins[pinNumber].SetPinMode(mode); - return pin; - } - - /// - /// Opens a pin and sets it to a specific mode and value. - /// - /// The pin number in the controller's numbering scheme. - /// The mode to be set. - /// The initial value to be set if the mode is output. The driver will attempt to set the mode without causing glitches to the other value. - /// (if is , the pin should not glitch to low during open) - public override GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) - { - var pin = OpenPin(pinNumber, mode); - _pins[pinNumber].Write(initialValue); - return pin; - } - /// /// 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 controller's numbering scheme. - public new GpioPin OpenPin(int pinNumber) + public override GpioPin OpenPin(int pinNumber) { if (IsPinOpen(pinNumber)) { - return _openPins[pinNumber]; + return _gpioPins[pinNumber]; } VirtualGpioPin pin = new VirtualGpioPin(_pins[pinNumber], pinNumber); - _openPins.TryAdd(pinNumber, pin); - return _openPins[pinNumber]; + _gpioPins.TryAdd(pinNumber, pin); + return _gpioPins[pinNumber]; } /// @@ -196,7 +160,7 @@ public override ComponentInformation QueryComponentInformation() { ComponentInformation self = new ComponentInformation(this, "Virtual GPIO Controller"); - self.Properties["OpenPins"] = string.Join(", ", _openPins.Select(x => x.Key)); + self.Properties["OpenPins"] = string.Join(", ", _gpioPins.Select(x => x.Key)); return self; } From ee0ee9c940b110e0fc3569803f0cd79ec20478a3 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Mon, 1 Jul 2024 16:48:17 +0200 Subject: [PATCH 7/8] adjusting based on PR feedback --- .../System/Device/Gpio/GpioPin.cs | 1 + src/devices/Gpio/Drivers/VirtualGpioController.cs | 14 ++++++++------ src/devices/Gpio/Drivers/VirtualGpioPin.cs | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs index 278e180419..c2c51b714c 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioPin.cs @@ -8,6 +8,7 @@ namespace System.Device.Gpio /// public class Gpio​Pin { + // This pin number is used when there is a virtual controller. private readonly int _externalPinNumber; /// diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs index c43460197b..1466d62bc9 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioController.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -9,8 +9,6 @@ using System.Device.Gpio; using System; using System.Device; -using System.Reflection; -using Iot.Device.Gpio.Drivers; namespace Iot.Device.Gpio { @@ -89,8 +87,7 @@ public void Add(GpioPin gpioPin) public override int PinCount => _pins.Count; /// - /// Closes an open pin. - /// If allowed by the driver, the state of the pin is not changed. + /// This removes the pin for the virtual controller. It does not close the pin as the pin has been opened by another controller. /// /// The pin number in the controller's numbering scheme. public override void ClosePin(int pinNumber) @@ -104,7 +101,7 @@ public override void ClosePin(int pinNumber) } /// - /// Disposes this instance and closes all open pins associated with this controller. + /// Disposes this instance and removes all pins associated with this virtual controller. /// /// True to dispose all instances, false to dispose only unmanaged resources protected override void Dispose(bool disposing) @@ -125,7 +122,7 @@ public override PinMode GetPinMode(int pinNumber) { if (!IsPinOpen(pinNumber)) { - throw new InvalidOperationException($"Can not set a mode to pin {pinNumber} because it is not open."); + throw new InvalidOperationException($"Can not get a mode to pin {pinNumber} because it is not open."); } return _pins[pinNumber].GetPinMode(); @@ -160,6 +157,11 @@ public override ComponentInformation QueryComponentInformation() { ComponentInformation self = new ComponentInformation(this, "Virtual GPIO Controller"); + foreach (var cp in self.SubComponents) + { + self.AddSubComponent(cp); + } + self.Properties["OpenPins"] = string.Join(", ", _gpioPins.Select(x => x.Key)); return self; diff --git a/src/devices/Gpio/Drivers/VirtualGpioPin.cs b/src/devices/Gpio/Drivers/VirtualGpioPin.cs index 22d841d32e..c21c14654b 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioPin.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioPin.cs @@ -3,7 +3,7 @@ using System.Device.Gpio; -namespace Iot.Device.Gpio.Drivers +namespace Iot.Device.Gpio { /// /// A virtual GpioPin to allow creating a new GpioPin and a new pin number From 87033c71349c382674e2c70f871194dd33da0679 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Thu, 4 Jul 2024 11:39:22 +0200 Subject: [PATCH 8/8] adjusting name --- .../System/Device/Gpio/GpioController.cs | 16 ++++++++-------- .../Gpio/Drivers/VirtualGpioController.cs | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs index 8fedcc58bf..609e11e09c 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs @@ -37,7 +37,7 @@ public class GpioController : IDisposable /// /// The GPIO pins open in the GpioController. /// - protected readonly ConcurrentDictionary _gpioPins; + protected readonly ConcurrentDictionary GpioPins; private GpioDriver _driver; /// @@ -58,7 +58,7 @@ public GpioController(PinNumberingScheme numberingScheme, GpioDriver driver) _driver = driver; NumberingScheme = numberingScheme; _openPins = new ConcurrentDictionary(); - _gpioPins = new ConcurrentDictionary(); + GpioPins = new ConcurrentDictionary(); } /// @@ -88,7 +88,7 @@ private IEnumerable OpenPins { get { - return _gpioPins.Values; + return GpioPins.Values; } } @@ -111,13 +111,13 @@ public virtual GpioPin OpenPin(int pinNumber) { if (IsPinOpen(pinNumber)) { - return _gpioPins[pinNumber]; + return GpioPins[pinNumber]; } OpenPinCore(pinNumber); _openPins.TryAdd(pinNumber, null); - _gpioPins[pinNumber] = new GpioPin(pinNumber, _driver); - return _gpioPins[pinNumber]; + GpioPins[pinNumber] = new GpioPin(pinNumber, _driver); + return GpioPins[pinNumber]; } /// @@ -183,7 +183,7 @@ protected virtual void ClosePinCore(int pinNumber) { int logicalPinNumber = GetLogicalPinNumber(pinNumber); _driver.ClosePin(logicalPinNumber); - _gpioPins.TryRemove(pinNumber, out _); + GpioPins.TryRemove(pinNumber, out _); } /// @@ -414,7 +414,7 @@ protected virtual void Dispose(bool disposing) } _openPins.Clear(); - _gpioPins.Clear(); + GpioPins.Clear(); _driver?.Dispose(); _driver = null!; } diff --git a/src/devices/Gpio/Drivers/VirtualGpioController.cs b/src/devices/Gpio/Drivers/VirtualGpioController.cs index 1466d62bc9..79f536223b 100644 --- a/src/devices/Gpio/Drivers/VirtualGpioController.cs +++ b/src/devices/Gpio/Drivers/VirtualGpioController.cs @@ -97,7 +97,7 @@ public override void ClosePin(int pinNumber) throw new InvalidOperationException($"Can not close pin {pinNumber} because it is not open."); } - _gpioPins.TryRemove(pinNumber, out _); + GpioPins.TryRemove(pinNumber, out _); } /// @@ -106,7 +106,7 @@ public override void ClosePin(int pinNumber) /// True to dispose all instances, false to dispose only unmanaged resources protected override void Dispose(bool disposing) { - foreach (int pin in _gpioPins.Keys) + foreach (int pin in GpioPins.Keys) { // The list contains the pin in the current NumberingScheme ClosePin(pin); @@ -114,7 +114,7 @@ protected override void Dispose(bool disposing) // We're just emptying the lists _pins.Clear(); - _gpioPins.Clear(); + GpioPins.Clear(); } /// @@ -143,13 +143,13 @@ public override GpioPin OpenPin(int pinNumber) { if (IsPinOpen(pinNumber)) { - return _gpioPins[pinNumber]; + return GpioPins[pinNumber]; } VirtualGpioPin pin = new VirtualGpioPin(_pins[pinNumber], pinNumber); - _gpioPins.TryAdd(pinNumber, pin); - return _gpioPins[pinNumber]; + GpioPins.TryAdd(pinNumber, pin); + return GpioPins[pinNumber]; } /// @@ -162,7 +162,7 @@ public override ComponentInformation QueryComponentInformation() self.AddSubComponent(cp); } - self.Properties["OpenPins"] = string.Join(", ", _gpioPins.Select(x => x.Key)); + self.Properties["OpenPins"] = string.Join(", ", GpioPins.Select(x => x.Key)); return self; }