// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : 11.cs // Author(s) : Natalia Portillo // // Component : Device structures decoders. // // --[ Description ] ---------------------------------------------------------- // // Decodes SCSI MODE PAGE 11h: Medium partition page (1). // // --[ 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-2018 Natalia Portillo // ****************************************************************************/ using System; using System.Text; namespace DiscImageChef.Decoders.SCSI { public static partial class Modes { #region Mode Page 0x11: Medium partition page (1) public enum PartitionSizeUnitOfMeasures : byte { /// /// Partition size is measures in bytes /// Bytes = 0, /// /// Partition size is measures in Kilobytes /// Kilobytes = 1, /// /// Partition size is measures in Megabytes /// Megabytes = 2, /// /// Partition size is 10eUNITS bytes /// Exponential = 3 } public enum MediumFormatRecognitionValues : byte { /// /// Logical unit is incapable of format or partition recognition /// Incapable = 0, /// /// Logical unit is capable of format recognition only /// FormatCapable = 1, /// /// Logical unit is capable of partition recognition only /// PartitionCapable = 2, /// /// Logical unit is capable of both format and partition recognition /// Capable = 3 } /// /// Medium partition page(1) /// Page code 0x11 /// public struct ModePage_11 { /// /// Parameters can be saved /// public bool PS; /// /// Maximum number of additional partitions supported /// public byte MaxAdditionalPartitions; /// /// Number of additional partitions to be defined for a volume /// public byte AdditionalPartitionsDefined; /// /// Device defines partitions based on its fixed definition /// public bool FDP; /// /// Device should divide medium according to the additional partitions defined field using sizes defined by device /// public bool SDP; /// /// Initiator defines number and size of partitions /// public bool IDP; /// /// Defines the unit on which the partition sizes are defined /// public PartitionSizeUnitOfMeasures PSUM; public bool POFM; public bool CLEAR; public bool ADDP; /// /// Defines the capabilities for the unit to recognize media partitions and format /// public MediumFormatRecognitionValues MediumFormatRecognition; public byte PartitionUnits; /// /// Array of partition sizes in units defined above /// public ushort[] PartitionSizes; } public static ModePage_11? DecodeModePage_11(byte[] pageResponse) { if((pageResponse?[0] & 0x40) == 0x40) return null; if((pageResponse?[0] & 0x3F) != 0x11) return null; if(pageResponse[1] + 2 != pageResponse.Length) return null; if(pageResponse.Length < 8) return null; ModePage_11 decoded = new ModePage_11(); decoded.PS |= (pageResponse[0] & 0x80) == 0x80; decoded.MaxAdditionalPartitions = pageResponse[2]; decoded.AdditionalPartitionsDefined = pageResponse[3]; decoded.FDP |= (pageResponse[4] & 0x80) == 0x80; decoded.SDP |= (pageResponse[4] & 0x40) == 0x40; decoded.IDP |= (pageResponse[4] & 0x20) == 0x20; decoded.PSUM = (PartitionSizeUnitOfMeasures)((pageResponse[4] & 0x18) >> 3); decoded.POFM |= (pageResponse[4] & 0x04) == 0x04; decoded.CLEAR |= (pageResponse[4] & 0x02) == 0x02; decoded.ADDP |= (pageResponse[4] & 0x01) == 0x01; decoded.PartitionUnits = (byte)(pageResponse[6] & 0x0F); decoded.MediumFormatRecognition = (MediumFormatRecognitionValues)pageResponse[5]; decoded.PartitionSizes = new ushort[(pageResponse.Length - 8) / 2]; for(int i = 8; i < pageResponse.Length; i += 2) { decoded.PartitionSizes[(i - 8) / 2] = (ushort)(pageResponse[i] << 8); decoded.PartitionSizes[(i - 8) / 2] += pageResponse[i + 1]; } return decoded; } public static string PrettifyModePage_11(byte[] pageResponse) { return PrettifyModePage_11(DecodeModePage_11(pageResponse)); } public static string PrettifyModePage_11(ModePage_11? modePage) { if(!modePage.HasValue) return null; ModePage_11 page = modePage.Value; StringBuilder sb = new StringBuilder(); sb.AppendLine("SCSI medium partition page:"); if(page.PS) sb.AppendLine("\tParameters can be saved"); sb.AppendFormat("\t{0} maximum additional partitions", page.MaxAdditionalPartitions).AppendLine(); sb.AppendFormat("\t{0} additional partitions defined", page.AdditionalPartitionsDefined).AppendLine(); if(page.FDP) sb.AppendLine("\tPartitions are fixed under device definitions"); if(page.SDP) sb.AppendLine("\tNumber of partitions can be defined but their size is defined by the device"); if(page.IDP) sb.AppendLine("\tNumber and size of partitions can be manually defined"); if(page.POFM) sb.AppendLine("\tPartition parameters will not be applied until a FORMAT MEDIUM command is received"); if(!page.CLEAR && !page.ADDP) sb.AppendLine("\tDevice may erase any or all partitions on MODE SELECT for partitioning"); else if(page.CLEAR && !page.ADDP) sb.AppendLine("\tDevice shall erase all partitions on MODE SELECT for partitioning"); else if(!page.CLEAR) sb.AppendLine("\tDevice shall not erase any partition on MODE SELECT for partitioning"); else sb.AppendLine("\tDevice shall erase all partitions differing on size on MODE SELECT for partitioning"); string measure; switch(page.PSUM) { case PartitionSizeUnitOfMeasures.Bytes: sb.AppendLine("\tPartitions are defined in bytes"); measure = "bytes"; break; case PartitionSizeUnitOfMeasures.Kilobytes: sb.AppendLine("\tPartitions are defined in kilobytes"); measure = "kilobytes"; break; case PartitionSizeUnitOfMeasures.Megabytes: sb.AppendLine("\tPartitions are defined in megabytes"); measure = "megabytes"; break; case PartitionSizeUnitOfMeasures.Exponential: sb.AppendFormat("\tPartitions are defined in units of {0} bytes", Math.Pow(10, page.PartitionUnits)) .AppendLine(); measure = $"units of {Math.Pow(10, page.PartitionUnits)} bytes"; break; default: sb.AppendFormat("\tUnknown partition size unit code {0}", (byte)page.PSUM).AppendLine(); measure = "units"; break; } switch(page.MediumFormatRecognition) { case MediumFormatRecognitionValues.Capable: sb.AppendLine("\tDevice is capable of recognizing both medium partitions and format"); break; case MediumFormatRecognitionValues.FormatCapable: sb.AppendLine("\tDevice is capable of recognizing medium format"); break; case MediumFormatRecognitionValues.PartitionCapable: sb.AppendLine("\tDevice is capable of recognizing medium partitions"); break; case MediumFormatRecognitionValues.Incapable: sb.AppendLine("\tDevice is not capable of recognizing neither medium partitions nor format"); break; default: sb.AppendFormat("\tUnknown medium recognition code {0}", (byte)page.MediumFormatRecognition) .AppendLine(); break; } sb.AppendFormat("\tMedium has defined {0} partitions", page.PartitionSizes.Length).AppendLine(); for(int i = 0; i < page.PartitionSizes.Length; i++) if(page.PartitionSizes[i] == 0) if(page.PartitionSizes.Length == 1) sb.AppendLine("\tDevice recognizes one single partition spanning whole medium"); else sb.AppendFormat("\tPartition {0} runs for rest of medium", i).AppendLine(); else sb.AppendFormat("\tPartition {0} is {1} {2} long", i, page.PartitionSizes[i], measure).AppendLine(); return sb.ToString(); } #endregion Mode Page 0x11: Medium partition page (1) } }