From 4cd0ec53e9331541eae01e639718ef8b879d9939 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 6 Dec 2017 23:05:59 +0000 Subject: [PATCH] Added support for SecureDigital / MultiMediaCard on Windows. --- DiscImageChef.Devices/Command.cs | 2 +- DiscImageChef.Devices/Device/Constructor.cs | 120 ++++++++++++++++---- DiscImageChef.Devices/Windows/Command.cs | 85 ++++++++++++++ DiscImageChef.Devices/Windows/Enums.cs | 47 ++++++++ DiscImageChef.Devices/Windows/Extern.cs | 17 ++- DiscImageChef.Devices/Windows/Structs.cs | 30 +++++ 6 files changed, 274 insertions(+), 27 deletions(-) diff --git a/DiscImageChef.Devices/Command.cs b/DiscImageChef.Devices/Command.cs index 239cc380..33d9b960 100644 --- a/DiscImageChef.Devices/Command.cs +++ b/DiscImageChef.Devices/Command.cs @@ -275,7 +275,7 @@ namespace DiscImageChef.Devices { case Interop.PlatformID.Win32NT: { - throw new NotImplementedException(); + return Windows.Command.SendMmcCommand((SafeFileHandle)fd, command, write, isApplication, flags, argument, blockSize, blocks, ref buffer, out response, out duration, out sense, timeout); } case Interop.PlatformID.Linux: { diff --git a/DiscImageChef.Devices/Device/Constructor.cs b/DiscImageChef.Devices/Device/Constructor.cs index 78cb7642..ea34ff5f 100644 --- a/DiscImageChef.Devices/Device/Constructor.cs +++ b/DiscImageChef.Devices/Device/Constructor.cs @@ -203,6 +203,74 @@ namespace DiscImageChef.Devices ntDevicePath = Windows.Command.GetDevicePath((SafeFileHandle)fd); DicConsole.DebugWriteLine("Windows devices", "NT device path: {0}", ntDevicePath); Marshal.FreeHGlobal(descriptorPtr); + + if(Windows.Command.IsSdhci((SafeFileHandle)fd)) + { + byte[] sdBuffer = new byte[16]; + bool sense = false; + + lastError = Windows.Command.SendMmcCommand((SafeFileHandle)fd, MmcCommands.SendCSD, false, false, MmcFlags.ResponseSPI_R2 | MmcFlags.Response_R2 | MmcFlags.CommandAC, + 0, 16, 1, ref sdBuffer, out uint[] response, out double duration, out sense, 0); + + if(!sense) + { + cachedCsd = new byte[16]; + Array.Copy(sdBuffer, 0, cachedCsd, 0, 16); + } + + sdBuffer = new byte[16]; + sense = false; + + lastError = Windows.Command.SendMmcCommand((SafeFileHandle)fd, MmcCommands.SendCID, false, false, MmcFlags.ResponseSPI_R2 | MmcFlags.Response_R2 | MmcFlags.CommandAC, + 0, 16, 1, ref sdBuffer, out response, out duration, out sense, 0); + + if(!sense) + { + cachedCid = new byte[16]; + Array.Copy(sdBuffer, 0, cachedCid, 0, 16); + } + + sdBuffer = new byte[8]; + sense = false; + + lastError = Windows.Command.SendMmcCommand((SafeFileHandle)fd, (MmcCommands)SecureDigitalCommands.SendSCR, false, true, MmcFlags.ResponseSPI_R1 | MmcFlags.Response_R1 | MmcFlags.CommandADTC, + 0, 8, 1, ref sdBuffer, out response, out duration, out sense, 0); + + if(!sense) + { + cachedScr = new byte[8]; + Array.Copy(sdBuffer, 0, cachedScr, 0, 8); + } + + if(cachedScr != null) + { + sdBuffer = new byte[4]; + sense = false; + + lastError = Windows.Command.SendMmcCommand((SafeFileHandle)fd, (MmcCommands)SecureDigitalCommands.SendOperatingCondition, false, true, MmcFlags.ResponseSPI_R3 | MmcFlags.Response_R3 | MmcFlags.CommandBCR, + 0, 4, 1, ref sdBuffer, out response, out duration, out sense, 0); + + if(!sense) + { + cachedScr = new byte[4]; + Array.Copy(sdBuffer, 0, cachedScr, 0, 4); + } + } + else + { + sdBuffer = new byte[4]; + sense = false; + + lastError = Windows.Command.SendMmcCommand((SafeFileHandle)fd, MmcCommands.SendOpCond, false, true, MmcFlags.ResponseSPI_R3 | MmcFlags.Response_R3 | MmcFlags.CommandBCR, + 0, 4, 1, ref sdBuffer, out response, out duration, out sense, 0); + + if(!sense) + { + cachedScr = new byte[4]; + Array.Copy(sdBuffer, 0, cachedScr, 0, 4); + } + } + } } else { @@ -236,34 +304,36 @@ namespace DiscImageChef.Devices if(len == 0) cachedOcr = null; } - - if(cachedCid != null) - { - scsiType = Decoders.SCSI.PeripheralDeviceTypes.DirectAccess; - removable = false; - - if(cachedScr != null) - { - type = DeviceType.SecureDigital; - Decoders.SecureDigital.CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid); - manufacturer = Decoders.SecureDigital.VendorString.Prettify(decoded.Manufacturer); - model = decoded.ProductName; - revision = string.Format("{0:X2}.{1:X2}", (decoded.ProductRevision & 0xF0) >> 4, decoded.ProductRevision & 0x0F); - serial = string.Format("{0}", 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 = string.Format("{0:X2}.{1:X2}", (decoded.ProductRevision & 0xF0) >> 4, decoded.ProductRevision & 0x0F); - serial = string.Format("{0}", decoded.ProductSerialNumber); - } - } } } + #region SecureDigital / MultiMediaCard + if(cachedCid != null) + { + scsiType = Decoders.SCSI.PeripheralDeviceTypes.DirectAccess; + removable = false; + + if(cachedScr != null) + { + type = DeviceType.SecureDigital; + Decoders.SecureDigital.CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid); + manufacturer = Decoders.SecureDigital.VendorString.Prettify(decoded.Manufacturer); + model = decoded.ProductName; + revision = string.Format("{0:X2}.{1:X2}", (decoded.ProductRevision & 0xF0) >> 4, decoded.ProductRevision & 0x0F); + serial = string.Format("{0}", 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 = string.Format("{0:X2}.{1:X2}", (decoded.ProductRevision & 0xF0) >> 4, decoded.ProductRevision & 0x0F); + serial = string.Format("{0}", decoded.ProductSerialNumber); + } + } + #endregion SecureDigital / MultiMediaCard + #region USB if(platformID == Interop.PlatformID.Linux) diff --git a/DiscImageChef.Devices/Windows/Command.cs b/DiscImageChef.Devices/Windows/Command.cs index 8c7ec55d..622eaa6d 100644 --- a/DiscImageChef.Devices/Windows/Command.cs +++ b/DiscImageChef.Devices/Windows/Command.cs @@ -570,6 +570,91 @@ namespace DiscImageChef.Devices.Windows Extern.SetupDiDestroyDeviceInfoList(hDevInfo); return null; } + + internal static bool IsSdhci(SafeFileHandle fd) + { + SffdiskQueryDeviceProtocolData queryData1 = new SffdiskQueryDeviceProtocolData(); + queryData1.size = (ushort)Marshal.SizeOf(queryData1); + uint bytesReturned; + Extern.DeviceIoControl(fd, WindowsIoctl.IOCTL_SFFDISK_QUERY_DEVICE_PROTOCOL, IntPtr.Zero, 0, + ref queryData1, queryData1.size, out bytesReturned, IntPtr.Zero); + return queryData1.protocolGuid.Equals(Consts.GUID_SFF_PROTOCOL_SD); + } + + /// + /// Sends a MMC/SD command + /// + /// The result of the command. + /// File handle + /// MMC/SD opcode + /// Buffer for MMC/SD command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if MMC/SD returned non-OK status + /// True if data is sent from host to card + /// True if command should be preceded with CMD55 + /// Flags indicating kind and place of response + /// How many blocks to transfer + /// Command argument + /// Response registers + /// Size of block in bytes + 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.Response_R1) || flags.HasFlag(MmcFlags.ResponseSPI_R1)) + commandDescriptor.responseType = SdResponseType.R1; + if(flags.HasFlag(MmcFlags.Response_R1b) || flags.HasFlag(MmcFlags.ResponseSPI_R1b)) + commandDescriptor.responseType = SdResponseType.R1b; + if(flags.HasFlag(MmcFlags.Response_R2) || flags.HasFlag(MmcFlags.ResponseSPI_R2)) + commandDescriptor.responseType = SdResponseType.R2; + if(flags.HasFlag(MmcFlags.Response_R3) || flags.HasFlag(MmcFlags.ResponseSPI_R3)) + commandDescriptor.responseType = SdResponseType.R3; + if(flags.HasFlag(MmcFlags.Response_R4) || flags.HasFlag(MmcFlags.ResponseSPI_R4)) + commandDescriptor.responseType = SdResponseType.R4; + if(flags.HasFlag(MmcFlags.Response_R5) || flags.HasFlag(MmcFlags.ResponseSPI_R5)) + commandDescriptor.responseType = SdResponseType.R5; + if(flags.HasFlag(MmcFlags.Response_R6)) + commandDescriptor.responseType = SdResponseType.R6; + + byte[] command_b = new byte[commandData.size + commandData.protocolArgumentSize + commandData.deviceDataBufferSize]; + IntPtr hBuf = Marshal.AllocHGlobal(command_b.Length); + Marshal.StructureToPtr(commandData, hBuf, true); + IntPtr descriptorOffset = new IntPtr(hBuf.ToInt32() + commandData.size); + Marshal.StructureToPtr(commandDescriptor, descriptorOffset, true); + Marshal.Copy(hBuf, command_b, 0, command_b.Length); + Marshal.FreeHGlobal(hBuf); + + uint bytesReturned; + int error = 0; + DateTime start = DateTime.Now; + sense = !Extern.DeviceIoControl(fd, WindowsIoctl.IOCTL_SFFDISK_DEVICE_COMMAND, command_b, + (uint)command_b.Length, command_b, (uint)command_b.Length, out bytesReturned, IntPtr.Zero); + DateTime end = DateTime.Now; + + if(sense) + error = Marshal.GetLastWin32Error(); + + buffer = new byte[blockSize * blocks]; + Buffer.BlockCopy(command_b, command_b.Length - buffer.Length, buffer, 0, buffer.Length); + + response = new uint[4]; + duration = (end - start).TotalMilliseconds; + + return error; + } } } diff --git a/DiscImageChef.Devices/Windows/Enums.cs b/DiscImageChef.Devices/Windows/Enums.cs index 51b175f9..4d5b2a3c 100644 --- a/DiscImageChef.Devices/Windows/Enums.cs +++ b/DiscImageChef.Devices/Windows/Enums.cs @@ -343,6 +343,8 @@ namespace DiscImageChef.Devices.Windows IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400, IOCTL_IDE_PASS_THROUGH = 0x4D028, IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080, + IOCTL_SFFDISK_QUERY_DEVICE_PROTOCOL = 0x71E80, + IOCTL_SFFDISK_DEVICE_COMMAND = 0x79E84, } [Flags] @@ -446,9 +448,54 @@ namespace DiscImageChef.Devices.Windows /// DeviceInterface = 0x10, } + + public enum SdCommandClass : uint + { + Standard, + AppCmd + } + public enum SdTransferDirection : uint + { + Unspecified, + Read, + Write + } + + public enum SdTransferType : uint + { + Unspecified, + CmdOnly, + SingleBlock, + MultiBlock, + MultiBlockNoCmd12 + } + + public enum SdResponseType : uint + { + Unspecified, + None, + R1, + R1b, + R2, + R3, + R4, + R5, + R5b, + R6 + } + + public enum SffdiskDcmd : uint + { + GetVersion, + LockChannel, + UnlockChannel, + DeviceCommand + }; + static class Consts { + public static Guid GUID_SFF_PROTOCOL_SD = new Guid("AD7536A8-D055-4C40-AA4D-96312DDB6B38"); public static Guid GUID_DEVINTERFACE_DISK = new Guid(0x53F56307, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B); } } diff --git a/DiscImageChef.Devices/Windows/Extern.cs b/DiscImageChef.Devices/Windows/Extern.cs index cb7a7702..a8a2194f 100644 --- a/DiscImageChef.Devices/Windows/Extern.cs +++ b/DiscImageChef.Devices/Windows/Extern.cs @@ -98,7 +98,6 @@ namespace DiscImageChef.Devices.Windows IntPtr Overlapped ); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] internal static extern bool DeviceIoControlGetDeviceNumber( SafeFileHandle hDevice, @@ -111,6 +110,22 @@ namespace DiscImageChef.Devices.Windows IntPtr Overlapped ); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControl( + SafeFileHandle hDevice, + WindowsIoctl IoControlCode, + IntPtr InBuffer, + uint nInBufferSize, + ref SffdiskQueryDeviceProtocolData OutBuffer, + uint nOutBufferSize, + out uint pBytesReturned, + IntPtr Overlapped + ); + + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControl(SafeFileHandle hDevice, WindowsIoctl IoControlCode, byte[] InBuffer, + uint nInBufferSize, byte[] OutBuffer, uint nOutBufferSize, out uint pBytesReturned, IntPtr Overlapped); + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] internal static extern SafeFileHandle SetupDiGetClassDevs( ref Guid ClassGuid, diff --git a/DiscImageChef.Devices/Windows/Structs.cs b/DiscImageChef.Devices/Windows/Structs.cs index 3eeacfc6..c382403a 100644 --- a/DiscImageChef.Devices/Windows/Structs.cs +++ b/DiscImageChef.Devices/Windows/Structs.cs @@ -248,6 +248,7 @@ namespace DiscImageChef.Devices.Windows public short wIndex; public short wLength; } + [StructLayout(LayoutKind.Sequential)] struct USB_DESCRIPTOR_REQUEST { @@ -255,5 +256,34 @@ namespace DiscImageChef.Devices.Windows public USB_SETUP_PACKET SetupPacket; //public byte[] Data; } + + [StructLayout(LayoutKind.Sequential)] + struct SffdiskQueryDeviceProtocolData + { + public ushort size; + public ushort reserved; + public Guid protocolGuid; + } + + [StructLayout(LayoutKind.Sequential)] + struct SffdiskDeviceCommandData + { + public ushort size; + public ushort reserved; + public SffdiskDcmd command; + public ushort protocolArgumentSize; + public uint deviceDataBufferSize; + public uint information; + } + + [StructLayout(LayoutKind.Sequential)] + struct SdCmdDescriptor + { + public byte commandCode; + public SdCommandClass cmdClass; + public SdTransferDirection transferDirection; + public SdTransferType transferType; + public SdResponseType responseType; + } }