diff --git a/DiscImageChef.Devices/ChangeLog b/DiscImageChef.Devices/ChangeLog index 1d5be10a7..1f2af695e 100644 --- a/DiscImageChef.Devices/ChangeLog +++ b/DiscImageChef.Devices/ChangeLog @@ -1,3 +1,22 @@ +2016-02-10 Natalia Portillo + + * Device/AtaCommands/Ata28.cs: + Correct SEEK command to not transfer anything. + + * Device/AtaCommands/AtaCHS.cs: + Correct SEEK command to not transfer anything. + Added differentiation between READ and READ WITH RETRIES for + CHS mode. + Changed blocksize to unsigned on READ LONG. + + * Device/AtaCommands/MCPT.cs: + Change MCPT enable and disable commands to use CHS + registers. + + * Linux/Command.cs: + Linux kernel granularity for SG_IO is only 1ms, use .NET + counters for faster times. Solves "infinity" speeds on SSDs. + 2016-02-08 Natalia Portillo * Device/AtaCommands/Cfa.cs: diff --git a/DiscImageChef.Devices/Device/AtaCommands/Ata28.cs b/DiscImageChef.Devices/Device/AtaCommands/Ata28.cs index 0ce3527c3..1b8e2aa4d 100644 --- a/DiscImageChef.Devices/Device/AtaCommands/Ata28.cs +++ b/DiscImageChef.Devices/Device/AtaCommands/Ata28.cs @@ -244,8 +244,8 @@ namespace DiscImageChef.Devices registers.lbaLow = (byte)((lba & 0xFF) / 0x1); registers.deviceHead += 0x40; - lastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, AtaTransferRegister.SectorCount, - ref buffer, timeout, true, out duration, out sense); + lastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, AtaTransferRegister.NoTransfer, + ref buffer, timeout, false, out duration, out sense); error = lastError != 0; DicConsole.DebugWriteLine("ATA Device", "SEEK took {0} ms.", duration); diff --git a/DiscImageChef.Devices/Device/AtaCommands/AtaCHS.cs b/DiscImageChef.Devices/Device/AtaCommands/AtaCHS.cs index 76c50f3a1..b614f7301 100644 --- a/DiscImageChef.Devices/Device/AtaCommands/AtaCHS.cs +++ b/DiscImageChef.Devices/Device/AtaCommands/AtaCHS.cs @@ -164,6 +164,11 @@ namespace DiscImageChef.Devices public bool Read(out byte[] buffer, out AtaErrorRegistersCHS statusRegisters, ushort cylinder, byte head, byte sector, byte count, uint timeout, out double duration) + { + return Read(out buffer, out statusRegisters, true, cylinder, head, sector, count, timeout, out duration); + } + + public bool Read(out byte[] buffer, out AtaErrorRegistersCHS statusRegisters, bool retry, ushort cylinder, byte head, byte sector, byte count, uint timeout, out double duration) { if (count == 0) buffer = new byte[512 * 256]; @@ -172,7 +177,10 @@ namespace DiscImageChef.Devices AtaRegistersCHS registers = new AtaRegistersCHS(); bool sense; - registers.command = (byte)AtaCommands.ReadRetry; + if(retry) + registers.command = (byte)AtaCommands.ReadRetry; + else + registers.command = (byte)AtaCommands.Read; registers.sectorCount = count; registers.cylinderHigh = (byte)((cylinder & 0xFF00) / 0x100); registers.cylinderLow = (byte)((cylinder & 0xFF) / 0x1); @@ -188,12 +196,12 @@ namespace DiscImageChef.Devices return sense; } - public bool ReadLong(out byte[] buffer, out AtaErrorRegistersCHS statusRegisters, ushort cylinder, byte head, byte sector, int blockSize, uint timeout, out double duration) + public bool ReadLong(out byte[] buffer, out AtaErrorRegistersCHS statusRegisters, ushort cylinder, byte head, byte sector, uint blockSize, uint timeout, out double duration) { return ReadLong(out buffer, out statusRegisters, true, cylinder, head, sector, blockSize, timeout, out duration); } - public bool ReadLong(out byte[] buffer, out AtaErrorRegistersCHS statusRegisters, bool retry, ushort cylinder, byte head, byte sector, int blockSize, uint timeout, out double duration) + public bool ReadLong(out byte[] buffer, out AtaErrorRegistersCHS statusRegisters, bool retry, ushort cylinder, byte head, byte sector, uint blockSize, uint timeout, out double duration) { buffer = new byte[blockSize]; AtaRegistersCHS registers = new AtaRegistersCHS(); @@ -230,7 +238,7 @@ namespace DiscImageChef.Devices registers.deviceHead = (byte)(head & 0x0F); registers.sector = sector; - lastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, AtaTransferRegister.SectorCount, + lastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, out sense); error = lastError != 0; diff --git a/DiscImageChef.Devices/Device/AtaCommands/MCPT.cs b/DiscImageChef.Devices/Device/AtaCommands/MCPT.cs index 3c4cdd96a..532eccc54 100644 --- a/DiscImageChef.Devices/Device/AtaCommands/MCPT.cs +++ b/DiscImageChef.Devices/Device/AtaCommands/MCPT.cs @@ -43,10 +43,10 @@ namespace DiscImageChef.Devices { public partial class Device { - public bool EnableMediaCardPassThrough(out AtaErrorRegistersLBA28 statusRegisters, uint lba, uint timeout, out double duration) + public bool EnableMediaCardPassThrough(out AtaErrorRegistersCHS statusRegisters, uint timeout, out double duration) { byte[] buffer = new byte[0]; - AtaRegistersLBA28 registers = new AtaRegistersLBA28(); + AtaRegistersCHS registers = new AtaRegistersCHS(); bool sense; registers.command = (byte)AtaCommands.CheckMediaCardType; @@ -61,10 +61,10 @@ namespace DiscImageChef.Devices return sense; } - public bool DisableMediaCardPassThrough(out AtaErrorRegistersLBA28 statusRegisters, uint lba, uint timeout, out double duration) + public bool DisableMediaCardPassThrough(out AtaErrorRegistersCHS statusRegisters, uint timeout, out double duration) { byte[] buffer = new byte[0]; - AtaRegistersLBA28 registers = new AtaRegistersLBA28(); + AtaRegistersCHS registers = new AtaRegistersCHS(); bool sense; registers.command = (byte)AtaCommands.CheckMediaCardType; diff --git a/DiscImageChef.Devices/Linux/Command.cs b/DiscImageChef.Devices/Linux/Command.cs index 2dce631a2..8e7ea4d70 100644 --- a/DiscImageChef.Devices/Linux/Command.cs +++ b/DiscImageChef.Devices/Linux/Command.cs @@ -84,7 +84,9 @@ namespace DiscImageChef.Devices.Linux Marshal.Copy(cdb, 0, io_hdr.cmdp, cdb.Length); Marshal.Copy(senseBuffer, 0, io_hdr.sbp, senseBuffer.Length); + DateTime start = DateTime.UtcNow; int error = Extern.ioctlSg(fd, LinuxIoctl.SG_IO, ref io_hdr); + DateTime end = DateTime.UtcNow; if (error < 0) error = Marshal.GetLastWin32Error(); @@ -95,7 +97,10 @@ namespace DiscImageChef.Devices.Linux sense |= (io_hdr.info & SgInfo.OkMask) != SgInfo.Ok; - duration = (double)io_hdr.duration; + if (io_hdr.duration > 0) + duration = (double)io_hdr.duration; + else + duration = (end - start).TotalMilliseconds; Marshal.FreeHGlobal(io_hdr.dxferp); Marshal.FreeHGlobal(io_hdr.cmdp); diff --git a/DiscImageChef.Metadata/ChangeLog b/DiscImageChef.Metadata/ChangeLog index a4ece50d8..ec002c581 100644 --- a/DiscImageChef.Metadata/ChangeLog +++ b/DiscImageChef.Metadata/ChangeLog @@ -1,3 +1,8 @@ +2016-02-10 Natalia Portillo + + * DeviceReport.cs: + Added support for ATA devices. + 2016-02-05 Natalia Portillo * MediaType.cs: diff --git a/DiscImageChef.Metadata/DeviceReport.cs b/DiscImageChef.Metadata/DeviceReport.cs index 86ae7de3a..e80d04bf5 100644 --- a/DiscImageChef.Metadata/DeviceReport.cs +++ b/DiscImageChef.Metadata/DeviceReport.cs @@ -51,6 +51,10 @@ namespace DiscImageChef.Metadata public ataType ATA; public ataType ATAPI; public scsiType SCSI; + public bool CompactFlash; + + [XmlIgnoreAttribute()] + public bool CompactFlashSpecified; } public class usbType @@ -73,8 +77,6 @@ namespace DiscImageChef.Metadata public class ataType { - public chsType CHS; - public chsType CurrentCHS; public string AdditionalPID; public Identify.TransferMode APIOSupported; @@ -98,7 +100,6 @@ namespace DiscImageChef.Metadata public Identify.TransferMode DMAActive; public Identify.TransferMode DMASupported; public byte DMATransferTimingMode; - public ushort EccBytes; public ushort EnhancedSecurityEraseTime; public Identify.CommandSetBit EnabledCommandSet; public Identify.CommandSetBit2 EnabledCommandSet2; @@ -111,11 +112,6 @@ namespace DiscImageChef.Metadata public Identify.GeneralConfigurationBit GeneralConfiguration; public ushort HardwareResetResult; public ushort InterseekDelay; - public uint LBASectors; - public uint LBASectorsCurrent; - public ulong LBA48Sectors; - public ushort LogicalAlignment; - public uint LogicalSectorWords; public Identify.MajorVersionBit MajorVersion; public ushort MasterPasswordRevisionCode; public ushort MaxDownloadMicroMode3; @@ -134,9 +130,7 @@ namespace DiscImageChef.Metadata public uint NVCacheSize; public ushort NVCacheWriteSpeed; public byte NVEstimatedSpinUp; - public ushort NominalRotationRate; public ushort PacketBusRelease; - public ushort PhysLogSectorSize; public byte PIOTransferTimingMode; public byte RecommendedAAM; public ushort RecommendedMDMACycleTime; @@ -160,12 +154,14 @@ namespace DiscImageChef.Metadata public Identify.TrustedComputingBit TrustedComputing; public Identify.TransferMode UDMAActive; public Identify.TransferMode UDMASupported; - public ushort UnformattedBPT; - public ushort UnformattedBPS; public byte WRVMode; public uint WRVSectorCountMode3; public uint WRVSectorCountMode2; + public testedMediaType ReadCapabilities; + public testedMediaType[] RemovableMedias; + + [XmlIgnoreAttribute()] public bool AdditionalPIDSpecified; [XmlIgnoreAttribute()] @@ -211,8 +207,6 @@ namespace DiscImageChef.Metadata [XmlIgnoreAttribute()] public bool DMATransferTimingModeSpecified; [XmlIgnoreAttribute()] - public bool EccBytesSpecified; - [XmlIgnoreAttribute()] public bool EnhancedSecurityEraseTimeSpecified; [XmlIgnoreAttribute()] public bool EnabledCommandSetSpecified; @@ -239,16 +233,6 @@ namespace DiscImageChef.Metadata [XmlIgnoreAttribute()] public bool InterseekDelaySpecified; [XmlIgnoreAttribute()] - public bool LBASectorsSpecified; - [XmlIgnoreAttribute()] - public bool LBASectorsCurrentSpecified; - [XmlIgnoreAttribute()] - public bool LBA48SectorsSpecified; - [XmlIgnoreAttribute()] - public bool LogicalAlignmentSpecified; - [XmlIgnoreAttribute()] - public bool LogicalSectorWordsSpecified; - [XmlIgnoreAttribute()] public bool MajorVersionSpecified; [XmlIgnoreAttribute()] public bool MasterPasswordRevisionCodeSpecified; @@ -285,12 +269,8 @@ namespace DiscImageChef.Metadata [XmlIgnoreAttribute()] public bool NVEstimatedSpinUpSpecified; [XmlIgnoreAttribute()] - public bool NominalRotationRateSpecified; - [XmlIgnoreAttribute()] public bool PacketBusReleaseSpecified; [XmlIgnoreAttribute()] - public bool PhysLogSectorSizeSpecified; - [XmlIgnoreAttribute()] public bool PIOTransferTimingModeSpecified; [XmlIgnoreAttribute()] public bool RecommendedAAMSpecified; @@ -337,10 +317,6 @@ namespace DiscImageChef.Metadata [XmlIgnoreAttribute()] public bool UDMASupportedSpecified; [XmlIgnoreAttribute()] - public bool UnformattedBPTSpecified; - [XmlIgnoreAttribute()] - public bool UnformattedBPSSpecified; - [XmlIgnoreAttribute()] public bool WRVModeSpecified; [XmlIgnoreAttribute()] public bool WRVSectorCountMode3Specified; @@ -888,6 +864,82 @@ namespace DiscImageChef.Metadata public bool SupportsReadLong16Specified; [XmlIgnoreAttribute()] public bool SupportsReadLongSpecified; + + public chsType CHS; + public chsType CurrentCHS; + public uint LBASectors; + public ulong LBA48Sectors; + public ushort LogicalAlignment; + public ushort NominalRotationRate; + public uint PhysicalBlockSize; + public bool SolidStateDevice; + public ushort UnformattedBPT; + public ushort UnformattedBPS; + + [XmlIgnoreAttribute()] + public bool LBASectorsSpecified; + [XmlIgnoreAttribute()] + public bool LBA48SectorsSpecified; + [XmlIgnoreAttribute()] + public bool LogicalAlignmentSpecified; + [XmlIgnoreAttribute()] + public bool NominalRotationRateSpecified; + [XmlIgnoreAttribute()] + public bool PhysicalBlockSizeSpecified; + [XmlIgnoreAttribute()] + public bool SolidStateDeviceSpecified; + [XmlIgnoreAttribute()] + public bool UnformattedBPTSpecified; + [XmlIgnoreAttribute()] + public bool UnformattedBPSSpecified; + + public bool SupportsReadDmaLba; + public bool SupportsReadDmaRetryLba; + public bool SupportsReadLba; + public bool SupportsReadRetryLba; + public bool SupportsReadLongLba; + public bool SupportsReadLongRetryLba; + public bool SupportsSeekLba; + + public bool SupportsReadDmaLba48; + public bool SupportsReadLba48; + + public bool SupportsReadDma; + public bool SupportsReadDmaRetry; + public bool SupportsReadRetry; + public bool SupportsReadLongRetry; + public bool SupportsSeek; + + [XmlIgnoreAttribute()] + public bool SupportsReadDmaLbaSpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadDmaRetryLbaSpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadLbaSpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadRetryLbaSpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadLongLbaSpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadLongRetryLbaSpecified; + [XmlIgnoreAttribute()] + public bool SupportsSeekLbaSpecified; + + [XmlIgnoreAttribute()] + public bool SupportsReadDmaLba48Specified; + [XmlIgnoreAttribute()] + public bool SupportsReadLba48Specified; + + [XmlIgnoreAttribute()] + public bool SupportsReadDmaSpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadDmaRetrySpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadRetrySpecified; + [XmlIgnoreAttribute()] + public bool SupportsReadLongRetrySpecified; + [XmlIgnoreAttribute()] + public bool SupportsSeekSpecified; } public class sscType diff --git a/DiscImageChef/ChangeLog b/DiscImageChef/ChangeLog index 6a38e924f..93bddbda7 100644 --- a/DiscImageChef/ChangeLog +++ b/DiscImageChef/ChangeLog @@ -1,3 +1,15 @@ +2016-02-10 Natalia Portillo + + * Commands/DeviceInfo.cs: + Added support for Media Card Pass Through detection and + information. + + * Commands/DumpMedia.cs: + * Commands/MediaInfo.cs: + * Commands/MediaScan.cs: + * Commands/DeviceReport.cs: + Added support for ATA devices. + 2016-02-05 Natalia Portillo * Commands/DeviceReport.cs: diff --git a/DiscImageChef/Commands/DeviceInfo.cs b/DiscImageChef/Commands/DeviceInfo.cs index a014b7903..7bd85c3d5 100644 --- a/DiscImageChef/Commands/DeviceInfo.cs +++ b/DiscImageChef/Commands/DeviceInfo.cs @@ -125,6 +125,43 @@ namespace DiscImageChef.Commands doWriteFile(options.OutputPrefix, "_ata_identify.bin", "ATA IDENTIFY", ataBuf); DicConsole.WriteLine(Decoders.ATA.Identify.Prettify(ataBuf)); + + double duration; + dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out duration); + + if (errorRegisters.sector == 0xAA && errorRegisters.sectorCount == 0x55) + { + DicConsole.WriteLine("Device supports the Media Card Pass Through Command Set"); + switch (errorRegisters.deviceHead & 0x7) + { + case 0: + DicConsole.WriteLine("Device reports incorrect media card type"); + break; + case 1: + DicConsole.WriteLine("Device contains a Secure Digital card"); + break; + case 2: + DicConsole.WriteLine("Device contains a MultiMediaCard "); + break; + case 3: + DicConsole.WriteLine("Device contains a Secure Digital I/O card"); + break; + case 4: + DicConsole.WriteLine("Device contains a Smart Media card"); + break; + default: + DicConsole.WriteLine("Device contains unknown media card type {0}", errorRegisters.deviceHead & 0x07); + break; + } + + if ((errorRegisters.deviceHead & 0x08) == 0x08) + DicConsole.WriteLine("Media card is write protected"); + + ushort specificData = (ushort)((errorRegisters.cylinderHigh * 0x100) + errorRegisters.cylinderLow); + if (specificData != 0) + DicConsole.WriteLine("Card specific data: 0x{0:X4}", specificData); + } + break; } case DeviceType.ATAPI: diff --git a/DiscImageChef/Commands/DeviceReport.cs b/DiscImageChef/Commands/DeviceReport.cs index bf7323e9e..a6dae7b48 100644 --- a/DiscImageChef/Commands/DeviceReport.cs +++ b/DiscImageChef/Commands/DeviceReport.cs @@ -115,6 +115,7 @@ namespace DiscImageChef.Commands xmlFile = dev.Model + ".xml"; ConsoleKeyInfo pressedKey; + bool removable = false; if (dev.IsUSB) { @@ -143,6 +144,7 @@ namespace DiscImageChef.Commands } report.USB.RemovableMedia = pressedKey.Key == ConsoleKey.Y; + removable = true; } } @@ -173,6 +175,7 @@ namespace DiscImageChef.Commands } report.FireWire.RemovableMedia = pressedKey.Key == ConsoleKey.Y; + removable = true; } } @@ -184,49 +187,36 @@ namespace DiscImageChef.Commands { Decoders.ATA.Identify.IdentifyDevice ataId = Decoders.ATA.Identify.Decode(buffer).Value; - report.ATA = new ataType(); - - if (ataId.CurrentCylinders != 0 || - ataId.CurrentHeads != 0 || - ataId.CurrentSectorsPerTrack != 0) + if ((ushort)ataId.GeneralConfiguration == 0x848A) { - report.ATA.CHS = new chsType(); - report.ATA.CHS.Cylinders = ataId.Cylinders; - report.ATA.CHS.Heads = ataId.Heads; - report.ATA.CHS.Sectors = ataId.SectorsPerTrack; + report.CompactFlash = true; + report.CompactFlashSpecified = true; + removable = false; } - - if ((ataId.Cylinders != ataId.CurrentCylinders || - ataId.Heads != ataId.CurrentHeads || - ataId.SectorsPerTrack != ataId.CurrentSectorsPerTrack) && - (ataId.CurrentCylinders != 0 || - ataId.CurrentHeads != 0 || - ataId.CurrentSectorsPerTrack != 0)) + else if (!removable && ataId.GeneralConfiguration.HasFlag(Decoders.ATA.Identify.GeneralConfigurationBit.Removable)) { - report.ATA.CurrentCHS = new chsType(); - report.ATA.CurrentCHS.Cylinders = ataId.CurrentCylinders; - report.ATA.CurrentCHS.Heads = ataId.CurrentHeads; - report.ATA.CurrentCHS.Sectors = ataId.CurrentSectorsPerTrack; - } - - if (ataId.Capabilities.HasFlag(Decoders.ATA.Identify.CapabilitiesBit.LBASupport) && ataId.LBASectors != 0) - { - report.ATA.LBASectors = ataId.LBASectors; - report.ATA.LBASectorsSpecified = true; - - if (ataId.LBASectors != 0 && ataId.LBASectors != ataId.CurrentSectors) + pressedKey = new ConsoleKeyInfo(); + while (pressedKey.Key != ConsoleKey.Y && pressedKey.Key != ConsoleKey.N) { - report.ATA.LBASectorsCurrent = ataId.LBASectors; - report.ATA.LBASectorsCurrentSpecified = true; + DicConsole.Write("Is the media removable from the reading/writing elements? (Y/N): "); + pressedKey = System.Console.ReadKey(); + DicConsole.WriteLine(); } + + removable = pressedKey.Key == ConsoleKey.Y; } - if (ataId.CommandSet2.HasFlag(Decoders.ATA.Identify.CommandSetBit2.LBA48) && ataId.LBA48Sectors != 0) - { - report.ATA.LBA48Sectors = ataId.LBA48Sectors; - report.ATA.LBA48SectorsSpecified = true; + if(removable) + { + DicConsole.WriteLine("Please remove any media from the device and press any key when it is out."); + System.Console.ReadKey(true); + DicConsole.WriteLine("Querying ATA IDENTIFY..."); + dev.AtaIdentify(out buffer, out errorRegs, timeout, out duration); + ataId = Decoders.ATA.Identify.Decode(buffer).Value; } + report.ATA = new ataType(); + if (!string.IsNullOrWhiteSpace(ataId.AdditionalPID)) { report.ATA.AdditionalPID = ataId.AdditionalPID; @@ -332,11 +322,6 @@ namespace DiscImageChef.Commands report.ATA.DMATransferTimingMode = ataId.DMATransferTimingMode; report.ATA.DMATransferTimingModeSpecified = true; } - if (ataId.EccBytes != 0) - { - report.ATA.EccBytes = ataId.EccBytes; - report.ATA.EccBytesSpecified = true; - } if (ataId.EnhancedSecurityEraseTime != 0) { report.ATA.EnhancedSecurityEraseTime = ataId.EnhancedSecurityEraseTime; @@ -397,16 +382,6 @@ namespace DiscImageChef.Commands report.ATA.InterseekDelay = ataId.InterseekDelay; report.ATA.InterseekDelaySpecified = true; } - if (ataId.LogicalAlignment != 0) - { - report.ATA.LogicalAlignment = ataId.LogicalAlignment; - report.ATA.LogicalAlignmentSpecified = true; - } - if (ataId.LogicalSectorWords != 0) - { - report.ATA.LogicalSectorWords = ataId.LogicalSectorWords; - report.ATA.LogicalSectorWordsSpecified = true; - } if (ataId.MajorVersion != 0) { report.ATA.MajorVersion = ataId.MajorVersion; @@ -497,21 +472,11 @@ namespace DiscImageChef.Commands report.ATA.NVEstimatedSpinUp = ataId.NVEstimatedSpinUp; report.ATA.NVEstimatedSpinUpSpecified = true; } - if (ataId.NominalRotationRate != 0) - { - report.ATA.NominalRotationRate = ataId.NominalRotationRate; - report.ATA.NominalRotationRateSpecified = true; - } if (ataId.PacketBusRelease != 0) { report.ATA.PacketBusRelease = ataId.PacketBusRelease; report.ATA.PacketBusReleaseSpecified = true; } - if (ataId.PhysLogSectorSize != 0) - { - report.ATA.PhysLogSectorSize = ataId.PhysLogSectorSize; - report.ATA.PhysLogSectorSizeSpecified = true; - } if (ataId.PIOTransferTimingMode != 0) { report.ATA.PIOTransferTimingMode = ataId.PIOTransferTimingMode; @@ -627,16 +592,6 @@ namespace DiscImageChef.Commands report.ATA.UDMASupported = ataId.UDMASupported; report.ATA.UDMASupportedSpecified = true; } - if (ataId.UnformattedBPT != 0) - { - report.ATA.UnformattedBPT = ataId.UnformattedBPT; - report.ATA.UnformattedBPTSpecified = true; - } - if (ataId.UnformattedBPS != 0) - { - report.ATA.UnformattedBPS = ataId.UnformattedBPS; - report.ATA.UnformattedBPSSpecified = true; - } if (ataId.WRVMode != 0) { report.ATA.WRVMode = ataId.WRVMode; @@ -652,6 +607,464 @@ namespace DiscImageChef.Commands report.ATA.WRVSectorCountMode2 = ataId.WRVSectorCountMode2; report.ATA.WRVSectorCountMode2Specified = true; } + + if (removable) + { + List mediaTests = new List(); + + pressedKey = new ConsoleKeyInfo(); + while (pressedKey.Key != ConsoleKey.N) + { + pressedKey = new ConsoleKeyInfo(); + while (pressedKey.Key != ConsoleKey.Y && pressedKey.Key != ConsoleKey.N) + { + DicConsole.Write("Do you have media that you can insert in the drive? (Y/N): "); + pressedKey = System.Console.ReadKey(); + DicConsole.WriteLine(); + } + + if (pressedKey.Key == ConsoleKey.Y) + { + DicConsole.WriteLine("Please insert it in the drive and press any key when it is ready."); + System.Console.ReadKey(true); + + testedMediaType mediaTest = new testedMediaType(); + DicConsole.Write("Please write a description of the media type and press enter: "); + mediaTest.MediumTypeName = System.Console.ReadLine(); + DicConsole.Write("Please write the media model and press enter: "); + mediaTest.Model = System.Console.ReadLine(); + + mediaTest.ManufacturerSpecified = true; + mediaTest.ModelSpecified = true; + mediaTest.MediaIsRecognized = true; + + DicConsole.WriteLine("Querying ATA IDENTIFY..."); + dev.AtaIdentify(out buffer, out errorRegs, timeout, out duration); + + if (Decoders.ATA.Identify.Decode(buffer).HasValue) + { + ataId = Decoders.ATA.Identify.Decode(buffer).Value; + + if (ataId.UnformattedBPT != 0) + { + mediaTest.UnformattedBPT = ataId.UnformattedBPT; + mediaTest.UnformattedBPTSpecified = true; + } + if (ataId.UnformattedBPS != 0) + { + mediaTest.UnformattedBPS = ataId.UnformattedBPS; + mediaTest.UnformattedBPSSpecified = true; + } + + if (ataId.Cylinders > 0 && ataId.Heads > 0 && ataId.SectorsPerTrack > 0) + { + mediaTest.CHS = new chsType(); + mediaTest.CHS.Cylinders = ataId.Cylinders; + mediaTest.CHS.Heads = ataId.Heads; + mediaTest.CHS.Sectors = ataId.SectorsPerTrack; + mediaTest.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack); + mediaTest.BlocksSpecified = true; + } + + if (ataId.CurrentCylinders > 0 && ataId.CurrentHeads > 0 && ataId.CurrentSectorsPerTrack > 0) + { + mediaTest.CurrentCHS = new chsType(); + mediaTest.CurrentCHS.Cylinders = ataId.CurrentCylinders; + mediaTest.CurrentCHS.Heads = ataId.CurrentHeads; + mediaTest.CurrentCHS.Sectors = ataId.CurrentSectorsPerTrack; + mediaTest.Blocks = (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack); + mediaTest.BlocksSpecified = true; + } + + if (ataId.Capabilities.HasFlag(Decoders.ATA.Identify.CapabilitiesBit.LBASupport)) + { + mediaTest.LBASectors = ataId.LBASectors; + mediaTest.LBASectorsSpecified = true; + mediaTest.Blocks = ataId.LBASectors; + mediaTest.BlocksSpecified = true; + } + + if (ataId.CommandSet2.HasFlag(Decoders.ATA.Identify.CommandSetBit2.LBA48)) + { + mediaTest.LBA48Sectors = ataId.LBA48Sectors; + mediaTest.LBA48SectorsSpecified = true; + mediaTest.Blocks = ataId.LBA48Sectors; + mediaTest.BlocksSpecified = true; + } + + if (ataId.NominalRotationRate != 0x0000 && + ataId.NominalRotationRate != 0xFFFF) + { + if (ataId.NominalRotationRate == 0x0001) + { + mediaTest.SolidStateDevice = true; + mediaTest.SolidStateDeviceSpecified = true; + } + else + { + mediaTest.SolidStateDevice = false; + mediaTest.SolidStateDeviceSpecified = true; + mediaTest.NominalRotationRate = ataId.NominalRotationRate; + mediaTest.NominalRotationRateSpecified = true; + } + } + + uint logicalsectorsize = 0; + uint physicalsectorsize; + if ((ataId.PhysLogSectorSize & 0x8000) == 0x0000 && + (ataId.PhysLogSectorSize & 0x4000) == 0x4000) + { + if ((ataId.PhysLogSectorSize & 0x1000) == 0x1000) + { + if (ataId.LogicalSectorWords <= 255 || ataId.LogicalAlignment == 0xFFFF) + logicalsectorsize = 512; + else + logicalsectorsize = ataId.LogicalSectorWords * 2; + } + else + logicalsectorsize = 512; + + if ((ataId.PhysLogSectorSize & 0x2000) == 0x2000) + { + physicalsectorsize = logicalsectorsize * (uint)Math.Pow(2, (double)(ataId.PhysLogSectorSize & 0xF)); + } + else + physicalsectorsize = logicalsectorsize; + } + else + { + logicalsectorsize = 512; + physicalsectorsize = 512; + } + + mediaTest.BlockSize = logicalsectorsize; + mediaTest.BlockSizeSpecified = true; + if (physicalsectorsize != logicalsectorsize) + { + mediaTest.PhysicalBlockSize = physicalsectorsize; + mediaTest.PhysicalBlockSizeSpecified = true; + + if ((ataId.LogicalAlignment & 0x8000) == 0x0000 && + (ataId.LogicalAlignment & 0x4000) == 0x4000) + { + mediaTest.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF); + mediaTest.LogicalAlignmentSpecified = true; + } + } + + if (ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) + { + mediaTest.LongBlockSize = logicalsectorsize + ataId.EccBytes; + mediaTest.LongBlockSizeSpecified = true; + } + + if (ataId.CommandSet3.HasFlag(Decoders.ATA.Identify.CommandSetBit3.MustBeSet) && + !ataId.CommandSet3.HasFlag(Decoders.ATA.Identify.CommandSetBit3.MustBeClear) && + ataId.EnabledCommandSet3.HasFlag(Decoders.ATA.Identify.CommandSetBit3.MediaSerial)) + { + mediaTest.CanReadMediaSerial = true; + mediaTest.CanReadMediaSerialSpecified = true; + if (!string.IsNullOrWhiteSpace(ataId.MediaManufacturer)) + { + mediaTest.Manufacturer = ataId.MediaManufacturer; + mediaTest.ManufacturerSpecified = true; + } + } + + mediaTest.SupportsReadLbaSpecified = true; + mediaTest.SupportsReadRetryLbaSpecified = true; + mediaTest.SupportsReadDmaLbaSpecified = true; + mediaTest.SupportsReadDmaRetryLbaSpecified = true; + mediaTest.SupportsReadLongLbaSpecified = true; + mediaTest.SupportsReadLongRetryLbaSpecified = true; + mediaTest.SupportsSeekLbaSpecified = true; + + mediaTest.SupportsReadLba48Specified = true; + mediaTest.SupportsReadDmaLba48Specified = true; + + mediaTest.SupportsReadSpecified = true; + mediaTest.SupportsReadRetrySpecified = true; + mediaTest.SupportsReadDmaSpecified = true; + mediaTest.SupportsReadDmaRetrySpecified = true; + mediaTest.SupportsReadLongSpecified = true; + mediaTest.SupportsReadLongRetrySpecified = true; + mediaTest.SupportsSeekSpecified = true; + + Decoders.ATA.AtaErrorRegistersCHS errorChs; + Decoders.ATA.AtaErrorRegistersLBA28 errorLba; + Decoders.ATA.AtaErrorRegistersLBA48 errorLba48; + + byte[] readBuf; + ulong checkCorrectRead = BitConverter.ToUInt64(buffer, 0); + bool sense = true; + + DicConsole.WriteLine("Trying READ SECTOR(S) in CHS mode..."); + sense = dev.Read(out readBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + mediaTest.SupportsRead = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ SECTOR(S) RETRY in CHS mode..."); + sense = dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + mediaTest.SupportsReadRetry = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA in CHS mode..."); + sense = dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + mediaTest.SupportsReadDma = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA RETRY in CHS mode..."); + sense = dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + mediaTest.SupportsReadDmaRetry = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ LONG in CHS mode..."); + sense = dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, mediaTest.LongBlockSize, timeout, out duration); + mediaTest.SupportsReadLong = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying READ LONG RETRY in CHS mode..."); + sense = dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, mediaTest.LongBlockSize, timeout, out duration); + mediaTest.SupportsReadLongRetry = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying SEEK in CHS mode..."); + sense = dev.Seek(out errorChs, 0, 0, 1, timeout, out duration); + mediaTest.SupportsSeek = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0); + + DicConsole.WriteLine("Trying READ SECTOR(S) in LBA mode..."); + sense = dev.Read(out readBuf, out errorLba, false, 0, 1, timeout, out duration); + mediaTest.SupportsReadLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ SECTOR(S) RETRY in LBA mode..."); + sense = dev.Read(out readBuf, out errorLba, true, 0, 1, timeout, out duration); + mediaTest.SupportsReadRetryLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA in LBA mode..."); + sense = dev.ReadDma(out readBuf, out errorLba, false, 0, 1, timeout, out duration); + mediaTest.SupportsReadDmaLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA RETRY in LBA mode..."); + sense = dev.ReadDma(out readBuf, out errorLba, true, 0, 1, timeout, out duration); + mediaTest.SupportsReadDmaRetryLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ LONG in LBA mode..."); + sense = dev.ReadLong(out readBuf, out errorLba, false, 0, mediaTest.LongBlockSize, timeout, out duration); + mediaTest.SupportsReadLongLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying READ LONG RETRY in LBA mode..."); + sense = dev.ReadLong(out readBuf, out errorLba, true, 0, mediaTest.LongBlockSize, timeout, out duration); + mediaTest.SupportsReadLongRetryLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying SEEK in LBA mode..."); + sense = dev.Seek(out errorLba, 0, timeout, out duration); + mediaTest.SupportsSeekLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0); + + DicConsole.WriteLine("Trying READ SECTOR(S) in LBA48 mode..."); + sense = dev.Read(out readBuf, out errorLba48, 0, 1, timeout, out duration); + mediaTest.SupportsReadLba48 = (!sense && (errorLba48.status & 0x01) != 0x01 && errorLba48.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA in LBA48 mode..."); + sense = dev.ReadDma(out readBuf, out errorLba48, 0, 1, timeout, out duration); + mediaTest.SupportsReadDmaLba48 = (!sense && (errorLba48.status & 0x01) != 0x01 && errorLba48.error == 0 && readBuf.Length > 0); + } + else + mediaTest.MediaIsRecognized = false; + + mediaTests.Add(mediaTest); + } + } + report.ATA.RemovableMedias = mediaTests.ToArray(); + } + else + { + report.ATA.ReadCapabilities = new testedMediaType(); + + if (ataId.UnformattedBPT != 0) + { + report.ATA.ReadCapabilities.UnformattedBPT = ataId.UnformattedBPT; + report.ATA.ReadCapabilities.UnformattedBPTSpecified = true; + } + if (ataId.UnformattedBPS != 0) + { + report.ATA.ReadCapabilities.UnformattedBPS = ataId.UnformattedBPS; + report.ATA.ReadCapabilities.UnformattedBPSSpecified = true; + } + + if (ataId.Cylinders > 0 && ataId.Heads > 0 && ataId.SectorsPerTrack > 0) + { + report.ATA.ReadCapabilities.CHS = new chsType(); + report.ATA.ReadCapabilities.CHS.Cylinders = ataId.Cylinders; + report.ATA.ReadCapabilities.CHS.Heads = ataId.Heads; + report.ATA.ReadCapabilities.CHS.Sectors = ataId.SectorsPerTrack; + report.ATA.ReadCapabilities.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack); + report.ATA.ReadCapabilities.BlocksSpecified = true; + } + + if (ataId.CurrentCylinders > 0 && ataId.CurrentHeads > 0 && ataId.CurrentSectorsPerTrack > 0) + { + report.ATA.ReadCapabilities.CurrentCHS = new chsType(); + report.ATA.ReadCapabilities.CurrentCHS.Cylinders = ataId.CurrentCylinders; + report.ATA.ReadCapabilities.CurrentCHS.Heads = ataId.CurrentHeads; + report.ATA.ReadCapabilities.CurrentCHS.Sectors = ataId.CurrentSectorsPerTrack; + report.ATA.ReadCapabilities.Blocks = (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack); + report.ATA.ReadCapabilities.BlocksSpecified = true; + } + + if (ataId.Capabilities.HasFlag(Decoders.ATA.Identify.CapabilitiesBit.LBASupport)) + { + report.ATA.ReadCapabilities.LBASectors = ataId.LBASectors; + report.ATA.ReadCapabilities.LBASectorsSpecified = true; + report.ATA.ReadCapabilities.Blocks = ataId.LBASectors; + report.ATA.ReadCapabilities.BlocksSpecified = true; + } + + if (ataId.CommandSet2.HasFlag(Decoders.ATA.Identify.CommandSetBit2.LBA48)) + { + report.ATA.ReadCapabilities.LBA48Sectors = ataId.LBA48Sectors; + report.ATA.ReadCapabilities.LBA48SectorsSpecified = true; + report.ATA.ReadCapabilities.Blocks = ataId.LBA48Sectors; + report.ATA.ReadCapabilities.BlocksSpecified = true; + } + + if (ataId.NominalRotationRate != 0x0000 && + ataId.NominalRotationRate != 0xFFFF) + { + if (ataId.NominalRotationRate == 0x0001) + { + report.ATA.ReadCapabilities.SolidStateDevice = true; + report.ATA.ReadCapabilities.SolidStateDeviceSpecified = true; + } + else + { + report.ATA.ReadCapabilities.SolidStateDevice = false; + report.ATA.ReadCapabilities.SolidStateDeviceSpecified = true; + report.ATA.ReadCapabilities.NominalRotationRate = ataId.NominalRotationRate; + report.ATA.ReadCapabilities.NominalRotationRateSpecified = true; + } + } + + uint logicalsectorsize = 0; + uint physicalsectorsize; + if ((ataId.PhysLogSectorSize & 0x8000) == 0x0000 && + (ataId.PhysLogSectorSize & 0x4000) == 0x4000) + { + if ((ataId.PhysLogSectorSize & 0x1000) == 0x1000) + { + if (ataId.LogicalSectorWords <= 255 || ataId.LogicalAlignment == 0xFFFF) + logicalsectorsize = 512; + else + logicalsectorsize = ataId.LogicalSectorWords * 2; + } + else + logicalsectorsize = 512; + + if ((ataId.PhysLogSectorSize & 0x2000) == 0x2000) + { + physicalsectorsize = logicalsectorsize * (uint)Math.Pow(2, (double)(ataId.PhysLogSectorSize & 0xF)); + } + else + physicalsectorsize = logicalsectorsize; + } + else + { + logicalsectorsize = 512; + physicalsectorsize = 512; + } + + report.ATA.ReadCapabilities.BlockSize = logicalsectorsize; + report.ATA.ReadCapabilities.BlockSizeSpecified = true; + if (physicalsectorsize != logicalsectorsize) + { + report.ATA.ReadCapabilities.PhysicalBlockSize = physicalsectorsize; + report.ATA.ReadCapabilities.PhysicalBlockSizeSpecified = true; + + if ((ataId.LogicalAlignment & 0x8000) == 0x0000 && + (ataId.LogicalAlignment & 0x4000) == 0x4000) + { + report.ATA.ReadCapabilities.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF); + report.ATA.ReadCapabilities.LogicalAlignmentSpecified = true; + } + } + + if (ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) + { + report.ATA.ReadCapabilities.LongBlockSize = logicalsectorsize + ataId.EccBytes; + report.ATA.ReadCapabilities.LongBlockSizeSpecified = true; + } + + if (ataId.CommandSet3.HasFlag(Decoders.ATA.Identify.CommandSetBit3.MustBeSet) && + !ataId.CommandSet3.HasFlag(Decoders.ATA.Identify.CommandSetBit3.MustBeClear) && + ataId.EnabledCommandSet3.HasFlag(Decoders.ATA.Identify.CommandSetBit3.MediaSerial)) + { + report.ATA.ReadCapabilities.CanReadMediaSerial = true; + report.ATA.ReadCapabilities.CanReadMediaSerialSpecified = true; + if (!string.IsNullOrWhiteSpace(ataId.MediaManufacturer)) + { + report.ATA.ReadCapabilities.Manufacturer = ataId.MediaManufacturer; + report.ATA.ReadCapabilities.ManufacturerSpecified = true; + } + } + + report.ATA.ReadCapabilities.SupportsReadLbaSpecified = true; + report.ATA.ReadCapabilities.SupportsReadRetryLbaSpecified = true; + report.ATA.ReadCapabilities.SupportsReadDmaLbaSpecified = true; + report.ATA.ReadCapabilities.SupportsReadDmaRetryLbaSpecified = true; + report.ATA.ReadCapabilities.SupportsReadLongLbaSpecified = true; + report.ATA.ReadCapabilities.SupportsReadLongRetryLbaSpecified = true; + report.ATA.ReadCapabilities.SupportsSeekLbaSpecified = true; + + report.ATA.ReadCapabilities.SupportsReadLba48Specified = true; + report.ATA.ReadCapabilities.SupportsReadDmaLba48Specified = true; + + report.ATA.ReadCapabilities.SupportsReadSpecified = true; + report.ATA.ReadCapabilities.SupportsReadRetrySpecified = true; + report.ATA.ReadCapabilities.SupportsReadDmaSpecified = true; + report.ATA.ReadCapabilities.SupportsReadDmaRetrySpecified = true; + report.ATA.ReadCapabilities.SupportsReadLongSpecified = true; + report.ATA.ReadCapabilities.SupportsReadLongRetrySpecified = true; + report.ATA.ReadCapabilities.SupportsSeekSpecified = true; + + Decoders.ATA.AtaErrorRegistersCHS errorChs; + Decoders.ATA.AtaErrorRegistersLBA28 errorLba; + Decoders.ATA.AtaErrorRegistersLBA48 errorLba48; + + byte[] readBuf; + ulong checkCorrectRead = BitConverter.ToUInt64(buffer, 0); + bool sense = true; + + DicConsole.WriteLine("Trying READ SECTOR(S) in CHS mode..."); + sense = dev.Read(out readBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsRead = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ SECTOR(S) RETRY in CHS mode..."); + sense = dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadRetry = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA in CHS mode..."); + sense = dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadDma = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA RETRY in CHS mode..."); + sense = dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadDmaRetry = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ LONG in CHS mode..."); + sense = dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, report.ATA.ReadCapabilities.LongBlockSize, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadLong = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying READ LONG RETRY in CHS mode..."); + sense = dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, report.ATA.ReadCapabilities.LongBlockSize, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadLongRetry = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying SEEK in CHS mode..."); + sense = dev.Seek(out errorChs, 0, 0, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsSeek = (!sense && (errorChs.status & 0x01) != 0x01 && errorChs.error == 0); + + DicConsole.WriteLine("Trying READ SECTOR(S) in LBA mode..."); + sense = dev.Read(out readBuf, out errorLba, false, 0, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ SECTOR(S) RETRY in LBA mode..."); + sense = dev.Read(out readBuf, out errorLba, true, 0, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadRetryLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA in LBA mode..."); + sense = dev.ReadDma(out readBuf, out errorLba, false, 0, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadDmaLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA RETRY in LBA mode..."); + sense = dev.ReadDma(out readBuf, out errorLba, true, 0, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadDmaRetryLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ LONG in LBA mode..."); + sense = dev.ReadLong(out readBuf, out errorLba, false, 0, report.ATA.ReadCapabilities.LongBlockSize, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadLongLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying READ LONG RETRY in LBA mode..."); + sense = dev.ReadLong(out readBuf, out errorLba, true, 0, report.ATA.ReadCapabilities.LongBlockSize, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadLongRetryLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0 && readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead); + DicConsole.WriteLine("Trying SEEK in LBA mode..."); + sense = dev.Seek(out errorLba, 0, timeout, out duration); + report.ATA.ReadCapabilities.SupportsSeekLba = (!sense && (errorLba.status & 0x01) != 0x01 && errorLba.error == 0); + + DicConsole.WriteLine("Trying READ SECTOR(S) in LBA48 mode..."); + sense = dev.Read(out readBuf, out errorLba48, 0, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadLba48 = (!sense && (errorLba48.status & 0x01) != 0x01 && errorLba48.error == 0 && readBuf.Length > 0); + DicConsole.WriteLine("Trying READ DMA in LBA48 mode..."); + sense = dev.ReadDma(out readBuf, out errorLba48, 0, 1, timeout, out duration); + report.ATA.ReadCapabilities.SupportsReadDmaLba48 = (!sense && (errorLba48.status & 0x01) != 0x01 && errorLba48.error == 0 && readBuf.Length > 0); + } } FileStream xmlFs = new FileStream(xmlFile, FileMode.Create); @@ -779,47 +1192,6 @@ namespace DiscImageChef.Commands report.ATAPI = new ataType(); - if (atapiId.CurrentCylinders != 0 || - atapiId.CurrentHeads != 0 || - atapiId.CurrentSectorsPerTrack != 0) - { - report.ATAPI.CHS = new chsType(); - report.ATAPI.CHS.Cylinders = atapiId.Cylinders; - report.ATAPI.CHS.Heads = atapiId.Heads; - report.ATAPI.CHS.Sectors = atapiId.SectorsPerTrack; - } - - if ((atapiId.Cylinders != atapiId.CurrentCylinders || - atapiId.Heads != atapiId.CurrentHeads || - atapiId.SectorsPerTrack != atapiId.CurrentSectorsPerTrack) && - (atapiId.CurrentCylinders != 0 || - atapiId.CurrentHeads != 0 || - atapiId.CurrentSectorsPerTrack != 0)) - { - report.ATAPI.CurrentCHS = new chsType(); - report.ATAPI.CurrentCHS.Cylinders = atapiId.CurrentCylinders; - report.ATAPI.CurrentCHS.Heads = atapiId.CurrentHeads; - report.ATAPI.CurrentCHS.Sectors = atapiId.CurrentSectorsPerTrack; - } - - if (atapiId.Capabilities.HasFlag(Decoders.ATA.Identify.CapabilitiesBit.LBASupport) && atapiId.LBASectors != 0) - { - report.ATAPI.LBASectors = atapiId.LBASectors; - report.ATAPI.LBASectorsSpecified = true; - - if (atapiId.LBASectors != 0 && atapiId.LBASectors != atapiId.CurrentSectors) - { - report.ATAPI.LBASectorsCurrent = atapiId.LBASectors; - report.ATAPI.LBASectorsCurrentSpecified = true; - } - } - - if (atapiId.CommandSet2.HasFlag(Decoders.ATA.Identify.CommandSetBit2.LBA48) && atapiId.LBA48Sectors != 0) - { - report.ATAPI.LBA48Sectors = atapiId.LBA48Sectors; - report.ATAPI.LBA48SectorsSpecified = true; - } - if (!string.IsNullOrWhiteSpace(atapiId.AdditionalPID)) { report.ATAPI.AdditionalPID = atapiId.AdditionalPID; @@ -930,11 +1302,6 @@ namespace DiscImageChef.Commands report.ATAPI.DMATransferTimingMode = atapiId.DMATransferTimingMode; report.ATAPI.DMATransferTimingModeSpecified = true; } - if (atapiId.EccBytes != 0) - { - report.ATAPI.EccBytes = atapiId.EccBytes; - report.ATAPI.EccBytesSpecified = true; - } if (atapiId.EnhancedSecurityEraseTime != 0) { report.ATAPI.EnhancedSecurityEraseTime = atapiId.EnhancedSecurityEraseTime; @@ -995,16 +1362,6 @@ namespace DiscImageChef.Commands report.ATAPI.InterseekDelay = atapiId.InterseekDelay; report.ATAPI.InterseekDelaySpecified = true; } - if (atapiId.LogicalAlignment != 0) - { - report.ATAPI.LogicalAlignment = atapiId.LogicalAlignment; - report.ATAPI.LogicalAlignmentSpecified = true; - } - if (atapiId.LogicalSectorWords != 0) - { - report.ATAPI.LogicalSectorWords = atapiId.LogicalSectorWords; - report.ATAPI.LogicalSectorWordsSpecified = true; - } if (atapiId.MajorVersion != 0) { report.ATAPI.MajorVersion = atapiId.MajorVersion; @@ -1095,21 +1452,11 @@ namespace DiscImageChef.Commands report.ATAPI.NVEstimatedSpinUp = atapiId.NVEstimatedSpinUp; report.ATAPI.NVEstimatedSpinUpSpecified = true; } - if (atapiId.NominalRotationRate != 0) - { - report.ATAPI.NominalRotationRate = atapiId.NominalRotationRate; - report.ATAPI.NominalRotationRateSpecified = true; - } if (atapiId.PacketBusRelease != 0) { report.ATAPI.PacketBusRelease = atapiId.PacketBusRelease; report.ATAPI.PacketBusReleaseSpecified = true; } - if (atapiId.PhysLogSectorSize != 0) - { - report.ATAPI.PhysLogSectorSize = atapiId.PhysLogSectorSize; - report.ATAPI.PhysLogSectorSizeSpecified = true; - } if (atapiId.PIOTransferTimingMode != 0) { report.ATAPI.PIOTransferTimingMode = atapiId.PIOTransferTimingMode; @@ -1225,16 +1572,6 @@ namespace DiscImageChef.Commands report.ATAPI.UDMASupported = atapiId.UDMASupported; report.ATAPI.UDMASupportedSpecified = true; } - if (atapiId.UnformattedBPT != 0) - { - report.ATAPI.UnformattedBPT = atapiId.UnformattedBPT; - report.ATAPI.UnformattedBPTSpecified = true; - } - if (atapiId.UnformattedBPS != 0) - { - report.ATAPI.UnformattedBPS = atapiId.UnformattedBPS; - report.ATAPI.UnformattedBPSSpecified = true; - } if (atapiId.WRVMode != 0) { report.ATAPI.WRVMode = atapiId.WRVMode; diff --git a/DiscImageChef/Commands/DumpMedia.cs b/DiscImageChef/Commands/DumpMedia.cs index 231555b0f..e3620b597 100644 --- a/DiscImageChef/Commands/DumpMedia.cs +++ b/DiscImageChef/Commands/DumpMedia.cs @@ -118,7 +118,785 @@ namespace DiscImageChef.Commands static void doATAMediaScan(DumpMediaSubOptions options, Device dev) { - throw new NotImplementedException("ATA devices not yet supported."); + if(options.Raw) + { + DicConsole.ErrorWriteLine("Raw dumping not yet supported in ATA devices."); + + if (options.Force) + DicConsole.ErrorWriteLine("Continuing..."); + else + { + DicConsole.ErrorWriteLine("Aborting..."); + return; + } + } + + byte[] cmdBuf; + bool sense; + ulong blocks = 0; + uint blockSize = 512; + ushort currentProfile = 0x0001; + Decoders.ATA.AtaErrorRegistersCHS errorChs; + Decoders.ATA.AtaErrorRegistersLBA28 errorLba; + Decoders.ATA.AtaErrorRegistersLBA48 errorLba48; + bool lbaMode = false; + byte heads = 0, sectors = 0; + ushort cylinders = 0; + uint timeout = 5; + double duration; + + sense = dev.AtaIdentify(out cmdBuf, out errorChs); + if (!sense && Decoders.ATA.Identify.Decode(cmdBuf).HasValue) + { + Decoders.ATA.Identify.IdentifyDevice ataId = Decoders.ATA.Identify.Decode(cmdBuf).Value; + + CICMMetadataType sidecar = new CICMMetadataType(); + sidecar.BlockMedia = new BlockMediaType[1]; + sidecar.BlockMedia[0] = new BlockMediaType(); + + if (dev.IsUSB) + { + sidecar.BlockMedia[0].USB = new USBType(); + sidecar.BlockMedia[0].USB.ProductID = dev.USBProductID; + sidecar.BlockMedia[0].USB.VendorID = dev.USBVendorID; + sidecar.BlockMedia[0].USB.Descriptors = new DumpType(); + sidecar.BlockMedia[0].USB.Descriptors.Image = options.OutputPrefix + ".usbdescriptors.bin"; + sidecar.BlockMedia[0].USB.Descriptors.Size = dev.USBDescriptors.Length; + sidecar.BlockMedia[0].USB.Descriptors.Checksums = Core.Checksum.GetChecksums(dev.USBDescriptors).ToArray(); + writeToFile(sidecar.BlockMedia[0].USB.Descriptors.Image, dev.USBDescriptors); + } + + sidecar.BlockMedia[0].ATA = new ATAType(); + sidecar.BlockMedia[0].ATA.Identify = new DumpType(); + sidecar.BlockMedia[0].ATA.Identify.Image = options.OutputPrefix + ".identify.bin"; + sidecar.BlockMedia[0].ATA.Identify.Size = cmdBuf.Length; + sidecar.BlockMedia[0].ATA.Identify.Checksums = Core.Checksum.GetChecksums(cmdBuf).ToArray(); + writeToFile(sidecar.BlockMedia[0].ATA.Identify.Image, cmdBuf); + + if (ataId.CurrentCylinders > 0 && ataId.CurrentHeads > 0 && ataId.CurrentSectorsPerTrack > 0) + { + cylinders = ataId.CurrentCylinders; + heads = (byte)ataId.CurrentHeads; + sectors = (byte)ataId.CurrentSectorsPerTrack; + blocks = (ulong)(cylinders * heads * sectors); + } + + if ((ataId.CurrentCylinders == 0 || ataId.CurrentHeads == 0 || ataId.CurrentSectorsPerTrack == 0) && + (ataId.Cylinders > 0 && ataId.Heads > 0 && ataId.SectorsPerTrack > 0)) + { + cylinders = ataId.Cylinders; + heads = (byte)ataId.Heads; + sectors = (byte)ataId.SectorsPerTrack; + blocks = (ulong)(cylinders * heads * sectors); + } + + if (ataId.Capabilities.HasFlag(Decoders.ATA.Identify.CapabilitiesBit.LBASupport)) + { + blocks = ataId.LBASectors; + lbaMode = true; + } + + if (ataId.CommandSet2.HasFlag(Decoders.ATA.Identify.CommandSetBit2.LBA48)) + { + blocks = ataId.LBA48Sectors; + lbaMode = true; + } + + uint physicalsectorsize = blockSize; + if ((ataId.PhysLogSectorSize & 0x8000) == 0x0000 && + (ataId.PhysLogSectorSize & 0x4000) == 0x4000) + { + if ((ataId.PhysLogSectorSize & 0x1000) == 0x1000) + { + if (ataId.LogicalSectorWords <= 255 || ataId.LogicalAlignment == 0xFFFF) + blockSize = 512; + else + blockSize = ataId.LogicalSectorWords * 2; + } + else + blockSize = 512; + + if ((ataId.PhysLogSectorSize & 0x2000) == 0x2000) + { + physicalsectorsize = blockSize * (uint)Math.Pow(2, (double)(ataId.PhysLogSectorSize & 0xF)); + } + else + physicalsectorsize = blockSize; + } + else + { + blockSize = 512; + physicalsectorsize = 512; + } + + bool ReadLba = false; + bool ReadRetryLba = false; + bool ReadDmaLba = false; + bool ReadDmaRetryLba = false; + + bool ReadLba48 = false; + bool ReadDmaLba48 = false; + + bool Read = false; + bool ReadRetry = false; + bool ReadDma = false; + bool ReadDmaRetry = false; + + sense = dev.Read(out cmdBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + Read = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + sense = dev.Read(out cmdBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + ReadRetry = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + ReadDma = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + ReadDmaRetry = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + + sense = dev.Read(out cmdBuf, out errorLba, false, 0, 1, timeout, out duration); + ReadLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + sense = dev.Read(out cmdBuf, out errorLba, true, 0, 1, timeout, out duration); + ReadRetryLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorLba, false, 0, 1, timeout, out duration); + ReadDmaLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorLba, true, 0, 1, timeout, out duration); + ReadDmaRetryLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + + sense = dev.Read(out cmdBuf, out errorLba48, 0, 1, timeout, out duration); + ReadLba48 = (!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorLba48, 0, 1, timeout, out duration); + ReadDmaLba48 = (!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + + if (!lbaMode) + { + if (blocks > 0xFFFFFFF && !ReadLba48 && !ReadDmaLba48) + { + DicConsole.ErrorWriteLine("Device needs 48-bit LBA commands but I can't issue them... Aborting."); + return; + } + + if (!ReadLba && !ReadRetryLba && !ReadDmaLba && !ReadDmaRetryLba) + { + DicConsole.ErrorWriteLine("Device needs 28-bit LBA commands but I can't issue them... Aborting."); + return; + } + } + else + { + if (!Read && !ReadRetry && !ReadDma && !ReadDmaRetry) + { + DicConsole.ErrorWriteLine("Device needs CHS commands but I can't issue them... Aborting."); + return; + } + } + + if (ReadDmaLba48) + DicConsole.WriteLine("Using ATA READ DMA EXT command."); + else if (ReadLba48) + DicConsole.WriteLine("Using ATA READ EXT command."); + else if (ReadDmaRetryLba) + DicConsole.WriteLine("Using ATA READ DMA command with retries (LBA)."); + else if (ReadDmaLba) + DicConsole.WriteLine("Using ATA READ DMA command (LBA)."); + else if (ReadRetryLba) + DicConsole.WriteLine("Using ATA READ command with retries (LBA)."); + else if (ReadLba) + DicConsole.WriteLine("Using ATA READ command (LBA)."); + else if (ReadDmaRetry) + DicConsole.WriteLine("Using ATA READ DMA command with retries (CHS)."); + else if (ReadDma) + DicConsole.WriteLine("Using ATA READ DMA command (CHS)."); + else if (ReadRetry) + DicConsole.WriteLine("Using ATA READ command with retries (CHS)."); + else if (Read) + DicConsole.WriteLine("Using ATA READ command (CHS)."); + + uint blocksToRead = 64; + bool error = true; + while (lbaMode) + { + if (ReadDmaLba48) + { + sense = dev.ReadDma(out cmdBuf, out errorLba48, 0, (byte)blocksToRead, timeout, out duration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + } + else if (ReadLba48) + { + sense = dev.Read(out cmdBuf, out errorLba48, 0, (byte)blocksToRead, timeout, out duration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + } + else if (ReadDmaRetryLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, true, 0, (byte)blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadDmaLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, false, 0, (byte)blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadRetryLba) + { + sense = dev.Read(out cmdBuf, out errorLba, true, 0, (byte)blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadLba) + { + sense = dev.Read(out cmdBuf, out errorLba, false, 0, (byte)blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + + if (error) + blocksToRead /= 2; + + if (!error || blocksToRead == 1) + break; + } + + if (error && lbaMode) + { + DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); + return; + } + + ulong A = 0; // <3ms + ulong B = 0; // >=3ms, <10ms + ulong C = 0; // >=10ms, <50ms + ulong D = 0; // >=50ms, <150ms + ulong E = 0; // >=150ms, <500ms + ulong F = 0; // >=500ms + ulong errored = 0; + DateTime start; + DateTime end; + double totalDuration = 0; + double totalChkDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + List unreadableSectors = new List(); + Core.Checksum dataChk; + + aborted = false; + System.Console.CancelKeyPress += (sender, e) => + { + e.Cancel = aborted = true; + }; + + if (lbaMode) + { + DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); + + mhddLog = new Core.MHDDLog(options.OutputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); + ibgLog = new Core.IBGLog(options.OutputPrefix + ".ibg", currentProfile); + initDataFile(options.OutputPrefix + ".bin"); + + start = DateTime.UtcNow; + for (ulong i = 0; i < blocks; i += blocksToRead) + { + if (aborted) + break; + + double cmdDuration = 0; + + if ((blocks - i) < blocksToRead) + blocksToRead = (byte)(blocks - i); + + if (currentSpeed > maxSpeed && currentSpeed != 0) + maxSpeed = currentSpeed; + if (currentSpeed < minSpeed && currentSpeed != 0) + minSpeed = currentSpeed; + + DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed); + + error = true; + byte status = 0, errorByte = 0; + + if (ReadDmaLba48) + { + sense = dev.ReadDma(out cmdBuf, out errorLba48, i, (byte)blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + status = errorLba48.status; + errorByte = errorLba48.error; + } + else if (ReadLba48) + { + sense = dev.Read(out cmdBuf, out errorLba48, i, (byte)blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + status = errorLba48.status; + errorByte = errorLba48.error; + } + else if (ReadDmaRetryLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, true, (uint)i, (byte)blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + else if (ReadDmaLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, false, (uint)i, (byte)blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + else if (ReadRetryLba) + { + sense = dev.Read(out cmdBuf, out errorLba, true, (uint)i, (byte)blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + else if (ReadLba) + { + sense = dev.Read(out cmdBuf, out errorLba, false, (uint)i, (byte)blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + + if (!error) + { + if (cmdDuration >= 500) + { + F += blocksToRead; + } + else if (cmdDuration >= 150) + { + E += blocksToRead; + } + else if (cmdDuration >= 50) + { + D += blocksToRead; + } + else if (cmdDuration >= 10) + { + C += blocksToRead; + } + else if (cmdDuration >= 3) + { + B += blocksToRead; + } + else + { + A += blocksToRead; + } + + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + writeToDataFile(cmdBuf); + } + else + { + DicConsole.DebugWriteLine("Media-Scan", "ATA ERROR: {0} STATUS: {1}", errorByte, status); + errored += blocksToRead; + unreadableSectors.Add(i); + if (cmdDuration < 500) + mhddLog.Write(i, 65535); + else + mhddLog.Write(i, cmdDuration); + + ibgLog.Write(i, 0); + writeToDataFile(new byte[blockSize * blocksToRead]); + } + + currentSpeed = ((double)blockSize * blocksToRead / (double)1048576) / (cmdDuration / (double)1000); + GC.Collect(); + } + end = DateTime.Now; + DicConsole.WriteLine(); + mhddLog.Close(); + ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), options.DevicePath); + + #region Error handling + if (unreadableSectors.Count > 0 && !aborted) + { + List tmpList = new List(); + + foreach (ulong ur in unreadableSectors) + { + for (ulong i = ur; i < ur + blocksToRead; i++) + tmpList.Add(i); + } + + tmpList.Sort(); + + int pass = 0; + bool forward = true; + bool runningPersistent = false; + + unreadableSectors = tmpList; + + repeatRetryLba: + ulong[] tmpArray = unreadableSectors.ToArray(); + foreach (ulong badSector in tmpArray) + { + if (aborted) + break; + + double cmdDuration = 0; + + DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1, forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : ""); + + if (ReadDmaLba48) + { + sense = dev.ReadDma(out cmdBuf, out errorLba48, badSector, 1, timeout, out cmdDuration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + } + else if (ReadLba48) + { + sense = dev.Read(out cmdBuf, out errorLba48, badSector, 1, timeout, out cmdDuration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + } + else if (ReadDmaRetryLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, true, (uint)badSector, 1, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadDmaLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, false, (uint)badSector, 1, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadRetryLba) + { + sense = dev.Read(out cmdBuf, out errorLba, true, (uint)badSector, 1, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadLba) + { + sense = dev.Read(out cmdBuf, out errorLba, false, (uint)badSector, 1, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + + totalDuration += cmdDuration; + + if (!error) + { + unreadableSectors.Remove(badSector); + writeToDataFileAtPosition(cmdBuf, badSector, blockSize); + } + else if(runningPersistent) + writeToDataFileAtPosition(cmdBuf, badSector, blockSize); + } + + if (pass < options.RetryPasses && !aborted && unreadableSectors.Count > 0) + { + pass++; + forward = !forward; + unreadableSectors.Sort(); + unreadableSectors.Reverse(); + goto repeatRetryLba; + } + + DicConsole.WriteLine(); + } + #endregion Error handling LBA + } + else + { + mhddLog = new Core.MHDDLog(options.OutputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); + ibgLog = new Core.IBGLog(options.OutputPrefix + ".ibg", currentProfile); + initDataFile(options.OutputPrefix + ".bin"); + + ulong currentBlock = 0; + blocks = (ulong)(cylinders * heads * sectors); + start = DateTime.UtcNow; + for (ushort Cy = 0; Cy < cylinders; Cy++) + { + for (byte Hd = 0; Hd < heads; Hd++) + { + for (byte Sc = 1; Sc < sectors; Sc++) + { + if (aborted) + break; + + double cmdDuration = 0; + + if (currentSpeed > maxSpeed && currentSpeed != 0) + maxSpeed = currentSpeed; + if (currentSpeed < minSpeed && currentSpeed != 0) + minSpeed = currentSpeed; + + DicConsole.Write("\rReading cylinder {0} head {1} sector {2} ({3:F3} MiB/sec.)", Cy, Hd, Sc, currentSpeed); + + error = true; + byte status = 0, errorByte = 0; + + if (ReadDmaRetry) + { + sense = dev.ReadDma(out cmdBuf, out errorChs, true, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + else if (ReadDma) + { + sense = dev.ReadDma(out cmdBuf, out errorChs, false, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + else if (ReadRetry) + { + sense = dev.Read(out cmdBuf, out errorChs, true, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + else if (Read) + { + sense = dev.Read(out cmdBuf, out errorChs, false, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + + totalDuration += cmdDuration; + + if (!error) + { + if (cmdDuration >= 500) + { + F += blocksToRead; + } + else if (cmdDuration >= 150) + { + E += blocksToRead; + } + else if (cmdDuration >= 50) + { + D += blocksToRead; + } + else if (cmdDuration >= 10) + { + C += blocksToRead; + } + else if (cmdDuration >= 3) + { + B += blocksToRead; + } + else + { + A += blocksToRead; + } + + mhddLog.Write(currentBlock, cmdDuration); + ibgLog.Write(currentBlock, currentSpeed * 1024); + writeToDataFile(cmdBuf); + } + else + { + DicConsole.DebugWriteLine("Media-Scan", "ATA ERROR: {0} STATUS: {1}", errorByte, status); + errored += blocksToRead; + unreadableSectors.Add(currentBlock); + if (cmdDuration < 500) + mhddLog.Write(currentBlock, 65535); + else + mhddLog.Write(currentBlock, cmdDuration); + + ibgLog.Write(currentBlock, 0); + writeToDataFile(new byte[blockSize]); + } + + currentSpeed = ((double)blockSize / (double)1048576) / (cmdDuration / (double)1000); + GC.Collect(); + + currentBlock++; + } + } + } + end = DateTime.Now; + DicConsole.WriteLine(); + mhddLog.Close(); + ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), options.DevicePath); + } + dataChk = new Core.Checksum(); + dataFs.Seek(0, SeekOrigin.Begin); + blocksToRead = 500; + + for (ulong i = 0; i < blocks; i += blocksToRead) + { + if (aborted) + break; + + if ((blocks - i) < blocksToRead) + blocksToRead = (byte)(blocks - i); + + DicConsole.Write("\rChecksumming sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed); + + DateTime chkStart = DateTime.UtcNow; + byte[] dataToCheck = new byte[blockSize * blocksToRead]; + dataFs.Read(dataToCheck, 0, (int)(blockSize * blocksToRead)); + dataChk.Update(dataToCheck); + DateTime chkEnd = DateTime.UtcNow; + + double chkDuration = (chkEnd - chkStart).TotalMilliseconds; + totalChkDuration += chkDuration; + + currentSpeed = ((double)blockSize * blocksToRead / (double)1048576) / (chkDuration / (double)1000); + } + DicConsole.WriteLine(); + closeDataFile(); + end = DateTime.UtcNow; + + PluginBase plugins = new PluginBase(); + plugins.RegisterAllPlugins(); + ImagePlugin _imageFormat; + _imageFormat = ImageFormat.Detect(options.OutputPrefix + ".bin"); + PartitionType[] xmlFileSysInfo = null; + + try + { + if (!_imageFormat.OpenImage(options.OutputPrefix + ".bin")) + _imageFormat = null; + } + catch + { + _imageFormat = null; + } + + if (_imageFormat != null) + { + List partitions = new List(); + + foreach (PartPlugin _partplugin in plugins.PartPluginsList.Values) + { + List _partitions; + + if (_partplugin.GetInformation(_imageFormat, out _partitions)) + { + partitions.AddRange(_partitions); + Core.Statistics.AddPartition(_partplugin.Name); + } + } + + if (partitions.Count > 0) + { + xmlFileSysInfo = new PartitionType[partitions.Count]; + for (int i = 0; i < partitions.Count; i++) + { + xmlFileSysInfo[i] = new PartitionType(); + xmlFileSysInfo[i].Description = partitions[i].PartitionDescription; + xmlFileSysInfo[i].EndSector = (int)(partitions[i].PartitionStartSector + partitions[i].PartitionSectors - 1); + xmlFileSysInfo[i].Name = partitions[i].PartitionName; + xmlFileSysInfo[i].Sequence = (int)partitions[i].PartitionSequence; + xmlFileSysInfo[i].StartSector = (int)partitions[i].PartitionStartSector; + xmlFileSysInfo[i].Type = partitions[i].PartitionType; + + List lstFs = new List(); + + foreach (Plugin _plugin in plugins.PluginsList.Values) + { + try + { + if (_plugin.Identify(_imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector + partitions[i].PartitionSectors - 1)) + { + string foo; + _plugin.GetInformation(_imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector + partitions[i].PartitionSectors - 1, out foo); + lstFs.Add(_plugin.XmlFSType); + Core.Statistics.AddFilesystem(_plugin.XmlFSType.Type); + } + } + catch + { + //DicConsole.DebugWriteLine("Dump-media command", "Plugin {0} crashed", _plugin.Name); + } + } + + if (lstFs.Count > 0) + xmlFileSysInfo[i].FileSystems = lstFs.ToArray(); + } + } + else + { + xmlFileSysInfo = new PartitionType[1]; + xmlFileSysInfo[0] = new PartitionType(); + xmlFileSysInfo[0].EndSector = (int)(blocks - 1); + xmlFileSysInfo[0].StartSector = 0; + + List lstFs = new List(); + + foreach (Plugin _plugin in plugins.PluginsList.Values) + { + try + { + if (_plugin.Identify(_imageFormat, (blocks - 1), 0)) + { + string foo; + _plugin.GetInformation(_imageFormat, (blocks - 1), 0, out foo); + lstFs.Add(_plugin.XmlFSType); + Core.Statistics.AddFilesystem(_plugin.XmlFSType.Type); + } + } + catch + { + //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); + } + } + + if (lstFs.Count > 0) + xmlFileSysInfo[0].FileSystems = lstFs.ToArray(); + } + } + + sidecar.BlockMedia[0].Checksums = dataChk.End().ToArray(); + string xmlDskTyp, xmlDskSubTyp; + Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD, out xmlDskTyp, out xmlDskSubTyp); + sidecar.BlockMedia[0].DiskType = xmlDskTyp; + sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; + // TODO: Implement device firmware revision + sidecar.BlockMedia[0].Image = new ImageType(); + sidecar.BlockMedia[0].Image.format = "Raw disk image (sector by sector copy)"; + sidecar.BlockMedia[0].Image.Value = options.OutputPrefix + ".bin"; + sidecar.BlockMedia[0].Interface = "ATA"; + sidecar.BlockMedia[0].LogicalBlocks = (long)blocks; + sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalsectorsize; + sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize; + sidecar.BlockMedia[0].Manufacturer = dev.Manufacturer; + sidecar.BlockMedia[0].Model = dev.Model; + sidecar.BlockMedia[0].Serial = dev.Serial; + sidecar.BlockMedia[0].Size = (long)(blocks * blockSize); + if (xmlFileSysInfo != null) + sidecar.BlockMedia[0].FileSystemInformation = xmlFileSysInfo; + if (cylinders > 0 && heads > 0 && sectors > 0) + { + sidecar.BlockMedia[0].Cylinders = cylinders; + sidecar.BlockMedia[0].CylindersSpecified = true; + sidecar.BlockMedia[0].Heads = heads; + sidecar.BlockMedia[0].HeadsSpecified = true; + sidecar.BlockMedia[0].SectorsPerTrack = sectors; + sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; + } + + DicConsole.WriteLine(); + + DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming).", (end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000); + DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.", (((double)blockSize * (double)(blocks + 1)) / 1048576) / (totalDuration / 1000)); + DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed); + DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed); + DicConsole.WriteLine("{0} sectors could not be read.", unreadableSectors.Count); + if (unreadableSectors.Count > 0) + { + unreadableSectors.Sort(); + foreach (ulong bad in unreadableSectors) + DicConsole.WriteLine("Sector {0} could not be read", bad); + } + DicConsole.WriteLine(); + + if (!aborted) + { + DicConsole.WriteLine("Writing metadata sidecar"); + + FileStream xmlFs = new FileStream(options.OutputPrefix + ".cicm.xml", + FileMode.Create); + + System.Xml.Serialization.XmlSerializer xmlSer = new System.Xml.Serialization.XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); + } + + Core.Statistics.AddMedia(MediaType.GENERIC_HDD, true); + } + else + DicConsole.ErrorWriteLine("Unable to communicate with ATA device."); } static void doNVMeMediaScan(DumpMediaSubOptions options, Device dev) diff --git a/DiscImageChef/Commands/MediaInfo.cs b/DiscImageChef/Commands/MediaInfo.cs index d883afd9e..375f92067 100644 --- a/DiscImageChef/Commands/MediaInfo.cs +++ b/DiscImageChef/Commands/MediaInfo.cs @@ -100,7 +100,7 @@ namespace DiscImageChef.Commands static void doATAMediaInfo(string outputPrefix, Device dev) { - throw new NotImplementedException("ATA devices not yet supported."); + DicConsole.ErrorWriteLine("Please use device-info command for ATA devices."); } static void doNVMeMediaInfo(string outputPrefix, Device dev) diff --git a/DiscImageChef/Commands/MediaScan.cs b/DiscImageChef/Commands/MediaScan.cs index e5746abbf..0989e800f 100644 --- a/DiscImageChef/Commands/MediaScan.cs +++ b/DiscImageChef/Commands/MediaScan.cs @@ -106,7 +106,553 @@ namespace DiscImageChef.Commands static void doATAMediaScan(string MHDDLogPath, string IBGLogPath, string devicePath, Device dev) { - throw new NotImplementedException("ATA devices not yet supported."); + byte[] cmdBuf; + bool sense; + ulong blocks = 0; + uint blockSize; + ushort currentProfile = 0x0001; + Decoders.ATA.AtaErrorRegistersCHS errorChs; + Decoders.ATA.AtaErrorRegistersLBA28 errorLba; + Decoders.ATA.AtaErrorRegistersLBA48 errorLba48; + bool lbaMode = false; + byte heads = 0, sectors = 0; + ushort cylinders = 0; + uint timeout = 5; + double duration; + + sense = dev.AtaIdentify(out cmdBuf, out errorChs); + if (!sense && Decoders.ATA.Identify.Decode(cmdBuf).HasValue) + { + Decoders.ATA.Identify.IdentifyDevice ataId = Decoders.ATA.Identify.Decode(cmdBuf).Value; + + if (ataId.CurrentCylinders > 0 && ataId.CurrentHeads > 0 && ataId.CurrentSectorsPerTrack > 0) + { + cylinders = ataId.CurrentCylinders; + heads = (byte)ataId.CurrentHeads; + sectors = (byte)ataId.CurrentSectorsPerTrack; + blocks = (ulong)(cylinders * heads * sectors); + } + + if ((ataId.CurrentCylinders == 0 || ataId.CurrentHeads == 0 || ataId.CurrentSectorsPerTrack == 0) && + (ataId.Cylinders > 0 && ataId.Heads > 0 && ataId.SectorsPerTrack > 0)) + { + cylinders = ataId.Cylinders; + heads = (byte)ataId.Heads; + sectors = (byte)ataId.SectorsPerTrack; + blocks = (ulong)(cylinders * heads * sectors); + } + + if (ataId.Capabilities.HasFlag(Decoders.ATA.Identify.CapabilitiesBit.LBASupport)) + { + blocks = ataId.LBASectors; + lbaMode = true; + } + + if (ataId.CommandSet2.HasFlag(Decoders.ATA.Identify.CommandSetBit2.LBA48)) + { + blocks = ataId.LBA48Sectors; + lbaMode = true; + } + + if ((ataId.PhysLogSectorSize & 0x8000) == 0x0000 && + (ataId.PhysLogSectorSize & 0x4000) == 0x4000) + { + if ((ataId.PhysLogSectorSize & 0x1000) == 0x1000) + { + if (ataId.LogicalSectorWords <= 255 || ataId.LogicalAlignment == 0xFFFF) + blockSize = 512; + else + blockSize = ataId.LogicalSectorWords * 2; + } + else + blockSize = 512; + } + else + blockSize = 512; + + bool ReadLba = false; + bool ReadRetryLba = false; + bool ReadDmaLba = false; + bool ReadDmaRetryLba = false; + bool SeekLba = false; + + bool ReadLba48 = false; + bool ReadDmaLba48 = false; + + bool Read = false; + bool ReadRetry = false; + bool ReadDma = false; + bool ReadDmaRetry = false; + bool Seek = false; + + sense = dev.Read(out cmdBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + Read = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + sense = dev.Read(out cmdBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + ReadRetry = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorChs, false, 0, 0, 1, 1, timeout, out duration); + ReadDma = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorChs, true, 0, 0, 1, 1, timeout, out duration); + ReadDmaRetry = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + sense = dev.Seek(out errorChs, 0, 0, 1, timeout, out duration); + Seek = (!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0); + + sense = dev.Read(out cmdBuf, out errorLba, false, 0, 1, timeout, out duration); + ReadLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + sense = dev.Read(out cmdBuf, out errorLba, true, 0, 1, timeout, out duration); + ReadRetryLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorLba, false, 0, 1, timeout, out duration); + ReadDmaLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorLba, true, 0, 1, timeout, out duration); + ReadDmaRetryLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + sense = dev.Seek(out errorLba, 0, timeout, out duration); + SeekLba = (!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0); + + sense = dev.Read(out cmdBuf, out errorLba48, 0, 1, timeout, out duration); + ReadLba48 = (!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + sense = dev.ReadDma(out cmdBuf, out errorLba48, 0, 1, timeout, out duration); + ReadDmaLba48 = (!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + + if (!lbaMode) + { + if (blocks > 0xFFFFFFF && !ReadLba48 && !ReadDmaLba48) + { + DicConsole.ErrorWriteLine("Device needs 48-bit LBA commands but I can't issue them... Aborting."); + return; + } + + if (!ReadLba && !ReadRetryLba && !ReadDmaLba && !ReadDmaRetryLba) + { + DicConsole.ErrorWriteLine("Device needs 28-bit LBA commands but I can't issue them... Aborting."); + return; + } + } + else + { + if (!Read && !ReadRetry && !ReadDma && !ReadDmaRetry) + { + DicConsole.ErrorWriteLine("Device needs CHS commands but I can't issue them... Aborting."); + return; + } + } + + if (ReadDmaLba48) + DicConsole.WriteLine("Using ATA READ DMA EXT command."); + else if (ReadLba48) + DicConsole.WriteLine("Using ATA READ EXT command."); + else if (ReadDmaRetryLba) + DicConsole.WriteLine("Using ATA READ DMA command with retries (LBA)."); + else if (ReadDmaLba) + DicConsole.WriteLine("Using ATA READ DMA command (LBA)."); + else if (ReadRetryLba) + DicConsole.WriteLine("Using ATA READ command with retries (LBA)."); + else if (ReadLba) + DicConsole.WriteLine("Using ATA READ command (LBA)."); + else if (ReadDmaRetry) + DicConsole.WriteLine("Using ATA READ DMA command with retries (CHS)."); + else if (ReadDma) + DicConsole.WriteLine("Using ATA READ DMA command (CHS)."); + else if (ReadRetry) + DicConsole.WriteLine("Using ATA READ command with retries (CHS)."); + else if (Read) + DicConsole.WriteLine("Using ATA READ command (CHS)."); + + byte blocksToRead = 64; + bool error = true; + while (lbaMode) + { + if (ReadDmaLba48) + { + sense = dev.ReadDma(out cmdBuf, out errorLba48, 0, blocksToRead, timeout, out duration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + } + else if (ReadLba48) + { + sense = dev.Read(out cmdBuf, out errorLba48, 0, blocksToRead, timeout, out duration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + } + else if (ReadDmaRetryLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, true, 0, blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadDmaLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, false, 0, blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadRetryLba) + { + sense = dev.Read(out cmdBuf, out errorLba, true, 0, blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + else if (ReadLba) + { + sense = dev.Read(out cmdBuf, out errorLba, false, 0, blocksToRead, timeout, out duration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + } + + if (error) + blocksToRead /= 2; + + if (!error || blocksToRead == 1) + break; + } + + if (error && lbaMode) + { + DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError); + return; + } + + ulong A = 0; // <3ms + ulong B = 0; // >=3ms, <10ms + ulong C = 0; // >=10ms, <50ms + ulong D = 0; // >=50ms, <150ms + ulong E = 0; // >=150ms, <500ms + ulong F = 0; // >=500ms + ulong errored = 0; + DateTime start; + DateTime end; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + List unreadableSectors = new List(); + double seekMax = double.MinValue; + double seekMin = double.MaxValue; + double seekTotal = 0; + const int seekTimes = 1000; + + double seekCur = 0; + + Random rnd = new Random(); + + uint seekPos = (uint)rnd.Next((int)blocks); + ushort seekCy = (ushort)rnd.Next((int)cylinders); + byte seekHd = (byte)rnd.Next((int)heads); + byte seekSc = (byte)rnd.Next((int)sectors); + + aborted = false; + System.Console.CancelKeyPress += (sender, e) => + { + e.Cancel = aborted = true; + }; + + if (lbaMode) + { + DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead); + + mhddLog = new Core.MHDDLog(MHDDLogPath, dev, blocks, blockSize, blocksToRead); + ibgLog = new Core.IBGLog(IBGLogPath, currentProfile); + + start = DateTime.UtcNow; + for (ulong i = 0; i < blocks; i += blocksToRead) + { + if (aborted) + break; + + double cmdDuration = 0; + + if ((blocks - i) < blocksToRead) + blocksToRead = (byte)(blocks - i); + + if (currentSpeed > maxSpeed && currentSpeed != 0) + maxSpeed = currentSpeed; + if (currentSpeed < minSpeed && currentSpeed != 0) + minSpeed = currentSpeed; + + DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed); + + error = true; + byte status = 0, errorByte = 0; + + if (ReadDmaLba48) + { + sense = dev.ReadDma(out cmdBuf, out errorLba48, i, blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + status = errorLba48.status; + errorByte = errorLba48.error; + } + else if (ReadLba48) + { + sense = dev.Read(out cmdBuf, out errorLba48, i, blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba48.status & 0x27) == 0 && errorLba48.error == 0 && cmdBuf.Length > 0); + status = errorLba48.status; + errorByte = errorLba48.error; + } + else if (ReadDmaRetryLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, true, (uint)i, blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + else if (ReadDmaLba) + { + sense = dev.ReadDma(out cmdBuf, out errorLba, false, (uint)i, blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + else if (ReadRetryLba) + { + sense = dev.Read(out cmdBuf, out errorLba, true, (uint)i, blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + else if (ReadLba) + { + sense = dev.Read(out cmdBuf, out errorLba, false, (uint)i, blocksToRead, timeout, out cmdDuration); + error = !(!sense && (errorLba.status & 0x27) == 0 && errorLba.error == 0 && cmdBuf.Length > 0); + status = errorLba.status; + errorByte = errorLba.error; + } + + if (!error) + { + if (cmdDuration >= 500) + { + F += blocksToRead; + } + else if (cmdDuration >= 150) + { + E += blocksToRead; + } + else if (cmdDuration >= 50) + { + D += blocksToRead; + } + else if (cmdDuration >= 10) + { + C += blocksToRead; + } + else if (cmdDuration >= 3) + { + B += blocksToRead; + } + else + { + A += blocksToRead; + } + + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + } + else + { + DicConsole.DebugWriteLine("Media-Scan", "ATA ERROR: {0} STATUS: {1}", errorByte, status); + errored += blocksToRead; + unreadableSectors.Add(i); + if (cmdDuration < 500) + mhddLog.Write(i, 65535); + else + mhddLog.Write(i, cmdDuration); + + ibgLog.Write(i, 0); + } + + currentSpeed = ((double)blockSize * blocksToRead / (double)1048576) / (cmdDuration / (double)1000); + GC.Collect(); + } + end = DateTime.UtcNow; + DicConsole.WriteLine(); + mhddLog.Close(); + ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); + + if (SeekLba) + { + for (int i = 0; i < seekTimes; i++) + { + if (aborted) + break; + + seekPos = (uint)rnd.Next((int)blocks); + + DicConsole.Write("\rSeeking to sector {0}...\t\t", seekPos); + + if (SeekLba) + dev.Seek(out errorLba, seekPos, timeout, out seekCur); + + if (seekCur > seekMax && seekCur != 0) + seekMax = seekCur; + if (seekCur < seekMin && seekCur != 0) + seekMin = seekCur; + + seekTotal += seekCur; + GC.Collect(); + } + } + } + else + { + mhddLog = new Core.MHDDLog(MHDDLogPath, dev, blocks, blockSize, blocksToRead); + ibgLog = new Core.IBGLog(IBGLogPath, currentProfile); + + ulong currentBlock = 0; + blocks = (ulong)(cylinders * heads * sectors); + start = DateTime.UtcNow; + for (ushort Cy = 0; Cy < cylinders; Cy++) + { + for (byte Hd = 0; Hd < heads; Hd++) + { + for (byte Sc = 1; Sc < sectors; Sc++) + { + if (aborted) + break; + + double cmdDuration = 0; + + if (currentSpeed > maxSpeed && currentSpeed != 0) + maxSpeed = currentSpeed; + if (currentSpeed < minSpeed && currentSpeed != 0) + minSpeed = currentSpeed; + + DicConsole.Write("\rReading cylinder {0} head {1} sector {2} ({3:F3} MiB/sec.)", Cy, Hd, Sc, currentSpeed); + + error = true; + byte status = 0, errorByte = 0; + + if (ReadDmaRetry) + { + sense = dev.ReadDma(out cmdBuf, out errorChs, true, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + else if (ReadDma) + { + sense = dev.ReadDma(out cmdBuf, out errorChs, false, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + else if (ReadRetry) + { + sense = dev.Read(out cmdBuf, out errorChs, true, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + else if (Read) + { + sense = dev.Read(out cmdBuf, out errorChs, false, Cy, Hd, Sc, 1, timeout, out cmdDuration); + error = !(!sense && (errorChs.status & 0x27) == 0 && errorChs.error == 0 && cmdBuf.Length > 0); + status = errorChs.status; + errorByte = errorChs.error; + } + + if (!error) + { + if (cmdDuration >= 500) + { + F += blocksToRead; + } + else if (cmdDuration >= 150) + { + E += blocksToRead; + } + else if (cmdDuration >= 50) + { + D += blocksToRead; + } + else if (cmdDuration >= 10) + { + C += blocksToRead; + } + else if (cmdDuration >= 3) + { + B += blocksToRead; + } + else + { + A += blocksToRead; + } + + mhddLog.Write(currentBlock, cmdDuration); + ibgLog.Write(currentBlock, currentSpeed * 1024); + } + else + { + DicConsole.DebugWriteLine("Media-Scan", "ATA ERROR: {0} STATUS: {1}", errorByte, status); + errored += blocksToRead; + unreadableSectors.Add(currentBlock); + if (cmdDuration < 500) + mhddLog.Write(currentBlock, 65535); + else + mhddLog.Write(currentBlock, cmdDuration); + + ibgLog.Write(currentBlock, 0); + } + + currentSpeed = ((double)blockSize / (double)1048576) / (cmdDuration / (double)1000); + GC.Collect(); + + currentBlock++; + } + } + } + end = DateTime.UtcNow; + DicConsole.WriteLine(); + mhddLog.Close(); + ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, (((double)blockSize * (double)(blocks + 1)) / 1024) / (totalDuration / 1000), devicePath); + + if (Seek) + { + for (int i = 0; i < seekTimes; i++) + { + if (aborted) + break; + + seekCy = (ushort)rnd.Next((int)cylinders); + seekHd = (byte)rnd.Next((int)heads); + seekSc = (byte)rnd.Next((int)sectors); + + DicConsole.Write("\rSeeking to cylinder {0}, head {1}, sector {2}...\t\t", seekCy, seekHd, seekSc); + + if (Seek) + dev.Seek(out errorChs, seekCy, seekHd, seekSc, timeout, out seekCur); + + if (seekCur > seekMax && seekCur != 0) + seekMax = seekCur; + if (seekCur < seekMin && seekCur != 0) + seekMin = seekCur; + + seekTotal += seekCur; + GC.Collect(); + } + } + } + + DicConsole.WriteLine(); + + DicConsole.WriteLine("Took a total of {0} seconds ({1} processing commands).", (end - start).TotalSeconds, totalDuration / 1000); + DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.", (((double)blockSize * (double)(blocks + 1)) / 1048576) / (totalDuration / 1000)); + DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed); + DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed); + DicConsole.WriteLine("Summary:"); + DicConsole.WriteLine("{0} sectors took less than 3 ms.", A); + DicConsole.WriteLine("{0} sectors took less than 10 ms but more than 3 ms.", B); + DicConsole.WriteLine("{0} sectors took less than 50 ms but more than 10 ms.", C); + DicConsole.WriteLine("{0} sectors took less than 150 ms but more than 50 ms.", D); + DicConsole.WriteLine("{0} sectors took less than 500 ms but more than 150 ms.", E); + DicConsole.WriteLine("{0} sectors took more than 500 ms.", F); + DicConsole.WriteLine("{0} sectors could not be read.", unreadableSectors.Count); + if (unreadableSectors.Count > 0) + { + foreach (ulong bad in unreadableSectors) + DicConsole.WriteLine("Sector {0} could not be read", bad); + } + DicConsole.WriteLine(); + + if (seekTotal != 0 || seekMin != double.MaxValue || seekMax != double.MinValue) + DicConsole.WriteLine("Testing {0} seeks, longest seek took {1:F3} ms, fastest one took {2:F3} ms. ({3:F3} ms average)", + seekTimes, seekMax, seekMin, seekTotal / 1000); + + Core.Statistics.AddMediaScan((long)A, (long)B, (long)C, (long)D, (long)E, (long)F, (long)blocks, (long)errored, (long)(blocks - errored)); + } + else + DicConsole.ErrorWriteLine("Unable to communicate with ATA device."); } static void doNVMeMediaScan(string MHDDLogPath, string IBGLogPath, string devicePath, Device dev) @@ -719,7 +1265,7 @@ namespace DiscImageChef.Commands DicConsole.WriteLine(); if (seekTotal != 0 || seekMin != double.MaxValue || seekMax != double.MinValue) - DicConsole.WriteLine("Testing {0} seeks, longest seek took {1} ms, fastest one took {2} ms. ({3} ms average)", + DicConsole.WriteLine("Testing {0} seeks, longest seek took {1:F3} ms, fastest one took {2:F3} ms. ({3:F3} ms average)", seekTimes, seekMax, seekMin, seekTotal / 1000); Core.Statistics.AddMediaScan((long)A, (long)B, (long)C, (long)D, (long)E, (long)F, (long)blocks, (long)errored, (long)(blocks - errored));