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