From 2bde9bf1b8a902b55e214d9492a2c598ef100ba9 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 2 Apr 2018 23:10:13 +0100 Subject: [PATCH] On Linux use direct SG_IO. --- DiscImageChef.Devices/Linux/Command.cs | 185 +++++++++++++------------ 1 file changed, 100 insertions(+), 85 deletions(-) diff --git a/DiscImageChef.Devices/Linux/Command.cs b/DiscImageChef.Devices/Linux/Command.cs index 15517febe..9cdc6cb32 100644 --- a/DiscImageChef.Devices/Linux/Command.cs +++ b/DiscImageChef.Devices/Linux/Command.cs @@ -56,12 +56,13 @@ namespace DiscImageChef.Devices.Linux /// True if SCSI error returned non-OK status and contains SCSI /// sense /// - internal static int SendScsiCommand(int fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, - ScsiIoctlDirection direction, out double duration, out bool sense) + internal static int SendScsiCommand(int 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; + duration = 0; + sense = false; if(buffer == null) return -1; @@ -69,29 +70,30 @@ namespace DiscImageChef.Devices.Linux senseBuffer = new byte[32]; - ioHdr.interface_id = 'S'; - ioHdr.cmd_len = (byte)cdb.Length; - ioHdr.mx_sb_len = (byte)senseBuffer.Length; + ioHdr.interface_id = 'S'; + ioHdr.cmd_len = (byte)cdb.Length; + ioHdr.mx_sb_len = (byte)senseBuffer.Length; ioHdr.dxfer_direction = direction; - ioHdr.dxfer_len = (uint)buffer.Length; - ioHdr.dxferp = Marshal.AllocHGlobal(buffer.Length); - ioHdr.cmdp = Marshal.AllocHGlobal(cdb.Length); - ioHdr.sbp = Marshal.AllocHGlobal(senseBuffer.Length); - ioHdr.timeout = timeout * 1000; + ioHdr.dxfer_len = (uint)buffer.Length; + ioHdr.dxferp = Marshal.AllocHGlobal(buffer.Length); + ioHdr.cmdp = Marshal.AllocHGlobal(cdb.Length); + ioHdr.sbp = Marshal.AllocHGlobal(senseBuffer.Length); + ioHdr.timeout = timeout * 1000; + ioHdr.flags = (uint)SgFlags.DirectIo; - Marshal.Copy(buffer, 0, ioHdr.dxferp, buffer.Length); - Marshal.Copy(cdb, 0, ioHdr.cmdp, cdb.Length); - Marshal.Copy(senseBuffer, 0, ioHdr.sbp, senseBuffer.Length); + Marshal.Copy(buffer, 0, ioHdr.dxferp, buffer.Length); + Marshal.Copy(cdb, 0, ioHdr.cmdp, cdb.Length); + Marshal.Copy(senseBuffer, 0, ioHdr.sbp, senseBuffer.Length); DateTime start = DateTime.UtcNow; - int error = Extern.ioctlSg(fd, LinuxIoctl.SgIo, ref ioHdr); - DateTime end = DateTime.UtcNow; + int error = Extern.ioctlSg(fd, LinuxIoctl.SgIo, ref ioHdr); + DateTime end = DateTime.UtcNow; if(error < 0) error = Marshal.GetLastWin32Error(); - Marshal.Copy(ioHdr.dxferp, buffer, 0, buffer.Length); - Marshal.Copy(ioHdr.cmdp, cdb, 0, cdb.Length); - Marshal.Copy(ioHdr.sbp, senseBuffer, 0, senseBuffer.Length); + Marshal.Copy(ioHdr.dxferp, buffer, 0, buffer.Length); + Marshal.Copy(ioHdr.cmdp, cdb, 0, cdb.Length); + Marshal.Copy(ioHdr.sbp, senseBuffer, 0, senseBuffer.Length); sense |= (ioHdr.info & SgInfo.OkMask) != SgInfo.Ok; @@ -141,13 +143,15 @@ namespace DiscImageChef.Devices.Linux /// ATA protocol to use /// Which register contains the transfer count /// Set to true if the transfer count is in blocks, otherwise it is in bytes - internal static int SendAtaCommand(int fd, AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, - ref byte[] buffer, uint timeout, bool transferBlocks, out double duration, - out bool sense) + internal static int SendAtaCommand(int fd, AtaRegistersChs registers, + out AtaErrorRegistersChs errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, + ref byte[] buffer, uint timeout, bool transferBlocks, + out double duration, + out bool sense) { - duration = 0; - sense = false; + duration = 0; + sense = false; errorRegisters = new AtaErrorRegistersChs(); if(buffer == null) return -1; @@ -175,27 +179,28 @@ namespace DiscImageChef.Devices.Linux //cdb[2] |= 0x20; - cdb[4] = registers.Feature; - cdb[6] = registers.SectorCount; - cdb[8] = registers.Sector; + cdb[4] = registers.Feature; + cdb[6] = registers.SectorCount; + cdb[8] = registers.Sector; cdb[10] = registers.CylinderLow; cdb[12] = registers.CylinderHigh; cdb[13] = registers.DeviceHead; cdb[14] = registers.Command; - int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, + int error = SendScsiCommand(fd, cdb, ref buffer, + out byte[] senseBuffer, timeout, AtaProtocolToScsiDirection(protocol), out duration, out sense); if(senseBuffer.Length < 22 || senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C) return error; errorRegisters.Error = senseBuffer[11]; - errorRegisters.SectorCount = senseBuffer[13]; - errorRegisters.Sector = senseBuffer[15]; - errorRegisters.CylinderLow = senseBuffer[17]; + errorRegisters.SectorCount = senseBuffer[13]; + errorRegisters.Sector = senseBuffer[15]; + errorRegisters.CylinderLow = senseBuffer[17]; errorRegisters.CylinderHigh = senseBuffer[19]; - errorRegisters.DeviceHead = senseBuffer[20]; - errorRegisters.Status = senseBuffer[21]; + errorRegisters.DeviceHead = senseBuffer[20]; + errorRegisters.Status = senseBuffer[21]; sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; @@ -216,13 +221,15 @@ namespace DiscImageChef.Devices.Linux /// ATA protocol to use /// Which register contains the transfer count /// Set to true if the transfer count is in blocks, otherwise it is in bytes - internal static int SendAtaCommand(int fd, AtaRegistersLba28 registers, - out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) + internal static int SendAtaCommand(int fd, AtaRegistersLba28 registers, + out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, + bool transferBlocks, out double duration, + out bool sense) { - duration = 0; - sense = false; + duration = 0; + sense = false; errorRegisters = new AtaErrorRegistersLba28(); if(buffer == null) return -1; @@ -250,15 +257,16 @@ namespace DiscImageChef.Devices.Linux cdb[2] |= 0x20; - cdb[4] = registers.Feature; - cdb[6] = registers.SectorCount; - cdb[8] = registers.LbaLow; + cdb[4] = registers.Feature; + cdb[6] = registers.SectorCount; + cdb[8] = registers.LbaLow; cdb[10] = registers.LbaMid; cdb[12] = registers.LbaHigh; cdb[13] = registers.DeviceHead; cdb[14] = registers.Command; - int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, + int error = SendScsiCommand(fd, cdb, ref buffer, + out byte[] senseBuffer, timeout, AtaProtocolToScsiDirection(protocol), out duration, out sense); if(senseBuffer.Length < 22 || senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C) return error; @@ -266,11 +274,11 @@ namespace DiscImageChef.Devices.Linux errorRegisters.Error = senseBuffer[11]; errorRegisters.SectorCount = senseBuffer[13]; - errorRegisters.LbaLow = senseBuffer[15]; - errorRegisters.LbaMid = senseBuffer[17]; - errorRegisters.LbaHigh = senseBuffer[19]; - errorRegisters.DeviceHead = senseBuffer[20]; - errorRegisters.Status = senseBuffer[21]; + errorRegisters.LbaLow = senseBuffer[15]; + errorRegisters.LbaMid = senseBuffer[17]; + errorRegisters.LbaHigh = senseBuffer[19]; + errorRegisters.DeviceHead = senseBuffer[20]; + errorRegisters.Status = senseBuffer[21]; sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; @@ -291,20 +299,22 @@ namespace DiscImageChef.Devices.Linux /// ATA protocol to use /// Which register contains the transfer count /// Set to true if the transfer count is in blocks, otherwise it is in bytes - internal static int SendAtaCommand(int fd, AtaRegistersLba48 registers, - out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) + internal static int SendAtaCommand(int fd, AtaRegistersLba48 registers, + out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, + bool transferBlocks, out double duration, + out bool sense) { - duration = 0; - sense = false; + duration = 0; + sense = false; errorRegisters = new AtaErrorRegistersLba48(); if(buffer == null) return -1; byte[] cdb = new byte[16]; - cdb[0] = (byte)ScsiCommands.AtaPassThrough16; - cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); + cdb[0] = (byte)ScsiCommands.AtaPassThrough16; + cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); cdb[1] |= 0x01; if(transferRegister != AtaTransferRegister.NoTransfer && protocol != AtaProtocol.NonData) { @@ -326,20 +336,21 @@ namespace DiscImageChef.Devices.Linux cdb[2] |= 0x20; - cdb[3] = (byte)((registers.Feature & 0xFF00) >> 8); - cdb[4] = (byte)(registers.Feature & 0xFF); - cdb[5] = (byte)((registers.SectorCount & 0xFF00) >> 8); - cdb[6] = (byte)(registers.SectorCount & 0xFF); - cdb[7] = (byte)((registers.LbaLow & 0xFF00) >> 8); - cdb[8] = (byte)(registers.LbaLow & 0xFF); - cdb[9] = (byte)((registers.LbaMid & 0xFF00) >> 8); + cdb[3] = (byte)((registers.Feature & 0xFF00) >> 8); + cdb[4] = (byte)(registers.Feature & 0xFF); + cdb[5] = (byte)((registers.SectorCount & 0xFF00) >> 8); + cdb[6] = (byte)(registers.SectorCount & 0xFF); + cdb[7] = (byte)((registers.LbaLow & 0xFF00) >> 8); + cdb[8] = (byte)(registers.LbaLow & 0xFF); + cdb[9] = (byte)((registers.LbaMid & 0xFF00) >> 8); cdb[10] = (byte)(registers.LbaMid & 0xFF); cdb[11] = (byte)((registers.LbaHigh & 0xFF00) >> 8); cdb[12] = (byte)(registers.LbaHigh & 0xFF); cdb[13] = registers.DeviceHead; cdb[14] = registers.Command; - int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, + int error = SendScsiCommand(fd, cdb, ref buffer, + out byte[] senseBuffer, timeout, AtaProtocolToScsiDirection(protocol), out duration, out sense); if(senseBuffer.Length < 22 || senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C) return error; @@ -347,11 +358,11 @@ namespace DiscImageChef.Devices.Linux errorRegisters.Error = senseBuffer[11]; errorRegisters.SectorCount = (ushort)((senseBuffer[12] << 8) + senseBuffer[13]); - errorRegisters.LbaLow = (ushort)((senseBuffer[14] << 8) + senseBuffer[15]); - errorRegisters.LbaMid = (ushort)((senseBuffer[16] << 8) + senseBuffer[17]); - errorRegisters.LbaHigh = (ushort)((senseBuffer[18] << 8) + senseBuffer[19]); - errorRegisters.DeviceHead = senseBuffer[20]; - errorRegisters.Status = senseBuffer[21]; + errorRegisters.LbaLow = (ushort)((senseBuffer[14] << 8) + senseBuffer[15]); + errorRegisters.LbaMid = (ushort)((senseBuffer[16] << 8) + senseBuffer[17]); + errorRegisters.LbaHigh = (ushort)((senseBuffer[18] << 8) + senseBuffer[19]); + errorRegisters.DeviceHead = senseBuffer[20]; + errorRegisters.Status = senseBuffer[21]; sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; @@ -377,13 +388,16 @@ namespace DiscImageChef.Devices.Linux /// Command argument /// Response registers /// Size of block in bytes - internal static int SendMmcCommand(int 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) + internal static int SendMmcCommand(int 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) { response = null; duration = 0; - sense = false; + sense = false; if(buffer == null) return -1; @@ -392,24 +406,25 @@ namespace DiscImageChef.Devices.Linux IntPtr bufPtr = Marshal.AllocHGlobal(buffer.Length); ioCmd.write_flag = write; - ioCmd.is_ascmd = isApplication; - ioCmd.opcode = (uint)command; - ioCmd.arg = argument; - ioCmd.flags = flags; - ioCmd.blksz = blockSize; - ioCmd.blocks = blocks; + ioCmd.is_ascmd = isApplication; + ioCmd.opcode = (uint)command; + ioCmd.arg = argument; + ioCmd.flags = flags; + ioCmd.blksz = blockSize; + ioCmd.blocks = blocks; if(timeout > 0) { ioCmd.data_timeout_ns = timeout * 1000000000; - ioCmd.cmd_timeout_ms = timeout * 1000; + ioCmd.cmd_timeout_ms = timeout * 1000; } + ioCmd.data_ptr = (ulong)bufPtr; Marshal.Copy(buffer, 0, bufPtr, buffer.Length); DateTime start = DateTime.UtcNow; - int error = Extern.ioctlMmc(fd, LinuxIoctl.MmcIocCmd, ref ioCmd); - DateTime end = DateTime.UtcNow; + int error = Extern.ioctlMmc(fd, LinuxIoctl.MmcIocCmd, ref ioCmd); + DateTime end = DateTime.UtcNow; sense |= error < 0; @@ -433,7 +448,7 @@ namespace DiscImageChef.Devices.Linux internal static string ReadLink(string path) { IntPtr buf = Marshal.AllocHGlobal(4096); - int resultSize; + int resultSize; if(DetectOS.Is64Bit()) {