// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : 10_SSC.cs // Author(s) : Natalia Portillo // // Component : Device structures decoders. // // --[ Description ] ---------------------------------------------------------- // // Decodes SCSI MODE PAGE 10h: Device configuration page. // // --[ License ] -------------------------------------------------------------- // // 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 // License, or (at your option) any later version. // // 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. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2023 Natalia Portillo // ****************************************************************************/ using System.Diagnostics.CodeAnalysis; using System.Text; namespace Aaru.Decoders.SCSI; [SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "MemberCanBeInternal")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] public static partial class Modes { #region Mode Page 0x10: Device configuration page /// Device configuration page Page code 0x10 16 bytes in SCSI-2, SSC-1, SSC-2, SSC-3 [SuppressMessage("ReSharper", "UnusedMember.Global")] public struct ModePage_10_SSC { /// Parameters can be saved public bool PS; /// Used in mode select to change partition to one specified in public bool CAP; /// Used in mode select to change format to one specified in public bool CAF; /// Active format, vendor-specific public byte ActiveFormat; /// Current logical partition public byte ActivePartition; /// How full the buffer shall be before writing to medium public byte WriteBufferFullRatio; /// How empty the buffer shall be before reading more data from the medium public byte ReadBufferEmptyRatio; /// Delay in 100 ms before buffered data is forcefully written to the medium even before buffer is full public ushort WriteDelayTime; /// Drive supports recovering data from buffer public bool DBR; /// Medium has block IDs public bool BIS; /// Drive recognizes and reports setmarks public bool RSmk; /// Drive selects best speed public bool AVC; /// If drive should stop pre-reading on filemarks public byte SOCF; /// If set, recovered buffer data is LIFO, otherwise, FIFO public bool RBO; /// Report early warnings public bool REW; /// Inter-block gap public byte GapSize; /// End-of-Data format public byte EODDefined; /// EOD generation enabled public bool EEG; /// Synchronize data to medium on early warning public bool SEW; /// Bytes to reduce buffer size on early warning public uint BufferSizeEarlyWarning; /// Selected data compression algorithm public byte SelectedCompression; /// Soft write protect public bool SWP; /// Associated write protect public bool ASOCWP; /// Persistent write protect public bool PERSWP; /// Permanent write protect public bool PRMWP; public bool BAML; public bool BAM; public byte RewindOnReset; /// How drive shall respond to detection of compromised WORM medium integrity public byte WTRE; /// Respond to commands only if a reservation exists public bool OIR; } public static ModePage_10_SSC? DecodeModePage_10_SSC(byte[] pageResponse) { if((pageResponse?[0] & 0x40) == 0x40) return null; if((pageResponse?[0] & 0x3F) != 0x10) return null; if(pageResponse[1] + 2 != pageResponse.Length) return null; if(pageResponse.Length < 16) return null; var decoded = new ModePage_10_SSC(); decoded.PS |= (pageResponse[0] & 0x80) == 0x80; decoded.CAP |= (pageResponse[2] & 0x40) == 0x40; decoded.CAF |= (pageResponse[2] & 0x20) == 0x20; decoded.ActiveFormat = (byte)(pageResponse[2] & 0x1F); decoded.ActivePartition = pageResponse[3]; decoded.WriteBufferFullRatio = pageResponse[4]; decoded.ReadBufferEmptyRatio = pageResponse[5]; decoded.WriteDelayTime = (ushort)((pageResponse[6] << 8) + pageResponse[7]); decoded.DBR |= (pageResponse[8] & 0x80) == 0x80; decoded.BIS |= (pageResponse[8] & 0x40) == 0x40; decoded.RSmk |= (pageResponse[8] & 0x20) == 0x20; decoded.AVC |= (pageResponse[8] & 0x10) == 0x10; decoded.RBO |= (pageResponse[8] & 0x02) == 0x02; decoded.REW |= (pageResponse[8] & 0x01) == 0x01; decoded.EEG |= (pageResponse[10] & 0x10) == 0x10; decoded.SEW |= (pageResponse[10] & 0x08) == 0x08; decoded.SOCF = (byte)((pageResponse[8] & 0x0C) >> 2); decoded.BufferSizeEarlyWarning = (uint)((pageResponse[11] << 16) + (pageResponse[12] << 8) + pageResponse[13]); decoded.SelectedCompression = pageResponse[14]; decoded.SWP |= (pageResponse[10] & 0x04) == 0x04; decoded.ASOCWP |= (pageResponse[15] & 0x04) == 0x04; decoded.PERSWP |= (pageResponse[15] & 0x02) == 0x02; decoded.PRMWP |= (pageResponse[15] & 0x01) == 0x01; decoded.BAML |= (pageResponse[10] & 0x02) == 0x02; decoded.BAM |= (pageResponse[10] & 0x01) == 0x01; decoded.RewindOnReset = (byte)((pageResponse[15] & 0x18) >> 3); decoded.OIR |= (pageResponse[15] & 0x20) == 0x20; decoded.WTRE = (byte)((pageResponse[15] & 0xC0) >> 6); return decoded; } public static string PrettifyModePage_10_SSC(byte[] pageResponse) => PrettifyModePage_10_SSC(DecodeModePage_10_SSC(pageResponse)); public static string PrettifyModePage_10_SSC(ModePage_10_SSC? modePage) { if(!modePage.HasValue) return null; ModePage_10_SSC page = modePage.Value; var sb = new StringBuilder(); sb.AppendLine(Localization.SCSI_Device_configuration_page); if(page.PS) sb.AppendLine("\t" + Localization.Parameters_can_be_saved); sb.AppendFormat("\t" + Localization.Active_format_0, page.ActiveFormat).AppendLine(); sb.AppendFormat("\t" + Localization.Active_partition_0, page.ActivePartition).AppendLine(); sb.AppendFormat("\t" + Localization.Write_buffer_shall_have_a_full_ratio_of_0_before_being_flushed_to_medium, page.WriteBufferFullRatio). AppendLine(); sb. AppendFormat("\t" + Localization.Read_buffer_shall_have_an_empty_ratio_of_0_before_more_data_is_read_from_medium, page.ReadBufferEmptyRatio). AppendLine(); sb. AppendFormat("\t" + Localization.Drive_will_delay_0_ms_before_buffered_data_is_forcefully_written_to_the_medium_even_before_buffer_is_full, page.WriteDelayTime * 100). AppendLine(); if(page.DBR) { sb.AppendLine("\t" + Localization.Drive_supports_recovering_data_from_buffer); sb.AppendLine(page.RBO ? "\t" + Localization.Recovered_buffer_data_comes_in_LIFO_order : "\t" + Localization.Recovered_buffer_data_comes_in_FIFO_order); } if(page.BIS) sb.AppendLine("\t" + Localization.Medium_supports_block_IDs); if(page.RSmk) sb.AppendLine("\t" + Localization.Drive_reports_setmarks); switch(page.SOCF) { case 0: sb.AppendLine("\t" + Localization.Drive_will_pre_read_until_buffer_is_full); break; case 1: sb.AppendLine("\t" + Localization.Drive_will_pre_read_until_one_filemark_is_detected); break; case 2: sb.AppendLine("\t" + Localization.Drive_will_pre_read_until_two_filemark_is_detected); break; case 3: sb.AppendLine("\t" + Localization.Drive_will_pre_read_until_three_filemark_is_detected); break; } if(page.REW) { sb.AppendLine("\t" + Localization.Drive_reports_early_warnings); if(page.SEW) sb.AppendLine("\t" + Localization.Drive_will_synchronize_buffer_to_medium_on_early_warnings); } switch(page.GapSize) { case 0: break; case 1: sb.AppendLine("\t" + Localization.Inter_block_gap_is_long_enough_to_support_update_in_place); break; case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: sb.AppendFormat("\t" + Localization.Inter_block_gap_is_0_times_the_device_defined_gap_size, page.GapSize). AppendLine(); break; default: sb.AppendFormat("\t" + Localization.Inter_block_gap_is_unknown_value_0, page.GapSize).AppendLine(); break; } if(page.EEG) sb.AppendLine("\t" + Localization.Drive_generates_end_of_data); switch(page.SelectedCompression) { case 0: sb.AppendLine("\t" + Localization.Drive_does_not_use_compression); break; case 1: sb.AppendLine("\t" + Localization.Drive_uses_default_compression); break; default: sb.AppendFormat("\t" + Localization.Drive_uses_unknown_compression_0, page.SelectedCompression). AppendLine(); break; } if(page.SWP) sb.AppendLine("\t" + Localization.Software_write_protect_is_enabled); if(page.ASOCWP) sb.AppendLine("\t" + Localization.Associated_write_protect_is_enabled); if(page.PERSWP) sb.AppendLine("\t" + Localization.Persistent_write_protect_is_enabled); if(page.PRMWP) sb.AppendLine("\t" + Localization.Permanent_write_protect_is_enabled); if(page.BAML) { sb.AppendLine(page.BAM ? "\t" + Localization.Drive_operates_using_explicit_address_mode : "\t" + Localization.Drive_operates_using_implicit_address_mode); } switch(page.RewindOnReset) { case 1: sb.AppendLine("\t" + Localization.Drive_shall_position_to_beginning_of_default_data_partition_on_reset); break; case 2: sb.AppendLine("\t" + Localization.Drive_shall_maintain_its_position_on_reset); break; } switch(page.WTRE) { case 1: sb.AppendLine("\t" + Localization.Drive_will_do_nothing_on_WORM_tampered_medium); break; case 2: sb.AppendLine("\t" + Localization.Drive_will_return_CHECK_CONDITION_on_WORM_tampered_medium); break; } if(page.OIR) sb.AppendLine("\t" + Localization.Drive_will_only_respond_to_commands_if_it_has_received_a_reservation); return sb.ToString(); } #endregion Mode Page 0x10: Device configuration page }