// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace System.Device.Gpio.Drivers
{
///
/// A GPIO driver for the Raspberry Pi 3 or 4, running Raspbian (or, with some limitations, ubuntu)
///
public class RaspberryPi3Driver : GpioDriver
{
private GpioDriver _internalDriver;
/* private delegates for register Properties */
private delegate void Set_Register(ulong value);
private delegate ulong Get_Register();
private readonly Set_Register _setSetRegister;
private readonly Get_Register _getSetRegister;
private readonly Set_Register _setClearRegister;
private readonly Get_Register _getClearRegister;
///
/// Creates an instance of the RaspberryPi3Driver.
/// This driver works on Raspberry 3 or 4, both on Linux and on Windows
///
public RaspberryPi3Driver()
{
if (Environment.OSVersion.Platform == PlatformID.Unix)
{
RaspberryPi3LinuxDriver? linuxDriver = CreateInternalRaspberryPi3LinuxDriver(out RaspberryBoardInfo boardInfo);
if (linuxDriver == null)
{
throw new PlatformNotSupportedException($"Not a supported Raspberry Pi type: " + boardInfo.BoardModel);
}
_setSetRegister = (value) => linuxDriver.SetRegister = value;
_setClearRegister = (value) => linuxDriver.ClearRegister = value;
_getSetRegister = () => linuxDriver.SetRegister;
_getClearRegister = () => linuxDriver.ClearRegister;
_internalDriver = linuxDriver;
}
else
{
_internalDriver = CreateWindows10GpioDriver();
_setSetRegister = (value) => throw new PlatformNotSupportedException();
_setClearRegister = (value) => throw new PlatformNotSupportedException();
_getSetRegister = () => throw new PlatformNotSupportedException();
_getClearRegister = () => throw new PlatformNotSupportedException();
}
}
internal RaspberryPi3Driver(RaspberryPi3LinuxDriver linuxDriver)
{
if (Environment.OSVersion.Platform == PlatformID.Unix)
{
_setSetRegister = (value) => linuxDriver.SetRegister = value;
_setClearRegister = (value) => linuxDriver.ClearRegister = value;
_getSetRegister = () => linuxDriver.SetRegister;
_getClearRegister = () => linuxDriver.ClearRegister;
_internalDriver = linuxDriver;
}
else
{
throw new NotSupportedException("This ctor is for internal use only");
}
}
internal static RaspberryPi3LinuxDriver? CreateInternalRaspberryPi3LinuxDriver(out RaspberryBoardInfo boardInfo)
{
RaspberryBoardInfo identification = RaspberryBoardInfo.LoadBoardInfo();
RaspberryPi3LinuxDriver? linuxDriver;
boardInfo = identification;
switch (identification.BoardModel)
{
case RaspberryBoardInfo.Model.RaspberryPi3B:
case RaspberryBoardInfo.Model.RaspberryPi3BPlus:
case RaspberryBoardInfo.Model.RaspberryPi4:
linuxDriver = new RaspberryPi3LinuxDriver();
break;
case RaspberryBoardInfo.Model.RaspberryPiComputeModule3:
linuxDriver = new RaspberryPiCm3Driver();
break;
default:
linuxDriver = null;
break;
}
return linuxDriver;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static GpioDriver CreateWindows10GpioDriver()
{
// This wrapper is needed to prevent Mono from loading Windows10Driver
// which causes all fields to be loaded - one of such fields is WinRT type which does not
// exist on Linux which causes TypeLoadException.
// Using NoInlining and no explicit type prevents this from happening.
return new Windows10Driver();
}
///
protected internal override int PinCount => _internalDriver.PinCount;
///
protected internal override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback) => _internalDriver.AddCallbackForPinValueChangedEvent(pinNumber, eventTypes, callback);
///
protected internal override void ClosePin(int pinNumber) => _internalDriver.ClosePin(pinNumber);
///
protected internal override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber)
{
return _internalDriver.ConvertPinNumberToLogicalNumberingScheme(pinNumber);
}
///
protected internal override PinMode GetPinMode(int pinNumber) => _internalDriver.GetPinMode(pinNumber);
///
protected internal override bool IsPinModeSupported(int pinNumber, PinMode mode) => _internalDriver.IsPinModeSupported(pinNumber, mode);
///
protected internal override void OpenPin(int pinNumber) => _internalDriver.OpenPin(pinNumber);
///
protected internal override PinValue Read(int pinNumber) => _internalDriver.Read(pinNumber);
///
protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) => _internalDriver.RemoveCallbackForPinValueChangedEvent(pinNumber, callback);
///
protected internal override void SetPinMode(int pinNumber, PinMode mode) => _internalDriver.SetPinMode(pinNumber, mode);
///
protected internal override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken) => _internalDriver.WaitForEvent(pinNumber, eventTypes, cancellationToken);
///
protected internal override ValueTask WaitForEventAsync(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken) => _internalDriver.WaitForEventAsync(pinNumber, eventTypes, cancellationToken);
///
protected internal override void Write(int pinNumber, PinValue value) => _internalDriver.Write(pinNumber, value);
///
/// Allows directly setting the "Set pin high" register. Used for special applications only
///
protected ulong SetRegister
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getSetRegister();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => _setSetRegister(value);
}
///
/// Allows directly setting the "Set pin low" register. Used for special applications only
///
protected ulong ClearRegister
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getClearRegister();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => _setClearRegister(value);
}
///
protected override void Dispose(bool disposing)
{
_internalDriver?.Dispose();
_internalDriver = null!;
base.Dispose(disposing);
}
}
}