Files
Aaru/Aaru.Core/Devices/Report/Scsi.cs

1078 lines
41 KiB
C#

// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : General.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Creates reports from SCSI devices.
//
// --[ 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/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2022 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Core.Devices.Report;
using System;
using System.Collections.Generic;
using System.Linq;
using Aaru.CommonTypes.Metadata;
using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Console;
using Aaru.Decoders.SCSI;
using Aaru.Devices;
using Aaru.Helpers;
using global::Spectre.Console;
using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry;
public sealed partial class DeviceReport
{
/// <summary>Creates a report for the SCSI INQUIRY response</summary>
/// <returns>SCSI report</returns>
public Scsi ReportScsiInquiry()
{
var sense = true;
byte[] buffer = Array.Empty<byte>();
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI INQUIRY...").IsIndeterminate();
sense = _dev.ScsiInquiry(out buffer, out _);
});
var report = new Scsi();
if(sense)
return null;
Inquiry? decodedNullable = Inquiry.Decode(buffer);
if(!decodedNullable.HasValue)
return null;
report.InquiryData = ClearInquiry(buffer);
return report;
}
internal static byte[] ClearInquiry(byte[] inquiry)
{
Inquiry? decodedNullable = Inquiry.Decode(inquiry);
if(!decodedNullable.HasValue)
return inquiry;
Inquiry decoded = decodedNullable.Value;
if(!decoded.SeagatePresent ||
StringHandlers.CToString(decoded.VendorIdentification)?.Trim().ToLowerInvariant() != "seagate")
return inquiry;
// Clear Seagate serial number
for(var i = 36; i <= 43; i++)
inquiry[i] = 0;
return inquiry;
}
/// <summary>Returns a list of decoded SCSI EVPD pages</summary>
/// <param name="vendor">Decoded SCSI vendor identification</param>
/// <returns>List of decoded SCSI EVPD pages</returns>
public List<ScsiPage> ReportEvpdPages(string vendor)
{
var sense = false;
byte[] buffer = Array.Empty<byte>();
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying list of SCSI EVPDs...").IsIndeterminate();
sense = _dev.ScsiInquiry(out buffer, out _, 0x00);
});
if(sense)
return null;
byte[] evpdPages = EVPD.DecodePage00(buffer);
if(evpdPages == null ||
evpdPages.Length <= 0)
return null;
List<ScsiPage> evpds = new();
Spectre.ProgressSingleSpinner(ctx =>
{
ProgressTask task = ctx.
AddTask("Querying SCSI EVPD pages...", maxValue: evpdPages.Count(page => page != 0x80)).
IsIndeterminate();
foreach(byte page in evpdPages.Where(page => page != 0x80))
{
task.Description = $"Querying SCSI EVPD {page:X2}h...";
task.Increment(1);
sense = _dev.ScsiInquiry(out buffer, out _, page);
if(sense)
continue;
byte[] empty;
switch(page)
{
case 0x83:
buffer = ClearPage83(buffer);
break;
case 0x80:
var identify = new byte[512];
Array.Copy(buffer, 60, identify, 0, 512);
identify = ClearIdentify(identify);
Array.Copy(identify, 0, buffer, 60, 512);
break;
case 0xB1:
case 0xB3:
empty = new byte[buffer.Length - 4];
Array.Copy(empty, 0, buffer, 4, buffer.Length - 4);
break;
case 0xC1 when vendor == "ibm":
empty = new byte[12];
Array.Copy(empty, 0, buffer, 4, 12);
Array.Copy(empty, 0, buffer, 16, 12);
break;
case 0xC2 when vendor == "certance":
case 0xC3 when vendor == "certance":
case 0xC4 when vendor == "certance":
case 0xC5 when vendor == "certance":
case 0xC6 when vendor == "certance":
Array.Copy(new byte[12], 0, buffer, 4, 12);
break;
}
var evpd = new ScsiPage
{
page = page,
value = buffer
};
evpds.Add(evpd);
}
});
return evpds.Count > 0 ? evpds : null;
}
byte[] ClearPage83(byte[] pageResponse)
{
if(pageResponse?[1] != 0x83)
return null;
if(pageResponse[3] + 4 != pageResponse.Length)
return null;
if(pageResponse.Length < 6)
return null;
var position = 4;
while(position < pageResponse.Length)
{
byte length = pageResponse[position + 3];
if(length + position + 4 >= pageResponse.Length)
length = (byte)(pageResponse.Length - position - 4);
var empty = new byte[length];
Array.Copy(empty, 0, pageResponse, position + 4, length);
position += 4 + length;
}
return pageResponse;
}
/// <summary>Adds reports for the decoded SCSI MODE SENSE pages to a device report</summary>
/// <param name="report">Device report</param>
/// <param name="cdromMode">Returns raw MODE SENSE page 2Ah, aka CD-ROM page</param>
/// <param name="mediumType">Returns decoded list of supported media types response</param>
public void ReportScsiModes(ref DeviceReportV2 report, out byte[] cdromMode, out MediumTypes mediumType)
{
Modes.DecodedMode? decMode = null;
PeripheralDeviceTypes devType = _dev.ScsiType;
byte[] mode10Buffer;
byte[] mode6Buffer;
bool sense;
mediumType = 0;
DeviceReportV2 v2 = report;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying all mode pages and subpages using SCSI MODE SENSE (10)...").IsIndeterminate();
foreach(ScsiModeSensePageControl pageControl in new[]
{
ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current,
ScsiModeSensePageControl.Changeable
})
{
var saveBuffer = false;
sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0xFF, _dev.Timeout,
out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0xFF,
_dev.Timeout, out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0x00,
_dev.Timeout, out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0x00,
_dev.Timeout, out _);
if(!sense &&
!_dev.Error)
{
v2.SCSI.SupportsModeSense10 = true;
decMode ??= Modes.DecodeMode10(mode10Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense10 = true;
decMode ??= Modes.DecodeMode10(mode10Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense10 = true;
v2.SCSI.SupportsModeSubpages = true;
decMode ??= Modes.DecodeMode10(mode10Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense10 = true;
v2.SCSI.SupportsModeSubpages = true;
decMode ??= Modes.DecodeMode10(mode10Buffer, devType);
saveBuffer = true;
}
if(!saveBuffer)
continue;
switch(pageControl)
{
case ScsiModeSensePageControl.Default:
v2.SCSI.ModeSense10Data = mode10Buffer;
break;
case ScsiModeSensePageControl.Changeable:
v2.SCSI.ModeSense10ChangeableData = mode10Buffer;
break;
case ScsiModeSensePageControl.Current:
v2.SCSI.ModeSense10CurrentData = mode10Buffer;
break;
}
}
});
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying all mode pages and subpages using SCSI MODE SENSE (6)...").IsIndeterminate();
foreach(ScsiModeSensePageControl pageControl in new[]
{
ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current,
ScsiModeSensePageControl.Changeable
})
{
var saveBuffer = false;
sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0xFF, _dev.Timeout, out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0xFF, _dev.Timeout,
out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0x00, _dev.Timeout,
out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0x00,
_dev.Timeout, out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x00, 0x00,
_dev.Timeout, out _);
if(sense || _dev.Error)
{
sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x00, 0x00,
_dev.Timeout, out _);
if(!sense &&
!_dev.Error)
{
v2.SCSI.SupportsModeSense6 = true;
decMode ??= Modes.DecodeMode6(mode6Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense6 = true;
decMode ??= Modes.DecodeMode6(mode6Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense6 = true;
decMode ??= Modes.DecodeMode6(mode6Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense6 = true;
decMode ??= Modes.DecodeMode6(mode6Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense10 = true;
v2.SCSI.SupportsModeSubpages = true;
decMode ??= Modes.DecodeMode6(mode6Buffer, devType);
saveBuffer = true;
}
}
else
{
v2.SCSI.SupportsModeSense6 = true;
v2.SCSI.SupportsModeSubpages = true;
decMode ??= Modes.DecodeMode6(mode6Buffer, devType);
saveBuffer = true;
}
if(!saveBuffer)
continue;
switch(pageControl)
{
case ScsiModeSensePageControl.Default:
v2.SCSI.ModeSense6Data = mode6Buffer;
break;
case ScsiModeSensePageControl.Changeable:
v2.SCSI.ModeSense6ChangeableData = mode6Buffer;
break;
case ScsiModeSensePageControl.Current:
v2.SCSI.ModeSense6CurrentData = mode6Buffer;
break;
}
}
});
report = v2;
cdromMode = null;
if(decMode == null)
return;
mediumType = decMode.Value.Header.MediumType;
report.SCSI.ModeSense = new ScsiMode
{
BlankCheckEnabled = decMode.Value.Header.EBC,
DPOandFUA = decMode.Value.Header.DPOFUA,
WriteProtected = decMode.Value.Header.WriteProtected
};
if(decMode.Value.Header.BufferedMode > 0)
report.SCSI.ModeSense.BufferedMode = decMode.Value.Header.BufferedMode;
if(decMode.Value.Header.Speed > 0)
report.SCSI.ModeSense.Speed = decMode.Value.Header.Speed;
if(decMode.Value.Pages == null)
return;
List<ScsiPage> modePages = new();
foreach(Modes.ModePage page in decMode.Value.Pages)
{
var modePage = new ScsiPage
{
page = page.Page,
subpage = page.Subpage,
value = page.PageResponse
};
modePages.Add(modePage);
if(modePage.page == 0x2A &&
modePage.subpage == 0x00)
cdromMode = page.PageResponse;
}
if(modePages.Count > 0)
report.SCSI.ModeSense.ModePages = modePages;
}
/// <summary>Creates a report for media inserted into a SCSI device</summary>
/// <returns>Media report</returns>
public TestedMedia ReportScsiMedia()
{
var mediaTest = new TestedMedia();
var sense = true;
byte[] buffer = Array.Empty<byte>();
byte[] senseBuffer = Array.Empty<byte>();
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate();
sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
mediaTest.SupportsReadCapacity = true;
mediaTest.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) &
0xFFFFFFFF) + 1;
mediaTest.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]);
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate();
sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
mediaTest.SupportsReadCapacity16 = true;
var temp = new byte[8];
Array.Copy(buffer, 0, temp, 0, 8);
Array.Reverse(temp);
mediaTest.Blocks = BitConverter.ToUInt64(temp, 0) + 1;
mediaTest.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]);
}
Modes.DecodedMode? decMode = null;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate();
sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, 0x3F,
0x00, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
decMode = Modes.DecodeMode10(buffer, _dev.ScsiType);
mediaTest.ModeSense10Data = buffer;
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate();
sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType);
mediaTest.ModeSense6Data = buffer;
}
if(decMode.HasValue)
{
mediaTest.MediumType = (byte)decMode.Value.Header.MediumType;
if(decMode.Value.Header.BlockDescriptors?.Length > 0)
mediaTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density;
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate();
mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, mediaTest.BlockSize ?? 512,
_dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6);
mediaTest.Read6Data = buffer;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate();
mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0,
mediaTest.BlockSize ?? 512, 0, 1, _dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10);
mediaTest.Read10Data = buffer;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate();
mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0,
mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12);
mediaTest.Read12Data = buffer;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate();
mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0,
mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16);
mediaTest.Read16Data = buffer;
mediaTest.LongBlockSize = mediaTest.BlockSize;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate();
sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _);
});
if(sense && !_dev.Error)
{
DecodedSense? decSense = Sense.Decode(senseBuffer);
if(decSense is { SenseKey: SenseKeys.IllegalRequest, ASC: 0x24, ASCQ: 0x00 })
{
mediaTest.SupportsReadLong = true;
bool valid = decSense.Value.Fixed?.InformationValid == true;
bool ili = decSense.Value.Fixed?.ILI == true;
uint information = decSense.Value.Fixed?.Information ?? 0;
if(decSense.Value.Descriptor.HasValue &&
decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00))
{
valid = true;
ili = true;
information = (uint)Sense.DecodeDescriptor00(desc00);
}
if(valid && ili)
mediaTest.LongBlockSize = 0xFFFF - (information & 0xFFFF);
}
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ LONG (16)...").IsIndeterminate();
sense = _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 0xFFFF, _dev.Timeout, out _);
if(sense && !_dev.Error)
{
DecodedSense? decSense = Sense.Decode(senseBuffer);
if(decSense is { SenseKey: SenseKeys.IllegalRequest, ASC: 0x24, ASCQ: 0x00 })
{
mediaTest.SupportsReadLong16 = true;
bool valid = decSense.Value.Fixed?.InformationValid == true;
bool ili = decSense.Value.Fixed?.ILI == true;
uint information = decSense.Value.Fixed?.Information ?? 0;
if(decSense.Value.Descriptor.HasValue &&
decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00))
{
valid = true;
ili = true;
information = (uint)Sense.DecodeDescriptor00(desc00);
}
if(valid && ili)
mediaTest.LongBlockSize = 0xFFFF - (information & 0xFFFF);
}
}
if((mediaTest.SupportsReadLong == true || mediaTest.SupportsReadLong16 == true) &&
mediaTest.LongBlockSize == mediaTest.BlockSize)
switch(mediaTest.BlockSize)
{
case 512:
{
foreach(ushort testSize in new ushort[]
{
// Long sector sizes for floppies
514,
// Long sector sizes for SuperDisk
536, 558,
// Long sector sizes for 512-byte magneto-opticals
600, 610, 630
})
{
sense = mediaTest.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout,
out _) : _dev.ReadLong10(out buffer, out senseBuffer, false,
false, 0, testSize, _dev.Timeout, out _);
if(sense || _dev.Error)
continue;
mediaTest.LongBlockSize = testSize;
break;
}
break;
}
case 1024:
{
foreach(ushort testSize in new ushort[]
{
// Long sector sizes for floppies
1026,
// Long sector sizes for 1024-byte magneto-opticals
1200
})
{
sense = mediaTest.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout,
out _) : _dev.ReadLong10(out buffer, out senseBuffer, false,
false, 0, testSize, _dev.Timeout, out _);
if(sense || _dev.Error)
continue;
mediaTest.LongBlockSize = testSize;
break;
}
break;
}
case 2048:
{
sense = mediaTest.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 2380, _dev.Timeout, out _)
: _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, _dev.Timeout,
out _);
if(!sense &&
!_dev.Error)
mediaTest.LongBlockSize = 2380;
break;
}
case 4096:
{
sense = mediaTest.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 4760, _dev.Timeout, out _)
: _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, _dev.Timeout,
out _);
if(!sense &&
!_dev.Error)
mediaTest.LongBlockSize = 4760;
break;
}
case 8192:
{
sense = mediaTest.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 9424, _dev.Timeout, out _)
: _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, _dev.Timeout,
out _);
if(!sense &&
!_dev.Error)
mediaTest.LongBlockSize = 9424;
break;
}
}
});
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ MEDIA SERIAL NUMBER...").IsIndeterminate();
mediaTest.CanReadMediaSerial =
!_dev.ReadMediaSerialNumber(out buffer, out senseBuffer, _dev.Timeout, out _);
});
return mediaTest;
}
/// <summary>Creates a media report for a non-removable SCSI device</summary>
/// <returns>Media report</returns>
public TestedMedia ReportScsi()
{
var sense = true;
byte[] buffer = Array.Empty<byte>();
byte[] senseBuffer = Array.Empty<byte>();
var capabilities = new TestedMedia
{
MediaIsRecognized = true
};
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate();
sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
capabilities.SupportsReadCapacity = true;
capabilities.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) &
0xFFFFFFFF) + 1;
capabilities.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]);
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate();
sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
capabilities.SupportsReadCapacity16 = true;
var temp = new byte[8];
Array.Copy(buffer, 0, temp, 0, 8);
Array.Reverse(temp);
capabilities.Blocks = BitConverter.ToUInt64(temp, 0) + 1;
capabilities.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]);
}
Modes.DecodedMode? decMode = null;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate();
sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, 0x3F,
0x00, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
decMode = Modes.DecodeMode10(buffer, _dev.ScsiType);
capabilities.ModeSense10Data = buffer;
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate();
sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _);
});
if(!sense &&
!_dev.Error)
{
decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType);
capabilities.ModeSense6Data = buffer;
}
if(decMode.HasValue)
{
capabilities.MediumType = (byte)decMode.Value.Header.MediumType;
if(decMode.Value.Header.BlockDescriptors?.Length > 0)
capabilities.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density;
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate();
capabilities.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, capabilities.BlockSize ?? 512,
_dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead6);
capabilities.Read6Data = buffer;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate();
capabilities.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0,
capabilities.BlockSize ?? 512, 0, 1, _dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead10);
capabilities.Read10Data = buffer;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate();
capabilities.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0,
capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead12);
capabilities.Read12Data = buffer;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate();
capabilities.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0,
capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _);
});
AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead16);
capabilities.Read16Data = buffer;
capabilities.LongBlockSize = capabilities.BlockSize;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate();
sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _);
});
if(sense && !_dev.Error)
{
DecodedSense? decSense = Sense.Decode(senseBuffer);
if(decSense is { SenseKey: SenseKeys.IllegalRequest, ASC: 0x24, ASCQ: 0x00 })
{
capabilities.SupportsReadLong = true;
bool valid = decSense.Value.Fixed?.InformationValid == true;
bool ili = decSense.Value.Fixed?.ILI == true;
uint information = decSense.Value.Fixed?.Information ?? 0;
if(decSense.Value.Descriptor.HasValue &&
decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00))
{
valid = true;
ili = true;
information = (uint)Sense.DecodeDescriptor00(desc00);
}
if(valid && ili)
capabilities.LongBlockSize = 0xFFFF - (information & 0xFFFF);
}
}
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask("Trying SCSI READ LONG (16)...").IsIndeterminate();
sense = _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 0xFFFF, _dev.Timeout, out _);
});
if(sense && !_dev.Error)
{
capabilities.SupportsReadLong16 = true;
DecodedSense? decSense = Sense.Decode(senseBuffer);
if(decSense is { SenseKey: SenseKeys.IllegalRequest, ASC: 0x24, ASCQ: 0x00 })
{
capabilities.SupportsReadLong16 = true;
bool valid = decSense.Value.Fixed?.InformationValid == true;
bool ili = decSense.Value.Fixed?.ILI == true;
uint information = decSense.Value.Fixed?.Information ?? 0;
if(decSense.Value.Descriptor.HasValue &&
decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00))
{
valid = true;
ili = true;
information = (uint)Sense.DecodeDescriptor00(desc00);
}
if(valid && ili)
capabilities.LongBlockSize = 0xFFFF - (information & 0xFFFF);
}
}
if(capabilities.SupportsReadLong != true && capabilities.SupportsReadLong16 != true ||
capabilities.LongBlockSize != capabilities.BlockSize)
return capabilities;
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(capabilities.SupportsReadLong16 == true ? "Trying SCSI READ LONG (16)..."
: "Trying SCSI READ LONG (10)...").IsIndeterminate();
switch(capabilities.BlockSize)
{
case 512:
{
foreach(ushort testSize in new ushort[]
{
// Long sector sizes for floppies
514,
// Long sector sizes for SuperDisk
536, 558,
// Long sector sizes for 512-byte magneto-opticals
600, 610, 630
})
{
sense = capabilities.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout,
out _) : _dev.ReadLong10(out buffer, out senseBuffer, false,
false, 0, testSize, _dev.Timeout, out _);
if(sense || _dev.Error)
continue;
capabilities.SupportsReadLong = true;
capabilities.LongBlockSize = testSize;
break;
}
break;
}
case 1024:
{
foreach(ushort testSize in new ushort[]
{
// Long sector sizes for floppies
1026,
// Long sector sizes for 1024-byte magneto-opticals
1200
})
{
sense = capabilities.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout,
out _) : _dev.ReadLong10(out buffer, out senseBuffer, false,
false, 0, testSize, _dev.Timeout, out _);
if(sense || _dev.Error)
continue;
capabilities.SupportsReadLong = true;
capabilities.LongBlockSize = testSize;
break;
}
break;
}
case 2048:
{
sense = capabilities.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 2380, _dev.Timeout, out _)
: _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, _dev.Timeout,
out _);
if(sense || _dev.Error)
return;
capabilities.SupportsReadLong = true;
capabilities.LongBlockSize = 2380;
break;
}
case 4096:
{
sense = capabilities.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 4760, _dev.Timeout, out _)
: _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, _dev.Timeout,
out _);
if(sense || _dev.Error)
return;
capabilities.SupportsReadLong = true;
capabilities.LongBlockSize = 4760;
break;
}
case 8192:
{
sense = capabilities.SupportsReadLong16 == true
? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 9424, _dev.Timeout, out _)
: _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, _dev.Timeout,
out _);
if(sense || _dev.Error)
return;
capabilities.SupportsReadLong = true;
capabilities.LongBlockSize = 9424;
break;
}
}
});
return capabilities;
}
}