未验证 提交 6c1b26f6 编写于 作者: B buyaa-n 提交者: GitHub

Libgpiod pinvoke driver Read/Write implementation (#219)

上级 f6867c36
cmake_minimum_required(VERSION 2.8.12)
project (IOT C)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_compile_options(-fPIC)
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
add_subdirectory(System.Device.Gpio.Native)
endif()
project (System.Device.Gpio.Native C)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_library(System.Device.Gpio.Native
SHARED
libgpiod.c
)
set_target_properties(System.Device.Gpio.Native PROPERTIES PREFIX "")
target_link_libraries(System.Device.Gpio.Native LINK_PUBLIC gpiod)
// 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.
#include <gpiod.h>
/**
* @brief Close a GPIO chip handle and release all allocated resources.
* @param chip The GPIO chip object.
*/
extern void CloseChip(struct gpiod_chip *chip)
{
gpiod_chip_close(chip);
}
/**
* @brief Get the number of GPIO lines exposed by this chip.
* @param chip The GPIO chip object.
* @return Number of GPIO lines.
*/
extern unsigned int GetNumberOfLines(struct gpiod_chip *chip)
{
return gpiod_chip_num_lines(chip);
}
/**
* @brief Get the handle to the GPIO line at given offset.
* @param chip The GPIO chip object.
* @param offset The offset of the GPIO line.
* @return Pointer to the GPIO line handle or NULL if an error occured.
*/
extern struct gpiod_line *GetChipLineByOffset(struct gpiod_chip *chip, unsigned int offset)
{
return gpiod_chip_get_line(chip, offset);
}
/**
* @brief Read the GPIO line direction setting.
* @param line GPIO line object.
* @return Returns GPIOD_DIRECTION_INPUT or GPIOD_DIRECTION_OUTPUT.
*/
extern int GetLineDirection(struct gpiod_line *line)
{
return gpiod_line_direction(line);
}
/**
* @brief Reserve a single line, set the direction to input.
* @param line GPIO line object.
* @param consumer Name of the consumer.
* @return 0 if the line was properly reserved, -1 on failure.
*/
extern int RequestLineInput(struct gpiod_line *line, const char *consumer)
{
return gpiod_line_request_input(line, consumer);
}
/**
* @brief Reserve a single line, set the direction to output.
* @param line GPIO line object.
* @param consumer Name of the consumer.
* @return 0 if the line was properly reserved, -1 on failure.
*/
extern int RequestLineOutput(struct gpiod_line *line, const char *consumer)
{
return gpiod_line_request_output(line, consumer, 0);
}
/**
* @brief Release a previously reserved line.
* @param line GPIO line object.
*/
extern void ReleaseGpiodLine(struct gpiod_line *line)
{
gpiod_line_release(line);
}
/**
* @brief Read current value of a single GPIO line.
* @param line GPIO line object.
* @return 0 or 1 if the operation succeeds. On error this routine returns -1
* and sets the last error number.
*/
extern int GetGpiodLineValue(struct gpiod_line *line)
{
return gpiod_line_get_value(line);
}
/**
* @brief Set the value of a single GPIO line.
* @param line GPIO line object.
* @param value New value.
* @return 0 is the operation succeeds. In case of an error this routine
* returns -1 and sets the last error number.
*/
extern int SetGpiodLineValue(struct gpiod_line *line, int value)
{
return gpiod_line_set_value(line, value);
}
/**
* @brief Create a new gpiochip iterator.
* @return Pointer to a new chip iterator object or NULL if an error occurred.
*/
extern struct gpiod_chip_iter * GetChipIterator(void)
{
return gpiod_chip_iter_new();
}
/**
* @brief Release all resources allocated for the gpiochip iterator and close
* the most recently opened gpiochip (if any).
* @param iter The gpiochip iterator object.
*/
extern void FreeChipIterator(struct gpiod_chip_iter *iter)
{
gpiod_chip_iter_free(iter);
}
/**
* @brief Release all resources allocated for the gpiochip iterator but
* don't close the most recently opened gpiochip (if any).
* @param iter The gpiochip iterator object.
*/
extern void FreeChipIteratorNoCloseCurrentChip(struct gpiod_chip_iter *iter)
{
gpiod_chip_iter_free_noclose(iter);
}
/**
* @brief Get the next gpiochip handle.
* @param iter The gpiochip iterator object.
* @return Pointer to the next open gpiochip handle or NULL if no more chips
* are present in the system.
* @note The previous chip handle will be closed.
*/
extern struct gpiod_chip * GetNextChipFromChipIterator(struct gpiod_chip_iter *iter)
{
return gpiod_chip_iter_next(iter);
}
// 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.Device.Gpio.Drivers;
namespace System.Device.Gpio.Tests
{
class LibGpiodDriverTests : GpioControllerTestBase
{
protected override GpioDriver GetTestDriver() => new LibGpiodDriver();
protected override PinNumberingScheme GetTestNumberingScheme() => PinNumberingScheme.Logical;
}
}
......@@ -6,10 +6,10 @@ using System.Device.Gpio.Drivers;
namespace System.Device.Gpio.Tests
{
public class UnixDriverTests : GpioControllerTestBase
public class SysfsDriverTests : GpioControllerTestBase
{
protected override GpioDriver GetTestDriver() => new UnixDriver();
protected override GpioDriver GetTestDriver() => new SysfsDriver();
protected override PinNumberingScheme GetTestNumberingScheme() => PinNumberingScheme.Logical;
}
}
\ No newline at end of file
}
// 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.InteropServices;
namespace System.Device.Gpio
{
/// <summary>
/// Pointer to a general-purpose I/O (GPIO) chip.
/// </summary>
internal class SafeChipHandle : SafeHandle
{
public SafeChipHandle() : base(IntPtr.Zero, true) { }
protected override bool ReleaseHandle()
{
Interop.CloseChip(handle);
return true;
}
public override bool IsInvalid => handle == IntPtr.Zero || handle == Interop.InvalidHandleValue;
}
}
// 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.InteropServices;
namespace System.Device.Gpio
{
/// <summary>
/// Pointer to an iterator of all GPIO chips available on the device.
/// </summary>
internal class SafeChipIteratorHandle : SafeHandle
{
public SafeChipIteratorHandle() : base(IntPtr.Zero, true) { }
protected override bool ReleaseHandle()
{
Interop.FreeChipIterator(handle);
return true;
}
public override bool IsInvalid => handle == IntPtr.Zero || handle == Interop.InvalidHandleValue;
}
}
// 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.InteropServices;
namespace System.Device.Gpio
{
/// <summary>
/// Pointer to a pin.
/// </summary>
internal class SafeLineHandle : SafeHandle
{
public SafeLineHandle() : base(IntPtr.Zero, false) { }
protected override bool ReleaseHandle() => true;
public override bool IsInvalid => handle == IntPtr.Zero || handle == Interop.InvalidHandleValue;
}
}
// 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;
using System.Device.Gpio;
using System.Runtime.InteropServices;
internal partial class Interop
{
private const string LibgpiodLibrary = "System.Device.Gpio.Native";
internal static IntPtr InvalidHandleValue = new IntPtr(-1);
/// <summary>
/// Create a new gpiochip iterator.
/// </summary>
/// <returns>Pointer to a new chip iterator object or <see langword="null" /> if an error occurred.</returns>
[DllImport(LibgpiodLibrary, SetLastError = true)]
internal static extern SafeChipIteratorHandle GetChipIterator();
/// <summary>
/// Release all resources allocated for the gpiochip iterator and close the most recently opened gpiochip(if any).
/// </summary>
/// <param name="iter">The gpiochip iterator object</param>
[DllImport(LibgpiodLibrary)]
internal static extern void FreeChipIterator(IntPtr iter);
/// <summary>
/// Release all resources allocated for the gpiochip iterator but don't close the most recently opened gpiochip (if any).
/// </summary>
/// <param name="iter">The gpiochip iterator object</param>
[DllImport(LibgpiodLibrary)]
internal static extern void FreeChipIteratorNoCloseCurrentChip(SafeChipIteratorHandle iter);
/// <summary>
/// Get the next gpiochip handle.
/// </summary>
/// <param name="iter">The gpiochip iterator object</param>
[DllImport(LibgpiodLibrary, SetLastError = true)]
internal static extern SafeChipHandle GetNextChipFromChipIterator(SafeChipIteratorHandle iter);
/// <summary>
/// Close a GPIO chip handle and release all allocated resources.
/// </summary>
/// <param name="chip">The GPIO chip pointer</param>
[DllImport(LibgpiodLibrary)]
internal static extern void CloseChip(IntPtr chip);
/// <summary>
/// Get the number of GPIO lines exposed by this chip.
/// </summary>
/// <param name="chip">The GPIO chip handle.</param>
/// <returns>Number of GPIO lines.</returns>
[DllImport(LibgpiodLibrary)]
internal static extern int GetNumberOfLines(SafeChipHandle chip);
/// <summary>
/// Get the handle to the GPIO line at given offset.
/// </summary>
/// <param name="chip">The GPIO chip handle</param>
/// <param name="offset">The offset of the GPIO line</param>
/// <returns>Handle to the GPIO line or <see langword="null" /> if an error occured.</returns>
[DllImport(LibgpiodLibrary, SetLastError = true)]
internal static extern SafeLineHandle GetChipLineByOffset(SafeChipHandle chip, int offset);
/// <summary>
/// Read the GPIO line direction setting.
/// </summary>
/// <param name="line">GPIO line handle</param>
/// <returns>GPIOD_DIRECTION_INPUT or GPIOD_DIRECTION_OUTPUT.</returns>
[DllImport(LibgpiodLibrary)]
internal static extern int GetLineDirection(SafeLineHandle line);
/// <summary>
/// Reserve a single line, set the direction to input.
/// </summary>
/// <param name="line">GPIO line handle</param>
/// <param name="consumer">Name of the consumer.</param>
/// <returns>0 if the line was properly reserved, -1 on failure.</returns>
[DllImport(LibgpiodLibrary)]
internal static extern int RequestLineInput(SafeLineHandle line, string consumer);
/// <summary>
/// Reserve a single line, set the direction to output.
/// </summary>
/// <param name="line">GPIO line handle</param>
/// <param name="consumer">Name of the consumer.</param>
/// <returns>0 if the line was properly reserved, -1 on failure.</returns>
[DllImport(LibgpiodLibrary)]
internal static extern int RequestLineOutput(SafeLineHandle line, string consumer);
/// <summary>
/// Set the value of a single GPIO line.
/// </summary>
/// <param name="line">GPIO line handle</param>
/// <param name="value">New value.</param>
/// <returns>0 if the operation succeeds. In case of an error this routine returns -1 and sets the last error number.</returns>
[DllImport(LibgpiodLibrary)]
internal static extern int SetGpiodLineValue(SafeLineHandle line, int value);
/// <summary>
/// Read current value of a single GPIO line.
/// </summary>
/// <param name="line">GPIO line handle</param>
/// <returns>0 or 1 if the operation succeeds. On error this routine returns -1 and sets the last error number.</returns>
[DllImport(LibgpiodLibrary, SetLastError = true)]
internal static extern int GetGpiodLineValue(SafeLineHandle line);
/// <summary>
/// Release a previously reserved line.
/// </summary>
/// <param name="line">GPIO line handle</param>
[DllImport(LibgpiodLibrary)]
internal static extern void ReleaseGpiodLine(SafeLineHandle lineHandle);
}
// 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.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;
namespace System.Device.Gpio.Drivers
{
public class LibGpiodDriver : UnixDriver
{
private SafeChipHandle _chip;
private Dictionary<int, SafeLineHandle> _pinNumberToSafeLineHandle;
protected internal override int PinCount => Interop.GetNumberOfLines(_chip);
public LibGpiodDriver()
{
SafeChipIteratorHandle iterator = Interop.GetChipIterator();
if (iterator == null)
{
throw ExceptionHelper.GetIOException(ExceptionResource.NoChipIteratorFound, Marshal.GetLastWin32Error());
}
_chip = Interop.GetNextChipFromChipIterator(iterator);
if (_chip == null)
{
throw ExceptionHelper.GetIOException(ExceptionResource.NoChipFound, Marshal.GetLastWin32Error());
}
// Freeing other chips opened
Interop.FreeChipIteratorNoCloseCurrentChip(iterator);
_pinNumberToSafeLineHandle = new Dictionary<int, SafeLineHandle>(PinCount);
}
protected internal override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventType, PinChangeEventHandler callback)
{
throw new NotImplementedException();
}
protected internal override void ClosePin(int pinNumber)
{
if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle))
{
Interop.ReleaseGpiodLine(pinHandle);
_pinNumberToSafeLineHandle.Remove(pinNumber);
}
}
protected internal override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber) =>
throw ExceptionHelper.GetPlatformNotSupportedException(ExceptionResource.ConvertPinNumberingSchemaError);
protected internal override PinMode GetPinMode(int pinNumber)
{
if (!_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle))
{
throw ExceptionHelper.GetInvalidOperationException(ExceptionResource.PinNotOpenedError, pin: pinNumber);
}
return (Interop.GetLineDirection(pinHandle) == 1) ? PinMode.Input : PinMode.Output;
}
protected internal override bool IsPinModeSupported(int pinNumber, PinMode mode)
{
// Libgpiod Api do not support pull up or pull down resistors for now.
return mode != PinMode.InputPullDown && mode != PinMode.InputPullUp;
}
protected internal override void OpenPin(int pinNumber)
{
SafeLineHandle pinHandle = Interop.GetChipLineByOffset(_chip, pinNumber);
if (pinHandle == null)
{
throw ExceptionHelper.GetIOException(ExceptionResource.OpenPinError, Marshal.GetLastWin32Error());
}
_pinNumberToSafeLineHandle.Add(pinNumber, pinHandle);
}
protected internal override PinValue Read(int pinNumber)
{
if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle))
{
int result = Interop.GetGpiodLineValue(pinHandle);
if (result == -1)
{
throw ExceptionHelper.GetIOException(ExceptionResource.ReadPinError, Marshal.GetLastWin32Error(), pinNumber);
}
return result;
}
throw ExceptionHelper.GetInvalidOperationException(ExceptionResource.PinNotOpenedError, pin: pinNumber);
}
protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback)
{
throw new NotImplementedException();
}
protected internal override void SetPinMode(int pinNumber, PinMode mode)
{
int requestResult = -1;
if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle))
{
string consumer = pinNumber.ToString();
if (mode == PinMode.Input)
{
requestResult = Interop.RequestLineInput(pinHandle, consumer);
}
else
{
requestResult = Interop.RequestLineOutput(pinHandle, consumer);
}
}
if (requestResult == -1)
{
throw ExceptionHelper.GetIOException(ExceptionResource.SetPinModeError, Marshal.GetLastWin32Error(), pinNumber);
}
}
protected internal override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventType, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
protected internal override void Write(int pinNumber, PinValue value)
{
if (!_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle))
{
throw ExceptionHelper.GetInvalidOperationException(ExceptionResource.PinNotOpenedError, pin: pinNumber);
}
Interop.SetGpiodLineValue(pinHandle, (value == PinValue.High) ? 1 : 0);
}
protected override void Dispose(bool disposing)
{
foreach (int pin in _pinNumberToSafeLineHandle.Keys)
{
if (_pinNumberToSafeLineHandle.TryGetValue(pin, out SafeLineHandle pinHandle))
{
Interop.ReleaseGpiodLine(pinHandle);
}
_pinNumberToSafeLineHandle.Remove(pin);
}
if (_chip != null)
{
_chip.Dispose();
_chip = null;
}
base.Dispose(disposing);
}
}
}
......@@ -324,7 +324,7 @@ namespace System.Device.Gpio.Drivers
{
return;
}
_sysFSDriver = new UnixDriver();
_sysFSDriver = new SysfsDriver();
}
}
......
// 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.Diagnostics;
using System.IO;
namespace System.Device.Gpio
{
internal static class ExceptionHelper
{
public static IOException GetIOException(ExceptionResource resource, int errorCode = -1, int pin = -1)
{
return new IOException(GetResourceString(resource, errorCode, pin));
}
public static InvalidOperationException GetInvalidOperationException(ExceptionResource resource, int pin = -1)
{
return new InvalidOperationException(GetResourceString(resource, -1, pin));
}
public static PlatformNotSupportedException GetPlatformNotSupportedException(ExceptionResource resource)
{
return new PlatformNotSupportedException(GetResourceString(resource, -1, -1));
}
private static string GetResourceString(ExceptionResource resource, int errorCode, int pin)
{
string message = "";
switch (resource)
{
case ExceptionResource.NoChipIteratorFound:
message = $"Unable to find a chip iterator, error code: {errorCode}";
break;
case ExceptionResource.NoChipFound:
message = $"Unable to find a chip, error code: {errorCode}";
break;
case ExceptionResource.PinModeReadError:
message = $"Error while reading pin mode for pin: {pin}";
break;
case ExceptionResource.OpenPinError:
message = $"Pin {pin} not available, error: {errorCode}";
break;
case ExceptionResource.ReadPinError:
message = $"Error while reading value from pin: {pin}, error: {errorCode}";
break;
case ExceptionResource.PinNotOpenedError:
message = $"Can not access pin {pin} that is not open.";
break;
case ExceptionResource.SetPinModeError:
message = $"Error setting pin mode, for pin: {pin}, error: {errorCode}";
break;
case ExceptionResource.ConvertPinNumberingSchemaError:
message = $"This driver is generic so it cannot perform conversions between pin numbering schemes.";
break;
default:
Debug.Fail($"The ExceptionResource enum value: {resource} is not part of the switch. Add the appropriate case and exception message.");
break;
}
return message;
}
}
internal enum ExceptionResource
{
NoChipIteratorFound,
NoChipFound,
PinModeReadError,
OpenPinError,
ReadPinError,
PinNotOpenedError,
SetPinModeError,
ConvertPinNumberingSchemaError
}
}
......@@ -49,11 +49,11 @@ namespace System.Device.Gpio
//{
// return new HummingBoardDriver();
//}
return new UnixDriver();
return UnixDriver.Create();
}
}
}
return new UnixDriver();
return UnixDriver.Create();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册