// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : Command.cs // Author(s) : Natalia Portillo // // Component : Windows direct device access. // // --[ Description ] ---------------------------------------------------------- // // Contains a high level representation of the Windows syscalls used to // directly interface devices. // // --[ 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-2017 Natalia Portillo // ****************************************************************************/ using System; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using DiscImageChef.Decoders.ATA; namespace DiscImageChef.Devices.Windows { static class Command { /// /// Sends a SCSI command /// /// 0 if no error occurred, otherwise, errno /// File handle /// SCSI CDB /// Buffer for SCSI command response /// Buffer with the SCSI sense /// Timeout in seconds /// SCSI command transfer direction /// Time it took to execute the command in milliseconds /// True if SCSI error returned non-OK status and contains SCSI sense 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 sptd_sb = new ScsiPassThroughDirectAndSenseBuffer(); sptd_sb.sptd = new ScsiPassThroughDirect(); sptd_sb.SenseBuf = new byte[32]; sptd_sb.sptd.Cdb = new byte[16]; Array.Copy(cdb, sptd_sb.sptd.Cdb, cdb.Length); sptd_sb.sptd.Length = (ushort)Marshal.SizeOf(sptd_sb.sptd); sptd_sb.sptd.CdbLength = (byte)cdb.Length; sptd_sb.sptd.SenseInfoLength = (byte)sptd_sb.SenseBuf.Length; sptd_sb.sptd.DataIn = direction; sptd_sb.sptd.DataTransferLength = (uint)buffer.Length; sptd_sb.sptd.TimeOutValue = timeout; sptd_sb.sptd.DataBuffer = Marshal.AllocHGlobal(buffer.Length); sptd_sb.sptd.SenseInfoOffset = (uint)Marshal.SizeOf(sptd_sb.sptd); uint k = 0; int error = 0; Marshal.Copy(buffer, 0, sptd_sb.sptd.DataBuffer, buffer.Length); DateTime start = DateTime.Now; bool hasError = !Extern.DeviceIoControlScsi(fd, WindowsIoctl.IOCTL_SCSI_PASS_THROUGH_DIRECT, ref sptd_sb, (uint)Marshal.SizeOf(sptd_sb), ref sptd_sb, (uint)Marshal.SizeOf(sptd_sb), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if(hasError) error = Marshal.GetLastWin32Error(); Marshal.Copy(sptd_sb.sptd.DataBuffer, buffer, 0, buffer.Length); sense |= sptd_sb.sptd.ScsiStatus != 0; senseBuffer = new byte[32]; Array.Copy(sptd_sb.SenseBuf, senseBuffer, 32); duration = (end - start).TotalMilliseconds; Marshal.FreeHGlobal(sptd_sb.sptd.DataBuffer); return error; } internal static int SendAtaCommand(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) return -1; GCHandle hd = GCHandle.Alloc(buffer); AtaPassThroughDirect aptd = new AtaPassThroughDirect { TimeOutValue = timeout, DataBuffer = Marshal.AllocHGlobal(buffer.Length), PreviousTaskFile = new AtaTaskFile(), CurrentTaskFile = new AtaTaskFile { Command = registers.command, CylinderHigh = registers.cylinderHigh, CylinderLow = registers.cylinderLow, DeviceHead = registers.deviceHead, Features = registers.feature, SectorCount = registers.sectorCount, SectorNumber = registers.sector } }; aptd.Length = (ushort)Marshal.SizeOf(aptd); if(protocol == AtaProtocol.PioIn || protocol == AtaProtocol.UDmaIn || protocol == AtaProtocol.Dma) aptd.AtaFlags = AtaFlags.DataIn; else if(protocol == AtaProtocol.PioOut || protocol == AtaProtocol.UDmaOut) aptd.AtaFlags = AtaFlags.DataOut; switch(protocol) { case AtaProtocol.Dma: case AtaProtocol.DmaQueued: case AtaProtocol.FPDma: case AtaProtocol.UDmaIn: case AtaProtocol.UDmaOut: aptd.AtaFlags |= AtaFlags.DMA; break; } uint k = 0; int error = 0; Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); DateTime start = DateTime.Now; sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IOCTL_ATA_PASS_THROUGH, ref aptd, (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if(sense) error = Marshal.GetLastWin32Error(); Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); duration = (end - start).TotalMilliseconds; Marshal.FreeHGlobal(aptd.DataBuffer); errorRegisters.command = aptd.CurrentTaskFile.Command; errorRegisters.cylinderHigh = aptd.CurrentTaskFile.CylinderHigh; errorRegisters.cylinderLow = aptd.CurrentTaskFile.CylinderLow; errorRegisters.deviceHead = aptd.CurrentTaskFile.DeviceHead; errorRegisters.error = aptd.CurrentTaskFile.Error; errorRegisters.sector = aptd.CurrentTaskFile.SectorNumber; errorRegisters.sectorCount = aptd.CurrentTaskFile.SectorCount; errorRegisters.status = aptd.CurrentTaskFile.Status; return error; } internal static int SendAtaCommand(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; GCHandle hd = GCHandle.Alloc(buffer); AtaPassThroughDirect aptd = new AtaPassThroughDirect { TimeOutValue = timeout, DataBuffer = Marshal.AllocHGlobal(buffer.Length), PreviousTaskFile = new AtaTaskFile(), CurrentTaskFile = new AtaTaskFile { Command = registers.command, CylinderHigh = registers.lbaHigh, CylinderLow = registers.lbaMid, DeviceHead = registers.deviceHead, Features = registers.feature, SectorCount = registers.sectorCount, SectorNumber = registers.lbaLow } }; aptd.Length = (ushort)Marshal.SizeOf(aptd); if(protocol == AtaProtocol.PioIn || protocol == AtaProtocol.UDmaIn || protocol == AtaProtocol.Dma) aptd.AtaFlags = AtaFlags.DataIn; else if(protocol == AtaProtocol.PioOut || protocol == AtaProtocol.UDmaOut) aptd.AtaFlags = AtaFlags.DataOut; switch(protocol) { case AtaProtocol.Dma: case AtaProtocol.DmaQueued: case AtaProtocol.FPDma: case AtaProtocol.UDmaIn: case AtaProtocol.UDmaOut: aptd.AtaFlags |= AtaFlags.DMA; break; } uint k = 0; int error = 0; Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); DateTime start = DateTime.Now; sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IOCTL_ATA_PASS_THROUGH, ref aptd, (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if(sense) error = Marshal.GetLastWin32Error(); Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); duration = (end - start).TotalMilliseconds; Marshal.FreeHGlobal(aptd.DataBuffer); errorRegisters.command = aptd.CurrentTaskFile.Command; errorRegisters.lbaHigh = aptd.CurrentTaskFile.CylinderHigh; errorRegisters.lbaMid = aptd.CurrentTaskFile.CylinderLow; errorRegisters.deviceHead = aptd.CurrentTaskFile.DeviceHead; errorRegisters.error = aptd.CurrentTaskFile.Error; errorRegisters.lbaLow = aptd.CurrentTaskFile.SectorNumber; errorRegisters.sectorCount = aptd.CurrentTaskFile.SectorCount; errorRegisters.status = aptd.CurrentTaskFile.Status; return error; } 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; GCHandle hd = GCHandle.Alloc(buffer); AtaPassThroughDirect aptd = new AtaPassThroughDirect { TimeOutValue = timeout, DataBuffer = Marshal.AllocHGlobal(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) } }; aptd.Length = (ushort)Marshal.SizeOf(aptd); if(protocol == AtaProtocol.PioIn || protocol == AtaProtocol.UDmaIn || protocol == AtaProtocol.Dma) aptd.AtaFlags = AtaFlags.DataIn; else if(protocol == AtaProtocol.PioOut || protocol == AtaProtocol.UDmaOut) aptd.AtaFlags = AtaFlags.DataOut; switch(protocol) { case AtaProtocol.Dma: case AtaProtocol.DmaQueued: case AtaProtocol.FPDma: case AtaProtocol.UDmaIn: case AtaProtocol.UDmaOut: aptd.AtaFlags |= AtaFlags.DMA; break; } uint k = 0; int error = 0; Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); DateTime start = DateTime.Now; sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IOCTL_ATA_PASS_THROUGH, ref aptd, (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if(sense) error = Marshal.GetLastWin32Error(); Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); duration = (end - start).TotalMilliseconds; Marshal.FreeHGlobal(aptd.DataBuffer); errorRegisters.sectorCount = (ushort)((aptd.PreviousTaskFile.SectorCount << 8) + aptd.CurrentTaskFile.SectorCount); errorRegisters.lbaLow = (ushort)((aptd.PreviousTaskFile.SectorNumber << 8) + aptd.CurrentTaskFile.SectorNumber); errorRegisters.lbaMid = (ushort)((aptd.PreviousTaskFile.CylinderLow << 8) + aptd.CurrentTaskFile.CylinderLow); errorRegisters.lbaHigh = (ushort)((aptd.PreviousTaskFile.CylinderHigh << 8) + aptd.CurrentTaskFile.CylinderHigh); errorRegisters.command = aptd.CurrentTaskFile.Command; errorRegisters.deviceHead = aptd.CurrentTaskFile.DeviceHead; errorRegisters.error = aptd.CurrentTaskFile.Error; errorRegisters.status = aptd.CurrentTaskFile.Status; return error; } } }