/// <summary> /// Returns true if the specified handle is controlled by a SFFDISK (aka SDHCI) driver /// </summary> /// <param name="fd">Device handle</param> /// <returns><c>true</c> if SDHCI, false otherwise</returns> internal static bool IsSdhci(SafeFileHandle fd) { SffdiskQueryDeviceProtocolData queryData1 = new SffdiskQueryDeviceProtocolData(); queryData1.size = (ushort)Marshal.SizeOf(queryData1); Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskQueryDeviceProtocol, IntPtr.Zero, 0, ref queryData1, queryData1.size, out _, IntPtr.Zero); return(queryData1.protocolGuid.Equals(Consts.GuidSffProtocolSd)); }
/// <summary> /// Gets the device number for a specified handle /// </summary> /// <param name="deviceHandle">Device handle</param> /// <returns>Device number</returns> static uint GetDeviceNumber(SafeFileHandle deviceHandle) { StorageDeviceNumber sdn = new StorageDeviceNumber { deviceNumber = -1 }; uint k = 0; if (!Extern.DeviceIoControlGetDeviceNumber(deviceHandle, WindowsIoctl.IoctlStorageGetDeviceNumber, IntPtr.Zero, 0, ref sdn, (uint)Marshal.SizeOf(sdn), ref k, IntPtr.Zero)) { return(uint.MaxValue); } return((uint)sdn.deviceNumber); }
/// <summary> /// Opens the device for sending direct commands /// </summary> /// <param name="devicePath">Device path</param> public Device(string devicePath) { PlatformId = DetectOS.GetRealPlatformID(); Timeout = 15; Error = false; IsRemovable = false; switch (PlatformId) { case PlatformID.Win32NT: { FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, FileAttributes.Normal, IntPtr.Zero); if (((SafeFileHandle)FileHandle).IsInvalid) { Error = true; LastError = Marshal.GetLastWin32Error(); } break; } case PlatformID.Linux: { FileHandle = Linux.Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); if ((int)FileHandle < 0) { LastError = Marshal.GetLastWin32Error(); if (LastError == 13 || LastError == 30) // EACCES or EROFS { FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); if ((int)FileHandle < 0) { Error = true; LastError = Marshal.GetLastWin32Error(); } } else { Error = true; } LastError = Marshal.GetLastWin32Error(); } break; } case PlatformID.FreeBSD: { FileHandle = FreeBSD.Extern.cam_open_device(devicePath, FreeBSD.FileFlags.ReadWrite); if (((IntPtr)FileHandle).ToInt64() == 0) { Error = true; LastError = Marshal.GetLastWin32Error(); } CamDevice camDevice = (CamDevice)Marshal.PtrToStructure((IntPtr)FileHandle, typeof(CamDevice)); if (StringHandlers.CToString(camDevice.SimName) == "ata") { throw new InvalidOperationException("Parallel ATA devices are not supported on FreeBSD due to upstream bug #224250."); } break; } default: throw new InvalidOperationException($"Platform {PlatformId} not yet supported."); } if (Error) { throw new SystemException($"Error {LastError} opening device."); } Type = DeviceType.Unknown; ScsiType = PeripheralDeviceTypes.UnknownDevice; byte[] ataBuf; byte[] inqBuf = null; if (Error) { throw new SystemException($"Error {LastError} trying device."); } bool scsiSense = true; // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first switch (PlatformId) { case PlatformID.Win32NT: StoragePropertyQuery query = new StoragePropertyQuery(); query.PropertyId = StoragePropertyId.Device; query.QueryType = StorageQueryType.Standard; query.AdditionalParameters = new byte[1]; IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); byte[] descriptorB = new byte[1000]; uint returned = 0; int error = 0; bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)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) { StorageDeviceDescriptor 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: Type = DeviceType.SCSI; break; case StorageBusType.FireWire: IsFireWire = true; Type = DeviceType.SCSI; break; case StorageBusType.USB: IsUsb = true; Type = DeviceType.SCSI; break; case StorageBusType.ATAPI: Type = DeviceType.ATAPI; break; case StorageBusType.ATA: case StorageBusType.SATA: Type = DeviceType.ATA; break; case StorageBusType.MultiMediaCard: Type = DeviceType.MMC; break; case StorageBusType.SecureDigital: Type = DeviceType.SecureDigital; break; case StorageBusType.NVMe: Type = DeviceType.NVMe; break; } switch (Type) { case DeviceType.SCSI: case DeviceType.ATAPI: scsiSense = ScsiInquiry(out inqBuf, out _); break; case DeviceType.ATA: bool atapiSense = AtapiIdentify(out ataBuf, out _); if (!atapiSense) { Type = DeviceType.ATAPI; Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf); if (ataid.HasValue) { scsiSense = ScsiInquiry(out inqBuf, out _); } } else { Manufacturer = "ATA"; } break; } } Marshal.FreeHGlobal(descriptorPtr); if (Windows.Command.IsSdhci((SafeFileHandle)FileHandle)) { byte[] sdBuffer = new byte[16]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd, false, false, MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _, out _, out bool sense); if (!sense) { cachedCsd = new byte[16]; Array.Copy(sdBuffer, 0, cachedCsd, 0, 16); } sdBuffer = new byte[16]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid, false, false, MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedCid = new byte[16]; Array.Copy(sdBuffer, 0, cachedCid, 0, 16); } sdBuffer = new byte[8]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, (MmcCommands)SecureDigitalCommands.SendScr, false, true, MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedScr = new byte[8]; Array.Copy(sdBuffer, 0, cachedScr, 0, 8); } if (cachedScr != null) { sdBuffer = new byte[4]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, (MmcCommands)SecureDigitalCommands .SendOperatingCondition, false, true, MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedScr = new byte[4]; Array.Copy(sdBuffer, 0, cachedScr, 0, 4); } } else { sdBuffer = new byte[4]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendOpCond, false, true, MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedScr = new byte[4]; Array.Copy(sdBuffer, 0, 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)) { scsiSense = 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 = ConvertFromHexAscii("/sys/block/" + devPath + "/device/csd", out cachedCsd); if (len == 0) { cachedCsd = null; } } if (File.Exists("/sys/block/" + devPath + "/device/cid")) { int len = ConvertFromHexAscii("/sys/block/" + devPath + "/device/cid", out cachedCid); if (len == 0) { cachedCid = null; } } if (File.Exists("/sys/block/" + devPath + "/device/scr")) { int len = ConvertFromHexAscii("/sys/block/" + devPath + "/device/scr", out cachedScr); if (len == 0) { cachedScr = null; } } if (File.Exists("/sys/block/" + devPath + "/device/ocr")) { int len = ConvertFromHexAscii("/sys/block/" + devPath + "/device/ocr", out cachedOcr); if (len == 0) { cachedOcr = null; } } } break; default: scsiSense = ScsiInquiry(out inqBuf, out _); break; } #region SecureDigital / MultiMediaCard if (cachedCid != null) { ScsiType = PeripheralDeviceTypes.DirectAccess; IsRemovable = false; if (cachedScr != null) { Type = DeviceType.SecureDigital; CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid); Manufacturer = VendorString.Prettify(decoded.Manufacturer); Model = decoded.ProductName; Revision = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; Serial = $"{decoded.ProductSerialNumber}"; } else { Type = DeviceType.MMC; Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(cachedCid); Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); Model = decoded.ProductName; Revision = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; Serial = $"{decoded.ProductSerialNumber}"; } } #endregion SecureDigital / MultiMediaCard #region USB switch (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); resolvedLink = "/sys" + resolvedLink.Substring(2); if (!string.IsNullOrEmpty(resolvedLink)) { while (resolvedLink.Contains("usb")) { resolvedLink = Path.GetDirectoryName(resolvedLink); if (!File.Exists(resolvedLink + "/descriptors") || !File.Exists(resolvedLink + "/idProduct") || !File.Exists(resolvedLink + "/idVendor")) { continue; } FileStream usbFs = new FileStream(resolvedLink + "/descriptors", System.IO.FileMode.Open, System.IO.FileAccess.Read); byte[] usbBuf = new byte[65536]; int usbCount = usbFs.Read(usbBuf, 0, 65536); UsbDescriptors = new byte[usbCount]; Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount); usbFs.Close(); StreamReader usbSr = new StreamReader(resolvedLink + "/idProduct"); string usbTemp = usbSr.ReadToEnd(); ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out usbProduct); usbSr.Close(); usbSr = new StreamReader(resolvedLink + "/idVendor"); usbTemp = usbSr.ReadToEnd(); ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out usbVendor); usbSr.Close(); if (File.Exists(resolvedLink + "/manufacturer")) { usbSr = new StreamReader(resolvedLink + "/manufacturer"); UsbManufacturerString = usbSr.ReadToEnd().Trim(); usbSr.Close(); } if (File.Exists(resolvedLink + "/product")) { usbSr = new StreamReader(resolvedLink + "/product"); UsbProductString = usbSr.ReadToEnd().Trim(); usbSr.Close(); } if (File.Exists(resolvedLink + "/serial")) { usbSr = new StreamReader(resolvedLink + "/serial"); UsbSerialString = usbSr.ReadToEnd().Trim(); usbSr.Close(); } 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.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk }) { usbDevice = Usb.FindDrivePath(devicePath, devGuid); if (usbDevice != null) { break; } } if (usbDevice != null) { UsbDescriptors = usbDevice.BinaryDescriptors; usbVendor = (ushort)usbDevice.DeviceDescriptor.idVendor; usbProduct = (ushort)usbDevice.DeviceDescriptor.idProduct; UsbManufacturerString = usbDevice.Manufacturer; UsbProductString = usbDevice.Product; UsbSerialString = usbDevice.SerialNumber; // This is incorrect filled by Windows with SCSI/ATA serial number } break; default: IsUsb = false; break; } #endregion USB #region FireWire if (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; } StreamReader fwSr = new StreamReader(resolvedLink + "/model"); string fwTemp = fwSr.ReadToEnd(); uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out firewireModel); fwSr.Close(); fwSr = new StreamReader(resolvedLink + "/vendor"); fwTemp = fwSr.ReadToEnd(); uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out firewireVendor); fwSr.Close(); fwSr = new StreamReader(resolvedLink + "/guid"); fwTemp = fwSr.ReadToEnd(); ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out firewireGuid); fwSr.Close(); if (File.Exists(resolvedLink + "/model_name")) { fwSr = new StreamReader(resolvedLink + "/model_name"); FireWireModelName = fwSr.ReadToEnd().Trim(); fwSr.Close(); } if (File.Exists(resolvedLink + "/vendor_name")) { fwSr = new StreamReader(resolvedLink + "/vendor_name"); FireWireVendorName = fwSr.ReadToEnd().Trim(); fwSr.Close(); } IsFireWire = true; break; } } } } } // TODO: Implement for other operating systems else { IsFireWire = false; } #endregion FireWire #region PCMCIA if (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; } FileStream cisFs = new FileStream(possibleDir + "/cis", System.IO.FileMode.Open, System.IO.FileAccess.Read); byte[] cisBuf = new byte[65536]; int cisCount = cisFs.Read(cisBuf, 0, 65536); Cis = new byte[cisCount]; Array.Copy(cisBuf, 0, Cis, 0, cisCount); cisFs.Close(); IsPcmcia = true; break; } } } } } // TODO: Implement for other operating systems else { IsPcmcia = false; } #endregion PCMCIA if (!scsiSense) { Inquiry.SCSIInquiry?inquiry = Inquiry.Decode(inqBuf); Type = DeviceType.SCSI; bool serialSense = ScsiInquiry(out inqBuf, out _, 0x80); if (!serialSense) { Serial = EVPD.DecodePage80(inqBuf); } if (inquiry.HasValue) { string tmp = StringHandlers.CToString(inquiry.Value.ProductRevisionLevel); if (tmp != null) { Revision = tmp.Trim(); } tmp = StringHandlers.CToString(inquiry.Value.ProductIdentification); if (tmp != null) { Model = tmp.Trim(); } tmp = StringHandlers.CToString(inquiry.Value.VendorIdentification); if (tmp != null) { Manufacturer = tmp.Trim(); } IsRemovable = inquiry.Value.RMB; ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType; } bool atapiSense = AtapiIdentify(out ataBuf, out _); if (!atapiSense) { Type = DeviceType.ATAPI; Identify.IdentifyDevice?ataId = Identify.Decode(ataBuf); if (ataId.HasValue) { Serial = ataId.Value.SerialNumber; } } LastError = 0; Error = false; } if (scsiSense && (IsUsb || IsFireWire) || Manufacturer == "ATA") { bool ataSense = AtaIdentify(out ataBuf, out _); if (!ataSense) { Type = DeviceType.ATA; Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf); if (ataid.HasValue) { string[] separated = ataid.Value.Model.Split(' '); if (separated.Length == 1) { Model = separated[0]; } else { Manufacturer = separated[0]; Model = separated[separated.Length - 1]; } Revision = ataid.Value.FirmwareRevision; Serial = ataid.Value.SerialNumber; ScsiType = PeripheralDeviceTypes.DirectAccess; if ((ushort)ataid.Value.GeneralConfiguration != 0x848A) { IsRemovable |= (ataid.Value.GeneralConfiguration & Identify.GeneralConfigurationBit.Removable) == Identify.GeneralConfigurationBit.Removable; } else { IsCompactFlash = true; } } } } if (Type == DeviceType.Unknown) { Manufacturer = null; Model = null; Revision = null; Serial = null; } if (IsUsb) { if (string.IsNullOrEmpty(Manufacturer)) { Manufacturer = UsbManufacturerString; } if (string.IsNullOrEmpty(Model)) { Model = UsbProductString; } if (string.IsNullOrEmpty(Serial)) { Serial = UsbSerialString; } else { foreach (char c in Serial.Where(char.IsControl)) { Serial = UsbSerialString; } } } if (IsFireWire) { if (string.IsNullOrEmpty(Manufacturer)) { Manufacturer = FireWireVendorName; } if (string.IsNullOrEmpty(Model)) { Model = FireWireModelName; } if (string.IsNullOrEmpty(Serial)) { Serial = $"{firewireGuid:X16}"; } else { foreach (char c in Serial.Where(char.IsControl)) { Serial = $"{firewireGuid:X16}"; } } } // Some optical drives are not getting the correct serial, and IDENTIFY PACKET DEVICE is blocked without // administrator privileges if (ScsiType != PeripheralDeviceTypes.MultiMediaDevice) { return; } bool featureSense = GetConfiguration(out byte[] featureBuffer, out _, 0x0108, MmcGetConfigurationRt.Single, Timeout, out _); if (featureSense) { return; } Features.SeparatedFeatures features = Features.Separate(featureBuffer); if (features.Descriptors?.Length != 1 || features.Descriptors[0].Code != 0x0108) { return; } Feature_0108?serialFeature = Features.Decode_0108(features.Descriptors[0].Data); if (serialFeature is null) { return; } Serial = serialFeature.Value.Serial; }
/// <summary> /// Sends a SCSI command /// </summary> /// <returns>0 if no error occurred, otherwise, errno</returns> /// <param name="fd">File handle</param> /// <param name="cdb">SCSI CDB</param> /// <param name="buffer">Buffer for SCSI command response</param> /// <param name="senseBuffer">Buffer with the SCSI sense</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="direction">SCSI command transfer direction</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"> /// <c>True</c> if SCSI error returned non-OK status and <paramref name="senseBuffer" /> contains SCSI /// sense /// </param> internal static int SendScsiCommand(SafeFileHandle fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, ScsiIoctlDirection direction, out double duration, out bool sense) { senseBuffer = null; duration = 0; sense = false; if (buffer == null) { return(-1); } ScsiPassThroughDirectAndSenseBuffer sptdSb = new ScsiPassThroughDirectAndSenseBuffer { SenseBuf = new byte[32], sptd = new ScsiPassThroughDirect { Cdb = new byte[16], CdbLength = (byte)cdb.Length, SenseInfoLength = 32, DataIn = direction, DataTransferLength = (uint)buffer.Length, TimeOutValue = timeout, DataBuffer = Marshal.AllocHGlobal(buffer.Length) } }; sptdSb.sptd.Length = (ushort)Marshal.SizeOf(sptdSb.sptd); sptdSb.sptd.SenseInfoOffset = (uint)Marshal.SizeOf(sptdSb.sptd); Array.Copy(cdb, sptdSb.sptd.Cdb, cdb.Length); uint k = 0; int error = 0; Marshal.Copy(buffer, 0, sptdSb.sptd.DataBuffer, buffer.Length); DateTime start = DateTime.Now; bool hasError = !Extern.DeviceIoControlScsi(fd, WindowsIoctl.IoctlScsiPassThroughDirect, ref sptdSb, (uint)Marshal.SizeOf(sptdSb), ref sptdSb, (uint)Marshal.SizeOf(sptdSb), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if (hasError) { error = Marshal.GetLastWin32Error(); } Marshal.Copy(sptdSb.sptd.DataBuffer, buffer, 0, buffer.Length); sense |= sptdSb.sptd.ScsiStatus != 0; senseBuffer = new byte[32]; Array.Copy(sptdSb.SenseBuf, senseBuffer, 32); duration = (end - start).TotalMilliseconds; Marshal.FreeHGlobal(sptdSb.sptd.DataBuffer); return(error); }
/// <summary> /// Sends a MMC/SD command /// </summary> /// <returns>The result of the command.</returns> /// <param name="fd">File handle</param> /// <param name="command">MMC/SD opcode</param> /// <param name="buffer">Buffer for MMC/SD command response</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"><c>True</c> if MMC/SD returned non-OK status</param> /// <param name="write"><c>True</c> if data is sent from host to card</param> /// <param name="isApplication"><c>True</c> if command should be preceded with CMD55</param> /// <param name="flags">Flags indicating kind and place of response</param> /// <param name="blocks">How many blocks to transfer</param> /// <param name="argument">Command argument</param> /// <param name="response">Response registers</param> /// <param name="blockSize">Size of block in bytes</param> internal static int SendMmcCommand(SafeFileHandle fd, MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, out double duration, out bool sense, uint timeout = 0) { SffdiskDeviceCommandData commandData = new SffdiskDeviceCommandData(); SdCmdDescriptor commandDescriptor = new SdCmdDescriptor(); commandData.size = (ushort)Marshal.SizeOf(commandData); commandData.command = SffdiskDcmd.DeviceCommand; commandData.protocolArgumentSize = (ushort)Marshal.SizeOf(commandDescriptor); commandData.deviceDataBufferSize = blockSize * blocks; commandDescriptor.commandCode = (byte)command; commandDescriptor.cmdClass = isApplication ? SdCommandClass.AppCmd : SdCommandClass.Standard; commandDescriptor.transferDirection = write ? SdTransferDirection.Write : SdTransferDirection.Read; commandDescriptor.transferType = flags.HasFlag(MmcFlags.CommandAdtc) ? SdTransferType.SingleBlock : SdTransferType.CmdOnly; commandDescriptor.responseType = 0; if (flags.HasFlag(MmcFlags.ResponseR1) || flags.HasFlag(MmcFlags.ResponseSpiR1)) { commandDescriptor.responseType = SdResponseType.R1; } if (flags.HasFlag(MmcFlags.ResponseR1B) || flags.HasFlag(MmcFlags.ResponseSpiR1B)) { commandDescriptor.responseType = SdResponseType.R1b; } if (flags.HasFlag(MmcFlags.ResponseR2) || flags.HasFlag(MmcFlags.ResponseSpiR2)) { commandDescriptor.responseType = SdResponseType.R2; } if (flags.HasFlag(MmcFlags.ResponseR3) || flags.HasFlag(MmcFlags.ResponseSpiR3)) { commandDescriptor.responseType = SdResponseType.R3; } if (flags.HasFlag(MmcFlags.ResponseR4) || flags.HasFlag(MmcFlags.ResponseSpiR4)) { commandDescriptor.responseType = SdResponseType.R4; } if (flags.HasFlag(MmcFlags.ResponseR5) || flags.HasFlag(MmcFlags.ResponseSpiR5)) { commandDescriptor.responseType = SdResponseType.R5; } if (flags.HasFlag(MmcFlags.ResponseR6)) { commandDescriptor.responseType = SdResponseType.R6; } byte[] commandB = new byte[commandData.size + commandData.protocolArgumentSize + commandData.deviceDataBufferSize]; IntPtr hBuf = Marshal.AllocHGlobal(commandB.Length); Marshal.StructureToPtr(commandData, hBuf, true); IntPtr descriptorOffset = IntPtr.Add(hBuf, commandData.size); Marshal.StructureToPtr(commandDescriptor, descriptorOffset, true); Marshal.Copy(hBuf, commandB, 0, commandB.Length); Marshal.FreeHGlobal(hBuf); int error = 0; DateTime start = DateTime.Now; sense = !Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskDeviceCommand, commandB, (uint)commandB.Length, commandB, (uint)commandB.Length, out _, IntPtr.Zero); DateTime end = DateTime.Now; if (sense) { error = Marshal.GetLastWin32Error(); } buffer = new byte[blockSize * blocks]; Buffer.BlockCopy(commandB, commandB.Length - buffer.Length, buffer, 0, buffer.Length); response = new uint[4]; duration = (end - start).TotalMilliseconds; return(error); }
/// <summary> /// Sends an ATA command in 48-bit LBA mode /// </summary> /// <returns>0 if no error occurred, otherwise, errno</returns> /// <param name="fd">File handle</param> /// <param name="buffer">Buffer for SCSI command response</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"><c>True</c> if ATA error returned non-OK status</param> /// <param name="registers">Registers to send to drive</param> /// <param name="errorRegisters">Registers returned by drive</param> /// <param name="protocol">ATA protocol to use</param> internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, ref byte[] buffer, uint timeout, out double duration, out bool sense) { duration = 0; sense = false; errorRegisters = new AtaErrorRegistersLba48(); if (buffer == null) { return(-1); } uint offsetForBuffer = (uint)(Marshal.SizeOf(typeof(AtaPassThroughDirect)) + Marshal.SizeOf(typeof(uint))); AtaPassThroughDirectWithBuffer aptdBuf = new AtaPassThroughDirectWithBuffer { aptd = new AtaPassThroughDirect { TimeOutValue = timeout, DataBuffer = (IntPtr)offsetForBuffer, Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)), DataTransferLength = (uint)buffer.Length, PreviousTaskFile = new AtaTaskFile { CylinderHigh = (byte)((registers.LbaHigh & 0xFF00) >> 8), CylinderLow = (byte)((registers.LbaMid & 0xFF00) >> 8), Features = (byte)((registers.Feature & 0xFF00) >> 8), SectorCount = (byte)((registers.SectorCount & 0xFF00) >> 8), SectorNumber = (byte)((registers.LbaLow & 0xFF00) >> 8) }, CurrentTaskFile = new AtaTaskFile { Command = registers.Command, CylinderHigh = (byte)(registers.LbaHigh & 0xFF), CylinderLow = (byte)(registers.LbaMid & 0xFF), DeviceHead = registers.DeviceHead, Features = (byte)(registers.Feature & 0xFF), SectorCount = (byte)(registers.SectorCount & 0xFF), SectorNumber = (byte)(registers.LbaLow & 0xFF) } }, dataBuffer = new byte[64 * 512] }; switch (protocol) { case AtaProtocol.PioIn: case AtaProtocol.UDmaIn: case AtaProtocol.Dma: aptdBuf.aptd.AtaFlags = AtaFlags.DataIn; break; case AtaProtocol.PioOut: case AtaProtocol.UDmaOut: aptdBuf.aptd.AtaFlags = AtaFlags.DataOut; break; } switch (protocol) { case AtaProtocol.Dma: case AtaProtocol.DmaQueued: case AtaProtocol.FpDma: case AtaProtocol.UDmaIn: case AtaProtocol.UDmaOut: aptdBuf.aptd.AtaFlags |= AtaFlags.Dma; break; } // Unknown if needed aptdBuf.aptd.AtaFlags |= AtaFlags.DrdyRequired; uint k = 0; int error = 0; Array.Copy(buffer, 0, aptdBuf.dataBuffer, 0, buffer.Length); DateTime start = DateTime.Now; sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThrough, ref aptdBuf, (uint)Marshal.SizeOf(aptdBuf), ref aptdBuf, (uint)Marshal.SizeOf(aptdBuf), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if (sense) { error = Marshal.GetLastWin32Error(); } Array.Copy(aptdBuf.dataBuffer, 0, buffer, 0, buffer.Length); duration = (end - start).TotalMilliseconds; errorRegisters.SectorCount = (ushort)((aptdBuf.aptd.PreviousTaskFile.SectorCount << 8) + aptdBuf.aptd.CurrentTaskFile.SectorCount); errorRegisters.LbaLow = (ushort)((aptdBuf.aptd.PreviousTaskFile.SectorNumber << 8) + aptdBuf.aptd.CurrentTaskFile.SectorNumber); errorRegisters.LbaMid = (ushort)((aptdBuf.aptd.PreviousTaskFile.CylinderLow << 8) + aptdBuf.aptd.CurrentTaskFile.CylinderLow); errorRegisters.LbaHigh = (ushort)((aptdBuf.aptd.PreviousTaskFile.CylinderHigh << 8) + aptdBuf.aptd.CurrentTaskFile.CylinderHigh); errorRegisters.DeviceHead = aptdBuf.aptd.CurrentTaskFile.DeviceHead; errorRegisters.Error = aptdBuf.aptd.CurrentTaskFile.Error; errorRegisters.Status = aptdBuf.aptd.CurrentTaskFile.Status; sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; return(error); }
/// <summary> /// Opens the device for sending direct commands /// </summary> /// <param name="devicePath">Device path</param> public Device(string devicePath) { PlatformId = DetectOS.GetRealPlatformID(); Timeout = 15; Error = false; IsRemovable = false; if (devicePath.StartsWith("dic://")) { devicePath = devicePath.Substring(6); var pieces = devicePath.Split('/'); var host = pieces[0]; devicePath = devicePath.Substring(host.Length); remote = new Remote.Remote(host); Error = !remote.Open(devicePath, out var errno); LastError = errno; } else { switch (PlatformId) { case PlatformID.Win32NT: { FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, FileAttributes.Normal, IntPtr.Zero); if (((SafeFileHandle)FileHandle).IsInvalid) { Error = true; LastError = Marshal.GetLastWin32Error(); } break; } case PlatformID.Linux: { FileHandle = Linux.Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); if ((int)FileHandle < 0) { LastError = Marshal.GetLastWin32Error(); if (LastError == 13 || LastError == 30) // EACCES or EROFS { FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); if ((int)FileHandle < 0) { Error = true; LastError = Marshal.GetLastWin32Error(); } } else { Error = true; } LastError = Marshal.GetLastWin32Error(); } break; } case PlatformID.FreeBSD: { FileHandle = FreeBSD.Extern.cam_open_device(devicePath, FreeBSD.FileFlags.ReadWrite); if (((IntPtr)FileHandle).ToInt64() == 0) { Error = true; LastError = Marshal.GetLastWin32Error(); } var camDevice = (CamDevice)Marshal.PtrToStructure((IntPtr)FileHandle, typeof(CamDevice)); if (StringHandlers.CToString(camDevice.SimName) == "ata") { throw new InvalidOperationException( "Parallel ATA devices are not supported on FreeBSD due to upstream bug #224250."); } break; } default: throw new InvalidOperationException($"Platform {PlatformId} not yet supported."); } } if (Error) { throw new SystemException($"Error {LastError} opening device."); } Type = DeviceType.Unknown; ScsiType = PeripheralDeviceTypes.UnknownDevice; byte[] ataBuf; byte[] inqBuf = null; if (Error) { throw new SystemException($"Error {LastError} trying device."); } var scsiSense = true; if (remote is null) { // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first switch (PlatformId) { case PlatformID.Win32NT: var query = new StoragePropertyQuery(); query.PropertyId = StoragePropertyId.Device; query.QueryType = StorageQueryType.Standard; query.AdditionalParameters = new byte[1]; var descriptorPtr = Marshal.AllocHGlobal(1000); var descriptorB = new byte[1000]; uint returned = 0; var error = 0; var hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)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: Type = DeviceType.SCSI; break; case StorageBusType.FireWire: IsFireWire = true; Type = DeviceType.SCSI; break; case StorageBusType.USB: IsUsb = true; Type = DeviceType.SCSI; break; case StorageBusType.ATAPI: Type = DeviceType.ATAPI; break; case StorageBusType.ATA: case StorageBusType.SATA: Type = DeviceType.ATA; break; case StorageBusType.MultiMediaCard: Type = DeviceType.MMC; break; case StorageBusType.SecureDigital: Type = DeviceType.SecureDigital; break; case StorageBusType.NVMe: Type = DeviceType.NVMe; break; } switch (Type) { case DeviceType.SCSI: case DeviceType.ATAPI: scsiSense = ScsiInquiry(out inqBuf, out _); break; case DeviceType.ATA: var atapiSense = AtapiIdentify(out ataBuf, out _); if (!atapiSense) { Type = DeviceType.ATAPI; var ataid = Identify.Decode(ataBuf); if (ataid.HasValue) { scsiSense = ScsiInquiry(out inqBuf, out _); } } else { Manufacturer = "ATA"; } break; } } Marshal.FreeHGlobal(descriptorPtr); if (Windows.Command.IsSdhci((SafeFileHandle)FileHandle)) { var sdBuffer = new byte[16]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd, false, false, MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _, out _, out var sense); if (!sense) { cachedCsd = new byte[16]; Array.Copy(sdBuffer, 0, cachedCsd, 0, 16); } sdBuffer = new byte[16]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid, false, false, MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedCid = new byte[16]; Array.Copy(sdBuffer, 0, cachedCid, 0, 16); } sdBuffer = new byte[8]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, (MmcCommands)SecureDigitalCommands.SendScr, false, true, MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedScr = new byte[8]; Array.Copy(sdBuffer, 0, cachedScr, 0, 8); } if (cachedScr != null) { sdBuffer = new byte[4]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, (MmcCommands)SecureDigitalCommands .SendOperatingCondition, false, true, MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedScr = new byte[4]; Array.Copy(sdBuffer, 0, cachedScr, 0, 4); } } else { sdBuffer = new byte[4]; LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendOpCond, false, true, MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer, out _, out _, out sense); if (!sense) { cachedScr = new byte[4]; Array.Copy(sdBuffer, 0, 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)) { scsiSense = ScsiInquiry(out inqBuf, out _); } // MultiMediaCard and SecureDigital go here else if (devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal)) { var devPath = devicePath.Substring(5); if (File.Exists("/sys/block/" + devPath + "/device/csd")) { var len = ConvertFromHexAscii("/sys/block/" + devPath + "/device/csd", out cachedCsd); if (len == 0) { cachedCsd = null; } } if (File.Exists("/sys/block/" + devPath + "/device/cid")) { var len = ConvertFromHexAscii("/sys/block/" + devPath + "/device/cid", out cachedCid); if (len == 0) { cachedCid = null; } } if (File.Exists("/sys/block/" + devPath + "/device/scr")) { var len = ConvertFromHexAscii("/sys/block/" + devPath + "/device/scr", out cachedScr); if (len == 0) { cachedScr = null; } } if (File.Exists("/sys/block/" + devPath + "/device/ocr")) { var len = ConvertFromHexAscii("/sys/block/" + devPath + "/device/ocr", out cachedOcr); if (len == 0) { cachedOcr = null; } } } break; default: scsiSense = ScsiInquiry(out inqBuf, out _); break; } } else { Type = remote.GetDeviceType(); switch (Type) { case DeviceType.ATAPI: case DeviceType.SCSI: scsiSense = ScsiInquiry(out inqBuf, out _); break; case DeviceType.SecureDigital: case DeviceType.MMC: if (!remote.GetSdhciRegisters(out cachedCsd, out cachedCid, out cachedOcr, out cachedScr)) { Type = DeviceType.SCSI; ScsiType = PeripheralDeviceTypes.DirectAccess; } break; } } #region SecureDigital / MultiMediaCard if (cachedCid != null) { ScsiType = PeripheralDeviceTypes.DirectAccess; IsRemovable = false; if (cachedScr != null) { Type = DeviceType.SecureDigital; var decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid); Manufacturer = VendorString.Prettify(decoded.Manufacturer); Model = decoded.ProductName; Revision = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; Serial = $"{decoded.ProductSerialNumber}"; } else { Type = DeviceType.MMC; var decoded = Decoders.MMC.Decoders.DecodeCID(cachedCid); Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); Model = decoded.ProductName; Revision = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; Serial = $"{decoded.ProductSerialNumber}"; } } #endregion SecureDigital / MultiMediaCard #region USB if (remote is null) { switch (PlatformId) { case PlatformID.Linux: if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) { var devPath = devicePath.Substring(5); if (Directory.Exists("/sys/block/" + devPath)) { var 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]; var usbCount = usbFs.Read(usbBuf, 0, 65536); UsbDescriptors = new byte[usbCount]; Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount); usbFs.Close(); var usbSr = new StreamReader(resolvedLink + "/idProduct"); var usbTemp = usbSr.ReadToEnd(); ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out usbProduct); usbSr.Close(); usbSr = new StreamReader(resolvedLink + "/idVendor"); usbTemp = usbSr.ReadToEnd(); ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out usbVendor); usbSr.Close(); if (File.Exists(resolvedLink + "/manufacturer")) { usbSr = new StreamReader(resolvedLink + "/manufacturer"); UsbManufacturerString = usbSr.ReadToEnd().Trim(); usbSr.Close(); } if (File.Exists(resolvedLink + "/product")) { usbSr = new StreamReader(resolvedLink + "/product"); UsbProductString = usbSr.ReadToEnd().Trim(); usbSr.Close(); } if (File.Exists(resolvedLink + "/serial")) { usbSr = new StreamReader(resolvedLink + "/serial"); UsbSerialString = usbSr.ReadToEnd().Trim(); usbSr.Close(); } 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 (var devGuid in new[] { Usb.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk }) { usbDevice = Usb.FindDrivePath(devicePath, devGuid); if (usbDevice != null) { break; } } if (usbDevice != null) { UsbDescriptors = usbDevice.BinaryDescriptors; usbVendor = (ushort)usbDevice.DeviceDescriptor.idVendor; usbProduct = (ushort)usbDevice.DeviceDescriptor.idProduct; UsbManufacturerString = usbDevice.Manufacturer; UsbProductString = usbDevice.Product; UsbSerialString = usbDevice .SerialNumber; // This is incorrect filled by Windows with SCSI/ATA serial number } break; default: IsUsb = false; break; } } else { if (remote.GetUsbData(out var remoteUsbDescriptors, out var remoteUsbVendor, out var remoteUsbProduct, out var remoteUsbManufacturer, out var remoteUsbProductString, out var remoteUsbSerial)) { IsUsb = true; UsbDescriptors = remoteUsbDescriptors; usbVendor = remoteUsbVendor; usbProduct = remoteUsbProduct; UsbManufacturerString = remoteUsbManufacturer; UsbProductString = remoteUsbProductString; UsbSerialString = remoteUsbSerial; } } #endregion USB #region FireWire if (!(remote is null)) { if (remote.GetFireWireData(out firewireVendor, out firewireModel, out firewireGuid, out var remoteFireWireVendorName, out var remoteFireWireModelName)) { IsFireWire = true; FireWireVendorName = remoteFireWireVendorName; FireWireModelName = remoteFireWireModelName; } }
internal static DeviceInfo[] GetList() { List <string> deviceIDs = new List <string>(); try { ManagementObjectSearcher mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive"); ManagementObjectCollection objCol = mgmtObjSearcher.Get(); deviceIDs.AddRange(from ManagementObject drive in objCol select(string) drive["DeviceID"]); mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_TapeDrive"); objCol = mgmtObjSearcher.Get(); deviceIDs.AddRange(from ManagementObject drive in objCol select(string) drive["DeviceID"]); mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_CDROMDrive"); objCol = mgmtObjSearcher.Get(); deviceIDs.AddRange(from ManagementObject drive in objCol select(string) drive["Drive"]); } catch (Exception) { #if DEBUG throw; #else return(null); #endif } List <DeviceInfo> devList = new List <DeviceInfo>(); foreach (string devId in deviceIDs) { string physId = devId; // TODO: This can be done better if (devId.Length == 2 && devId[1] == ':') { physId = "\\\\?\\" + devId; } SafeFileHandle fd = Extern.CreateFile(physId, 0, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, 0, IntPtr.Zero); if (fd.IsInvalid) { continue; } StoragePropertyQuery query = new StoragePropertyQuery { PropertyId = StoragePropertyId.Device, QueryType = StorageQueryType.Standard, AdditionalParameters = new byte[1] }; //StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor(); //descriptor.RawDeviceProperties = new byte[16384]; IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); byte[] descriptorB = new byte[1000]; uint returned = 0; int error = 0; bool hasError = !Extern.DeviceIoControlStorageQuery(fd, 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) { continue; } StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor { Version = BitConverter.ToUInt32(descriptorB, 0), Size = BitConverter.ToUInt32(descriptorB, 4), DeviceType = descriptorB[8], DeviceTypeModifier = descriptorB[9], RemovableMedia = BitConverter.ToBoolean(descriptorB, 10), CommandQueueing = BitConverter.ToBoolean(descriptorB, 11), 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) }; DeviceInfo info = new DeviceInfo { Path = physId, Bus = descriptor.BusType.ToString() }; if (descriptor.VendorIdOffset > 0) { info.Vendor = StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.VendorIdOffset); } if (descriptor.ProductIdOffset > 0) { info.Model = StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.ProductIdOffset); } // TODO: Get serial number of SCSI and USB devices, probably also FireWire (untested) if (descriptor.SerialNumberOffset > 0) { info.Serial = StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.SerialNumberOffset); // fix any serial numbers that are returned as hex-strings if (Array.TrueForAll(info.Serial.ToCharArray(), c => "0123456789abcdef".IndexOf(c) >= 0) && info.Serial.Length == 40) { info.Serial = HexStringToString(info.Serial).Trim(); } } if (string.IsNullOrEmpty(info.Vendor) || info.Vendor == "ATA") { string[] pieces = info.Model.Split(' '); if (pieces.Length > 1) { info.Vendor = pieces[0]; info.Model = info.Model.Substring(pieces[0].Length + 1); } } switch (descriptor.BusType) { case StorageBusType.SCSI: case StorageBusType.ATAPI: case StorageBusType.ATA: case StorageBusType.FireWire: case StorageBusType.SSA: case StorageBusType.Fibre: case StorageBusType.USB: case StorageBusType.iSCSI: case StorageBusType.SAS: case StorageBusType.SATA: info.Supported = true; break; } Marshal.FreeHGlobal(descriptorPtr); devList.Add(info); } DeviceInfo[] devices = devList.ToArray(); return(devices); }
/// <summary> /// Gets the internal device path for a specified handle /// </summary> /// <param name="fd">Device handle</param> /// <returns>Device path</returns> internal static string GetDevicePath(SafeFileHandle fd) { uint devNumber = GetDeviceNumber(fd); if (devNumber == uint.MaxValue) { return(null); } SafeFileHandle hDevInfo = Extern.SetupDiGetClassDevs(ref Consts.GuidDevinterfaceDisk, IntPtr.Zero, IntPtr.Zero, DeviceGetClassFlags.Present | DeviceGetClassFlags.DeviceInterface); if (hDevInfo.IsInvalid) { return(null); } uint index = 0; DeviceInterfaceData spdid = new DeviceInterfaceData(); spdid.cbSize = Marshal.SizeOf(spdid); while (true) { byte[] buffer = new byte[2048]; if (!Extern.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref Consts.GuidDevinterfaceDisk, index, ref spdid)) { break; } uint size = 0; Extern.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, IntPtr.Zero, 0, ref size, IntPtr.Zero); if (size > 0 && size < buffer.Length) { buffer[0] = (byte)(IntPtr.Size == 8 ? IntPtr.Size : IntPtr.Size + Marshal.SystemDefaultCharSize); // Funny... IntPtr pspdidd = Marshal.AllocHGlobal(buffer.Length); Marshal.Copy(buffer, 0, pspdidd, buffer.Length); bool result = Extern.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, pspdidd, size, ref size, IntPtr.Zero); buffer = new byte[size]; Marshal.Copy(pspdidd, buffer, 0, buffer.Length); Marshal.FreeHGlobal(pspdidd); if (result) { string devicePath = Encoding.Unicode.GetString(buffer, 4, (int)size - 4); SafeFileHandle hDrive = Extern.CreateFile(devicePath, 0, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, 0, IntPtr.Zero); if (!hDrive.IsInvalid) { uint newDeviceNumber = GetDeviceNumber(hDrive); if (newDeviceNumber == devNumber) { Extern.CloseHandle(hDrive); return(devicePath); } } Extern.CloseHandle(hDrive); } } index++; } Extern.SetupDiDestroyDeviceInfoList(hDevInfo); return(null); }
/// <summary> /// Sends an ATA command in 28-bit LBA mode using undocumented Windows XP ioctl /// </summary> /// <returns>0 if no error occurred, otherwise, errno</returns> /// <param name="fd">File handle</param> /// <param name="buffer">Buffer for SCSI command response</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"><c>True</c> if ATA error returned non-OK status</param> /// <param name="registers">Registers to send to drive</param> /// <param name="errorRegisters">Registers returned by drive</param> /// <param name="protocol">ATA protocol to use</param> internal static int SendIdeCommand(SafeFileHandle fd, AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, ref byte[] buffer, uint timeout, out double duration, out bool sense) { duration = 0; sense = false; errorRegisters = new AtaErrorRegistersLba28(); if (buffer == null) { return(-1); } IdePassThroughDirect iptd = new IdePassThroughDirect { CurrentTaskFile = new AtaTaskFile { Command = registers.Command, CylinderHigh = registers.LbaHigh, CylinderLow = registers.LbaMid, DeviceHead = registers.DeviceHead, Features = registers.Feature, SectorCount = registers.SectorCount, SectorNumber = registers.LbaLow }, DataBufferSize = 512, DataBuffer = new byte[512] }; uint k = 0; int error = 0; Array.Copy(buffer, 0, iptd.DataBuffer, 0, buffer.Length); DateTime start = DateTime.Now; sense = !Extern.DeviceIoControlIde(fd, WindowsIoctl.IoctlIdePassThrough, ref iptd, (uint)Marshal.SizeOf(iptd), ref iptd, (uint)Marshal.SizeOf(iptd), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if (sense) { error = Marshal.GetLastWin32Error(); } buffer = new byte[k - 12]; Array.Copy(iptd.DataBuffer, 0, buffer, 0, buffer.Length); duration = (end - start).TotalMilliseconds; errorRegisters.LbaHigh = iptd.CurrentTaskFile.CylinderHigh; errorRegisters.LbaMid = iptd.CurrentTaskFile.CylinderLow; errorRegisters.DeviceHead = iptd.CurrentTaskFile.DeviceHead; errorRegisters.Error = iptd.CurrentTaskFile.Error; errorRegisters.LbaLow = iptd.CurrentTaskFile.SectorNumber; errorRegisters.SectorCount = iptd.CurrentTaskFile.SectorCount; errorRegisters.Status = iptd.CurrentTaskFile.Status; sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; return(error); }