diff --git a/System.IO.FileSystem.UnitTests/DirectoryUnitTests.cs b/System.IO.FileSystem.UnitTests/DirectoryUnitTests.cs index fa9667b..ef9957b 100644 --- a/System.IO.FileSystem.UnitTests/DirectoryUnitTests.cs +++ b/System.IO.FileSystem.UnitTests/DirectoryUnitTests.cs @@ -98,6 +98,10 @@ public void TestEnumerateDirectories() Directory.CreateDirectory($@"{path}subdir2\"); Directory.CreateDirectory($@"{path}subdir3\"); + // Add files to test we don't see files only directories + File.Create($@"{path}file1.txt").Close(); + File.Create($@"{path}file2.txt").Close(); + var directories = Directory.GetDirectories(path); Assert.AreEqual(3, directories.Length); @@ -119,14 +123,27 @@ public void TestEnumerateFiles() Directory.CreateDirectory(path); - File.Create($@"{path}file1.txt").Close(); - File.Create($@"{path}file2.txt").Close(); - File.Create($@"{path}file3.txt").Close(); + string file1 = $@"{path}file1.txt"; + string file2 = $@"{path}file2.txt"; + string file3 = $@"{path}file3.txt"; + + // Add a mix of directories and files + Directory.CreateDirectory($@"{path}TestDir1"); + Directory.CreateDirectory($@"{path}TestDir2"); + + File.Create(file1).Close(); + File.Create(file2).Close(); + File.Create(file3).Close(); var files = Directory.GetFiles(path); Assert.AreEqual(3, files.Length); + // Check correct file names returned + Assert.IsTrue((files[0] == file1), "Invalid file1 in GetFiles()"); + Assert.IsTrue((files[1] == file2), "Invalid file2 in GetFiles()"); + Assert.IsTrue((files[2] == file3), "Invalid file3 in GetFiles()"); + // Clean up after the test Directory.Delete(path, true); } diff --git a/System.IO.FileSystem.UnitTests/FileSystemUnitTestsBase.cs b/System.IO.FileSystem.UnitTests/FileSystemUnitTestsBase.cs index ecd90ea..ffdf97c 100644 --- a/System.IO.FileSystem.UnitTests/FileSystemUnitTestsBase.cs +++ b/System.IO.FileSystem.UnitTests/FileSystemUnitTestsBase.cs @@ -38,14 +38,20 @@ public abstract class FileSystemUnitTestsBase protected SDCard InitializeSDCard() { // Example initialization logic - SDCard.SDCardMmcParameters parameters = new SDCard.SDCardMmcParameters + SDCardMmcParameters parameters = new SDCardMmcParameters { + slotIndex = 0, dataWidth = SDCard.SDDataWidth._4_bit, + }; + + CardDetectParameters cdParameters = new CardDetectParameters() + { enableCardDetectPin = true, - cardDetectPin = 21 + cardDetectPin = 21, + autoMount = true }; - return new SDCard(parameters); + return new SDCard(parameters, cdParameters); } /// diff --git a/System.IO.FileSystem/Path.cs b/System.IO.FileSystem/Path.cs index 1fcfcab..583cafc 100644 --- a/System.IO.FileSystem/Path.cs +++ b/System.IO.FileSystem/Path.cs @@ -164,10 +164,8 @@ internal static int GetDirectoryNameOffset(string path) return -1; } - while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end])) - { - } - + while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end])); + // Trim off any remaining separators (to deal with C:\foo\\bar) while (end > rootLength && PathInternal.IsDirectorySeparator(path[end - 1])) { diff --git a/System.IO.FileSystem/Properties/AssemblyInfo.cs b/System.IO.FileSystem/Properties/AssemblyInfo.cs index a63aad4..66b839d 100644 --- a/System.IO.FileSystem/Properties/AssemblyInfo.cs +++ b/System.IO.FileSystem/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ //////////////////////////////////////////////////////////////// // update this whenever the native assembly signature changes // -[assembly: AssemblyNativeVersion("1.1.0.3")] +[assembly: AssemblyNativeVersion("1.1.0.4")] //////////////////////////////////////////////////////////////// [assembly: InternalsVisibleTo("NFUnitTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001001120aa3e809b3da4f65e1b1f65c0a3a1bf6335c39860ca41acb3c48de278c6b63c5df38239ec1f2e32d58cb897c8c174a5f8e78a9c0b6087d3aef373d7d0f3d9be67700fc2a5a38de1fb71b5b6f6046d841ff35abee2e0b0840a6291a312be184eb311baff5fef0ff6895b9a5f2253aed32fb06b819134f6bb9d531488a87ea2")] diff --git a/System.IO.FileSystem/System.IO.FileSystem.nfproj b/System.IO.FileSystem/System.IO.FileSystem.nfproj index 804dfd3..cba8830 100644 --- a/System.IO.FileSystem/System.IO.FileSystem.nfproj +++ b/System.IO.FileSystem/System.IO.FileSystem.nfproj @@ -53,6 +53,10 @@ + + + + diff --git a/System.IO.FileSystem/nanoFramework/CardDetectChangedEventArgs.cs b/System.IO.FileSystem/nanoFramework/CardDetectChangedEventArgs.cs new file mode 100644 index 0000000..a886856 --- /dev/null +++ b/System.IO.FileSystem/nanoFramework/CardDetectChangedEventArgs.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.System.IO +{ + /// + /// State of card detect + /// + public enum CardDetectState + { + /// + /// Card Inserted + /// + Inserted, + + /// + /// Card removed + /// + Removed + }; + + /// + /// Arguments for Card detect event + /// + public class CardDetectChangedEventArgs : EventArgs + { + readonly private CardDetectState _cardState; + readonly private uint _slotIndex; + + internal CardDetectChangedEventArgs(CardDetectState state, uint SlotIndex) + { + _cardState = state; + _slotIndex = SlotIndex; + } + + /// + /// State of Card Detect. + /// + public CardDetectState CardState => _cardState; + + /// + /// SD card slot index + /// + public uint SlotIndex => _slotIndex; + } +} diff --git a/System.IO.FileSystem/nanoFramework/CardDetectParameters.cs b/System.IO.FileSystem/nanoFramework/CardDetectParameters.cs new file mode 100644 index 0000000..5365561 --- /dev/null +++ b/System.IO.FileSystem/nanoFramework/CardDetectParameters.cs @@ -0,0 +1,41 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.System.IO.FileSystem +{ + /// + /// Parameter used for Card detection when creating a SDcard instance. + /// + public class CardDetectParameters + { + /// + /// Set true when an Card Detect Pin is used. + /// The cardDetectPin parameter must have a valid GPIO pin. + /// + /// + /// Not all SD Card modules have a card detect pin or the pin connected to a GPIO pin. + /// + public bool enableCardDetectPin; + + /// + /// The state of the pin when the card is detected. + /// Defaults to false(low) if not specified. + /// If using card detect logic then this depends on connected hardware. + /// + public bool cardDetectedState; + + /// + /// The optional card detect GPIO pin which must be set to a valid pin if EnableCardDetectPin is true. + /// If defined a StorageEventManager event will be raised when a card is inserted or removed. + /// + public uint cardDetectPin; + + /// + /// When enabled will try to automatically mount the SD card when the card is inserted. + /// If the card is removed unexpectedly it will try to unmount card to release resources. + /// + public bool autoMount; + } +} diff --git a/System.IO.FileSystem/nanoFramework/SDCard.cs b/System.IO.FileSystem/nanoFramework/SDCard.cs index bef932c..885d443 100644 --- a/System.IO.FileSystem/nanoFramework/SDCard.cs +++ b/System.IO.FileSystem/nanoFramework/SDCard.cs @@ -9,6 +9,16 @@ namespace nanoFramework.System.IO.FileSystem { + + /// + /// Provides an event handler that is called when a SD card detect state change event occurs. + /// + /// Specifies the object that sent the event. + /// Contains the Card detect changed event arguments + public delegate void CardDetectStateEventHandler( + object sender, + CardDetectChangedEventArgs e); + /// /// Class to allow a SD memory card to be configured and mounted on the system. /// @@ -26,17 +36,19 @@ public class SDCard : IDisposable [Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)] private bool _disposed; - // Common parameters - private SDInterfaceType _sdCardType; - private bool _enableCardDetectPin; + readonly private SDInterfaceType _sdCardType; + // Card detect parameters + private bool _enableCardDetectPin = false; + private bool _cardDetectedState = false; private uint _cardDetectPin; + private uint _slotIndex; + private bool _autoMount = true; // MMC parameters - private SDDataWidth _dataWidth; + readonly private SDDataWidth _dataWidth; // SPI parameters - private uint _spiBus; - private uint _chipSelectPin; - + readonly private uint _spiBus; + readonly private uint _chipSelectPin; #region Properties /// @@ -45,24 +57,47 @@ public class SDCard : IDisposable public SDInterfaceType CardType => _sdCardType; /// - /// Indicates if the SD card has been mounted + /// Indicates if the SD card has been mounted. /// public bool IsMounted => _mounted; /// - /// Return true if Card detection is enabled + /// Return true if Card detection is enabled. /// public bool CardDetectEnabled => _enableCardDetectPin; + /// + /// SD card slot index. + /// + public uint SlotIndex => _slotIndex; + /// /// The parameters for a MMC connected SD card. /// - public SDCardMmcParameters MmcParameters { get => new SDCardMmcParameters { dataWidth=_dataWidth, enableCardDetectPin=_enableCardDetectPin, cardDetectPin=_cardDetectPin }; } + public SDCardMmcParameters MmcParameters { get => new SDCardMmcParameters { slotIndex = _slotIndex, dataWidth = _dataWidth}; } /// /// The parameters for a SPI connected SD card. /// - public SDCardSpiParameters SpiParameters { get => new SDCardSpiParameters { spiBus=_spiBus, chipSelectPin=_chipSelectPin, enableCardDetectPin = _enableCardDetectPin, cardDetectPin = _cardDetectPin }; } + public SDCardSpiParameters SpiParameters { get => new SDCardSpiParameters { slotIndex = _slotIndex, spiBus = _spiBus, chipSelectPin = _chipSelectPin}; } + + /// + /// The Card detect parameters for SD card. + /// + public CardDetectParameters CdParameters { get => new CardDetectParameters { autoMount=_autoMount, cardDetectedState = _cardDetectedState, cardDetectPin = _cardDetectPin, enableCardDetectPin = _enableCardDetectPin }; } + + /// + /// Event that occurs when SD card detect changes state. + /// + /// + /// + /// The class raises the event when an SD Cards is inserted or removed. + /// This is only raised if SD card is configured with a Card Detect pin. Some SD card holders don't have this feature. + /// + /// You only need to use this event if the are configured for a manual mount of card on card detect. The default is automatic. + /// + public event CardDetectStateEventHandler CardDetectChanged; + /// /// Indicates if SD card has been detected if optional cardDetectPin parameter is enabled with a valid GPIO pin. @@ -84,100 +119,60 @@ public bool IsCardDetected } #endregion - #region Parameters - /// - /// Parameter used for creating a MMC card instance. - /// - public class SDCardMmcParameters - { - /// - /// Data width to use on MMC SD protocol. - /// - public SDDataWidth dataWidth; - - /// - /// Set true when an Card Detect Pin is used. - /// The cardDetectPin parameter must have a valid GPIO pin. - /// - /// - /// Not all SD Card modules have a card detect pin or the pin connected to a GPIO pin. - /// - public bool enableCardDetectPin; - - /// - /// The optional card detect GPIO pin which must be set to a valid pin if EnableCardDetectPin is true. - /// If defined a StorageEventManager event will be raised when a card is inserted or removed. - /// - public uint cardDetectPin; - } - - /// - /// Parameter used for creating a SPI card instance. - /// - public class SDCardSpiParameters - { - /// - /// The SPI bus to use for SD Card. - /// - public uint spiBus; - /// - /// The chip select pin to use for SD Card. - /// - public uint chipSelectPin; - - /// - /// Set true when an Card Detect Pin is used. - /// The cardDetectPin parameter must have a valid GPIO pin. - /// - /// - /// Not all SD Card modules have a card detect pin or the pin connected to a GPIO pin. - /// - public bool enableCardDetectPin; - - /// - /// The optional card detect GPIO pin which must be set to a valid pin if EnableCardDetectPin is true. - /// If defined a StorageEventManager event will be raised when a card is inserted or removed. - /// - public uint cardDetectPin; - }; - - #endregion - /// /// Creates an instance of SDcard where parameters have already been defined in firmware. /// - public SDCard() + public SDCard(uint slotIndex = 0) { _sdCardType = SDInterfaceType.System; - InitNative(); + Initialise(slotIndex, null); } /// /// Create an instance of SDCard for a MMC connected SD card. /// - /// Connection parameters - public SDCard(SDCardMmcParameters parameters) + /// Connection parameters + /// Card detect parameters + public SDCard(SDCardMmcParameters mmcParameters, CardDetectParameters cdParameters = null) { _sdCardType = SDInterfaceType.Mmc; - _dataWidth = parameters.dataWidth; - _enableCardDetectPin = parameters.enableCardDetectPin; - _cardDetectPin = parameters.cardDetectPin; + _dataWidth = mmcParameters.dataWidth; - InitNative(); + Initialise(mmcParameters.slotIndex, cdParameters); } /// /// Create an instance of SDCard for a SPI connected SD card. /// - /// Connection parameters - public SDCard(SDCardSpiParameters parameters) + /// Connection parameters + /// Card detect parameters + public SDCard(SDCardSpiParameters spiParameters, CardDetectParameters cdParameters = null) { _sdCardType = SDInterfaceType.Spi; - _spiBus = parameters.spiBus; - _chipSelectPin = parameters.chipSelectPin; - _enableCardDetectPin = parameters.enableCardDetectPin; - _cardDetectPin = parameters.cardDetectPin; + _spiBus = spiParameters.spiBus; + _chipSelectPin = spiParameters.chipSelectPin; + + Initialise(spiParameters.slotIndex, cdParameters); + } + + private void Initialise(uint slotIndex, CardDetectParameters cdParameters) + { + _slotIndex = slotIndex; + + _enableCardDetectPin = false; + if (cdParameters != null) + { + _enableCardDetectPin = cdParameters.enableCardDetectPin; + _cardDetectedState = cdParameters.cardDetectedState; + _cardDetectPin = cdParameters.cardDetectPin; + _autoMount = cdParameters.autoMount; + } + + if (!StorageEventManager.RegisterSDcardForEvents(this)) + { + throw new ArgumentException(); + } InitNative(); } @@ -195,7 +190,7 @@ public void Mount() MountNative(); } - /// + /// /// Unmount a mounted SD memory card. /// public void Unmount() @@ -204,12 +199,54 @@ public void Unmount() UnmountNative(); } + internal void OnEvent(bool pinstate, uint slotIndex) + { + CardDetectState cdstate = (pinstate == _cardDetectedState) ? CardDetectState.Inserted : CardDetectState.Removed; + if (_autoMount) + { + // ignore any exceptions + try + { + if (cdstate == CardDetectState.Inserted) + { + // Auto mount volume if not already mounted + if (!IsMounted) + { + Mount(); + } + } + else + { + // Auto unmount volume if mounted + if (IsMounted) + { + Unmount(); + } + } + } + catch (Exception) + { + //ignore exception + } + } + + CardDetectChanged?.Invoke(this, new CardDetectChangedEventArgs(cdstate, slotIndex)); + } + #region IDisposable Support private void Dispose(bool disposing) { if (!_disposed) { + StorageEventManager.RemoveSDcardFromEvents(this); + + // Try to unmount if disposed + if (IsMounted) + { + Unmount(); + } + NativeDispose(); _disposed = true; @@ -250,7 +287,7 @@ public void Dispose() [Diagnostics.DebuggerStepThrough] [MethodImpl(MethodImplOptions.InternalCall)] private extern void MountNative(); - + [Diagnostics.DebuggerStepThrough] [MethodImpl(MethodImplOptions.InternalCall)] private extern void UnmountNative(); @@ -266,8 +303,8 @@ public void Dispose() /// /// SDCard interface type. /// - public enum SDInterfaceType - { + public enum SDInterfaceType + { /// /// Interface already defined in firmware. /// @@ -299,6 +336,7 @@ public enum SDDataWidth /// _4_bit = 2 } + #endregion } } diff --git a/System.IO.FileSystem/nanoFramework/SDCardMmcParameters.cs b/System.IO.FileSystem/nanoFramework/SDCardMmcParameters.cs new file mode 100644 index 0000000..ccd129f --- /dev/null +++ b/System.IO.FileSystem/nanoFramework/SDCardMmcParameters.cs @@ -0,0 +1,27 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using static nanoFramework.System.IO.FileSystem.SDCard; + +namespace nanoFramework.System.IO.FileSystem +{ + /// + /// Parameter used for creating a MMC card instance. + /// + public class SDCardMmcParameters + { + /// + /// The slot index to mount. Some devices can have more then 1 SD card slot + /// Defaults to 0 + /// Slot 0 will mount as drive D:\ , slot 1 = E:\ etc + /// + public uint slotIndex = 0; + + /// + /// Data width to use on MMC SD protocol. + /// + public SDDataWidth dataWidth; + } +} diff --git a/System.IO.FileSystem/nanoFramework/SDCardSpiParameters.cs b/System.IO.FileSystem/nanoFramework/SDCardSpiParameters.cs new file mode 100644 index 0000000..900ded0 --- /dev/null +++ b/System.IO.FileSystem/nanoFramework/SDCardSpiParameters.cs @@ -0,0 +1,30 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.System.IO.FileSystem +{ + /// + /// Parameter used for creating a SPI card instance. + /// + public class SDCardSpiParameters + { + /// + /// The slot index to mount. Some devices can have more then 1 SD card slot + /// Defaults to 0. + /// Slot 0 will mount as drive D:\ , slot 1 = E:\ etc + /// + public uint slotIndex = 0; + + /// + /// The SPI bus to use for SD Card. + /// + public uint spiBus; + + /// + /// The chip select pin to use for SD Card. + /// + public uint chipSelectPin; + }; +} diff --git a/System.IO.FileSystem/nanoFramework/StorageEventManager.cs b/System.IO.FileSystem/nanoFramework/StorageEventManager.cs index 5f818b3..254aa24 100644 --- a/System.IO.FileSystem/nanoFramework/StorageEventManager.cs +++ b/System.IO.FileSystem/nanoFramework/StorageEventManager.cs @@ -4,9 +4,11 @@ // using nanoFramework.Runtime.Events; +using nanoFramework.System.IO.FileSystem; using System; using System.Collections; using System.IO; +using System.Threading; using static nanoFramework.System.IO.RemovableDriveEventArgs; namespace nanoFramework.System.IO @@ -26,18 +28,21 @@ public delegate void RemovableDeviceEventHandler( public static class StorageEventManager { [Flags] - internal enum StorageEventType : byte + internal enum StorageEventTypes : byte { - Invalid = 0, + None = 0, RemovableDeviceInsertion = 1, RemovableDeviceRemoval = 2, + CardDetectChanged = 3 } internal class StorageEvent : BaseEvent { - public StorageEventType EventType; + public StorageEventTypes EventType; public uint VolumeIndex; + public uint SlotIndex; public DateTime Time; + public bool state; } internal class StorageEventListener : IEventListener, IEventProcessor @@ -49,10 +54,17 @@ public void InitializeForEventSource() public BaseEvent ProcessEvent(uint data1, uint data2, DateTime time) { - StorageEvent storageEvent = new StorageEvent + // Data1 = 0xddddccss + // dddd = data1 + // cc = category (StorageEvent) + // ss = subCategory ( insert, remove ) + StorageEvent storageEvent = new StorageEvent() { - EventType = (StorageEventType)(data1 & 0xFF), + // EventType = subCategory + EventType = (StorageEventTypes)(data1 & 0xFF), + state = ((data1 >> 16) == 1), VolumeIndex = data2, + SlotIndex = data2, Time = time }; @@ -74,7 +86,7 @@ public bool OnEvent(BaseEvent ev) /// Event that occurs when a Removable Device is inserted. /// /// - /// The class raises events when Removable Devices (typically SD Cards and USB mass storage device) are inserted and removed. + /// The class raises events when Removable Devices (typically SD Cards and USB mass storage device) are inserted and removed. /// /// To have a object call an event-handling method when a event occurs, /// you must associate the method with a delegate, and add this delegate to this event. @@ -85,7 +97,7 @@ public bool OnEvent(BaseEvent ev) /// Event that occurs when a Removable Device is removed. /// /// - /// The class raises events when Removable Devices (typically SD Cards and USB mass storage device) are inserted and removed. + /// The class raises events when Removable Devices (typically SD Cards and USB mass storage device) are inserted and removed. /// /// To have a object call an event-handling method when a event occurs, /// you must associate the method with a delegate, and add this delegate to this event. @@ -93,6 +105,7 @@ public bool OnEvent(BaseEvent ev) public static event RemovableDeviceEventHandler RemovableDeviceRemoved; private static ArrayList _drives; + private static ArrayList _sdCardList; static StorageEventManager() { @@ -102,6 +115,7 @@ static StorageEventManager() EventSink.AddEventListener(EventCategory.Storage, storageEventListener); _drives = new ArrayList(); + _sdCardList = new ArrayList(); DriveInfo.MountRemovableVolumes(); } @@ -112,7 +126,7 @@ internal static void OnStorageEventCallback(StorageEvent storageEvent) { switch (storageEvent.EventType) { - case StorageEventType.RemovableDeviceInsertion: + case StorageEventTypes.RemovableDeviceInsertion: { DriveInfo drive = new(storageEvent.VolumeIndex); _drives.Add(drive); @@ -123,7 +137,7 @@ internal static void OnStorageEventCallback(StorageEvent storageEvent) break; } - case StorageEventType.RemovableDeviceRemoval: + case StorageEventTypes.RemovableDeviceRemoval: { DriveInfo drive = RemoveDrive(storageEvent.VolumeIndex); @@ -137,13 +151,23 @@ internal static void OnStorageEventCallback(StorageEvent storageEvent) break; } + case StorageEventTypes.CardDetectChanged: + { + SDCard card = FindRegisteredEvent(storageEvent.SlotIndex); + if (card != null) + { + card.OnEvent(storageEvent.state, storageEvent.SlotIndex); + } + + break; + } + default: break; } } } - private static DriveInfo RemoveDrive(uint volumeIndex) { for (int i = 0; i < _drives.Count; i++) @@ -159,5 +183,48 @@ private static DriveInfo RemoveDrive(uint volumeIndex) return null; } + + /// + /// Register SDCard object for events + /// + /// SDcard object reference. + /// True if successfully registered, false for duplicate index + internal static bool RegisterSDcardForEvents(SDCard card) + { + if (FindRegisteredEvent(card.SlotIndex) == null) + { + _sdCardList.Add(card); + return true; + } + + // Slot index already registered for events, duplicate + return false; + } + + internal static void RemoveSDcardFromEvents(SDCard card) + { + for (int i = 0; i < _sdCardList.Count; i++) + { + SDCard item = (SDCard)_sdCardList[i]; + + if (item.SlotIndex == card.SlotIndex) + { + _sdCardList.RemoveAt(i); + } + } + } + + internal static SDCard FindRegisteredEvent(uint slotIndex) + { + for (int i = 0; i < _sdCardList.Count; i++) + { + SDCard item = (SDCard)_sdCardList[i]; + if (item.SlotIndex == slotIndex) + { + return item; + } + } + return null; + } } }