From 7d5046608cfe506b5e4062ca060f831828655f81 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 29 Oct 2015 05:15:27 +0000 Subject: [PATCH] Implemented decoding mode page 05h. --- ChangeLog | 5 + SCSI/Modes.cs | 338 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 338 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1fae38699..5a1a4284f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-10-29 Natalia Portillo + + * SCSI/Modes.cs: + Implemented decoding mode page 05h. + 2015-10-29 Natalia Portillo * SCSI/Modes.cs: diff --git a/SCSI/Modes.cs b/SCSI/Modes.cs index b05d8109c..cf66127c2 100644 --- a/SCSI/Modes.cs +++ b/SCSI/Modes.cs @@ -1572,25 +1572,353 @@ namespace DiscImageChef.Decoders.SCSI } if (page.MF) - sb.AppendLine("Pre-fetch values indicate a block multiplier"); + sb.AppendLine("\tPre-fetch values indicate a block multiplier"); if (page.DisablePreFetch == 0) - sb.AppendLine("No pre-fetch will be done"); + sb.AppendLine("\tNo pre-fetch will be done"); else { - sb.AppendFormat("Pre-fetch will be done for READ commands of {0} blocks or less", page.DisablePreFetch).AppendLine(); + sb.AppendFormat("\tPre-fetch will be done for READ commands of {0} blocks or less", page.DisablePreFetch).AppendLine(); if (page.MinimumPreFetch > 0) sb.AppendFormat("At least {0} blocks will be always pre-fetched", page.MinimumPreFetch).AppendLine(); if(page.MaximumPreFetch > 0) - sb.AppendFormat("A maximum of {0} blocks will be pre-fetched", page.MaximumPreFetch).AppendLine(); + sb.AppendFormat("\tA maximum of {0} blocks will be pre-fetched", page.MaximumPreFetch).AppendLine(); if(page.MaximumPreFetchCeiling > 0) - sb.AppendFormat("A maximum of {0} blocks will be pre-fetched even if it is commanded to pre-fetch more", page.MaximumPreFetchCeiling).AppendLine(); + sb.AppendFormat("\tA maximum of {0} blocks will be pre-fetched even if it is commanded to pre-fetch more", page.MaximumPreFetchCeiling).AppendLine(); } return sb.ToString(); } #endregion Mode Page 0x08: Caching page + + #region Mode Page 0x05: Flexible disk page + /// + /// Disconnect-reconnect page + /// Page code 0x05 + /// 32 bytes in SCSI-2 + /// + 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 == null) + return null; + + if ((pageResponse[0] & 0x3F) != 0x05) + return null; + + if (pageResponse[1] + 2 != pageResponse.Length) + return null; + + if (pageResponse.Length < 32) + return null; + + ModePage_05 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) + { + return PrettifyModePage_05(DecodeModePage_05(pageResponse)); + } + + public static string PrettifyModePage_05(ModePage_05? modePage) + { + if (!modePage.HasValue) + return null; + + ModePage_05 page = modePage.Value; + StringBuilder sb = new StringBuilder(); + + sb.AppendLine("SCSI Flexible disk page:"); + + if (page.PS) + sb.AppendLine("\tParameters can be saved"); + + sb.AppendFormat("\tTransfer rate: {0} kbit/s", page.TransferRate).AppendLine(); + sb.AppendFormat("\t{0} heads", page.Heads).AppendLine(); + sb.AppendFormat("\t{0} cylinders", page.Cylinders).AppendLine(); + sb.AppendFormat("\t{0} sectors per track", page.SectorsPerTrack).AppendLine(); + sb.AppendFormat("\t{0} bytes per sector", page.BytesPerSector).AppendLine(); + if(page.WritePrecompCylinder < page.Cylinders) + sb.AppendFormat("\tWrite pre-compensation starts at cylinder {0}", page.WritePrecompCylinder).AppendLine(); + if(page.WriteReduceCylinder < page.Cylinders) + sb.AppendFormat("\tWrite current reduction starts at cylinder {0}", page.WriteReduceCylinder).AppendLine(); + if (page.DriveStepRate > 0) + sb.AppendFormat("\tDrive steps in {0} μs", (uint)page.DriveStepRate * 100).AppendLine(); + if (page.DriveStepPulse > 0) + sb.AppendFormat("\tEach step pulse is {0} ms", page.DriveStepPulse).AppendLine(); + if (page.HeadSettleDelay > 0) + sb.AppendFormat("\tHeads settles in {0} μs", (uint)page.HeadSettleDelay * 100).AppendLine(); + + if(!page.TRDY) + sb.AppendFormat("\tTarget shall wait {0} seconds before attempting to access the medium after motor on is asserted", + (double)page.MotorOnDelay * 10).AppendLine(); + else + sb.AppendFormat("\tTarget shall wait {0} seconds after drive is ready before aborting medium access attemps", + (double)page.MotorOnDelay * 10).AppendLine(); + + if (page.MotorOffDelay != 0xFF) + sb.AppendFormat("\tTarget shall wait {0} seconds before releasing the motor on signal after becoming idle", + (double)page.MotorOffDelay * 10).AppendLine(); + else + sb.AppendLine("\tTarget shall never release the motor on signal"); + + if (page.TRDY) + sb.AppendLine("\tThere is a drive ready signal"); + if (page.SSN) + sb.AppendLine("\tSectors start at 1"); + if (page.MO) + sb.AppendLine("\tThe motor on signal shall remain released"); + + sb.AppendFormat("\tDrive needs to do {0} step pulses per cylinder", page.SPC + 1).AppendLine(); + + if (page.WriteCompensation > 0) + sb.AppendFormat("\tWrite pre-compensation is {0}", page.WriteCompensation).AppendLine(); + if (page.HeadLoadDelay > 0) + sb.AppendFormat("\tHead takes {0} ms to load", page.HeadLoadDelay).AppendLine(); + if (page.HeadUnloadDelay > 0) + sb.AppendFormat("\tHead takes {0} ms to unload", page.HeadUnloadDelay).AppendLine(); + + if (page.MediumRotationRate > 0) + sb.AppendFormat("\tMedium rotates at {0} rpm", page.MediumRotationRate).AppendLine(); + + switch (page.Pin34 & 0x07) + { + case 0: + sb.AppendLine("\tPin 34 is unconnected"); + break; + case 1: + sb.Append("\tPin 34 indicates drive is ready when active "); + if ((page.Pin34 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + case 2: + sb.Append("\tPin 34 indicates disk has changed when active "); + if ((page.Pin34 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + default: + sb.AppendFormat("\tPin 34 indicates unknown function {0} when active ", page.Pin34 & 0x07); + if ((page.Pin34 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + } + + switch (page.Pin4 & 0x07) + { + case 0: + sb.AppendLine("\tPin 4 is unconnected"); + break; + case 1: + sb.Append("\tPin 4 indicates drive is in use when active "); + if ((page.Pin4 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + case 2: + sb.Append("\tPin 4 indicates eject when active "); + if ((page.Pin4 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + case 3: + sb.Append("\tPin 4 indicates head load when active "); + if ((page.Pin4 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + default: + sb.AppendFormat("\tPin 4 indicates unknown function {0} when active ", page.Pin4 & 0x07); + if ((page.Pin4 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + } + + switch (page.Pin2 & 0x07) + { + case 0: + sb.AppendLine("\tPin 2 is unconnected"); + break; + default: + sb.AppendFormat("\tPin 2 indicates unknown function {0} when active ", page.Pin2 & 0x07); + if ((page.Pin2 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + } + + switch (page.Pin1 & 0x07) + { + case 0: + sb.AppendLine("\tPin 1 is unconnected"); + break; + case 1: + sb.Append("\tPin 1 indicates disk change reset when active "); + if ((page.Pin1 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + default: + sb.AppendFormat("\tPin 1 indicates unknown function {0} when active ", page.Pin1 & 0x07); + if ((page.Pin1 & 0x08) == 0x08) + sb.Append("high"); + else + sb.Append("low"); + break; + } + + return sb.ToString(); + } + #endregion Mode Page 0x05: Flexible disk page } }