mode.c 20 KB

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