diff --git a/DiscImageChef.Devices/Command.cs b/DiscImageChef.Devices/Command.cs index 96f0781fb..239cc3806 100644 --- a/DiscImageChef.Devices/Command.cs +++ b/DiscImageChef.Devices/Command.cs @@ -144,6 +144,23 @@ namespace DiscImageChef.Devices { case Interop.PlatformID.Win32NT: { + if( + ( // Windows XP <= SP1 + Environment.OSVersion.Version.Major == 5 && + Environment.OSVersion.Version.Minor == 1 && + (Environment.OSVersion.ServicePack == "Service Pack 1" || Environment.OSVersion.ServicePack == "")) || + ( // Windows 2000 + Environment.OSVersion.Version.Major == 5 && + Environment.OSVersion.Version.Minor == 0) + ) + return Windows.Command.SendIdeCommand((SafeFileHandle)fd, registers, out errorRegisters, + protocol, ref buffer, timeout, out duration, out sense); + // Windows NT 4 or earlier, requires special ATA pass thru SCSI. But DiscImageChef cannot run there (or can it?) + if( + Environment.OSVersion.Version.Major <= 4 + ) + throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported."); + return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, ref buffer, timeout, out duration, out sense); } @@ -177,6 +194,23 @@ namespace DiscImageChef.Devices { case Interop.PlatformID.Win32NT: { + if( + ( // Windows XP <= SP1 + Environment.OSVersion.Version.Major == 5 && + Environment.OSVersion.Version.Minor == 1 && + (Environment.OSVersion.ServicePack == "Service Pack 1" || Environment.OSVersion.ServicePack == "")) || + ( // Windows 2000 + Environment.OSVersion.Version.Major == 5 && + Environment.OSVersion.Version.Minor == 0) + ) + return Windows.Command.SendIdeCommand((SafeFileHandle)fd, registers, out errorRegisters, + protocol, ref buffer, timeout, out duration, out sense); + // Windows NT 4 or earlier, requires special ATA pass thru SCSI. But DiscImageChef cannot run there (or can it?) + if( + Environment.OSVersion.Version.Major <= 4 + ) + throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported."); + return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, ref buffer, timeout, out duration, out sense); } @@ -210,6 +244,7 @@ namespace DiscImageChef.Devices { case Interop.PlatformID.Win32NT: { + // No check for Windows version. A 48-bit ATA disk simply does not work on earlier systems return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, ref buffer, timeout, out duration, out sense); } diff --git a/DiscImageChef.Devices/Windows/Command.cs b/DiscImageChef.Devices/Windows/Command.cs index 425a7c580..3452f4989 100644 --- a/DiscImageChef.Devices/Windows/Command.cs +++ b/DiscImageChef.Devices/Windows/Command.cs @@ -363,6 +363,124 @@ namespace DiscImageChef.Devices.Windows return error; } + + internal static int SendIdeCommand(SafeFileHandle fd, AtaRegistersCHS registers, out AtaErrorRegistersCHS errorRegisters, + AtaProtocol protocol, ref byte[] buffer, uint timeout, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new AtaErrorRegistersCHS(); + + if(buffer == null || buffer.Length > 512) + return -1; + + IdePassThroughDirect iptd = new IdePassThroughDirect + { + CurrentTaskFile = new AtaTaskFile + { + Command = registers.command, + CylinderHigh = registers.cylinderHigh, + CylinderLow = registers.cylinderLow, + DeviceHead = registers.deviceHead, + Features = registers.feature, + SectorCount = registers.sectorCount, + SectorNumber = registers.sector + }, + 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.IOCTL_IDE_PASS_THROUGH, 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.command = iptd.CurrentTaskFile.Command; + errorRegisters.cylinderHigh = iptd.CurrentTaskFile.CylinderHigh; + errorRegisters.cylinderLow = iptd.CurrentTaskFile.CylinderLow; + errorRegisters.deviceHead = iptd.CurrentTaskFile.DeviceHead; + errorRegisters.error = iptd.CurrentTaskFile.Error; + errorRegisters.sector = iptd.CurrentTaskFile.SectorNumber; + errorRegisters.sectorCount = iptd.CurrentTaskFile.SectorCount; + errorRegisters.status = iptd.CurrentTaskFile.Status; + + sense = errorRegisters.error != 0 || (errorRegisters.status & 0xA5) != 0; + + return error; + } + + 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; + + uint offsetForBuffer = (uint)(Marshal.SizeOf(typeof(AtaPassThroughDirect)) + Marshal.SizeOf(typeof(uint))); + + 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.IOCTL_IDE_PASS_THROUGH, 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.command = iptd.CurrentTaskFile.Command; + 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; + } } } diff --git a/DiscImageChef.Devices/Windows/Enums.cs b/DiscImageChef.Devices/Windows/Enums.cs index ea39d0c62..5b54fa3bc 100644 --- a/DiscImageChef.Devices/Windows/Enums.cs +++ b/DiscImageChef.Devices/Windows/Enums.cs @@ -270,6 +270,7 @@ namespace DiscImageChef.Devices.Windows /// IOCTL_SCSI_GET_ADDRESS = 0x41018, IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400, + IOCTL_IDE_PASS_THROUGH = 0x4D028, } [Flags] diff --git a/DiscImageChef.Devices/Windows/Extern.cs b/DiscImageChef.Devices/Windows/Extern.cs index 4254ea31e..1356d868b 100644 --- a/DiscImageChef.Devices/Windows/Extern.cs +++ b/DiscImageChef.Devices/Windows/Extern.cs @@ -84,7 +84,19 @@ namespace DiscImageChef.Devices.Windows uint nOutBufferSize, ref uint pBytesReturned, IntPtr Overlapped - ); + ); + + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlIde( + SafeFileHandle hDevice, + WindowsIoctl IoControlCode, + ref IdePassThroughDirect InBuffer, + uint nInBufferSize, + ref IdePassThroughDirect OutBuffer, + uint nOutBufferSize, + ref uint pBytesReturned, + IntPtr Overlapped + ); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool CloseHandle(SafeFileHandle hDevice); diff --git a/DiscImageChef.Devices/Windows/Structs.cs b/DiscImageChef.Devices/Windows/Structs.cs index 231bbcf19..c107fd6f8 100644 --- a/DiscImageChef.Devices/Windows/Structs.cs +++ b/DiscImageChef.Devices/Windows/Structs.cs @@ -194,5 +194,23 @@ namespace DiscImageChef.Devices.Windows public uint RawPropertiesLength; public byte[] RawDeviceProperties; } + + [StructLayout(LayoutKind.Sequential)] + struct IdePassThroughDirect + { + /// + /// ATA registers + /// + public AtaTaskFile CurrentTaskFile; + /// + /// Size of data buffer + /// + public uint DataBufferSize; + /// + /// Data buffer + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] + public byte[] DataBuffer; + } }