mode.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. // Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
  2. // Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>
  3. // Copyright (C) 2019 Landon Rodgers <g.landon.rodgers@gmail.com>
  4. //
  5. // This file is part of SCSI2SD.
  6. //
  7. // SCSI2SD is free software: you can redistribute it and/or modify
  8. // it under the terms of the GNU General Public License as published by
  9. // the Free Software Foundation, either version 3 of the License, or
  10. // (at your option) any later version.
  11. //
  12. // SCSI2SD is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU General Public License
  18. // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
  19. #include "scsi.h"
  20. #include "mode.h"
  21. #include "disk.h"
  22. #include "inquiry.h"
  23. #include "BlueSCSI_mode.h"
  24. #include <string.h>
  25. // "Vendor" defined page which was included by Seagate, and required for\r
  26. // Amiga 500 using DKB SpitFire controller.\r
  27. static const uint8_t OperatingPage[] =
  28. {
  29. 0x00, // Page code
  30. 0x02, // Page length
  31. // Bit 4 = unit attension (0 = on, 1 = off).
  32. // Bit 7 = usage bit, EEPROM life exceeded warning = 1.
  33. 0x80,
  34. // Bit 7 = reserved.
  35. // Bits 0:6: Device type qualifier, as per Inquiry data
  36. 0x00
  37. };
  38. static const uint8_t ReadWriteErrorRecoveryPage[] =
  39. {
  40. 0x01, // Page code
  41. 0x0A, // Page length
  42. // VMS 5.5-2 is very particular regarding the mode page values.
  43. // The required values for a SCSI2/NoTCQ device are:
  44. // AWRE=0 ARRE=0 TB=1 RC=0 EER=? PER=1 DTE=1 DCR=?
  45. // See ftp://www.digiater.nl/openvms/decus/vms94b/net94b/scsi_params_dkdriver.txt
  46. // X-Newsgroups: comp.os.vms
  47. // Subject: Re: VMS 6.1 vs. Seagate Disk Drives
  48. // Message-Id: <32g87h$8q@nntpd.lkg.dec.com>
  49. // From: weber@evms.enet.dec.com (Ralph O. Weber -- OpenVMS AXP)
  50. // Date: 12 Aug 1994 16:32:49 GMT
  51. 0x26,
  52. 0x00, // Don't try recovery algorithm during reads
  53. 0x00, // Correction span 0
  54. 0x00, // Head offset count 0,
  55. 0x00, // Data strobe offset count 0,
  56. 0x00, // Reserved
  57. 0x00, // Don't try recovery algorithm during writes
  58. 0x00, // Reserved
  59. 0x00, 0x00 // Recovery time limit 0 (use default)*/
  60. };
  61. static const uint8_t ReadWriteErrorRecoveryPage_SCSI1[] =
  62. {
  63. 0x01, // Page code
  64. 0x06, // Page length
  65. 0x26,
  66. 0x00, // Don't try recovery algorithm during reads
  67. 0x00, // Correction span 0
  68. 0x00, // Head offset count 0,
  69. 0x00, // Data strobe offset count 0,
  70. 0xFF // Reserved
  71. };
  72. static const uint8_t DisconnectReconnectPage[] =
  73. {
  74. 0x02, // Page code
  75. 0x0E, // Page length
  76. 0, // Buffer full ratio
  77. 0, // Buffer empty ratio
  78. 0x00, 10, // Bus inactivity limit, 100us increments. Allow 1ms.
  79. 0x00, 0x00, // Disconnect time limit
  80. 0x00, 0x00, // Connect time limit
  81. 0x00, 0x00, // Maximum burst size
  82. 0x00 ,// DTDC. Not used.
  83. 0x00, 0x00, 0x00 // Reserved
  84. };
  85. static const uint8_t DisconnectReconnectPage_SCSI1[] =
  86. {
  87. 0x02, // Page code
  88. 0x0A, // Page length
  89. 0, // Buffer full ratio
  90. 0, // Buffer empty ratio
  91. 0x00, 10, // Bus inactivity limit, 100us increments. Allow 1ms.
  92. 0x00, 0x00, // Disconnect time limit
  93. 0x00, 0x00, // Connect time limit
  94. 0x00, 0x00 // Maximum burst size
  95. };
  96. static const uint8_t FormatDevicePage[] =
  97. {
  98. 0x03 | 0x80, // Page code | PS (persist) bit.
  99. 0x16, // Page length
  100. 0x00, 0x00, // Single zone
  101. 0x00, 0x00, // No alternate sectors
  102. 0x00, 0x00, // No alternate tracks
  103. 0x00, 0x00, // No alternate tracks per lun
  104. 0x00, 0x00, // Sectors per track, configurable
  105. 0xFF, 0xFF, // Data bytes per physical sector. Configurable.
  106. 0x00, 0x01, // Interleave
  107. 0x00, 0x00, // Track skew factor
  108. 0x00, 0x00, // Cylinder skew factor
  109. 0xC0, // SSEC(set) HSEC(set) RMB SURF
  110. 0x00, 0x00, 0x00 // Reserved
  111. };
  112. static const uint8_t RigidDiskDriveGeometry[] =
  113. {
  114. 0x04, // Page code
  115. 0x16, // Page length
  116. 0xFF, 0xFF, 0xFF, // Number of cylinders
  117. 0x00, // Number of heads (replaced by configured value)
  118. 0xFF, 0xFF, 0xFF, // Starting cylinder-write precompensation
  119. 0xFF, 0xFF, 0xFF, // Starting cylinder-reduced write current
  120. 0x00, 0x1, // Drive step rate (units of 100ns)
  121. 0x00, 0x00, 0x00, // Landing zone cylinder
  122. 0x00, // RPL
  123. 0x00, // Rotational offset
  124. 0x00, // Reserved
  125. 5400 >> 8, 5400 & 0xFF, // Medium rotation rate (RPM)
  126. 0x00, 0x00 // Reserved
  127. };
  128. static const uint8_t FlexibleDiskDriveGeometry[] =
  129. {
  130. 0x05, // Page code
  131. 0x1E, // Page length
  132. 0x01, 0xF4, // Transfer Rate (500kbits)
  133. 0x01, // heads
  134. 18, // sectors per track
  135. 0x20,0x00, // bytes per sector
  136. 0x00, 80, // Cylinders
  137. 0x00, 0x80, // Write-precomp
  138. 0x00, 0x80, // reduced current,
  139. 0x00, 0x00, // Drive step rate
  140. 0x00, // pulse width
  141. 0x00, 0x00, // Head settle delay
  142. 0x00, // motor on delay
  143. 0x00, // motor off delay
  144. 0x00,
  145. 0x00,
  146. 0x00,
  147. 0x00,
  148. 0x00,
  149. 0x00,
  150. 0x00,
  151. 0x00,
  152. 0x00,
  153. 0x00,
  154. 0x00
  155. };
  156. static const uint8_t RigidDiskDriveGeometry_SCSI1[] =
  157. {
  158. 0x04, // Page code
  159. 0x12, // Page length
  160. 0xFF, 0xFF, 0xFF, // Number of cylinders
  161. 0x00, // Number of heads (replaced by configured value)
  162. 0xFF, 0xFF, 0xFF, // Starting cylinder-write precompensation
  163. 0xFF, 0xFF, 0xFF, // Starting cylinder-reduced write current
  164. 0x00, 0x1, // Drive step rate (units of 100ns)
  165. 0x00, 0x00, 0x00, // Landing zone cylinder
  166. 0x00, // RPL
  167. 0x00, // Rotational offset
  168. 0x00 // Reserved
  169. };
  170. static const uint8_t CachingPage[] =
  171. {
  172. 0x08, // Page Code
  173. 0x0A, // Page length
  174. 0x01, // Read cache disable
  175. 0x00, // No useful rention policy.
  176. 0x00, 0x00, // Pre-fetch always disabled
  177. 0x00, 0x00, // Minimum pre-fetch
  178. 0x00, 0x00, // Maximum pre-fetch
  179. 0x00, 0x00, // Maximum pre-fetch ceiling
  180. };
  181. // Old CCS SCSI-1 cache page
  182. static const uint8_t CCSCachingPage[] =
  183. {
  184. 0x38, // Page Code
  185. 0x0E, // Page length
  186. 0x00, // Read cache disable
  187. 0x00, // Prefetch threshold
  188. 0x00, 0x00, // Max threshold / multiplier
  189. 0x00, 0x00, // Min threshold / multiplier
  190. 0x00, 0x00, // Reserved
  191. 0x00, 0x00,
  192. 0x00, 0x00,
  193. 0x00, 0x00,
  194. };
  195. static const uint8_t ControlModePage[] =
  196. {
  197. 0x0A, // Page code
  198. 0x06, // Page length
  199. 0x00, // No logging
  200. 0x01, // Disable tagged queuing
  201. 0x00, // No async event notifications
  202. 0x00, // Reserved
  203. 0x00, 0x00 // AEN holdoff period.
  204. };
  205. static const uint8_t SequentialDeviceConfigPage[] =
  206. {
  207. 0x10, // page code
  208. 0x0E, // Page length
  209. 0x00, // CAP, CAF, Active Format
  210. 0x00, // Active partition
  211. 0x00, // Write buffer full ratio
  212. 0x00, // Read buffer empty ratio
  213. 0x00,0x01, // Write delay time, in 100ms units
  214. 0x00, // Default gap size
  215. 0x10, // auto-generation of default eod (end of data)
  216. 0x00,0x00,0x00, // buffer-size at early warning
  217. 0x00, // No data compression
  218. 0x00 // reserved
  219. };
  220. // Allow Apple 68k Drive Setup to format this drive.
  221. // Code
  222. static const uint8_t AppleVendorPage[] =
  223. {
  224. 0x30, // Page code
  225. 0x16, // Page length
  226. 'A','P','P','L','E',' ','C','O','M','P','U','T','E','R',',',' ','I','N','C',' ',' ',' '
  227. };
  228. static const uint8_t BlueSCSIVendorPage[] =
  229. {
  230. 0x31, // Page code
  231. 42, // Page length
  232. 'B','l','u','e','S','C','S','I',' ','i','s',' ','t','h','e',' ','B','E','S','T',' ',
  233. 'S','T','O','L','E','N',' ','F','R','O','M',' ','B','L','U','E','S','C','S','I',0x00
  234. };
  235. static const uint8_t IomegaZip100VendorPage[] =
  236. {
  237. 0x2f, // Page Code
  238. 4, // Page Length
  239. 0x5c, 0xf, 0xff, 0xf
  240. };
  241. __attribute__((unused)) static const uint8_t IomegaZip250VendorPage[] =
  242. {
  243. 0x2f, // Page Code
  244. 4, // Page Length
  245. 0x5c, 0xf, 0x3c, 0xf
  246. };
  247. static void pageIn(int pc, int dataIdx, const uint8_t* pageData, int pageLen)
  248. {
  249. memcpy(&scsiDev.data[dataIdx], pageData, pageLen);
  250. if (pc == 0x01) // Mask out (un)changable values
  251. {
  252. memset(&scsiDev.data[dataIdx+2], 0, pageLen - 2);
  253. }
  254. }
  255. static void doModeSense(
  256. int sixByteCmd, int dbd, int pc, int pageCode, int allocLength)
  257. {
  258. ////////////// Mode Parameter Header
  259. ////////////////////////////////////
  260. // Skip the Mode Data Length, we set that last.
  261. int idx = 1;
  262. if (!sixByteCmd) ++idx;
  263. uint8_t mediumType = 0;
  264. uint8_t deviceSpecificParam = 0;
  265. uint8_t density = 0;
  266. switch (scsiDev.target->cfg->deviceType)
  267. {
  268. case S2S_CFG_FIXED:
  269. case S2S_CFG_REMOVEABLE:
  270. mediumType = 0; // We should support various floppy types here!
  271. // Contains cache bits (0) and a Write-Protect bit.
  272. deviceSpecificParam =
  273. (blockDev.state & DISK_WP) ? 0x80 : 0;
  274. density = 0; // reserved for direct access
  275. break;
  276. case S2S_CFG_FLOPPY_14MB:
  277. mediumType = 0x1E; // 90mm/3.5"
  278. deviceSpecificParam =
  279. (blockDev.state & DISK_WP) ? 0x80 : 0;
  280. density = 0; // reserved for direct access
  281. break;
  282. case S2S_CFG_OPTICAL:
  283. mediumType = 0x02; // 120mm CDROM, data only.
  284. deviceSpecificParam = 0;
  285. density = 0x01; // User data only, 2048bytes per sector.
  286. break;
  287. case S2S_CFG_SEQUENTIAL:
  288. mediumType = 0; // reserved
  289. deviceSpecificParam =
  290. (blockDev.state & DISK_WP) ? 0x80 : 0;
  291. density = 0x13; // DAT Data Storage, X3B5/88-185A
  292. break;
  293. case S2S_CFG_MO:
  294. mediumType = 0x03; // Optical reversible or erasable medium
  295. deviceSpecificParam =
  296. (blockDev.state & DISK_WP) ? 0x80 : 0;
  297. density = 0x00; // Default
  298. break;
  299. };
  300. scsiDev.data[idx++] = mediumType;
  301. scsiDev.data[idx++] = deviceSpecificParam;
  302. if (sixByteCmd)
  303. {
  304. if (dbd)
  305. {
  306. scsiDev.data[idx++] = 0; // No block descriptor
  307. }
  308. else
  309. {
  310. // One block descriptor of length 8 bytes.
  311. scsiDev.data[idx++] = 8;
  312. }
  313. }
  314. else
  315. {
  316. scsiDev.data[idx++] = 0; // Reserved
  317. scsiDev.data[idx++] = 0; // Reserved
  318. if (dbd)
  319. {
  320. scsiDev.data[idx++] = 0; // No block descriptor
  321. scsiDev.data[idx++] = 0; // No block descriptor
  322. }
  323. else
  324. {
  325. // One block descriptor of length 8 bytes.
  326. scsiDev.data[idx++] = 0;
  327. scsiDev.data[idx++] = 8;
  328. }
  329. }
  330. ////////////// Block Descriptor
  331. ////////////////////////////////////
  332. if (!dbd)
  333. {
  334. scsiDev.data[idx++] = density;
  335. // Number of blocks
  336. // Zero == all remaining blocks shall have the medium
  337. // characteristics specified.
  338. scsiDev.data[idx++] = 0;
  339. scsiDev.data[idx++] = 0;
  340. scsiDev.data[idx++] = 0;
  341. scsiDev.data[idx++] = 0; // reserved
  342. // Block length
  343. uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
  344. scsiDev.data[idx++] = bytesPerSector >> 16;
  345. scsiDev.data[idx++] = bytesPerSector >> 8;
  346. scsiDev.data[idx++] = bytesPerSector & 0xFF;
  347. }
  348. int pageFound = 0;
  349. if (pageCode == 0x01 || pageCode == 0x3F)
  350. {
  351. pageFound = 1;
  352. if ((scsiDev.compatMode >= COMPAT_SCSI2))
  353. {
  354. pageIn(pc, idx, ReadWriteErrorRecoveryPage, sizeof(ReadWriteErrorRecoveryPage));
  355. idx += sizeof(ReadWriteErrorRecoveryPage);
  356. }
  357. else
  358. {
  359. pageIn(pc, idx, ReadWriteErrorRecoveryPage_SCSI1, sizeof(ReadWriteErrorRecoveryPage_SCSI1));
  360. idx += sizeof(ReadWriteErrorRecoveryPage_SCSI1);
  361. }
  362. }
  363. if (pageCode == 0x02 || pageCode == 0x3F)
  364. {
  365. pageFound = 1;
  366. if ((scsiDev.compatMode >= COMPAT_SCSI2))
  367. {
  368. pageIn(pc, idx, DisconnectReconnectPage, sizeof(DisconnectReconnectPage));
  369. idx += sizeof(DisconnectReconnectPage);
  370. }
  371. else
  372. {
  373. pageIn(pc, idx, DisconnectReconnectPage_SCSI1, sizeof(DisconnectReconnectPage_SCSI1));
  374. idx += sizeof(DisconnectReconnectPage_SCSI1);
  375. }
  376. }
  377. if ((pageCode == 0x03 || pageCode == 0x3F) &&
  378. (scsiDev.target->cfg->deviceType != S2S_CFG_OPTICAL))
  379. {
  380. pageFound = 1;
  381. pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));
  382. if (pc != 0x01)
  383. {
  384. uint16_t sectorsPerTrack = scsiDev.target->cfg->sectorsPerTrack;
  385. scsiDev.data[idx+10] = sectorsPerTrack >> 8;
  386. scsiDev.data[idx+11] = sectorsPerTrack & 0xFF;
  387. // Fill out the configured bytes-per-sector
  388. uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
  389. scsiDev.data[idx+12] = bytesPerSector >> 8;
  390. scsiDev.data[idx+13] = bytesPerSector & 0xFF;
  391. }
  392. else
  393. {
  394. // Set a mask for the changeable values.
  395. scsiDev.data[idx+12] = 0xFF;
  396. scsiDev.data[idx+13] = 0xFF;
  397. }
  398. idx += sizeof(FormatDevicePage);
  399. }
  400. if ((pageCode == 0x04 || pageCode == 0x3F) &&
  401. (scsiDev.target->cfg->deviceType != S2S_CFG_OPTICAL))
  402. {
  403. pageFound = 1;
  404. if ((scsiDev.compatMode >= COMPAT_SCSI2))
  405. {
  406. pageIn(pc, idx, RigidDiskDriveGeometry, sizeof(RigidDiskDriveGeometry));
  407. }
  408. else
  409. {
  410. pageIn(pc, idx, RigidDiskDriveGeometry_SCSI1, sizeof(RigidDiskDriveGeometry_SCSI1));
  411. }
  412. if (pc != 0x01)
  413. {
  414. // Need to fill out the number of cylinders.
  415. uint32_t cyl;
  416. uint8_t head;
  417. uint32_t sector;
  418. LBA2CHS(
  419. getScsiCapacity(
  420. scsiDev.target->cfg->sdSectorStart,
  421. scsiDev.target->liveCfg.bytesPerSector,
  422. scsiDev.target->cfg->scsiSectors),
  423. &cyl,
  424. &head,
  425. &sector,
  426. scsiDev.target->cfg->headsPerCylinder,
  427. scsiDev.target->cfg->sectorsPerTrack);
  428. scsiDev.data[idx+2] = cyl >> 16;
  429. scsiDev.data[idx+3] = cyl >> 8;
  430. scsiDev.data[idx+4] = cyl;
  431. memcpy(&scsiDev.data[idx+6], &scsiDev.data[idx+2], 3);
  432. memcpy(&scsiDev.data[idx+9], &scsiDev.data[idx+2], 3);
  433. scsiDev.data[idx+5] = scsiDev.target->cfg->headsPerCylinder;
  434. }
  435. if ((scsiDev.compatMode >= COMPAT_SCSI2))
  436. {
  437. idx += sizeof(RigidDiskDriveGeometry);
  438. }
  439. else
  440. {
  441. idx += sizeof(RigidDiskDriveGeometry_SCSI1);
  442. }
  443. }
  444. if ((pageCode == 0x05 || pageCode == 0x3F) &&
  445. (scsiDev.target->cfg->deviceType == S2S_CFG_FLOPPY_14MB))
  446. {
  447. pageFound = 1;
  448. pageIn(pc, idx, FlexibleDiskDriveGeometry, sizeof(FlexibleDiskDriveGeometry));
  449. idx += sizeof(FlexibleDiskDriveGeometry);
  450. }
  451. // DON'T output the following pages for SCSI1 hosts. They get upset when
  452. // we have more data to send than the allocation length provided.
  453. // (ie. Try not to output any more pages below this comment)
  454. if ((scsiDev.compatMode >= COMPAT_SCSI2) &&
  455. (pageCode == 0x08 || pageCode == 0x3F))
  456. {
  457. pageFound = 1;
  458. pageIn(pc, idx, CachingPage, sizeof(CachingPage));
  459. idx += sizeof(CachingPage);
  460. }
  461. if ((scsiDev.compatMode >= COMPAT_SCSI2)
  462. && (pageCode == 0x0A || pageCode == 0x3F))
  463. {
  464. pageFound = 1;
  465. pageIn(pc, idx, ControlModePage, sizeof(ControlModePage));
  466. idx += sizeof(ControlModePage);
  467. }
  468. idx += modeSenseCDDevicePage(pc, idx, pageCode, &pageFound);
  469. idx += modeSenseCDAudioControlPage(pc, idx, pageCode, &pageFound);
  470. if ((scsiDev.target->cfg->deviceType == S2S_CFG_ZIP100) &&
  471. (pageCode == 0x2f || pageCode == 0x3f))
  472. {
  473. pageFound = 1;
  474. pageIn(pc, idx, IomegaZip100VendorPage, sizeof(IomegaZip100VendorPage));
  475. idx += sizeof(IomegaZip100VendorPage);
  476. }
  477. if ((scsiDev.target->cfg->deviceType == S2S_CFG_SEQUENTIAL) &&
  478. (pageCode == 0x10 || pageCode == 0x3F))
  479. {
  480. pageFound = 1;
  481. pageIn(
  482. pc,
  483. idx,
  484. SequentialDeviceConfigPage,
  485. sizeof(SequentialDeviceConfigPage));
  486. idx += sizeof(SequentialDeviceConfigPage);
  487. }
  488. idx += modeSenseCDCapabilitiesPage(pc, idx, pageCode, &pageFound);
  489. if ((scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_APPLE) &&
  490. (pageCode == 0x30 || pageCode == 0x3F))
  491. {
  492. pageFound = 1;
  493. pageIn(pc, idx, AppleVendorPage, sizeof(AppleVendorPage));
  494. idx += sizeof(AppleVendorPage);
  495. }
  496. if (pageCode == 0x31 || pageCode == 0x3F)
  497. {
  498. pageFound = 1;
  499. pageIn(pc, idx, BlueSCSIVendorPage, sizeof(BlueSCSIVendorPage));
  500. idx += sizeof(BlueSCSIVendorPage);
  501. }
  502. if (pageCode == 0x38) // Don't send unless requested
  503. {
  504. pageFound = 1;
  505. pageIn(pc, idx, CCSCachingPage, sizeof(CCSCachingPage));
  506. idx += sizeof(CCSCachingPage);
  507. }
  508. // SCSI 2 standard says page 0 is always last.
  509. if (pageCode == 0x00 || pageCode == 0x3F)
  510. {
  511. pageFound = 1;
  512. pageIn(pc, idx, OperatingPage, sizeof(OperatingPage));
  513. // Note inverted logic for the flag.
  514. scsiDev.data[idx+2] =
  515. (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_UNIT_ATTENTION) ? 0x80 : 0x90;
  516. scsiDev.data[idx+3] = getDeviceTypeQualifier();
  517. idx += sizeof(OperatingPage);
  518. }
  519. if (!pageFound)
  520. {
  521. // Unknown Page Code
  522. pageFound = 0;
  523. scsiDev.status = CHECK_CONDITION;
  524. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  525. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  526. scsiDev.phase = STATUS;
  527. }
  528. else
  529. {
  530. // Go back and fill out the mode data length
  531. if (sixByteCmd)
  532. {
  533. // Cannot currently exceed limits. yay
  534. scsiDev.data[0] = idx - 1;
  535. }
  536. else
  537. {
  538. scsiDev.data[0] = ((idx - 2) >> 8);
  539. scsiDev.data[1] = (idx - 2);
  540. }
  541. scsiDev.dataLen = idx > allocLength ? allocLength : idx;
  542. scsiDev.phase = DATA_IN;
  543. }
  544. }
  545. // Callback after the DATA OUT phase is complete.
  546. static void doModeSelect(void)
  547. {
  548. if (scsiDev.status == GOOD) // skip if we've already encountered an error
  549. {
  550. // scsiDev.dataLen bytes are in scsiDev.data
  551. int idx;
  552. int blockDescLen;
  553. if (scsiDev.cdb[0] == 0x55)
  554. {
  555. blockDescLen =
  556. (((uint16_t)scsiDev.data[6]) << 8) |scsiDev.data[7];
  557. idx = 8;
  558. }
  559. else
  560. {
  561. blockDescLen = scsiDev.data[3];
  562. idx = 4;
  563. }
  564. // The unwritten rule. Blocksizes are normally set using the
  565. // block descriptor value, not by changing page 0x03.
  566. if (blockDescLen >= 8)
  567. {
  568. uint32_t bytesPerSector =
  569. (((uint32_t)scsiDev.data[idx+5]) << 16) |
  570. (((uint32_t)scsiDev.data[idx+6]) << 8) |
  571. scsiDev.data[idx+7];
  572. if ((bytesPerSector < MIN_SECTOR_SIZE) ||
  573. (bytesPerSector > MAX_SECTOR_SIZE))
  574. {
  575. goto bad;
  576. }
  577. else
  578. {
  579. scsiDev.target->liveCfg.bytesPerSector = bytesPerSector;
  580. if (bytesPerSector != scsiDev.target->cfg->bytesPerSector)
  581. {
  582. s2s_configSave(scsiDev.target->targetId, bytesPerSector);
  583. }
  584. }
  585. }
  586. idx += blockDescLen;
  587. while (idx < scsiDev.dataLen)
  588. {
  589. // Change from SCSI2SD: if code page is 0x0 (vendor-specific) it
  590. // will not follow the normal page mode format and cannot be
  591. // parsed, but isn't necessarily an error. Instead, just treat it
  592. // as an 'end of data' field and allow normal command completion.
  593. int pageCode = scsiDev.data[idx] & 0x3F;
  594. if (pageCode == 0) goto out;
  595. int pageLen = scsiDev.data[idx + 1];
  596. if (idx + 2 + pageLen > scsiDev.dataLen) goto bad;
  597. switch (pageCode)
  598. {
  599. case 0x03: // Format Device Page
  600. {
  601. if (pageLen != 0x16) goto bad;
  602. // Fill out the configured bytes-per-sector
  603. uint16_t bytesPerSector =
  604. (((uint16_t)scsiDev.data[idx+12]) << 8) |
  605. scsiDev.data[idx+13];
  606. // Sane values only, ok ?
  607. if ((bytesPerSector < MIN_SECTOR_SIZE) ||
  608. (bytesPerSector > MAX_SECTOR_SIZE))
  609. {
  610. goto bad;
  611. }
  612. scsiDev.target->liveCfg.bytesPerSector = bytesPerSector;
  613. if (scsiDev.cdb[1] & 1) // SP Save Pages flag
  614. {
  615. s2s_configSave(scsiDev.target->targetId, bytesPerSector);
  616. }
  617. }
  618. break;
  619. case 0x0E: // CD audio control page
  620. {
  621. if (!modeSelectCDAudioControlPage(pageLen, idx)) goto bad;
  622. }
  623. break;
  624. //default:
  625. // Easiest to just ignore for now. We'll get here when changing
  626. // the SCSI block size via the descriptor header.
  627. }
  628. idx += 2 + pageLen;
  629. }
  630. }
  631. goto out;
  632. bad:
  633. scsiDev.status = CHECK_CONDITION;
  634. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  635. scsiDev.target->sense.asc = INVALID_FIELD_IN_PARAMETER_LIST;
  636. out:
  637. scsiDev.phase = STATUS;
  638. }
  639. int scsiModeCommand()
  640. {
  641. int commandHandled = 1;
  642. uint8_t command = scsiDev.cdb[0];
  643. // We don't currently support the setting of any parameters.
  644. // (ie. no MODE SELECT(6) or MODE SELECT(10) commands)
  645. if (command == 0x1A)
  646. {
  647. // MODE SENSE(6)
  648. int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors
  649. int pc = scsiDev.cdb[2] >> 6; // Page Control
  650. int pageCode = scsiDev.cdb[2] & 0x3F;
  651. int allocLength = scsiDev.cdb[4];
  652. // SCSI1 standard: (CCS X3T9.2/86-52)
  653. // "An Allocation Length of zero indicates that no MODE SENSE data shall
  654. // be transferred. This condition shall not be considered as an error."
  655. doModeSense(1, dbd, pc, pageCode, allocLength);
  656. }
  657. else if (command == 0x5A)
  658. {
  659. // MODE SENSE(10)
  660. int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors
  661. int pc = scsiDev.cdb[2] >> 6; // Page Control
  662. int pageCode = scsiDev.cdb[2] & 0x3F;
  663. int allocLength =
  664. (((uint16_t) scsiDev.cdb[7]) << 8) +
  665. scsiDev.cdb[8];
  666. doModeSense(0, dbd, pc, pageCode, allocLength);
  667. }
  668. else if (command == 0x15)
  669. {
  670. // MODE SELECT(6)
  671. int len = scsiDev.cdb[4];
  672. if (len == 0)
  673. {
  674. // If len == 0, then transfer no data. From the SCSI 2 standard:
  675. // A parameter list length of zero indicates that no data shall
  676. // be transferred. This condition shall not be considered as an
  677. // error.
  678. scsiDev.phase = STATUS;
  679. }
  680. else
  681. {
  682. scsiDev.dataLen = len;
  683. scsiDev.phase = DATA_OUT;
  684. scsiDev.postDataOutHook = doModeSelect;
  685. }
  686. }
  687. else if (command == 0x55)
  688. {
  689. // MODE SELECT(10)
  690. int allocLength = (((uint16_t) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];
  691. if (allocLength == 0)
  692. {
  693. // If len == 0, then transfer no data. From the SCSI 2 standard:
  694. // A parameter list length of zero indicates that no data shall
  695. // be transferred. This condition shall not be considered as an
  696. // error.
  697. scsiDev.phase = STATUS;
  698. }
  699. else
  700. {
  701. scsiDev.dataLen = allocLength;
  702. scsiDev.phase = DATA_OUT;
  703. scsiDev.postDataOutHook = doModeSelect;
  704. }
  705. }
  706. else
  707. {
  708. commandHandled = 0;
  709. }
  710. return commandHandled;
  711. }