mode.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
  2. //
  3. // This file is part of SCSI2SD.
  4. //
  5. // SCSI2SD is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // SCSI2SD is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
  17. #include "device.h"
  18. #include "scsi.h"
  19. #include "mode.h"
  20. #include "disk.h"
  21. #include <string.h>
  22. static const uint8 DisconnectReconnectPage[] =
  23. {
  24. 0x02, // Page code
  25. 0x0E, // Page length
  26. 0, // Buffer full ratio
  27. 0, // Buffer empty ratio
  28. 0x00, 10, // Bus inactivity limit, 100us increments. Allow 1ms.
  29. 0x00, 0x00, // Disconnect time limit
  30. 0x00, 0x00, // Connect time limit
  31. 0x00, 0x00, // Maximum burst size
  32. 0x00 ,// DTDC. Not used.
  33. 0x00, 0x00, 0x00 // Reserved
  34. };
  35. static const uint8 FormatDevicePage[] =
  36. {
  37. 0x03, // Page code
  38. 0x16, // Page length
  39. 0x00, 0x00, // Single zone
  40. 0x00, 0x00, // No alternate sectors
  41. 0x00, 0x00, // No alternate tracks
  42. 0x00, 0x00, // No alternate tracks per lun
  43. 0x00, SCSI_SECTORS_PER_TRACK, // Sectors per track
  44. SCSI_SECTOR_SIZE >> 8, SCSI_SECTOR_SIZE & 0xFF, // Data bytes per physical sector
  45. 0x00, 0x01, // Interleave
  46. 0x00, 0x00, // Track skew factor
  47. 0x00, 0x00, // Cylinder skew factor
  48. 0xC0, // SSEC(set) HSEC(set) RMB SURF
  49. 0x00, 0x00, 0x00 // Reserved
  50. };
  51. static const uint8 RigidDiskDriveGeometry[] =
  52. {
  53. 0x04, // Page code
  54. 0x16, // Page length
  55. 0xFF, 0xFF, 0xFF, // Number of cylinders
  56. SCSI_HEADS_PER_CYLINDER, // Number of heads
  57. 0xFF, 0xFF, 0xFF, // Starting cylinder-write precompensation
  58. 0xFF, 0xFF, 0xFF, // Starting cylinder-reduced write current
  59. 0x00, 0x1, // Drive step rate (units of 100ns)
  60. 0x00, 0x00, 0x00, // Landing zone cylinder
  61. 0x00, // RPL
  62. 0x00, // Rotational offset
  63. 0x00, // Reserved
  64. 5400 >> 8, 5400 & 0xFF, // Medium rotation rate (RPM)
  65. 0x00, 0x00 // Reserved
  66. };
  67. static const uint8 CachingPage[] =
  68. {
  69. 0x08, // Page Code
  70. 0x0A, // Page length
  71. 0x01, // Read cache disable
  72. 0x00, // No useful rention policy.
  73. 0x00, 0x00, // Pre-fetch always disabled
  74. 0x00, 0x00, // Minimum pre-fetch
  75. 0x00, 0x00, // Maximum pre-fetch
  76. 0x00, 0x00, // Maximum pre-fetch ceiling
  77. };
  78. static const uint8 ControlModePage[] =
  79. {
  80. 0x0A, // Page code
  81. 0x06, // Page length
  82. 0x00, // No logging
  83. 0x01, // Disable tagged queuing
  84. 0x00, // No async event notifications
  85. 0x00, // Reserved
  86. 0x00, 0x00 // AEN holdoff period.
  87. };
  88. // Allow Apple 68k Drive Setup to format this drive.
  89. // Code
  90. // TODO make this string configurable.
  91. static const uint8 AppleVendorPage[] =
  92. {
  93. 0x30, // Page code
  94. 28, // Page length
  95. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  96. 'A','P','P','L','E',' ','C','O','M','P','U','T','E','R',',',' ','I','N','C','.'
  97. };
  98. static void pageIn(int pc, int dataIdx, const uint8* pageData, int pageLen)
  99. {
  100. memcpy(&scsiDev.data[dataIdx], pageData, pageLen);
  101. if (pc == 0x01) // Mask out (un)changable values
  102. {
  103. memset(&scsiDev.data[dataIdx+2], 0, pageLen - 2);
  104. }
  105. }
  106. static void doModeSense(
  107. int sixByteCmd, int dbd, int pc, int pageCode, int allocLength)
  108. {
  109. // TODO Apple HD SC Drive Setup requests Page 3 (FormatDevicePage) with an
  110. // allocLength of 0x20. We need 0x24 if we include a block descriptor, and
  111. // thus return CHECK CONDITION. A block descriptor is optional, so we
  112. // chose to ignore it.
  113. // TODO make configurable
  114. dbd = 1;
  115. if (pc == 0x03) // Saved Values not supported.
  116. {
  117. scsiDev.status = CHECK_CONDITION;
  118. scsiDev.sense.code = ILLEGAL_REQUEST;
  119. scsiDev.sense.asc = SAVING_PARAMETERS_NOT_SUPPORTED;
  120. scsiDev.phase = STATUS;
  121. }
  122. else
  123. {
  124. ////////////// Mode Parameter Header
  125. ////////////////////////////////////
  126. // Skip the Mode Data Length, we set that last.
  127. int idx = 1;
  128. if (!sixByteCmd) ++idx;
  129. scsiDev.data[idx++] = 0; // Medium type. 0 = default
  130. // Device-specific parameter. Contains cache bits (0) and
  131. // a Write-Protect bit.
  132. scsiDev.data[idx++] = (blockDev.state & DISK_WP) ? 0x80 : 0;
  133. if (sixByteCmd)
  134. {
  135. if (dbd)
  136. {
  137. scsiDev.data[idx++] = 0; // No block descriptor
  138. }
  139. else
  140. {
  141. // One block descriptor of length 8 bytes.
  142. scsiDev.data[idx++] = 8;
  143. }
  144. }
  145. else
  146. {
  147. scsiDev.data[idx++] = 0; // Reserved
  148. scsiDev.data[idx++] = 0; // Reserved
  149. if (dbd)
  150. {
  151. scsiDev.data[idx++] = 0; // No block descriptor
  152. scsiDev.data[idx++] = 0; // No block descriptor
  153. }
  154. else
  155. {
  156. // One block descriptor of length 8 bytes.
  157. scsiDev.data[idx++] = 0;
  158. scsiDev.data[idx++] = 8;
  159. }
  160. }
  161. ////////////// Block Descriptor
  162. ////////////////////////////////////
  163. if (!dbd)
  164. {
  165. scsiDev.data[idx++] = 0; // Density code. Reserved for direct-access
  166. // Number of blocks
  167. // Zero == all remaining blocks shall have the medium
  168. // characteristics specified.
  169. scsiDev.data[idx++] = 0;
  170. scsiDev.data[idx++] = 0;
  171. scsiDev.data[idx++] = 0;
  172. scsiDev.data[idx++] = 0; // reserved
  173. // Block length
  174. scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 16;
  175. scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 8;
  176. scsiDev.data[idx++] = SCSI_BLOCK_SIZE & 0xFF;
  177. }
  178. int pageFound = 1;
  179. switch (pageCode)
  180. {
  181. case 0x3F:
  182. // EVERYTHING
  183. case 0x02:
  184. pageIn(pc, idx, DisconnectReconnectPage, sizeof(DisconnectReconnectPage));
  185. idx += sizeof(DisconnectReconnectPage);
  186. if (pageCode != 0x3f) break;
  187. case 0x03:
  188. pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));
  189. idx += sizeof(FormatDevicePage);
  190. if (pageCode != 0x3f) break;
  191. case 0x04:
  192. {
  193. pageIn(pc, idx, RigidDiskDriveGeometry, sizeof(RigidDiskDriveGeometry));
  194. if (pc != 0x01)
  195. {
  196. // Need to fill out the number of cylinders.
  197. uint32 cyl;
  198. uint8 head;
  199. uint32 sector;
  200. LBA2CHS(blockDev.capacity, &cyl, &head, &sector);
  201. scsiDev.data[idx+2] = cyl >> 16;
  202. scsiDev.data[idx+3] = cyl >> 8;
  203. scsiDev.data[idx+4] = cyl;
  204. memcpy(&scsiDev.data[idx+6], &scsiDev.data[idx+2], 3);
  205. memcpy(&scsiDev.data[idx+9], &scsiDev.data[idx+2], 3);
  206. }
  207. idx += sizeof(RigidDiskDriveGeometry);
  208. if (pageCode != 0x3f) break;
  209. }
  210. case 0x08:
  211. pageIn(pc, idx, CachingPage, sizeof(CachingPage));
  212. idx += sizeof(CachingPage);
  213. if (pageCode != 0x3f) break;
  214. case 0x0A:
  215. pageIn(pc, idx, ControlModePage, sizeof(ControlModePage));
  216. idx += sizeof(ControlModePage);
  217. break;
  218. case 0x30:
  219. pageIn(pc, idx, AppleVendorPage, sizeof(AppleVendorPage));
  220. idx += sizeof(AppleVendorPage);
  221. break;
  222. default:
  223. // Unknown Page Code
  224. pageFound = 0;
  225. scsiDev.status = CHECK_CONDITION;
  226. scsiDev.sense.code = ILLEGAL_REQUEST;
  227. scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
  228. scsiDev.phase = STATUS;
  229. }
  230. if (idx > allocLength)
  231. {
  232. // Initiator may not have space to receive results.
  233. scsiDev.status = CHECK_CONDITION;
  234. scsiDev.sense.code = ILLEGAL_REQUEST;
  235. scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
  236. scsiDev.phase = STATUS;
  237. }
  238. else if (pageFound)
  239. {
  240. // Go back and fill out the mode data length
  241. if (sixByteCmd)
  242. {
  243. // Cannot currently exceed limits. yay
  244. scsiDev.data[0] = idx - 1;
  245. }
  246. else
  247. {
  248. scsiDev.data[0] = ((idx - 2) >> 8);
  249. scsiDev.data[1] = (idx - 2);
  250. }
  251. scsiDev.dataLen = idx;
  252. scsiDev.phase = DATA_IN;
  253. }
  254. else
  255. {
  256. // Initiator may not have space to receive results.
  257. scsiDev.status = CHECK_CONDITION;
  258. scsiDev.sense.code = ILLEGAL_REQUEST;
  259. scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
  260. scsiDev.phase = STATUS;
  261. }
  262. }
  263. }
  264. int scsiModeCommand()
  265. {
  266. int commandHandled = 1;
  267. uint8 command = scsiDev.cdb[0];
  268. // We don't currently support the setting of any parameters.
  269. // (ie. no MODE SELECT(6) or MODE SELECT(10) commands)
  270. if (command == 0x1A)
  271. {
  272. // MODE SENSE(6)
  273. int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors
  274. int pc = scsiDev.cdb[2] >> 6; // Page Control
  275. int pageCode = scsiDev.cdb[2] & 0x3F;
  276. int allocLength = scsiDev.cdb[4];
  277. if (allocLength == 0) allocLength = 256;
  278. doModeSense(1, dbd, pc, pageCode, allocLength);
  279. }
  280. else if (command == 0x5A)
  281. {
  282. // MODE SENSE(10)
  283. int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors
  284. int pc = scsiDev.cdb[2] >> 6; // Page Control
  285. int pageCode = scsiDev.cdb[2] & 0x3F;
  286. int allocLength =
  287. (((uint16) scsiDev.cdb[7]) << 8) +
  288. scsiDev.cdb[8];
  289. doModeSense(0, dbd, pc, pageCode, allocLength);
  290. }
  291. else
  292. {
  293. commandHandled = 0;
  294. }
  295. return commandHandled;
  296. }