ZuluSCSI_cdrom.cpp 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599
  1. /* Advanced CD-ROM drive emulation.
  2. * Adds a few capabilities on top of the SCSI2SD CD-ROM emulation:
  3. *
  4. * - bin/cue support for support of multiple tracks
  5. * - on the fly image switching
  6. *
  7. * SCSI2SD V6 - Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
  8. * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
  9. *
  10. * This file is licensed under the GPL version 3 or any later version. 
  11. * It is derived from cdrom.c in SCSI2SD V6
  12. *
  13. * https://www.gnu.org/licenses/gpl-3.0.html
  14. * ----
  15. * This program is free software: you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation, either version 3 of the License, or
  18. * (at your option) any later version. 
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23. * GNU General Public License for more details. 
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  27. */
  28. #include <string.h>
  29. #include "ZuluSCSI_cdrom.h"
  30. #include "ZuluSCSI_log.h"
  31. #include "ZuluSCSI_config.h"
  32. #include <CUEParser.h>
  33. #include <assert.h>
  34. #ifdef ENABLE_AUDIO_OUTPUT
  35. #include "ZuluSCSI_audio.h"
  36. #endif
  37. extern "C" {
  38. #include <scsi.h>
  39. }
  40. /******************************************/
  41. /* CDROM positioning information */
  42. /******************************************/
  43. typedef struct {
  44. uint32_t last_lba = 0;
  45. } mechanism_status_t;
  46. static mechanism_status_t mechanism_status[8];
  47. /******************************************/
  48. /* Basic TOC generation without cue sheet */
  49. /******************************************/
  50. static const uint8_t SimpleTOC[] =
  51. {
  52. 0x00, // toc length, MSB
  53. 0x12, // toc length, LSB
  54. 0x01, // First track number
  55. 0x01, // Last track number,
  56. // TRACK 1 Descriptor
  57. 0x00, // reserved
  58. 0x14, // Q sub-channel encodes current position, Digital track
  59. 0x01, // Track 1,
  60. 0x00, // Reserved
  61. 0x00,0x00,0x00,0x00, // Track start sector (LBA)
  62. 0x00, // reserved
  63. 0x14, // Q sub-channel encodes current position, Digital track
  64. 0xAA, // Leadout Track
  65. 0x00, // Reserved
  66. 0x00,0x00,0x00,0x00, // Track start sector (LBA)
  67. };
  68. static const uint8_t LeadoutTOC[] =
  69. {
  70. 0x00, // toc length, MSB
  71. 0x0A, // toc length, LSB
  72. 0x01, // First track number
  73. 0x01, // Last track number,
  74. 0x00, // reserved
  75. 0x14, // Q sub-channel encodes current position, Digital track
  76. 0xAA, // Leadout Track
  77. 0x00, // Reserved
  78. 0x00,0x00,0x00,0x00, // Track start sector (LBA)
  79. };
  80. static const uint8_t SessionTOC[] =
  81. {
  82. 0x00, // toc length, MSB
  83. 0x0A, // toc length, LSB
  84. 0x01, // First session number
  85. 0x01, // Last session number,
  86. // TRACK 1 Descriptor
  87. 0x00, // reserved
  88. 0x14, // Q sub-channel encodes current position, Digital track
  89. 0x01, // First track number in last complete session
  90. 0x00, // Reserved
  91. 0x00,0x00,0x00,0x00 // LBA of first track in last session
  92. };
  93. static const uint8_t FullTOC[] =
  94. {
  95. 0x00, // 0: toc length, MSB
  96. 0x44, // 1: toc length, LSB
  97. 0x01, // 2: First session number
  98. 0x01, // 3: Last session number,
  99. // A0 Descriptor
  100. 0x01, // 4: session number
  101. 0x14, // 5: ADR/Control
  102. 0x00, // 6: TNO
  103. 0xA0, // 7: POINT
  104. 0x00, // 8: Min
  105. 0x00, // 9: Sec
  106. 0x00, // 10: Frame
  107. 0x00, // 11: Zero
  108. 0x01, // 12: First Track number.
  109. 0x00, // 13: Disc type 00 = Mode 1
  110. 0x00, // 14: PFRAME
  111. // A1
  112. 0x01, // 15: session number
  113. 0x14, // 16: ADR/Control
  114. 0x00, // 17: TNO
  115. 0xA1, // 18: POINT
  116. 0x00, // 19: Min
  117. 0x00, // 20: Sec
  118. 0x00, // 21: Frame
  119. 0x00, // 22: Zero
  120. 0x01, // 23: Last Track number
  121. 0x00, // 24: PSEC
  122. 0x00, // 25: PFRAME
  123. // A2
  124. 0x01, // 26: session number
  125. 0x14, // 27: ADR/Control
  126. 0x00, // 28: TNO
  127. 0xA2, // 29: POINT
  128. 0x00, // 30: Min
  129. 0x00, // 31: Sec
  130. 0x00, // 32: Frame
  131. 0x00, // 33: Zero
  132. 0x79, // 34: LEADOUT position BCD
  133. 0x59, // 35: leadout PSEC BCD
  134. 0x74, // 36: leadout PFRAME BCD
  135. // TRACK 1 Descriptor
  136. 0x01, // 37: session number
  137. 0x14, // 38: ADR/Control
  138. 0x00, // 39: TNO
  139. 0x01, // 40: Point
  140. 0x00, // 41: Min
  141. 0x00, // 42: Sec
  142. 0x00, // 43: Frame
  143. 0x00, // 44: Zero
  144. 0x00, // 45: PMIN
  145. 0x00, // 46: PSEC
  146. 0x00, // 47: PFRAME
  147. // b0
  148. 0x01, // 48: session number
  149. 0x54, // 49: ADR/Control
  150. 0x00, // 50: TNO
  151. 0xB1, // 51: POINT
  152. 0x79, // 52: Min BCD
  153. 0x59, // 53: Sec BCD
  154. 0x74, // 54: Frame BCD
  155. 0x00, // 55: Zero
  156. 0x79, // 56: PMIN BCD
  157. 0x59, // 57: PSEC BCD
  158. 0x74, // 58: PFRAME BCD
  159. // c0
  160. 0x01, // 59: session number
  161. 0x54, // 60: ADR/Control
  162. 0x00, // 61: TNO
  163. 0xC0, // 62: POINT
  164. 0x00, // 63: Min
  165. 0x00, // 64: Sec
  166. 0x00, // 65: Frame
  167. 0x00, // 66: Zero
  168. 0x00, // 67: PMIN
  169. 0x00, // 68: PSEC
  170. 0x00 // 69: PFRAME
  171. };
  172. static uint8_t SimpleHeader[] =
  173. {
  174. 0x01, // 2048byte user data, L-EC in 288 byte aux field.
  175. 0x00, // reserved
  176. 0x00, // reserved
  177. 0x00, // reserved
  178. 0x00,0x00,0x00,0x00 // Track start sector (LBA or MSF)
  179. };
  180. static const uint8_t DiscInformation[] =
  181. {
  182. 0x00, // 0: disc info length, MSB
  183. 0x20, // 1: disc info length, LSB
  184. 0x0E, // 2: disc status (finalized, single session non-rewritable)
  185. 0x01, // 3: first track number
  186. 0x01, // 4: number of sessions (LSB)
  187. 0x01, // 5: first track in last session (LSB)
  188. 0x01, // 6: last track in last session (LSB)
  189. 0x00, // 7: format status (0x00 = non-rewritable, no barcode, no disc id)
  190. 0x00, // 8: disc type (0x00 = CD-ROM)
  191. 0x00, // 9: number of sessions (MSB)
  192. 0x00, // 10: first track in last session (MSB)
  193. 0x00, // 11: last track in last session (MSB)
  194. 0x00, // 12: disc ID (MSB)
  195. 0x00, // 13: .
  196. 0x00, // 14: .
  197. 0x00, // 15: disc ID (LSB)
  198. 0x00, // 16: last session lead-in start (MSB)
  199. 0x00, // 17: .
  200. 0x00, // 18: .
  201. 0x00, // 19: last session lead-in start (LSB)
  202. 0x00, // 20: last possible lead-out start (MSB)
  203. 0x00, // 21: .
  204. 0x00, // 22: .
  205. 0x00, // 23: last possible lead-out start (LSB)
  206. 0x00, // 24: disc bar code (MSB)
  207. 0x00, // 25: .
  208. 0x00, // 26: .
  209. 0x00, // 27: .
  210. 0x00, // 28: .
  211. 0x00, // 29: .
  212. 0x00, // 30: .
  213. 0x00, // 31: disc bar code (LSB)
  214. 0x00, // 32: disc application code
  215. 0x00, // 33: number of opc tables
  216. };
  217. // Convert logical block address to CD-ROM time in formatted TOC format
  218. static void LBA2MSF(uint32_t LBA, uint8_t* MSF)
  219. {
  220. MSF[0] = 0; // reserved.
  221. MSF[3] = LBA % 75; // Frames
  222. uint32_t rem = LBA / 75;
  223. MSF[2] = rem % 60; // Seconds
  224. MSF[1] = rem / 60; // Minutes
  225. }
  226. static void doReadTOCSimple(bool MSF, uint8_t track, uint16_t allocationLength)
  227. {
  228. if (track == 0xAA)
  229. {
  230. // 0xAA requests only lead-out track information (reports capacity)
  231. uint32_t len = sizeof(LeadoutTOC);
  232. memcpy(scsiDev.data, LeadoutTOC, len);
  233. uint32_t capacity = getScsiCapacity(
  234. scsiDev.target->cfg->sdSectorStart,
  235. scsiDev.target->liveCfg.bytesPerSector,
  236. scsiDev.target->cfg->scsiSectors);
  237. // Replace start of leadout track
  238. if (MSF)
  239. {
  240. LBA2MSF(capacity, scsiDev.data + 8);
  241. }
  242. else
  243. {
  244. scsiDev.data[8] = capacity >> 24;
  245. scsiDev.data[9] = capacity >> 16;
  246. scsiDev.data[10] = capacity >> 8;
  247. scsiDev.data[11] = capacity;
  248. }
  249. if (len > allocationLength)
  250. {
  251. len = allocationLength;
  252. }
  253. scsiDev.dataLen = len;
  254. scsiDev.phase = DATA_IN;
  255. }
  256. else if (track <= 1)
  257. {
  258. // We only support track 1.
  259. // track 0 means "return all tracks"
  260. uint32_t len = sizeof(SimpleTOC);
  261. memcpy(scsiDev.data, SimpleTOC, len);
  262. uint32_t capacity = getScsiCapacity(
  263. scsiDev.target->cfg->sdSectorStart,
  264. scsiDev.target->liveCfg.bytesPerSector,
  265. scsiDev.target->cfg->scsiSectors);
  266. // Replace start of leadout track
  267. if (MSF)
  268. {
  269. LBA2MSF(capacity, scsiDev.data + 0x10);
  270. }
  271. else
  272. {
  273. scsiDev.data[0x10] = capacity >> 24;
  274. scsiDev.data[0x11] = capacity >> 16;
  275. scsiDev.data[0x12] = capacity >> 8;
  276. scsiDev.data[0x13] = capacity;
  277. }
  278. if (len > allocationLength)
  279. {
  280. len = allocationLength;
  281. }
  282. scsiDev.dataLen = len;
  283. scsiDev.phase = DATA_IN;
  284. }
  285. else
  286. {
  287. scsiDev.status = CHECK_CONDITION;
  288. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  289. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  290. scsiDev.phase = STATUS;
  291. }
  292. }
  293. static void doReadSessionInfoSimple(uint8_t session, uint16_t allocationLength)
  294. {
  295. uint32_t len = sizeof(SessionTOC);
  296. memcpy(scsiDev.data, SessionTOC, len);
  297. if (len > allocationLength)
  298. {
  299. len = allocationLength;
  300. }
  301. scsiDev.dataLen = len;
  302. scsiDev.phase = DATA_IN;
  303. }
  304. static inline uint8_t
  305. fromBCD(uint8_t val)
  306. {
  307. return ((val >> 4) * 10) + (val & 0xF);
  308. }
  309. static void doReadFullTOCSimple(int convertBCD, uint8_t session, uint16_t allocationLength)
  310. {
  311. // We only support session 1.
  312. if (session > 1)
  313. {
  314. scsiDev.status = CHECK_CONDITION;
  315. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  316. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  317. scsiDev.phase = STATUS;
  318. }
  319. else
  320. {
  321. uint32_t len = sizeof(FullTOC);
  322. memcpy(scsiDev.data, FullTOC, len);
  323. if (convertBCD)
  324. {
  325. int descriptor = 4;
  326. while (descriptor < len)
  327. {
  328. int i;
  329. for (i = 0; i < 7; ++i)
  330. {
  331. scsiDev.data[descriptor + i] =
  332. fromBCD(scsiDev.data[descriptor + 4 + i]);
  333. }
  334. descriptor += 11;
  335. }
  336. }
  337. if (len > allocationLength)
  338. {
  339. len = allocationLength;
  340. }
  341. scsiDev.dataLen = len;
  342. scsiDev.phase = DATA_IN;
  343. }
  344. }
  345. void doReadHeaderSimple(bool MSF, uint32_t lba, uint16_t allocationLength)
  346. {
  347. uint32_t len = sizeof(SimpleHeader);
  348. memcpy(scsiDev.data, SimpleHeader, len);
  349. if (len > allocationLength)
  350. {
  351. len = allocationLength;
  352. }
  353. scsiDev.dataLen = len;
  354. scsiDev.phase = DATA_IN;
  355. }
  356. void doReadDiscInformationSimple(uint16_t allocationLength)
  357. {
  358. uint32_t len = sizeof(DiscInformation);
  359. memcpy(scsiDev.data, DiscInformation, len);
  360. if (len > allocationLength)
  361. {
  362. len = allocationLength;
  363. }
  364. scsiDev.dataLen = len;
  365. scsiDev.phase = DATA_IN;
  366. }
  367. /*********************************/
  368. /* TOC generation from cue sheet */
  369. /*********************************/
  370. // Fetch track info based on LBA
  371. static void getTrackFromLBA(CUEParser &parser, uint32_t lba, CUETrackInfo *result)
  372. {
  373. // Track info in case we have no .cue file
  374. result->file_mode = CUEFile_BINARY;
  375. result->track_mode = CUETrack_MODE1_2048;
  376. result->sector_length = 2048;
  377. result->track_number = 1;
  378. const CUETrackInfo *tmptrack;
  379. while ((tmptrack = parser.next_track()) != NULL)
  380. {
  381. if (tmptrack->track_start <= lba)
  382. {
  383. *result = *tmptrack;
  384. }
  385. else
  386. {
  387. break;
  388. }
  389. }
  390. }
  391. // Format track info read from cue sheet into the format used by ReadTOC command.
  392. // Refer to T10/1545-D MMC-4 Revision 5a, "Response Format 0000b: Formatted TOC"
  393. static void formatTrackInfo(const CUETrackInfo *track, uint8_t *dest, bool use_MSF_time)
  394. {
  395. uint8_t control_adr = 0x14; // Digital track
  396. if (track->track_mode == CUETrack_AUDIO)
  397. {
  398. control_adr = 0x10; // Audio track
  399. }
  400. dest[0] = 0; // Reserved
  401. dest[1] = control_adr;
  402. dest[2] = track->track_number;
  403. dest[3] = 0; // Reserved
  404. if (use_MSF_time)
  405. {
  406. // Time in minute-second-frame format
  407. LBA2MSF(track->data_start, &dest[4]);
  408. }
  409. else
  410. {
  411. // Time as logical block address
  412. dest[4] = (track->data_start >> 24) & 0xFF;
  413. dest[5] = (track->data_start >> 16) & 0xFF;
  414. dest[6] = (track->data_start >> 8) & 0xFF;
  415. dest[7] = (track->data_start >> 0) & 0xFF;
  416. }
  417. }
  418. // Load data from CUE sheet for the given device,
  419. // using the second half of scsiDev.data buffer for temporary storage.
  420. // Returns false if no cue sheet or it could not be opened.
  421. static bool loadCueSheet(image_config_t &img, CUEParser &parser)
  422. {
  423. if (!img.cuesheetfile.isOpen())
  424. {
  425. return false;
  426. }
  427. // Use second half of scsiDev.data as the buffer for cue sheet text
  428. size_t halfbufsize = sizeof(scsiDev.data) / 2;
  429. char *cuebuf = (char*)&scsiDev.data[halfbufsize];
  430. img.cuesheetfile.seek(0);
  431. int len = img.cuesheetfile.read(cuebuf, halfbufsize);
  432. if (len <= 0)
  433. {
  434. return false;
  435. }
  436. cuebuf[len] = '\0';
  437. parser = CUEParser(cuebuf);
  438. return true;
  439. }
  440. static void doReadTOC(bool MSF, uint8_t track, uint16_t allocationLength)
  441. {
  442. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  443. CUEParser parser;
  444. if (!loadCueSheet(img, parser))
  445. {
  446. // No CUE sheet, use hardcoded data
  447. return doReadTOCSimple(MSF, track, allocationLength);
  448. }
  449. // Format track info
  450. uint8_t *trackdata = &scsiDev.data[4];
  451. int trackcount = 0;
  452. int firsttrack = -1;
  453. int lasttrack = -1;
  454. const CUETrackInfo *trackinfo;
  455. while ((trackinfo = parser.next_track()) != NULL)
  456. {
  457. if (firsttrack < 0) firsttrack = trackinfo->track_number;
  458. lasttrack = trackinfo->track_number;
  459. if (track == 0 || track == trackinfo->track_number)
  460. {
  461. formatTrackInfo(trackinfo, &trackdata[8 * trackcount], MSF);
  462. trackcount += 1;
  463. }
  464. }
  465. // Format lead-out track info
  466. if (track == 0 || track == 0xAA)
  467. {
  468. CUETrackInfo leadout = {};
  469. leadout.track_number = 0xAA;
  470. leadout.track_mode = CUETrack_MODE1_2048;
  471. leadout.data_start = img.scsiSectors;
  472. formatTrackInfo(&leadout, &trackdata[8 * trackcount], MSF);
  473. trackcount += 1;
  474. }
  475. // Format response header
  476. uint16_t toc_length = 2 + trackcount * 8;
  477. scsiDev.data[0] = toc_length >> 8;
  478. scsiDev.data[1] = toc_length & 0xFF;
  479. scsiDev.data[2] = firsttrack;
  480. scsiDev.data[3] = lasttrack;
  481. if (trackcount == 0)
  482. {
  483. // Unknown track requested
  484. scsiDev.status = CHECK_CONDITION;
  485. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  486. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  487. scsiDev.phase = STATUS;
  488. }
  489. else
  490. {
  491. uint32_t len = 2 + toc_length;
  492. if (len > allocationLength)
  493. {
  494. len = allocationLength;
  495. }
  496. scsiDev.dataLen = len;
  497. scsiDev.phase = DATA_IN;
  498. }
  499. }
  500. static void doReadSessionInfo(uint8_t session, uint16_t allocationLength)
  501. {
  502. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  503. CUEParser parser;
  504. if (!loadCueSheet(img, parser))
  505. {
  506. // No CUE sheet, use hardcoded data
  507. return doReadSessionInfoSimple(session, allocationLength);
  508. }
  509. uint32_t len = sizeof(SessionTOC);
  510. memcpy(scsiDev.data, SessionTOC, len);
  511. // Replace first track info in the session table
  512. // based on data from CUE sheet.
  513. const CUETrackInfo *trackinfo = parser.next_track();
  514. if (trackinfo)
  515. {
  516. formatTrackInfo(trackinfo, &scsiDev.data[4], false);
  517. }
  518. if (len > allocationLength)
  519. {
  520. len = allocationLength;
  521. }
  522. scsiDev.dataLen = len;
  523. scsiDev.phase = DATA_IN;
  524. }
  525. // Convert logical block address to CD-ROM time in the raw TOC format
  526. static void LBA2MSFRaw(uint32_t LBA, uint8_t* MSF)
  527. {
  528. MSF[2] = LBA % 75; // Frames
  529. uint32_t rem = LBA / 75;
  530. MSF[1] = rem % 60; // Seconds
  531. MSF[0] = rem / 60; // Minutes
  532. }
  533. // Convert logical block address to CD-ROM time in binary coded decimal format
  534. static void LBA2MSFBCD(uint32_t LBA, uint8_t* MSF)
  535. {
  536. uint8_t fra = LBA % 75;
  537. uint32_t rem = LBA / 75;
  538. uint8_t sec = rem % 60;
  539. uint8_t min = rem / 60;
  540. MSF[0] = ((min / 10) << 4) | (min % 10);
  541. MSF[1] = ((sec / 10) << 4) | (sec % 10);
  542. MSF[2] = ((fra / 10) << 4) | (fra % 10);
  543. }
  544. // Format track info read from cue sheet into the format used by ReadFullTOC command.
  545. // Refer to T10/1545-D MMC-4 Revision 5a, "Response Format 0010b: Raw TOC"
  546. static void formatRawTrackInfo(const CUETrackInfo *track, uint8_t *dest)
  547. {
  548. uint8_t control_adr = 0x14; // Digital track
  549. if (track->track_mode == CUETrack_AUDIO)
  550. {
  551. control_adr = 0x10; // Audio track
  552. }
  553. dest[0] = 0x01; // Session always 1
  554. dest[1] = control_adr;
  555. dest[2] = 0x00; // "TNO", always 0?
  556. dest[3] = track->track_number; // "POINT", contains track number
  557. if (track->pregap_start > 0)
  558. {
  559. LBA2MSFRaw(track->pregap_start, &dest[4]);
  560. }
  561. else
  562. {
  563. LBA2MSFRaw(track->data_start, &dest[4]);
  564. }
  565. dest[7] = 0; // HOUR
  566. LBA2MSFBCD(track->data_start, &dest[8]);
  567. }
  568. static void doReadFullTOC(int convertBCD, uint8_t session, uint16_t allocationLength)
  569. {
  570. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  571. CUEParser parser;
  572. if (!loadCueSheet(img, parser))
  573. {
  574. // No CUE sheet, use hardcoded data
  575. return doReadFullTOCSimple(convertBCD, session, allocationLength);
  576. }
  577. // We only support session 1.
  578. if (session > 1)
  579. {
  580. scsiDev.status = CHECK_CONDITION;
  581. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  582. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  583. scsiDev.phase = STATUS;
  584. return;
  585. }
  586. // Take the beginning of the hardcoded TOC as base
  587. uint32_t len = 4 + 11 * 3; // Header, A0, A1, A2
  588. memcpy(scsiDev.data, FullTOC, len);
  589. // Add track descriptors
  590. int trackcount = 0;
  591. int firsttrack = -1;
  592. int lasttrack = -1;
  593. const CUETrackInfo *trackinfo;
  594. while ((trackinfo = parser.next_track()) != NULL)
  595. {
  596. if (firsttrack < 0) firsttrack = trackinfo->track_number;
  597. lasttrack = trackinfo->track_number;
  598. formatRawTrackInfo(trackinfo, &scsiDev.data[len]);
  599. trackcount += 1;
  600. len += 11;
  601. }
  602. // First and last track numbers
  603. scsiDev.data[12] = firsttrack;
  604. scsiDev.data[23] = lasttrack;
  605. // Leadout track position
  606. LBA2MSFBCD(img.scsiSectors, &scsiDev.data[34]);
  607. // Append recordable disc records b0 and c0 indicating non-recordable disc
  608. memcpy(scsiDev.data + len, &FullTOC[48], 22);
  609. len += 22;
  610. // Correct the record length in header
  611. uint16_t toclen = len - 2;
  612. scsiDev.data[0] = toclen >> 8;
  613. scsiDev.data[1] = toclen & 0xFF;
  614. if (len > allocationLength)
  615. {
  616. len = allocationLength;
  617. }
  618. scsiDev.dataLen = len;
  619. scsiDev.phase = DATA_IN;
  620. }
  621. // SCSI-3 MMC Read Header command, seems to be deprecated in later standards.
  622. // Refer to ANSI X3.304-1997
  623. void doReadHeader(bool MSF, uint32_t lba, uint16_t allocationLength)
  624. {
  625. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  626. #if ENABLE_AUDIO_OUTPUT
  627. // terminate audio playback if active on this target (Annex C)
  628. audio_stop(img.scsiId & 7);
  629. #endif
  630. CUEParser parser;
  631. if (!loadCueSheet(img, parser))
  632. {
  633. // No CUE sheet, use hardcoded data
  634. return doReadHeaderSimple(MSF, lba, allocationLength);
  635. }
  636. // Take the hardcoded header as base
  637. uint32_t len = sizeof(SimpleHeader);
  638. memcpy(scsiDev.data, SimpleHeader, len);
  639. // Search the track with the requested LBA
  640. CUETrackInfo trackinfo = {};
  641. getTrackFromLBA(parser, lba, &trackinfo);
  642. // Track mode (audio / data)
  643. if (trackinfo.track_mode == CUETrack_AUDIO)
  644. {
  645. scsiDev.data[0] = 0;
  646. }
  647. // Track start
  648. if (MSF)
  649. {
  650. LBA2MSF(trackinfo.data_start, &scsiDev.data[4]);
  651. }
  652. else
  653. {
  654. scsiDev.data[4] = (trackinfo.data_start >> 24) & 0xFF;
  655. scsiDev.data[5] = (trackinfo.data_start >> 16) & 0xFF;
  656. scsiDev.data[6] = (trackinfo.data_start >> 8) & 0xFF;
  657. scsiDev.data[7] = (trackinfo.data_start >> 0) & 0xFF;
  658. }
  659. if (len > allocationLength)
  660. {
  661. len = allocationLength;
  662. }
  663. scsiDev.dataLen = len;
  664. scsiDev.phase = DATA_IN;
  665. }
  666. void doReadDiscInformation(uint16_t allocationLength)
  667. {
  668. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  669. CUEParser parser;
  670. if (!loadCueSheet(img, parser))
  671. {
  672. // No CUE sheet, use hardcoded data
  673. return doReadDiscInformationSimple(allocationLength);
  674. }
  675. // Take the hardcoded header as base
  676. uint32_t len = sizeof(DiscInformation);
  677. memcpy(scsiDev.data, DiscInformation, len);
  678. // Find first and last track number
  679. int firsttrack = -1;
  680. int lasttrack = -1;
  681. const CUETrackInfo *trackinfo;
  682. while ((trackinfo = parser.next_track()) != NULL)
  683. {
  684. if (firsttrack < 0) firsttrack = trackinfo->track_number;
  685. lasttrack = trackinfo->track_number;
  686. }
  687. scsiDev.data[3] = firsttrack;
  688. scsiDev.data[5] = firsttrack;
  689. scsiDev.data[6] = lasttrack;
  690. if (len > allocationLength)
  691. {
  692. len = allocationLength;
  693. }
  694. scsiDev.dataLen = len;
  695. scsiDev.phase = DATA_IN;
  696. }
  697. /****************************************/
  698. /* CUE sheet check at image load time */
  699. /****************************************/
  700. bool cdromValidateCueSheet(image_config_t &img)
  701. {
  702. CUEParser parser;
  703. if (!loadCueSheet(img, parser))
  704. {
  705. return false;
  706. }
  707. const CUETrackInfo *trackinfo;
  708. int trackcount = 0;
  709. while ((trackinfo = parser.next_track()) != NULL)
  710. {
  711. trackcount++;
  712. if (trackinfo->track_mode != CUETrack_AUDIO &&
  713. trackinfo->track_mode != CUETrack_MODE1_2048 &&
  714. trackinfo->track_mode != CUETrack_MODE1_2352)
  715. {
  716. logmsg("---- Warning: track ", trackinfo->track_number, " has unsupported mode ", (int)trackinfo->track_mode);
  717. }
  718. if (trackinfo->file_mode != CUEFile_BINARY)
  719. {
  720. logmsg("---- Unsupported CUE data file mode ", (int)trackinfo->file_mode);
  721. }
  722. }
  723. if (trackcount == 0)
  724. {
  725. logmsg("---- Opened cue sheet but no valid tracks found");
  726. return false;
  727. }
  728. logmsg("---- Cue sheet loaded with ", (int)trackcount, " tracks");
  729. return true;
  730. }
  731. /**************************************/
  732. /* Ejection and image switching logic */
  733. /**************************************/
  734. // Reinsert any ejected CDROMs on reboot
  735. void cdromReinsertFirstImage(image_config_t &img)
  736. {
  737. if (img.image_index > 0)
  738. {
  739. // Multiple images for this drive, force restart from first one
  740. dbgmsg("---- Restarting from first CD-ROM image");
  741. img.image_index = 9;
  742. cdromSwitchNextImage(img);
  743. }
  744. else if (img.ejected)
  745. {
  746. // Reinsert the single image
  747. dbgmsg("---- Closing CD-ROM tray");
  748. img.ejected = false;
  749. img.cdrom_events = 2; // New media
  750. }
  751. }
  752. // Check if we have multiple CD-ROM images to cycle when drive is ejected.
  753. bool cdromSwitchNextImage(image_config_t &img)
  754. {
  755. // Check if we have a next image to load, so that drive is closed next time the host asks.
  756. img.image_index++;
  757. char filename[MAX_FILE_PATH];
  758. int target_idx = img.scsiId & 7;
  759. if (!scsiDiskGetImageNameFromConfig(img, filename, sizeof(filename)))
  760. {
  761. img.image_index = 0;
  762. scsiDiskGetImageNameFromConfig(img, filename, sizeof(filename));
  763. }
  764. #ifdef ENABLE_AUDIO_OUTPUT
  765. // if in progress for this device, terminate audio playback immediately (Annex C)
  766. audio_stop(target_idx);
  767. // Reset position tracking for the new image
  768. audio_get_status_code(target_idx); // trash audio status code
  769. audio_clear_bytes_read(target_idx);
  770. #endif
  771. mechanism_status[target_idx].last_lba = 0;
  772. if (filename[0] != '\0')
  773. {
  774. logmsg("Switching to next CD-ROM image for ", target_idx, ": ", filename);
  775. img.file.close();
  776. bool status = scsiDiskOpenHDDImage(target_idx, filename, target_idx, 0, 2048);
  777. if (status)
  778. {
  779. img.ejected = false;
  780. img.cdrom_events = 2; // New media
  781. return true;
  782. }
  783. }
  784. return false;
  785. }
  786. static void doGetEventStatusNotification(bool immed)
  787. {
  788. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  789. if (!immed)
  790. {
  791. // Asynchronous notification not supported
  792. scsiDev.status = CHECK_CONDITION;
  793. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  794. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  795. scsiDev.phase = STATUS;
  796. }
  797. else if (img.cdrom_events)
  798. {
  799. scsiDev.data[0] = 0;
  800. scsiDev.data[1] = 6; // EventDataLength
  801. scsiDev.data[2] = 0x04; // Media status events
  802. scsiDev.data[3] = 0x04; // Supported events
  803. scsiDev.data[4] = img.cdrom_events;
  804. scsiDev.data[5] = 0x01; // Power status
  805. scsiDev.data[6] = 0; // Start slot
  806. scsiDev.data[7] = 0; // End slot
  807. scsiDev.dataLen = 8;
  808. scsiDev.phase = DATA_IN;
  809. img.cdrom_events = 0;
  810. if (img.ejected)
  811. {
  812. // We are now reporting to host that the drive is open.
  813. // Simulate a "close" for next time the host polls.
  814. cdromSwitchNextImage(img);
  815. }
  816. }
  817. else
  818. {
  819. scsiDev.data[0] = 0;
  820. scsiDev.data[1] = 2; // EventDataLength
  821. scsiDev.data[2] = 0x00; // Media status events
  822. scsiDev.data[3] = 0x04; // Supported events
  823. scsiDev.dataLen = 4;
  824. scsiDev.phase = DATA_IN;
  825. }
  826. }
  827. /**************************************/
  828. /* CD-ROM audio playback */
  829. /**************************************/
  830. void cdromGetAudioPlaybackStatus(uint8_t *status, uint32_t *current_lba, bool current_only)
  831. {
  832. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  833. uint8_t target = img.scsiId & 7;
  834. #ifdef ENABLE_AUDIO_OUTPUT
  835. if (status) {
  836. if (current_only) {
  837. *status = audio_is_playing(target) ? 1 : 0;
  838. } else {
  839. *status = (uint8_t) audio_get_status_code(target);
  840. }
  841. }
  842. if (current_lba) *current_lba = mechanism_status[target].last_lba
  843. + audio_get_bytes_read(target) / 2352;
  844. #else
  845. if (status) *status = 0; // audio status code for 'unsupported/invalid' and not-playing indicator
  846. if (current_lba) *current_lba = mechanism_status[target].last_lba;
  847. #endif
  848. }
  849. static void doPlayAudio(uint32_t lba, uint32_t length)
  850. {
  851. #ifdef ENABLE_AUDIO_OUTPUT
  852. dbgmsg("------ CD-ROM Play Audio request at ", lba, " for ", length, " sectors");
  853. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  854. uint8_t target_id = img.scsiId & 7;
  855. // Per Annex C terminate playback immediately if already in progress on
  856. // the current target. Non-current targets may also get their audio
  857. // interrupted later due to hardware limitations
  858. audio_stop(img.scsiId & 7);
  859. // if transfer length is zero no audio playback happens.
  860. // don't treat as an error per SCSI-2; handle via short-circuit
  861. if (length == 0)
  862. {
  863. scsiDev.status = 0;
  864. scsiDev.phase = STATUS;
  865. return;
  866. }
  867. // if actual playback is requested perform steps to verify prior to playback
  868. CUEParser parser;
  869. if (loadCueSheet(img, parser))
  870. {
  871. CUETrackInfo trackinfo = {};
  872. getTrackFromLBA(parser, lba, &trackinfo);
  873. if (lba == 0xFFFFFFFF)
  874. {
  875. // request to start playback from 'current position'
  876. lba = mechanism_status[target_id].last_lba + audio_get_bytes_read(target_id) / 2352;
  877. }
  878. // --- TODO --- determine proper track offset, software I tested with had a tendency
  879. // to ask for offsets that seem to hint at 2048 here, not the 2352 you'd assume.
  880. // Might be due to a mode page reporting something unexpected? Needs investigation.
  881. uint64_t offset = trackinfo.file_offset + 2048 * (lba - trackinfo.data_start);
  882. dbgmsg("------ Play audio CD: ", (int)length, " sectors starting at ", (int)lba,
  883. ", track number ", trackinfo.track_number, ", data offset in file ", (int)offset);
  884. if (trackinfo.track_mode != CUETrack_AUDIO)
  885. {
  886. dbgmsg("---- Host tried audio playback on track type ", (int)trackinfo.track_mode);
  887. scsiDev.status = CHECK_CONDITION;
  888. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  889. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  890. scsiDev.phase = STATUS;
  891. return;
  892. }
  893. // playback request appears to be sane, so perform it
  894. char filename[MAX_FILE_PATH];
  895. if (!img.file.name(filename, sizeof(filename)))
  896. {
  897. // No underlying file available?
  898. logmsg("---- No filename for SCSI ID ", target_id);
  899. scsiDev.status = CHECK_CONDITION;
  900. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  901. scsiDev.target->sense.asc = 0x0000;
  902. scsiDev.phase = STATUS;
  903. return;
  904. }
  905. if (!audio_play(target_id, filename, offset, offset + length * 2352, false))
  906. {
  907. // Underlying data/media error? Fake a disk scratch, which should
  908. // be a condition most CD-DA players are expecting
  909. scsiDev.status = CHECK_CONDITION;
  910. scsiDev.target->sense.code = MEDIUM_ERROR;
  911. scsiDev.target->sense.asc = 0x1106; // CIRC UNRECOVERED ERROR
  912. scsiDev.phase = STATUS;
  913. return;
  914. }
  915. mechanism_status[target_id].last_lba = lba;
  916. scsiDev.status = 0;
  917. scsiDev.phase = STATUS;
  918. }
  919. else
  920. {
  921. // virtual drive supports audio, just not with this disk image
  922. dbgmsg("---- Request to play audio on non-audio image");
  923. scsiDev.status = CHECK_CONDITION;
  924. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  925. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  926. scsiDev.phase = STATUS;
  927. }
  928. #else
  929. dbgmsg("---- Target does not support audio playback");
  930. // per SCSI-2, targets not supporting audio respond to zero-length
  931. // PLAY AUDIO commands with ILLEGAL REQUEST; this seems to be a check
  932. // performed by at least some audio playback software
  933. scsiDev.status = CHECK_CONDITION;
  934. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  935. scsiDev.target->sense.asc = 0x0000; // NO ADDITIONAL SENSE INFORMATION
  936. scsiDev.phase = STATUS;
  937. #endif
  938. }
  939. static void doPauseResumeAudio(bool resume)
  940. {
  941. #ifdef ENABLE_AUDIO_OUTPUT
  942. logmsg("------ CD-ROM ", resume ? "resume" : "pause", " audio playback");
  943. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  944. uint8_t target_id = img.scsiId & 7;
  945. if (audio_is_playing(target_id))
  946. {
  947. audio_set_paused(target_id, !resume);
  948. scsiDev.status = 0;
  949. scsiDev.phase = STATUS;
  950. }
  951. else
  952. {
  953. scsiDev.status = CHECK_CONDITION;
  954. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  955. scsiDev.target->sense.asc = 0x2C00; // COMMAND SEQUENCE ERROR
  956. scsiDev.phase = STATUS;
  957. }
  958. #else
  959. dbgmsg("---- Target does not support audio pausing");
  960. scsiDev.status = CHECK_CONDITION;
  961. scsiDev.target->sense.code = ILLEGAL_REQUEST; // assumed from PLAY AUDIO(10)
  962. scsiDev.target->sense.asc = 0x0000; // NO ADDITIONAL SENSE INFORMATION
  963. scsiDev.phase = STATUS;
  964. #endif
  965. }
  966. static void doMechanismStatus(uint16_t allocation_length)
  967. {
  968. uint8_t *buf = scsiDev.data;
  969. uint8_t status;
  970. uint32_t lba;
  971. cdromGetAudioPlaybackStatus(&status, &lba, true);
  972. *buf++ = 0x00; // No fault state
  973. *buf++ = (status) ? 0x20 : 0x00; // Currently playing?
  974. *buf++ = (lba >> 16) & 0xFF;
  975. *buf++ = (lba >> 8) & 0xFF;
  976. *buf++ = (lba >> 0) & 0xFF;
  977. *buf++ = 0; // No CD changer
  978. *buf++ = 0;
  979. *buf++ = 0;
  980. int len = 8;
  981. if (len > allocation_length) len = allocation_length;
  982. scsiDev.dataLen = len;
  983. scsiDev.phase = DATA_IN;
  984. }
  985. /*******************************************/
  986. /* CD-ROM data reading in low level format */
  987. /*******************************************/
  988. static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
  989. uint8_t main_channel, uint8_t sub_channel)
  990. {
  991. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  992. #if ENABLE_AUDIO_OUTPUT
  993. // terminate audio playback if active on this target (Annex C)
  994. audio_stop(img.scsiId & 7);
  995. #endif
  996. CUEParser parser;
  997. if (!loadCueSheet(img, parser)
  998. && (sector_type == 0 || sector_type == 2)
  999. && main_channel == 0x10 && sub_channel == 0)
  1000. {
  1001. // Simple case, return sector data directly
  1002. scsiDiskStartRead(lba, length);
  1003. return;
  1004. }
  1005. // Search the track with the requested LBA
  1006. // Supplies dummy data if no cue sheet is active.
  1007. CUETrackInfo trackinfo = {};
  1008. getTrackFromLBA(parser, lba, &trackinfo);
  1009. // Figure out the data offset in the file
  1010. uint64_t offset = trackinfo.file_offset + trackinfo.sector_length * (lba - trackinfo.data_start);
  1011. dbgmsg("------ Read CD: ", (int)length, " sectors starting at ", (int)lba,
  1012. ", track number ", trackinfo.track_number, ", sector size ", (int)trackinfo.sector_length,
  1013. ", main channel ", main_channel, ", sub channel ", sub_channel,
  1014. ", data offset in file ", (int)offset);
  1015. // Verify sector type
  1016. if (sector_type != 0)
  1017. {
  1018. bool sector_type_ok = false;
  1019. if (sector_type == 1 && trackinfo.track_mode == CUETrack_AUDIO)
  1020. {
  1021. sector_type_ok = true;
  1022. }
  1023. else if (sector_type == 2 && trackinfo.track_mode == CUETrack_MODE1_2048)
  1024. {
  1025. sector_type_ok = true;
  1026. }
  1027. if (!sector_type_ok)
  1028. {
  1029. dbgmsg("---- Failed sector type check, host requested ", (int)sector_type, " CUE file has ", (int)trackinfo.track_mode);
  1030. scsiDev.status = CHECK_CONDITION;
  1031. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1032. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  1033. scsiDev.phase = STATUS;
  1034. return;
  1035. }
  1036. }
  1037. // Select fields to transfer
  1038. // Refer to table 351 in T10/1545-D MMC-4 Revision 5a
  1039. // Only the mandatory cases are supported.
  1040. int sector_length = 0;
  1041. int skip_begin = 0;
  1042. bool add_fake_headers = false;
  1043. if (main_channel == 0)
  1044. {
  1045. // No actual data requested, just sector type check or subchannel
  1046. sector_length = 0;
  1047. }
  1048. else if (trackinfo.track_mode == CUETrack_AUDIO)
  1049. {
  1050. // Transfer whole 2352 byte audio sectors from file to host
  1051. sector_length = 2352;
  1052. }
  1053. else if (trackinfo.track_mode == CUETrack_MODE1_2048 && main_channel == 0x10)
  1054. {
  1055. // Transfer whole 2048 byte data sectors from file to host
  1056. sector_length = 2048;
  1057. }
  1058. else if (trackinfo.track_mode == CUETrack_MODE1_2048 && (main_channel & 0xB8) == 0xB8)
  1059. {
  1060. // Transfer 2048 bytes of data from file and fake the headers
  1061. sector_length = 2048;
  1062. add_fake_headers = true;
  1063. dbgmsg("------ Host requested ECC data but image file lacks it, replacing with zeros");
  1064. }
  1065. else if (trackinfo.track_mode == CUETrack_MODE1_2352 && main_channel == 0x10)
  1066. {
  1067. // Transfer the 2048 byte payload of data sector to host.
  1068. sector_length = 2048;
  1069. skip_begin = 16;
  1070. }
  1071. else if (trackinfo.track_mode == CUETrack_MODE1_2352 && (main_channel & 0xB8) == 0xB8)
  1072. {
  1073. // Transfer whole 2352 byte data sector with ECC to host
  1074. sector_length = 2352;
  1075. }
  1076. else
  1077. {
  1078. dbgmsg("---- Unsupported channel request for track type ", (int)trackinfo.track_mode);
  1079. scsiDev.status = CHECK_CONDITION;
  1080. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1081. scsiDev.target->sense.asc = 0x6400; // ILLEGAL MODE FOR THIS TRACK
  1082. scsiDev.phase = STATUS;
  1083. return;
  1084. }
  1085. bool field_q_subchannel = false;
  1086. if (sub_channel == 2)
  1087. {
  1088. // Include position information in Q subchannel
  1089. field_q_subchannel = true;
  1090. }
  1091. else if (sub_channel != 0)
  1092. {
  1093. dbgmsg("---- Unsupported subchannel request");
  1094. scsiDev.status = CHECK_CONDITION;
  1095. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1096. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  1097. scsiDev.phase = STATUS;
  1098. return;
  1099. }
  1100. scsiDev.phase = DATA_IN;
  1101. scsiDev.dataLen = 0;
  1102. scsiDev.dataPtr = 0;
  1103. scsiEnterPhase(DATA_IN);
  1104. // Use two buffers alternately for formatting sector data
  1105. uint32_t result_length = sector_length + (field_q_subchannel ? 16 : 0) + (add_fake_headers ? 304 : 0);
  1106. uint8_t *buf0 = scsiDev.data;
  1107. uint8_t *buf1 = scsiDev.data + result_length;
  1108. // Format the sectors for transfer
  1109. for (uint32_t idx = 0; idx < length; idx++)
  1110. {
  1111. platform_poll();
  1112. img.file.seek(offset + idx * trackinfo.sector_length + skip_begin);
  1113. // Verify that previous write using this buffer has finished
  1114. uint8_t *buf = ((idx & 1) ? buf1 : buf0);
  1115. uint8_t *bufstart = buf;
  1116. uint32_t start = millis();
  1117. while (!scsiIsWriteFinished(buf + result_length - 1) && !scsiDev.resetFlag)
  1118. {
  1119. if ((uint32_t)(millis() - start) > 5000)
  1120. {
  1121. logmsg("doReadCD() timeout waiting for previous to finish");
  1122. scsiDev.resetFlag = 1;
  1123. }
  1124. platform_poll();
  1125. }
  1126. if (scsiDev.resetFlag) break;
  1127. if (add_fake_headers)
  1128. {
  1129. // 12-byte data sector sync pattern
  1130. *buf++ = 0x00;
  1131. for (int i = 0; i < 10; i++)
  1132. {
  1133. *buf++ = 0xFF;
  1134. }
  1135. *buf++ = 0x00;
  1136. // 4-byte data sector header
  1137. LBA2MSFBCD(lba + idx, buf);
  1138. buf += 3;
  1139. *buf++ = 0x01; // Mode 1
  1140. }
  1141. if (sector_length > 0)
  1142. {
  1143. // User data
  1144. img.file.read(buf, sector_length);
  1145. buf += sector_length;
  1146. }
  1147. if (add_fake_headers)
  1148. {
  1149. // 288 bytes of ECC
  1150. memset(buf, 0, 288);
  1151. buf += 288;
  1152. }
  1153. if (field_q_subchannel)
  1154. {
  1155. // Formatted Q subchannel data
  1156. // Refer to table 354 in T10/1545-D MMC-4 Revision 5a
  1157. *buf++ = (trackinfo.track_mode == CUETrack_AUDIO ? 0x10 : 0x14); // Control & ADR
  1158. *buf++ = trackinfo.track_number;
  1159. *buf++ = (lba + idx >= trackinfo.data_start) ? 1 : 0; // Index number (0 = pregap)
  1160. LBA2MSFRaw(lba + idx, buf); buf += 3;
  1161. *buf++ = 0;
  1162. LBA2MSFRaw(lba + idx, buf); buf += 3;
  1163. *buf++ = 0; *buf++ = 0; // CRC (optional)
  1164. *buf++ = 0; *buf++ = 0; *buf++ = 0; // (pad)
  1165. *buf++ = 0; // No P subchannel
  1166. }
  1167. assert(buf == bufstart + result_length);
  1168. scsiStartWrite(bufstart, result_length);
  1169. }
  1170. scsiFinishWrite();
  1171. scsiDev.status = 0;
  1172. scsiDev.phase = STATUS;
  1173. }
  1174. static void doReadSubchannel(bool time, bool subq, uint8_t parameter, uint8_t track_number, uint16_t allocation_length)
  1175. {
  1176. uint8_t *buf = scsiDev.data;
  1177. if (parameter == 0x01)
  1178. {
  1179. uint8_t audiostatus;
  1180. uint32_t lba;
  1181. cdromGetAudioPlaybackStatus(&audiostatus, &lba, false);
  1182. dbgmsg("------ Get audio playback position: status ", (int)audiostatus, " lba ", (int)lba);
  1183. // Fetch current track info
  1184. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  1185. CUEParser parser;
  1186. CUETrackInfo trackinfo = {};
  1187. loadCueSheet(img, parser);
  1188. getTrackFromLBA(parser, lba, &trackinfo);
  1189. // Request sub channel data at current playback position
  1190. *buf++ = 0; // Reserved
  1191. *buf++ = audiostatus;
  1192. int len = 12;
  1193. *buf++ = 0; // Subchannel data length (MSB)
  1194. *buf++ = len; // Subchannel data length (LSB)
  1195. *buf++ = 0x01; // Subchannel data format
  1196. *buf++ = (trackinfo.track_mode == CUETrack_AUDIO ? 0x10 : 0x14);
  1197. *buf++ = trackinfo.track_number;
  1198. *buf++ = (lba >= trackinfo.data_start) ? 1 : 0; // Index number (0 = pregap)
  1199. *buf++ = (lba >> 24) & 0xFF; // Absolute block address
  1200. *buf++ = (lba >> 16) & 0xFF;
  1201. *buf++ = (lba >> 8) & 0xFF;
  1202. *buf++ = (lba >> 0) & 0xFF;
  1203. uint32_t relpos = (uint32_t)((int32_t)lba - (int32_t)trackinfo.data_start);
  1204. *buf++ = (relpos >> 24) & 0xFF; // Track relative position (may be negative)
  1205. *buf++ = (relpos >> 16) & 0xFF;
  1206. *buf++ = (relpos >> 8) & 0xFF;
  1207. *buf++ = (relpos >> 0) & 0xFF;
  1208. if (len > allocation_length) len = allocation_length;
  1209. scsiDev.dataLen = len;
  1210. scsiDev.phase = DATA_IN;
  1211. }
  1212. else
  1213. {
  1214. dbgmsg("---- Unsupported subchannel request");
  1215. scsiDev.status = CHECK_CONDITION;
  1216. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1217. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  1218. scsiDev.phase = STATUS;
  1219. return;
  1220. }
  1221. }
  1222. /**************************************/
  1223. /* CD-ROM command dispatching */
  1224. /**************************************/
  1225. // Handle direct-access scsi device commands
  1226. extern "C" int scsiCDRomCommand()
  1227. {
  1228. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  1229. int commandHandled = 1;
  1230. uint8_t command = scsiDev.cdb[0];
  1231. if (command == 0x1B)
  1232. {
  1233. #if ENABLE_AUDIO_OUTPUT
  1234. // terminate audio playback if active on this target (Annex C)
  1235. audio_stop(img.scsiId & 7);
  1236. #endif
  1237. if ((scsiDev.cdb[4] & 2))
  1238. {
  1239. // CD-ROM load & eject
  1240. int start = scsiDev.cdb[4] & 1;
  1241. if (start)
  1242. {
  1243. dbgmsg("------ CDROM close tray");
  1244. img.ejected = false;
  1245. img.cdrom_events = 2; // New media
  1246. }
  1247. else
  1248. {
  1249. dbgmsg("------ CDROM open tray");
  1250. img.ejected = true;
  1251. img.cdrom_events = 3; // Media removal
  1252. }
  1253. }
  1254. else
  1255. {
  1256. // flow through to disk handler
  1257. commandHandled = 0;
  1258. }
  1259. }
  1260. else if (command == 0x43)
  1261. {
  1262. // CD-ROM Read TOC
  1263. bool MSF = (scsiDev.cdb[1] & 0x02);
  1264. uint8_t track = scsiDev.cdb[6];
  1265. uint16_t allocationLength =
  1266. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1267. scsiDev.cdb[8];
  1268. // Reject MMC commands for now, otherwise the TOC data format
  1269. // won't be understood.
  1270. // The "format" field is reserved for SCSI-2
  1271. uint8_t format = scsiDev.cdb[2] & 0x0F;
  1272. switch (format)
  1273. {
  1274. case 0: doReadTOC(MSF, track, allocationLength); break; // SCSI-2
  1275. case 1: doReadSessionInfo(MSF, allocationLength); break; // MMC2
  1276. case 2: doReadFullTOC(0, track, allocationLength); break; // MMC2
  1277. case 3: doReadFullTOC(1, track, allocationLength); break; // MMC2
  1278. default:
  1279. {
  1280. scsiDev.status = CHECK_CONDITION;
  1281. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  1282. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  1283. scsiDev.phase = STATUS;
  1284. }
  1285. }
  1286. }
  1287. else if (command == 0x44)
  1288. {
  1289. // CD-ROM Read Header
  1290. bool MSF = (scsiDev.cdb[1] & 0x02);
  1291. uint32_t lba = 0; // IGNORED for now
  1292. uint16_t allocationLength =
  1293. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1294. scsiDev.cdb[8];
  1295. doReadHeader(MSF, lba, allocationLength);
  1296. }
  1297. else if (command == 0x51)
  1298. {
  1299. uint16_t allocationLength =
  1300. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1301. scsiDev.cdb[8];
  1302. doReadDiscInformation(allocationLength);
  1303. }
  1304. else if (command == 0x4A)
  1305. {
  1306. // Get event status notifications (media change notifications)
  1307. bool immed = scsiDev.cdb[1] & 1;
  1308. doGetEventStatusNotification(immed);
  1309. }
  1310. else if (command == 0x45)
  1311. {
  1312. // PLAY AUDIO (10)
  1313. uint32_t lba =
  1314. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1315. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1316. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1317. scsiDev.cdb[5];
  1318. uint32_t blocks =
  1319. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1320. scsiDev.cdb[8];
  1321. doPlayAudio(lba, blocks);
  1322. }
  1323. else if (command == 0xA5)
  1324. {
  1325. // PLAY AUDIO (12)
  1326. uint32_t lba =
  1327. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1328. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1329. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1330. scsiDev.cdb[5];
  1331. uint32_t blocks =
  1332. (((uint32_t) scsiDev.cdb[6]) << 24) +
  1333. (((uint32_t) scsiDev.cdb[7]) << 16) +
  1334. (((uint32_t) scsiDev.cdb[8]) << 8) +
  1335. scsiDev.cdb[9];
  1336. doPlayAudio(lba, blocks);
  1337. }
  1338. else if (command == 0x47)
  1339. {
  1340. // PLAY AUDIO (MSF)
  1341. uint32_t start = (scsiDev.cdb[3] * 60 + scsiDev.cdb[4]) * 75 + scsiDev.cdb[5];
  1342. uint32_t end = (scsiDev.cdb[6] * 60 + scsiDev.cdb[7]) * 75 + scsiDev.cdb[8];
  1343. doPlayAudio(start, end - start);
  1344. }
  1345. else if (command == 0x4B)
  1346. {
  1347. // PAUSE/RESUME AUDIO
  1348. doPauseResumeAudio(scsiDev.cdb[8] & 1);
  1349. }
  1350. else if (command == 0xBD)
  1351. {
  1352. // Mechanism status
  1353. uint16_t allocationLength = (((uint32_t) scsiDev.cdb[8]) << 8) + scsiDev.cdb[9];
  1354. doMechanismStatus(allocationLength);
  1355. }
  1356. else if (command == 0xBB)
  1357. {
  1358. // Set CD speed (just ignored)
  1359. scsiDev.status = 0;
  1360. scsiDev.phase = STATUS;
  1361. }
  1362. else if (command == 0xBE)
  1363. {
  1364. // ReadCD (in low level format)
  1365. uint8_t sector_type = (scsiDev.cdb[1] >> 2) & 7;
  1366. uint32_t lba =
  1367. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1368. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1369. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1370. scsiDev.cdb[5];
  1371. uint32_t blocks =
  1372. (((uint32_t) scsiDev.cdb[6]) << 16) +
  1373. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1374. (((uint32_t) scsiDev.cdb[8]));
  1375. uint8_t main_channel = scsiDev.cdb[9];
  1376. uint8_t sub_channel = scsiDev.cdb[10];
  1377. doReadCD(lba, blocks, sector_type, main_channel, sub_channel);
  1378. }
  1379. else if (command == 0xB9)
  1380. {
  1381. // ReadCD MSF
  1382. uint8_t sector_type = (scsiDev.cdb[1] >> 2) & 7;
  1383. uint32_t start = (scsiDev.cdb[3] * 60 + scsiDev.cdb[4]) * 75 + scsiDev.cdb[5];
  1384. uint32_t end = (scsiDev.cdb[6] * 60 + scsiDev.cdb[7]) * 75 + scsiDev.cdb[8];
  1385. uint8_t main_channel = scsiDev.cdb[9];
  1386. uint8_t sub_channel = scsiDev.cdb[10];
  1387. doReadCD(start, end - start, sector_type, main_channel, sub_channel);
  1388. }
  1389. else if (command == 0x42)
  1390. {
  1391. // Read subchannel data
  1392. bool time = (scsiDev.cdb[1] & 0x02);
  1393. bool subq = (scsiDev.cdb[2] & 0x40);
  1394. uint8_t parameter = scsiDev.cdb[3];
  1395. uint8_t track_number = scsiDev.cdb[6];
  1396. uint16_t allocationLength = (((uint32_t) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];
  1397. doReadSubchannel(time, subq, parameter, track_number, allocationLength);
  1398. }
  1399. else if (command == 0x28)
  1400. {
  1401. // READ(10) for CDs (may need sector translation for cue file handling)
  1402. uint32_t lba =
  1403. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1404. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1405. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1406. scsiDev.cdb[5];
  1407. uint32_t blocks =
  1408. (((uint32_t) scsiDev.cdb[7]) << 8) +
  1409. scsiDev.cdb[8];
  1410. doReadCD(lba, blocks, 0, 0x10, 0);
  1411. }
  1412. else if (command == 0xA8)
  1413. {
  1414. // READ(12) for CDs (may need sector translation for cue file handling)
  1415. uint32_t lba =
  1416. (((uint32_t) scsiDev.cdb[2]) << 24) +
  1417. (((uint32_t) scsiDev.cdb[3]) << 16) +
  1418. (((uint32_t) scsiDev.cdb[4]) << 8) +
  1419. scsiDev.cdb[5];
  1420. uint32_t blocks =
  1421. (((uint32_t) scsiDev.cdb[6]) << 24) +
  1422. (((uint32_t) scsiDev.cdb[7]) << 16) +
  1423. (((uint32_t) scsiDev.cdb[8]) << 8) +
  1424. scsiDev.cdb[9];
  1425. doReadCD(lba, blocks, 0, 0x10, 0);
  1426. }
  1427. else
  1428. {
  1429. commandHandled = 0;
  1430. }
  1431. return commandHandled;
  1432. }