diff --git a/Aaru.Devices/Aaru.Devices.csproj b/Aaru.Devices/Aaru.Devices.csproj index b22627029..7811699e2 100644 --- a/Aaru.Devices/Aaru.Devices.csproj +++ b/Aaru.Devices/Aaru.Devices.csproj @@ -56,78 +56,81 @@ false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - + + + + - + diff --git a/Aaru.Devices/Device/AtaCommands/Ata28.cs b/Aaru.Devices/Device/AtaCommands/Ata28.cs index af89f4914..9b1993a6c 100644 --- a/Aaru.Devices/Device/AtaCommands/Ata28.cs +++ b/Aaru.Devices/Device/AtaCommands/Ata28.cs @@ -36,7 +36,7 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -public sealed partial class Device +public partial class Device { /// Reads the drive buffer using PIO transfer /// Buffer that contains the read data diff --git a/Aaru.Devices/Device/AtaCommands/Ata48.cs b/Aaru.Devices/Device/AtaCommands/Ata48.cs index 633840aad..5a9ee5c99 100644 --- a/Aaru.Devices/Device/AtaCommands/Ata48.cs +++ b/Aaru.Devices/Device/AtaCommands/Ata48.cs @@ -36,7 +36,7 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -public sealed partial class Device +public partial class Device { /// Gets native max address using 48-bit addressing /// Maximum addressable block diff --git a/Aaru.Devices/Device/AtaCommands/AtaCHS.cs b/Aaru.Devices/Device/AtaCommands/AtaCHS.cs index e821da648..a00e6b686 100644 --- a/Aaru.Devices/Device/AtaCommands/AtaCHS.cs +++ b/Aaru.Devices/Device/AtaCommands/AtaCHS.cs @@ -36,7 +36,7 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -public sealed partial class Device +public partial class Device { /// Sends the ATA IDENTIFY DEVICE command to the device, using default device timeout /// true if the command failed and contains the error registers. diff --git a/Aaru.Devices/Device/AtaCommands/Atapi.cs b/Aaru.Devices/Device/AtaCommands/Atapi.cs index 322ce7456..6a87a5e75 100644 --- a/Aaru.Devices/Device/AtaCommands/Atapi.cs +++ b/Aaru.Devices/Device/AtaCommands/Atapi.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using Aaru.Console; using Aaru.Decoders.ATA; -public sealed partial class Device +public partial class Device { /// Sends the ATA IDENTIFY PACKET DEVICE command to the device, using default device timeout /// true if the command failed and contains the error registers. diff --git a/Aaru.Devices/Device/AtaCommands/Cfa.cs b/Aaru.Devices/Device/AtaCommands/Cfa.cs index 233a41ea9..8298b52fc 100644 --- a/Aaru.Devices/Device/AtaCommands/Cfa.cs +++ b/Aaru.Devices/Device/AtaCommands/Cfa.cs @@ -36,7 +36,7 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -public sealed partial class Device +public partial class Device { /// Requests to translate an LBA to a card physical address /// Data buffer diff --git a/Aaru.Devices/Device/AtaCommands/MCPT.cs b/Aaru.Devices/Device/AtaCommands/MCPT.cs index 5ebe080d1..dd74f25fe 100644 --- a/Aaru.Devices/Device/AtaCommands/MCPT.cs +++ b/Aaru.Devices/Device/AtaCommands/MCPT.cs @@ -36,7 +36,7 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -public sealed partial class Device +public partial class Device { /// Enables media card pass through /// Status registers. diff --git a/Aaru.Devices/Device/AtaCommands/Smart.cs b/Aaru.Devices/Device/AtaCommands/Smart.cs index 1438f4655..63983c6a5 100644 --- a/Aaru.Devices/Device/AtaCommands/Smart.cs +++ b/Aaru.Devices/Device/AtaCommands/Smart.cs @@ -36,7 +36,7 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -public sealed partial class Device +public partial class Device { /// Disables S.M.A.R.T. /// Returned status registers diff --git a/Aaru.Devices/Device/Commands.cs b/Aaru.Devices/Device/Commands.cs index 33f64d3fb..8a79de53c 100644 --- a/Aaru.Devices/Device/Commands.cs +++ b/Aaru.Devices/Device/Commands.cs @@ -37,7 +37,7 @@ using System.Diagnostics.CodeAnalysis; using Aaru.Decoders.ATA; [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] -public sealed partial class Device +public partial class Device { /// Sends a SCSI command to this device /// 0 if no error occurred, otherwise, errno diff --git a/Aaru.Devices/Device/Constructor.cs b/Aaru.Devices/Device/Constructor.cs index a49ecc8ed..a197f0c66 100644 --- a/Aaru.Devices/Device/Constructor.cs +++ b/Aaru.Devices/Device/Constructor.cs @@ -34,48 +34,26 @@ namespace Aaru.Devices; using System; using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; using System.Linq; using Aaru.CommonTypes.Enums; -using Aaru.CommonTypes.Interop; using Aaru.CommonTypes.Structs.Devices.ATA; using Aaru.CommonTypes.Structs.Devices.SCSI; using Aaru.Decoders.SCSI; using Aaru.Decoders.SCSI.MMC; -using Aaru.Decoders.SecureDigital; -using Aaru.Devices.Linux; -using Aaru.Devices.Windows; using Aaru.Helpers; -using Microsoft.Win32.SafeHandles; -using Extern = Aaru.Devices.Windows.Extern; -using FileAccess = Aaru.Devices.Windows.FileAccess; -using FileAttributes = Aaru.Devices.Windows.FileAttributes; -using FileMode = Aaru.Devices.Windows.FileMode; -using FileShare = Aaru.Devices.Windows.FileShare; using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry; -using Marshal = System.Runtime.InteropServices.Marshal; -using PlatformID = Aaru.CommonTypes.Interop.PlatformID; -using VendorString = Aaru.Decoders.SecureDigital.VendorString; /// Implements a device or media containing drive [SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global"), SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")] -public sealed partial class Device +public partial class Device { /// Opens the device for sending direct commands /// Device path public static Device Create(string devicePath) { - var dev = new Device(); - - dev.PlatformId = DetectOS.GetRealPlatformID(); - dev.Timeout = 15; - dev.Error = false; - dev.IsRemovable = false; - dev._devicePath = devicePath; - - Uri aaruUri; + Device dev = null; + Uri aaruUri; try { @@ -83,686 +61,24 @@ public sealed partial class Device } catch(Exception) { - // Ignore, treat as local path below - aaruUri = null; + return null; } - if(aaruUri?.Scheme is "dic" or "aaru") + if(aaruUri.Scheme is "dic" or "aaru") + dev = Remote.Device.Create(aaruUri); + else if(OperatingSystem.IsLinux()) + dev = Linux.Device.Create(devicePath); + else if(OperatingSystem.IsWindows()) + dev = Windows.Device.Create(devicePath); + + if(dev is null) + throw new DeviceException("Platform not supported."); + + if(dev.Type == DeviceType.SCSI || + dev.Type == DeviceType.ATAPI) { - devicePath = aaruUri.AbsolutePath; + dev.ScsiInquiry(out byte[] inqBuf, out _); - if(devicePath.StartsWith('/')) - devicePath = devicePath.Substring(1); - - if(devicePath.StartsWith("dev", StringComparison.Ordinal)) - devicePath = $"/{devicePath}"; - - dev._remote = new Remote.Remote(aaruUri); - - dev.Error = !dev._remote.Open(devicePath, out int errno); - dev.LastError = errno; - } - else - switch(dev.PlatformId) - { - case PlatformID.Win32NT: - { - dev.FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, - FileShare.Read | FileShare.Write, IntPtr.Zero, - FileMode.OpenExisting, FileAttributes.Normal, IntPtr.Zero); - - if(((SafeFileHandle)dev.FileHandle).IsInvalid) - { - dev.Error = true; - dev.LastError = Marshal.GetLastWin32Error(); - } - - break; - } - case PlatformID.Linux: - { - dev.FileHandle = - Linux.Extern.open(devicePath, - FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); - - if((int)dev.FileHandle < 0) - { - dev.LastError = Marshal.GetLastWin32Error(); - - if(dev.LastError is 13 or 30) // EACCES or EROFS - { - dev.FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); - - if((int)dev.FileHandle < 0) - { - dev.Error = true; - dev.LastError = Marshal.GetLastWin32Error(); - } - } - else - dev.Error = true; - - dev.LastError = Marshal.GetLastWin32Error(); - } - - break; - } - default: throw new DeviceException($"Platform {dev.PlatformId} not supported."); - } - - if(dev.Error) - throw new DeviceException(dev.LastError); - - // Seems ioctl(2) does not allow the atomicity needed - if(dev._remote is null) - { - if(dev.PlatformId == PlatformID.Linux) - _readMultipleBlockCannotSetBlockCount = true; - } - else if(dev._remote.ServerOperatingSystem == "Linux") - _readMultipleBlockCannotSetBlockCount = true; - - dev.Type = DeviceType.Unknown; - dev.ScsiType = PeripheralDeviceTypes.UnknownDevice; - - byte[] ataBuf; - byte[] inqBuf = null; - - if(dev.Error) - throw new DeviceException(dev.LastError); - - var scsiSense = true; - - if(dev._remote is null) - - // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first - switch(dev.PlatformId) - { - case PlatformID.Win32NT: - var query = new StoragePropertyQuery(); - query.PropertyId = StoragePropertyId.Device; - query.QueryType = StorageQueryType.Standard; - query.AdditionalParameters = new byte[1]; - - IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); - var descriptorB = new byte[1000]; - - uint returned = 0; - var error = 0; - - bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)dev.FileHandle, - WindowsIoctl.IoctlStorageQueryProperty, - ref query, (uint)Marshal.SizeOf(query), - descriptorPtr, 1000, ref returned, IntPtr.Zero); - - if(hasError) - error = Marshal.GetLastWin32Error(); - - Marshal.Copy(descriptorPtr, descriptorB, 0, 1000); - - if(!hasError && - error == 0) - { - var descriptor = new StorageDeviceDescriptor - { - Version = BitConverter.ToUInt32(descriptorB, 0), - Size = BitConverter.ToUInt32(descriptorB, 4), - DeviceType = descriptorB[8], - DeviceTypeModifier = descriptorB[9], - RemovableMedia = descriptorB[10] > 0, - CommandQueueing = descriptorB[11] > 0, - VendorIdOffset = BitConverter.ToInt32(descriptorB, 12), - ProductIdOffset = BitConverter.ToInt32(descriptorB, 16), - ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20), - SerialNumberOffset = BitConverter.ToInt32(descriptorB, 24), - BusType = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28), - RawPropertiesLength = BitConverter.ToUInt32(descriptorB, 32) - }; - - descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength]; - - Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0, descriptor.RawPropertiesLength); - - switch(descriptor.BusType) - { - case StorageBusType.SCSI: - case StorageBusType.SSA: - case StorageBusType.Fibre: - case StorageBusType.iSCSI: - case StorageBusType.SAS: - dev.Type = DeviceType.SCSI; - - break; - case StorageBusType.FireWire: - dev.IsFireWire = true; - dev.Type = DeviceType.SCSI; - - break; - case StorageBusType.USB: - dev.IsUsb = true; - dev.Type = DeviceType.SCSI; - - break; - case StorageBusType.ATAPI: - dev.Type = DeviceType.ATAPI; - - break; - case StorageBusType.ATA: - case StorageBusType.SATA: - dev.Type = DeviceType.ATA; - - break; - case StorageBusType.MultiMediaCard: - dev.Type = DeviceType.MMC; - - break; - case StorageBusType.SecureDigital: - dev.Type = DeviceType.SecureDigital; - - break; - case StorageBusType.NVMe: - dev.Type = DeviceType.NVMe; - - break; - } - - switch(dev.Type) - { - case DeviceType.SCSI: - case DeviceType.ATAPI: - scsiSense = dev.ScsiInquiry(out inqBuf, out _); - - break; - case DeviceType.ATA: - bool atapiSense = dev.AtapiIdentify(out ataBuf, out _); - - if(!atapiSense) - { - dev.Type = DeviceType.ATAPI; - Identify.IdentifyDevice? ataid = Identify.Decode(ataBuf); - - if(ataid.HasValue) - scsiSense = dev.ScsiInquiry(out inqBuf, out _); - } - else - dev.Manufacturer = "ATA"; - - break; - } - } - - Marshal.FreeHGlobal(descriptorPtr); - - if(Windows.Command.IsSdhci((SafeFileHandle)dev.FileHandle)) - { - var sdBuffer = new byte[16]; - - dev.LastError = Windows.Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, - MmcCommands.SendCsd, false, false, - MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | - MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, - out _, out _, out bool sense); - - if(!sense) - { - dev._cachedCsd = new byte[16]; - Array.Copy(sdBuffer, 0, dev._cachedCsd, 0, 16); - } - - sdBuffer = new byte[16]; - - dev.LastError = Windows.Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, - MmcCommands.SendCid, false, false, - MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | - MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, - out _, out _, out sense); - - if(!sense) - { - dev._cachedCid = new byte[16]; - Array.Copy(sdBuffer, 0, dev._cachedCid, 0, 16); - } - - sdBuffer = new byte[8]; - - dev.LastError = Windows.Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, - (MmcCommands)SecureDigitalCommands.SendScr, - false, true, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | - MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, - out _, out _, out sense); - - if(!sense) - { - dev._cachedScr = new byte[8]; - Array.Copy(sdBuffer, 0, dev._cachedScr, 0, 8); - } - - sdBuffer = new byte[4]; - - dev.LastError = Windows.Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, - dev._cachedScr != null - ? (MmcCommands)SecureDigitalCommands. - SendOperatingCondition - : MmcCommands.SendOpCond, false, true, - MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | - MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer, - out _, out _, out sense); - - if(!sense) - { - dev._cachedScr = new byte[4]; - Array.Copy(sdBuffer, 0, dev._cachedScr, 0, 4); - } - } - - break; - case PlatformID.Linux: - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sg", StringComparison.Ordinal)) - scsiSense = dev.ScsiInquiry(out inqBuf, out _); - - // MultiMediaCard and SecureDigital go here - else if(devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal)) - { - string devPath = devicePath.Substring(5); - - if(File.Exists("/sys/block/" + devPath + "/device/csd")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/csd", - out dev._cachedCsd); - - if(len == 0) - dev._cachedCsd = null; - } - - if(File.Exists("/sys/block/" + devPath + "/device/cid")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/cid", - out dev._cachedCid); - - if(len == 0) - dev._cachedCid = null; - } - - if(File.Exists("/sys/block/" + devPath + "/device/scr")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/scr", - out dev._cachedScr); - - if(len == 0) - dev._cachedScr = null; - } - - if(File.Exists("/sys/block/" + devPath + "/device/ocr")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/ocr", - out dev._cachedOcr); - - if(len == 0) - dev._cachedOcr = null; - } - } - - break; - default: - scsiSense = dev.ScsiInquiry(out inqBuf, out _); - - break; - } - else - { - dev.Type = dev._remote.GetDeviceType(); - - switch(dev.Type) - { - case DeviceType.ATAPI: - case DeviceType.SCSI: - scsiSense = dev.ScsiInquiry(out inqBuf, out _); - - break; - case DeviceType.SecureDigital: - case DeviceType.MMC: - if(!dev._remote.GetSdhciRegisters(out dev._cachedCsd, out dev._cachedCid, out dev._cachedOcr, - out dev._cachedScr)) - { - dev.Type = DeviceType.SCSI; - dev.ScsiType = PeripheralDeviceTypes.DirectAccess; - } - - break; - } - } - - #region SecureDigital / MultiMediaCard - if(dev._cachedCid != null) - { - dev.ScsiType = PeripheralDeviceTypes.DirectAccess; - dev.IsRemovable = false; - - if(dev._cachedScr != null) - { - dev.Type = DeviceType.SecureDigital; - CID decoded = Decoders.DecodeCID(dev._cachedCid); - dev.Manufacturer = VendorString.Prettify(decoded.Manufacturer); - dev.Model = decoded.ProductName; - - dev.FirmwareRevision = - $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; - - dev.Serial = $"{decoded.ProductSerialNumber}"; - } - else - { - dev.Type = DeviceType.MMC; - Aaru.Decoders.MMC.CID decoded = Aaru.Decoders.MMC.Decoders.DecodeCID(dev._cachedCid); - dev.Manufacturer = Aaru.Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); - dev.Model = decoded.ProductName; - - dev.FirmwareRevision = - $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; - - dev.Serial = $"{decoded.ProductSerialNumber}"; - } - - return dev; - } - #endregion SecureDigital / MultiMediaCard - - #region USB - if(dev._remote is null) - switch(dev.PlatformId) - { - case PlatformID.Linux: - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) - { - string devPath = devicePath.Substring(5); - - if(Directory.Exists("/sys/block/" + devPath)) - { - string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); - - if(!string.IsNullOrEmpty(resolvedLink)) - { - resolvedLink = "/sys" + resolvedLink.Substring(2); - - while(resolvedLink.Contains("usb")) - { - resolvedLink = Path.GetDirectoryName(resolvedLink); - - if(!File.Exists(resolvedLink + "/descriptors") || - !File.Exists(resolvedLink + "/idProduct") || - !File.Exists(resolvedLink + "/idVendor")) - continue; - - var usbFs = new FileStream(resolvedLink + "/descriptors", System.IO.FileMode.Open, - System.IO.FileAccess.Read); - - var usbBuf = new byte[65536]; - int usbCount = usbFs.Read(usbBuf, 0, 65536); - dev.UsbDescriptors = new byte[usbCount]; - Array.Copy(usbBuf, 0, dev.UsbDescriptors, 0, usbCount); - usbFs.Close(); - - var usbSr = new StreamReader(resolvedLink + "/idProduct"); - string usbTemp = usbSr.ReadToEnd(); - - ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out dev._usbProduct); - - usbSr.Close(); - - usbSr = new StreamReader(resolvedLink + "/idVendor"); - usbTemp = usbSr.ReadToEnd(); - - ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out dev._usbVendor); - - usbSr.Close(); - - if(File.Exists(resolvedLink + "/manufacturer")) - { - usbSr = new StreamReader(resolvedLink + "/manufacturer"); - dev.UsbManufacturerString = usbSr.ReadToEnd().Trim(); - usbSr.Close(); - } - - if(File.Exists(resolvedLink + "/product")) - { - usbSr = new StreamReader(resolvedLink + "/product"); - dev.UsbProductString = usbSr.ReadToEnd().Trim(); - usbSr.Close(); - } - - if(File.Exists(resolvedLink + "/serial")) - { - usbSr = new StreamReader(resolvedLink + "/serial"); - dev.UsbSerialString = usbSr.ReadToEnd().Trim(); - usbSr.Close(); - } - - dev.IsUsb = true; - - break; - } - } - } - } - - break; - case PlatformID.Win32NT: - Usb.UsbDevice usbDevice = null; - - // I have to search for USB disks, floppies and CD-ROMs as separate device types - foreach(string devGuid in new[] - { - Usb.GUID_DEVINTERFACE_FLOPPY, Usb.GUID_DEVINTERFACE_CDROM, Usb.GUID_DEVINTERFACE_DISK, - Usb.GUID_DEVINTERFACE_TAPE - }) - { - usbDevice = Usb.FindDrivePath(devicePath, devGuid); - - if(usbDevice != null) - break; - } - - if(usbDevice != null) - { - dev.UsbDescriptors = usbDevice.BinaryDescriptors; - dev._usbVendor = (ushort)usbDevice._deviceDescriptor.idVendor; - dev._usbProduct = (ushort)usbDevice._deviceDescriptor.idProduct; - dev.UsbManufacturerString = usbDevice.Manufacturer; - dev.UsbProductString = usbDevice.Product; - - dev.UsbSerialString = - usbDevice.SerialNumber; // This is incorrect filled by Windows with SCSI/ATA serial number - } - - break; - default: - dev.IsUsb = false; - - break; - } - else - { - if(dev._remote.GetUsbData(out byte[] remoteUsbDescriptors, out ushort remoteUsbVendor, - out ushort remoteUsbProduct, out string remoteUsbManufacturer, - out string remoteUsbProductString, out string remoteUsbSerial)) - { - dev.IsUsb = true; - dev.UsbDescriptors = remoteUsbDescriptors; - dev._usbVendor = remoteUsbVendor; - dev._usbProduct = remoteUsbProduct; - dev.UsbManufacturerString = remoteUsbManufacturer; - dev.UsbProductString = remoteUsbProductString; - dev.UsbSerialString = remoteUsbSerial; - } - } - #endregion USB - - #region FireWire - if(!(dev._remote is null)) - { - if(dev._remote.GetFireWireData(out dev._firewireVendor, out dev._firewireModel, out dev._firewireGuid, - out string remoteFireWireVendorName, out string remoteFireWireModelName)) - { - dev.IsFireWire = true; - dev.FireWireVendorName = remoteFireWireVendorName; - dev.FireWireModelName = remoteFireWireModelName; - } - } - else - { - if(dev.PlatformId == PlatformID.Linux) - { - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) - { - string devPath = devicePath.Substring(5); - - if(Directory.Exists("/sys/block/" + devPath)) - { - string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); - resolvedLink = "/sys" + resolvedLink.Substring(2); - - if(!string.IsNullOrEmpty(resolvedLink)) - while(resolvedLink.Contains("firewire")) - { - resolvedLink = Path.GetDirectoryName(resolvedLink); - - if(!File.Exists(resolvedLink + "/model") || - !File.Exists(resolvedLink + "/vendor") || - !File.Exists(resolvedLink + "/guid")) - continue; - - var fwSr = new StreamReader(resolvedLink + "/model"); - string fwTemp = fwSr.ReadToEnd(); - - uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out dev._firewireModel); - - fwSr.Close(); - - fwSr = new StreamReader(resolvedLink + "/vendor"); - fwTemp = fwSr.ReadToEnd(); - - uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out dev._firewireVendor); - - fwSr.Close(); - - fwSr = new StreamReader(resolvedLink + "/guid"); - fwTemp = fwSr.ReadToEnd(); - - ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out dev._firewireGuid); - - fwSr.Close(); - - if(File.Exists(resolvedLink + "/model_name")) - { - fwSr = new StreamReader(resolvedLink + "/model_name"); - dev.FireWireModelName = fwSr.ReadToEnd().Trim(); - fwSr.Close(); - } - - if(File.Exists(resolvedLink + "/vendor_name")) - { - fwSr = new StreamReader(resolvedLink + "/vendor_name"); - dev.FireWireVendorName = fwSr.ReadToEnd().Trim(); - fwSr.Close(); - } - - dev.IsFireWire = true; - - break; - } - } - } - } - - // TODO: Implement for other operating systems - else - dev.IsFireWire = false; - } - #endregion FireWire - - #region PCMCIA - if(dev._remote is null) - { - if(dev.PlatformId == PlatformID.Linux) - { - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) - { - string devPath = devicePath.Substring(5); - - if(Directory.Exists("/sys/block/" + devPath)) - { - string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); - resolvedLink = "/sys" + resolvedLink.Substring(2); - - if(!string.IsNullOrEmpty(resolvedLink)) - while(resolvedLink.Contains("/sys/devices")) - { - resolvedLink = Path.GetDirectoryName(resolvedLink); - - if(!Directory.Exists(resolvedLink + "/pcmcia_socket")) - continue; - - string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket", - "pcmcia_socket*", - SearchOption.TopDirectoryOnly); - - if(subdirs.Length <= 0) - continue; - - string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]); - - if(!File.Exists(possibleDir + "/card_type") || - !File.Exists(possibleDir + "/cis")) - continue; - - var cisFs = new FileStream(possibleDir + "/cis", System.IO.FileMode.Open, - System.IO.FileAccess.Read); - - var cisBuf = new byte[65536]; - int cisCount = cisFs.Read(cisBuf, 0, 65536); - dev.Cis = new byte[cisCount]; - Array.Copy(cisBuf, 0, dev.Cis, 0, cisCount); - cisFs.Close(); - - dev.IsPcmcia = true; - - break; - } - } - } - } - - // TODO: Implement for other operating systems - else - dev.IsPcmcia = false; - } - else - { - if(dev._remote.GetPcmciaData(out byte[] cisBuf)) - { - dev.IsPcmcia = true; - dev.Cis = cisBuf; - } - } - #endregion PCMCIA - - if(!scsiSense) - { Inquiry? inquiry = Inquiry.Decode(inqBuf); dev.Type = DeviceType.SCSI; @@ -793,7 +109,7 @@ public sealed partial class Device dev.ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType; } - bool atapiSense = dev.AtapiIdentify(out ataBuf, out _); + bool atapiSense = dev.AtapiIdentify(out byte[] ataBuf, out _); if(!atapiSense) { @@ -808,10 +124,10 @@ public sealed partial class Device dev.Error = false; } - if(scsiSense && !(dev.IsUsb || dev.IsFireWire) || + if(dev.Type != DeviceType.SCSI && dev.Type != DeviceType.ATAPI && !(dev.IsUsb || dev.IsFireWire) || dev.Manufacturer == "ATA") { - bool ataSense = dev.AtaIdentify(out ataBuf, out _); + bool ataSense = dev.AtaIdentify(out byte[] ataBuf, out _); if(!ataSense) { @@ -910,17 +226,5 @@ public sealed partial class Device return dev; } - Device() {} - - static int ConvertFromFileHexAscii(string file, out byte[] outBuf) - { - var sr = new StreamReader(file); - string ins = sr.ReadToEnd().Trim(); - - int count = Helpers.Marshal.ConvertFromHexAscii(ins, out outBuf); - - sr.Close(); - - return count; - } + protected Device() {} } \ No newline at end of file diff --git a/Aaru.Devices/Device/Destructor.cs b/Aaru.Devices/Device/Destructor.cs index 1b8ffa1dc..c2dd9be04 100644 --- a/Aaru.Devices/Device/Destructor.cs +++ b/Aaru.Devices/Device/Destructor.cs @@ -32,11 +32,7 @@ namespace Aaru.Devices; -using Aaru.CommonTypes.Interop; -using Aaru.Devices.Linux; -using Microsoft.Win32.SafeHandles; - -public sealed partial class Device +public partial class Device { /// /// Releases unmanaged resources and performs other cleanup operations before the is @@ -45,31 +41,5 @@ public sealed partial class Device ~Device() => Close(); /// Closes a device - public void Close() - { - if(_remote != null) - { - _remote.Close(); - _remote.Disconnect(); - - return; - } - - if(FileHandle == null) - return; - - switch(PlatformId) - { - case PlatformID.Win32NT: - (FileHandle as SafeFileHandle)?.Close(); - - break; - case PlatformID.Linux: - Extern.close((int)FileHandle); - - break; - } - - FileHandle = null; - } + public virtual void Close() {} } \ No newline at end of file diff --git a/Aaru.Devices/Device/List.cs b/Aaru.Devices/Device/List.cs index 0a60ad7fe..a1c222b5f 100644 --- a/Aaru.Devices/Device/List.cs +++ b/Aaru.Devices/Device/List.cs @@ -73,7 +73,7 @@ public struct DeviceInfo public readonly byte[] Padding; } -public sealed partial class Device +public partial class Device { /// Lists devices attached to current machine /// List of devices diff --git a/Aaru.Devices/Device/MmcCommands/MMC.cs b/Aaru.Devices/Device/MmcCommands/MMC.cs index 77ac7c05b..43de1d9c3 100644 --- a/Aaru.Devices/Device/MmcCommands/MMC.cs +++ b/Aaru.Devices/Device/MmcCommands/MMC.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Reads the CSD register from a SecureDigital or MultiMediaCard device /// Data buffer @@ -206,7 +206,7 @@ public sealed partial class Device return sense; } - static bool _readMultipleBlockCannotSetBlockCount; + protected static bool _readMultipleBlockCannotSetBlockCount; /// Reads multiple blocks from a SecureDigital or MultiMediaCard device /// Data buffer diff --git a/Aaru.Devices/Device/MmcCommands/SecureDigital.cs b/Aaru.Devices/Device/MmcCommands/SecureDigital.cs index 2be782035..8d72e28d6 100644 --- a/Aaru.Devices/Device/MmcCommands/SecureDigital.cs +++ b/Aaru.Devices/Device/MmcCommands/SecureDigital.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Reads the status register from a SecureDigital device /// Data buffer diff --git a/Aaru.Devices/Device/ScsiCommands/Adaptec.cs b/Aaru.Devices/Device/ScsiCommands/Adaptec.cs index 7e3335dd2..ad721c54e 100644 --- a/Aaru.Devices/Device/ScsiCommands/Adaptec.cs +++ b/Aaru.Devices/Device/ScsiCommands/Adaptec.cs @@ -36,7 +36,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. /// Buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs b/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs index 6da1cfe0f..a2372aa1b 100644 --- a/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs +++ b/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. /// Buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/Certance.cs b/Aaru.Devices/Device/ScsiCommands/Certance.cs index 2aba2048f..55bfaa5c8 100644 --- a/Aaru.Devices/Device/ScsiCommands/Certance.cs +++ b/Aaru.Devices/Device/ScsiCommands/Certance.cs @@ -37,7 +37,7 @@ using System.Diagnostics.CodeAnalysis; using Aaru.Console; [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] -public sealed partial class Device +public partial class Device { /// Parks the load arm in preparation for transport /// Sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs b/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs index c78ef013f..26f0fdb37 100644 --- a/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs +++ b/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs @@ -37,7 +37,7 @@ using System.Text; using Aaru.Console; using Aaru.Helpers; -public sealed partial class Device +public partial class Device { /// Sets the data for the integrated display /// Returned SENSE buffer diff --git a/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs b/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs index 4344bf8cc..b21f2b97a 100644 --- a/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs +++ b/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Reads a "raw" sector from DVD on HL-DT-ST drives. /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/HP.cs b/Aaru.Devices/Device/ScsiCommands/HP.cs index 3226d465a..82ea451cf 100644 --- a/Aaru.Devices/Device/ScsiCommands/HP.cs +++ b/Aaru.Devices/Device/ScsiCommands/HP.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sends the HP READ LONG vendor command /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/Kreon.cs b/Aaru.Devices/Device/ScsiCommands/Kreon.cs index ad94c2eb6..4e530528e 100644 --- a/Aaru.Devices/Device/ScsiCommands/Kreon.cs +++ b/Aaru.Devices/Device/ScsiCommands/Kreon.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sets the drive to the xtreme unlocked state /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/MMC.cs b/Aaru.Devices/Device/ScsiCommands/MMC.cs index 21c1c8e9c..98c31e8f8 100644 --- a/Aaru.Devices/Device/ScsiCommands/MMC.cs +++ b/Aaru.Devices/Device/ScsiCommands/MMC.cs @@ -37,7 +37,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sends the MMC GET CONFIGURATION command for all Features /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/MediaTek.cs b/Aaru.Devices/Device/ScsiCommands/MediaTek.cs index f4fb58923..584353f57 100644 --- a/Aaru.Devices/Device/ScsiCommands/MediaTek.cs +++ b/Aaru.Devices/Device/ScsiCommands/MediaTek.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Reads from the drive's DRAM. /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs b/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs index b433c66f3..0e10ff229 100644 --- a/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs +++ b/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs @@ -39,7 +39,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Reads the data TOC from an MD-DATA /// Buffer where the response will be stored diff --git a/Aaru.Devices/Device/ScsiCommands/NEC.cs b/Aaru.Devices/Device/ScsiCommands/NEC.cs index 7a69d704c..3fa30dfdc 100644 --- a/Aaru.Devices/Device/ScsiCommands/NEC.cs +++ b/Aaru.Devices/Device/ScsiCommands/NEC.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sends the NEC READ CD-DA command /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/Optical.cs b/Aaru.Devices/Device/ScsiCommands/Optical.cs index 13817d0d8..1fa238fa4 100644 --- a/Aaru.Devices/Device/ScsiCommands/Optical.cs +++ b/Aaru.Devices/Device/ScsiCommands/Optical.cs @@ -36,7 +36,7 @@ using System; using Aaru.Console; using Aaru.Decoders.SCSI; -public sealed partial class Device +public partial class Device { /// Scan the medium for a contiguous set of written or blank logical blocks /// Sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/Pioneer.cs b/Aaru.Devices/Device/ScsiCommands/Pioneer.cs index e8608cefb..1f80cdd8f 100644 --- a/Aaru.Devices/Device/ScsiCommands/Pioneer.cs +++ b/Aaru.Devices/Device/ScsiCommands/Pioneer.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sends the Pioneer READ CD-DA command /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/Plasmon.cs b/Aaru.Devices/Device/ScsiCommands/Plasmon.cs index c6ff39d7a..2455d1d08 100644 --- a/Aaru.Devices/Device/ScsiCommands/Plasmon.cs +++ b/Aaru.Devices/Device/ScsiCommands/Plasmon.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sends the Plasmon READ LONG vendor command /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/Plextor.cs b/Aaru.Devices/Device/ScsiCommands/Plextor.cs index 4f35c2c26..86fba3685 100644 --- a/Aaru.Devices/Device/ScsiCommands/Plextor.cs +++ b/Aaru.Devices/Device/ScsiCommands/Plextor.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using Aaru.Console; using Aaru.Helpers; -public sealed partial class Device +public partial class Device { /// Sends the Plextor READ CD-DA command /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/SBC.cs b/Aaru.Devices/Device/ScsiCommands/SBC.cs index 2ee5d6fa4..9a093eccb 100644 --- a/Aaru.Devices/Device/ScsiCommands/SBC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SBC.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sends the SBC READ (6) command /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/SMC.cs b/Aaru.Devices/Device/ScsiCommands/SMC.cs index 423aab663..ce15f3542 100644 --- a/Aaru.Devices/Device/ScsiCommands/SMC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SMC.cs @@ -34,7 +34,7 @@ namespace Aaru.Devices; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Reads an attribute from the medium auxiliary memory, or reports which elements in the changer contain one /// Buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/SPC.cs b/Aaru.Devices/Device/ScsiCommands/SPC.cs index d9bc7ccd9..0cdefbaa3 100644 --- a/Aaru.Devices/Device/ScsiCommands/SPC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SPC.cs @@ -38,7 +38,7 @@ using Aaru.Console; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] -public sealed partial class Device +public partial class Device { /// Sends the SPC INQUIRY command to the device using default device timeout. /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/ScsiCommands/SSC.cs b/Aaru.Devices/Device/ScsiCommands/SSC.cs index e28b2a244..da407f366 100644 --- a/Aaru.Devices/Device/ScsiCommands/SSC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SSC.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Prepares the medium for reading /// true, if load was successful, false otherwise. diff --git a/Aaru.Devices/Device/ScsiCommands/SyQuest.cs b/Aaru.Devices/Device/ScsiCommands/SyQuest.cs index a074b8e18..d4e14ce25 100644 --- a/Aaru.Devices/Device/ScsiCommands/SyQuest.cs +++ b/Aaru.Devices/Device/ScsiCommands/SyQuest.cs @@ -35,7 +35,7 @@ namespace Aaru.Devices; using System; using Aaru.Console; -public sealed partial class Device +public partial class Device { /// Sends the SyQuest READ (6) command /// true if the command failed and contains the sense buffer. diff --git a/Aaru.Devices/Device/Variables.cs b/Aaru.Devices/Device/Variables.cs index 2bed57d19..7e1d5dac6 100644 --- a/Aaru.Devices/Device/Variables.cs +++ b/Aaru.Devices/Device/Variables.cs @@ -36,28 +36,28 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interop; using Aaru.CommonTypes.Structs.Devices.SCSI; -public sealed partial class Device +public partial class Device { - ushort _usbVendor; - ushort _usbProduct; - ulong _firewireGuid; - uint _firewireModel; - uint _firewireVendor; + private protected ushort _usbVendor; + private protected ushort _usbProduct; + private protected ulong _firewireGuid; + private protected uint _firewireModel; + private protected uint _firewireVendor; // MMC and SecureDigital, values that need to be get with card idle, something that may // not be possible to do but usually is already done by the SDHCI driver. - byte[] _cachedCsd; - byte[] _cachedCid; - byte[] _cachedScr; - byte[] _cachedOcr; + private protected byte[] _cachedCsd; + private protected byte[] _cachedCid; + private protected byte[] _cachedScr; + private protected byte[] _cachedOcr; /// Gets the Platform ID for this device /// The Platform ID - public PlatformID PlatformId { get; private set; } + public PlatformID PlatformId { get; private protected set; } /// Gets the file handle representing this device /// The file handle - public object FileHandle { get; private set; } + public object FileHandle { get; private protected set; } /// Gets or sets the standard timeout for commands sent to this device /// The timeout in seconds @@ -65,43 +65,43 @@ public sealed partial class Device /// Gets a value indicating whether this is in error. /// true if error; otherwise, false. - public bool Error { get; private set; } + public bool Error { get; private protected set; } /// Gets the last error number. /// The last error. - public int LastError { get; private set; } + public int LastError { get; private protected set; } /// Gets the device type. /// The device type. - public DeviceType Type { get; private set; } + public DeviceType Type { get; private protected set; } /// Gets the device's manufacturer /// The manufacturer. - public string Manufacturer { get; private set; } + public string Manufacturer { get; private protected set; } /// Gets the device model /// The model. - public string Model { get; private set; } + public string Model { get; private protected set; } /// Gets the device's firmware version. /// The firmware version. - public string FirmwareRevision { get; private set; } + public string FirmwareRevision { get; private protected set; } /// Gets the device's serial number. /// The serial number. - public string Serial { get; private set; } + public string Serial { get; private protected set; } /// Gets the device's SCSI peripheral device type /// The SCSI peripheral device type. - public PeripheralDeviceTypes ScsiType { get; private set; } + public PeripheralDeviceTypes ScsiType { get; private protected set; } /// Gets a value indicating whether this device's media is removable. /// true if this device's media is removable; otherwise, false. - public bool IsRemovable { get; private set; } + public bool IsRemovable { get; private protected set; } /// Gets a value indicating whether this device is attached via USB. /// true if this device is attached via USB; otherwise, false. - public bool IsUsb { get; private set; } + public bool IsUsb { get; private protected set; } /// Gets the USB vendor ID. /// The USB vendor ID. @@ -113,23 +113,23 @@ public sealed partial class Device /// Gets the USB descriptors. /// The USB descriptors. - public byte[] UsbDescriptors { get; private set; } + public byte[] UsbDescriptors { get; private protected set; } /// Gets the USB manufacturer string. /// The USB manufacturer string. - public string UsbManufacturerString { get; private set; } + public string UsbManufacturerString { get; private protected set; } /// Gets the USB product string. /// The USB product string. - public string UsbProductString { get; private set; } + public string UsbProductString { get; private protected set; } /// Gets the USB serial string. /// The USB serial string. - public string UsbSerialString { get; private set; } + public string UsbSerialString { get; private protected set; } /// Gets a value indicating whether this device is attached via FireWire. /// true if this device is attached via FireWire; otherwise, false. - public bool IsFireWire { get; private set; } + public bool IsFireWire { get; private protected set; } /// Gets the FireWire GUID /// The FireWire GUID. @@ -141,7 +141,7 @@ public sealed partial class Device /// Gets the FireWire model name. /// The FireWire model name. - public string FireWireModelName { get; private set; } + public string FireWireModelName { get; private protected set; } /// Gets the FireWire vendor number. /// The FireWire vendor number. @@ -149,7 +149,7 @@ public sealed partial class Device /// Gets the FireWire vendor name. /// The FireWire vendor name. - public string FireWireVendorName { get; private set; } + public string FireWireVendorName { get; private protected set; } /// Gets a value indicating whether this device is a CompactFlash device. /// true if this device is a CompactFlash device; otherwise, false. @@ -157,14 +157,14 @@ public sealed partial class Device /// Gets a value indicating whether this device is a PCMCIA device. /// true if this device is a PCMCIA device; otherwise, false. - public bool IsPcmcia { get; private set; } + public bool IsPcmcia { get; private protected set; } /// Contains the PCMCIA CIS if applicable - public byte[] Cis { get; private set; } + public byte[] Cis { get; private protected set; } - Remote.Remote _remote; - bool? _isRemoteAdmin; - string _devicePath; + private protected Remote.Remote _remote; + bool? _isRemoteAdmin; + private protected string _devicePath; /// Returns if remote is running under administrative (aka root) privileges public bool IsRemoteAdmin diff --git a/Aaru.Devices/Linux/Device.cs b/Aaru.Devices/Linux/Device.cs new file mode 100644 index 000000000..4eb3d11ca --- /dev/null +++ b/Aaru.Devices/Linux/Device.cs @@ -0,0 +1,409 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : Device.cs +// Author(s) : Natalia Portillo +// +// Component : Linux direct device access. +// +// --[ Description ] ---------------------------------------------------------- +// +// Prepares a Linux device for direct access. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2022 Natalia Portillo +// ****************************************************************************/ + +namespace Aaru.Devices.Linux; + +using System; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interop; +using Aaru.CommonTypes.Structs.Devices.SCSI; +using Aaru.Decoders.SecureDigital; +using PlatformID = Aaru.CommonTypes.Interop.PlatformID; +using VendorString = Aaru.Decoders.MMC.VendorString; + +/// +[SupportedOSPlatform("linux")] +public class Device : Devices.Device +{ + Device() {} + + public new static Device Create(string devicePath) + { + var dev = new Device + { + PlatformId = DetectOS.GetRealPlatformID(), + Timeout = 15, + Error = false, + IsRemovable = false + }; + + dev.FileHandle = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); + + if((int)dev.FileHandle < 0) + { + dev.LastError = Marshal.GetLastWin32Error(); + + if(dev.LastError is 13 or 30) // EACCES or EROFS + { + dev.FileHandle = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); + + if((int)dev.FileHandle < 0) + { + dev.Error = true; + dev.LastError = Marshal.GetLastWin32Error(); + } + } + else + dev.Error = true; + + dev.LastError = Marshal.GetLastWin32Error(); + } + + if(dev.Error) + throw new DeviceException(dev.LastError); + + // Seems ioctl(2) does not allow the atomicity needed + if(dev.PlatformId == PlatformID.Linux) + _readMultipleBlockCannotSetBlockCount = true; + + dev.Type = DeviceType.Unknown; + dev.ScsiType = PeripheralDeviceTypes.UnknownDevice; + + byte[] ataBuf; + byte[] inqBuf = null; + + if(dev.Error) + throw new DeviceException(dev.LastError); + + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sg", StringComparison.Ordinal)) + if(!dev.ScsiInquiry(out inqBuf, out _)) + dev.Type = DeviceType.SCSI; + + // MultiMediaCard and SecureDigital go here + else if(devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal)) + { + string devPath = devicePath.Substring(5); + + if(File.Exists("/sys/block/" + devPath + "/device/csd")) + { + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/csd", out dev._cachedCsd); + + if(len == 0) + dev._cachedCsd = null; + } + + if(File.Exists("/sys/block/" + devPath + "/device/cid")) + { + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/cid", out dev._cachedCid); + + if(len == 0) + dev._cachedCid = null; + } + + if(File.Exists("/sys/block/" + devPath + "/device/scr")) + { + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/scr", out dev._cachedScr); + + if(len == 0) + dev._cachedScr = null; + } + + if(File.Exists("/sys/block/" + devPath + "/device/ocr")) + { + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/ocr", out dev._cachedOcr); + + if(len == 0) + dev._cachedOcr = null; + } + } + + #region SecureDigital / MultiMediaCard + if(dev._cachedCid != null) + { + dev.ScsiType = PeripheralDeviceTypes.DirectAccess; + dev.IsRemovable = false; + + if(dev._cachedScr != null) + { + dev.Type = DeviceType.SecureDigital; + CID decoded = Decoders.DecodeCID(dev._cachedCid); + dev.Manufacturer = VendorString.Prettify(decoded.Manufacturer); + dev.Model = decoded.ProductName; + + dev.FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + dev.Serial = $"{decoded.ProductSerialNumber}"; + } + else + { + dev.Type = DeviceType.MMC; + Aaru.Decoders.MMC.CID decoded = Aaru.Decoders.MMC.Decoders.DecodeCID(dev._cachedCid); + dev.Manufacturer = VendorString.Prettify(decoded.Manufacturer); + dev.Model = decoded.ProductName; + + dev.FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + dev.Serial = $"{decoded.ProductSerialNumber}"; + } + + return dev; + } + #endregion SecureDigital / MultiMediaCard + + #region USB + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + { + string devPath = devicePath.Substring(5); + + if(Directory.Exists("/sys/block/" + devPath)) + { + string resolvedLink = Command.ReadLink("/sys/block/" + devPath); + + if(!string.IsNullOrEmpty(resolvedLink)) + { + resolvedLink = "/sys" + resolvedLink.Substring(2); + + while(resolvedLink.Contains("usb")) + { + resolvedLink = Path.GetDirectoryName(resolvedLink); + + if(!File.Exists(resolvedLink + "/descriptors") || + !File.Exists(resolvedLink + "/idProduct") || + !File.Exists(resolvedLink + "/idVendor")) + continue; + + var usbFs = new FileStream(resolvedLink + "/descriptors", FileMode.Open, FileAccess.Read); + + var usbBuf = new byte[65536]; + int usbCount = usbFs.Read(usbBuf, 0, 65536); + dev.UsbDescriptors = new byte[usbCount]; + Array.Copy(usbBuf, 0, dev.UsbDescriptors, 0, usbCount); + usbFs.Close(); + + var usbSr = new StreamReader(resolvedLink + "/idProduct"); + string usbTemp = usbSr.ReadToEnd(); + + ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out dev._usbProduct); + + usbSr.Close(); + + usbSr = new StreamReader(resolvedLink + "/idVendor"); + usbTemp = usbSr.ReadToEnd(); + + ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out dev._usbVendor); + + usbSr.Close(); + + if(File.Exists(resolvedLink + "/manufacturer")) + { + usbSr = new StreamReader(resolvedLink + "/manufacturer"); + dev.UsbManufacturerString = usbSr.ReadToEnd().Trim(); + usbSr.Close(); + } + + if(File.Exists(resolvedLink + "/product")) + { + usbSr = new StreamReader(resolvedLink + "/product"); + dev.UsbProductString = usbSr.ReadToEnd().Trim(); + usbSr.Close(); + } + + if(File.Exists(resolvedLink + "/serial")) + { + usbSr = new StreamReader(resolvedLink + "/serial"); + dev.UsbSerialString = usbSr.ReadToEnd().Trim(); + usbSr.Close(); + } + + dev.IsUsb = true; + + break; + } + } + } + } + #endregion USB + + #region FireWire + { + if(true) + { + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + { + string devPath = devicePath.Substring(5); + + if(Directory.Exists("/sys/block/" + devPath)) + { + string resolvedLink = Command.ReadLink("/sys/block/" + devPath); + resolvedLink = "/sys" + resolvedLink.Substring(2); + + if(!string.IsNullOrEmpty(resolvedLink)) + while(resolvedLink.Contains("firewire")) + { + resolvedLink = Path.GetDirectoryName(resolvedLink); + + if(!File.Exists(resolvedLink + "/model") || + !File.Exists(resolvedLink + "/vendor") || + !File.Exists(resolvedLink + "/guid")) + continue; + + var fwSr = new StreamReader(resolvedLink + "/model"); + string fwTemp = fwSr.ReadToEnd(); + + uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out dev._firewireModel); + + fwSr.Close(); + + fwSr = new StreamReader(resolvedLink + "/vendor"); + fwTemp = fwSr.ReadToEnd(); + + uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out dev._firewireVendor); + + fwSr.Close(); + + fwSr = new StreamReader(resolvedLink + "/guid"); + fwTemp = fwSr.ReadToEnd(); + + ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out dev._firewireGuid); + + fwSr.Close(); + + if(File.Exists(resolvedLink + "/model_name")) + { + fwSr = new StreamReader(resolvedLink + "/model_name"); + dev.FireWireModelName = fwSr.ReadToEnd().Trim(); + fwSr.Close(); + } + + if(File.Exists(resolvedLink + "/vendor_name")) + { + fwSr = new StreamReader(resolvedLink + "/vendor_name"); + dev.FireWireVendorName = fwSr.ReadToEnd().Trim(); + fwSr.Close(); + } + + dev.IsFireWire = true; + + break; + } + } + } + } + + // TODO: Implement for other operating systems + else + dev.IsFireWire = false; + } + #endregion FireWire + + #region PCMCIA + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + { + string devPath = devicePath.Substring(5); + + if(Directory.Exists("/sys/block/" + devPath)) + { + string resolvedLink = Command.ReadLink("/sys/block/" + devPath); + resolvedLink = "/sys" + resolvedLink.Substring(2); + + if(!string.IsNullOrEmpty(resolvedLink)) + while(resolvedLink.Contains("/sys/devices")) + { + resolvedLink = Path.GetDirectoryName(resolvedLink); + + if(!Directory.Exists(resolvedLink + "/pcmcia_socket")) + continue; + + string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket", "pcmcia_socket*", + SearchOption.TopDirectoryOnly); + + if(subdirs.Length <= 0) + continue; + + string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]); + + if(!File.Exists(possibleDir + "/card_type") || + !File.Exists(possibleDir + "/cis")) + continue; + + var cisFs = new FileStream(possibleDir + "/cis", FileMode.Open, FileAccess.Read); + + var cisBuf = new byte[65536]; + int cisCount = cisFs.Read(cisBuf, 0, 65536); + dev.Cis = new byte[cisCount]; + Array.Copy(cisBuf, 0, dev.Cis, 0, cisCount); + cisFs.Close(); + + dev.IsPcmcia = true; + + break; + } + } + } + #endregion PCMCIA + + return dev; + } + + static int ConvertFromFileHexAscii(string file, out byte[] outBuf) + { + var sr = new StreamReader(file); + string ins = sr.ReadToEnd().Trim(); + + int count = Helpers.Marshal.ConvertFromHexAscii(ins, out outBuf); + + sr.Close(); + + return count; + } + + /// + public override void Close() + { + if(FileHandle == null) + return; + + Extern.close((int)FileHandle); + + FileHandle = null; + } +} \ No newline at end of file diff --git a/Aaru.Devices/Remote/Device.cs b/Aaru.Devices/Remote/Device.cs new file mode 100644 index 000000000..74482aeb8 --- /dev/null +++ b/Aaru.Devices/Remote/Device.cs @@ -0,0 +1,192 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : Device.cs +// Author(s) : Natalia Portillo +// +// Component : Remote device access. +// +// --[ Description ] ---------------------------------------------------------- +// +// Prepares a remote device for direct access. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2022 Natalia Portillo +// ****************************************************************************/ + +namespace Aaru.Devices.Remote; + +using System; +using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interop; +using Aaru.CommonTypes.Structs.Devices.SCSI; +using Aaru.Decoders.SecureDigital; + +/// +public sealed class Device : Devices.Device +{ + Device() {} + + /// Opens the device for sending direct commands + /// AaruRemote URI + /// Device + public static Device Create(Uri aaruUri) + { + var dev = new Device + { + PlatformId = DetectOS.GetRealPlatformID(), + Timeout = 15, + Error = false, + IsRemovable = false + }; + + if(aaruUri.Scheme is not ("dic" or "aaru")) + return null; + + string devicePath = aaruUri.AbsolutePath; + + if(devicePath.StartsWith('/')) + devicePath = devicePath[1..]; + + if(devicePath.StartsWith("dev", StringComparison.Ordinal)) + devicePath = $"/{devicePath}"; + + dev._devicePath = devicePath; + + dev._remote = new Remote(aaruUri); + + dev.Error = !dev._remote.Open(devicePath, out int errno); + dev.LastError = errno; + + if(dev.Error) + throw new DeviceException(dev.LastError); + + if(dev._remote.ServerOperatingSystem == "Linux") + _readMultipleBlockCannotSetBlockCount = true; + + dev.Type = DeviceType.Unknown; + dev.ScsiType = PeripheralDeviceTypes.UnknownDevice; + + byte[] ataBuf; + byte[] inqBuf = null; + + if(dev.Error) + throw new DeviceException(dev.LastError); + + dev.Type = dev._remote.GetDeviceType(); + + switch(dev.Type) + { + case DeviceType.ATAPI: + case DeviceType.SCSI: + bool scsiSense = dev.ScsiInquiry(out inqBuf, out _); + + break; + case DeviceType.SecureDigital: + case DeviceType.MMC: + if(!dev._remote.GetSdhciRegisters(out dev._cachedCsd, out dev._cachedCid, out dev._cachedOcr, + out dev._cachedScr)) + { + dev.Type = DeviceType.SCSI; + dev.ScsiType = PeripheralDeviceTypes.DirectAccess; + } + + break; + } + + #region SecureDigital / MultiMediaCard + if(dev._cachedCid != null) + { + dev.ScsiType = PeripheralDeviceTypes.DirectAccess; + dev.IsRemovable = false; + + if(dev._cachedScr != null) + { + dev.Type = DeviceType.SecureDigital; + CID decoded = Decoders.DecodeCID(dev._cachedCid); + dev.Manufacturer = VendorString.Prettify(decoded.Manufacturer); + dev.Model = decoded.ProductName; + + dev.FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + dev.Serial = $"{decoded.ProductSerialNumber}"; + } + else + { + dev.Type = DeviceType.MMC; + Aaru.Decoders.MMC.CID decoded = Aaru.Decoders.MMC.Decoders.DecodeCID(dev._cachedCid); + dev.Manufacturer = Aaru.Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); + dev.Model = decoded.ProductName; + + dev.FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + dev.Serial = $"{decoded.ProductSerialNumber}"; + } + + return dev; + } + #endregion SecureDigital / MultiMediaCard + + #region USB + if(dev._remote.GetUsbData(out byte[] remoteUsbDescriptors, out ushort remoteUsbVendor, + out ushort remoteUsbProduct, out string remoteUsbManufacturer, + out string remoteUsbProductString, out string remoteUsbSerial)) + { + dev.IsUsb = true; + dev.UsbDescriptors = remoteUsbDescriptors; + dev._usbVendor = remoteUsbVendor; + dev._usbProduct = remoteUsbProduct; + dev.UsbManufacturerString = remoteUsbManufacturer; + dev.UsbProductString = remoteUsbProductString; + dev.UsbSerialString = remoteUsbSerial; + } + #endregion USB + + #region FireWire + if(dev._remote.GetFireWireData(out dev._firewireVendor, out dev._firewireModel, out dev._firewireGuid, + out string remoteFireWireVendorName, out string remoteFireWireModelName)) + { + dev.IsFireWire = true; + dev.FireWireVendorName = remoteFireWireVendorName; + dev.FireWireModelName = remoteFireWireModelName; + } + #endregion FireWire + #region PCMCIA + if(dev._remote.GetPcmciaData(out byte[] cisBuf)) + { + dev.IsPcmcia = true; + dev.Cis = cisBuf; + } + #endregion PCMCIA + + return dev; + } + + /// + public override void Close() + { + if(_remote == null) + return; + + _remote.Close(); + _remote.Disconnect(); + } +} \ No newline at end of file diff --git a/Aaru.Devices/Windows/Device.cs b/Aaru.Devices/Windows/Device.cs new file mode 100644 index 000000000..b2edaeece --- /dev/null +++ b/Aaru.Devices/Windows/Device.cs @@ -0,0 +1,328 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : Device.cs +// Author(s) : Natalia Portillo +// +// Component : Windows direct device access. +// +// --[ Description ] ---------------------------------------------------------- +// +// Prepares a Windows device for direct access. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2022 Natalia Portillo +// ****************************************************************************/ + +namespace Aaru.Devices.Windows; + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interop; +using Aaru.CommonTypes.Structs.Devices.SCSI; +using Aaru.Decoders.SecureDigital; +using Microsoft.Win32.SafeHandles; + +/// +[SupportedOSPlatform("windows")] +public class Device : Devices.Device +{ + Device() {} + + public new static Device Create(string devicePath) + { + var dev = new Device + { + PlatformId = DetectOS.GetRealPlatformID(), + Timeout = 15, + Error = false, + IsRemovable = false + }; + + dev.FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, + FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, + FileAttributes.Normal, IntPtr.Zero); + + if(((SafeFileHandle)dev.FileHandle).IsInvalid) + { + dev.Error = true; + dev.LastError = Marshal.GetLastWin32Error(); + } + + if(dev.Error) + throw new DeviceException(dev.LastError); + + dev.Type = DeviceType.Unknown; + dev.ScsiType = PeripheralDeviceTypes.UnknownDevice; + + byte[] ataBuf; + byte[] inqBuf = null; + + if(dev.Error) + throw new DeviceException(dev.LastError); + + // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first + var query = new StoragePropertyQuery(); + query.PropertyId = StoragePropertyId.Device; + query.QueryType = StorageQueryType.Standard; + query.AdditionalParameters = new byte[1]; + + IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); + var descriptorB = new byte[1000]; + + uint returned = 0; + var error = 0; + + bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)dev.FileHandle, + WindowsIoctl.IoctlStorageQueryProperty, ref query, + (uint)Marshal.SizeOf(query), descriptorPtr, 1000, + ref returned, IntPtr.Zero); + + if(hasError) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(descriptorPtr, descriptorB, 0, 1000); + + if(!hasError && + error == 0) + { + var descriptor = new StorageDeviceDescriptor + { + Version = BitConverter.ToUInt32(descriptorB, 0), + Size = BitConverter.ToUInt32(descriptorB, 4), + DeviceType = descriptorB[8], + DeviceTypeModifier = descriptorB[9], + RemovableMedia = descriptorB[10] > 0, + CommandQueueing = descriptorB[11] > 0, + VendorIdOffset = BitConverter.ToInt32(descriptorB, 12), + ProductIdOffset = BitConverter.ToInt32(descriptorB, 16), + ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20), + SerialNumberOffset = BitConverter.ToInt32(descriptorB, 24), + BusType = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28), + RawPropertiesLength = BitConverter.ToUInt32(descriptorB, 32) + }; + + descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength]; + + Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0, descriptor.RawPropertiesLength); + + switch(descriptor.BusType) + { + case StorageBusType.SCSI: + case StorageBusType.SSA: + case StorageBusType.Fibre: + case StorageBusType.iSCSI: + case StorageBusType.SAS: + dev.Type = DeviceType.SCSI; + + break; + case StorageBusType.FireWire: + dev.IsFireWire = true; + dev.Type = DeviceType.SCSI; + + break; + case StorageBusType.USB: + dev.IsUsb = true; + dev.Type = DeviceType.SCSI; + + break; + case StorageBusType.ATAPI: + dev.Type = DeviceType.ATAPI; + + break; + case StorageBusType.ATA: + case StorageBusType.SATA: + dev.Type = DeviceType.ATA; + + break; + case StorageBusType.MultiMediaCard: + dev.Type = DeviceType.MMC; + + break; + case StorageBusType.SecureDigital: + dev.Type = DeviceType.SecureDigital; + + break; + case StorageBusType.NVMe: + dev.Type = DeviceType.NVMe; + + break; + } + + switch(dev.Type) + { + case DeviceType.ATA: + bool atapiSense = dev.AtapiIdentify(out ataBuf, out _); + + if(!atapiSense) + dev.Type = DeviceType.ATAPI; + else + dev.Manufacturer = "ATA"; + + break; + } + } + + Marshal.FreeHGlobal(descriptorPtr); + + if(Command.IsSdhci((SafeFileHandle)dev.FileHandle)) + { + var sdBuffer = new byte[16]; + + dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, MmcCommands.SendCsd, false, false, + MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, + 16, 1, ref sdBuffer, out _, out _, out bool sense); + + if(!sense) + { + dev._cachedCsd = new byte[16]; + Array.Copy(sdBuffer, 0, dev._cachedCsd, 0, 16); + } + + sdBuffer = new byte[16]; + + dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, MmcCommands.SendCid, false, false, + MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, + 16, 1, ref sdBuffer, out _, out _, out sense); + + if(!sense) + { + dev._cachedCid = new byte[16]; + Array.Copy(sdBuffer, 0, dev._cachedCid, 0, 16); + } + + sdBuffer = new byte[8]; + + dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, + (MmcCommands)SecureDigitalCommands.SendScr, false, true, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, + 0, 8, 1, ref sdBuffer, out _, out _, out sense); + + if(!sense) + { + dev._cachedScr = new byte[8]; + Array.Copy(sdBuffer, 0, dev._cachedScr, 0, 8); + } + + sdBuffer = new byte[4]; + + dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, + dev._cachedScr != null + ? (MmcCommands)SecureDigitalCommands.SendOperatingCondition + : MmcCommands.SendOpCond, false, true, + MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, + 0, 4, 1, ref sdBuffer, out _, out _, out sense); + + if(!sense) + { + dev._cachedScr = new byte[4]; + Array.Copy(sdBuffer, 0, dev._cachedScr, 0, 4); + } + } + + #region SecureDigital / MultiMediaCard + if(dev._cachedCid != null) + { + dev.ScsiType = PeripheralDeviceTypes.DirectAccess; + dev.IsRemovable = false; + + if(dev._cachedScr != null) + { + dev.Type = DeviceType.SecureDigital; + CID decoded = Decoders.DecodeCID(dev._cachedCid); + dev.Manufacturer = VendorString.Prettify(decoded.Manufacturer); + dev.Model = decoded.ProductName; + + dev.FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + dev.Serial = $"{decoded.ProductSerialNumber}"; + } + else + { + dev.Type = DeviceType.MMC; + Aaru.Decoders.MMC.CID decoded = Aaru.Decoders.MMC.Decoders.DecodeCID(dev._cachedCid); + dev.Manufacturer = Aaru.Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); + dev.Model = decoded.ProductName; + + dev.FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + dev.Serial = $"{decoded.ProductSerialNumber}"; + } + + return dev; + } + #endregion SecureDigital / MultiMediaCard + + #region USB + Usb.UsbDevice usbDevice = null; + + // I have to search for USB disks, floppies and CD-ROMs as separate device types + foreach(string devGuid in new[] + { + Usb.GUID_DEVINTERFACE_FLOPPY, Usb.GUID_DEVINTERFACE_CDROM, Usb.GUID_DEVINTERFACE_DISK, + Usb.GUID_DEVINTERFACE_TAPE + }) + { + usbDevice = Usb.FindDrivePath(devicePath, devGuid); + + if(usbDevice != null) + break; + } + + if(usbDevice != null) + { + dev.UsbDescriptors = usbDevice.BinaryDescriptors; + dev._usbVendor = (ushort)usbDevice._deviceDescriptor.idVendor; + dev._usbProduct = (ushort)usbDevice._deviceDescriptor.idProduct; + dev.UsbManufacturerString = usbDevice.Manufacturer; + dev.UsbProductString = usbDevice.Product; + + dev.UsbSerialString = + usbDevice.SerialNumber; // This is incorrect filled by Windows with SCSI/ATA serial number + } + #endregion USB + + #region FireWire + // TODO: Implement + + dev.IsFireWire = false; + #endregion FireWire + + #region PCMCIA + // TODO: Implement + #endregion PCMCIA + + return dev; + } + + /// + public override void Close() + { + if(FileHandle == null) + return; + + (FileHandle as SafeFileHandle)?.Close(); + + FileHandle = null; + } +} \ No newline at end of file