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