diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props index 7171f9c2e84fddde03d53c1e8ea79a07dca7fbaa..aa3fc7671b8cd3ebe1ec5d1c8a23a2b988385df6 100644 --- a/samples/Directory.Build.props +++ b/samples/Directory.Build.props @@ -10,6 +10,7 @@ $(NoWarn);CS8321;CS1591 false false + enable $(MSBuildThisFileDirectory)../src/System.Device.Gpio/ $(MSBuildThisFileDirectory)../src/Iot.Device.Bindings/ diff --git a/samples/led-matrix-weather/Program.cs b/samples/led-matrix-weather/Program.cs index de4107e4525e949f01bafd48de3d65acdfc18518..5874a28ce55486ae2ff2bcec0720bfc6d182bbbc 100644 --- a/samples/led-matrix-weather/Program.cs +++ b/samples/led-matrix-weather/Program.cs @@ -20,11 +20,11 @@ namespace LedMatrixWeather { internal partial class Program { - private static Action s_scenario = WeatherDemo; - private static Stopwatch s_showLocalIp = null; - private static string[] s_ips; + private static Action? s_scenario = WeatherDemo; + private static Stopwatch? s_showLocalIp = null; + private static string[]? s_ips; private static bool s_networkAvailable = false; - private static Weather s_client; + private static Weather? s_client; private static OpenWeatherResponse s_weatherResponse; private static readonly TimeZoneInfo s_timeZonePst = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles"); @@ -55,17 +55,14 @@ namespace LedMatrixWeather { matrix.StartRendering(); - while (true) + while (s_scenario is object) { Action scenario = s_scenario; - if (scenario == null) - break; - Stopwatch sw = Stopwatch.StartNew(); scenario(matrix); - if (s_scenario != null && sw.ElapsedMilliseconds < 100) + if (sw.ElapsedMilliseconds < 100) { Debug.WriteLine("Scenario execution finished in less than 100ms. This is likely due to bug."); } @@ -76,7 +73,7 @@ namespace LedMatrixWeather { if (!Console.IsOutputRedirected) { - while (s_scenario != null) + while (s_scenario is object) { switch (Console.ReadKey(intercept: true).Key) { @@ -127,7 +124,7 @@ namespace LedMatrixWeather { try { - s_weatherResponse = s_client.GetWeatherFromOpenWeather(); + s_weatherResponse = s_client!.GetWeatherFromOpenWeather(); s_networkAvailable = true; } catch (Exception e) @@ -212,7 +209,7 @@ namespace LedMatrixWeather return (byte)Math.Clamp(x * 255, 0, 255); } - string text = null; + string text = string.Empty; int fullTextWidth = 0; Stopwatch sw = Stopwatch.StartNew(); diff --git a/samples/led-more-blinking-lights/Program.cs b/samples/led-more-blinking-lights/Program.cs index 4582c27be2bef2a229fd251c8810e4602088bc1d..95648b064d5cf79125275b196f06024043854449 100644 --- a/samples/led-more-blinking-lights/Program.cs +++ b/samples/led-more-blinking-lights/Program.cs @@ -25,7 +25,7 @@ namespace led_more_blinking_lights // volume support var initialSleep = 100; var sleep = initialSleep; - Volume volume = null; + Volume? volume = null; // this line should only be enabled if a trimpot is connected volume = Volume.EnableVolume(); @@ -87,7 +87,7 @@ namespace led_more_blinking_lights } // behavior for buttonOne - if (volume != null) + if (volume is object) { var update = true; var value = 0; diff --git a/samples/samples.sln b/samples/samples.sln new file mode 100644 index 0000000000000000000000000000000000000000..db0204570b0b9946bf242f0fda956f20eb1c8acc --- /dev/null +++ b/samples/samples.sln @@ -0,0 +1,102 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "force-sensitive-resistor", "force-sensitive-resistor\force-sensitive-resistor.csproj", "{61530E24-4BF0-4FE0-9523-F283AD529E7B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "led-blink", "led-blink\led-blink.csproj", "{3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "led-matrix-weather", "led-matrix-weather\led-matrix-weather.csproj", "{C36FF5D4-0159-4588-8384-B23F07DD1827}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "led-more-blinking-lights", "led-more-blinking-lights\led-more-blinking-lights.csproj", "{6B3DB7E2-FDFA-4429-936F-1C0854388C9E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "serialport_arduino", "serialport-arduino\serialport_arduino.csproj", "{5B81A152-AA24-4CFE-B774-CA034F09F749}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Debug|x64.ActiveCfg = Debug|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Debug|x64.Build.0 = Debug|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Debug|x86.ActiveCfg = Debug|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Debug|x86.Build.0 = Debug|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Release|Any CPU.Build.0 = Release|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Release|x64.ActiveCfg = Release|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Release|x64.Build.0 = Release|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Release|x86.ActiveCfg = Release|Any CPU + {61530E24-4BF0-4FE0-9523-F283AD529E7B}.Release|x86.Build.0 = Release|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Debug|x64.ActiveCfg = Debug|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Debug|x64.Build.0 = Debug|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Debug|x86.ActiveCfg = Debug|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Debug|x86.Build.0 = Debug|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Release|Any CPU.Build.0 = Release|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Release|x64.ActiveCfg = Release|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Release|x64.Build.0 = Release|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Release|x86.ActiveCfg = Release|Any CPU + {3151F9FD-175A-4CFE-B8B7-51A4B02EB4E9}.Release|x86.Build.0 = Release|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Debug|x64.ActiveCfg = Debug|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Debug|x64.Build.0 = Debug|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Debug|x86.ActiveCfg = Debug|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Debug|x86.Build.0 = Debug|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Release|Any CPU.Build.0 = Release|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Release|x64.ActiveCfg = Release|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Release|x64.Build.0 = Release|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Release|x86.ActiveCfg = Release|Any CPU + {C36FF5D4-0159-4588-8384-B23F07DD1827}.Release|x86.Build.0 = Release|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Debug|x64.Build.0 = Debug|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Debug|x86.ActiveCfg = Debug|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Debug|x86.Build.0 = Debug|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Release|Any CPU.Build.0 = Release|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Release|x64.ActiveCfg = Release|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Release|x64.Build.0 = Release|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Release|x86.ActiveCfg = Release|Any CPU + {6B3DB7E2-FDFA-4429-936F-1C0854388C9E}.Release|x86.Build.0 = Release|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Debug|x64.Build.0 = Debug|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Debug|x86.ActiveCfg = Debug|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Debug|x86.Build.0 = Debug|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Release|Any CPU.Build.0 = Release|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Release|x64.ActiveCfg = Release|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Release|x64.Build.0 = Release|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Release|x86.ActiveCfg = Release|Any CPU + {9B019F9E-0645-43F5-9AE1-F468B02E6061}.Release|x86.Build.0 = Release|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Debug|x64.Build.0 = Debug|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Debug|x86.Build.0 = Debug|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Release|Any CPU.Build.0 = Release|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Release|x64.ActiveCfg = Release|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Release|x64.Build.0 = Release|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Release|x86.ActiveCfg = Release|Any CPU + {5B81A152-AA24-4CFE-B774-CA034F09F749}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/samples/serialport-arduino/arduino-demo.csproj b/samples/serialport-arduino/serialport_arduino.csproj similarity index 96% rename from samples/serialport-arduino/arduino-demo.csproj rename to samples/serialport-arduino/serialport_arduino.csproj index 8163e9a84f2a6b98ef5920281128dfa79b8040d3..f6eeda0c985be5d7a15948eaaa9c9116d1f46a9a 100644 --- a/samples/serialport-arduino/arduino-demo.csproj +++ b/samples/serialport-arduino/serialport_arduino.csproj @@ -1,15 +1,15 @@ - - - - Exe - netcoreapp3.1 - arduino_demo - linux-arm - - - - - - - - + + + + Exe + netcoreapp3.1 + arduino_demo + linux-arm + + + + + + + + diff --git a/src/System.Device.Gpio/Directory.Build.props b/src/System.Device.Gpio/Directory.Build.props index 334471a4110b0513c8740578273334276d2cfaeb..544b85c1ac366b6403c1d399cc96849e79444176 100644 --- a/src/System.Device.Gpio/Directory.Build.props +++ b/src/System.Device.Gpio/Directory.Build.props @@ -6,6 +6,7 @@ The System.Device.Gpio package supports general-purpose I/O (GPIO) pins, PWM, I2C, SPI and related interfaces for interacting with low level hardware pins to control hardware sensors, displays and input devices on single-board-computers; Raspberry Pi, BeagleBoard, HummingBoard, ODROID, and other single-board-computers that are supported by Linux and Windows 10 IoT Core OS can be used with .NET Core and System.Device.Gpio. On Windows 10 IoT Core OS, the library wraps the Windows.Devices.Gpio.dll assembly. On Linux, the library supports three driver modes: libgpiod for fast full-featured GPIO access on all Linux distros since version 4.8 of the Linux kernel; slower and limited-functionality GPIO access via the deprecated Sysfs interface (/sys/class/gpio) when running on older Linux distro versions with a Linux kernel older than version 4.8; and lastly board-specific Linux drivers that access GPIO addresses in /dev/mem for fasted performance at the trade-off of being able to run on very specific versions of single-board-computers. In the future, the board-specific Linux drivers may be removed in favor of only supporting libgpiod and sysfs Linux interfaces. In addition to System.Device.Gpio, the optional IoT.Device.Bindings NuGet package contains device bindings for many sensors, displays, and input devices that can be used with System.Device.Gpio. .NET Core GPIO Pins SPI I2C PWM BCM2835 RPi IoT + enable diff --git a/src/System.Device.Gpio/Interop/Windows/WinRT/Interop.WaitForCompletion.cs b/src/System.Device.Gpio/Interop/Windows/WinRT/Interop.WaitForCompletion.cs index 3d67add1f1ec2e7c86b9f33db014462d42b53d3a..e9cd5b003f2e9ab5e501c3070f8cd462e3ac09d1 100644 --- a/src/System.Device.Gpio/Interop/Windows/WinRT/Interop.WaitForCompletion.cs +++ b/src/System.Device.Gpio/Interop/Windows/WinRT/Interop.WaitForCompletion.cs @@ -8,7 +8,7 @@ using Windows.Foundation; internal static partial class Interop { - public static TResult WaitForCompletion(this IAsyncOperation operation) + public static TResult? WaitForCompletion(this IAsyncOperation operation) { using (var waitEvent = new ManualResetEvent(false)) { diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs index 8b7dab76f99ac29adfe091676e3bc27d9f63b4be..780d5d4eac1b743e89d4d1be6ef8fdb22637954f 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/HummingBoardDriver.cs @@ -102,7 +102,7 @@ namespace System.Device.Gpio.Drivers protected override void Dispose(bool disposing) { _internalDriver?.Dispose(); - _internalDriver = null; + _internalDriver = null!; base.Dispose(disposing); } } diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/InterruptSysFsDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/InterruptSysFsDriver.cs index da213a954cda0bd6f6aee01f12006d62c08e8cee..b989edc7500286bb3a13e5eea85214645e197262 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/InterruptSysFsDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/InterruptSysFsDriver.cs @@ -45,7 +45,7 @@ namespace System.Device.Gpio.Drivers if (disposing) { // not our instance - _gpioDriver = null; + _gpioDriver = null!; } base.Dispose(disposing); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs index 1d6ddb5abe68fa5feb6b31135de15f518973bd82..7fddfd194c69a32068fea155600280a2212c4e5f 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/LibGpiodDriver.cs @@ -31,10 +31,15 @@ namespace System.Device.Gpio.Drivers private static bool IsLibgpiodVersion1_5orHigher() { IntPtr libgpiodVersionPtr = Interop.libgpiod.gpiod_version_string(); - string libgpiodVersionMatch = Marshal.PtrToStringAnsi(libgpiodVersionPtr); - Version libgpiodVersion = new Version(libgpiodVersionMatch); + string? libgpiodVersionMatch = Marshal.PtrToStringAnsi(libgpiodVersionPtr); - return (libgpiodVersion.Major >= 1 && libgpiodVersion.Minor >= 5); + if (libgpiodVersionMatch is object) + { + Version libgpiodVersion = new Version(libgpiodVersionMatch); + return (libgpiodVersion.Major >= 1 && libgpiodVersion.Minor >= 5); + } + + return false; } private static bool s_isLibgpiodVersion1_5orHigher = IsLibgpiodVersion1_5orHigher(); @@ -106,17 +111,16 @@ namespace System.Device.Gpio.Drivers { lock (_pinNumberLock) { - if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle)) + _pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle? pinHandle); + + if (pinHandle is null || (pinHandle is object && !Interop.libgpiod.gpiod_line_is_free(pinHandle))) { - if (!Interop.libgpiod.gpiod_line_is_free(pinHandle)) - { - pinHandle.Dispose(); - pinHandle = Interop.libgpiod.gpiod_chip_get_line(_chip, pinNumber); - _pinNumberToSafeLineHandle[pinNumber] = pinHandle; - } + pinHandle?.Dispose(); + pinHandle = Interop.libgpiod.gpiod_chip_get_line(_chip, pinNumber); + _pinNumberToSafeLineHandle[pinNumber] = pinHandle; } - return new LibGpiodDriverEventHandler(pinNumber, pinHandle); + return new LibGpiodDriverEventHandler(pinNumber, pinHandle!); } } @@ -125,7 +129,7 @@ namespace System.Device.Gpio.Drivers { lock (_pinNumberLock) { - if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle) && + if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle? pinHandle) && !IsListeningEvent(pinNumber)) { pinHandle?.Dispose(); @@ -149,7 +153,7 @@ namespace System.Device.Gpio.Drivers { lock (_pinNumberLock) { - if (!_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle)) + if (!_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle? pinHandle)) { throw ExceptionHelper.GetInvalidOperationException(ExceptionResource.PinNotOpenedError, pin: pinNumber); @@ -199,7 +203,7 @@ namespace System.Device.Gpio.Drivers /// protected internal override PinValue Read(int pinNumber) { - if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle)) + if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle? pinHandle)) { int result = Interop.libgpiod.gpiod_line_get_value(pinHandle); if (result == -1) @@ -216,14 +220,14 @@ namespace System.Device.Gpio.Drivers /// protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) { - if (_pinNumberToEventHandler.TryGetValue(pinNumber, out LibGpiodDriverEventHandler eventHandler)) + if (_pinNumberToEventHandler.TryGetValue(pinNumber, out LibGpiodDriverEventHandler? eventHandler)) { eventHandler.ValueFalling -= callback; eventHandler.ValueRising -= callback; if (eventHandler.IsCallbackListEmpty()) { _pinNumberToEventHandler.TryRemove(pinNumber, out eventHandler); - eventHandler.Dispose(); + eventHandler?.Dispose(); } } else @@ -236,7 +240,7 @@ namespace System.Device.Gpio.Drivers protected internal override void SetPinMode(int pinNumber, PinMode mode) { int requestResult = -1; - if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle)) + if (_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle? pinHandle)) { switch (mode) { @@ -317,7 +321,7 @@ namespace System.Device.Gpio.Drivers /// protected internal override void Write(int pinNumber, PinValue value) { - if (!_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle pinHandle)) + if (!_pinNumberToSafeLineHandle.TryGetValue(pinNumber, out SafeLineHandle? pinHandle)) { throw ExceptionHelper.GetInvalidOperationException(ExceptionResource.PinNotOpenedError, pin: pinNumber); @@ -344,7 +348,7 @@ namespace System.Device.Gpio.Drivers { foreach (int pin in _pinNumberToSafeLineHandle.Keys) { - if (_pinNumberToSafeLineHandle.TryGetValue(pin, out SafeLineHandle pinHandle)) + if (_pinNumberToSafeLineHandle.TryGetValue(pin, out SafeLineHandle? pinHandle)) { pinHandle?.Dispose(); } @@ -354,7 +358,7 @@ namespace System.Device.Gpio.Drivers } _chip?.Dispose(); - _chip = null; + _chip = null!; base.Dispose(disposing); } diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs index a79cba95360b210919a5dbd9b4f1fe007e0d6b0e..1dc3710dfa3a33add151500d07e6af33e0aff3c9 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3Driver.cs @@ -32,7 +32,7 @@ namespace System.Device.Gpio.Drivers { if (Environment.OSVersion.Platform == PlatformID.Unix) { - RaspberryPi3LinuxDriver linuxDriver = CreateInternalRaspberryPi3LinuxDriver(out RaspberryBoardInfo boardInfo); + RaspberryPi3LinuxDriver? linuxDriver = CreateInternalRaspberryPi3LinuxDriver(out RaspberryBoardInfo boardInfo); if (linuxDriver == null) { @@ -71,10 +71,10 @@ namespace System.Device.Gpio.Drivers } } - internal static RaspberryPi3LinuxDriver CreateInternalRaspberryPi3LinuxDriver(out RaspberryBoardInfo boardInfo) + internal static RaspberryPi3LinuxDriver? CreateInternalRaspberryPi3LinuxDriver(out RaspberryBoardInfo boardInfo) { RaspberryBoardInfo identification = RaspberryBoardInfo.LoadBoardInfo(); - RaspberryPi3LinuxDriver linuxDriver; + RaspberryPi3LinuxDriver? linuxDriver; boardInfo = identification; switch (identification.BoardModel) { @@ -172,7 +172,7 @@ namespace System.Device.Gpio.Drivers protected override void Dispose(bool disposing) { _internalDriver?.Dispose(); - _internalDriver = null; + _internalDriver = null!; base.Dispose(disposing); } } diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs index 22c53f51dd0f93088c374897f39001d8cd3e63d7..19480e3b757bdeeefa26407969b8512fcedccd7b 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/RaspberryPi3LinuxDriver.cs @@ -10,6 +10,8 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +#pragma warning disable SA1011 // Closing square brackets should be spaced correctly https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2927 + namespace System.Device.Gpio.Drivers { /// @@ -29,11 +31,11 @@ namespace System.Device.Gpio.Drivers private const string DeviceTreeRanges = "/proc/device-tree/soc/ranges"; private const string ModelFilePath = "/proc/device-tree/model"; - private readonly PinState[] _pinModes; + private readonly PinState?[] _pinModes; private RegisterView* _registerViewPointer = null; private static readonly object s_initializationLock = new object(); - private UnixDriver _interruptDriver = null; + private UnixDriver? _interruptDriver = null; /// /// Returns true if this is a Raspberry Pi4 @@ -113,9 +115,8 @@ namespace System.Device.Gpio.Drivers { ValidatePinNumber(pinNumber); - _interruptDriver.OpenPin(pinNumber); - _pinModes[pinNumber].InUseByInterruptDriver = true; - + _interruptDriver!.OpenPin(pinNumber); + _pinModes[pinNumber]!.InUseByInterruptDriver = true; _interruptDriver.AddCallbackForPinValueChangedEvent(pinNumber, eventTypes, callback); } @@ -127,10 +128,9 @@ namespace System.Device.Gpio.Drivers { ValidatePinNumber(pinNumber); - bool isOpen = _pinModes[pinNumber] != null && _pinModes[pinNumber].InUseByInterruptDriver; - if (isOpen) + if (_pinModes[pinNumber]?.InUseByInterruptDriver ?? false) { - _interruptDriver.ClosePin(pinNumber); + _interruptDriver!.ClosePin(pinNumber); } // Set pin low and mode to input upon closing a pin @@ -197,8 +197,8 @@ namespace System.Device.Gpio.Drivers { ValidatePinNumber(pinNumber); - _interruptDriver.OpenPin(pinNumber); - _pinModes[pinNumber].InUseByInterruptDriver = true; + _interruptDriver!.OpenPin(pinNumber); + _pinModes[pinNumber]!.InUseByInterruptDriver = true; _interruptDriver.RemoveCallbackForPinValueChangedEvent(pinNumber, callback); } @@ -234,9 +234,9 @@ namespace System.Device.Gpio.Drivers register |= (mode == PinMode.Output ? 1u : 0u) << shift; *registerPointer = register; - if (_pinModes[pinNumber] != null) + if (_pinModes[pinNumber] is object) { - _pinModes[pinNumber].CurrentPinMode = mode; + _pinModes[pinNumber]!.CurrentPinMode = mode; } else { @@ -378,8 +378,8 @@ namespace System.Device.Gpio.Drivers { ValidatePinNumber(pinNumber); - _interruptDriver.OpenPin(pinNumber); - _pinModes[pinNumber].InUseByInterruptDriver = true; + _interruptDriver!.OpenPin(pinNumber); + _pinModes[pinNumber]!.InUseByInterruptDriver = true; return _interruptDriver.WaitForEvent(pinNumber, eventTypes, cancellationToken); } @@ -395,8 +395,8 @@ namespace System.Device.Gpio.Drivers { ValidatePinNumber(pinNumber); - _interruptDriver.OpenPin(pinNumber); - _pinModes[pinNumber].InUseByInterruptDriver = true; + _interruptDriver!.OpenPin(pinNumber); + _pinModes[pinNumber]!.InUseByInterruptDriver = true; return _interruptDriver.WaitForEventAsync(pinNumber, eventTypes, cancellationToken); } @@ -623,11 +623,8 @@ namespace System.Device.Gpio.Drivers _registerViewPointer = null; } - if (_interruptDriver != null) - { - _interruptDriver.Dispose(); - _interruptDriver = null; - } + _interruptDriver?.Dispose(); + _interruptDriver = null; } private class PinState diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs index 8bf6a920b6d67a414530d9aff1a8d87e0eb9e03d..8918dc029bc4f0c780402cbd752195ddefe43198 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/SysFsDriver.cs @@ -28,7 +28,7 @@ namespace System.Device.Gpio.Drivers private static readonly int s_pinOffset = ReadOffset(); private TimeSpan _statusUpdateSleepTime = TimeSpan.FromMilliseconds(1); private int _pollFileDescriptor = -1; - private Thread _eventDetectionThread; + private Thread? _eventDetectionThread; private int _pinsToDetectEventsCount; private static int ReadOffset() diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriver.cs index bb77ad37c55686c16487e4654a14fd30c944f007..8ecc0a4e8f2acef5a082c9e0bbc5e7fd09229b8f 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriver.cs @@ -32,7 +32,7 @@ namespace System.Device.Gpio.Drivers throw new PlatformNotSupportedException(nameof(UnixDriver) + " is only supported on Linux/Unix"); } - UnixDriver driver = null; + UnixDriver? driver = null; try { driver = new LibGpiodDriver(); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriverDevicePin.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriverDevicePin.cs index 2f3371ce1e290b1cd441f2a893352ae0d7f3ea06..2b073efbdcae1786736d1a3518361d82bb0c504d 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriverDevicePin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/UnixDriverDevicePin.cs @@ -13,8 +13,8 @@ namespace System.Device.Gpio.Drivers ActiveEdges = PinEventTypes.None; } - public event PinChangeEventHandler ValueRising; - public event PinChangeEventHandler ValueFalling; + public event PinChangeEventHandler? ValueRising; + public event PinChangeEventHandler? ValueFalling; public int FileDescriptor; diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs index a721ca466febfc0fef8472a602093bff65f6fae6..8bef56ac967d0b977c4f38d0d739733595bd728c 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10Driver.cs @@ -67,7 +67,7 @@ namespace System.Device.Gpio.Drivers /// The pin number in the driver's logical numbering scheme. protected internal override void ClosePin(int pinNumber) { - if (_openPins.TryGetValue(pinNumber, out Windows10DriverPin pin)) + if (_openPins.TryGetValue(pinNumber, out Windows10DriverPin? pin)) { pin.ClosePin(); _openPins.Remove(pinNumber); diff --git a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10DriverPin.cs b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10DriverPin.cs index 1802107442bd269cf717b0f907794758e3457c73..213c7567e03b47b4ce0abdd265a5ea1a25c8606b 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10DriverPin.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/Drivers/Windows10DriverPin.cs @@ -16,8 +16,8 @@ namespace System.Device.Gpio.Drivers private readonly int _pinNumber; private WeakReference _driver; private WinGpio.GpioPin _pin; - private PinChangeEventHandler _risingCallbacks; - private PinChangeEventHandler _fallingCallbacks; + private PinChangeEventHandler? _risingCallbacks; + private PinChangeEventHandler? _fallingCallbacks; public Windows10DriverPin(Windows10Driver driver, WinGpio.GpioPin pin) { @@ -40,10 +40,10 @@ namespace System.Device.Gpio.Drivers { _pin.ValueChanged -= Pin_ValueChanged; _pin.Dispose(); - _pin = null; + _pin = null!; } - _driver = null; + _driver = null!; _fallingCallbacks = null; _risingCallbacks = null; GC.SuppressFinalize(this); @@ -76,7 +76,7 @@ namespace System.Device.Gpio.Drivers private void Pin_ValueChanged(WinGpio.GpioPin sender, WinGpio.GpioPinValueChangedEventArgs args) { - if (!_driver.TryGetTarget(out Windows10Driver driver)) + if (!_driver.TryGetTarget(out Windows10Driver? driver)) { return; } diff --git a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs index ca5d2ee81ae84142468229bd301c9efa75d7e095..8e9ce877c5e2f55c41838b7166790f06936501c2 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/GpioController.cs @@ -381,9 +381,9 @@ namespace System.Device.Gpio /// A driver that works with the board the program is executing on. private static GpioDriver GetBestDriverForBoardOnLinux() { - RaspberryPi3LinuxDriver internalDriver = RaspberryPi3Driver.CreateInternalRaspberryPi3LinuxDriver(out _); + RaspberryPi3LinuxDriver? internalDriver = RaspberryPi3Driver.CreateInternalRaspberryPi3LinuxDriver(out _); - if (internalDriver != null) + if (internalDriver is object) { return new RaspberryPi3Driver(internalDriver); } @@ -403,7 +403,12 @@ namespace System.Device.Gpio /// private static GpioDriver GetBestDriverForBoardOnWindows() { - string baseBoardProduct = Registry.LocalMachine.GetValue(BaseBoardProductRegistryValue, string.Empty).ToString(); + string? baseBoardProduct = Registry.LocalMachine.GetValue(BaseBoardProductRegistryValue, string.Empty).ToString(); + + if (baseBoardProduct is null) + { + throw new Exception("Single board computer type cannot be detected."); + } if (baseBoardProduct == RaspberryPi3Product || baseBoardProduct.StartsWith($"{RaspberryPi3Product} ") || baseBoardProduct == RaspberryPi2Product || baseBoardProduct.StartsWith($"{RaspberryPi2Product} ")) diff --git a/src/System.Device.Gpio/System/Device/Gpio/LibgpiodDriverEventHandler.cs b/src/System.Device.Gpio/System/Device/Gpio/LibgpiodDriverEventHandler.cs index fd496c5220b3d096e985c2cd54e5cf86d3a19993..29999ec3b67e9b390352912a20cac34de123fd92 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/LibgpiodDriverEventHandler.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/LibgpiodDriverEventHandler.cs @@ -11,9 +11,9 @@ namespace System.Device.Gpio.Drivers { internal sealed class LibGpiodDriverEventHandler : IDisposable { - public event PinChangeEventHandler ValueRising; + public event PinChangeEventHandler? ValueRising; - public event PinChangeEventHandler ValueFalling; + public event PinChangeEventHandler? ValueFalling; private int _pinNumber; diff --git a/src/System.Device.Gpio/System/Device/Gpio/PinValue.cs b/src/System.Device.Gpio/System/Device/Gpio/PinValue.cs index f3ef52cf835fb1b9ab09b7deb8afee257453c0af..3e56b4afc33c113a3e70089b32fdfc6bd1d9700e 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/PinValue.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/PinValue.cs @@ -62,7 +62,7 @@ namespace System.Device.Gpio public bool Equals(PinValue other) => other._value == _value; /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is PinValue) { diff --git a/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs b/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs index ae0fa63fdf1027abae5ba616c2f85d0a5d6690db..cd85f8b751b771ab5fad9f54bac1faef6950a5d1 100644 --- a/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs +++ b/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs @@ -96,23 +96,17 @@ namespace System.Device.Gpio private RaspberryBoardInfo(Dictionary settings) { _settings = settings; - Firmware = 0; - ProcessorName = string.Empty; - if (_settings.TryGetValue("Hardware", out string hardware)) - { - ProcessorName = hardware; - } + ProcessorName = _settings.TryGetValue("Hardware", out string? hardware) && hardware is object ? hardware : string.Empty; - if (_settings.TryGetValue("Revision", out string revision) + if (_settings.TryGetValue("Revision", out string? revision) && !string.IsNullOrEmpty(revision) && int.TryParse(revision, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int firmware)) { Firmware = firmware; } - if (_settings.TryGetValue("Serial", out string serial) - && !string.IsNullOrEmpty(serial)) + if (_settings.TryGetValue("Serial", out string? serial)) { SerialNumber = serial; } @@ -215,7 +209,7 @@ namespace System.Device.Gpio /// /// Gets the serial number. /// - public string SerialNumber + public string? SerialNumber { get; } @@ -230,8 +224,7 @@ namespace System.Device.Gpio { get { - var firmware = Firmware; - return (firmware & 0xFFFF0000) != 0; + return (Firmware & 0xFFFF0000) != 0; } } diff --git a/src/System.Device.Gpio/System/Device/I2c/Devices/Windows10I2cDevice.cs b/src/System.Device.Gpio/System/Device/I2c/Devices/Windows10I2cDevice.cs index 249da04c74026e9d7858b4f2623a567dd148e4cb..e5123ba6d803b713df107092e8133f030a16097a 100644 --- a/src/System.Device.Gpio/System/Device/I2c/Devices/Windows10I2cDevice.cs +++ b/src/System.Device.Gpio/System/Device/I2c/Devices/Windows10I2cDevice.cs @@ -27,17 +27,19 @@ namespace System.Device.I2c string busFriendlyName = $"I2C{settings.BusId}"; string deviceSelector = WinI2c.I2cDevice.GetDeviceSelector(busFriendlyName); - DeviceInformationCollection deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); - if (deviceInformationCollection.Count == 0) + DeviceInformationCollection? deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); + if (deviceInformationCollection is null || deviceInformationCollection.Count == 0) { throw new ArgumentException($"No I2C device exists for bus ID {settings.BusId}.", $"{nameof(settings)}.{nameof(settings.BusId)}"); } - _winI2cDevice = WinI2c.I2cDevice.FromIdAsync(deviceInformationCollection[0].Id, winSettings).WaitForCompletion(); - if (_winI2cDevice == null) + WinI2c.I2cDevice? winI2cDevice = WinI2c.I2cDevice.FromIdAsync(deviceInformationCollection[0].Id, winSettings).WaitForCompletion(); + if (winI2cDevice == null) { - throw new PlatformNotSupportedException($"I2C devices are not supported."); + throw new PlatformNotSupportedException("An I2C device could not be found."); } + + _winI2cDevice = winI2cDevice; } /// @@ -123,7 +125,7 @@ namespace System.Device.I2c protected override void Dispose(bool disposing) { _winI2cDevice?.Dispose(); - _winI2cDevice = null; + _winI2cDevice = null!; base.Dispose(disposing); } diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.cs index e2c97da01a5dbb661facb501373045880b0a1eaa..725796735e2e789a4096d252a7a5cf074e802151 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.cs @@ -235,9 +235,9 @@ namespace System.Device.Pwm.Channels protected override void Dispose(bool disposing) { _dutyCycleWriter?.Dispose(); - _dutyCycleWriter = null; + _dutyCycleWriter = null!; _frequencyWriter?.Dispose(); - _frequencyWriter = null; + _frequencyWriter = null!; Close(); base.Dispose(disposing); } diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.cs index 151392e9b1c0b81520aa7645213b0dbae7dc3a9d..2220789784c1e3ab6b9a07d54d710c8c045dc946 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.cs @@ -42,21 +42,28 @@ namespace System.Device.Pwm.Channels // Open the Windows PWM controller for the specified PWM chip. string deviceSelector = useDefaultChip ? WinPwm.PwmController.GetDeviceSelector() : WinPwm.PwmController.GetDeviceSelector($"PWM{chip}"); - DeviceInformationCollection deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); - if (deviceInformationCollection.Count == 0) + DeviceInformationCollection? deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); + if (deviceInformationCollection is null || deviceInformationCollection.Count == 0) { throw new ArgumentException($"No PWM device exists for PWM chip at index {chip}.", nameof(chip)); } string deviceId = deviceInformationCollection[0].Id; - _winController = WinPwm.PwmController.FromIdAsync(deviceId).WaitForCompletion(); + WinPwm.PwmController? winController = WinPwm.PwmController.FromIdAsync(deviceId).WaitForCompletion(); - _winPin = _winController.OpenPin(channel); - if (_winPin == null) + if (winController is null) + { + throw new Exception("A PWM device could not be found."); + } + + WinPwm.PwmPin? winPin = winController.OpenPin(channel); + if (winPin is null) { throw new ArgumentOutOfRangeException($"The PWM chip is unable to open a channel at index {channel}.", nameof(channel)); } + _winController = winController; + _winPin = winPin; Frequency = frequency; DutyCycle = dutyCycle; } @@ -111,8 +118,8 @@ namespace System.Device.Pwm.Channels { Stop(); _winPin?.Dispose(); - _winPin = null; - _winController = null; + _winPin = null!; + _winController = null!; base.Dispose(disposing); } } diff --git a/src/System.Device.Gpio/System/Device/Spi/Devices/Windows10SpiDevice.cs b/src/System.Device.Gpio/System/Device/Spi/Devices/Windows10SpiDevice.cs index c48e0d0e1e692fd0b0c847aa072af81cf1a23a10..54e6af592b19df181b0929d66b76d3210d36f2bf 100644 --- a/src/System.Device.Gpio/System/Device/Spi/Devices/Windows10SpiDevice.cs +++ b/src/System.Device.Gpio/System/Device/Spi/Devices/Windows10SpiDevice.cs @@ -44,13 +44,20 @@ namespace System.Device.Spi string busFriendlyName = $"SPI{settings.BusId}"; string deviceSelector = WinSpi.SpiDevice.GetDeviceSelector(busFriendlyName); - DeviceInformationCollection deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); - if (deviceInformationCollection.Count == 0) + DeviceInformationCollection? deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); + if (deviceInformationCollection is null || deviceInformationCollection.Count == 0) { 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(); + WinSpi.SpiDevice? winDevice = WinSpi.SpiDevice.FromIdAsync(deviceInformationCollection[0].Id, winSettings).WaitForCompletion(); + + if (winDevice is null) + { + throw new Exception("A SPI device could not be found."); + } + + _winDevice = winDevice; } /// @@ -129,7 +136,7 @@ namespace System.Device.Spi protected override void Dispose(bool disposing) { _winDevice?.Dispose(); - _winDevice = null; + _winDevice = null!; base.Dispose(disposing); }