2017-05-19 20:28:49 +01:00
|
|
|
// /***************************************************************************
|
2015-10-19 04:32:16 +01:00
|
|
|
// The Disc Image Chef
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Filename : EVPD.cs
|
2016-07-28 18:13:49 +01:00
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
2015-10-19 04:32:16 +01:00
|
|
|
//
|
2016-07-28 18:13:49 +01:00
|
|
|
// Component : Device structures decoders.
|
2015-10-19 04:32:16 +01:00
|
|
|
//
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
//
|
2016-07-28 18:13:49 +01:00
|
|
|
// Decodes SCSI EVPDs.
|
2015-10-19 04:32:16 +01:00
|
|
|
//
|
|
|
|
|
// --[ License ] --------------------------------------------------------------
|
|
|
|
|
//
|
2016-07-28 18:13:49 +01:00
|
|
|
// This library is free software; you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Lesser General Public License as
|
|
|
|
|
// published by the Free Software Foundation; either version 2.1 of the
|
2015-10-19 04:32:16 +01:00
|
|
|
// License, or (at your option) any later version.
|
|
|
|
|
//
|
2016-07-28 18:13:49 +01:00
|
|
|
// This library 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
|
|
|
|
|
// Lesser General Public License for more details.
|
2015-10-19 04:32:16 +01:00
|
|
|
//
|
2016-07-28 18:13:49 +01:00
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2015-10-19 04:32:16 +01:00
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2017-12-19 03:50:57 +00:00
|
|
|
// Copyright © 2011-2018 Natalia Portillo
|
2015-10-19 04:32:16 +01:00
|
|
|
// ****************************************************************************/
|
2016-07-28 18:13:49 +01:00
|
|
|
|
2015-10-19 04:32:16 +01:00
|
|
|
using System;
|
2016-10-13 04:55:00 +01:00
|
|
|
using System.Collections.Generic;
|
2016-10-13 20:28:08 +01:00
|
|
|
using System.Linq;
|
2016-10-16 07:29:19 +01:00
|
|
|
using System.Text;
|
2016-10-14 02:20:00 +01:00
|
|
|
using System.Text.RegularExpressions;
|
2017-12-21 14:30:38 +00:00
|
|
|
using DiscImageChef.Decoders.ATA;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
|
|
|
|
namespace DiscImageChef.Decoders.SCSI
|
|
|
|
|
{
|
|
|
|
|
public static class EVPD
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Decodes VPD page 0x00: Supported VPD pages
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>A byte array containing all supported VPD pages.</returns>
|
|
|
|
|
/// <param name="page">Page 0x00.</param>
|
|
|
|
|
public static byte[] DecodePage00(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return null;
|
2016-04-19 02:11:47 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] != 0) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != page[3] + 4) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
|
|
|
|
byte[] decoded = new byte[page.Length - 4];
|
|
|
|
|
|
|
|
|
|
Array.Copy(page, 4, decoded, 0, page.Length - 4);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Decides VPD pages 0x01 to 0x7F: ASCII Information
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>An ASCII string with the contents of the page.</returns>
|
|
|
|
|
/// <param name="page">Page 0x01-0x7F.</param>
|
|
|
|
|
public static string DecodeASCIIPage(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] == 0 || page[1] > 0x7F) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != page[3] + 4) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
|
|
|
|
byte[] ascii = new byte[page[4]];
|
|
|
|
|
|
|
|
|
|
Array.Copy(page, 5, ascii, 0, page[4]);
|
|
|
|
|
|
|
|
|
|
return StringHandlers.CToString(ascii);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Decodes VPD page 0x80: Unit Serial Number
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The unit serial number.</returns>
|
|
|
|
|
/// <param name="page">Page 0x80.</param>
|
|
|
|
|
public static string DecodePage80(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] != 0x80) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != page[3] + 4) return null;
|
2015-10-19 04:32:16 +01:00
|
|
|
|
|
|
|
|
byte[] ascii = new byte[page.Length - 4];
|
|
|
|
|
|
|
|
|
|
Array.Copy(page, 4, ascii, 0, page.Length - 4);
|
|
|
|
|
|
|
|
|
|
return StringHandlers.CToString(ascii);
|
|
|
|
|
}
|
2016-10-13 04:01:39 +01:00
|
|
|
|
2016-10-13 06:18:12 +01:00
|
|
|
#region EVPD Page 0x81: Implemented operating definition page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Implemented operating definition page
|
|
|
|
|
/// Page code 0x81
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_81
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Current operating definition
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ScsiDefinitions Current;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Default operating definition
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ScsiDefinitions Default;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Support operating definition list
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ScsiDefinitions[] Supported;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_81? DecodePage_81(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 06:18:12 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0x81) return null;
|
2016-10-13 06:18:12 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] + 4 != pageResponse.Length) return null;
|
2016-10-13 06:18:12 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 6) return null;
|
2016-10-13 06:18:12 +01:00
|
|
|
|
|
|
|
|
Page_81 decoded = new Page_81();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
decoded.Current = (ScsiDefinitions)(pageResponse[4] & 0x7F);
|
|
|
|
|
decoded.Default = (ScsiDefinitions)(pageResponse[5] & 0x7F);
|
|
|
|
|
|
|
|
|
|
int position = 6;
|
|
|
|
|
List<ScsiDefinitions> definitions = new List<ScsiDefinitions>();
|
|
|
|
|
|
|
|
|
|
while(position < pageResponse.Length)
|
|
|
|
|
{
|
|
|
|
|
ScsiDefinitions definition = (ScsiDefinitions)(pageResponse[position] & 0x7F);
|
|
|
|
|
position++;
|
|
|
|
|
definitions.Add(definition);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decoded.Supported = definitions.ToArray();
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_81(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_81(DecodePage_81(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string DefinitionToString(ScsiDefinitions definition)
|
|
|
|
|
{
|
|
|
|
|
switch(definition)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
case ScsiDefinitions.Current: return "";
|
|
|
|
|
case ScsiDefinitions.CCS: return "CCS";
|
|
|
|
|
case ScsiDefinitions.SCSI1: return "SCSI-1";
|
|
|
|
|
case ScsiDefinitions.SCSI2: return "SCSI-2";
|
|
|
|
|
case ScsiDefinitions.SCSI3: return "SCSI-3";
|
2017-12-21 17:58:51 +00:00
|
|
|
default: return $"Unknown definition code {(byte)definition}";
|
2016-10-13 06:18:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_81(Page_81? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 06:18:12 +01:00
|
|
|
|
|
|
|
|
Page_81 page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("SCSI Implemented operating definitions:");
|
|
|
|
|
|
|
|
|
|
sb.AppendFormat("\tDefault operating definition: {0}", DefinitionToString(page.Current)).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tCurrent operating definition: {0}", DefinitionToString(page.Current)).AppendLine();
|
|
|
|
|
|
|
|
|
|
if(page.Supported.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
sb.AppendLine("\tThere are no supported definitions");
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("\tSupported operating definitions:");
|
|
|
|
|
foreach(ScsiDefinitions definition in page.Supported)
|
|
|
|
|
sb.AppendFormat("\t\t{0}", DefinitionToString(definition)).AppendLine();
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0x81: Implemented operating definition page
|
|
|
|
|
|
2016-10-13 04:01:39 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Decodes VPD page 0x82: ASCII implemented operating definition
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>ASCII implemented operating definition.</returns>
|
|
|
|
|
/// <param name="page">Page 0x82.</param>
|
|
|
|
|
public static string DecodePage82(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return null;
|
2016-10-13 04:01:39 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] != 0x82) return null;
|
2016-10-13 04:01:39 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != page[3] + 4) return null;
|
2016-10-13 04:01:39 +01:00
|
|
|
|
|
|
|
|
byte[] ascii = new byte[page.Length - 4];
|
|
|
|
|
|
|
|
|
|
Array.Copy(page, 4, ascii, 0, page.Length - 4);
|
|
|
|
|
|
|
|
|
|
return StringHandlers.CToString(ascii);
|
|
|
|
|
}
|
2016-10-13 04:55:00 +01:00
|
|
|
|
|
|
|
|
#region EVPD Page 0x83: Device identification page
|
2016-10-13 05:49:11 +01:00
|
|
|
public enum IdentificationAssociation : byte
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier field is associated with the addressed logical unit
|
|
|
|
|
/// </summary>
|
|
|
|
|
LogicalUnit = 0,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier field is associated with the target port
|
|
|
|
|
/// </summary>
|
|
|
|
|
TargetPort = 1,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier field is associated with the target device that contains the LUN
|
|
|
|
|
/// </summary>
|
|
|
|
|
TargetDevice = 2
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-13 04:55:00 +01:00
|
|
|
public enum IdentificationCodeSet : byte
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier is binary
|
|
|
|
|
/// </summary>
|
|
|
|
|
Binary = 1,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier is pure ASCII
|
|
|
|
|
/// </summary>
|
2016-10-13 05:49:11 +01:00
|
|
|
ASCII = 2,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier is in UTF-8
|
|
|
|
|
/// </summary>
|
|
|
|
|
UTF8 = 3
|
2016-10-13 04:55:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum IdentificationTypes : byte
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// No assignment authority was used and there is no guarantee the identifier is unique
|
|
|
|
|
/// </summary>
|
|
|
|
|
NoAuthority = 0,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Concatenates vendor and product identifier from INQUIRY plus unit serial number from page 80h
|
|
|
|
|
/// </summary>
|
|
|
|
|
Inquiry = 1,
|
|
|
|
|
/// <summary>
|
2016-10-13 05:49:11 +01:00
|
|
|
/// Identifier is a 64-bit IEEE EUI-64, or extended
|
2016-10-13 04:55:00 +01:00
|
|
|
/// </summary>
|
|
|
|
|
EUI = 2,
|
|
|
|
|
/// <summary>
|
2016-10-13 05:49:11 +01:00
|
|
|
/// Identifier is compatible with 64-bit FC-PH Name_Identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
NAA = 3,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier to relative port in device
|
2016-10-13 04:55:00 +01:00
|
|
|
/// </summary>
|
2016-10-13 05:49:11 +01:00
|
|
|
Relative = 4,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier to group of target ports in device
|
|
|
|
|
/// </summary>
|
|
|
|
|
TargetPortGroup = 5,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifier to group of target LUNs in device
|
|
|
|
|
/// </summary>
|
|
|
|
|
LogicalUnitGroup = 6,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MD5 of device identification values
|
|
|
|
|
/// </summary>
|
|
|
|
|
MD5 = 7,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SCSI name string
|
|
|
|
|
/// </summary>
|
|
|
|
|
SCSI = 8,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Protocol specific port identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
ProtocolSpecific = 9
|
2016-10-13 04:55:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct IdentificatonDescriptor
|
|
|
|
|
{
|
2016-10-13 05:49:11 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Protocol identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ProtocolIdentifiers ProtocolIdentifier;
|
2016-10-13 04:55:00 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Defines how the identifier is stored
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IdentificationCodeSet CodeSet;
|
|
|
|
|
/// <summary>
|
2016-10-13 05:49:11 +01:00
|
|
|
/// Set if protocol identifier is valid
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool PIV;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifies which decide the identifier associates with
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IdentificationAssociation Association;
|
|
|
|
|
/// <summary>
|
2016-10-13 04:55:00 +01:00
|
|
|
/// Defines the type of the identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IdentificationTypes Type;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Length of the identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte Length;
|
|
|
|
|
/// <summary>
|
2016-10-13 05:49:11 +01:00
|
|
|
/// Identifier as a string if applicable
|
2016-10-13 04:55:00 +01:00
|
|
|
/// </summary>
|
|
|
|
|
public string ASCII;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Binary identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] Binary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Device identification page
|
|
|
|
|
/// Page code 0x83
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_83
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The descriptors.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IdentificatonDescriptor[] Descriptors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_83? DecodePage_83(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 04:55:00 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0x83) return null;
|
2016-10-13 04:55:00 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] + 4 != pageResponse.Length) return null;
|
2016-10-13 04:55:00 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 6) return null;
|
2016-10-13 04:55:00 +01:00
|
|
|
|
|
|
|
|
Page_83 decoded = new Page_83();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
int position = 4;
|
|
|
|
|
List<IdentificatonDescriptor> descriptors = new List<IdentificatonDescriptor>();
|
|
|
|
|
|
|
|
|
|
while(position < pageResponse.Length)
|
|
|
|
|
{
|
|
|
|
|
IdentificatonDescriptor descriptor = new IdentificatonDescriptor();
|
2016-10-13 05:49:11 +01:00
|
|
|
descriptor.ProtocolIdentifier = (ProtocolIdentifiers)((pageResponse[position] & 0xF0) >> 4);
|
2016-10-13 04:55:00 +01:00
|
|
|
descriptor.CodeSet = (IdentificationCodeSet)(pageResponse[position] & 0x0F);
|
2016-10-13 05:49:11 +01:00
|
|
|
descriptor.PIV |= (pageResponse[position + 1] & 0x80) == 0x80;
|
|
|
|
|
descriptor.Association = (IdentificationAssociation)((pageResponse[position + 1] & 0x30) >> 4);
|
2016-10-13 04:55:00 +01:00
|
|
|
descriptor.Type = (IdentificationTypes)(pageResponse[position + 1] & 0x0F);
|
|
|
|
|
descriptor.Length = pageResponse[position + 3];
|
|
|
|
|
descriptor.Binary = new byte[descriptor.Length];
|
2017-06-03 01:11:51 +01:00
|
|
|
if(descriptor.Length + position + 4 >= pageResponse.Length)
|
|
|
|
|
descriptor.Length = (byte)(pageResponse.Length - position - 4);
|
2016-10-13 04:55:00 +01:00
|
|
|
Array.Copy(pageResponse, position + 4, descriptor.Binary, 0, descriptor.Length);
|
2017-12-21 04:43:29 +00:00
|
|
|
switch(descriptor.CodeSet) {
|
|
|
|
|
case IdentificationCodeSet.ASCII: descriptor.ASCII = StringHandlers.CToString(descriptor.Binary);
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationCodeSet.UTF8: descriptor.ASCII = Encoding.UTF8.GetString(descriptor.Binary);
|
|
|
|
|
break;
|
|
|
|
|
default: descriptor.ASCII = "";
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-13 04:55:00 +01:00
|
|
|
|
|
|
|
|
position += 4 + descriptor.Length;
|
2016-10-13 05:49:11 +01:00
|
|
|
descriptors.Add(descriptor);
|
2016-10-13 04:55:00 +01:00
|
|
|
}
|
|
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
decoded.Descriptors = descriptors.ToArray();
|
|
|
|
|
|
2016-10-13 04:55:00 +01:00
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_83(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_83(DecodePage_83(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_83(Page_83? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 04:55:00 +01:00
|
|
|
|
|
|
|
|
Page_83 page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendLine("SCSI Device identification:");
|
2016-10-13 04:55:00 +01:00
|
|
|
|
|
|
|
|
if(page.Descriptors.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
sb.AppendLine("\tThere are no identifiers");
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach(IdentificatonDescriptor descriptor in page.Descriptors)
|
|
|
|
|
{
|
2016-10-13 05:49:11 +01:00
|
|
|
switch(descriptor.Association)
|
|
|
|
|
{
|
|
|
|
|
case IdentificationAssociation.LogicalUnit:
|
|
|
|
|
sb.AppendLine("\tIdentifier belongs to addressed logical unit");
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationAssociation.TargetPort:
|
|
|
|
|
sb.AppendLine("\tIdentifier belongs to target port");
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationAssociation.TargetDevice:
|
|
|
|
|
sb.AppendLine("\tIdentifier belongs to target device that contains the addressed logical unit");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tIndentifier has unknown association with code {0}",
|
|
|
|
|
(byte)descriptor.Association).AppendLine();
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(descriptor.PIV)
|
|
|
|
|
{
|
2017-12-21 16:07:20 +00:00
|
|
|
string protocol;
|
2016-10-13 05:49:11 +01:00
|
|
|
switch(descriptor.ProtocolIdentifier)
|
|
|
|
|
{
|
|
|
|
|
case ProtocolIdentifiers.ADT:
|
|
|
|
|
protocol = "Automation/Drive Interface Transport";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.ATA:
|
|
|
|
|
protocol = "AT Attachment Interface (ATA/ATAPI)";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.FibreChannel:
|
|
|
|
|
protocol = "Fibre Channel";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.Firewire:
|
|
|
|
|
protocol = "IEEE 1394";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.iSCSI:
|
|
|
|
|
protocol = "Internet SCSI";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.NoProtocol:
|
|
|
|
|
protocol = "no specific";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.PCIe:
|
|
|
|
|
protocol = "PCI Express";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.RDMAP:
|
|
|
|
|
protocol = "SCSI Remote Direct Memory Access";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SAS:
|
|
|
|
|
protocol = "Serial Attachment SCSI";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SCSI:
|
|
|
|
|
protocol = "Parallel SCSI";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SCSIe:
|
|
|
|
|
protocol = "SCSI over PCI Express";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SSA:
|
|
|
|
|
protocol = "SSA";
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.UAS:
|
|
|
|
|
protocol = "USB Attached SCSI";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-21 17:58:51 +00:00
|
|
|
protocol = $"unknown code {(byte)descriptor.ProtocolIdentifier}";
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tDescriptor referes to {0} protocol", protocol).AppendLine();
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-13 04:55:00 +01:00
|
|
|
switch(descriptor.Type)
|
|
|
|
|
{
|
|
|
|
|
case IdentificationTypes.NoAuthority:
|
2017-12-21 04:43:29 +00:00
|
|
|
switch(descriptor.CodeSet) {
|
|
|
|
|
case IdentificationCodeSet.ASCII:
|
|
|
|
|
case IdentificationCodeSet.UTF8: sb.AppendFormat("\tVendor descriptor contains: {0}", descriptor.ASCII).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationCodeSet.Binary:
|
|
|
|
|
sb.AppendFormat("\tVendor descriptor contains binary data (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40)).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tVendor descriptor contains unknown kind {1} of data (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40),
|
|
|
|
|
(byte)descriptor.CodeSet).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-13 04:55:00 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.Inquiry:
|
2017-12-21 04:43:29 +00:00
|
|
|
switch(descriptor.CodeSet) {
|
|
|
|
|
case IdentificationCodeSet.ASCII:
|
|
|
|
|
case IdentificationCodeSet.UTF8: sb.AppendFormat("\tInquiry descriptor contains: {0}", descriptor.ASCII).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationCodeSet.Binary:
|
|
|
|
|
sb.AppendFormat("\tInquiry descriptor contains binary data (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40)).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tInquiry descriptor contains unknown kind {1} of data (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40),
|
|
|
|
|
(byte)descriptor.CodeSet).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-13 04:55:00 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.EUI:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(descriptor.CodeSet == IdentificationCodeSet.ASCII ||
|
|
|
|
|
descriptor.CodeSet == IdentificationCodeSet.UTF8)
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tIEEE EUI-64: {0}", descriptor.ASCII).AppendLine();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sb.AppendFormat("\tIEEE EUI-64: {0:X2}", descriptor.Binary[0]);
|
|
|
|
|
for(int i = 1; i < descriptor.Binary.Length; i++)
|
|
|
|
|
sb.AppendFormat(":{0:X2}", descriptor.Binary[i]);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendLine();
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.NAA:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(descriptor.CodeSet == IdentificationCodeSet.ASCII ||
|
|
|
|
|
descriptor.CodeSet == IdentificationCodeSet.UTF8)
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tNAA: {0}", descriptor.ASCII).AppendLine();
|
2016-10-13 04:55:00 +01:00
|
|
|
else
|
2016-10-13 05:49:11 +01:00
|
|
|
{
|
|
|
|
|
sb.AppendFormat("\tNAA: {0:X2}", descriptor.Binary[0]);
|
|
|
|
|
for(int i = 1; i < descriptor.Binary.Length; i++)
|
|
|
|
|
sb.AppendFormat(":{0:X2}", descriptor.Binary[i]);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendLine();
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 04:55:00 +01:00
|
|
|
break;
|
2016-10-13 05:49:11 +01:00
|
|
|
case IdentificationTypes.Relative:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(descriptor.CodeSet == IdentificationCodeSet.ASCII ||
|
|
|
|
|
descriptor.CodeSet == IdentificationCodeSet.UTF8)
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tRelative target port identifier: {0}", descriptor.ASCII).AppendLine();
|
2016-10-13 04:55:00 +01:00
|
|
|
else
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tRelative target port identifier: {0}",
|
|
|
|
|
(descriptor.Binary[2] << 8) + descriptor.Binary[3]).AppendLine();
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.TargetPortGroup:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(descriptor.CodeSet == IdentificationCodeSet.ASCII ||
|
|
|
|
|
descriptor.CodeSet == IdentificationCodeSet.UTF8)
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tTarget group identifier: {0}", descriptor.ASCII).AppendLine();
|
|
|
|
|
else
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tTarget group identifier: {0}",
|
|
|
|
|
(descriptor.Binary[2] << 8) + descriptor.Binary[3]).AppendLine();
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.LogicalUnitGroup:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(descriptor.CodeSet == IdentificationCodeSet.ASCII ||
|
|
|
|
|
descriptor.CodeSet == IdentificationCodeSet.UTF8)
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tLogical unit group identifier: {0}", descriptor.ASCII).AppendLine();
|
|
|
|
|
else
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tLogical unit group identifier: {0}",
|
|
|
|
|
(descriptor.Binary[2] << 8) + descriptor.Binary[3]).AppendLine();
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.MD5:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(descriptor.CodeSet == IdentificationCodeSet.ASCII ||
|
|
|
|
|
descriptor.CodeSet == IdentificationCodeSet.UTF8)
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tMD5 logical unit identifier: {0}", descriptor.ASCII).AppendLine();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sb.AppendFormat("\tMD5 logical unit identifier: {0:x2}", descriptor.Binary[0]);
|
|
|
|
|
for(int i = 1; i < descriptor.Binary.Length; i++)
|
|
|
|
|
sb.AppendFormat("{0:x2}", descriptor.Binary[i]);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendLine();
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.SCSI:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(descriptor.CodeSet == IdentificationCodeSet.ASCII ||
|
|
|
|
|
descriptor.CodeSet == IdentificationCodeSet.UTF8)
|
2016-10-13 05:49:11 +01:00
|
|
|
sb.AppendFormat("\tSCSI name string identifier: {0}", descriptor.ASCII).AppendLine();
|
|
|
|
|
else
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tSCSI name string identifier (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40)).AppendLine();
|
2016-10-13 05:49:11 +01:00
|
|
|
break;
|
|
|
|
|
case IdentificationTypes.ProtocolSpecific:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(descriptor.PIV)
|
|
|
|
|
switch(descriptor.ProtocolIdentifier)
|
2016-10-13 05:49:11 +01:00
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
case ProtocolIdentifiers.ADT:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (Automation/Drive Interface Transport) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.ATA:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (ATA/ATAPI) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.FibreChannel:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (Fibre Channel) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.Firewire:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (IEEE 1394) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.iSCSI:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (Internet SCSI) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.NoProtocol:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (unknown) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.PCIe:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (PCI Express) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.RDMAP:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (SCSI Remote Direct Memory Access) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SAS:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (Serial Attachment SCSI) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SCSI:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (Parallel SCSI) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SSA:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (SSA) specific descriptor with unknown format (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.SCSIe:
|
|
|
|
|
sb.AppendFormat("\tProtocol (SCSIe) specific descriptor: Routing ID is {0}",
|
|
|
|
|
(descriptor.Binary[0] << 8) + descriptor.Binary[1]).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case ProtocolIdentifiers.UAS:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (UAS) specific descriptor: USB address {0} interface {1}",
|
|
|
|
|
descriptor.Binary[0] & 0x7F, descriptor.Binary[2]).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb
|
|
|
|
|
.AppendFormat("\tProtocol (unknown code {0}) specific descriptor with unknown format (hex): {1}",
|
|
|
|
|
(byte)descriptor.ProtocolIdentifier,
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
2016-10-13 05:49:11 +01:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2016-10-13 04:55:00 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-21 04:43:29 +00:00
|
|
|
switch(descriptor.CodeSet) {
|
|
|
|
|
case IdentificationCodeSet.ASCII:
|
|
|
|
|
case IdentificationCodeSet.UTF8:
|
|
|
|
|
sb.AppendFormat("\tUnknown descriptor type {1} contains: {0}", descriptor.ASCII,
|
|
|
|
|
(byte)descriptor.Type).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationCodeSet.Binary:
|
|
|
|
|
sb.AppendFormat("\tUnknown descriptor type {1} contains binary data (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40),
|
|
|
|
|
(byte)descriptor.Type).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("Inquiry descriptor type {2} contains unknown kind {1} of data (hex): {0}",
|
|
|
|
|
PrintHex.ByteArrayToHexArrayString(descriptor.Binary, 40),
|
|
|
|
|
(byte)descriptor.CodeSet, (byte)descriptor.Type).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-13 04:55:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0x83: Device identification page
|
2016-10-13 06:59:56 +01:00
|
|
|
|
|
|
|
|
#region EVPD Page 0x84: Software Interface Identification page
|
|
|
|
|
public struct SoftwareIdentifier
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// EUI-48 identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] Identifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Software Interface Identification page
|
|
|
|
|
/// Page code 0x84
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_84
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The descriptors.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public SoftwareIdentifier[] Identifiers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_84? DecodePage_84(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0x84) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] + 4 != pageResponse.Length) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 10) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
|
|
|
|
Page_84 decoded = new Page_84();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
int position = 4;
|
|
|
|
|
List<SoftwareIdentifier> identifiers = new List<SoftwareIdentifier>();
|
|
|
|
|
|
|
|
|
|
while(position < pageResponse.Length)
|
|
|
|
|
{
|
|
|
|
|
SoftwareIdentifier identifier = new SoftwareIdentifier();
|
|
|
|
|
identifier.Identifier = new byte[6];
|
|
|
|
|
Array.Copy(pageResponse, position, identifier.Identifier, 0, 6);
|
|
|
|
|
identifiers.Add(identifier);
|
|
|
|
|
position += 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decoded.Identifiers = identifiers.ToArray();
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_84(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_84(DecodePage_84(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_84(Page_84? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
|
|
|
|
Page_84 page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("SCSI Software Interface Identifiers:");
|
|
|
|
|
|
|
|
|
|
if(page.Identifiers.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
sb.AppendLine("\tThere are no identifiers");
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach(SoftwareIdentifier identifier in page.Identifiers)
|
|
|
|
|
{
|
|
|
|
|
sb.AppendFormat("\t{0:X2}", identifier.Identifier[0]);
|
|
|
|
|
for(int i = 1; i < identifier.Identifier.Length; i++)
|
2016-10-16 07:29:19 +01:00
|
|
|
sb.AppendFormat(":{0:X2}", identifier.Identifier[i]);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2016-10-13 06:59:56 +01:00
|
|
|
sb.AppendLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0x84: Software Interface Identification page
|
|
|
|
|
|
|
|
|
|
#region EVPD Page 0x85: Management Network Addresses page
|
|
|
|
|
public enum NetworkServiceTypes : byte
|
|
|
|
|
{
|
|
|
|
|
Unspecified = 0,
|
|
|
|
|
StorageConf = 1,
|
|
|
|
|
Diagnostics = 2,
|
|
|
|
|
Status = 3,
|
|
|
|
|
Logging = 4,
|
2016-10-13 07:02:33 +01:00
|
|
|
CodeDownload = 5,
|
|
|
|
|
CopyService = 6,
|
|
|
|
|
Administrative = 7
|
2016-10-13 06:59:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct NetworkDescriptor
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Identifies which device the identifier associates with
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IdentificationAssociation Association;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Defines the type of the identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public NetworkServiceTypes Type;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Length of the identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort Length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Binary identifier
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] Address;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Device identification page
|
|
|
|
|
/// Page code 0x85
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_85
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The descriptors.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public NetworkDescriptor[] Descriptors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_85? DecodePage_85(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0x85) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if((pageResponse[2] << 8) + pageResponse[3] + 4 != pageResponse.Length) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 4) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
|
|
|
|
Page_85 decoded = new Page_85();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
2016-10-13 19:15:52 +01:00
|
|
|
decoded.PageLength = (ushort)((pageResponse[2] << 8) + pageResponse[3] + 4);
|
2016-10-13 06:59:56 +01:00
|
|
|
|
|
|
|
|
int position = 4;
|
|
|
|
|
List<NetworkDescriptor> descriptors = new List<NetworkDescriptor>();
|
|
|
|
|
|
|
|
|
|
while(position < pageResponse.Length)
|
|
|
|
|
{
|
|
|
|
|
NetworkDescriptor descriptor = new NetworkDescriptor();
|
|
|
|
|
descriptor.Association = (IdentificationAssociation)((pageResponse[position] & 0x60) >> 5);
|
|
|
|
|
descriptor.Type = (NetworkServiceTypes)(pageResponse[position] & 0x1F);
|
2016-10-13 19:15:52 +01:00
|
|
|
descriptor.Length = (ushort)((pageResponse[position + 2] << 8) + pageResponse[position + 3]);
|
2016-10-13 06:59:56 +01:00
|
|
|
descriptor.Address = new byte[descriptor.Length];
|
|
|
|
|
Array.Copy(pageResponse, position + 4, descriptor.Address, 0, descriptor.Length);
|
|
|
|
|
|
|
|
|
|
position += 4 + descriptor.Length;
|
|
|
|
|
descriptors.Add(descriptor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decoded.Descriptors = descriptors.ToArray();
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_85(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_85(DecodePage_85(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_85(Page_85? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 06:59:56 +01:00
|
|
|
|
|
|
|
|
Page_85 page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("SCSI Management Network Addresses:");
|
|
|
|
|
|
|
|
|
|
if(page.Descriptors.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
sb.AppendLine("\tThere are no addresses");
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach(NetworkDescriptor descriptor in page.Descriptors)
|
|
|
|
|
{
|
|
|
|
|
switch(descriptor.Association)
|
|
|
|
|
{
|
|
|
|
|
case IdentificationAssociation.LogicalUnit:
|
|
|
|
|
sb.AppendLine("\tIdentifier belongs to addressed logical unit");
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationAssociation.TargetPort:
|
|
|
|
|
sb.AppendLine("\tIdentifier belongs to target port");
|
|
|
|
|
break;
|
|
|
|
|
case IdentificationAssociation.TargetDevice:
|
|
|
|
|
sb.AppendLine("\tIdentifier belongs to target device that contains the addressed logical unit");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tIndentifier has unknown association with code {0}",
|
|
|
|
|
(byte)descriptor.Association).AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(descriptor.Type)
|
|
|
|
|
{
|
|
|
|
|
case NetworkServiceTypes.CodeDownload:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address for code download: {0}", StringHandlers.CToString(descriptor.Address))
|
|
|
|
|
.AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
|
|
|
|
case NetworkServiceTypes.Diagnostics:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address for diagnostics: {0}", StringHandlers.CToString(descriptor.Address))
|
|
|
|
|
.AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
|
|
|
|
case NetworkServiceTypes.Logging:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address for logging: {0}", StringHandlers.CToString(descriptor.Address))
|
|
|
|
|
.AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
|
|
|
|
case NetworkServiceTypes.Status:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address for status: {0}", StringHandlers.CToString(descriptor.Address))
|
|
|
|
|
.AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
|
|
|
|
case NetworkServiceTypes.StorageConf:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address for storage configuration service: {0}",
|
|
|
|
|
StringHandlers.CToString(descriptor.Address)).AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
|
|
|
|
case NetworkServiceTypes.Unspecified:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Unspecified address: {0}", StringHandlers.CToString(descriptor.Address))
|
|
|
|
|
.AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
2016-10-13 07:02:33 +01:00
|
|
|
case NetworkServiceTypes.CopyService:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address for copy service: {0}", StringHandlers.CToString(descriptor.Address))
|
|
|
|
|
.AppendLine();
|
2016-10-13 07:02:33 +01:00
|
|
|
break;
|
|
|
|
|
case NetworkServiceTypes.Administrative:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address for administrative configuration service: {0}",
|
|
|
|
|
StringHandlers.CToString(descriptor.Address)).AppendLine();
|
2016-10-13 07:02:33 +01:00
|
|
|
break;
|
2016-10-13 06:59:56 +01:00
|
|
|
default:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Address of unknown type {1}: {0}",
|
|
|
|
|
StringHandlers.CToString(descriptor.Address), (byte)descriptor.Type)
|
|
|
|
|
.AppendLine();
|
2016-10-13 06:59:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0x85: Management Network Addresses page
|
|
|
|
|
|
2016-10-13 18:31:05 +01:00
|
|
|
#region EVPD Page 0x86: Extended INQUIRY data page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Device identification page
|
|
|
|
|
/// Page code 0x86
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_86
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Indicates how a device server activates microcode
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte ActivateMicrocode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Protection types supported by device
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte SPT;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks logical block guard field
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool GRD_CHK;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks logical block application tag
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool APP_CHK;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks logical block reference
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool REF_CHK;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports unit attention condition sense key specific data
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool UASK_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports grouping
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool GROUP_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports priority
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool PRIOR_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports head of queue
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool HEADSUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports ordered
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool ORDSUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports simple
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool SIMPSUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports marking a block as uncorrectable
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool WU_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports disabling correction on WRITE LONG
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool CRD_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports a non-volatile cache
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool NV_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Supports a volatile cache
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool V_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Disable protection information checks
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool NO_PI_CHK;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Protection information interval supported
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool P_I_I_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Clears all LUNs unit attention when clearing one
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool LUICLR;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Referrals support
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool R_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// History snapshots release effects
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool HSSRELEF;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Capability based command security
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool CBCS;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Indicates how it handles microcode updating with multiple nexuxes
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte Nexus;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Time to complete extended self-test
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort ExtendedTestMinutes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Power on activation support
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool POA_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Hard reset actication
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool HRA_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Vendor specific activation
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool VSA_SUP;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Maximum length in bytes of sense data
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte MaximumSenseLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_86? DecodePage_86(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0x86) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] + 4 != pageResponse.Length) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 64) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
Page_86 decoded = new Page_86();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
2016-10-13 19:15:52 +01:00
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
decoded.ActivateMicrocode = (byte)((pageResponse[4] & 0xC0) >> 6);
|
|
|
|
|
decoded.SPT = (byte)((pageResponse[4] & 0x38) >> 3);
|
|
|
|
|
decoded.GRD_CHK |= (pageResponse[4] & 0x04) == 0x04;
|
|
|
|
|
decoded.APP_CHK |= (pageResponse[4] & 0x02) == 0x02;
|
|
|
|
|
decoded.REF_CHK |= (pageResponse[4] & 0x01) == 0x01;
|
|
|
|
|
decoded.UASK_SUP |= (pageResponse[5] & 0x20) == 0x20;
|
|
|
|
|
decoded.GROUP_SUP |= (pageResponse[5] & 0x10) == 0x10;
|
|
|
|
|
decoded.PRIOR_SUP |= (pageResponse[5] & 0x08) == 0x08;
|
|
|
|
|
decoded.HEADSUP |= (pageResponse[5] & 0x04) == 0x04;
|
|
|
|
|
decoded.ORDSUP |= (pageResponse[5] & 0x02) == 0x02;
|
|
|
|
|
decoded.SIMPSUP |= (pageResponse[5] & 0x01) == 0x01;
|
|
|
|
|
decoded.WU_SUP |= (pageResponse[6] & 0x08) == 0x08;
|
|
|
|
|
decoded.CRD_SUP |= (pageResponse[6] & 0x04) == 0x04;
|
|
|
|
|
decoded.NV_SUP |= (pageResponse[6] & 0x02) == 0x02;
|
|
|
|
|
decoded.V_SUP |= (pageResponse[6] & 0x01) == 0x01;
|
|
|
|
|
decoded.NO_PI_CHK |= (pageResponse[7] & 0x20) == 0x20;
|
|
|
|
|
decoded.P_I_I_SUP |= (pageResponse[7] & 0x10) == 0x10;
|
|
|
|
|
decoded.LUICLR |= (pageResponse[7] & 0x01) == 0x01;
|
|
|
|
|
decoded.R_SUP |= (pageResponse[8] & 0x10) == 0x10;
|
|
|
|
|
decoded.HSSRELEF |= (pageResponse[8] & 0x02) == 0x02;
|
|
|
|
|
decoded.CBCS |= (pageResponse[8] & 0x01) == 0x01;
|
|
|
|
|
decoded.Nexus = (byte)(pageResponse[9] & 0x0F);
|
|
|
|
|
decoded.ExtendedTestMinutes = (ushort)((pageResponse[10] << 8) + pageResponse[11]);
|
|
|
|
|
decoded.POA_SUP |= (pageResponse[12] & 0x80) == 0x80;
|
|
|
|
|
decoded.HRA_SUP |= (pageResponse[12] & 0x40) == 0x40;
|
|
|
|
|
decoded.VSA_SUP |= (pageResponse[12] & 0x20) == 0x20;
|
|
|
|
|
decoded.MaximumSenseLength = pageResponse[13];
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_86(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_86(DecodePage_86(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_86(Page_86? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
Page_86 page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("SCSI Extended INQUIRY Data:");
|
|
|
|
|
|
2017-12-21 04:43:29 +00:00
|
|
|
switch(page.PeripheralDeviceType) {
|
|
|
|
|
case PeripheralDeviceTypes.DirectAccess:
|
|
|
|
|
case PeripheralDeviceTypes.SCSIZonedBlockDevice:
|
|
|
|
|
switch(page.SPT)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
sb.AppendLine("Logical unit supports type 1 protection");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
sb.AppendLine("Logical unit supports types 1 and 2 protection");
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
sb.AppendLine("Logical unit supports type 2 protection");
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
sb.AppendLine("Logical unit supports types 1 and 3 protection");
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
sb.AppendLine("Logical unit supports type 3 protection");
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
sb.AppendLine("Logical unit supports types 2 and 3 protection");
|
|
|
|
|
break;
|
|
|
|
|
case 7:
|
|
|
|
|
sb.AppendLine("Logical unit supports types 1, 2 and 3 protection");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-21 14:30:38 +00:00
|
|
|
sb.AppendFormat("Logical unit supports unknown protection defined by code {0}", page.SPT)
|
2017-12-21 04:43:29 +00:00
|
|
|
.AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PeripheralDeviceTypes.SequentialAccess when page.SPT == 1: sb.AppendLine("Logical unit supports logical block protection");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-13 18:31:05 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.GRD_CHK) sb.AppendLine("Device checks the logical block guard");
|
|
|
|
|
if(page.APP_CHK) sb.AppendLine("Device checks the logical block application tag");
|
|
|
|
|
if(page.REF_CHK) sb.AppendLine("Device checks the logical block reference tag");
|
|
|
|
|
if(page.UASK_SUP) sb.AppendLine("Device supports unit attention condition sense key specific data");
|
|
|
|
|
if(page.GROUP_SUP) sb.AppendLine("Device supports grouping");
|
|
|
|
|
if(page.PRIOR_SUP) sb.AppendLine("Device supports priority");
|
|
|
|
|
if(page.HEADSUP) sb.AppendLine("Device supports head of queue");
|
|
|
|
|
if(page.ORDSUP) sb.AppendLine("Device supports the ORDERED task attribute");
|
|
|
|
|
if(page.SIMPSUP) sb.AppendLine("Device supports the SIMPLE task attribute");
|
|
|
|
|
if(page.WU_SUP) sb.AppendLine("Device supports marking a block as uncorrectable with WRITE LONG");
|
|
|
|
|
if(page.CRD_SUP) sb.AppendLine("Device supports disabling correction with WRITE LONG");
|
|
|
|
|
if(page.NV_SUP) sb.AppendLine("Device has a non-volatile cache");
|
|
|
|
|
if(page.V_SUP) sb.AppendLine("Device has a volatile cache");
|
|
|
|
|
if(page.NO_PI_CHK) sb.AppendLine("Device has disabled protection information checks");
|
|
|
|
|
if(page.P_I_I_SUP) sb.AppendLine("Device supports protection information intervals");
|
2016-10-13 18:31:05 +01:00
|
|
|
if(page.LUICLR)
|
|
|
|
|
sb.AppendLine("Device clears any unit attention condition in all LUNs after reporting for any LUN");
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.R_SUP) sb.AppendLine("Device supports referrals");
|
|
|
|
|
if(page.HSSRELEF) sb.AppendLine("Devoce implements alternate reset handling");
|
|
|
|
|
if(page.CBCS) sb.AppendLine("Device supports capability-based command security");
|
|
|
|
|
if(page.POA_SUP) sb.AppendLine("Device supports power-on activation for new microcode");
|
|
|
|
|
if(page.HRA_SUP) sb.AppendLine("Device supports hard reset activation for new microcode");
|
|
|
|
|
if(page.VSA_SUP) sb.AppendLine("Device supports vendor specific activation for new microcode");
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
if(page.ExtendedTestMinutes > 0)
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Extended self-test takes {0} to complete",
|
|
|
|
|
TimeSpan.FromMinutes(page.ExtendedTestMinutes)).AppendLine();
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
if(page.MaximumSenseLength > 0)
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("Device supports a maximum of {0} bytes for sense data", page.MaximumSenseLength)
|
|
|
|
|
.AppendLine();
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0x86: Extended INQUIRY data page
|
|
|
|
|
|
|
|
|
|
#region EVPD Page 0x89: ATA Information page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ATA Information page
|
|
|
|
|
/// Page code 0x89
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_89
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains the SAT vendor identification
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] VendorIdentification;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains the SAT product identification
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] ProductIdentification;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains the SAT revision level
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] ProductRevisionLevel;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains the ATA device signature
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] Signature;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains the command code used to identify the device
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte CommandCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains the response to ATA IDENTIFY (PACKET) DEVICE
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] IdentifyData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_89? DecodePage_89(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0x89) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if((pageResponse[2] << 8) + pageResponse[3] + 4 != pageResponse.Length) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 572) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
Page_89 decoded = new Page_89();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
2016-10-13 19:15:52 +01:00
|
|
|
decoded.PageLength = (ushort)((pageResponse[2] << 8) + pageResponse[3] + 4);
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
decoded.VendorIdentification = new byte[8];
|
|
|
|
|
decoded.ProductIdentification = new byte[16];
|
|
|
|
|
decoded.ProductRevisionLevel = new byte[4];
|
|
|
|
|
decoded.Signature = new byte[20];
|
|
|
|
|
decoded.IdentifyData = new byte[512];
|
|
|
|
|
|
|
|
|
|
Array.Copy(pageResponse, 8, decoded.VendorIdentification, 0, 8);
|
|
|
|
|
Array.Copy(pageResponse, 8, decoded.ProductIdentification, 0, 16);
|
|
|
|
|
Array.Copy(pageResponse, 8, decoded.ProductRevisionLevel, 0, 4);
|
|
|
|
|
Array.Copy(pageResponse, 8, decoded.Signature, 0, 20);
|
|
|
|
|
decoded.CommandCode = pageResponse[56];
|
|
|
|
|
Array.Copy(pageResponse, 8, decoded.IdentifyData, 0, 512);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_89(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_89(DecodePage_89(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Decode ATA signature?
|
|
|
|
|
public static string PrettifyPage_89(Page_89? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
Page_89 page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("SCSI to ATA Translation Layer Data:");
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tTranslation layer vendor: {0}",
|
|
|
|
|
VendorString.Prettify(StringHandlers.CToString(page.VendorIdentification).Trim()))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
sb.AppendFormat("\tTranslation layer name: {0}",
|
|
|
|
|
StringHandlers.CToString(page.ProductIdentification).Trim()).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tTranslation layer release level: {0}",
|
|
|
|
|
StringHandlers.CToString(page.ProductRevisionLevel).Trim()).AppendLine();
|
2016-10-13 18:31:05 +01:00
|
|
|
switch(page.CommandCode)
|
|
|
|
|
{
|
|
|
|
|
case 0xEC:
|
|
|
|
|
sb.AppendLine("\tDevice responded to ATA IDENTIFY DEVICE command.");
|
|
|
|
|
break;
|
|
|
|
|
case 0xA1:
|
|
|
|
|
sb.AppendLine("\tDevice responded to ATA IDENTIFY PACKET DEVICE command.");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tDevice responded to ATA command {0:X2}h", page.CommandCode).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch(page.Signature[0])
|
|
|
|
|
{
|
|
|
|
|
case 0x00:
|
|
|
|
|
sb.AppendLine("\tDevice uses Parallel ATA.");
|
|
|
|
|
break;
|
|
|
|
|
case 0x34:
|
|
|
|
|
sb.AppendLine("\tDevice uses Serial ATA.");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tDevice uses unknown transport with code {0}", page.Signature[0]).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-21 14:30:38 +00:00
|
|
|
Identify.IdentifyDevice? id = Identify.Decode(page.IdentifyData);
|
2016-10-13 18:31:05 +01:00
|
|
|
if(id.HasValue)
|
|
|
|
|
{
|
|
|
|
|
sb.AppendLine("\tATA IDENTIFY information follows:");
|
2017-12-21 14:30:38 +00:00
|
|
|
sb.AppendFormat("{0}", Identify.Prettify(id)).AppendLine();
|
2016-10-13 18:31:05 +01:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
else sb.AppendLine("\tCould not decode ATA IDENTIFY information");
|
2016-10-13 18:31:05 +01:00
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0x89: ATA Information page
|
|
|
|
|
|
2016-10-13 19:15:52 +01:00
|
|
|
#region EVPD Page 0xC0 (Quantum): Firmware Build Information page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Firmware Build Information page
|
|
|
|
|
/// Page code 0xC0 (Quantum)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_C0_Quantum
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Servo firmware checksum
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort ServoFirmwareChecksum;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Servo EEPROM checksum
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort ServoEEPROMChecksum;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Read/Write firmware checksum
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint ReadWriteFirmwareChecksum;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Read/Write firmware build data
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] ReadWriteFirmwareBuildData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_C0_Quantum? DecodePage_C0_Quantum(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 19:15:52 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xC0) return null;
|
2016-10-13 19:15:52 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] != 20) return null;
|
2016-10-13 19:15:52 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length != 36) return null;
|
2016-10-13 19:15:52 +01:00
|
|
|
|
|
|
|
|
Page_C0_Quantum decoded = new Page_C0_Quantum();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
decoded.ServoFirmwareChecksum = (ushort)((pageResponse[4] << 8) + pageResponse[5]);
|
|
|
|
|
decoded.ServoEEPROMChecksum = (ushort)((pageResponse[6] << 8) + pageResponse[7]);
|
2017-12-19 20:33:03 +00:00
|
|
|
decoded.ReadWriteFirmwareChecksum = (uint)((pageResponse[8] << 24) + (pageResponse[9] << 16) +
|
|
|
|
|
(pageResponse[10] << 8) + pageResponse[11]);
|
2016-10-13 19:15:52 +01:00
|
|
|
decoded.ReadWriteFirmwareBuildData = new byte[24];
|
|
|
|
|
Array.Copy(pageResponse, 12, decoded.ReadWriteFirmwareBuildData, 0, 24);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_Quantum(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_C0_Quantum(DecodePage_C0_Quantum(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_Quantum(Page_C0_Quantum? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 19:15:52 +01:00
|
|
|
|
|
|
|
|
Page_C0_Quantum page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("Quantum Firmware Build Information page:");
|
|
|
|
|
|
|
|
|
|
sb.AppendFormat("\tServo firmware checksum: 0x{0:X4}", page.ServoFirmwareChecksum).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tEEPROM firmware checksum: 0x{0:X4}", page.ServoEEPROMChecksum).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tRead/write firmware checksum: 0x{0:X8}", page.ReadWriteFirmwareChecksum).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tRead/write firmware build date: {0}",
|
|
|
|
|
StringHandlers.CToString(page.ReadWriteFirmwareBuildData)).AppendLine();
|
2016-10-13 19:15:52 +01:00
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xC0 (Quantum): Firmware Build Information page
|
|
|
|
|
|
2016-10-13 20:28:08 +01:00
|
|
|
#region EVPD Pages 0xC0, 0xC1 (Certance): Drive component revision level pages
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drive component revision level pages
|
|
|
|
|
/// Page codes 0xC0, 0xC1 (Certance)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_C0_C1_Certance
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
public byte[] Component;
|
|
|
|
|
public byte[] Version;
|
|
|
|
|
public byte[] Date;
|
|
|
|
|
public byte[] Variant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_C0_C1_Certance? DecodePage_C0_C1_Certance(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xC0 && pageResponse[1] != 0xC1) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] != 92) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length != 96) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
Page_C0_C1_Certance decoded = new Page_C0_C1_Certance();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
decoded.Component = new byte[26];
|
|
|
|
|
decoded.Version = new byte[19];
|
|
|
|
|
decoded.Date = new byte[24];
|
|
|
|
|
decoded.Variant = new byte[23];
|
|
|
|
|
|
|
|
|
|
Array.Copy(pageResponse, 4, decoded.Component, 0, 26);
|
|
|
|
|
Array.Copy(pageResponse, 30, decoded.Version, 0, 19);
|
|
|
|
|
Array.Copy(pageResponse, 49, decoded.Date, 0, 24);
|
|
|
|
|
Array.Copy(pageResponse, 73, decoded.Variant, 0, 23);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_C1_Certance(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_C0_C1_Certance(DecodePage_C0_C1_Certance(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_C1_Certance(Page_C0_C1_Certance? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
Page_C0_C1_Certance page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("Certance Drive Component Revision Levels page:");
|
|
|
|
|
|
|
|
|
|
sb.AppendFormat("\tComponent: {0}", StringHandlers.CToString(page.Component)).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tVersion: {0}", StringHandlers.CToString(page.Version)).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tDate: {0}", StringHandlers.CToString(page.Date)).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tVariant: {0}", StringHandlers.CToString(page.Variant)).AppendLine();
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Pages 0xC0, 0xC1 (Certance): Drive component revision level pages
|
|
|
|
|
|
|
|
|
|
#region EVPD Pages 0xC2, 0xC3, 0xC4, 0xC5, 0xC6 (Certance): Drive component serial number pages
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drive component serial number pages
|
|
|
|
|
/// Page codes 0xC2, 0xC3, 0xC4, 0xC5, 0xC6 (Certance)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_C2_C3_C4_C5_C6_Certance
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
public byte[] SerialNumber;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_C2_C3_C4_C5_C6_Certance? DecodePage_C2_C3_C4_C5_C6_Certance(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xC2 && pageResponse[1] != 0xC3 && pageResponse[1] != 0xC4 &&
|
|
|
|
|
pageResponse[1] != 0xC5 && pageResponse[1] != 0xC6) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] != 12) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length != 16) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
Page_C2_C3_C4_C5_C6_Certance decoded = new Page_C2_C3_C4_C5_C6_Certance();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
decoded.SerialNumber = new byte[12];
|
|
|
|
|
Array.Copy(pageResponse, 4, decoded.SerialNumber, 0, 12);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C2_C3_C4_C5_C6_Certance(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_C2_C3_C4_C5_C6_Certance(DecodePage_C2_C3_C4_C5_C6_Certance(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C2_C3_C4_C5_C6_Certance(Page_C2_C3_C4_C5_C6_Certance? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
Page_C2_C3_C4_C5_C6_Certance page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("Certance Drive Component Serial Number page:");
|
|
|
|
|
|
|
|
|
|
switch(page.PageCode)
|
|
|
|
|
{
|
|
|
|
|
case 0xC2:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tHead Assembly Serial Number: {0}", StringHandlers.CToString(page.SerialNumber))
|
|
|
|
|
.AppendLine();
|
2016-10-13 20:28:08 +01:00
|
|
|
break;
|
|
|
|
|
case 0xC3:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tReel Motor 1 Serial Number: {0}", StringHandlers.CToString(page.SerialNumber))
|
|
|
|
|
.AppendLine();
|
2016-10-13 20:28:08 +01:00
|
|
|
break;
|
|
|
|
|
case 0xC4:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tReel Motor 2 Serial Number: {0}", StringHandlers.CToString(page.SerialNumber))
|
|
|
|
|
.AppendLine();
|
2016-10-13 20:28:08 +01:00
|
|
|
break;
|
|
|
|
|
case 0xC5:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tBoard Serial Number: {0}", StringHandlers.CToString(page.SerialNumber))
|
|
|
|
|
.AppendLine();
|
2016-10-13 20:28:08 +01:00
|
|
|
break;
|
|
|
|
|
case 0xC6:
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tBase Mechanical Serial Number: {0}", StringHandlers.CToString(page.SerialNumber))
|
|
|
|
|
.AppendLine();
|
2016-10-13 20:28:08 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Pages 0xC0, 0xC1 (Certance): Drive component revision level pages
|
|
|
|
|
|
|
|
|
|
#region EVPD Page 0xDF (Certance): Drive status pages
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drive status pages
|
|
|
|
|
/// Page codes 0xDF (Certance)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_DF_Certance
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Command forwarding
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte CmdFwd;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Alerts
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool Alerts;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removable prevention
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool NoRemov;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Unit reservation
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool UnitRsvd;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Needs cleaning
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool Clean;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Tape threaded
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool Threaded;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Commands await forwarding
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool Lun1Cmd;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Autoload mode
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte AutoloadMode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Cartridge type
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte CartridgeType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Cartridge format
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte CartridgeFormat;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Cartridge capacity in 10e9 bytes
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort CartridgeCapacity;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Port A transport type
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PortATransportType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Port A SCSI ID
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PortASelectionID;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total number of head-tape contact time
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint OperatingHours;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ID that reserved the device
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong InitiatorID;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Cartridge serial number
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte[] CartridgeSerialNumber;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_DF_Certance? DecodePage_DF_Certance(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xDF) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] != 60) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length != 64) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
Page_DF_Certance decoded = new Page_DF_Certance();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
decoded.CmdFwd = (byte)((pageResponse[5] & 0xC0) >> 5);
|
|
|
|
|
decoded.Alerts |= (pageResponse[5] & 0x20) == 0x20;
|
|
|
|
|
decoded.NoRemov |= (pageResponse[5] & 0x08) == 0x08;
|
|
|
|
|
decoded.UnitRsvd |= (pageResponse[5] & 0x04) == 0x04;
|
|
|
|
|
decoded.Clean |= (pageResponse[5] & 0x01) == 0x01;
|
|
|
|
|
decoded.Threaded |= (pageResponse[6] & 0x10) == 0x10;
|
|
|
|
|
decoded.Lun1Cmd |= (pageResponse[6] & 0x08) == 0x08;
|
|
|
|
|
decoded.AutoloadMode = (byte)(pageResponse[6] & 0x07);
|
|
|
|
|
decoded.CartridgeType = pageResponse[8];
|
|
|
|
|
decoded.CartridgeFormat = pageResponse[9];
|
|
|
|
|
decoded.CartridgeCapacity = (ushort)((pageResponse[10] << 8) + pageResponse[11] + 4);
|
|
|
|
|
decoded.PortATransportType = pageResponse[12];
|
|
|
|
|
decoded.PortASelectionID = pageResponse[15];
|
2017-12-19 20:33:03 +00:00
|
|
|
decoded.OperatingHours = (uint)((pageResponse[20] << 24) + (pageResponse[21] << 16) +
|
|
|
|
|
(pageResponse[22] << 8) + pageResponse[23]);
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
byte[] buf = new byte[8];
|
|
|
|
|
Array.Copy(pageResponse, 24, buf, 0, 8);
|
|
|
|
|
decoded.InitiatorID = BitConverter.ToUInt64(buf.Reverse().ToArray(), 0);
|
|
|
|
|
|
|
|
|
|
decoded.CartridgeSerialNumber = new byte[32];
|
|
|
|
|
Array.Copy(pageResponse, 32, decoded.CartridgeSerialNumber, 0, 32);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_DF_Certance(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_DF_Certance(DecodePage_DF_Certance(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_DF_Certance(Page_DF_Certance? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
Page_DF_Certance page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("Certance drive status page:");
|
|
|
|
|
|
|
|
|
|
switch(page.CmdFwd)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
sb.AppendLine("\tCommand forwarding is disabled");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
sb.AppendLine("\tCommand forwarding is enabled");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tUnknown command forwarding code {0}", page.CmdFwd).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Alerts) sb.AppendLine("\tAlerts are enabled");
|
|
|
|
|
if(page.NoRemov) sb.AppendLine("\tCartridge removable is prevented");
|
2016-10-13 20:28:08 +01:00
|
|
|
if(page.UnitRsvd)
|
|
|
|
|
sb.AppendFormat("\tUnit is reserved by initiator ID {0:X16}", page.InitiatorID).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Clean) sb.AppendLine("\tDevice needs cleaning cartridge");
|
|
|
|
|
if(page.Threaded) sb.AppendLine("\tCartridge tape is threaded");
|
|
|
|
|
if(page.Lun1Cmd) sb.AppendLine("\tThere are commands pending to be forwarded");
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
switch(page.AutoloadMode)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
sb.AppendLine("\tCartridge will be loaded and threaded on insertion");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
sb.AppendLine("\tCartridge will be loaded but not threaded on insertion");
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
sb.AppendLine("\tCartridge will not be loaded");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tUnknown autoloading mode code {0}", page.AutoloadMode).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(page.PortATransportType)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
sb.AppendLine("\tPort A link is down");
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
sb.AppendLine("\tPort A uses Parallel SCSI Ultra-160 interface");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2016-10-13 21:17:42 +01:00
|
|
|
sb.AppendFormat("\tUnknown port A transport type code {0}", page.PortATransportType).AppendLine();
|
2016-10-13 20:28:08 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(page.PortATransportType > 0)
|
|
|
|
|
sb.AppendFormat("\tDrive responds to SCSI ID {0}", page.PortASelectionID).AppendLine();
|
|
|
|
|
|
|
|
|
|
sb.AppendFormat("\tDrive has been operating {0}", TimeSpan.FromHours(page.OperatingHours)).AppendLine();
|
|
|
|
|
|
|
|
|
|
if(page.CartridgeType > 0)
|
|
|
|
|
{
|
|
|
|
|
switch(page.CartridgeFormat)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
sb.AppendLine("\tInserted cartridge is LTO");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tUnknown cartridge format code {0}", page.CartridgeType).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(page.CartridgeType)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
sb.AppendLine("\tThere is no cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
sb.AppendLine("\tCleaning cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
sb.AppendLine("\tUnknown data cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
sb.AppendLine("\tFirmware cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
sb.AppendLine("\tLTO Ultrium 1 Type A cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
sb.AppendLine("\tLTO Ultrium 1 Type B cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 6:
|
|
|
|
|
sb.AppendLine("\tLTO Ultrium 1 Type C cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 7:
|
|
|
|
|
sb.AppendLine("\tLTO Ultrium 1 Type D cartridge inserted");
|
|
|
|
|
break;
|
|
|
|
|
case 8:
|
|
|
|
|
sb.AppendLine("\tLTO Ultrium 2 cartridge inserted");
|
2016-10-16 07:29:19 +01:00
|
|
|
break;
|
2016-10-13 20:28:08 +01:00
|
|
|
default:
|
|
|
|
|
sb.AppendFormat("\tUnknown cartridge type code {0}", page.CartridgeType).AppendLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tCartridge has an uncompressed capabity of {0} gigabytes", page.CartridgeCapacity)
|
|
|
|
|
.AppendLine();
|
|
|
|
|
sb.AppendFormat("\tCartridge serial number: {0}",
|
|
|
|
|
StringHandlers.SpacePaddedToString(page.CartridgeSerialNumber)).AppendLine();
|
2016-10-13 20:28:08 +01:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
else sb.AppendLine("\tThere is no cartridge inserted");
|
2016-10-13 20:28:08 +01:00
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xDF (Certance): Drive status pages
|
|
|
|
|
|
2016-10-13 22:56:16 +01:00
|
|
|
#region EVPD Page 0xC0 (IBM): Drive Component Revision Levels page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drive Component Revision Levels page
|
|
|
|
|
/// Page code 0xC0 (IBM)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_C0_IBM
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
public byte[] CodeName;
|
|
|
|
|
public byte[] Date;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_C0_IBM? DecodePage_C0_IBM(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xC0) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] != 39) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length != 43) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
|
|
|
|
Page_C0_IBM decoded = new Page_C0_IBM();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
decoded.CodeName = new byte[12];
|
|
|
|
|
decoded.Date = new byte[8];
|
|
|
|
|
|
|
|
|
|
Array.Copy(pageResponse, 4, decoded.CodeName, 0, 12);
|
|
|
|
|
Array.Copy(pageResponse, 23, decoded.Date, 0, 8);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_IBM(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_C0_IBM(DecodePage_C0_IBM(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_IBM(Page_C0_IBM? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
|
|
|
|
Page_C0_IBM page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("IBM Drive Component Revision Levels page:");
|
|
|
|
|
|
|
|
|
|
sb.AppendFormat("\tCode name: {0}", StringHandlers.CToString(page.CodeName)).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tDate: {0}", StringHandlers.CToString(page.Date)).AppendLine();
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xC0 (IBM): Drive Component Revision Levels page
|
|
|
|
|
|
|
|
|
|
#region EVPD Page 0xC1 (IBM): Drive Serial Numbers page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drive Serial Numbers page
|
|
|
|
|
/// Page code 0xC1 (IBM)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_C1_IBM
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
public byte[] ManufacturingSerial;
|
|
|
|
|
public byte[] ReportedSerial;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_C1_IBM? DecodePage_C1_IBM(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xC1) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] != 24) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length != 28) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
|
|
|
|
Page_C1_IBM decoded = new Page_C1_IBM();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
|
|
|
|
|
decoded.ManufacturingSerial = new byte[12];
|
|
|
|
|
decoded.ReportedSerial = new byte[12];
|
|
|
|
|
|
|
|
|
|
Array.Copy(pageResponse, 4, decoded.ManufacturingSerial, 0, 12);
|
|
|
|
|
Array.Copy(pageResponse, 16, decoded.ReportedSerial, 0, 12);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C1_IBM(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_C1_IBM(DecodePage_C1_IBM(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C1_IBM(Page_C1_IBM? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 22:56:16 +01:00
|
|
|
|
|
|
|
|
Page_C1_IBM page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("IBM Drive Serial Numbers page:");
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tManufacturing serial number: {0}", StringHandlers.CToString(page.ManufacturingSerial))
|
|
|
|
|
.AppendLine();
|
|
|
|
|
sb.AppendFormat("\tReported serial number: {0}", StringHandlers.CToString(page.ReportedSerial))
|
|
|
|
|
.AppendLine();
|
2016-10-13 22:56:16 +01:00
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xC1 (IBM): Drive Serial Numbers page
|
|
|
|
|
|
2016-10-13 22:59:48 +01:00
|
|
|
#region EVPD Page 0xB0: Sequential-access device capabilities page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sequential-access device capabilities page
|
|
|
|
|
/// Page code 0xB0
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_B0
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort PageLength;
|
2016-10-14 00:02:42 +01:00
|
|
|
public bool TSMC;
|
2016-10-13 22:59:48 +01:00
|
|
|
public bool WORM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_B0? DecodePage_B0(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-13 22:59:48 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xB0) return null;
|
2016-10-13 22:59:48 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if((pageResponse[2] << 8) + pageResponse[3] + 4 != pageResponse.Length) return null;
|
2016-10-13 22:59:48 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 5) return null;
|
2016-10-13 22:59:48 +01:00
|
|
|
|
|
|
|
|
Page_B0 decoded = new Page_B0();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (ushort)((pageResponse[2] << 8) + pageResponse[3] + 4);
|
|
|
|
|
|
2016-10-14 00:02:42 +01:00
|
|
|
decoded.TSMC = (pageResponse[4] & 0x02) == 0x02;
|
2016-10-13 22:59:48 +01:00
|
|
|
decoded.WORM = (pageResponse[4] & 0x01) == 0x01;
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_B0(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_B0(DecodePage_B0(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_B0(Page_B0? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-13 22:59:48 +01:00
|
|
|
|
|
|
|
|
Page_B0 page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("SCSI Sequential-access Device Capabilities:");
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.WORM) sb.AppendLine("\tDevice supports WORM media");
|
|
|
|
|
if(page.TSMC) sb.AppendLine("\tDevice supports Tape Stream Mirroring");
|
2016-10-13 22:59:48 +01:00
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xB0: Sequential-access device capabilities page
|
|
|
|
|
|
2016-10-14 00:02:42 +01:00
|
|
|
#region EVPD Page 0xB1: Manufacturer-assigned Serial Number page
|
|
|
|
|
public static string DecodePageB1(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] != 0xB1) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != page[3] + 4) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
|
|
|
|
byte[] ascii = new byte[page.Length - 4];
|
|
|
|
|
|
|
|
|
|
Array.Copy(page, 4, ascii, 0, page.Length - 4);
|
|
|
|
|
|
|
|
|
|
return StringHandlers.CToString(ascii).Trim();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xB1: Manufacturer-assigned Serial Number page
|
|
|
|
|
|
|
|
|
|
#region EVPD Page 0xB2: TapeAlert Supported Flags page
|
|
|
|
|
public static ulong DecodePageB2(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return 0;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] != 0xB2) return 0;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != 12) return 0;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
|
|
|
|
byte[] bitmap = new byte[8];
|
|
|
|
|
|
|
|
|
|
Array.Copy(page, 4, bitmap, 0, 8);
|
|
|
|
|
|
|
|
|
|
return BitConverter.ToUInt64(bitmap.Reverse().ToArray(), 0);
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xB2: TapeAlert Supported Flags page
|
2016-10-16 07:29:19 +01:00
|
|
|
|
2016-10-14 00:02:42 +01:00
|
|
|
#region EVPD Page 0xB3: Automation Device Serial Number page
|
|
|
|
|
public static string DecodePageB3(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] != 0xB3) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != page[3] + 4) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
|
|
|
|
byte[] ascii = new byte[page.Length - 4];
|
|
|
|
|
|
|
|
|
|
Array.Copy(page, 4, ascii, 0, page.Length - 4);
|
|
|
|
|
|
|
|
|
|
return StringHandlers.CToString(ascii).Trim();
|
|
|
|
|
}
|
2016-10-14 02:20:00 +01:00
|
|
|
#endregion EVPD Page 0xB3: Automation Device Serial Number page
|
2016-10-14 00:02:42 +01:00
|
|
|
|
|
|
|
|
#region EVPD Page 0xB4: Data Transfer Device Element Address page
|
|
|
|
|
public static string DecodePageB4(byte[] page)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page == null) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page[1] != 0xB3) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(page.Length != page[3] + 4) return null;
|
2016-10-14 00:02:42 +01:00
|
|
|
|
|
|
|
|
byte[] element = new byte[page.Length - 4];
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
2017-12-19 20:33:03 +00:00
|
|
|
foreach(byte b in element) sb.AppendFormat("{0:X2}", b);
|
2016-10-14 00:02:42 +01:00
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xB4: Data Transfer Device Element Address page
|
2016-10-14 02:20:00 +01:00
|
|
|
|
|
|
|
|
#region EVPD Pages 0xC0 to 0xC5 (HP): Drive component revision level pages
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drive component revision level pages
|
|
|
|
|
/// Page codes 0xC0 to 0xC5 (HP)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_C0_to_C5_HP
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
public byte[] Component;
|
|
|
|
|
public byte[] Version;
|
|
|
|
|
public byte[] Date;
|
|
|
|
|
public byte[] Variant;
|
|
|
|
|
public byte[] Copyright;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_C0_to_C5_HP? DecodePage_C0_to_C5_HP(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-14 02:20:00 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xC0 && pageResponse[1] != 0xC1 && pageResponse[1] != 0xC2 &&
|
|
|
|
|
pageResponse[1] != 0xC3 && pageResponse[1] != 0xC4 && pageResponse[1] != 0xC5) return null;
|
2016-10-14 02:20:00 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length < 4) return null;
|
2016-10-14 02:20:00 +01:00
|
|
|
|
|
|
|
|
Page_C0_to_C5_HP decoded = new Page_C0_to_C5_HP();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
decoded.PageCode = pageResponse[1];
|
|
|
|
|
|
2016-10-14 23:05:36 +01:00
|
|
|
if(pageResponse[3] == 92 && pageResponse.Length >= 96)
|
2016-10-14 02:20:00 +01:00
|
|
|
{
|
|
|
|
|
decoded.Component = new byte[26];
|
|
|
|
|
decoded.Version = new byte[19];
|
|
|
|
|
decoded.Date = new byte[24];
|
|
|
|
|
decoded.Variant = new byte[23];
|
|
|
|
|
|
|
|
|
|
Array.Copy(pageResponse, 4, decoded.Component, 0, 26);
|
|
|
|
|
Array.Copy(pageResponse, 30, decoded.Version, 0, 19);
|
|
|
|
|
Array.Copy(pageResponse, 49, decoded.Date, 0, 24);
|
|
|
|
|
Array.Copy(pageResponse, 73, decoded.Variant, 0, 23);
|
|
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
if(pageResponse[4] != pageResponse[3] - 1) return null;
|
|
|
|
|
|
|
|
|
|
List<byte> array = new List<byte>();
|
|
|
|
|
string fwRegExStr =
|
|
|
|
|
"Firmware Rev\\s+=\\s+(?<fw>\\d+\\.\\d+)\\s+Build date\\s+=\\s+(?<date>(\\w|\\d|\\s*.)*)\\s*$";
|
|
|
|
|
string fwcRegExStr = "FW_CONF\\s+=\\s+(?<value>0x[0-9A-Fa-f]{8})\\s*$";
|
|
|
|
|
string servoRegExStr = "Servo\\s+Rev\\s+=\\s+(?<version>\\d+\\.\\d+)\\s*$";
|
|
|
|
|
Regex fwRegEx = new Regex(fwRegExStr);
|
|
|
|
|
Regex fwcRegEx = new Regex(fwcRegExStr);
|
|
|
|
|
Regex servoRegEx = new Regex(servoRegExStr);
|
|
|
|
|
Match fwMatch;
|
|
|
|
|
Match fwcMatch;
|
|
|
|
|
Match servoMatch;
|
|
|
|
|
|
|
|
|
|
for(int pos = 5; pos < pageResponse.Length; pos++)
|
|
|
|
|
if(pageResponse[pos] == 0x00)
|
|
|
|
|
{
|
|
|
|
|
string str = StringHandlers.CToString(array.ToArray());
|
|
|
|
|
fwMatch = fwRegEx.Match(str);
|
|
|
|
|
fwcMatch = fwcRegEx.Match(str);
|
|
|
|
|
servoMatch = servoRegEx.Match(str);
|
|
|
|
|
|
|
|
|
|
if(str.ToLowerInvariant().StartsWith("copyright", StringComparison.Ordinal))
|
|
|
|
|
decoded.Copyright = Encoding.ASCII.GetBytes(str);
|
|
|
|
|
else if(fwMatch.Success)
|
2016-10-14 02:20:00 +01:00
|
|
|
{
|
2017-12-21 06:06:19 +00:00
|
|
|
decoded.Component = Encoding.ASCII.GetBytes("Firmware");
|
|
|
|
|
decoded.Version = Encoding.ASCII.GetBytes(fwMatch.Groups["fw"].Value);
|
|
|
|
|
decoded.Date = Encoding.ASCII.GetBytes(fwMatch.Groups["date"].Value);
|
|
|
|
|
}
|
|
|
|
|
else if(fwcMatch.Success)
|
|
|
|
|
decoded.Variant = Encoding.ASCII.GetBytes(fwMatch.Groups["value"].Value);
|
|
|
|
|
else if(servoMatch.Success)
|
|
|
|
|
{
|
|
|
|
|
decoded.Component = Encoding.ASCII.GetBytes("Servo");
|
|
|
|
|
decoded.Version = Encoding.ASCII.GetBytes(servoMatch.Groups["version"].Value);
|
2016-10-14 02:20:00 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
array = new List<byte>();
|
|
|
|
|
}
|
|
|
|
|
else array.Add(pageResponse[pos]);
|
2016-10-14 02:20:00 +01:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
return decoded;
|
2016-10-14 02:20:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_to_C5_HP(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_C0_to_C5_HP(DecodePage_C0_to_C5_HP(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_to_C5_HP(Page_C0_to_C5_HP? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-14 02:20:00 +01:00
|
|
|
|
|
|
|
|
Page_C0_to_C5_HP page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
switch(page.PageCode)
|
|
|
|
|
{
|
|
|
|
|
case 0xC0:
|
|
|
|
|
sb.AppendLine("HP Drive Firmware Revision Levels page:");
|
|
|
|
|
break;
|
|
|
|
|
case 0xC1:
|
|
|
|
|
sb.AppendLine("HP Drive Hardware Revision Levels page:");
|
|
|
|
|
break;
|
|
|
|
|
case 0xC2:
|
|
|
|
|
sb.AppendLine("HP Drive PCA Revision Levels page:");
|
|
|
|
|
break;
|
|
|
|
|
case 0xC3:
|
|
|
|
|
sb.AppendLine("HP Drive Mechanism Revision Levels page:");
|
|
|
|
|
break;
|
|
|
|
|
case 0xC4:
|
|
|
|
|
sb.AppendLine("HP Drive Head Assembly Revision Levels page:");
|
|
|
|
|
break;
|
|
|
|
|
case 0xC5:
|
|
|
|
|
sb.AppendLine("HP Drive ACI Revision Levels page:");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(page.Component != null && page.Component.Length > 0)
|
|
|
|
|
sb.AppendFormat("\tComponent: {0}", StringHandlers.CToString(page.Component)).AppendLine();
|
|
|
|
|
if(page.Version != null && page.Version.Length > 0)
|
|
|
|
|
sb.AppendFormat("\tVersion: {0}", StringHandlers.CToString(page.Version)).AppendLine();
|
|
|
|
|
if(page.Date != null && page.Date.Length > 0)
|
|
|
|
|
sb.AppendFormat("\tDate: {0}", StringHandlers.CToString(page.Date)).AppendLine();
|
|
|
|
|
if(page.Variant != null && page.Variant.Length > 0)
|
|
|
|
|
sb.AppendFormat("\tVariant: {0}", StringHandlers.CToString(page.Variant)).AppendLine();
|
|
|
|
|
if(page.Copyright != null && page.Copyright.Length > 0)
|
|
|
|
|
sb.AppendFormat("\tCopyright: {0}", StringHandlers.CToString(page.Copyright)).AppendLine();
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Pages 0xC0 to 0xC5 (HP): Drive component revision level pages
|
|
|
|
|
|
2016-10-14 23:05:36 +01:00
|
|
|
#region EVPD Page 0xC0 (Seagate): Firmware numbers page
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Firmware numbers page
|
|
|
|
|
/// Page code 0xC0 (Seagate)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public struct Page_C0_Seagate
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The peripheral qualifier.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralQualifiers PeripheralQualifier;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The type of the peripheral device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PeripheralDeviceTypes PeripheralDeviceType;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The page code.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageCode;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The length of the page.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte PageLength;
|
|
|
|
|
public byte[] ControllerFirmware;
|
|
|
|
|
public byte[] BootFirmware;
|
|
|
|
|
public byte[] ServoFirmware;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page_C0_Seagate? DecodePage_C0_Seagate(byte[] pageResponse)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse == null) return null;
|
2016-10-14 23:05:36 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[1] != 0xC0) return null;
|
2016-10-14 23:05:36 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse[3] != 12) return null;
|
2016-10-14 23:05:36 +01:00
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
if(pageResponse.Length != 16) return null;
|
2016-10-14 23:05:36 +01:00
|
|
|
|
|
|
|
|
Page_C0_Seagate decoded = new Page_C0_Seagate();
|
|
|
|
|
|
|
|
|
|
decoded.PeripheralQualifier = (PeripheralQualifiers)((pageResponse[0] & 0xE0) >> 5);
|
|
|
|
|
decoded.PeripheralDeviceType = (PeripheralDeviceTypes)(pageResponse[0] & 0x1F);
|
|
|
|
|
decoded.PageLength = (byte)(pageResponse[3] + 4);
|
|
|
|
|
decoded.PageCode = pageResponse[1];
|
|
|
|
|
|
|
|
|
|
decoded.ControllerFirmware = new byte[4];
|
|
|
|
|
decoded.BootFirmware = new byte[4];
|
|
|
|
|
decoded.ServoFirmware = new byte[4];
|
|
|
|
|
|
|
|
|
|
Array.Copy(pageResponse, 4, decoded.ControllerFirmware, 0, 4);
|
|
|
|
|
Array.Copy(pageResponse, 8, decoded.BootFirmware, 0, 4);
|
2016-10-16 07:29:19 +01:00
|
|
|
Array.Copy(pageResponse, 12, decoded.ServoFirmware, 0, 4);
|
2016-10-14 23:05:36 +01:00
|
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_Seagate(byte[] pageResponse)
|
|
|
|
|
{
|
|
|
|
|
return PrettifyPage_C0_Seagate(DecodePage_C0_Seagate(pageResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettifyPage_C0_Seagate(Page_C0_Seagate? modePage)
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
if(!modePage.HasValue) return null;
|
2016-10-14 23:05:36 +01:00
|
|
|
|
|
|
|
|
Page_C0_Seagate page = modePage.Value;
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
sb.AppendLine("Seagate Firmware Numbers page:");
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
sb.AppendFormat("\tController firmware version: {0}", StringHandlers.CToString(page.ControllerFirmware))
|
|
|
|
|
.AppendLine();
|
2016-10-14 23:05:36 +01:00
|
|
|
sb.AppendFormat("\tBoot firmware version: {0}", StringHandlers.CToString(page.BootFirmware)).AppendLine();
|
|
|
|
|
sb.AppendFormat("\tServo firmware version: {0}", StringHandlers.CToString(page.ServoFirmware)).AppendLine();
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
#endregion EVPD Page 0xC0 (Seagate): Firmware numbers page
|
2015-10-19 04:32:16 +01:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|