123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 |
- /* Ported from:
- ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDevice.c,v
- ** Revision: 1.10
- ** Date: 2007-05-21 21:38:29 +0200 (Mon, 21 May 2007)
- **
- ** More info: http://www.bluemsx.com
- **
- ** Copyright (C) 2003-2007 Daniel Vik, white cat
- */
- /*
- * Notes:
- * It follows the SCSI1(CCS) standard or the SCSI2 standard.
- * Only the direct access device is supported now.
- * Message system might be imperfect.
- *
- * NOTE: this version only supports a non-removable harddisk, as the class
- * name suggests. Refer to revision 6526 of this file to see what was removed
- * from the generic/parameterised code.
- */
- #include "SCSIHD.hh"
- #include "FileOperations.hh"
- #include "MSXException.hh"
- #include "LedStatus.hh"
- #include "MSXMotherBoard.hh"
- #include "DeviceConfig.hh"
- #include "endian.hh"
- #include "one_of.hh"
- #include "serialize.hh"
- #include <algorithm>
- #include <cstring>
- using std::string;
- namespace openmsx {
- // Medium type (value like LS-120)
- constexpr byte MT_UNKNOWN = 0x00;
- constexpr byte MT_2DD_UN = 0x10;
- constexpr byte MT_2DD = 0x11;
- constexpr byte MT_2HD_UN = 0x20;
- constexpr byte MT_2HD_12_98 = 0x22;
- constexpr byte MT_2HD_12 = 0x23;
- constexpr byte MT_2HD_144 = 0x24;
- constexpr byte MT_LS120 = 0x31;
- constexpr byte MT_NO_DISK = 0x70;
- constexpr byte MT_DOOR_OPEN = 0x71;
- constexpr byte MT_FMT_ERROR = 0x72;
- constexpr byte inqdata[36] = {
- 0, // bit5-0 device type code.
- 0, // bit7 = 1 removable device
- 2, // bit7,6 ISO version. bit5,4,3 ECMA version.
- // bit2,1,0 ANSI Version (001=SCSI1, 010=SCSI2)
- 2, // bit7 AENC. bit6 TrmIOP.
- // bit3-0 Response Data Format. (0000=SCSI1, 0001=CCS, 0010=SCSI2)
- 51, // addtional length
- 0, 0,// reserved
- 0, // bit7 RelAdr, bit6 WBus32, bit5 Wbus16, bit4 Sync, bit3 Linked,
- // bit2 reseved bit1 CmdQue, bit0 SftRe
- 'o', 'p', 'e', 'n', 'M', 'S', 'X', ' ', // vendor ID (8bytes)
- 'S', 'C', 'S', 'I', '2', ' ', 'H', 'a', // product ID (16bytes)
- 'r', 'd', 'd', 'i', 's', 'k', ' ', ' ',
- '0', '1', '0', 'a' // product version (ASCII 4bytes)
- };
- constexpr unsigned BUFFER_BLOCK_SIZE = SCSIHD::BUFFER_SIZE /
- SectorAccessibleDisk::SECTOR_SIZE;
- SCSIHD::SCSIHD(const DeviceConfig& targetconfig,
- AlignedBuffer& buf, unsigned mode_)
- : HD(targetconfig)
- , buffer(buf)
- , mode(mode_)
- , scsiId(targetconfig.getAttributeAsInt("id"))
- {
- lun = 0; // move to reset() ?
- message = 0;
- reset();
- }
- void SCSIHD::reset()
- {
- currentSector = 0;
- currentLength = 0;
- busReset();
- }
- void SCSIHD::busReset()
- {
- keycode = 0;
- unitAttention = (mode & MODE_UNITATTENTION) != 0;
- }
- void SCSIHD::disconnect()
- {
- getMotherBoard().getLedStatus().setLed(LedStatus::FDD, false);
- }
- // Check the initiator in the call origin.
- bool SCSIHD::isSelected()
- {
- lun = 0;
- return true;
- }
- unsigned SCSIHD::inquiry()
- {
- unsigned length = currentLength;
- if (length == 0) return 0;
- memcpy(buffer + 2, inqdata + 2, 34);
- buffer[0] = SCSI::DT_DirectAccess;
- buffer[1] = 0; // removable
- if (!(mode & BIT_SCSI2)) {
- buffer[2] = 1;
- buffer[3] = 1;
- buffer[20] = '1';
- } else {
- if (mode & BIT_SCSI3) {
- buffer[2] = 5;
- buffer[20] = '3';
- }
- }
- if (mode & BIT_SCSI3) {
- length = std::min(length, 96u);
- buffer[4] = 91;
- if (length > 56) {
- memset(buffer + 56, 0, 40);
- buffer[58] = 0x03;
- buffer[60] = 0x01;
- buffer[61] = 0x80;
- }
- } else {
- length = std::min(length, 56u);
- }
- if (length > 36) {
- string imageName(FileOperations::getFilename(
- getImageName().getOriginal()));
- imageName.resize(20, ' ');
- memcpy(buffer + 36, imageName.data(), 20);
- }
- return length;
- }
- unsigned SCSIHD::modeSense()
- {
- byte* pBuffer = buffer;
- if ((currentLength > 0) && (cdb[2] == 3)) {
- // TODO check for too many sectors
- auto total = unsigned(getNbSectors());
- byte media = MT_UNKNOWN;
- byte sectors = 64;
- byte blockLength = SECTOR_SIZE >> 8;
- byte tracks = 8;
- byte size = 4 + 24;
- byte removable = 0x80; // == not removable
- memset(pBuffer + 2, 0, 34);
- if (total == 0) {
- media = MT_NO_DISK;
- }
- // Mode Parameter Header 4bytes
- pBuffer[1] = media; // Medium Type
- pBuffer[3] = 8; // block descripter length
- pBuffer += 4;
- // Disable Block Descriptor check
- if (!(cdb[1] & 0x08)) {
- // Block Descriptor 8bytes
- pBuffer[1] = (total >> 16) & 0xff; // 1..3 Number of Blocks
- pBuffer[2] = (total >> 8) & 0xff;
- pBuffer[3] = (total >> 0) & 0xff;
- pBuffer[6] = blockLength & 0xff; // 5..7 Block Length in Bytes
- pBuffer += 8;
- size += 8;
- }
- // Format Device Page 24bytes
- pBuffer[ 0] = 3; // 0 Page
- pBuffer[ 1] = 0x16; // 1 Page Length
- pBuffer[ 3] = tracks; // 2, 3 Tracks per Zone
- pBuffer[11] = sectors; // 10,11 Sectors per Track
- pBuffer[12] = blockLength; // 12,13 Data Bytes per Physical Sector
- pBuffer[20] = removable; // 20 bit7 Soft Sector bit5 Removable
- buffer[0] = size - 1; // sense data length
- return std::min<unsigned>(currentLength, size);
- }
- keycode = SCSI::SENSE_INVALID_COMMAND_CODE;
- return 0;
- }
- unsigned SCSIHD::requestSense()
- {
- unsigned length = currentLength;
- unsigned tmpKeycode = unitAttention ? SCSI::SENSE_POWER_ON : keycode;
- unitAttention = false;
- keycode = SCSI::SENSE_NO_SENSE;
- memset(buffer + 1, 0, 17);
- if (length == 0) {
- if (mode & BIT_SCSI2) {
- return 0;
- }
- buffer[ 0] = (tmpKeycode >> 8) & 0xff; // Sense code
- length = 4;
- } else {
- buffer[ 0] = 0x70;
- buffer[ 2] = (tmpKeycode >> 16) & 0xff; // Sense key
- buffer[ 7] = 10; // Additional sense length
- buffer[12] = (tmpKeycode >> 8) & 0xff; // Additional sense code
- buffer[13] = (tmpKeycode >> 0) & 0xff; // Additional sense code qualifier
- length = std::min(length, 18u);
- }
- return length;
- }
- bool SCSIHD::checkReadOnly()
- {
- if (isWriteProtected()) {
- keycode = SCSI::SENSE_WRITE_PROTECT;
- return true;
- }
- return false;
- }
- unsigned SCSIHD::readCapacity()
- {
- // TODO check for overflow
- auto block = unsigned(getNbSectors());
- if (block == 0) {
- // drive not ready
- keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT;
- return 0;
- }
- --block;
- Endian::writeB32(&buffer[0], block);
- 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?
- return 8;
- }
- bool SCSIHD::checkAddress()
- {
- auto total = unsigned(getNbSectors());
- if (total == 0) {
- // drive not ready
- keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT;
- return false;
- }
- if ((currentLength > 0) && (currentSector + currentLength <= total)) {
- return true;
- }
- keycode = SCSI::SENSE_ILLEGAL_BLOCK_ADDRESS;
- return false;
- }
- // Execute scsiDeviceCheckAddress previously.
- unsigned SCSIHD::readSectors(unsigned& blocks)
- {
- getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
- unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
- unsigned counter = currentLength * SECTOR_SIZE;
- try {
- for (unsigned i = 0; i < numSectors; ++i) {
- auto* sbuf = aligned_cast<SectorBuffer*>(buffer);
- readSector(currentSector, sbuf[i]);
- ++currentSector;
- --currentLength;
- }
- blocks = currentLength;
- return counter;
- } catch (MSXException&) {
- blocks = 0;
- keycode = SCSI::SENSE_UNRECOVERED_READ_ERROR;
- return 0;
- }
- }
- unsigned SCSIHD::dataIn(unsigned& blocks)
- {
- if (cdb[0] == SCSI::OP_READ10) {
- unsigned counter = readSectors(blocks);
- if (counter) return counter;
- }
- // error
- blocks = 0;
- return 0;
- }
- // Execute scsiDeviceCheckAddress and scsiDeviceCheckReadOnly previously.
- unsigned SCSIHD::writeSectors(unsigned& blocks)
- {
- getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
- unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
- try {
- for (unsigned i = 0; i < numSectors; ++i) {
- auto* sbuf = aligned_cast<const SectorBuffer*>(buffer);
- writeSector(currentSector, sbuf[i]);
- ++currentSector;
- --currentLength;
- }
- unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
- blocks = currentLength - tmp;
- unsigned counter = tmp * SECTOR_SIZE;
- return counter;
- } catch (MSXException&) {
- keycode = SCSI::SENSE_WRITE_FAULT;
- blocks = 0;
- return 0;
- }
- }
- unsigned SCSIHD::dataOut(unsigned& blocks)
- {
- if (cdb[0] == SCSI::OP_WRITE10) {
- return writeSectors(blocks);
- }
- // error
- blocks = 0;
- return 0;
- }
- // MBR erase only
- void SCSIHD::formatUnit()
- {
- if (!checkReadOnly()) {
- auto& sbuf = *aligned_cast<SectorBuffer*>(buffer);
- memset(&sbuf, 0, sizeof(sbuf));
- try {
- writeSector(0, sbuf);
- unitAttention = true;
- } catch (MSXException&) {
- keycode = SCSI::SENSE_WRITE_FAULT;
- }
- }
- }
- byte SCSIHD::getStatusCode()
- {
- return keycode ? SCSI::ST_CHECK_CONDITION : SCSI::ST_GOOD;
- }
- unsigned SCSIHD::executeCmd(const byte* cdb_, SCSI::Phase& phase, unsigned& blocks)
- {
- memcpy(cdb, cdb_, sizeof(cdb));
- message = 0;
- phase = SCSI::STATUS;
- blocks = 0;
- // check unit attention
- if (unitAttention && (mode & MODE_UNITATTENTION) &&
- (cdb[0] != one_of(SCSI::OP_INQUIRY, SCSI::OP_REQUEST_SENSE))) {
- unitAttention = false;
- keycode = SCSI::SENSE_POWER_ON;
- if (cdb[0] == SCSI::OP_TEST_UNIT_READY) {
- // changed = false;
- }
- // Unit Attention. This command is not executed.
- return 0;
- }
- // check LUN
- if (((cdb[1] & 0xe0) || lun) && (cdb[0] != SCSI::OP_REQUEST_SENSE) &&
- !(cdb[0] == SCSI::OP_INQUIRY && !(mode & MODE_NOVAXIS))) {
- keycode = SCSI::SENSE_INVALID_LUN;
- // check LUN error
- return 0;
- }
- if (cdb[0] != SCSI::OP_REQUEST_SENSE) {
- keycode = SCSI::SENSE_NO_SENSE;
- }
- if (cdb[0] < SCSI::OP_GROUP1) {
- currentSector = ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3];
- currentLength = cdb[4];
- switch (cdb[0]) {
- case SCSI::OP_TEST_UNIT_READY:
- return 0;
- case SCSI::OP_INQUIRY: {
- unsigned counter = inquiry();
- if (counter) {
- phase = SCSI::DATA_IN;
- }
- return counter;
- }
- case SCSI::OP_REQUEST_SENSE: {
- unsigned counter = requestSense();
- if (counter) {
- phase = SCSI::DATA_IN;
- }
- return counter;
- }
- case SCSI::OP_READ6:
- if (currentLength == 0) {
- currentLength = SECTOR_SIZE / 2;
- }
- if (checkAddress()) {
- unsigned counter = readSectors(blocks);
- if (counter) {
- cdb[0] = SCSI::OP_READ10;
- phase = SCSI::DATA_IN;
- return counter;
- }
- }
- return 0;
- case SCSI::OP_WRITE6:
- if (currentLength == 0) {
- currentLength = SECTOR_SIZE / 2;
- }
- if (checkAddress() && !checkReadOnly()) {
- getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
- unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
- blocks = currentLength - tmp;
- unsigned counter = tmp * SECTOR_SIZE;
- cdb[0] = SCSI::OP_WRITE10;
- phase = SCSI::DATA_OUT;
- return counter;
- }
- return 0;
- case SCSI::OP_SEEK6:
- getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
- currentLength = 1;
- checkAddress();
- return 0;
- case SCSI::OP_MODE_SENSE: {
- unsigned counter = modeSense();
- if (counter) {
- phase = SCSI::DATA_IN;
- }
- return counter;
- }
- case SCSI::OP_FORMAT_UNIT:
- formatUnit();
- return 0;
- case SCSI::OP_START_STOP_UNIT:
- // Not supported for this device
- return 0;
- case SCSI::OP_REZERO_UNIT:
- case SCSI::OP_REASSIGN_BLOCKS:
- case SCSI::OP_RESERVE_UNIT:
- case SCSI::OP_RELEASE_UNIT:
- case SCSI::OP_SEND_DIAGNOSTIC:
- // SCSI_Group0 dummy
- return 0;
- }
- } else {
- currentSector = Endian::read_UA_B32(&cdb[2]);
- currentLength = Endian::read_UA_B16(&cdb[7]);
- switch (cdb[0]) {
- case SCSI::OP_READ10:
- if (checkAddress()) {
- unsigned counter = readSectors(blocks);
- if (counter) {
- phase = SCSI::DATA_IN;
- return counter;
- }
- }
- return 0;
- case SCSI::OP_WRITE10:
- if (checkAddress() && !checkReadOnly()) {
- unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
- blocks = currentLength - tmp;
- unsigned counter = tmp * SECTOR_SIZE;
- phase = SCSI::DATA_OUT;
- return counter;
- }
- return 0;
- case SCSI::OP_READ_CAPACITY: {
- unsigned counter = readCapacity();
- if (counter) {
- phase = SCSI::DATA_IN;
- }
- return counter;
- }
- case SCSI::OP_SEEK10:
- getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true);
- currentLength = 1;
- checkAddress();
- return 0;
- }
- }
- // unsupported command
- keycode = SCSI::SENSE_INVALID_COMMAND_CODE;
- return 0;
- }
- unsigned SCSIHD::executingCmd(SCSI::Phase& phase, unsigned& blocks)
- {
- phase = SCSI::EXECUTE;
- blocks = 0;
- return 0; // Always for non-CD-ROM it seems
- }
- byte SCSIHD::msgIn()
- {
- byte result = message;
- message = 0;
- return result;
- }
- /*
- scsiDeviceMsgOut()
- Notes:
- [out]
- -1: Busfree demand. (Please process it in the call origin.)
- bit2: Status phase demand. Error happend.
- bit1: Make it to a busfree if ATN has not been released.
- bit0: There is a message(MsgIn).
- */
- int SCSIHD::msgOut(byte value)
- {
- if (value & 0x80) {
- lun = value & 7;
- return 0;
- }
- switch (value) {
- case SCSI::MSG_INITIATOR_DETECT_ERROR:
- keycode = SCSI::SENSE_INITIATOR_DETECTED_ERR;
- return 6;
- case SCSI::MSG_BUS_DEVICE_RESET:
- busReset();
- [[fallthrough]];
- case SCSI::MSG_ABORT:
- return -1;
- case SCSI::MSG_REJECT:
- case SCSI::MSG_PARITY_ERROR:
- case SCSI::MSG_NO_OPERATION:
- return 2;
- }
- message = SCSI::MSG_REJECT;
- return ((value >= 0x04) && (value <= 0x11)) ? 3 : 1;
- }
- template<typename Archive>
- void SCSIHD::serialize(Archive& ar, unsigned /*version*/)
- {
- // don't serialize SCSIDevice, SectorAccessibleDisk, DiskContainer
- // base classes
- ar.template serializeBase<HD>(*this);
- ar.serialize("keycode", keycode,
- "currentSector", currentSector,
- "currentLength", currentLength,
- "unitAttention", unitAttention,
- "message", message,
- "lun", lun);
- ar.serialize_blob("cdb", cdb, sizeof(cdb));
- }
- INSTANTIATE_SERIALIZE_METHODS(SCSIHD);
- REGISTER_POLYMORPHIC_INITIALIZER(SCSIDevice, SCSIHD, "SCSIHD");
- } // namespace openmsx
|