SCSIHD.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /* Ported from:
  2. ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDevice.c,v
  3. ** Revision: 1.10
  4. ** Date: 2007-05-21 21:38:29 +0200 (Mon, 21 May 2007)
  5. **
  6. ** More info: http://www.bluemsx.com
  7. **
  8. ** Copyright (C) 2003-2007 Daniel Vik, white cat
  9. */
  10. /*
  11. * Notes:
  12. * It follows the SCSI1(CCS) standard or the SCSI2 standard.
  13. * Only the direct access device is supported now.
  14. * Message system might be imperfect.
  15. *
  16. * NOTE: this version only supports a non-removable harddisk, as the class
  17. * name suggests. Refer to revision 6526 of this file to see what was removed
  18. * from the generic/parameterised code.
  19. */
  20. #include "SCSIHD.hh"
  21. #include "FileOperations.hh"
  22. #include "MSXException.hh"
  23. #include "LedStatus.hh"
  24. #include "MSXMotherBoard.hh"
  25. #include "DeviceConfig.hh"
  26. #include "endian.hh"
  27. #include "one_of.hh"
  28. #include "serialize.hh"
  29. #include <algorithm>
  30. #include <cstring>
  31. using std::string;
  32. namespace openmsx {
  33. // Medium type (value like LS-120)
  34. constexpr byte MT_UNKNOWN = 0x00;
  35. constexpr byte MT_2DD_UN = 0x10;
  36. constexpr byte MT_2DD = 0x11;
  37. constexpr byte MT_2HD_UN = 0x20;
  38. constexpr byte MT_2HD_12_98 = 0x22;
  39. constexpr byte MT_2HD_12 = 0x23;
  40. constexpr byte MT_2HD_144 = 0x24;
  41. constexpr byte MT_LS120 = 0x31;
  42. constexpr byte MT_NO_DISK = 0x70;
  43. constexpr byte MT_DOOR_OPEN = 0x71;
  44. constexpr byte MT_FMT_ERROR = 0x72;
  45. constexpr byte inqdata[36] = {
  46. 0, // bit5-0 device type code.
  47. 0, // bit7 = 1 removable device
  48. 2, // bit7,6 ISO version. bit5,4,3 ECMA version.
  49. // bit2,1,0 ANSI Version (001=SCSI1, 010=SCSI2)
  50. 2, // bit7 AENC. bit6 TrmIOP.
  51. // bit3-0 Response Data Format. (0000=SCSI1, 0001=CCS, 0010=SCSI2)
  52. 51, // addtional length
  53. 0, 0,// reserved
  54. 0, // bit7 RelAdr, bit6 WBus32, bit5 Wbus16, bit4 Sync, bit3 Linked,
  55. // bit2 reseved bit1 CmdQue, bit0 SftRe
  56. 'o', 'p', 'e', 'n', 'M', 'S', 'X', ' ', // vendor ID (8bytes)
  57. 'S', 'C', 'S', 'I', '2', ' ', 'H', 'a', // product ID (16bytes)
  58. 'r', 'd', 'd', 'i', 's', 'k', ' ', ' ',
  59. '0', '1', '0', 'a' // product version (ASCII 4bytes)
  60. };
  61. constexpr unsigned BUFFER_BLOCK_SIZE = SCSIHD::BUFFER_SIZE /
  62. SectorAccessibleDisk::SECTOR_SIZE;
  63. SCSIHD::SCSIHD(const DeviceConfig& targetconfig,
  64. AlignedBuffer& buf, unsigned mode_)
  65. : HD(targetconfig)
  66. , buffer(buf)
  67. , mode(mode_)
  68. , scsiId(targetconfig.getAttributeAsInt("id"))
  69. {
  70. lun = 0; // move to reset() ?
  71. message = 0;
  72. reset();
  73. }
  74. void SCSIHD::reset()
  75. {
  76. currentSector = 0;
  77. currentLength = 0;
  78. busReset();
  79. }
  80. void SCSIHD::busReset()
  81. {
  82. keycode = 0;
  83. unitAttention = (mode & MODE_UNITATTENTION) != 0;
  84. }
  85. void SCSIHD::disconnect()
  86. {
  87. getMotherBoard().getLedStatus().setLed(LedStatus::FDD, false);
  88. }
  89. // Check the initiator in the call origin.
  90. bool SCSIHD::isSelected()
  91. {
  92. lun = 0;
  93. return true;
  94. }
  95. unsigned SCSIHD::inquiry()
  96. {
  97. unsigned length = currentLength;
  98. if (length == 0) return 0;
  99. memcpy(buffer + 2, inqdata + 2, 34);
  100. buffer[0] = SCSI::DT_DirectAccess;
  101. buffer[1] = 0; // removable
  102. if (!(mode & BIT_SCSI2)) {
  103. buffer[2] = 1;
  104. buffer[3] = 1;
  105. buffer[20] = '1';
  106. } else {
  107. if (mode & BIT_SCSI3) {
  108. buffer[2] = 5;
  109. buffer[20] = '3';
  110. }
  111. }
  112. if (mode & BIT_SCSI3) {
  113. length = std::min(length, 96u);
  114. buffer[4] = 91;
  115. if (length > 56) {
  116. memset(buffer + 56, 0, 40);
  117. buffer[58] = 0x03;
  118. buffer[60] = 0x01;
  119. buffer[61] = 0x80;
  120. }
  121. } else {
  122. length = std::min(length, 56u);
  123. }
  124. if (length > 36) {
  125. string imageName(FileOperations::getFilename(
  126. getImageName().getOriginal()));
  127. imageName.resize(20, ' ');
  128. memcpy(buffer + 36, imageName.data(), 20);
  129. }
  130. return length;
  131. }
  132. unsigned SCSIHD::modeSense()
  133. {
  134. byte* pBuffer = buffer;
  135. if ((currentLength > 0) && (cdb[2] == 3)) {
  136. // TODO check for too many sectors
  137. auto total = unsigned(getNbSectors());
  138. byte media = MT_UNKNOWN;
  139. byte sectors = 64;
  140. byte blockLength = SECTOR_SIZE >> 8;
  141. byte tracks = 8;
  142. byte size = 4 + 24;
  143. byte removable = 0x80; // == not removable
  144. memset(pBuffer + 2, 0, 34);
  145. if (total == 0) {
  146. media = MT_NO_DISK;
  147. }
  148. // Mode Parameter Header 4bytes
  149. pBuffer[1] = media; // Medium Type
  150. pBuffer[3] = 8; // block descripter length
  151. pBuffer += 4;
  152. // Disable Block Descriptor check
  153. if (!(cdb[1] & 0x08)) {
  154. // Block Descriptor 8bytes
  155. pBuffer[1] = (total >> 16) & 0xff; // 1..3 Number of Blocks
  156. pBuffer[2] = (total >> 8) & 0xff;
  157. pBuffer[3] = (total >> 0) & 0xff;
  158. pBuffer[6] = blockLength & 0xff; // 5..7 Block Length in Bytes
  159. pBuffer += 8;
  160. size += 8;
  161. }
  162. // Format Device Page 24bytes
  163. pBuffer[ 0] = 3; // 0 Page
  164. pBuffer[ 1] = 0x16; // 1 Page Length
  165. pBuffer[ 3] = tracks; // 2, 3 Tracks per Zone
  166. pBuffer[11] = sectors; // 10,11 Sectors per Track
  167. pBuffer[12] = blockLength; // 12,13 Data Bytes per Physical Sector
  168. pBuffer[20] = removable; // 20 bit7 Soft Sector bit5 Removable
  169. buffer[0] = size - 1; // sense data length
  170. return std::min<unsigned>(currentLength, size);
  171. }
  172. keycode = SCSI::SENSE_INVALID_COMMAND_CODE;
  173. return 0;
  174. }
  175. unsigned SCSIHD::requestSense()
  176. {
  177. unsigned length = currentLength;
  178. unsigned tmpKeycode = unitAttention ? SCSI::SENSE_POWER_ON : keycode;
  179. unitAttention = false;
  180. keycode = SCSI::SENSE_NO_SENSE;
  181. memset(buffer + 1, 0, 17);
  182. if (length == 0) {
  183. if (mode & BIT_SCSI2) {
  184. return 0;
  185. }
  186. buffer[ 0] = (tmpKeycode >> 8) & 0xff; // Sense code
  187. length = 4;
  188. } else {
  189. buffer[ 0] = 0x70;
  190. buffer[ 2] = (tmpKeycode >> 16) & 0xff; // Sense key
  191. buffer[ 7] = 10; // Additional sense length
  192. buffer[12] = (tmpKeycode >> 8) & 0xff; // Additional sense code
  193. buffer[13] = (tmpKeycode >> 0) & 0xff; // Additional sense code qualifier
  194. length = std::min(length, 18u);
  195. }
  196. return length;
  197. }
  198. bool SCSIHD::checkReadOnly()
  199. {
  200. if (isWriteProtected()) {
  201. keycode = SCSI::SENSE_WRITE_PROTECT;
  202. return true;
  203. }
  204. return false;
  205. }
  206. unsigned SCSIHD::readCapacity()
  207. {
  208. // TODO check for overflow
  209. auto block = unsigned(getNbSectors());
  210. if (block == 0) {
  211. // drive not ready
  212. keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT;
  213. return 0;
  214. }
  215. --block;
  216. Endian::writeB32(&buffer[0], block);
  217. Endian::writeB32(&buffer[4], SECTOR_SIZE); // TODO is this a 32 bit field or 2x16-bit fields where the first field happens to have the value 0?
  218. return 8;
  219. }
  220. bool SCSIHD::checkAddress()
  221. {
  222. auto total = unsigned(getNbSectors());
  223. if (total == 0) {
  224. // drive not ready
  225. keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT;
  226. return false;
  227. }
  228. if ((currentLength > 0) && (currentSector + currentLength <= total)) {
  229. return true;
  230. }
  231. keycode = SCSI::SENSE_ILLEGAL_BLOCK_ADDRESS;
  232. return false;
  233. }
  234. // Execute scsiDeviceCheckAddress previously.
  235. unsigned SCSIHD::readSectors(unsigned& blocks)
  236. {
  237. getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
  238. unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
  239. unsigned counter = currentLength * SECTOR_SIZE;
  240. try {
  241. for (unsigned i = 0; i < numSectors; ++i) {
  242. auto* sbuf = aligned_cast<SectorBuffer*>(buffer);
  243. readSector(currentSector, sbuf[i]);
  244. ++currentSector;
  245. --currentLength;
  246. }
  247. blocks = currentLength;
  248. return counter;
  249. } catch (MSXException&) {
  250. blocks = 0;
  251. keycode = SCSI::SENSE_UNRECOVERED_READ_ERROR;
  252. return 0;
  253. }
  254. }
  255. unsigned SCSIHD::dataIn(unsigned& blocks)
  256. {
  257. if (cdb[0] == SCSI::OP_READ10) {
  258. unsigned counter = readSectors(blocks);
  259. if (counter) return counter;
  260. }
  261. // error
  262. blocks = 0;
  263. return 0;
  264. }
  265. // Execute scsiDeviceCheckAddress and scsiDeviceCheckReadOnly previously.
  266. unsigned SCSIHD::writeSectors(unsigned& blocks)
  267. {
  268. getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
  269. unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
  270. try {
  271. for (unsigned i = 0; i < numSectors; ++i) {
  272. auto* sbuf = aligned_cast<const SectorBuffer*>(buffer);
  273. writeSector(currentSector, sbuf[i]);
  274. ++currentSector;
  275. --currentLength;
  276. }
  277. unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
  278. blocks = currentLength - tmp;
  279. unsigned counter = tmp * SECTOR_SIZE;
  280. return counter;
  281. } catch (MSXException&) {
  282. keycode = SCSI::SENSE_WRITE_FAULT;
  283. blocks = 0;
  284. return 0;
  285. }
  286. }
  287. unsigned SCSIHD::dataOut(unsigned& blocks)
  288. {
  289. if (cdb[0] == SCSI::OP_WRITE10) {
  290. return writeSectors(blocks);
  291. }
  292. // error
  293. blocks = 0;
  294. return 0;
  295. }
  296. // MBR erase only
  297. void SCSIHD::formatUnit()
  298. {
  299. if (!checkReadOnly()) {
  300. auto& sbuf = *aligned_cast<SectorBuffer*>(buffer);
  301. memset(&sbuf, 0, sizeof(sbuf));
  302. try {
  303. writeSector(0, sbuf);
  304. unitAttention = true;
  305. } catch (MSXException&) {
  306. keycode = SCSI::SENSE_WRITE_FAULT;
  307. }
  308. }
  309. }
  310. byte SCSIHD::getStatusCode()
  311. {
  312. return keycode ? SCSI::ST_CHECK_CONDITION : SCSI::ST_GOOD;
  313. }
  314. unsigned SCSIHD::executeCmd(const byte* cdb_, SCSI::Phase& phase, unsigned& blocks)
  315. {
  316. memcpy(cdb, cdb_, sizeof(cdb));
  317. message = 0;
  318. phase = SCSI::STATUS;
  319. blocks = 0;
  320. // check unit attention
  321. if (unitAttention && (mode & MODE_UNITATTENTION) &&
  322. (cdb[0] != one_of(SCSI::OP_INQUIRY, SCSI::OP_REQUEST_SENSE))) {
  323. unitAttention = false;
  324. keycode = SCSI::SENSE_POWER_ON;
  325. if (cdb[0] == SCSI::OP_TEST_UNIT_READY) {
  326. // changed = false;
  327. }
  328. // Unit Attention. This command is not executed.
  329. return 0;
  330. }
  331. // check LUN
  332. if (((cdb[1] & 0xe0) || lun) && (cdb[0] != SCSI::OP_REQUEST_SENSE) &&
  333. !(cdb[0] == SCSI::OP_INQUIRY && !(mode & MODE_NOVAXIS))) {
  334. keycode = SCSI::SENSE_INVALID_LUN;
  335. // check LUN error
  336. return 0;
  337. }
  338. if (cdb[0] != SCSI::OP_REQUEST_SENSE) {
  339. keycode = SCSI::SENSE_NO_SENSE;
  340. }
  341. if (cdb[0] < SCSI::OP_GROUP1) {
  342. currentSector = ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3];
  343. currentLength = cdb[4];
  344. switch (cdb[0]) {
  345. case SCSI::OP_TEST_UNIT_READY:
  346. return 0;
  347. case SCSI::OP_INQUIRY: {
  348. unsigned counter = inquiry();
  349. if (counter) {
  350. phase = SCSI::DATA_IN;
  351. }
  352. return counter;
  353. }
  354. case SCSI::OP_REQUEST_SENSE: {
  355. unsigned counter = requestSense();
  356. if (counter) {
  357. phase = SCSI::DATA_IN;
  358. }
  359. return counter;
  360. }
  361. case SCSI::OP_READ6:
  362. if (currentLength == 0) {
  363. currentLength = SECTOR_SIZE / 2;
  364. }
  365. if (checkAddress()) {
  366. unsigned counter = readSectors(blocks);
  367. if (counter) {
  368. cdb[0] = SCSI::OP_READ10;
  369. phase = SCSI::DATA_IN;
  370. return counter;
  371. }
  372. }
  373. return 0;
  374. case SCSI::OP_WRITE6:
  375. if (currentLength == 0) {
  376. currentLength = SECTOR_SIZE / 2;
  377. }
  378. if (checkAddress() && !checkReadOnly()) {
  379. getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
  380. unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
  381. blocks = currentLength - tmp;
  382. unsigned counter = tmp * SECTOR_SIZE;
  383. cdb[0] = SCSI::OP_WRITE10;
  384. phase = SCSI::DATA_OUT;
  385. return counter;
  386. }
  387. return 0;
  388. case SCSI::OP_SEEK6:
  389. getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
  390. currentLength = 1;
  391. checkAddress();
  392. return 0;
  393. case SCSI::OP_MODE_SENSE: {
  394. unsigned counter = modeSense();
  395. if (counter) {
  396. phase = SCSI::DATA_IN;
  397. }
  398. return counter;
  399. }
  400. case SCSI::OP_FORMAT_UNIT:
  401. formatUnit();
  402. return 0;
  403. case SCSI::OP_START_STOP_UNIT:
  404. // Not supported for this device
  405. return 0;
  406. case SCSI::OP_REZERO_UNIT:
  407. case SCSI::OP_REASSIGN_BLOCKS:
  408. case SCSI::OP_RESERVE_UNIT:
  409. case SCSI::OP_RELEASE_UNIT:
  410. case SCSI::OP_SEND_DIAGNOSTIC:
  411. // SCSI_Group0 dummy
  412. return 0;
  413. }
  414. } else {
  415. currentSector = Endian::read_UA_B32(&cdb[2]);
  416. currentLength = Endian::read_UA_B16(&cdb[7]);
  417. switch (cdb[0]) {
  418. case SCSI::OP_READ10:
  419. if (checkAddress()) {
  420. unsigned counter = readSectors(blocks);
  421. if (counter) {
  422. phase = SCSI::DATA_IN;
  423. return counter;
  424. }
  425. }
  426. return 0;
  427. case SCSI::OP_WRITE10:
  428. if (checkAddress() && !checkReadOnly()) {
  429. unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
  430. blocks = currentLength - tmp;
  431. unsigned counter = tmp * SECTOR_SIZE;
  432. phase = SCSI::DATA_OUT;
  433. return counter;
  434. }
  435. return 0;
  436. case SCSI::OP_READ_CAPACITY: {
  437. unsigned counter = readCapacity();
  438. if (counter) {
  439. phase = SCSI::DATA_IN;
  440. }
  441. return counter;
  442. }
  443. case SCSI::OP_SEEK10:
  444. getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
  445. currentLength = 1;
  446. checkAddress();
  447. return 0;
  448. }
  449. }
  450. // unsupported command
  451. keycode = SCSI::SENSE_INVALID_COMMAND_CODE;
  452. return 0;
  453. }
  454. unsigned SCSIHD::executingCmd(SCSI::Phase& phase, unsigned& blocks)
  455. {
  456. phase = SCSI::EXECUTE;
  457. blocks = 0;
  458. return 0; // Always for non-CD-ROM it seems
  459. }
  460. byte SCSIHD::msgIn()
  461. {
  462. byte result = message;
  463. message = 0;
  464. return result;
  465. }
  466. /*
  467. scsiDeviceMsgOut()
  468. Notes:
  469. [out]
  470. -1: Busfree demand. (Please process it in the call origin.)
  471. bit2: Status phase demand. Error happend.
  472. bit1: Make it to a busfree if ATN has not been released.
  473. bit0: There is a message(MsgIn).
  474. */
  475. int SCSIHD::msgOut(byte value)
  476. {
  477. if (value & 0x80) {
  478. lun = value & 7;
  479. return 0;
  480. }
  481. switch (value) {
  482. case SCSI::MSG_INITIATOR_DETECT_ERROR:
  483. keycode = SCSI::SENSE_INITIATOR_DETECTED_ERR;
  484. return 6;
  485. case SCSI::MSG_BUS_DEVICE_RESET:
  486. busReset();
  487. [[fallthrough]];
  488. case SCSI::MSG_ABORT:
  489. return -1;
  490. case SCSI::MSG_REJECT:
  491. case SCSI::MSG_PARITY_ERROR:
  492. case SCSI::MSG_NO_OPERATION:
  493. return 2;
  494. }
  495. message = SCSI::MSG_REJECT;
  496. return ((value >= 0x04) && (value <= 0x11)) ? 3 : 1;
  497. }
  498. template<typename Archive>
  499. void SCSIHD::serialize(Archive& ar, unsigned /*version*/)
  500. {
  501. // don't serialize SCSIDevice, SectorAccessibleDisk, DiskContainer
  502. // base classes
  503. ar.template serializeBase<HD>(*this);
  504. ar.serialize("keycode", keycode,
  505. "currentSector", currentSector,
  506. "currentLength", currentLength,
  507. "unitAttention", unitAttention,
  508. "message", message,
  509. "lun", lun);
  510. ar.serialize_blob("cdb", cdb, sizeof(cdb));
  511. }
  512. INSTANTIATE_SERIALIZE_METHODS(SCSIHD);
  513. REGISTER_POLYMORPHIC_INITIALIZER(SCSIDevice, SCSIHD, "SCSIHD");
  514. } // namespace openmsx