diff --git a/ChangeLog b/ChangeLog index 763fbc160..9e1a7ec7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2016-10-14 Natalia Portillo + + * EVPD.cs: + * Modes.cs: + * Inquiry.cs: Added HP vendor EVPD, MODE pages and INQUIRY + fields. + 2016-10-14 Natalia Portillo * EVPD.cs: Added EVPD pages B1h, B2h, B3h and B4h. diff --git a/SCSI/EVPD.cs b/SCSI/EVPD.cs index 15d082a87..b42c5851c 100644 --- a/SCSI/EVPD.cs +++ b/SCSI/EVPD.cs @@ -35,6 +35,7 @@ using System.Collections.Generic; using System.Text; using System.Security.Policy; using System.Linq; +using System.Text.RegularExpressions; namespace DiscImageChef.Decoders.SCSI { @@ -2256,7 +2257,7 @@ namespace DiscImageChef.Decoders.SCSI return StringHandlers.CToString(ascii).Trim(); } - #region EVPD Page 0xB3: Automation Device Serial Number page + #endregion EVPD Page 0xB3: Automation Device Serial Number page #region EVPD Page 0xB4: Data Transfer Device Element Address page @@ -2280,6 +2281,174 @@ namespace DiscImageChef.Decoders.SCSI } #endregion EVPD Page 0xB4: Data Transfer Device Element Address page + + #region EVPD Pages 0xC0 to 0xC5 (HP): Drive component revision level pages + + /// + /// Drive component revision level pages + /// Page codes 0xC0 to 0xC5 (HP) + /// + public struct Page_C0_to_C5_HP + { + /// + /// The peripheral qualifier. + /// + public PeripheralQualifiers PeripheralQualifier; + /// + /// The type of the peripheral device. + /// + public PeripheralDeviceTypes PeripheralDeviceType; + /// + /// The page code. + /// + public byte PageCode; + /// + /// The length of the page. + /// + 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) + { + if(pageResponse == null) + return null; + + if(pageResponse[1] != 0xC0 && pageResponse[1] != 0xC1 && + pageResponse[1] != 0xC2 && pageResponse[1] != 0xC3 && + pageResponse[1] != 0xC4 && pageResponse[1] != 0xC5) + return null; + + if(pageResponse.Length < 4) + return null; + + 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]; + + if(pageResponse[3] == 92 && pageResponse.Length < 96) + { + 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; + } + + if(pageResponse[4] == pageResponse[3] - 1) + { + List array = new List(); + string fwRegExStr = "Firmware Rev\\s+=\\s+(?\\d+\\.\\d+)\\s+Build date\\s+=\\s+(?(\\w|\\d|\\s*.)*)\\s*$"; + string fwcRegExStr = "FW_CONF\\s+=\\s+(?0x[0-9A-Fa-f]{8})\\s*$"; + string servoRegExStr = "Servo\\s+Rev\\s+=\\s+(?\\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) + { + 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); + } + + array = new List(); + } + else + array.Add(pageResponse[pos]); + } + + return decoded; + } + + return null; + } + + 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) + { + if(!modePage.HasValue) + return null; + + 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 + } } diff --git a/SCSI/Inquiry.cs b/SCSI/Inquiry.cs index cf3690053..778e4110e 100644 --- a/SCSI/Inquiry.cs +++ b/SCSI/Inquiry.cs @@ -33,6 +33,7 @@ using System; using System.Text; using DiscImageChef.Console; +using System.Linq; namespace DiscImageChef.Decoders.SCSI { @@ -142,6 +143,15 @@ namespace DiscImageChef.Decoders.SCSI decoded.ProductRevisionLevel = new byte[4]; Array.Copy(SCSIInquiryResponse, 32, decoded.ProductRevisionLevel, 0, 4); } + if(SCSIInquiryResponse.Length >= 49) + { + // HP + decoded.HPPresent = true; + decoded.HP_WORM |= (SCSIInquiryResponse[40] & 0x01) == 0x01; + decoded.HP_WORMVersion = (byte)((SCSIInquiryResponse[40] & 0x7F) >> 1); + decoded.HP_OBDR = new byte[6]; + Array.Copy(SCSIInquiryResponse, 43, decoded.HP_OBDR, 0, 6); + } if(SCSIInquiryResponse.Length >= 56) { decoded.VendorSpecific = new byte[20]; @@ -1896,7 +1906,7 @@ namespace DiscImageChef.Decoders.SCSI } #region Quantum vendor prettifying - if(response.QuantumPresent && StringHandlers.CToString(response.VendorIdentification).ToLowerInvariant() == "quantum") + if(response.QuantumPresent && StringHandlers.CToString(response.VendorIdentification).ToLowerInvariant().Trim() == "quantum") { sb.AppendLine("Quantum vendor-specific information:"); switch(response.Qt_ProductFamily) @@ -1945,7 +1955,7 @@ namespace DiscImageChef.Decoders.SCSI #endregion Quantum vendor prettifying #region IBM vendor prettifying - if(response.IBMPresent && StringHandlers.CToString(response.VendorIdentification).ToLowerInvariant() == "ibm") + if(response.IBMPresent && StringHandlers.CToString(response.VendorIdentification).ToLowerInvariant().Trim() == "ibm") { sb.AppendLine("IBM vendor-specific information:"); @@ -1961,6 +1971,20 @@ namespace DiscImageChef.Decoders.SCSI } #endregion IBM vendor prettifying + #region HP vendor prettifying + if(response.HPPresent && StringHandlers.CToString(response.VendorIdentification).ToLowerInvariant().Trim() == "hp") + { + sb.AppendLine("HP vendor-specific information:"); + + if(response.HP_WORM) + sb.AppendFormat("Device supports WORM version {0}", response.HP_WORMVersion).AppendLine(); + + byte[] OBDRSign = { 0x24, 0x44, 0x52, 0x2D, 0x31, 0x30}; + if(OBDRSign.SequenceEqual(response.HP_OBDR)) + sb.AppendLine("Device supports Tape Disaster Recovery"); + } + #endregion HP vendor prettifying + #if DEBUG if(response.DeviceTypeModifier != 0) sb.AppendFormat("Vendor's device type modifier = 0x{0:X2}", response.DeviceTypeModifier).AppendLine(); @@ -2365,6 +2389,27 @@ namespace DiscImageChef.Decoders.SCSI /// public byte IBM_OEMSpecific; #endregion IBM vendor unique inquiry data structure + + #region HP vendor unique inquiry data structure + /// + /// Means that the INQUIRY response contains 49 bytes or more, so this data has been filled + /// + public bool HPPresent; + /// + /// WORM version + /// Byte 40 bits 7 to 1 + /// + public byte HP_WORMVersion; + /// + /// WORM supported + /// Byte 40 bit 0 + /// + public bool HP_WORM; + /// + /// Bytes 43 to 48 + /// + public byte[] HP_OBDR; + #endregion HP vendor unique inquiry data structure } #endregion Public structures diff --git a/SCSI/Modes.cs b/SCSI/Modes.cs index 024a63687..92b012d30 100644 --- a/SCSI/Modes.cs +++ b/SCSI/Modes.cs @@ -7768,7 +7768,318 @@ namespace DiscImageChef.Decoders.SCSI return sb.ToString(); } - #endregion IBM Mode Page 0x24: Drive Capabilities Control Mode page + #endregion IBM Mode Page 0x3D: Behaviour Configuration Mode page + + #region HP Mode Page 0x3B: Serial Number Override Mode page + public struct HP_ModePage_3B + { + /// + /// Parameters can be saved + /// + public bool PS; + public byte MSN; + public byte[] SerialNumber; + } + + public static HP_ModePage_3B? DecodeHPModePage_3B(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if((pageResponse[0] & 0x40) == 0x40) + return null; + + if((pageResponse[0] & 0x3F) != 0x3B) + return null; + + if(pageResponse[1] + 2 != pageResponse.Length) + return null; + + if(pageResponse.Length != 16) + return null; + + HP_ModePage_3B decoded = new HP_ModePage_3B(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + decoded.MSN = (byte)(pageResponse[2] & 0x03); + decoded.SerialNumber = new byte[10]; + Array.Copy(pageResponse, 6, decoded.SerialNumber, 0, 10); + + return decoded; + } + + public static string PrettifyHPModePage_3B(byte[] pageResponse) + { + return PrettifyHPModePage_3B(DecodeHPModePage_3B(pageResponse)); + } + + public static string PrettifyHPModePage_3B(HP_ModePage_3B? modePage) + { + if(!modePage.HasValue) + return null; + + HP_ModePage_3B page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("HP Serial Number Override Mode Page:"); + + if(page.PS) + sb.AppendLine("\tParameters can be saved"); + + switch(page.MSN) + { + case 1: + sb.AppendLine("\tSerial number is the manufacturer's default value"); + break; + case 3: + sb.AppendLine("\tSerial number is not the manufacturer's default value"); + break; + } + + sb.AppendFormat("\tSerial number: {0}", StringHandlers.CToString(page.SerialNumber)).AppendLine(); + + return sb.ToString(); + } + + #endregion HP Mode Page 0x3B: Serial Number Override Mode page + + #region HP Mode Page 0x3C: Device Time Mode page + public struct HP_ModePage_3C + { + /// + /// Parameters can be saved + /// + public bool PS; + public bool LT; + public bool WT; + public bool PT; + public ushort CurrentPowerOn; + public uint PowerOnTime; + public bool UTC; + public bool NTP; + public uint WorldTime; + public byte LibraryHours; + public byte LibraryMinutes; + public byte LibrarySeconds; + public uint CumulativePowerOn; + } + + public static HP_ModePage_3C? DecodeHPModePage_3C(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if((pageResponse[0] & 0x40) == 0x40) + return null; + + if((pageResponse[0] & 0x3F) != 0x3C) + return null; + + if(pageResponse[1] + 2 != pageResponse.Length) + return null; + + if(pageResponse.Length != 36) + return null; + + HP_ModePage_3C decoded = new HP_ModePage_3C(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + decoded.LT |= (pageResponse[2] & 0x04) == 0x04; + decoded.WT |= (pageResponse[2] & 0x02) == 0x02; + decoded.PT |= (pageResponse[2] & 0x01) == 0x01; + decoded.CurrentPowerOn = (ushort)((pageResponse[6] << 8) + pageResponse[7]); + decoded.PowerOnTime = (uint)((pageResponse[8] << 24) + (pageResponse[9] << 16) + (pageResponse[10] << 8) + pageResponse[11]); + decoded.UTC |= (pageResponse[14] & 0x02) == 0x02; + decoded.NTP |= (pageResponse[14] & 0x01) == 0x01; + decoded.WorldTime = (uint)((pageResponse[16] << 24) + (pageResponse[17] << 16) + (pageResponse[18] << 8) + pageResponse[19]); + decoded.LibraryHours = pageResponse[23]; + decoded.LibraryMinutes = pageResponse[24]; + decoded.LibrarySeconds = pageResponse[25]; + decoded.CumulativePowerOn = (uint)((pageResponse[32] << 24) + (pageResponse[33] << 16) + (pageResponse[34] << 8) + pageResponse[35]); + + return decoded; + } + + public static string PrettifyHPModePage_3C(byte[] pageResponse) + { + return PrettifyHPModePage_3C(DecodeHPModePage_3C(pageResponse)); + } + + public static string PrettifyHPModePage_3C(HP_ModePage_3C? modePage) + { + if(!modePage.HasValue) + return null; + + HP_ModePage_3C page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("HP Device Time Mode Page:"); + + if(page.PS) + sb.AppendLine("\tParameters can be saved"); + + if(page.PT) + { + sb.AppendFormat("\tDrive has been powered up {0} times", page.CurrentPowerOn); + sb.AppendFormat("\tDrive has been powered up since {0} this time", TimeSpan.FromSeconds(page.PowerOnTime)).AppendLine(); + sb.AppendFormat("\tDrive has been powered up a total of {0}", TimeSpan.FromSeconds(page.CumulativePowerOn)).AppendLine(); + } + + if(page.WT) + { + sb.AppendFormat("\tDrive's date/time is: {0}", DateHandlers.UNIXUnsignedToDateTime(page.WorldTime)).AppendLine(); + if(page.UTC) + sb.AppendLine("\tDrive's time is UTC"); + if(page.NTP) + sb.AppendLine("\tDrive's time is synchronized with a NTP source"); + } + + if(page.LT) + sb.AppendFormat("\tLibrary time is {0}", new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, page.LibraryHours, page.LibraryMinutes, page.LibrarySeconds)).AppendLine(); + + return sb.ToString(); + } + + #endregion HP Mode Page 0x3C: Device Time Mode page + + #region HP Mode Page 0x3D: Extended Reset Mode page + public struct HP_ModePage_3D + { + /// + /// Parameters can be saved + /// + public bool PS; + public byte ResetBehaviour; + } + + public static HP_ModePage_3D? DecodeHPModePage_3D(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if((pageResponse[0] & 0x40) == 0x40) + return null; + + if((pageResponse[0] & 0x3F) != 0x3D) + return null; + + if(pageResponse[1] + 2 != pageResponse.Length) + return null; + + if(pageResponse.Length != 4) + return null; + + HP_ModePage_3D decoded = new HP_ModePage_3D(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + decoded.ResetBehaviour = (byte)(pageResponse[2] & 0x03); + + return decoded; + } + + public static string PrettifyHPModePage_3D(byte[] pageResponse) + { + return PrettifyHPModePage_3D(DecodeHPModePage_3D(pageResponse)); + } + + public static string PrettifyHPModePage_3D(HP_ModePage_3D? modePage) + { + if(!modePage.HasValue) + return null; + + HP_ModePage_3D page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("HP Extended Reset Mode Page:"); + + if(page.PS) + sb.AppendLine("\tParameters can be saved"); + + switch(page.ResetBehaviour) + { + case 0: + sb.AppendLine("\tNormal reset behaviour"); + break; + case 1: + sb.AppendLine("\tDrive will flush and position itself on a LUN or target reset"); + break; + case 2: + sb.AppendLine("\tDrive will maintain position on a LUN or target reset"); + break; + } + + return sb.ToString(); + } + + #endregion HP Mode Page 0x3D: Extended Reset Mode page + + #region HP Mode Page 0x3E: CD-ROM Emulation/Disaster Recovery Mode page + public struct HP_ModePage_3E + { + /// + /// Parameters can be saved + /// + public bool PS; + public bool NonAuto; + public bool CDmode; + } + + public static HP_ModePage_3E? DecodeHPModePage_3E(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if((pageResponse[0] & 0x40) == 0x40) + return null; + + if((pageResponse[0] & 0x3F) != 0x3E) + return null; + + if(pageResponse[1] + 2 != pageResponse.Length) + return null; + + if(pageResponse.Length != 4) + return null; + + HP_ModePage_3E decoded = new HP_ModePage_3E(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + decoded.NonAuto |= (pageResponse[2] & 0x02) == 0x02; + decoded.CDmode|= (pageResponse[2] & 0x01) == 0x01; + + return decoded; + } + + public static string PrettifyHPModePage_3E(byte[] pageResponse) + { + return PrettifyHPModePage_3E(DecodeHPModePage_3E(pageResponse)); + } + + public static string PrettifyHPModePage_3E(HP_ModePage_3E? modePage) + { + if(!modePage.HasValue) + return null; + + HP_ModePage_3E page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("HP CD-ROM Emulation/Disaster Recovery Mode Page:"); + + if(page.PS) + sb.AppendLine("\tParameters can be saved"); + + if(page.CDmode) + sb.AppendLine("\tDrive is emulating a CD-ROM drive"); + else + sb.AppendLine("\tDrive is not emulating a CD-ROM drive"); + if(page.NonAuto) + sb.AppendLine("\tDrive will not exit emulation automatically"); + + return sb.ToString(); + } + + #endregion HP Mode Page 0x3E: CD-ROM Emulation/Disaster Recovery Mode page + } }