diff --git a/DiscImageChef.Devices/Command.cs b/DiscImageChef.Devices/Command.cs
index 239cc3806..33d9b960b 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 78cb76428..ea34ff5fd 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 8c7ec55df..622eaa6d5 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 51b175f94..4d5b2a3c7 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 cb7a7702e..a8a2194fb 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 3eeacfc6a..c382403ab 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;
+ }
}