Skip to content

Commit

Permalink
Move to fully native implementation (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
josesimoes authored Oct 15, 2020
1 parent 2231cf0 commit 481b165
Show file tree
Hide file tree
Showing 40 changed files with 280 additions and 1,894 deletions.
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[*.cs]


# S4200: Native methods should be wrapped
# OK to call directly native methods in nanoFramework
dotnet_diagnostic.S4200.severity = none
321 changes: 194 additions & 127 deletions System.Device.Gpio/GpioController.cs

Large diffs are not rendered by default.

114 changes: 40 additions & 74 deletions Windows.Devices.Gpio/GpioPin.cs → System.Device.Gpio/GpioPin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,49 @@
// See LICENSE file in the project root for full license information.
//

using System;
using System.Runtime.CompilerServices;

namespace Windows.Devices.Gpio
namespace System.Device.Gpio
{
// This should be a TypedEventHandler "EventHandler<GpioPinValueChangedEventArgs>"
#pragma warning disable 1591
public delegate void GpioPinValueChangedEventHandler(
Object sender,
GpioPinValueChangedEventArgs e);

// This should be a TypedEventHandler "EventHandler<PinValueChangedEventArgs>"
#pragma warning disable 1591
public delegate void PinValueChangedEventHandler(
object sender,
PinValueChangedEventArgs e);

/// <summary>
/// Represents a general-purpose I/O (GPIO) pin.
/// </summary>
public sealed class Gpio​Pin : IGpioPin, IDisposable
public sealed class Gpio​Pin : IDisposable
{
private static GpioPinEventListener s_eventListener;
private static readonly GpioPinEventListener s_gpioPinEventManager = new GpioPinEventListener();

// this is used as the lock object
// a lock is required because multiple threads can access the GpioPin
private object _syncLock;
private readonly object _syncLock = new object();

private readonly int _pinNumber;
private GpioPinDriveMode _driveMode = GpioPinDriveMode.Input;
private PinMode _driveMode = PinMode.Input;
private TimeSpan _debounceTimeout = TimeSpan.Zero;
private GpioPinValueChangedEventHandler _callbacks = null;
private GpioPinValue _lastOutputValue = GpioPinValue.Low;
private PinValueChangedEventHandler _callbacks = null;
private PinValue _lastOutputValue = PinValue.Low;

#pragma warning disable 0414
// this field is used in native so it must be kept here despite "not being used"
private GpioPinValue _lastInputValue = GpioPinValue.Low;
private PinValue _lastInputValue = PinValue.Low;
#pragma warning restore 0414

internal GpioPin(int pinNumber)
{
_pinNumber = pinNumber;

_syncLock = new object();

lock (_syncLock)
{
if (s_eventListener == null)
{
s_eventListener = new GpioPinEventListener();
}
}
}

internal bool Init()
{
if(NativeInit(_pinNumber))
{
// add the pin to the event listener in order to receive the callbacks from the native interrupts
s_eventListener.AddPin(this);
s_gpioPinEventManager.AddPin(this);

return true;
}
Expand Down Expand Up @@ -93,31 +81,11 @@ public TimeSpan DebounceTimeout
/// <value>
/// The pin number of the GPIO pin.
/// </value>
public int PinNumber {
get
{
lock (_syncLock)
{
// check if pin has been disposed
if (!_disposedValue) { return _pinNumber; }

throw new ObjectDisposedException();
}
}
}

/// <summary>
/// Gets the sharing mode in which the general-purpose I/O (GPIO) pin is open.
/// </summary>
/// <value>
/// The sharing mode in which the GPIO pin is open.
/// </value>
public GpioSharingMode SharingMode
public int PinNumber
{
get
{
// at this time pins can't be shared, use is exclusive exclusive (pun intended!)
return GpioSharingMode.Exclusive;
return _pinNumber;
}
}

Expand All @@ -126,7 +94,7 @@ public GpioSharingMode SharingMode
/// </summary>
/// <returns>An enumeration value that indicates the current drive mode for the GPIO pin.
/// The drive mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin.</returns>
public GpioPinDriveMode GetDriveMode()
public PinMode GetDriveMode()
{
lock (_syncLock)
{
Expand All @@ -142,11 +110,11 @@ public GpioPinDriveMode GetDriveMode()
/// </summary>
/// <param name="driveMode">The drive mode that you want to check for support.</param>
/// <returns>
/// True if the GPIO pin supports the drive mode that driveMode specifies; otherwise false.
/// <see langword="true"/> if the GPIO pin supports the drive mode that driveMode specifies; otherwise false.
/// If you specify a drive mode for which this method returns false when you call <see cref="SetDriveMode"/>, <see cref="SetDriveMode"/> generates an exception.
/// </returns>

public bool IsDriveModeSupported(GpioPinDriveMode driveMode)
public bool IsDriveModeSupported(PinMode driveMode)
{
lock (_syncLock)
{
Expand All @@ -166,10 +134,10 @@ public bool IsDriveModeSupported(GpioPinDriveMode driveMode)
/// <remarks>The following exceptions can be thrown by this method:
/// <list type="bullet">
/// <item><term>E_INVALIDARG : The GPIO pin does not support the specified drive mode.</term></item>
/// <item><term>E_ACCESSDENIED : The pin is open in shared read-only mode.Close the pin and reopen it in exclusive mode to change the drive mode of the pin.</term></item>
/// <item><term>E_ACCESSDENIED : The pin is open in shared read-only mode. Close the pin and reopen it in exclusive mode to change the drive mode of the pin.</term></item>
/// </list>
/// </remarks>
public void SetDriveMode(GpioPinDriveMode value)
public void SetDriveMode(PinMode value)
{
lock (_syncLock)
{
Expand All @@ -191,7 +159,7 @@ public void SetDriveMode(GpioPinDriveMode value)
/// </summary>
/// <returns>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.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
public extern GpioPinValue Read();
public extern PinValue Read();

/// <summary>
/// Drives the specified value onto the general purpose I/O (GPIO) pin according to the current drive mode for the pin
Expand All @@ -206,7 +174,7 @@ public void SetDriveMode(GpioPinDriveMode value)
/// <item><term>E_ACCESSDENIED : The GPIO pin is open in shared read-only mode. To write to the pin, close the pin and reopen the pin in exclusive mode.</term></item>
/// </list>
/// </remarks>
public void Write(GpioPinValue value)
public void Write(PinValue value)
{
lock (_syncLock)
{
Expand All @@ -220,17 +188,17 @@ public void Write(GpioPinValue value)
WriteNative(value);

// trigger the pin value changed event, if any is set
GpioPinValueChangedEventHandler callbacks = _callbacks;
PinValueChangedEventHandler callbacks = _callbacks;

if (_lastOutputValue == GpioPinValue.Low)
if (_lastOutputValue == PinValue.Low)
{
// last value is now LOW, so it was HIGH
callbacks?.Invoke(this, new GpioPinValueChangedEventArgs(GpioPinEdge.FallingEdge));
callbacks?.Invoke(this, new PinValueChangedEventArgs(PinEventTypes.Falling, _pinNumber));
}
else
{
// last value is now HIGH, so it was LOW
callbacks?.Invoke(this, new GpioPinValueChangedEventArgs(GpioPinEdge.RisingEdge));
callbacks?.Invoke(this, new PinValueChangedEventArgs(PinEventTypes.Rising, _pinNumber));
}
}
}
Expand All @@ -239,7 +207,7 @@ public void Write(GpioPinValue value)
/// <summary>
/// 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.
/// </summary>
public event GpioPinValueChangedEventHandler ValueChanged
public event PinValueChangedEventHandler ValueChanged
{
add
{
Expand All @@ -251,7 +219,7 @@ public event GpioPinValueChangedEventHandler ValueChanged
}

var callbacksOld = _callbacks;
var callbacksNew = (GpioPinValueChangedEventHandler)Delegate.Combine(callbacksOld, value);
var callbacksNew = (PinValueChangedEventHandler)Delegate.Combine(callbacksOld, value);

try
{
Expand All @@ -276,7 +244,7 @@ public event GpioPinValueChangedEventHandler ValueChanged
}

var callbacksOld = _callbacks;
var callbacksNew = (GpioPinValueChangedEventHandler)Delegate.Remove(callbacksOld, value);
var callbacksNew = (PinValueChangedEventHandler)Delegate.Remove(callbacksOld, value);

try
{
Expand All @@ -296,9 +264,9 @@ public event GpioPinValueChangedEventHandler ValueChanged
/// Handles internal events and re-dispatches them to the publicly subscribed delegates.
/// </summary>
/// <param name="edge">The state transition for this event.</param>
internal void OnPinChangedInternal(GpioPinEdge edge)
internal void OnPinChangedInternal(PinEventTypes edge)
{
GpioPinValueChangedEventHandler callbacks = null;
PinValueChangedEventHandler callbacks = null;

lock (_syncLock)
{
Expand All @@ -308,18 +276,16 @@ internal void OnPinChangedInternal(GpioPinEdge edge)
}
}

callbacks?.Invoke(this, new GpioPinValueChangedEventArgs(edge));
callbacks?.Invoke(this, new PinValueChangedEventArgs(edge, _pinNumber));
}

/// <summary>
/// Toggles the output of the general purpose I/O (GPIO) pin if the pin is configured as an output.
/// </summary>
/// <remarks>
/// This method is exclusive of nanoFramework and it's not available in the UWP API.
/// </remarks>
[MethodImpl(MethodImplOptions.InternalCall)]
#pragma warning disable S4200 // OK to call native methods directly in nanoFramework
public extern void Toggle();

#pragma warning restore S4200 // Native methods should be wrapped

#region IDisposable Support

Expand All @@ -331,8 +297,8 @@ private void Dispose(bool disposing)
{
if (disposing)
{
// remove the pin from the event listner
s_eventListener.RemovePin(_pinNumber);
// remove the pin from the event listener
s_gpioPinEventManager.RemovePin(_pinNumber);
}

DisposeNative();
Expand Down Expand Up @@ -368,10 +334,10 @@ public void Dispose()
#region external calls to native implementations

[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool NativeIsDriveModeSupported(GpioPinDriveMode driveMode);
private extern bool NativeIsDriveModeSupported(PinMode driveMode);

[MethodImpl(MethodImplOptions.InternalCall)]
private extern void NativeSetDriveMode(GpioPinDriveMode driveMode);
private extern void NativeSetDriveMode(PinMode driveMode);

[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool NativeInit(int pinNumber);
Expand All @@ -380,7 +346,7 @@ public void Dispose()
private extern void NativeSetDebounceTimeout();

[MethodImpl(MethodImplOptions.InternalCall)]
private extern void WriteNative(GpioPinValue value);
private extern void WriteNative(PinValue value);

[MethodImpl(MethodImplOptions.InternalCall)]
internal extern void NativeSetAlternateFunction(int alternateFunction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

using nanoFramework.Runtime.Events;

namespace Windows.Devices.Gpio
namespace System.Device.Gpio
{
internal class GpioPinEvent : BaseEvent
{
public int PinNumber;
public GpioPinEdge Edge;
public PinEventTypes EventType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
//

using nanoFramework.Runtime.Events;
using System;
using System.Collections;

namespace Windows.Devices.Gpio
namespace System.Device.Gpio
{
internal class GpioPinEventListener : IEventProcessor, IEventListener
{
// Map of pin numbers to GpioPin objects.
private ArrayList _pinMap = new ArrayList();
private static readonly ArrayList _pinMap = new ArrayList();

public GpioPinEventListener()
{
Expand All @@ -26,7 +25,7 @@ public BaseEvent ProcessEvent(uint data1, uint data2, DateTime time)
{
// Data1 is packed by PostManagedEvent, so we need to unpack the high word.
PinNumber = (int)(data1 >> 16),
Edge = (data2 == 0) ? GpioPinEdge.FallingEdge : GpioPinEdge.RisingEdge,
EventType = (data2 == 0) ? PinEventTypes.Falling : PinEventTypes.Rising,
};
}

Expand All @@ -47,7 +46,7 @@ public bool OnEvent(BaseEvent ev)
// Avoid calling this under a lock to prevent a potential lock inversion.
if (pin != null)
{
pin.OnPinChangedInternal(pinEvent.Edge);
pin.OnPinChangedInternal(pinEvent.EventType);
}

return true;
Expand Down
4 changes: 3 additions & 1 deletion System.Device.Gpio/PinValueChangedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using nanoFramework.Runtime.Events;

namespace System.Device.Gpio
{
/// <summary>
/// Arguments passed in when an event is triggered by the GPIO.
/// </summary>
public class PinValueChangedEventArgs
public class PinValueChangedEventArgs : BaseEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="PinValueChangedEventArgs"/> class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Windows.Devices.Gpio")]
[assembly: AssemblyTitle("System.Device.Gpio")]
[assembly: AssemblyCompany("nanoFramework Contributors")]
[assembly: AssemblyProduct("Windows.Devices.Gpio")]
[assembly: AssemblyProduct("System.Device.Gpio")]
[assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")]

////////////////////////////////////////////////////////////////
Expand Down
Loading

0 comments on commit 481b165

Please # to comment.