diff --git a/DiscImageChef.Devices/ChangeLog b/DiscImageChef.Devices/ChangeLog index 90950917e..4120bc907 100644 --- a/DiscImageChef.Devices/ChangeLog +++ b/DiscImageChef.Devices/ChangeLog @@ -1,3 +1,19 @@ +2015-10-14 Natalia Portillo + + * Structs.cs: + * DiscImageChef.Devices.csproj: + Added structs with ATA registers. + + * Command.cs: + * Device/Commands.cs: + Added ATA commands. + + * Enums.cs: + Added SCSI's ATA Command Pass-Through enumerations. + + * Linux/Command.cs: + Added ATA commands using libATA's SATL. + 2015-10-14 Natalia Portillo * Linux/Enums.cs: diff --git a/DiscImageChef.Devices/Command.cs b/DiscImageChef.Devices/Command.cs index 857c0de0a..9cb5637e6 100644 --- a/DiscImageChef.Devices/Command.cs +++ b/DiscImageChef.Devices/Command.cs @@ -128,6 +128,102 @@ namespace DiscImageChef.Devices throw new InvalidOperationException(String.Format("Platform {0} not yet supported.", ptID)); } } + + public static int SendAtaCommand(object fd, Structs.AtaRegistersCHS registers, + out Structs.AtaErrorRegistersCHS errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + Interop.PlatformID ptID = DetectOS.GetRealPlatformID(); + + return SendAtaCommand(ptID, fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, out sense); + } + + public static int SendAtaCommand(Interop.PlatformID ptID, object fd, Structs.AtaRegistersCHS registers, + out Structs.AtaErrorRegistersCHS errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + switch (ptID) + { + case Interop.PlatformID.Win32NT: + { + throw new NotImplementedException(); + } + case Interop.PlatformID.Linux: + { + return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, out sense); + } + default: + throw new InvalidOperationException(String.Format("Platform {0} not yet supported.", ptID)); + } + } + + public static int SendAtaCommand(object fd, Structs.AtaRegistersLBA28 registers, + out Structs.AtaErrorRegistersLBA28 errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + Interop.PlatformID ptID = DetectOS.GetRealPlatformID(); + + return SendAtaCommand(ptID, fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, out sense); + } + + public static int SendAtaCommand(Interop.PlatformID ptID, object fd, Structs.AtaRegistersLBA28 registers, + out Structs.AtaErrorRegistersLBA28 errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + switch (ptID) + { + case Interop.PlatformID.Win32NT: + { + throw new NotImplementedException(); + } + case Interop.PlatformID.Linux: + { + return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, out sense); + } + default: + throw new InvalidOperationException(String.Format("Platform {0} not yet supported.", ptID)); + } + } + + public static int SendAtaCommand(object fd, Structs.AtaRegistersLBA48 registers, + out Structs.AtaErrorRegistersLBA48 errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + Interop.PlatformID ptID = DetectOS.GetRealPlatformID(); + + return SendAtaCommand(ptID, fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, out sense); + } + + public static int SendAtaCommand(Interop.PlatformID ptID, object fd, Structs.AtaRegistersLBA48 registers, + out Structs.AtaErrorRegistersLBA48 errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + switch (ptID) + { + case Interop.PlatformID.Win32NT: + { + throw new NotImplementedException(); + } + case Interop.PlatformID.Linux: + { + return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, out sense); + } + default: + throw new InvalidOperationException(String.Format("Platform {0} not yet supported.", ptID)); + } + } } } diff --git a/DiscImageChef.Devices/Device/Commands.cs b/DiscImageChef.Devices/Device/Commands.cs index 1a3497499..e9880f4f6 100644 --- a/DiscImageChef.Devices/Device/Commands.cs +++ b/DiscImageChef.Devices/Device/Commands.cs @@ -56,6 +56,30 @@ namespace DiscImageChef.Devices { return Command.SendScsiCommand(platformID, fd, cdb, ref buffer, out senseBuffer, timeout, direction, out duration, out sense); } + + public int SendAtaCommand(Structs.AtaRegistersCHS registers, out Structs.AtaErrorRegistersCHS errorRegisters, + Enums.AtaProtocol protocol, Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + return Command.SendAtaCommand(platformID, fd, registers, out errorRegisters, protocol, transferRegister, + ref buffer, timeout, transferBlocks, out duration, out sense); + } + + public int SendAtaCommand(Structs.AtaRegistersLBA28 registers, out Structs.AtaErrorRegistersLBA28 errorRegisters, + Enums.AtaProtocol protocol, Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + return Command.SendAtaCommand(platformID, fd, registers, out errorRegisters, protocol, transferRegister, + ref buffer, timeout, transferBlocks, out duration, out sense); + } + + public int SendAtaCommand(Structs.AtaRegistersLBA48 registers, out Structs.AtaErrorRegistersLBA48 errorRegisters, + Enums.AtaProtocol protocol, Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + return Command.SendAtaCommand(platformID, fd, registers, out errorRegisters, protocol, transferRegister, + ref buffer, timeout, transferBlocks, out duration, out sense); + } } } diff --git a/DiscImageChef.Devices/DiscImageChef.Devices.csproj b/DiscImageChef.Devices/DiscImageChef.Devices.csproj index a92c2b35d..dedb491df 100644 --- a/DiscImageChef.Devices/DiscImageChef.Devices.csproj +++ b/DiscImageChef.Devices/DiscImageChef.Devices.csproj @@ -50,6 +50,7 @@ + diff --git a/DiscImageChef.Devices/Enums.cs b/DiscImageChef.Devices/Enums.cs index e766fe150..712a794c5 100644 --- a/DiscImageChef.Devices/Enums.cs +++ b/DiscImageChef.Devices/Enums.cs @@ -2398,6 +2398,87 @@ namespace DiscImageChef.Devices Unspecified } + #region SCSI's ATA Command Pass-Through + public enum AtaProtocol : byte + { + /// + /// Requests a device hard reset (pin 1) + /// + HardReset = 0, + /// + /// Requests a device soft reset (COMRESET issue) + /// + SoftReset = 1, + /// + /// No data is to be transfered + /// + NonData = 3, + /// + /// Requests a host->device transfer using PIO + /// + PioIn = 4, + /// + /// Requests a device->host transfer using PIO + /// + PioOut = 5, + /// + /// Requests a DMA transfer + /// + Dma = 6, + /// + /// Requests to queue a DMA transfer + /// + DmaQueued = 7, + /// + /// Requests device diagnostics + /// + DeviceDiagnostic = 8, + /// + /// Requets device reset + /// + DeviceReset = 9, + /// + /// Requests a host->device transfer using UltraDMA + /// + UDmaIn = 10, + /// + /// Requests a device->host transfer using UltraDMA + /// + UDmaOut = 11, + /// + /// Unknown Serial ATA + /// + FPDma = 12, + /// + /// Requests the Extended ATA Status Return Descriptor + /// + ReturnResponse = 15 + } + + /// + /// Indicates the STL which ATA register contains the length of data to + /// be transfered + /// + public enum AtaTransferRegister : byte + { + /// + /// There is no transfer + /// + NoTransfer = 0, + /// + /// FEATURE register contains the data length + /// + Feature = 1, + /// + /// SECTOR_COUNT register contains the data length + /// + SectorCount = 2, + /// + /// The STPSIU contains the data length + /// + SPTSIU = 3 + } + #endregion SCSI's ATA Command Pass-Through } } diff --git a/DiscImageChef.Devices/Linux/Command.cs b/DiscImageChef.Devices/Linux/Command.cs index 161de8e72..e06d54a11 100644 --- a/DiscImageChef.Devices/Linux/Command.cs +++ b/DiscImageChef.Devices/Linux/Command.cs @@ -98,6 +98,248 @@ namespace DiscImageChef.Devices.Linux return error; } + + static ScsiIoctlDirection AtaProtocolToScsiDirection(Enums.AtaProtocol protocol) + { + switch (protocol) + { + case Enums.AtaProtocol.DeviceDiagnostic: + case Enums.AtaProtocol.DeviceReset: + case Enums.AtaProtocol.HardReset: + case Enums.AtaProtocol.NonData: + case Enums.AtaProtocol.SoftReset: + case Enums.AtaProtocol.ReturnResponse: + return ScsiIoctlDirection.None; + case Enums.AtaProtocol.PioIn: + case Enums.AtaProtocol.UDmaIn: + return ScsiIoctlDirection.Out; + case Enums.AtaProtocol.PioOut: + case Enums.AtaProtocol.UDmaOut: + return ScsiIoctlDirection.In; + default: + return ScsiIoctlDirection.Unspecified; + } + } + + internal static int SendAtaCommand(int fd, Structs.AtaRegistersCHS registers, + out Structs.AtaErrorRegistersCHS errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new Structs.AtaErrorRegistersCHS(); + + if (buffer == null) + return -1; + + byte[] cdb = new byte[12]; + cdb[0] = (byte)Enums.ScsiCommands.AtaPassThrough; + cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); + if (transferRegister != Enums.AtaTransferRegister.NoTransfer && + protocol != Enums.AtaProtocol.NonData) + { + switch (protocol) + { + case Enums.AtaProtocol.PioIn: + case Enums.AtaProtocol.UDmaIn: + cdb[2] = 0x08; + break; + default: + cdb[2] = 0x00; + break; + } + + if (transferBlocks) + cdb[2] |= 0x04; + + cdb[2] |= (byte)((int)transferRegister & 0x03); + } + + cdb[3] = registers.feature; + cdb[4] = registers.sectorCount; + cdb[5] = registers.sector; + cdb[6] = registers.cylinderHigh; + cdb[7] = registers.cylinderLow; + cdb[8] = registers.deviceHead; + cdb[9] = registers.command; + + byte[] senseBuffer; + int error = SendScsiCommand(fd, cdb, ref buffer, out senseBuffer, timeout, AtaProtocolToScsiDirection(protocol), out duration, out sense); + + // Now get error registers + byte[] returnCdb = new byte[12]; + returnCdb[0] = (byte)Enums.ScsiCommands.AtaPassThrough; + returnCdb[1] = (byte)(((byte)Enums.AtaProtocol.ReturnResponse << 1) & 0x1E); + byte[] returnBuffer = new byte[14]; + bool returnSense; + double returnDuration; + + SendScsiCommand(fd, returnCdb, ref returnBuffer, out senseBuffer, timeout, ScsiIoctlDirection.In, out returnDuration, out returnSense); + if (returnBuffer[0] != 0x09 && returnBuffer[1] != 0x0C) + return error; + + errorRegisters.error = returnBuffer[3]; + errorRegisters.sectorCount = returnBuffer[5]; + errorRegisters.sector = returnBuffer[7]; + errorRegisters.cylinderHigh = returnBuffer[9]; + errorRegisters.cylinderLow = returnBuffer[11]; + errorRegisters.deviceHead = returnBuffer[12]; + errorRegisters.status = returnBuffer[13]; + + sense |= error != 0; + + return error; + } + + internal static int SendAtaCommand(int fd, Structs.AtaRegistersLBA28 registers, + out Structs.AtaErrorRegistersLBA28 errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new Structs.AtaErrorRegistersLBA28(); + + if (buffer == null) + return -1; + + byte[] cdb = new byte[12]; + cdb[0] = (byte)Enums.ScsiCommands.AtaPassThrough; + cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); + if (transferRegister != Enums.AtaTransferRegister.NoTransfer && + protocol != Enums.AtaProtocol.NonData) + { + switch (protocol) + { + case Enums.AtaProtocol.PioIn: + case Enums.AtaProtocol.UDmaIn: + cdb[2] = 0x08; + break; + default: + cdb[2] = 0x00; + break; + } + + if (transferBlocks) + cdb[2] |= 0x04; + + cdb[2] |= (byte)((int)transferRegister & 0x03); + } + + cdb[3] = registers.feature; + cdb[4] = registers.sectorCount; + cdb[5] = registers.lbaLow; + cdb[6] = registers.lbaMid; + cdb[7] = registers.lbaHigh; + cdb[8] = registers.deviceHead; + cdb[9] = registers.command; + + byte[] senseBuffer; + int error = SendScsiCommand(fd, cdb, ref buffer, out senseBuffer, timeout, AtaProtocolToScsiDirection(protocol), out duration, out sense); + + // Now get error registers + byte[] returnCdb = new byte[12]; + returnCdb[0] = (byte)Enums.ScsiCommands.AtaPassThrough; + returnCdb[1] = (byte)(((byte)Enums.AtaProtocol.ReturnResponse << 1) & 0x1E); + byte[] returnBuffer = new byte[14]; + bool returnSense; + double returnDuration; + + SendScsiCommand(fd, returnCdb, ref returnBuffer, out senseBuffer, timeout, ScsiIoctlDirection.In, out returnDuration, out returnSense); + if (returnBuffer[0] != 0x09 && returnBuffer[1] != 0x0C) + return error; + + errorRegisters.error = returnBuffer[3]; + errorRegisters.sectorCount = returnBuffer[5]; + errorRegisters.lbaLow = returnBuffer[7]; + errorRegisters.lbaMid = returnBuffer[9]; + errorRegisters.lbaHigh = returnBuffer[11]; + errorRegisters.deviceHead = returnBuffer[12]; + errorRegisters.status = returnBuffer[13]; + + sense |= error != 0; + + return error; + } + + internal static int SendAtaCommand(int fd, Structs.AtaRegistersLBA48 registers, + out Structs.AtaErrorRegistersLBA48 errorRegisters, Enums.AtaProtocol protocol, + Enums.AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new Structs.AtaErrorRegistersLBA48(); + + if (buffer == null) + return -1; + + byte[] cdb = new byte[16]; + cdb[0] = (byte)Enums.ScsiCommands.AtaPassThrough16; + cdb[1] |= 0x01; + cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); + if (transferRegister != Enums.AtaTransferRegister.NoTransfer && + protocol != Enums.AtaProtocol.NonData) + { + switch (protocol) + { + case Enums.AtaProtocol.PioIn: + case Enums.AtaProtocol.UDmaIn: + cdb[2] = 0x08; + break; + default: + cdb[2] = 0x00; + break; + } + + if (transferBlocks) + cdb[2] |= 0x04; + + cdb[2] |= (byte)((int)transferRegister & 0x03); + } + + 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; + + byte[] senseBuffer; + int error = SendScsiCommand(fd, cdb, ref buffer, out senseBuffer, timeout, AtaProtocolToScsiDirection(protocol), out duration, out sense); + + // Now get error registers + byte[] returnCdb = new byte[16]; + returnCdb[0] = (byte)Enums.ScsiCommands.AtaPassThrough16; + returnCdb[1] = (byte)(((byte)Enums.AtaProtocol.ReturnResponse << 1) & 0x1E); + byte[] returnBuffer = new byte[14]; + bool returnSense; + double returnDuration; + + SendScsiCommand(fd, returnCdb, ref returnBuffer, out senseBuffer, timeout, ScsiIoctlDirection.In, out returnDuration, out returnSense); + if (returnBuffer[0] != 0x09 && returnBuffer[1] != 0x0C) + return error; + + errorRegisters.error = returnBuffer[3]; + + errorRegisters.sectorCount = (ushort)((returnBuffer[4] << 8) + returnBuffer[5]); + errorRegisters.lbaLow = (ushort)((returnBuffer[6] << 8) + returnBuffer[7]); + errorRegisters.lbaMid = (ushort)((returnBuffer[8] << 8) + returnBuffer[9]); + errorRegisters.lbaHigh = (ushort)((returnBuffer[10] << 8) + returnBuffer[11]); + errorRegisters.deviceHead = returnBuffer[12]; + errorRegisters.status = returnBuffer[13]; + + sense |= error != 0; + + return error; + } } } diff --git a/DiscImageChef.Devices/Structs.cs b/DiscImageChef.Devices/Structs.cs new file mode 100644 index 000000000..a0e8dd674 --- /dev/null +++ b/DiscImageChef.Devices/Structs.cs @@ -0,0 +1,114 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Structs.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright (C) 2011-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; + +namespace DiscImageChef.Devices +{ + public static class Structs + { + public struct AtaRegistersCHS + { + public byte feature; + public byte sectorCount; + public byte sector; + public byte cylinderLow; + public byte cylinderHigh; + public byte deviceHead; + public byte command; + } + + public struct AtaRegistersLBA28 + { + public byte feature; + public byte sectorCount; + public byte lbaLow; + public byte lbaMid; + public byte lbaHigh; + public byte deviceHead; + public byte command; + } + + public struct AtaRegistersLBA48 + { + public ushort feature; + public ushort sectorCount; + public ushort lbaLow; + public ushort lbaMid; + public ushort lbaHigh; + public byte deviceHead; + public byte command; + } + + public struct AtaErrorRegistersCHS + { + public byte status; + public byte error; + public byte sectorCount; + public byte sector; + public byte cylinderLow; + public byte cylinderHigh; + public byte deviceHead; + public byte command; + } + + public struct AtaErrorRegistersLBA28 + { + public byte status; + public byte error; + public byte sectorCount; + public byte lbaLow; + public byte lbaMid; + public byte lbaHigh; + public byte deviceHead; + public byte command; + } + + public struct AtaErrorRegistersLBA48 + { + public byte status; + public byte error; + public ushort sectorCount; + public ushort lbaLow; + public ushort lbaMid; + public ushort lbaHigh; + public byte deviceHead; + public byte command; + } + } +} +