// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : 05.cs // Author(s) : Natalia Portillo // // Component : Device structures decoders. // // --[ Description ] ---------------------------------------------------------- // // Decodes SCSI MODE PAGE 05h: Flexible disk 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 0x05: Flexible disk page /// Disconnect-reconnect page Page code 0x05 32 bytes in SCSI-2, SBC-1 public struct ModePage_05 { /// Parameters can be saved public bool PS; /// Data rate of peripheral device on kbit/s public ushort TransferRate; /// Heads for reading and/or writing public byte Heads; /// Sectors per revolution per head public byte SectorsPerTrack; /// Bytes of data per sector public ushort BytesPerSector; /// Cylinders used for data storage public ushort Cylinders; /// Cylinder where write precompensation starts public ushort WritePrecompCylinder; /// Cylinder where write current reduction starts public ushort WriteReduceCylinder; /// Step rate in 100 μs units public ushort DriveStepRate; /// Width of step pulse in μs public byte DriveStepPulse; /// Head settle time in 100 μs units public ushort HeadSettleDelay; /// /// If is true, specified in 1/10s of a second the time waiting for read status before /// aborting medium access. Otherwise, indicates time to way before medimum access after motor on signal is asserted. /// public byte MotorOnDelay; /// /// Time in 1/10s of a second to wait before releasing the motor on signal after an idle condition. 0xFF means to /// never release the signal /// public byte MotorOffDelay; /// Specifies if a signal indicates that the medium is ready to be accessed public bool TRDY; /// If true sectors start with one. Otherwise, they start with zero. public bool SSN; /// If true specifies that motor on shall remain released. public bool MO; /// Number of additional step pulses per cylinder. public byte SPC; /// Write compensation value public byte WriteCompensation; /// Head loading time in ms. public byte HeadLoadDelay; /// Head unloading time in ms. public byte HeadUnloadDelay; /// Description of shugart's bus pin 34 usage public byte Pin34; /// Description of shugart's bus pin 2 usage public byte Pin2; /// Description of shugart's bus pin 4 usage public byte Pin4; /// Description of shugart's bus pin 1 usage public byte Pin1; /// Medium speed in rpm public ushort MediumRotationRate; } public static ModePage_05? DecodeModePage_05(byte[] pageResponse) { if((pageResponse?[0] & 0x40) == 0x40) return null; if((pageResponse?[0] & 0x3F) != 0x05) return null; if(pageResponse[1] + 2 != pageResponse.Length) return null; if(pageResponse.Length < 32) return null; var decoded = new ModePage_05(); decoded.PS |= (pageResponse[0] & 0x80) == 0x80; decoded.TransferRate = (ushort)((pageResponse[2] << 8) + pageResponse[3]); decoded.Heads = pageResponse[4]; decoded.SectorsPerTrack = pageResponse[5]; decoded.BytesPerSector = (ushort)((pageResponse[6] << 8) + pageResponse[7]); decoded.Cylinders = (ushort)((pageResponse[8] << 8) + pageResponse[9]); decoded.WritePrecompCylinder = (ushort)((pageResponse[10] << 8) + pageResponse[11]); decoded.WriteReduceCylinder = (ushort)((pageResponse[12] << 8) + pageResponse[13]); decoded.DriveStepRate = (ushort)((pageResponse[14] << 8) + pageResponse[15]); decoded.DriveStepPulse = pageResponse[16]; decoded.HeadSettleDelay = (ushort)((pageResponse[17] << 8) + pageResponse[18]); decoded.MotorOnDelay = pageResponse[19]; decoded.MotorOffDelay = pageResponse[20]; decoded.TRDY |= (pageResponse[21] & 0x80) == 0x80; decoded.SSN |= (pageResponse[21] & 0x40) == 0x40; decoded.MO |= (pageResponse[21] & 0x20) == 0x20; decoded.SPC = (byte)(pageResponse[22] & 0x0F); decoded.WriteCompensation = pageResponse[23]; decoded.HeadLoadDelay = pageResponse[24]; decoded.HeadUnloadDelay = pageResponse[25]; decoded.Pin34 = (byte)((pageResponse[26] & 0xF0) >> 4); decoded.Pin2 = (byte)(pageResponse[26] & 0x0F); decoded.Pin4 = (byte)((pageResponse[27] & 0xF0) >> 4); decoded.Pin1 = (byte)(pageResponse[27] & 0x0F); decoded.MediumRotationRate = (ushort)((pageResponse[28] << 8) + pageResponse[29]); return decoded; } public static string PrettifyModePage_05(byte[] pageResponse) => PrettifyModePage_05(DecodeModePage_05(pageResponse)); public static string PrettifyModePage_05(ModePage_05? modePage) { if(!modePage.HasValue) return null; ModePage_05 page = modePage.Value; var sb = new StringBuilder(); sb.AppendLine(Localization.SCSI_Flexible_disk_page); if(page.PS) sb.AppendLine("\t" + Localization.Parameters_can_be_saved); sb.AppendFormat("\t" + Localization.Transfer_rate_0_kbits, page.TransferRate).AppendLine(); sb.AppendFormat("\t" + Localization._0_heads, page.Heads).AppendLine(); sb.AppendFormat("\t" + Localization._0_cylinders, page.Cylinders).AppendLine(); sb.AppendFormat("\t" + Localization._0_sectors_per_track, page.SectorsPerTrack).AppendLine(); sb.AppendFormat("\t" + Localization._0_bytes_per_sector, page.BytesPerSector).AppendLine(); if(page.WritePrecompCylinder < page.Cylinders) { sb.AppendFormat("\t" + Localization.Write_pre_compensation_starts_at_cylinder_0, page.WritePrecompCylinder). AppendLine(); } if(page.WriteReduceCylinder < page.Cylinders) { sb.AppendFormat("\t" + Localization.Write_current_reduction_starts_at_cylinder_0, page.WriteReduceCylinder). AppendLine(); } if(page.DriveStepRate > 0) sb.AppendFormat("\t" + Localization.Drive_steps_in_0_μs, (uint)page.DriveStepRate * 100).AppendLine(); if(page.DriveStepPulse > 0) sb.AppendFormat("\t" + Localization.Each_step_pulse_is_0_ms, page.DriveStepPulse).AppendLine(); if(page.HeadSettleDelay > 0) sb.AppendFormat("\t" + Localization.Heads_settles_in_0_μs, (uint)page.HeadSettleDelay * 100).AppendLine(); if(!page.TRDY) { sb. AppendFormat("\t" + Localization.Target_shall_wait_0_seconds_before_attempting_to_access_the_medium_after_motor_on_is_asserted, (double)page.MotorOnDelay * 10). AppendLine(); } else { sb. AppendFormat("\t" + Localization.Target_shall_wait_0_seconds_after_drive_is_ready_before_aborting_medium_access_attempts, (double)page.MotorOnDelay * 10). AppendLine(); } if(page.MotorOffDelay != 0xFF) { sb. AppendFormat("\t" + Localization.Target_shall_wait_0_seconds_before_releasing_the_motor_on_signal_after_becoming_idle, (double)page.MotorOffDelay * 10). AppendLine(); } else sb.AppendLine("\t" + Localization.Target_shall_never_release_the_motor_on_signal); if(page.TRDY) sb.AppendLine("\t" + Localization.There_is_a_drive_ready_signal); if(page.SSN) sb.AppendLine("\t" + Localization.Sectors_start_at_1); if(page.MO) sb.AppendLine("\t" + Localization.The_motor_on_signal_shall_remain_released); sb.AppendFormat("\t" + Localization.Drive_needs_to_do_0_step_pulses_per_cylinder, page.SPC + 1).AppendLine(); if(page.WriteCompensation > 0) sb.AppendFormat("\t" + Localization.Write_pre_compensation_is_0, page.WriteCompensation).AppendLine(); if(page.HeadLoadDelay > 0) sb.AppendFormat("\t" + Localization.Head_takes_0_ms_to_load, page.HeadLoadDelay).AppendLine(); if(page.HeadUnloadDelay > 0) sb.AppendFormat("\t" + Localization.Head_takes_0_ms_to_unload, page.HeadUnloadDelay).AppendLine(); if(page.MediumRotationRate > 0) sb.AppendFormat("\t" + Localization.Medium_rotates_at_0_rpm, page.MediumRotationRate).AppendLine(); switch(page.Pin34 & 0x07) { case 0: sb.AppendLine("\t" + Localization.Pin_34_is_unconnected); break; case 1: sb.Append("\t" + ((page.Pin34 & 0x08) == 0x08 ? Localization.Pin_34_indicates_drive_is_ready_when_active_high : Localization.Pin_34_indicates_drive_is_ready_when_active_low)); break; case 2: sb.Append("\t" + ((page.Pin34 & 0x08) == 0x08 ? Localization.Pin_34_indicates_disk_has_changed_when_active_high : Localization.Pin_34_indicates_disk_has_changed_when_active_low)); break; default: sb.AppendFormat("\t" + ((page.Pin34 & 0x08) == 0x08 ? Localization.Pin_34_indicates_unknown_function_0_when_active_high : Localization.Pin_34_indicates_unknown_function_0_when_active_low), page.Pin34 & 0x07); break; } switch(page.Pin4 & 0x07) { case 0: sb.AppendLine("\t" + Localization.Pin_4_is_unconnected); break; case 1: sb.Append("\t" + ((page.Pin4 & 0x08) == 0x08 ? Localization.Pin_4_indicates_drive_is_in_use_when_active_high : Localization.Pin_4_indicates_drive_is_in_use_when_active_low)); break; case 2: sb.Append("\t" + ((page.Pin4 & 0x08) == 0x08 ? Localization.Pin_4_indicates_eject_when_active_high : Localization.Pin_4_indicates_eject_when_active_low)); break; case 3: sb.Append("\t" + ((page.Pin4 & 0x08) == 0x08 ? Localization.Pin_4_indicates_head_load_when_active_high : Localization.Pin_4_indicates_head_load_when_active_low)); break; default: sb.AppendFormat("\t" + ((page.Pin4 & 0x08) == 0x08 ? Localization.Pin_4_indicates_unknown_function_0_when_active_high : Localization.Pin_4_indicates_unknown_function_0_when_active_low), page.Pin4 & 0x07); break; } switch(page.Pin2 & 0x07) { case 0: sb.AppendLine("\t" + Localization.Pin_2_is_unconnected); break; default: sb.AppendFormat("\t" + ((page.Pin2 & 0x08) == 0x08 ? Localization.Pin_2_indicates_unknown_function_0_when_active_high : Localization.Pin_2_indicates_unknown_function_0_when_active_low), page.Pin2 & 0x07); break; } switch(page.Pin1 & 0x07) { case 0: sb.AppendLine("\t" + Localization.Pin_1_is_unconnected); break; case 1: sb.Append("\t" + ((page.Pin1 & 0x08) == 0x08 ? Localization.Pin_1_indicates_disk_change_reset_when_active_high : Localization.Pin_1_indicates_disk_change_reset_when_active_low)); break; default: sb.AppendFormat("\t" + ((page.Pin1 & 0x08) == 0x08 ? Localization.Pin_1_indicates_unknown_function_0_when_active_high : Localization.Pin_1_indicates_unknown_function_0_when_active_low), page.Pin1 & 0x07); break; } return sb.ToString(); } #endregion Mode Page 0x05: Flexible disk page }