SCSICommandParser.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>.
  2. * Copyright (C) 2017 Alex Bowden <alex.bowden@outlook.com>.
  3. *
  4. * You can redistribute this program and/or modify it under the terms of
  5. * the GNU Lesser Public License as published by the Free Software Foundation,
  6. * either version 3 of the License, or (at your option) any later version.
  7. */
  8. using System;
  9. using Utilities;
  10. namespace SCSI.Win32
  11. {
  12. public class SCSICommandParser
  13. {
  14. private static bool IsBitSet(byte b, int pos)
  15. {
  16. return (b & (1 << pos)) != 0;
  17. }
  18. public static SCSIDataDirection GetDataDirection(byte[] commandBytes)
  19. {
  20. switch ((SCSIOpCodeName)commandBytes[0])
  21. {
  22. case SCSIOpCodeName.Read16:
  23. case SCSIOpCodeName.ReadReverse16:
  24. case SCSIOpCodeName.Read6:
  25. case SCSIOpCodeName.ReadReverse6:
  26. case SCSIOpCodeName.Read10:
  27. case SCSIOpCodeName.Read12:
  28. case SCSIOpCodeName.ReadBlockLimits:
  29. case SCSIOpCodeName.ReadCapacity10:
  30. case SCSIOpCodeName.ReadDefectData10:
  31. case SCSIOpCodeName.ReadDefectData12:
  32. case SCSIOpCodeName.ReadLong10:
  33. case SCSIOpCodeName.ReadPosition:
  34. case SCSIOpCodeName.RecoverBufferedData:
  35. case SCSIOpCodeName.ReportDensitySupport:
  36. case SCSIOpCodeName.MaintenanceIn:
  37. case SCSIOpCodeName.ServiceActionIn12:
  38. case SCSIOpCodeName.ServiceActionIn16:
  39. case SCSIOpCodeName.Inquiry:
  40. case SCSIOpCodeName.LogSelect10:
  41. case SCSIOpCodeName.LogSense10:
  42. case SCSIOpCodeName.ModeSelect6:
  43. case SCSIOpCodeName.ModeSelect10:
  44. case SCSIOpCodeName.ModeSense6:
  45. case SCSIOpCodeName.ModeSense10:
  46. case SCSIOpCodeName.PersistentReserveIn:
  47. case SCSIOpCodeName.ReadAttribute16:
  48. case SCSIOpCodeName.ReadBuffer10:
  49. case SCSIOpCodeName.ThirdPartyCopyIn:
  50. case SCSIOpCodeName.ReceiveDiagnosticResults:
  51. case SCSIOpCodeName.ReportLUNs:
  52. case SCSIOpCodeName.RequestSense:
  53. case SCSIOpCodeName.SecurityProtocolIn:
  54. return SCSIDataDirection.In;
  55. case SCSIOpCodeName.Erase16:
  56. case SCSIOpCodeName.WriteFilemarks16:
  57. case SCSIOpCodeName.Erase6:
  58. case SCSIOpCodeName.Locate10:
  59. case SCSIOpCodeName.Space6:
  60. case SCSIOpCodeName.WriteFilemarks6:
  61. case SCSIOpCodeName.FormatUnit:
  62. case SCSIOpCodeName.LoadUnload:
  63. case SCSIOpCodeName.Locate16:
  64. case SCSIOpCodeName.Rewind:
  65. case SCSIOpCodeName.SetCapacity:
  66. case SCSIOpCodeName.TestUnitReady:
  67. case SCSIOpCodeName.PreFetch16:
  68. return SCSIDataDirection.NoData;
  69. default:
  70. return SCSIDataDirection.In;
  71. }
  72. }
  73. // Parse CDB allocation length (bytes) based on SPC-3, SSC-3, and SBC-3
  74. public static uint GetCDBTransferLength(byte[] commandBytes, PeripheralDeviceType deviceType)
  75. {
  76. switch ((SCSIOpCodeName)commandBytes[0])
  77. {
  78. case SCSIOpCodeName.RecoverBufferedData: // DATA_IN (2-4)
  79. return BigEndianReader.ReadUInt24(commandBytes, 2);
  80. case SCSIOpCodeName.ReadBlockLimits: // DATA_IN (6 bytes)
  81. return 6;
  82. case SCSIOpCodeName.ReadCapacity10: // DATA_IN (8 bytes)
  83. return 8;
  84. case SCSIOpCodeName.ReadPosition: // DATA_IN (7-8)
  85. if (deviceType == PeripheralDeviceType.SequentialAccessDevice)
  86. {
  87. if (commandBytes[1] == 0x00 || commandBytes[1] == 0x01)
  88. {
  89. return 20;
  90. }
  91. else if (commandBytes[1] == 0x06)
  92. {
  93. return 32;
  94. }
  95. else
  96. {
  97. return BigEndianConverter.ToUInt16(commandBytes, 7);
  98. }
  99. }
  100. else
  101. {
  102. return BigEndianConverter.ToUInt16(commandBytes, 7);
  103. }
  104. case SCSIOpCodeName.ReportDensitySupport: // DATA_IN (7-8)
  105. case SCSIOpCodeName.LogSelect10: // DATA_IN (7-8)
  106. case SCSIOpCodeName.LogSense10: // DATA_IN (7-8)
  107. case SCSIOpCodeName.ModeSelect10: // DATA_IN (7-8)
  108. case SCSIOpCodeName.ModeSense10: // DATA_IN (7-8)
  109. case SCSIOpCodeName.PersistentReserveIn: // DATA_IN (7-8)
  110. case SCSIOpCodeName.ReadLong10: // DATA_IN (7-8)
  111. case SCSIOpCodeName.ReadDefectData10: // DATA_IN (7-8)
  112. return BigEndianConverter.ToUInt16(commandBytes, 7);
  113. case SCSIOpCodeName.ModeSelect6: // DATA_IN (4)
  114. case SCSIOpCodeName.ModeSense6: // DATA_IN (4)
  115. case SCSIOpCodeName.RequestSense: // DATA_IN (4)
  116. return (uint)commandBytes[4];
  117. case SCSIOpCodeName.ReadAttribute16: // DATA_IN (10-13)
  118. case SCSIOpCodeName.ThirdPartyCopyIn: // DATA_IN (10-13) ?
  119. return BigEndianConverter.ToUInt32(commandBytes, 10);
  120. case SCSIOpCodeName.ReadBuffer10: // DATA_IN (6-8)
  121. return BigEndianReader.ReadUInt24(commandBytes, 6);
  122. case SCSIOpCodeName.ReceiveDiagnosticResults: // DATA_IN (3-4)
  123. case SCSIOpCodeName.Inquiry: // DATA_IN (3-4)
  124. return BigEndianConverter.ToUInt16(commandBytes, 3);
  125. case SCSIOpCodeName.ReportLUNs: // DATA_IN (6-9)
  126. case SCSIOpCodeName.SecurityProtocolIn: // DATA_IN (6-9)
  127. case SCSIOpCodeName.ReadDefectData12: // DATA_IN (6-9)
  128. return BigEndianConverter.ToUInt32(commandBytes, 6);
  129. case SCSIOpCodeName.ServiceActionIn16:
  130. if (commandBytes[1] == (byte)ServiceAction.ReadLong16) // DATA_IN (12-13)
  131. {
  132. return BigEndianConverter.ToUInt16(commandBytes, 12);
  133. }
  134. if (commandBytes[1] == (byte)ServiceAction.ReadCapacity16) // DATA_IN (10-13)
  135. {
  136. return BigEndianConverter.ToUInt32(commandBytes, 10);
  137. }
  138. return 512;
  139. default:
  140. // XXX: Need to complete SBC-3 (ex: XDREAD)
  141. return 512;
  142. }
  143. }
  144. public static uint GetDeviceReadTransferLength(byte[] commandBytes, PeripheralDeviceType deviceType, uint blockSize)
  145. {
  146. if (deviceType == PeripheralDeviceType.DirectAccessBlockDevice ||
  147. deviceType == PeripheralDeviceType.CDRomDevice)
  148. {
  149. return GetBlockDeviceReadTransferLength(commandBytes, blockSize);
  150. }
  151. else if (deviceType == PeripheralDeviceType.SequentialAccessDevice)
  152. {
  153. return SCSICommandParser.GetSequentialAccessDeviceReadTransferLength(commandBytes, blockSize);
  154. }
  155. throw new NotSupportedException("Device Type Not Supported!");
  156. }
  157. public static uint GetBlockDeviceReadTransferLength(byte[] commandBytes, uint blockSize)
  158. {
  159. uint transferLength = 0;
  160. switch ((SCSIOpCodeName)commandBytes[0])
  161. {
  162. case SCSIOpCodeName.Read6: // DATA_IN (4)
  163. transferLength = (uint)commandBytes[4];
  164. if (transferLength == 0)
  165. {
  166. transferLength = 256;
  167. }
  168. break;
  169. case SCSIOpCodeName.Read10: // DATA_IN (7-8)
  170. transferLength = BigEndianConverter.ToUInt16(commandBytes, 7);
  171. break;
  172. case SCSIOpCodeName.Read12: // DATA_IN (6-9)
  173. transferLength = BigEndianConverter.ToUInt32(commandBytes, 6);
  174. break;
  175. case SCSIOpCodeName.Read16: // DATA_IN (10-13)
  176. transferLength = BigEndianConverter.ToUInt32(commandBytes, 10);
  177. break;
  178. default:
  179. throw new NotSupportedException("Invalid CDB when parsing READ Transfer Length");
  180. }
  181. return blockSize * transferLength;
  182. }
  183. public static uint GetSequentialAccessDeviceReadTransferLength(byte[] commandBytes, uint blockSize)
  184. {
  185. uint transferLength = 0;
  186. bool fixedBlockSize = false;
  187. switch ((SCSIOpCodeName)commandBytes[0])
  188. {
  189. case SCSIOpCodeName.Read16: // DATA_IN (12-14)
  190. case SCSIOpCodeName.ReadReverse16: // DATA_IN (12-14)
  191. transferLength = BigEndianReader.ReadUInt24(commandBytes, 12);
  192. break;
  193. case SCSIOpCodeName.Read6: // DATA_IN (2-4)
  194. case SCSIOpCodeName.ReadReverse6: // DATA_IN (2-4)
  195. transferLength = BigEndianReader.ReadUInt24(commandBytes, 2);
  196. break;
  197. default:
  198. throw new NotSupportedException("Invalid CDB when parsing READ Transfer Length");
  199. }
  200. fixedBlockSize = IsBitSet(commandBytes[1], 0);
  201. if (fixedBlockSize)
  202. {
  203. return blockSize * transferLength;
  204. }
  205. else
  206. {
  207. /*
  208. * If FIXED == 0, using Variable Block Length
  209. * This means TRANSFER LENGTH is in bytes, not blocks
  210. */
  211. return transferLength;
  212. }
  213. }
  214. }
  215. }