From 5799eb8a5ed952c19e4bd15e984a9891e8e489f9 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 13 Oct 2016 22:56:16 +0100 Subject: [PATCH] Added IBM vendor EVPD, MODE pages and INQUIRY fields. --- DiscImageChef.Decoders/ChangeLog | 7 + DiscImageChef.Decoders/SCSI/EVPD.cs | 160 ++++++++++++++ DiscImageChef.Decoders/SCSI/Inquiry.cs | 97 ++++----- DiscImageChef.Decoders/SCSI/Modes.cs | 280 +++++++++++++++++++++++++ DiscImageChef/ChangeLog | 5 + DiscImageChef/Commands/DeviceInfo.cs | 45 ++++ 6 files changed, 542 insertions(+), 52 deletions(-) diff --git a/DiscImageChef.Decoders/ChangeLog b/DiscImageChef.Decoders/ChangeLog index 4cf44388..76338ef1 100644 --- a/DiscImageChef.Decoders/ChangeLog +++ b/DiscImageChef.Decoders/ChangeLog @@ -1,3 +1,10 @@ +2016-10-13 Natalia Portillo + + * EVPD.cs: + * Modes.cs: + * Inquiry.cs: Added IBM vendor EVPD, MODE pages and INQUIRY + fields. + 2016-10-13 Natalia Portillo * Modes.cs: Added MODE PAGE 1Dh diff --git a/DiscImageChef.Decoders/SCSI/EVPD.cs b/DiscImageChef.Decoders/SCSI/EVPD.cs index 388a7624..5dd0ac09 100644 --- a/DiscImageChef.Decoders/SCSI/EVPD.cs +++ b/DiscImageChef.Decoders/SCSI/EVPD.cs @@ -1953,6 +1953,166 @@ namespace DiscImageChef.Decoders.SCSI #endregion EVPD Page 0xDF (Certance): Drive status pages + #region EVPD Page 0xC0 (IBM): Drive Component Revision Levels page + + /// + /// Drive Component Revision Levels page + /// Page code 0xC0 (IBM) + /// + public struct Page_C0_IBM + { + /// + /// 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[] CodeName; + public byte[] Date; + } + + public static Page_C0_IBM? DecodePage_C0_IBM(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if(pageResponse[1] != 0xC0) + return null; + + if(pageResponse[3] != 39) + return null; + + if(pageResponse.Length != 43) + return null; + + 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) + { + if(!modePage.HasValue) + return null; + + 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 + + /// + /// Drive Serial Numbers page + /// Page code 0xC1 (IBM) + /// + public struct Page_C1_IBM + { + /// + /// 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[] ManufacturingSerial; + public byte[] ReportedSerial; + } + + public static Page_C1_IBM? DecodePage_C1_IBM(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if(pageResponse[1] != 0xC1) + return null; + + if(pageResponse[3] != 24) + return null; + + if(pageResponse.Length != 28) + return null; + + 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) + { + if(!modePage.HasValue) + return null; + + Page_C1_IBM page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("IBM Drive Serial Numbers page:"); + + sb.AppendFormat("\tManufacturing serial number: {0}", StringHandlers.CToString(page.ManufacturingSerial)).AppendLine(); + sb.AppendFormat("\tReported serial number: {0}", StringHandlers.CToString(page.ReportedSerial)).AppendLine(); + + return sb.ToString(); + } + + #endregion EVPD Page 0xC1 (IBM): Drive Serial Numbers page + } } diff --git a/DiscImageChef.Decoders/SCSI/Inquiry.cs b/DiscImageChef.Decoders/SCSI/Inquiry.cs index e75f6085..cf369005 100644 --- a/DiscImageChef.Decoders/SCSI/Inquiry.cs +++ b/DiscImageChef.Decoders/SCSI/Inquiry.cs @@ -168,7 +168,14 @@ namespace DiscImageChef.Decoders.SCSI decoded.Qt_LibraryPresent = SCSIInquiryResponse[51] > 0; decoded.Qt_ModuleRevision = new byte[4]; Array.Copy(SCSIInquiryResponse, 52, decoded.Qt_ModuleRevision, 0, 4); + + // IBM + decoded.IBMPresent = true; + decoded.IBM_AutDis |= (SCSIInquiryResponse[36] & 0x01) == 0x01; + decoded.IBM_PerformanceLimit = SCSIInquiryResponse[37]; + decoded.IBM_OEMSpecific = SCSIInquiryResponse[41]; } + if(SCSIInquiryResponse.Length >= 57) { decoded.Reserved3 = (byte)((SCSIInquiryResponse[56] & 0xF0) >> 4); @@ -1892,10 +1899,6 @@ namespace DiscImageChef.Decoders.SCSI if(response.QuantumPresent && StringHandlers.CToString(response.VendorIdentification).ToLowerInvariant() == "quantum") { sb.AppendLine("Quantum vendor-specific information:"); - /// - /// The product family. - /// Byte 36, bits 7 to 5 - /// switch(response.Qt_ProductFamily) { case 0: @@ -1921,65 +1924,17 @@ namespace DiscImageChef.Decoders.SCSI break; } - /// - /// The released firmware. - /// Byte 36, bits 4 to 0 - /// sb.AppendFormat("Release firmware: {0}", response.Qt_ReleasedFirmware).AppendLine(); - /// - /// The . - /// Byte 37 - /// sb.AppendFormat("Firmware version: {0}.{1}", response.Qt_FirmwareMajorVersion, response.Qt_FirmwareMinorVersion).AppendLine(); - /// - /// The EEPROM format major version. - /// Byte 39 - /// sb.AppendFormat("EEPROM format version: {0}.{1}", response.Qt_EEPROMFormatMajorVersion, response.Qt_EEPROMFormatMinorVersion).AppendLine(); - /// - /// The firmware personality. - /// Byte 41 - /// sb.AppendFormat("Firmware personality: {0}", response.Qt_FirmwarePersonality).AppendLine(); - /// - /// The firmware sub personality. - /// Byte 42 - /// sb.AppendFormat("Firmware subpersonality: {0}", response.Qt_FirmwareSubPersonality).AppendLine(); - /// - /// The tape directory format version. - /// Byte 43 - /// sb.AppendFormat("Tape directory format version: {0}", response.Qt_TapeDirectoryFormatVersion).AppendLine(); - /// - /// The controller hardware version. - /// Byte 44 - /// sb.AppendFormat("Controller hardware version: {0}", response.Qt_ControllerHardwareVersion).AppendLine(); - /// - /// The drive EEPROM version. - /// Byte 45 - /// sb.AppendFormat("Drive EEPROM version: {0}", response.Qt_DriveEEPROMVersion).AppendLine(); - /// - /// The drive hardware version. - /// Byte 46 - /// sb.AppendFormat("Drive hardware version: {0}", response.Qt_DriveHardwareVersion).AppendLine(); - /// - /// The media loader firmware version. - /// Byte 47 - /// sb.AppendFormat("Media loader firmware version: {0}", response.Qt_MediaLoaderFirmwareVersion).AppendLine(); - /// - /// The media loader hardware version. - /// Byte 48 - /// sb.AppendFormat("Media loader hardware version: {0}", response.Qt_MediaLoaderHardwareVersion).AppendLine(); - /// - /// The media loader mechanical version. - /// Byte 49 - /// sb.AppendFormat("Media loader mechanical version: {0}", response.Qt_MediaLoaderMechanicalVersion).AppendLine(); if(response.Qt_LibraryPresent) sb.AppendLine("Library is present"); @@ -1989,6 +1944,23 @@ namespace DiscImageChef.Decoders.SCSI } #endregion Quantum vendor prettifying + #region IBM vendor prettifying + if(response.IBMPresent && StringHandlers.CToString(response.VendorIdentification).ToLowerInvariant() == "ibm") + { + sb.AppendLine("IBM vendor-specific information:"); + + if(response.IBM_PerformanceLimit == 0) + sb.AppendLine("Performance is not limited"); + else + sb.AppendFormat("Performance is limited using factor {0}", response.IBM_PerformanceLimit); + + if(response.IBM_AutDis) + sb.AppendLine("Automation is disabled"); + + sb.AppendFormat("IBM OEM Specific Field: {0}", response.IBM_OEMSpecific).AppendLine(); + } + #endregion IBM vendor prettifying + #if DEBUG if(response.DeviceTypeModifier != 0) sb.AppendFormat("Vendor's device type modifier = 0x{0:X2}", response.DeviceTypeModifier).AppendLine(); @@ -2372,6 +2344,27 @@ namespace DiscImageChef.Decoders.SCSI /// public byte[] Qt_ModuleRevision; #endregion Quantum vendor unique inquiry data structure + + #region IBM vendor unique inquiry data structure + /// + /// Means that the INQUIRY response contains 56 bytes or more, so this data has been filled + /// + public bool IBMPresent; + /// + /// Drive is not capable of automation + /// Byte 36 bit 0 + /// + public bool IBM_AutDis; + /// + /// If not zero, limit in MB/s = Max * (this / 256) + /// Byte 37 + /// + public byte IBM_PerformanceLimit; + /// + /// Byte 41 + /// + public byte IBM_OEMSpecific; + #endregion IBM vendor unique inquiry data structure } #endregion Public structures diff --git a/DiscImageChef.Decoders/SCSI/Modes.cs b/DiscImageChef.Decoders/SCSI/Modes.cs index 455998fc..024a6368 100644 --- a/DiscImageChef.Decoders/SCSI/Modes.cs +++ b/DiscImageChef.Decoders/SCSI/Modes.cs @@ -7489,6 +7489,286 @@ namespace DiscImageChef.Decoders.SCSI } #endregion Mode Page 0x1D: Medium Configuration Mode Page + + #region IBM Mode Page 0x24: Drive Capabilities Control Mode page + public struct IBM_ModePage_24 + { + /// + /// Parameters can be saved + /// + public bool PS; + public byte ModeControl; + public byte VelocitySetting; + public bool EncryptionEnabled; + public bool EncryptionCapable; + } + + public static IBM_ModePage_24? DecodeIBMModePage_24(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if((pageResponse[0] & 0x40) == 0x40) + return null; + + if((pageResponse[0] & 0x3F) != 0x24) + return null; + + if(pageResponse[1] + 2 != pageResponse.Length) + return null; + + if(pageResponse.Length != 8) + return null; + + IBM_ModePage_24 decoded = new IBM_ModePage_24(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + decoded.ModeControl = pageResponse[2]; + decoded.VelocitySetting = pageResponse[3]; + decoded.EncryptionEnabled |= (pageResponse[7] & 0x08) == 0x08; + decoded.EncryptionCapable |= (pageResponse[7] & 0x01) == 0x01; + + return decoded; + } + + public static string PrettifyIBMModePage_24(byte[] pageResponse) + { + return PrettifyIBMModePage_24(DecodeIBMModePage_24(pageResponse)); + } + + public static string PrettifyIBMModePage_24(IBM_ModePage_24? modePage) + { + if(!modePage.HasValue) + return null; + + IBM_ModePage_24 page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("IBM Vendor-Specific Control Mode Page:"); + + if(page.PS) + sb.AppendLine("\tParameters can be saved"); + + sb.AppendFormat("\tVendor-specific mode control: {0}", page.ModeControl); + sb.AppendFormat("\tVendor-specific velocity setting: {0}", page.VelocitySetting); + + if(page.EncryptionCapable) + { + sb.AppendLine("\tDrive supports encryption"); + if(page.EncryptionEnabled) + sb.AppendLine("\tDrive has encryption enabled"); + } + + return sb.ToString(); + } + + #endregion IBM Mode Page 0x24: Drive Capabilities Control Mode page + + #region IBM Mode Page 0x2F: Behaviour Configuration Mode page + public struct IBM_ModePage_2F + { + /// + /// Parameters can be saved + /// + public bool PS; + public byte FenceBehaviour; + public byte CleanBehaviour; + public byte WORMEmulation; + public byte SenseDataBehaviour; + public bool CCDM; + public bool DDEOR; + public bool CLNCHK; + public byte FirmwareUpdateBehaviour; + public byte UOE_D; + public byte UOE_F; + public byte UOE_C; + } + + public static IBM_ModePage_2F? DecodeIBMModePage_2F(byte[] pageResponse) + { + if(pageResponse == null) + return null; + + if((pageResponse[0] & 0x40) == 0x40) + return null; + + if((pageResponse[0] & 0x3F) != 0x2F) + return null; + + if(pageResponse[1] + 2 != pageResponse.Length) + return null; + + if(pageResponse.Length < 8) + return null; + + IBM_ModePage_2F decoded = new IBM_ModePage_2F(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + decoded.FenceBehaviour = pageResponse[2]; + decoded.CleanBehaviour = pageResponse[3]; + decoded.WORMEmulation = pageResponse[4]; + decoded.SenseDataBehaviour = pageResponse[5]; + decoded.CCDM |= (pageResponse[6] & 0x04) == 0x04; + decoded.DDEOR |= (pageResponse[6] & 0x02) == 0x02; + decoded.CLNCHK |= (pageResponse[6] & 0x01) == 0x01; + decoded.FirmwareUpdateBehaviour = pageResponse[7]; + decoded.UOE_C = (byte)((pageResponse[8] & 0x30) >> 4); + decoded.UOE_F = (byte)((pageResponse[8] & 0x0C) >> 2); + decoded.UOE_F = ((byte)(pageResponse[8] & 0x03)); + + return decoded; + } + + public static string PrettifyIBMModePage_2F(byte[] pageResponse) + { + return PrettifyIBMModePage_2F(DecodeIBMModePage_2F(pageResponse)); + } + + public static string PrettifyIBMModePage_2F(IBM_ModePage_2F? modePage) + { + if(!modePage.HasValue) + return null; + + IBM_ModePage_2F page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("IBM Behaviour Configuration Mode Page:"); + + if(page.PS) + sb.AppendLine("\tParameters can be saved"); + + switch(page.FenceBehaviour) + { + case 0: + sb.AppendLine("\tFence behaviour is normal"); + break; + case 1: + sb.AppendLine("\tPanic fence behaviour is enabled"); + break; + default: + sb.AppendFormat("\tUnknown fence behaviour code {0}", page.FenceBehaviour).AppendLine(); + break; + } + + switch(page.CleanBehaviour) + { + case 0: + sb.AppendLine("\tCleaning behaviour is normal"); + break; + case 1: + sb.AppendLine("\tDrive will periodically request cleaning"); + break; + default: + sb.AppendFormat("\tUnknown cleaning behaviour code {0}", page.CleanBehaviour).AppendLine(); + break; + } + + switch(page.WORMEmulation) + { + case 0: + sb.AppendLine("\tWORM emulation is disabled"); + break; + case 1: + sb.AppendLine("\tWORM emulation is enabled"); + break; + default: + sb.AppendFormat("\tUnknown WORM emulation code {0}", page.WORMEmulation).AppendLine(); + break; + } + + switch(page.SenseDataBehaviour) + { + case 0: + sb.AppendLine("\tUses 35-bytes sense data"); + break; + case 1: + sb.AppendLine("\tUses 96-bytes sense data"); + break; + default: + sb.AppendFormat("\tUnknown sense data behaviour code {0}", page.WORMEmulation).AppendLine(); + break; + } + + if(page.CLNCHK) + sb.AppendLine("\tDrive will set Check Condition when cleaning is needed"); + if(page.DDEOR) + sb.AppendLine("\tNo deferred error will be reported to a rewind command"); + if(page.CCDM) + sb.AppendLine("\tDrive will set Check Condition when the criteria for Dead Media is met"); + if(page.FirmwareUpdateBehaviour > 0) + sb.AppendLine("\tDrive will not accept downlevel firmware via an FMR tape"); + + if(page.UOE_C == 1) + sb.AppendLine("\tDrive will eject cleaning cartridges on error"); + if(page.UOE_F == 1) + sb.AppendLine("\tDrive will eject firmware cartridges on error"); + if(page.UOE_D == 1) + sb.AppendLine("\tDrive will eject data cartridges on error"); + + return sb.ToString(); + } + + #endregion IBM Mode Page 0x24: Drive Capabilities Control Mode page + + #region IBM Mode Page 0x3D: Behaviour Configuration Mode page + public struct IBM_ModePage_3D + { + /// + /// Parameters can be saved + /// + public bool PS; + public ushort NumberOfWraps; + } + + public static IBM_ModePage_3D? DecodeIBMModePage_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 != 5) + return null; + + IBM_ModePage_3D decoded = new IBM_ModePage_3D(); + + decoded.PS |= (pageResponse[0] & 0x80) == 0x80; + decoded.NumberOfWraps = (ushort)((pageResponse[3] << 8) + pageResponse[4]); + + return decoded; + } + + public static string PrettifyIBMModePage_3D(byte[] pageResponse) + { + return PrettifyIBMModePage_3D(DecodeIBMModePage_3D(pageResponse)); + } + + public static string PrettifyIBMModePage_3D(IBM_ModePage_3D? modePage) + { + if(!modePage.HasValue) + return null; + + IBM_ModePage_3D page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("IBM LEOT Mode Page:"); + + if(page.PS) + sb.AppendLine("\tParameters can be saved"); + + sb.AppendFormat("\t{0} wraps", page.NumberOfWraps).AppendLine(); + + return sb.ToString(); + } + + #endregion IBM Mode Page 0x24: Drive Capabilities Control Mode page } } diff --git a/DiscImageChef/ChangeLog b/DiscImageChef/ChangeLog index 6185b807..84c37774 100644 --- a/DiscImageChef/ChangeLog +++ b/DiscImageChef/ChangeLog @@ -1,3 +1,8 @@ +2016-10-13 Natalia Portillo + + * Commands/DeviceInfo.cs: + Added IBM vendor EVPD, MODE pages and INQUIRY fields. + 2016-10-13 Natalia Portillo * Commands/DeviceInfo.cs: diff --git a/DiscImageChef/Commands/DeviceInfo.cs b/DiscImageChef/Commands/DeviceInfo.cs index 421e86c6..47ed4ebe 100644 --- a/DiscImageChef/Commands/DeviceInfo.cs +++ b/DiscImageChef/Commands/DeviceInfo.cs @@ -310,6 +310,24 @@ namespace DiscImageChef.Commands doWriteFile(options.OutputPrefix, string.Format("_scsi_evpd_{0:X2}h.bin", page), string.Format("SCSI INQUIRY EVPD {0:X2}h", page), inqBuf); } } + else if(page == 0xC0 && StringHandlers.CToString(inq.Value.VendorIdentification).ToLowerInvariant().Trim() == "ibm") + { + sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page); + if(!sense) + { + DicConsole.WriteLine("{0}", Decoders.SCSI.EVPD.PrettifyPage_C0_IBM(inqBuf)); + doWriteFile(options.OutputPrefix, string.Format("_scsi_evpd_{0:X2}h.bin", page), string.Format("SCSI INQUIRY EVPD {0:X2}h", page), inqBuf); + } + } + else if(page == 0xC1 && StringHandlers.CToString(inq.Value.VendorIdentification).ToLowerInvariant().Trim() == "ibm") + { + sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page); + if(!sense) + { + DicConsole.WriteLine("{0}", Decoders.SCSI.EVPD.PrettifyPage_C1_IBM(inqBuf)); + doWriteFile(options.OutputPrefix, string.Format("_scsi_evpd_{0:X2}h.bin", page), string.Format("SCSI INQUIRY EVPD {0:X2}h", page), inqBuf); + } + } else if((page == 0xC0 || page == 0xC1) && StringHandlers.CToString(inq.Value.VendorIdentification).ToLowerInvariant().Trim() == "certance") { sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page); @@ -630,6 +648,15 @@ namespace DiscImageChef.Commands else goto default; + break; + } + case 0x24: + { + if(StringHandlers.CToString(inq.Value.VendorIdentification).Trim() == "IBM") + DicConsole.WriteLine(Decoders.SCSI.Modes.PrettifyIBMModePage_24(page.PageResponse)); + else + goto default; + break; } case 0x2A: @@ -639,6 +666,24 @@ namespace DiscImageChef.Commands else goto default; + break; + } + case 0x2F: + { + if(StringHandlers.CToString(inq.Value.VendorIdentification).Trim() == "IBM") + DicConsole.WriteLine(Decoders.SCSI.Modes.PrettifyIBMModePage_2F(page.PageResponse)); + else + goto default; + + break; + } + case 0x3D: + { + if(StringHandlers.CToString(inq.Value.VendorIdentification).Trim() == "IBM") + DicConsole.WriteLine(Decoders.SCSI.Modes.PrettifyIBMModePage_3D(page.PageResponse)); + else + goto default; + break; } case 0x3E: