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