Skip to content

Commit

Permalink
Cleanup for SPI drivers (#144)
Browse files Browse the repository at this point in the history
* Added editconfig file

* Cleanup for SPI drivers

* Updated for feedback
  • Loading branch information
shaggygi authored and joperezr committed Jan 16, 2019
1 parent 103e45e commit 2bf78c2
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 17 deletions.
Expand Up @@ -6,22 +6,37 @@

namespace System.Device.Spi.Drivers
{
/// <summary>
/// Represents an SPI communication channel running on Unix.
/// </summary>
public class UnixSpiDevice : SpiDevice
{
private const string Default_Device_Path = "/dev/spidev";
private const string DefaultDevicePath = "/dev/spidev";
private const uint SPI_IOC_MESSAGE_1 = 0x40206b00;
private int _deviceFileDescriptor = -1;
private readonly SpiConnectionSettings _settings;
private static readonly object s_InitializationLock = new object();

private static readonly object s_initializationLock = new object();

/// <summary>
/// Initializes new instance of UnixSpiDevice that will use the specified settings to communicate with the SPI device.
/// </summary>
/// <param name="settings">
/// The connection settings of a device on a SPI bus.
/// </param>
public UnixSpiDevice(SpiConnectionSettings settings)
{
_settings = settings;
DevicePath = Default_Device_Path;
DevicePath = DefaultDevicePath;
}

/// <summary>
/// Path to SPI resources located on the platform.
/// </summary>
public string DevicePath { get; set; }

/// <summary>
/// The connection settings of a device on a SPI bus.
/// </summary>
public override SpiConnectionSettings ConnectionSettings => _settings;

private unsafe void Initialize()
Expand All @@ -30,7 +45,7 @@ private unsafe void Initialize()
{
return;
}
lock (s_InitializationLock)
lock (s_initializationLock)
{
string deviceFileName = $"{DevicePath}{_settings.BusId}.{_settings.ChipSelectLine}";
if (_deviceFileDescriptor >= 0)
Expand All @@ -40,7 +55,7 @@ private unsafe void Initialize()
_deviceFileDescriptor = Interop.open(deviceFileName, FileOpenFlags.O_RDWR);
if (_deviceFileDescriptor < 0)
{
throw new IOException($"Cannot open Spi device file '{deviceFileName}'");
throw new IOException($"Can not open SPI device file '{deviceFileName}'.");
}

UnixSpiMode mode = SpiModeToUnixSpiMode(_settings.Mode);
Expand All @@ -49,7 +64,7 @@ private unsafe void Initialize()
int result = Interop.ioctl(_deviceFileDescriptor, (uint)SpiSettings.SPI_IOC_WR_MODE, nativePtr);
if (result == -1)
{
throw new IOException($"Cannot set Spi mode to {_settings.Mode}");
throw new IOException($"Can not set SPI mode to {_settings.Mode}.");
}

byte dataLengthInBits = (byte)_settings.DataBitLength;
Expand All @@ -58,7 +73,7 @@ private unsafe void Initialize()
result = Interop.ioctl(_deviceFileDescriptor, (uint)SpiSettings.SPI_IOC_WR_BITS_PER_WORD, nativePtr);
if (result == -1)
{
throw new IOException($"Cannot set Spi data bit length to {_settings.DataBitLength}");
throw new IOException($"Can not set SPI data bit length to {_settings.DataBitLength}.");
}

int clockFrequency = _settings.ClockFrequency;
Expand All @@ -67,7 +82,7 @@ private unsafe void Initialize()
result = Interop.ioctl(_deviceFileDescriptor, (uint)SpiSettings.SPI_IOC_WR_MAX_SPEED_HZ, nativePtr);
if (result == -1)
{
throw new IOException($"Cannot set Spi clock frequency to {_settings.ClockFrequency}");
throw new IOException($"Can not set SPI clock frequency to {_settings.ClockFrequency}.");
}
}
}
Expand All @@ -85,10 +100,14 @@ private UnixSpiMode SpiModeToUnixSpiMode(SpiMode mode)
case SpiMode.Mode3:
return UnixSpiMode.SPI_MODE_3;
default:
throw new ArgumentException("Invalid SpiMode", nameof(mode));
throw new ArgumentException("Invalid SPI mode.", nameof(mode));
}
}

/// <summary>
/// Reads a byte from the SPI device.
/// </summary>
/// <returns>A byte read from the SPI device.</returns>
public override unsafe byte ReadByte()
{
Initialize();
Expand All @@ -100,6 +119,13 @@ public override unsafe byte ReadByte()
return result;
}

/// <summary>
/// Reads data from the SPI device.
/// </summary>
/// <param name="buffer">
/// The buffer to read the data from the SPI device.
/// The length of the buffer determines how much data to read from the SPI device.
/// </param>
public override unsafe void Read(Span<byte> buffer)
{
Initialize();
Expand All @@ -110,6 +136,10 @@ public override unsafe void Read(Span<byte> buffer)
}
}

/// <summary>
/// Writes a byte to the SPI device.
/// </summary>
/// <param name="data">The byte to be written to the SPI device.</param>
public override unsafe void WriteByte(byte data)
{
Initialize();
Expand All @@ -118,6 +148,12 @@ public override unsafe void WriteByte(byte data)
Transfer(&data, null, length);
}

/// <summary>
/// Writes data to the SPI device.
/// </summary>
/// <param name="data">
/// The buffer that contains the data to be written to the SPI device.
/// </param>
public override unsafe void Write(Span<byte> data)
{
Initialize();
Expand All @@ -127,19 +163,27 @@ public override unsafe void Write(Span<byte> data)
Transfer(dataPtr, null, data.Length);
}
}

/// <summary>
/// Writes and reads data from the SPI device.
/// </summary>
/// <param name="writeBuffer">The buffer that contains the data to be written to the SPI device.</param>
/// <param name="readBuffer">The buffer to read the data from the SPI device.</param>
public override unsafe void TransferFullDuplex(Span<byte> writeBuffer, Span<byte> readBuffer)
{
Initialize();

if (writeBuffer.Length != readBuffer.Length)
{
throw new ArgumentException($"Parameters '{nameof(writeBuffer)}' and '{nameof(readBuffer)}' must have the same length");
throw new ArgumentException($"Parameters '{nameof(writeBuffer)}' and '{nameof(readBuffer)}' must have the same length.");
}

fixed (byte* writeBufferPtr = writeBuffer)
fixed (byte* readBufferPtr = readBuffer)
{
Transfer(writeBufferPtr, readBufferPtr, writeBuffer.Length);
fixed (byte* readBufferPtr = readBuffer)
{
Transfer(writeBufferPtr, readBufferPtr, writeBuffer.Length);
}
}
}

Expand All @@ -158,7 +202,7 @@ private unsafe void Transfer(byte* writeBufferPtr, byte* readBufferPtr, int buff
int result = Interop.ioctl(_deviceFileDescriptor, SPI_IOC_MESSAGE_1, new IntPtr(&tr));
if (result < 1)
{
throw new IOException("Error while performing the Spi data transfer.");
throw new IOException("Error performing SPI data transfer.");
}
}

Expand Down
Expand Up @@ -7,11 +7,20 @@

namespace System.Device.Spi.Drivers
{
/// <summary>
/// Represents an SPI communication channel running on Windows 10 IoT.
/// </summary>
public class Windows10SpiDevice : SpiDevice
{
private readonly SpiConnectionSettings _settings;
private WinSpi.SpiDevice _winDevice;

/// <summary>
/// Initializes new instance of Windows10SpiDevice that will use the specified settings to communicate with the SPI device.
/// </summary>
/// <param name="settings">
/// The connection settings of a device on a SPI bus.
/// </param>
public Windows10SpiDevice(SpiConnectionSettings settings)
{
_settings = settings;
Expand All @@ -28,43 +37,72 @@ public Windows10SpiDevice(SpiConnectionSettings settings)
DeviceInformationCollection deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion();
if (deviceInformationCollection.Count == 0)
{
throw new ArgumentException($"No SPI device exists for BusId {settings.BusId}", $"{nameof(settings)}.{nameof(settings.BusId)}");
throw new ArgumentException($"No SPI device exists for bus ID {settings.BusId}.", $"{nameof(settings)}.{nameof(settings.BusId)}");
}

_winDevice = WinSpi.SpiDevice.FromIdAsync(deviceInformationCollection[0].Id, winSettings).WaitForCompletion();
}

/// <summary>
/// The connection settings of a device on a SPI bus.
/// </summary>
public override SpiConnectionSettings ConnectionSettings => _settings;

/// <summary>
/// Reads a byte from the SPI device.
/// </summary>
/// <returns>A byte read from the SPI device.</returns>
public override byte ReadByte()
{
byte[] buffer = new byte[1];
_winDevice.Read(buffer);
return buffer[0];
}

/// <summary>
/// Reads data from the SPI device.
/// </summary>
/// <param name="buffer">
/// The buffer to read the data from the SPI device.
/// The length of the buffer determines how much data to read from the SPI device.
/// </param>
public override void Read(Span<byte> buffer)
{
byte[] byteArray = new byte[buffer.Length];
_winDevice.Read(byteArray);
new Span<byte>(byteArray).CopyTo(buffer);
}

/// <summary>
/// Writes a byte to the SPI device.
/// </summary>
/// <param name="data">The byte to be written to the SPI device.</param>
public override void WriteByte(byte data)
{
_winDevice.Write(new[] { data });
}

/// <summary>
/// Writes data to the SPI device.
/// </summary>
/// <param name="data">
/// The buffer that contains the data to be written to the SPI device.
/// </param>
public override void Write(Span<byte> data)
{
_winDevice.Write(data.ToArray());
}

/// <summary>
/// Writes and reads data from the SPI device.
/// </summary>
/// <param name="writeBuffer">The buffer that contains the data to be written to the SPI device.</param>
/// <param name="readBuffer">The buffer to read the data from the SPI device.</param>
public override void TransferFullDuplex(Span<byte> writeBuffer, Span<byte> readBuffer)
{
if (writeBuffer.Length != readBuffer.Length)
{
throw new ArgumentException($"Parameters '{nameof(writeBuffer)}' and '{nameof(readBuffer)}' must have the same length");
throw new ArgumentException($"Parameters '{nameof(writeBuffer)}' and '{nameof(readBuffer)}' must have the same length.");
}
byte[] byteArray = new byte[readBuffer.Length];
_winDevice.TransferFullDuplex(writeBuffer.ToArray(), byteArray);
Expand All @@ -91,7 +129,7 @@ private static WinSpi.SpiMode ToWinMode(SpiMode mode)
case SpiMode.Mode3:
return WinSpi.SpiMode.Mode3;
default:
throw new ArgumentException($"SPI mode not supported: {mode}", nameof(mode));
throw new ArgumentException($"SPI mode {mode} not supported.", nameof(mode));
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions src/System.Device.Gpio/System/Device/Spi/SpiDevice.cs
Expand Up @@ -4,13 +4,50 @@

namespace System.Device.Spi
{
/// <summary>
/// The communications channel to a device on a SPI bus.
/// </summary>
public abstract class SpiDevice : IDisposable
{
/// <summary>
/// The connection settings of a device on a SPI bus.
/// </summary>
public abstract SpiConnectionSettings ConnectionSettings { get; }

/// <summary>
/// Reads a byte from the SPI device.
/// </summary>
/// <returns>A byte read from the SPI device.</returns>
public abstract byte ReadByte();

/// <summary>
/// Reads data from the SPI device.
/// </summary>
/// <param name="buffer">
/// The buffer to read the data from the SPI device.
/// The length of the buffer determines how much data to read from the SPI device.
/// </param>
public abstract void Read(Span<byte> buffer);

/// <summary>
/// Writes a byte to the SPI device.
/// </summary>
/// <param name="data">The byte to be written to the SPI device.</param>
public abstract void WriteByte(byte data);

/// <summary>
/// Writes data to the SPI device.
/// </summary>
/// <param name="data">
/// The buffer that contains the data to be written to the SPI device.
/// </param>
public abstract void Write(Span<byte> data);

/// <summary>
/// Writes and reads data from the SPI device.
/// </summary>
/// <param name="writeBuffer">The buffer that contains the data to be written to the SPI device.</param>
/// <param name="readBuffer">The buffer to read the data from the SPI device.</param>
public abstract void TransferFullDuplex(Span<byte> writeBuffer, Span<byte> readBuffer);

public void Dispose()
Expand Down

0 comments on commit 2bf78c2

Please sign in to comment.