mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Introduces constant fields for respective debug module names, replacing the hardcoded ones. This is done to standardize the naming convention, reduce redundancy and potentially avoid any typos or name mismatches across the project. This change makes the code more maintainable and easier to update in case module names need to be changed.
782 lines
30 KiB
C#
782 lines
30 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : Inquiry.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// Component : Common structures for SCSI devices.
|
|
//
|
|
// --[ Description ] ----------------------------------------------------------
|
|
//
|
|
// Defines a high level interpretation of the SCSI INQUIRY response.
|
|
//
|
|
// --[ License ] --------------------------------------------------------------
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2011-2023 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using Aaru.Console;
|
|
|
|
namespace Aaru.CommonTypes.Structs.Devices.SCSI;
|
|
|
|
/// <summary>
|
|
/// Information from the following standards: T9/375-D revision 10l T10/995-D revision 10 T10/1236-D revision 20
|
|
/// T10/1416-D revision 23 T10/1731-D revision 16 T10/502 revision 05 RFC 7144 ECMA-111
|
|
/// </summary>
|
|
[SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "MemberCanBeInternal"),
|
|
SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
|
public struct Inquiry
|
|
{
|
|
const string MODULE_NAME = "SCSI INQUIRY decoder";
|
|
/// <summary>Peripheral qualifier Byte 0, bits 7 to 5</summary>
|
|
public byte PeripheralQualifier;
|
|
/// <summary>Peripheral device type Byte 0, bits 4 to 0</summary>
|
|
public byte PeripheralDeviceType;
|
|
/// <summary>Removable device Byte 1, bit 7</summary>
|
|
public bool RMB;
|
|
/// <summary>SCSI-1 vendor-specific qualification codes Byte 1, bits 6 to 0</summary>
|
|
public byte DeviceTypeModifier;
|
|
/// <summary>ISO/IEC SCSI Standard Version Byte 2, bits 7 to 6, mask = 0xC0, >> 6</summary>
|
|
public byte ISOVersion;
|
|
/// <summary>ECMA SCSI Standard Version Byte 2, bits 5 to 3, mask = 0x38, >> 3</summary>
|
|
public byte ECMAVersion;
|
|
/// <summary>ANSI SCSI Standard Version Byte 2, bits 2 to 0, mask = 0x07</summary>
|
|
public byte ANSIVersion;
|
|
/// <summary>Asynchronous Event Reporting Capability supported Byte 3, bit 7</summary>
|
|
public bool AERC;
|
|
/// <summary>Device supports TERMINATE TASK command Byte 3, bit 6</summary>
|
|
public bool TrmTsk;
|
|
/// <summary>Supports setting Normal ACA Byte 3, bit 5</summary>
|
|
public bool NormACA;
|
|
/// <summary>Supports LUN hierarchical addressing Byte 3, bit 4</summary>
|
|
public bool HiSup;
|
|
/// <summary>Responde data format Byte 3, bit 3 to 0</summary>
|
|
public byte ResponseDataFormat;
|
|
/// <summary>Lenght of total INQUIRY response minus 4 Byte 4</summary>
|
|
public byte AdditionalLength;
|
|
/// <summary>Device contains an embedded storage array controller Byte 5, bit 7</summary>
|
|
public bool SCCS;
|
|
/// <summary>Device contains an Access Control Coordinator Byte 5, bit 6</summary>
|
|
public bool ACC;
|
|
/// <summary>Supports asymetrical logical unit access Byte 5, bits 5 to 4</summary>
|
|
public byte TPGS;
|
|
/// <summary>Supports third-party copy commands Byte 5, bit 3</summary>
|
|
public bool ThreePC;
|
|
/// <summary>Reserved Byte 5, bits 2 to 1</summary>
|
|
public byte Reserved2;
|
|
/// <summary>Supports protection information Byte 5, bit 0</summary>
|
|
public bool Protect;
|
|
/// <summary>Supports basic queueing Byte 6, bit 7</summary>
|
|
public bool BQue;
|
|
/// <summary>Device contains an embedded enclosure services component Byte 6, bit 6</summary>
|
|
public bool EncServ;
|
|
/// <summary>Vendor-specific Byte 6, bit 5</summary>
|
|
public bool VS1;
|
|
/// <summary>Multi-port device Byte 6, bit 4</summary>
|
|
public bool MultiP;
|
|
/// <summary>Device contains or is attached to a medium changer Byte 6, bit 3</summary>
|
|
public bool MChngr;
|
|
/// <summary>Device supports request and acknowledge handshakes Byte 6, bit 2</summary>
|
|
public bool ACKREQQ;
|
|
/// <summary>Supports 32-bit wide SCSI addresses Byte 6, bit 1</summary>
|
|
public bool Addr32;
|
|
/// <summary>Supports 16-bit wide SCSI addresses Byte 6, bit 0</summary>
|
|
public bool Addr16;
|
|
/// <summary>Device supports relative addressing Byte 7, bit 7</summary>
|
|
public bool RelAddr;
|
|
/// <summary>Supports 32-bit wide data transfers Byte 7, bit 6</summary>
|
|
public bool WBus32;
|
|
/// <summary>Supports 16-bit wide data transfers Byte 7, bit 5</summary>
|
|
public bool WBus16;
|
|
/// <summary>Supports synchronous data transfer Byte 7, bit 4</summary>
|
|
public bool Sync;
|
|
/// <summary>Supports linked commands Byte 7, bit 3</summary>
|
|
public bool Linked;
|
|
/// <summary>Supports CONTINUE TASK and TARGET TRANSFER DISABLE commands Byte 7, bit 2</summary>
|
|
public bool TranDis;
|
|
/// <summary>Supports TCQ queue Byte 7, bit 1</summary>
|
|
public bool CmdQue;
|
|
/// <summary>Indicates that the devices responds to RESET with soft reset Byte 7, bit 0</summary>
|
|
public bool SftRe;
|
|
/// <summary>Vendor identification Bytes 8 to 15</summary>
|
|
public byte[] VendorIdentification;
|
|
/// <summary>Product identification Bytes 16 to 31</summary>
|
|
public byte[] ProductIdentification;
|
|
/// <summary>Product revision level Bytes 32 to 35</summary>
|
|
public byte[] ProductRevisionLevel;
|
|
/// <summary>Vendor-specific data Bytes 36 to 55</summary>
|
|
public byte[] VendorSpecific;
|
|
/// <summary>Byte 56, bits 7 to 4</summary>
|
|
public byte Reserved3;
|
|
/// <summary>Supported SPI clocking Byte 56, bits 3 to 2</summary>
|
|
public byte Clocking;
|
|
/// <summary>Device supports Quick Arbitration and Selection Byte 56, bit 1</summary>
|
|
public bool QAS;
|
|
/// <summary>Supports information unit transfers Byte 56, bit 0</summary>
|
|
public bool IUS;
|
|
/// <summary>Reserved Byte 57</summary>
|
|
public byte Reserved4;
|
|
/// <summary>Array of version descriptors Bytes 58 to 73</summary>
|
|
public ushort[] VersionDescriptors;
|
|
/// <summary>Reserved Bytes 74 to 95</summary>
|
|
public byte[] Reserved5;
|
|
/// <summary>Reserved Bytes 96 to end</summary>
|
|
public byte[] VendorSpecific2;
|
|
|
|
// Per DLT4000/DLT4500/DLT4700 Cartridge Tape Subsystem Product Manual
|
|
|
|
#region Quantum vendor unique inquiry data structure
|
|
/// <summary>Means that the INQUIRY response contains 56 bytes or more, so this data has been filled</summary>
|
|
public bool QuantumPresent;
|
|
/// <summary>The product family. Byte 36, bits 7 to 5</summary>
|
|
public byte Qt_ProductFamily;
|
|
/// <summary>The released firmware. Byte 36, bits 4 to 0</summary>
|
|
public byte Qt_ReleasedFirmware;
|
|
/// <summary>The firmware major version. Byte 37</summary>
|
|
public byte Qt_FirmwareMajorVersion;
|
|
/// <summary>The firmware minor version. Byte 38</summary>
|
|
public byte Qt_FirmwareMinorVersion;
|
|
/// <summary>The EEPROM format major version. Byte 39</summary>
|
|
public byte Qt_EEPROMFormatMajorVersion;
|
|
/// <summary>The EEPROM format minor version. Byte 40</summary>
|
|
public byte Qt_EEPROMFormatMinorVersion;
|
|
/// <summary>The firmware personality. Byte 41</summary>
|
|
public byte Qt_FirmwarePersonality;
|
|
/// <summary>The firmware sub personality. Byte 42</summary>
|
|
public byte Qt_FirmwareSubPersonality;
|
|
/// <summary>The tape directory format version. Byte 43</summary>
|
|
public byte Qt_TapeDirectoryFormatVersion;
|
|
/// <summary>The controller hardware version. Byte 44</summary>
|
|
public byte Qt_ControllerHardwareVersion;
|
|
/// <summary>The drive EEPROM version. Byte 45</summary>
|
|
public byte Qt_DriveEEPROMVersion;
|
|
/// <summary>The drive hardware version. Byte 46</summary>
|
|
public byte Qt_DriveHardwareVersion;
|
|
/// <summary>The media loader firmware version. Byte 47</summary>
|
|
public byte Qt_MediaLoaderFirmwareVersion;
|
|
/// <summary>The media loader hardware version. Byte 48</summary>
|
|
public byte Qt_MediaLoaderHardwareVersion;
|
|
/// <summary>The media loader mechanical version. Byte 49</summary>
|
|
public byte Qt_MediaLoaderMechanicalVersion;
|
|
/// <summary>Is a media loader present? Byte 50</summary>
|
|
public bool Qt_MediaLoaderPresent;
|
|
/// <summary>Is a library present? Byte 51</summary>
|
|
public bool Qt_LibraryPresent;
|
|
/// <summary>The module revision. Bytes 52 to 55</summary>
|
|
public byte[] Qt_ModuleRevision;
|
|
#endregion Quantum vendor unique inquiry data structure
|
|
|
|
#region IBM vendor unique inquiry data structure
|
|
/// <summary>Means that the INQUIRY response contains 56 bytes or more, so this data has been filled</summary>
|
|
public bool IBMPresent;
|
|
/// <summary>Drive is not capable of automation Byte 36 bit 0</summary>
|
|
public bool IBM_AutDis;
|
|
/// <summary>If not zero, limit in MB/s = Max * (this / 256) Byte 37</summary>
|
|
public byte IBM_PerformanceLimit;
|
|
/// <summary>Byte 41</summary>
|
|
public byte IBM_OEMSpecific;
|
|
#endregion IBM vendor unique inquiry data structure
|
|
|
|
#region HP vendor unique inquiry data structure
|
|
/// <summary>Means that the INQUIRY response contains 49 bytes or more, so this data has been filled</summary>
|
|
public bool HPPresent;
|
|
/// <summary>WORM version Byte 40 bits 7 to 1</summary>
|
|
public byte HP_WORMVersion;
|
|
/// <summary>WORM supported Byte 40 bit 0</summary>
|
|
public bool HP_WORM;
|
|
/// <summary>Bytes 43 to 48</summary>
|
|
public byte[] HP_OBDR;
|
|
#endregion HP vendor unique inquiry data structure
|
|
|
|
#region Seagate vendor unique inquiry data structure
|
|
/// <summary>Means that bytes 36 to 43 are filled</summary>
|
|
public bool SeagatePresent;
|
|
/// <summary>Drive Serial Number Bytes 36 to 43</summary>
|
|
public byte[] Seagate_DriveSerialNumber;
|
|
/// <summary>Means that bytes 96 to 143 are filled</summary>
|
|
public bool Seagate2Present;
|
|
/// <summary>Contains Seagate copyright notice Bytes 96 to 143</summary>
|
|
public byte[] Seagate_Copyright;
|
|
/// <summary>Means that bytes 144 to 147 are filled</summary>
|
|
public bool Seagate3Present;
|
|
/// <summary>Reserved Seagate field Bytes 144 to 147</summary>
|
|
public byte[] Seagate_ServoPROMPartNo;
|
|
#endregion Seagate vendor unique inquiry data structure
|
|
|
|
#region Kreon vendor unique inquiry data structure
|
|
/// <summary>Means that firmware is Kreon</summary>
|
|
public bool KreonPresent;
|
|
/// <summary>Kreon identifier Bytes 36 to 40</summary>
|
|
public byte[] KreonIdentifier;
|
|
/// <summary>Kreon just a 0x20 Bytes 41</summary>
|
|
public byte KreonSpace;
|
|
/// <summary>Kreon version string Bytes 42 to 46</summary>
|
|
public byte[] KreonVersion;
|
|
#endregion Kreon vendor unique inquiry data structure
|
|
|
|
#region Sony Hi-MD data
|
|
/// <summary>Set if Hi-MD signature is present</summary>
|
|
public bool IsHiMD;
|
|
/// <summary>Hi-MD signature, bytes 36 to 44</summary>
|
|
public byte[] HiMDSignature;
|
|
/// <summary>Unknown data, bytes 44 to 55</summary>
|
|
public byte[] HiMDSpecific;
|
|
#endregion Sony Hi-MD data
|
|
|
|
static readonly byte[] HiMDSignatureContents = "Hi-MD "u8.ToArray();
|
|
|
|
/// <summary>Decodes a SCSI INQUIRY response</summary>
|
|
/// <param name="SCSIInquiryResponse">INQUIRY raw response data</param>
|
|
/// <returns>Decoded SCSI INQUIRY</returns>
|
|
#region Public methods
|
|
public static Inquiry? Decode(byte[] SCSIInquiryResponse)
|
|
{
|
|
if(SCSIInquiryResponse == null)
|
|
return null;
|
|
|
|
if(SCSIInquiryResponse.Length < 36 &&
|
|
SCSIInquiryResponse.Length != 5)
|
|
{
|
|
AaruConsole.DebugWriteLine(MODULE_NAME,
|
|
Localization.INQUIRY_response_is_0_bytes_less_than_minimum_of_36_bytes,
|
|
SCSIInquiryResponse.Length);
|
|
|
|
return null;
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length < SCSIInquiryResponse[4] + 4 &&
|
|
SCSIInquiryResponse.Length != SCSIInquiryResponse[4])
|
|
{
|
|
AaruConsole.DebugWriteLine(MODULE_NAME,
|
|
Localization.
|
|
INQUIRY_response_length_0_bytes_is_different_than_specified_in_length_field,
|
|
SCSIInquiryResponse.Length, SCSIInquiryResponse[4] + 4);
|
|
|
|
return null;
|
|
}
|
|
|
|
var decoded = new Inquiry();
|
|
|
|
if(SCSIInquiryResponse.Length >= 1)
|
|
{
|
|
decoded.PeripheralQualifier = (byte)((SCSIInquiryResponse[0] & 0xE0) >> 5);
|
|
decoded.PeripheralDeviceType = (byte)(SCSIInquiryResponse[0] & 0x1F);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 2)
|
|
{
|
|
decoded.RMB = Convert.ToBoolean(SCSIInquiryResponse[1] & 0x80);
|
|
decoded.DeviceTypeModifier = (byte)(SCSIInquiryResponse[1] & 0x7F);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 3)
|
|
{
|
|
decoded.ISOVersion = (byte)((SCSIInquiryResponse[2] & 0xC0) >> 6);
|
|
decoded.ECMAVersion = (byte)((SCSIInquiryResponse[2] & 0x38) >> 3);
|
|
decoded.ANSIVersion = (byte)(SCSIInquiryResponse[2] & 0x07);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 4)
|
|
{
|
|
decoded.AERC = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x80);
|
|
decoded.TrmTsk = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x40);
|
|
decoded.NormACA = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x20);
|
|
decoded.HiSup = Convert.ToBoolean(SCSIInquiryResponse[3] & 0x10);
|
|
decoded.ResponseDataFormat = (byte)(SCSIInquiryResponse[3] & 0x07);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 5)
|
|
decoded.AdditionalLength = SCSIInquiryResponse[4];
|
|
|
|
if(SCSIInquiryResponse.Length >= 6)
|
|
{
|
|
decoded.SCCS = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x80);
|
|
decoded.ACC = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x40);
|
|
decoded.TPGS = (byte)((SCSIInquiryResponse[5] & 0x30) >> 4);
|
|
decoded.ThreePC = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x08);
|
|
decoded.Reserved2 = (byte)((SCSIInquiryResponse[5] & 0x06) >> 1);
|
|
decoded.Protect = Convert.ToBoolean(SCSIInquiryResponse[5] & 0x01);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 7)
|
|
{
|
|
decoded.BQue = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x80);
|
|
decoded.EncServ = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x40);
|
|
decoded.VS1 = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x20);
|
|
decoded.MultiP = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x10);
|
|
decoded.MChngr = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x08);
|
|
decoded.ACKREQQ = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x04);
|
|
decoded.Addr32 = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x02);
|
|
decoded.Addr16 = Convert.ToBoolean(SCSIInquiryResponse[6] & 0x01);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 8)
|
|
{
|
|
decoded.RelAddr = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x80);
|
|
decoded.WBus32 = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x40);
|
|
decoded.WBus16 = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x20);
|
|
decoded.Sync = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x10);
|
|
decoded.Linked = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x08);
|
|
decoded.TranDis = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x04);
|
|
decoded.CmdQue = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x02);
|
|
decoded.SftRe = Convert.ToBoolean(SCSIInquiryResponse[7] & 0x01);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 16)
|
|
{
|
|
decoded.VendorIdentification = new byte[8];
|
|
Array.Copy(SCSIInquiryResponse, 8, decoded.VendorIdentification, 0, 8);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 32)
|
|
{
|
|
decoded.ProductIdentification = new byte[16];
|
|
Array.Copy(SCSIInquiryResponse, 16, decoded.ProductIdentification, 0, 16);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 36)
|
|
{
|
|
decoded.ProductRevisionLevel = new byte[4];
|
|
Array.Copy(SCSIInquiryResponse, 32, decoded.ProductRevisionLevel, 0, 4);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 44)
|
|
{
|
|
// Seagate 1
|
|
decoded.SeagatePresent = true;
|
|
decoded.Seagate_DriveSerialNumber = new byte[8];
|
|
Array.Copy(SCSIInquiryResponse, 36, decoded.Seagate_DriveSerialNumber, 0, 8);
|
|
|
|
// Hi-MD
|
|
decoded.HiMDSignature = new byte[8];
|
|
Array.Copy(SCSIInquiryResponse, 36, decoded.HiMDSignature, 0, 8);
|
|
decoded.IsHiMD = HiMDSignatureContents.SequenceEqual(decoded.HiMDSignature);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 46)
|
|
{
|
|
// Kreon
|
|
decoded.KreonIdentifier = new byte[5];
|
|
Array.Copy(SCSIInquiryResponse, 36, decoded.KreonIdentifier, 0, 5);
|
|
decoded.KreonSpace = SCSIInquiryResponse[41];
|
|
decoded.KreonVersion = new byte[5];
|
|
Array.Copy(SCSIInquiryResponse, 42, decoded.KreonVersion, 0, 5);
|
|
|
|
if(decoded.KreonSpace == 0x20 &&
|
|
decoded.KreonIdentifier.SequenceEqual("KREON"u8.ToArray()))
|
|
decoded.KreonPresent = true;
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 49)
|
|
{
|
|
// HP
|
|
decoded.HPPresent = true;
|
|
decoded.HP_WORM |= (SCSIInquiryResponse[40] & 0x01) == 0x01;
|
|
decoded.HP_WORMVersion = (byte)((SCSIInquiryResponse[40] & 0x7F) >> 1);
|
|
decoded.HP_OBDR = new byte[6];
|
|
Array.Copy(SCSIInquiryResponse, 43, decoded.HP_OBDR, 0, 6);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 56)
|
|
{
|
|
if(decoded.IsHiMD)
|
|
{
|
|
decoded.HiMDSpecific = new byte[12];
|
|
Array.Copy(SCSIInquiryResponse, 44, decoded.HiMDSpecific, 0, 12);
|
|
}
|
|
else
|
|
{
|
|
decoded.VendorSpecific = new byte[20];
|
|
Array.Copy(SCSIInquiryResponse, 36, decoded.VendorSpecific, 0, 20);
|
|
}
|
|
|
|
// Quantum
|
|
decoded.QuantumPresent = true;
|
|
decoded.Qt_ProductFamily = (byte)((SCSIInquiryResponse[36] & 0xF0) >> 4);
|
|
decoded.Qt_ReleasedFirmware = (byte)(SCSIInquiryResponse[36] & 0x0F);
|
|
decoded.Qt_FirmwareMajorVersion = SCSIInquiryResponse[37];
|
|
decoded.Qt_FirmwareMinorVersion = SCSIInquiryResponse[38];
|
|
decoded.Qt_EEPROMFormatMajorVersion = SCSIInquiryResponse[39];
|
|
decoded.Qt_EEPROMFormatMinorVersion = SCSIInquiryResponse[40];
|
|
decoded.Qt_FirmwarePersonality = SCSIInquiryResponse[41];
|
|
decoded.Qt_FirmwareSubPersonality = SCSIInquiryResponse[42];
|
|
decoded.Qt_TapeDirectoryFormatVersion = SCSIInquiryResponse[43];
|
|
decoded.Qt_ControllerHardwareVersion = SCSIInquiryResponse[44];
|
|
decoded.Qt_DriveEEPROMVersion = SCSIInquiryResponse[45];
|
|
decoded.Qt_DriveHardwareVersion = SCSIInquiryResponse[46];
|
|
decoded.Qt_MediaLoaderFirmwareVersion = SCSIInquiryResponse[47];
|
|
decoded.Qt_MediaLoaderHardwareVersion = SCSIInquiryResponse[48];
|
|
decoded.Qt_MediaLoaderMechanicalVersion = SCSIInquiryResponse[49];
|
|
decoded.Qt_MediaLoaderPresent = SCSIInquiryResponse[50] > 0;
|
|
decoded.Qt_LibraryPresent = SCSIInquiryResponse[51] > 0;
|
|
decoded.Qt_ModuleRevision = new byte[4];
|
|
Array.Copy(SCSIInquiryResponse, 52, decoded.Qt_ModuleRevision, 0, 4);
|
|
|
|
// IBM
|
|
decoded.IBMPresent = true;
|
|
decoded.IBM_AutDis |= (SCSIInquiryResponse[36] & 0x01) == 0x01;
|
|
decoded.IBM_PerformanceLimit = SCSIInquiryResponse[37];
|
|
decoded.IBM_OEMSpecific = SCSIInquiryResponse[41];
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 57)
|
|
{
|
|
decoded.Reserved3 = (byte)((SCSIInquiryResponse[56] & 0xF0) >> 4);
|
|
decoded.Clocking = (byte)((SCSIInquiryResponse[56] & 0x0C) >> 2);
|
|
decoded.QAS = Convert.ToBoolean(SCSIInquiryResponse[56] & 0x02);
|
|
decoded.IUS = Convert.ToBoolean(SCSIInquiryResponse[56] & 0x01);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 58)
|
|
decoded.Reserved4 = SCSIInquiryResponse[57];
|
|
|
|
if(SCSIInquiryResponse.Length >= 60)
|
|
{
|
|
int descriptorsNo;
|
|
|
|
if(SCSIInquiryResponse.Length >= 74)
|
|
descriptorsNo = 8;
|
|
else
|
|
descriptorsNo = (SCSIInquiryResponse.Length - 58) / 2;
|
|
|
|
decoded.VersionDescriptors = new ushort[descriptorsNo];
|
|
|
|
for(int i = 0; i < descriptorsNo; i++)
|
|
decoded.VersionDescriptors[i] = BitConverter.ToUInt16(SCSIInquiryResponse, 58 + (i * 2));
|
|
}
|
|
|
|
switch(SCSIInquiryResponse.Length)
|
|
{
|
|
case >= 75 and < 96:
|
|
decoded.Reserved5 = new byte[SCSIInquiryResponse.Length - 74];
|
|
Array.Copy(SCSIInquiryResponse, 74, decoded.Reserved5, 0, SCSIInquiryResponse.Length - 74);
|
|
|
|
break;
|
|
case >= 96:
|
|
decoded.Reserved5 = new byte[22];
|
|
Array.Copy(SCSIInquiryResponse, 74, decoded.Reserved5, 0, 22);
|
|
|
|
break;
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length > 96)
|
|
{
|
|
decoded.VendorSpecific2 = new byte[SCSIInquiryResponse.Length - 96];
|
|
Array.Copy(SCSIInquiryResponse, 96, decoded.VendorSpecific2, 0, SCSIInquiryResponse.Length - 96);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length >= 144)
|
|
{
|
|
// Seagate 2
|
|
decoded.Seagate2Present = true;
|
|
decoded.Seagate_Copyright = new byte[48];
|
|
Array.Copy(SCSIInquiryResponse, 96, decoded.Seagate_Copyright, 0, 48);
|
|
}
|
|
|
|
if(SCSIInquiryResponse.Length < 148)
|
|
return decoded;
|
|
|
|
// Seagate 2
|
|
decoded.Seagate3Present = true;
|
|
decoded.Seagate_ServoPROMPartNo = new byte[4];
|
|
Array.Copy(SCSIInquiryResponse, 144, decoded.Seagate_ServoPROMPartNo, 0, 4);
|
|
|
|
return decoded;
|
|
}
|
|
|
|
/// <summary>Encodes a SCSI INQUIRY response</summary>
|
|
/// <param name="inq">Decoded SCSI INQUIRY</param>
|
|
/// <returns>Raw SCSI INQUIRY response</returns>
|
|
public static byte[] Encode(Inquiry? inq)
|
|
{
|
|
if(inq is null)
|
|
return null;
|
|
|
|
Inquiry decoded = inq.Value;
|
|
|
|
byte[] buffer = new byte[512];
|
|
byte length = 0;
|
|
|
|
buffer[0] = (byte)(decoded.PeripheralQualifier << 5);
|
|
buffer[0] += decoded.PeripheralDeviceType;
|
|
|
|
if(decoded.RMB)
|
|
buffer[1] = 0x80;
|
|
|
|
buffer[1] += decoded.DeviceTypeModifier;
|
|
|
|
buffer[2] = (byte)(decoded.ISOVersion << 6);
|
|
buffer[2] += (byte)(decoded.ECMAVersion << 3);
|
|
buffer[2] += decoded.ANSIVersion;
|
|
|
|
if(decoded.AERC)
|
|
buffer[3] = 0x80;
|
|
|
|
if(decoded.TrmTsk)
|
|
buffer[3] += 0x40;
|
|
|
|
if(decoded.NormACA)
|
|
buffer[3] += 0x20;
|
|
|
|
if(decoded.HiSup)
|
|
buffer[3] += 0x10;
|
|
|
|
buffer[3] += decoded.ResponseDataFormat;
|
|
|
|
if(decoded.AdditionalLength > 0)
|
|
{
|
|
length = 5;
|
|
buffer[4] = decoded.AdditionalLength;
|
|
}
|
|
|
|
if(decoded.SCCS ||
|
|
decoded.ACC ||
|
|
decoded.TPGS > 0 ||
|
|
decoded.ThreePC ||
|
|
decoded.Reserved2 > 0 ||
|
|
decoded.Protect)
|
|
{
|
|
length = 6;
|
|
|
|
if(decoded.SCCS)
|
|
buffer[5] = 0x80;
|
|
|
|
if(decoded.ACC)
|
|
buffer[5] += 0x40;
|
|
|
|
buffer[5] += (byte)(decoded.TPGS << 4);
|
|
|
|
if(decoded.ThreePC)
|
|
buffer[5] += 0x08;
|
|
|
|
buffer[5] += (byte)(decoded.Reserved2 << 1);
|
|
|
|
if(decoded.Protect)
|
|
buffer[5] += 0x01;
|
|
}
|
|
|
|
if(decoded.BQue ||
|
|
decoded.EncServ ||
|
|
decoded.VS1 ||
|
|
decoded.MultiP ||
|
|
decoded.MChngr ||
|
|
decoded.ACKREQQ ||
|
|
decoded.Addr32 ||
|
|
decoded.Addr16)
|
|
{
|
|
length = 7;
|
|
|
|
if(decoded.BQue)
|
|
buffer[6] = 0x80;
|
|
|
|
if(decoded.EncServ)
|
|
buffer[6] += 0x40;
|
|
|
|
if(decoded.VS1)
|
|
buffer[6] += 0x20;
|
|
|
|
if(decoded.MultiP)
|
|
buffer[6] += 0x10;
|
|
|
|
if(decoded.MChngr)
|
|
buffer[6] += 0x08;
|
|
|
|
if(decoded.ACKREQQ)
|
|
buffer[6] += 0x04;
|
|
|
|
if(decoded.Addr32)
|
|
buffer[6] += 0x02;
|
|
|
|
if(decoded.Addr16)
|
|
buffer[6] += 0x01;
|
|
}
|
|
|
|
if(decoded.RelAddr ||
|
|
decoded.WBus32 ||
|
|
decoded.WBus16 ||
|
|
decoded.Sync ||
|
|
decoded.Linked ||
|
|
decoded.TranDis ||
|
|
decoded.CmdQue ||
|
|
decoded.SftRe)
|
|
|
|
{
|
|
length = 8;
|
|
|
|
if(decoded.RelAddr)
|
|
buffer[7] = 0x80;
|
|
|
|
if(decoded.WBus32)
|
|
buffer[7] += 0x40;
|
|
|
|
if(decoded.WBus16)
|
|
buffer[7] += 0x20;
|
|
|
|
if(decoded.Sync)
|
|
buffer[7] += 0x10;
|
|
|
|
if(decoded.Linked)
|
|
buffer[7] += 0x08;
|
|
|
|
if(decoded.TranDis)
|
|
buffer[7] += 0x04;
|
|
|
|
if(decoded.CmdQue)
|
|
buffer[7] += 0x02;
|
|
|
|
if(decoded.SftRe)
|
|
buffer[7] += 0x01;
|
|
}
|
|
|
|
if(decoded.VendorIdentification != null)
|
|
{
|
|
length = 16;
|
|
|
|
Array.Copy(decoded.VendorIdentification, 0, buffer, 8,
|
|
decoded.VendorIdentification.Length >= 8 ? 8 : decoded.VendorIdentification.Length);
|
|
}
|
|
|
|
if(decoded.ProductIdentification != null)
|
|
{
|
|
length = 32;
|
|
|
|
Array.Copy(decoded.ProductIdentification, 0, buffer, 16,
|
|
decoded.ProductIdentification.Length >= 16 ? 16 : decoded.ProductIdentification.Length);
|
|
}
|
|
|
|
if(decoded.ProductRevisionLevel != null)
|
|
{
|
|
length = 36;
|
|
|
|
Array.Copy(decoded.ProductRevisionLevel, 0, buffer, 32,
|
|
decoded.ProductRevisionLevel.Length >= 4 ? 4 : decoded.ProductRevisionLevel.Length);
|
|
}
|
|
|
|
if(decoded.Seagate_DriveSerialNumber != null)
|
|
{
|
|
length = 44;
|
|
Array.Copy(decoded.Seagate_DriveSerialNumber, 0, buffer, 36, 8);
|
|
}
|
|
|
|
if(decoded is { KreonIdentifier: {}, KreonVersion: {} })
|
|
{
|
|
length = 46;
|
|
Array.Copy(decoded.KreonIdentifier, 0, buffer, 36, 5);
|
|
buffer[41] = decoded.KreonSpace;
|
|
Array.Copy(decoded.KreonVersion, 0, buffer, 42, 5);
|
|
}
|
|
|
|
if(decoded.HP_WORM ||
|
|
decoded.HP_WORMVersion > 0 ||
|
|
decoded.HP_OBDR != null)
|
|
{
|
|
length = 49;
|
|
|
|
if(decoded.HP_WORM)
|
|
buffer[40] = 0x01;
|
|
|
|
buffer[40] += (byte)(decoded.HP_WORMVersion << 1);
|
|
Array.Copy(decoded.HP_OBDR, 0, buffer, 43, 6);
|
|
}
|
|
|
|
if(decoded.IsHiMD)
|
|
{
|
|
length = 56;
|
|
Array.Copy(HiMDSignatureContents, 0, buffer, 36, 8);
|
|
|
|
if(decoded.HiMDSpecific != null)
|
|
Array.Copy(decoded.HiMDSpecific, 0, buffer, 44, 12);
|
|
}
|
|
|
|
if(decoded is { VendorSpecific: {}, IsHiMD: false })
|
|
{
|
|
length = 56;
|
|
Array.Copy(decoded.VendorSpecific, 0, buffer, 36, 20);
|
|
}
|
|
|
|
if(decoded.Reserved3 > 0 ||
|
|
decoded.Clocking > 0 ||
|
|
decoded.QAS ||
|
|
decoded.IUS)
|
|
{
|
|
length = 57;
|
|
buffer[56] = (byte)(decoded.Reserved3 << 4);
|
|
buffer[56] += (byte)(decoded.Clocking << 2);
|
|
|
|
if(decoded.QAS)
|
|
buffer[56] += 0x02;
|
|
|
|
if(decoded.IUS)
|
|
buffer[56] += 0x01;
|
|
}
|
|
|
|
if(decoded.Reserved4 != 0)
|
|
{
|
|
length = 58;
|
|
buffer[57] = decoded.Reserved4;
|
|
}
|
|
|
|
if(decoded.VersionDescriptors != null)
|
|
{
|
|
length = (byte)(58 + (decoded.VersionDescriptors.Length * 2));
|
|
|
|
for(int i = 0; i < decoded.VersionDescriptors.Length; i++)
|
|
Array.Copy(BitConverter.GetBytes(decoded.VersionDescriptors[i]), 0, buffer, 56 + (i * 2), 2);
|
|
}
|
|
|
|
if(decoded.Reserved5 != null)
|
|
{
|
|
length = (byte)(74 + decoded.Reserved5.Length);
|
|
Array.Copy(decoded.Reserved5, 0, buffer, 74, decoded.Reserved5.Length);
|
|
}
|
|
|
|
if(decoded.VendorSpecific2 != null)
|
|
{
|
|
length = (byte)(96 + decoded.VendorSpecific2.Length);
|
|
Array.Copy(decoded.VendorSpecific2, 0, buffer, 96, decoded.VendorSpecific2.Length);
|
|
}
|
|
|
|
if(decoded.Seagate_Copyright != null)
|
|
{
|
|
length = 144;
|
|
Array.Copy(decoded.Seagate_Copyright, 0, buffer, 96, 48);
|
|
}
|
|
|
|
if(decoded.Seagate_ServoPROMPartNo != null)
|
|
{
|
|
length = 148;
|
|
Array.Copy(decoded.Seagate_ServoPROMPartNo, 0, buffer, 144, 4);
|
|
}
|
|
|
|
buffer[4] = length;
|
|
byte[] dest = new byte[length];
|
|
Array.Copy(buffer, 0, dest, 0, length);
|
|
|
|
return dest;
|
|
}
|
|
#endregion Public methods
|
|
} |