2017-05-28 21:01:17 +01:00
|
|
|
|
// /***************************************************************************
|
2020-02-27 12:31:25 +00:00
|
|
|
|
// Aaru Data Preservation Suite
|
2017-05-28 21:01:17 +01:00
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
//
|
|
|
|
|
|
// Filename : ATA.cs
|
2017-12-19 03:50:57 +00:00
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
2017-05-28 21:01:17 +01:00
|
|
|
|
//
|
2017-12-19 03:50:57 +00:00
|
|
|
|
// Component : Core algorithms.
|
2017-05-28 21:01:17 +01:00
|
|
|
|
//
|
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
|
//
|
2017-12-19 03:50:57 +00:00
|
|
|
|
// Creates reports from ATA devices.
|
2017-05-28 21:01:17 +01:00
|
|
|
|
//
|
|
|
|
|
|
// --[ 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
//
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2022-02-18 10:02:53 +00:00
|
|
|
|
// Copyright © 2011-2022 Natalia Portillo
|
2017-05-28 21:01:17 +01:00
|
|
|
|
// ****************************************************************************/
|
2017-12-19 03:50:57 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
namespace Aaru.Core.Devices.Report;
|
|
|
|
|
|
|
2017-05-28 21:01:17 +01:00
|
|
|
|
using System;
|
2020-02-27 00:33:26 +00:00
|
|
|
|
using Aaru.CommonTypes.Metadata;
|
|
|
|
|
|
using Aaru.Console;
|
|
|
|
|
|
using Aaru.Decoders.ATA;
|
|
|
|
|
|
using Aaru.Devices;
|
2022-03-07 07:36:44 +00:00
|
|
|
|
using global::Spectre.Console;
|
2020-02-27 00:33:26 +00:00
|
|
|
|
using Identify = Aaru.CommonTypes.Structs.Devices.ATA.Identify;
|
2017-05-28 21:01:17 +01:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
public sealed partial class DeviceReport
|
2017-05-28 21:01:17 +01:00
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
/// <summary>Creates a report for media inserted into an ATA device</summary>
|
|
|
|
|
|
/// <returns>Media report</returns>
|
|
|
|
|
|
public TestedMedia ReportAtaMedia()
|
2017-05-28 21:01:17 +01:00
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
|
var sense = true;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
AtaErrorRegistersChs errorChs = new();
|
|
|
|
|
|
AtaErrorRegistersLba28 errorLba = new();
|
|
|
|
|
|
AtaErrorRegistersLba48 errorLba48 = new();
|
|
|
|
|
|
byte[] buffer = Array.Empty<byte>();
|
|
|
|
|
|
byte[] readBuf = Array.Empty<byte>();
|
|
|
|
|
|
|
|
|
|
|
|
var mediaTest = new TestedMedia
|
2017-05-28 21:01:17 +01:00
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
|
MediumTypeName = AnsiConsole.Ask<string>("Please write a description of the media type and press enter: "),
|
|
|
|
|
|
Model = AnsiConsole.Ask<string>("Please write the media model and press enter: "),
|
2022-03-06 13:29:38 +00:00
|
|
|
|
MediaIsRecognized = true
|
|
|
|
|
|
};
|
2017-12-23 01:46:08 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Querying ATA IDENTIFY...").IsIndeterminate();
|
|
|
|
|
|
_dev.AtaIdentify(out buffer, out _, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.IdentifyData = ClearIdentify(buffer);
|
|
|
|
|
|
mediaTest.IdentifyDevice = Identify.Decode(buffer);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
if(mediaTest.IdentifyDevice.HasValue)
|
2018-11-25 19:13:21 +00:00
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
Identify.IdentifyDevice ataId = mediaTest.IdentifyDevice.Value;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.UnformattedBPT != 0)
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.UnformattedBPT = ataId.UnformattedBPT;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.UnformattedBPS != 0)
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.UnformattedBPS = ataId.UnformattedBPS;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.Cylinders > 0 &&
|
|
|
|
|
|
ataId.Heads > 0 &&
|
|
|
|
|
|
ataId.SectorsPerTrack > 0)
|
2018-11-25 19:13:21 +00:00
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.CHS = new Chs
|
2018-11-25 19:13:21 +00:00
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
|
Cylinders = ataId.Cylinders,
|
|
|
|
|
|
Heads = ataId.Heads,
|
|
|
|
|
|
Sectors = ataId.SectorsPerTrack
|
2018-11-25 19:13:21 +00:00
|
|
|
|
};
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.CurrentCylinders > 0 &&
|
|
|
|
|
|
ataId.CurrentHeads > 0 &&
|
|
|
|
|
|
ataId.CurrentSectorsPerTrack > 0)
|
2018-11-25 19:13:21 +00:00
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.CurrentCHS = new Chs
|
2018-11-25 19:13:21 +00:00
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
|
Cylinders = ataId.CurrentCylinders,
|
|
|
|
|
|
Heads = ataId.CurrentHeads,
|
2018-11-25 19:13:21 +00:00
|
|
|
|
Sectors = ataId.CurrentSectorsPerTrack
|
|
|
|
|
|
};
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
if(mediaTest.Blocks == 0)
|
|
|
|
|
|
mediaTest.Blocks =
|
|
|
|
|
|
(ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport))
|
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.LBASectors = ataId.LBASectors;
|
|
|
|
|
|
mediaTest.Blocks = ataId.LBASectors;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48))
|
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.LBA48Sectors = ataId.LBA48Sectors;
|
|
|
|
|
|
mediaTest.Blocks = ataId.LBA48Sectors;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.NominalRotationRate != 0x0000 &&
|
|
|
|
|
|
ataId.NominalRotationRate != 0xFFFF)
|
2018-11-25 19:13:21 +00:00
|
|
|
|
if(ataId.NominalRotationRate == 0x0001)
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SolidStateDevice = true;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SolidStateDevice = false;
|
|
|
|
|
|
mediaTest.NominalRotationRate = ataId.NominalRotationRate;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
uint logicalSectorSize;
|
|
|
|
|
|
uint physicalSectorSize;
|
|
|
|
|
|
|
|
|
|
|
|
if((ataId.PhysLogSectorSize & 0x8000) == 0x0000 &&
|
|
|
|
|
|
(ataId.PhysLogSectorSize & 0x4000) == 0x4000)
|
2018-11-25 19:13:21 +00:00
|
|
|
|
{
|
|
|
|
|
|
if((ataId.PhysLogSectorSize & 0x1000) == 0x1000)
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.LogicalSectorWords <= 255 ||
|
|
|
|
|
|
ataId.LogicalAlignment == 0xFFFF)
|
|
|
|
|
|
logicalSectorSize = 512;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
else
|
2019-12-27 18:00:03 +00:00
|
|
|
|
logicalSectorSize = ataId.LogicalSectorWords * 2;
|
|
|
|
|
|
else
|
|
|
|
|
|
logicalSectorSize = 512;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
|
|
|
|
|
if((ataId.PhysLogSectorSize & 0x2000) == 0x2000)
|
2022-03-06 13:29:38 +00:00
|
|
|
|
physicalSectorSize = (uint)(logicalSectorSize * ((1 << ataId.PhysLogSectorSize) & 0xF));
|
2019-12-27 18:00:03 +00:00
|
|
|
|
else
|
|
|
|
|
|
physicalSectorSize = logicalSectorSize;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2019-12-27 18:00:03 +00:00
|
|
|
|
logicalSectorSize = 512;
|
|
|
|
|
|
physicalSectorSize = 512;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.BlockSize = logicalSectorSize;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
|
|
|
|
|
if(physicalSectorSize != logicalSectorSize)
|
2018-11-25 19:13:21 +00:00
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.PhysicalBlockSize = physicalSectorSize;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if((ataId.LogicalAlignment & 0x8000) == 0x0000 &&
|
|
|
|
|
|
(ataId.LogicalAlignment & 0x4000) == 0x4000)
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.EccBytes != 0x0000 &&
|
|
|
|
|
|
ataId.EccBytes != 0xFFFF)
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.LongBlockSize = logicalSectorSize + ataId.EccBytes;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.UnformattedBPS > logicalSectorSize &&
|
2022-03-06 13:29:38 +00:00
|
|
|
|
(!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || mediaTest.LongBlockSize == 516))
|
|
|
|
|
|
mediaTest.LongBlockSize = ataId.UnformattedBPS;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
|
|
|
|
|
if(ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeSet) &&
|
|
|
|
|
|
!ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeClear) &&
|
|
|
|
|
|
ataId.EnabledCommandSet3.HasFlag(Identify.CommandSetBit3.MediaSerial))
|
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.CanReadMediaSerial = true;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2018-11-25 19:13:21 +00:00
|
|
|
|
if(!string.IsNullOrWhiteSpace(ataId.MediaManufacturer))
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.Manufacturer = ataId.MediaManufacturer;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
var checkCorrectRead = BitConverter.ToUInt64(buffer, 0);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadSectors = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadSectorsData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) RETRY in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadSectorsRetryData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadDma = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadDmaData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA RETRY in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadDmaRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadDmaRetryData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying SEEK in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Seek(out errorChs, 0, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsSeek = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense,
|
2020-02-29 18:03:35 +00:00
|
|
|
|
errorChs.Status, errorChs.Error);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadLbaData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) RETRY in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadRetryLbaData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadDmaLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadDmaLbaData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA RETRY in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadDmaRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadDmaRetryLbaData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying SEEK in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Seek(out errorLba, 0, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense,
|
2022-03-06 13:29:38 +00:00
|
|
|
|
errorChs.Status, errorChs.Error);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) in LBA48 mode...").IsIndeterminate();
|
2022-03-06 13:29:38 +00:00
|
|
|
|
sense = _dev.Read(out readBuf, out AtaErrorRegistersLba48 errorLba48, 0, 1, _dev.Timeout, out _);
|
2021-09-13 18:08:44 +01:00
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadLba48Data = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA in LBA48 mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
mediaTest.SupportsReadDmaLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadDmaLba48Data = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
|
|
|
|
|
// Send SET FEATURES before sending READ LONG commands, retrieve IDENTIFY again and
|
|
|
|
|
|
// check if ECC size changed. Sector is set to 1 because without it most drives just return
|
|
|
|
|
|
// CORRECTABLE ERROR for this command.
|
2019-12-27 17:48:23 +00:00
|
|
|
|
_dev.SetFeatures(out _, AtaFeatures.EnableReadLongVendorLength, 0, 0, 1, 0, _dev.Timeout, out _);
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
_dev.AtaIdentify(out buffer, out _, _dev.Timeout, out _);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2018-11-25 19:13:21 +00:00
|
|
|
|
if(Identify.Decode(buffer).HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
ataId = Identify.Decode(buffer).Value;
|
|
|
|
|
|
|
2019-12-27 18:00:03 +00:00
|
|
|
|
if(ataId.EccBytes != 0x0000 &&
|
|
|
|
|
|
ataId.EccBytes != 0xFFFF)
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.LongBlockSize = logicalSectorSize + ataId.EccBytes;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
|
|
|
|
|
if(ataId.UnformattedBPS > logicalSectorSize &&
|
2022-03-06 13:29:38 +00:00
|
|
|
|
(!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || mediaTest.LongBlockSize == 516))
|
|
|
|
|
|
mediaTest.LongBlockSize = ataId.UnformattedBPS;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG in CHS mode...").IsIndeterminate();
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, mediaTest.LongBlockSize ?? 0,
|
2021-09-13 18:08:44 +01:00
|
|
|
|
_dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadLong = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
2022-03-07 07:36:44 +00:00
|
|
|
|
readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadLongData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG RETRY in CHS mode...").IsIndeterminate();
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, mediaTest.LongBlockSize ?? 0,
|
2021-09-13 18:08:44 +01:00
|
|
|
|
_dev.Timeout, out _);
|
|
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadLongRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) !=
|
|
|
|
|
|
checkCorrectRead;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadLongRetryData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG in LBA mode...").IsIndeterminate();
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorLba, false, 0, mediaTest.LongBlockSize ?? 0, _dev.Timeout,
|
|
|
|
|
|
out _);
|
2021-09-13 18:08:44 +01:00
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.SupportsReadLongLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
2022-03-07 07:36:44 +00:00
|
|
|
|
readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
mediaTest.ReadLongLbaData = readBuf;
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2021-09-13 18:08:44 +01:00
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG RETRY in LBA mode...").IsIndeterminate();
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorLba, true, 0, mediaTest.LongBlockSize ?? 0, _dev.Timeout,
|
|
|
|
|
|
out _);
|
2021-09-13 18:08:44 +01:00
|
|
|
|
});
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
mediaTest.SupportsReadLongRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) !=
|
|
|
|
|
|
checkCorrectRead;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
|
|
mediaTest.ReadLongRetryLbaData = readBuf;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
mediaTest.MediaIsRecognized = false;
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
return mediaTest;
|
|
|
|
|
|
}
|
2019-12-27 18:00:03 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
/// <summary>Creates a report of an ATA device</summary>
|
|
|
|
|
|
public TestedMedia ReportAta(Identify.IdentifyDevice ataId)
|
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
|
var sense = true;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
byte[] readBuf = Array.Empty<byte>();
|
|
|
|
|
|
AtaErrorRegistersChs errorChs = new();
|
|
|
|
|
|
AtaErrorRegistersLba28 errorLba = new();
|
|
|
|
|
|
AtaErrorRegistersLba48 errorLba48 = new();
|
|
|
|
|
|
var capabilities = new TestedMedia();
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.UnformattedBPT != 0)
|
|
|
|
|
|
capabilities.UnformattedBPT = ataId.UnformattedBPT;
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.UnformattedBPS != 0)
|
|
|
|
|
|
capabilities.UnformattedBPS = ataId.UnformattedBPS;
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.Cylinders > 0 &&
|
|
|
|
|
|
ataId.Heads > 0 &&
|
|
|
|
|
|
ataId.SectorsPerTrack > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
capabilities.CHS = new Chs
|
|
|
|
|
|
{
|
|
|
|
|
|
Cylinders = ataId.Cylinders,
|
|
|
|
|
|
Heads = ataId.Heads,
|
|
|
|
|
|
Sectors = ataId.SectorsPerTrack
|
|
|
|
|
|
};
|
2018-11-25 19:13:21 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
capabilities.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack);
|
2017-05-28 21:01:17 +01:00
|
|
|
|
}
|
2018-12-25 14:46:02 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
if(ataId.CurrentCylinders > 0 &&
|
|
|
|
|
|
ataId.CurrentHeads > 0 &&
|
|
|
|
|
|
ataId.CurrentSectorsPerTrack > 0)
|
2018-12-25 14:46:02 +00:00
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
|
capabilities.CurrentCHS = new Chs
|
|
|
|
|
|
{
|
|
|
|
|
|
Cylinders = ataId.CurrentCylinders,
|
|
|
|
|
|
Heads = ataId.CurrentHeads,
|
|
|
|
|
|
Sectors = ataId.CurrentSectorsPerTrack
|
|
|
|
|
|
};
|
2018-12-25 14:46:02 +00:00
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
capabilities.Blocks = (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack);
|
2022-03-06 13:29:38 +00:00
|
|
|
|
}
|
2018-12-25 14:46:02 +00:00
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
|
if(ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport))
|
|
|
|
|
|
{
|
|
|
|
|
|
capabilities.LBASectors = ataId.LBASectors;
|
|
|
|
|
|
capabilities.Blocks = ataId.LBASectors;
|
2018-12-25 14:46:02 +00:00
|
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
|
|
if(ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48))
|
|
|
|
|
|
{
|
|
|
|
|
|
capabilities.LBA48Sectors = ataId.LBA48Sectors;
|
|
|
|
|
|
capabilities.Blocks = ataId.LBA48Sectors;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.NominalRotationRate != 0x0000 &&
|
|
|
|
|
|
ataId.NominalRotationRate != 0xFFFF)
|
|
|
|
|
|
if(ataId.NominalRotationRate == 0x0001)
|
|
|
|
|
|
capabilities.SolidStateDevice = true;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
capabilities.SolidStateDevice = false;
|
|
|
|
|
|
capabilities.NominalRotationRate = ataId.NominalRotationRate;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint logicalSectorSize;
|
|
|
|
|
|
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, ataId.PhysLogSectorSize & 0xF);
|
|
|
|
|
|
else
|
|
|
|
|
|
physicalSectorSize = logicalSectorSize;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
logicalSectorSize = 512;
|
|
|
|
|
|
physicalSectorSize = 512;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.BlockSize = logicalSectorSize;
|
|
|
|
|
|
|
|
|
|
|
|
if(physicalSectorSize != logicalSectorSize)
|
|
|
|
|
|
{
|
|
|
|
|
|
capabilities.PhysicalBlockSize = physicalSectorSize;
|
|
|
|
|
|
|
|
|
|
|
|
if((ataId.LogicalAlignment & 0x8000) == 0x0000 &&
|
|
|
|
|
|
(ataId.LogicalAlignment & 0x4000) == 0x4000)
|
|
|
|
|
|
capabilities.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.EccBytes != 0x0000 &&
|
|
|
|
|
|
ataId.EccBytes != 0xFFFF)
|
|
|
|
|
|
capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes;
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.UnformattedBPS > logicalSectorSize &&
|
|
|
|
|
|
(!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516))
|
|
|
|
|
|
capabilities.LongBlockSize = ataId.UnformattedBPS;
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeSet) &&
|
|
|
|
|
|
!ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeClear) &&
|
|
|
|
|
|
ataId.EnabledCommandSet3.HasFlag(Identify.CommandSetBit3.MediaSerial))
|
|
|
|
|
|
{
|
|
|
|
|
|
capabilities.CanReadMediaSerial = true;
|
|
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(ataId.MediaManufacturer))
|
|
|
|
|
|
capabilities.Manufacturer = ataId.MediaManufacturer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ulong checkCorrectRead = 0;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadSectors = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadSectorsData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) RETRY in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadRetry =
|
|
|
|
|
|
!sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadSectorsRetryData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadDma =
|
|
|
|
|
|
!sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadDmaData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA RETRY in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadDmaRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadDmaRetryData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying SEEK in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Seek(out errorChs, 0, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsSeek = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense,
|
|
|
|
|
|
errorChs.Status, errorChs.Error);
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadLba =
|
|
|
|
|
|
!sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba.Status, errorLba.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadLbaData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) RETRY in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba.Status, errorLba.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadRetryLbaData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadDmaLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba.Status, errorLba.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadDmaLbaData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA RETRY in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadDmaRetryLba =
|
|
|
|
|
|
!sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba.Status, errorLba.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadDmaRetryLbaData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying SEEK in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Seek(out errorLba, 0, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense,
|
|
|
|
|
|
errorLba.Status, errorLba.Error);
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ SECTOR(S) in LBA48 mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.Read(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba48.Status, errorLba48.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadLba48Data = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ DMA in LBA48 mode...").IsIndeterminate();
|
|
|
|
|
|
sense = _dev.ReadDma(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadDmaLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba48.Status, errorLba48.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadDmaLba48Data = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
// Send SET FEATURES before sending READ LONG commands, retrieve IDENTIFY again and
|
|
|
|
|
|
// check if ECC size changed. Sector is set to 1 because without it most drives just return
|
|
|
|
|
|
// CORRECTABLE ERROR for this command.
|
|
|
|
|
|
_dev.SetFeatures(out _, AtaFeatures.EnableReadLongVendorLength, 0, 0, 1, 0, _dev.Timeout, out _);
|
|
|
|
|
|
|
|
|
|
|
|
_dev.AtaIdentify(out byte[] buffer, out _, _dev.Timeout, out _);
|
|
|
|
|
|
|
|
|
|
|
|
if(Identify.Decode(buffer).HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
ataId = Identify.Decode(buffer).Value;
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.EccBytes != 0x0000 &&
|
|
|
|
|
|
ataId.EccBytes != 0xFFFF)
|
|
|
|
|
|
capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes;
|
|
|
|
|
|
|
|
|
|
|
|
if(ataId.UnformattedBPS > logicalSectorSize &&
|
|
|
|
|
|
(!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516))
|
|
|
|
|
|
capabilities.LongBlockSize = ataId.UnformattedBPS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
|
|
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, capabilities.LongBlockSize ?? 0,
|
|
|
|
|
|
_dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadLong = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadLongData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG RETRY in CHS mode...").IsIndeterminate();
|
|
|
|
|
|
|
|
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, capabilities.LongBlockSize ?? 0,
|
|
|
|
|
|
_dev.Timeout, out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadLongRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
|
2022-03-07 07:36:44 +00:00
|
|
|
|
readBuf.Length > 0 &&
|
|
|
|
|
|
BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorChs.Status, errorChs.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadLongRetryData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorLba, false, 0, capabilities.LongBlockSize ?? 0, _dev.Timeout,
|
|
|
|
|
|
out _);
|
2022-03-06 13:29:38 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadLongLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
2022-03-07 07:36:44 +00:00
|
|
|
|
readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba.Status, errorLba.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadLongLbaData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
Spectre.ProgressSingleSpinner(ctx =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.AddTask("Trying READ LONG RETRY in LBA mode...").IsIndeterminate();
|
|
|
|
|
|
|
|
|
|
|
|
sense = _dev.ReadLong(out readBuf, out errorLba, true, 0, capabilities.LongBlockSize ?? 0, _dev.Timeout,
|
|
|
|
|
|
out _);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.SupportsReadLongRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
|
|
|
|
|
|
readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) !=
|
|
|
|
|
|
checkCorrectRead;
|
|
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
|
|
|
|
|
|
sense, errorLba.Status, errorLba.Error, readBuf.Length);
|
|
|
|
|
|
|
|
|
|
|
|
capabilities.ReadLongRetryLbaData = readBuf;
|
|
|
|
|
|
|
|
|
|
|
|
return capabilities;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>Clear serial numbers and other private fields from an IDENTIFY ATA DEVICE response</summary>
|
|
|
|
|
|
/// <param name="buffer">IDENTIFY ATA DEVICE response</param>
|
|
|
|
|
|
/// <returns>IDENTIFY ATA DEVICE response without the private fields</returns>
|
|
|
|
|
|
public static byte[] ClearIdentify(byte[] buffer)
|
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
|
var empty = new byte[512];
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
|
|
Array.Copy(empty, 0, buffer, 20, 20);
|
|
|
|
|
|
Array.Copy(empty, 0, buffer, 216, 8);
|
|
|
|
|
|
Array.Copy(empty, 0, buffer, 224, 8);
|
|
|
|
|
|
Array.Copy(empty, 0, buffer, 352, 40);
|
|
|
|
|
|
|
|
|
|
|
|
return buffer;
|
2017-05-28 21:01:17 +01:00
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
}
|