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