From fac75a28babb087d323a49af4814ac173f54ebc3 Mon Sep 17 00:00:00 2001 From: Troy Date: Sat, 28 May 2022 17:27:01 -0400 Subject: [PATCH] Switched to function table for SCSI commands SCSI device state is now per device instead of global --- src/BlueSCSI.cpp | 592 +++++++++++++++++++++++++++++++---------------- 1 file changed, 390 insertions(+), 202 deletions(-) diff --git a/src/BlueSCSI.cpp b/src/BlueSCSI.cpp index 5342757..7413271 100644 --- a/src/BlueSCSI.cpp +++ b/src/BlueSCSI.cpp @@ -52,8 +52,9 @@ // 2: Debug information output to LOG.txt (slow) // SCSI config -#define NUM_SCSIID 7 // Maximum number of supported SCSI-IDs (The minimum is 0) -#define NUM_SCSILUN 2 // Maximum number of LUNs supported (The minimum is 0) +#define NUM_SCSIID 7 // Maximum number of supported SCSI-IDs (The minimum is 1) +#define MAX_SCSILUN 1 // Maximum number of LUNs supported (The minimum is 1) +#define NUM_SCSILUN 1 // number of LUNs enabled (The minimum is 1) #define READ_PARITY_CHECK 0 // Perform read parity check (unverified) #define SCSI_BUF_SIZE 512 // Size of the SCSI Buffer // HDD format @@ -114,7 +115,7 @@ SdFs SD; #define SD_CS PA4 // SDCARD:CS #define LED PC13 // LED -// Image Set Selector +// Image Set Selectord #ifdef XCVR #define IMAGE_SELECT1 PC14 #define IMAGE_SELECT2 PC15 @@ -296,6 +297,122 @@ byte SCSI_INFO_BUF[36] = { '1', '.', '0', ' ' // version 4 }; +enum SCSI_DEVICE_TYPE +{ + SCSI_DEVICE_HDD, + SCSI_DEVICE_OPTICAL, +}; + +struct SCSI_INQUIRY_DATA +{ + union + { + struct { + // bitfields are in REVERSE order for ARM + // byte 0 + byte peripheral_device_type:5; + byte peripheral_qualifier:3; + // byte 1 + byte reserved_byte2:7; + byte rmb:1; + // byte 2 + byte ansi_version:3; + byte always_zero_byte3:5; + // byte 3 + byte response_format:4; + byte reserved_byte4:2; + byte tiop:1; + byte always_zero_byte4:1; + // byte 4 + byte additional_length; + // byte 5-6 + byte reserved_byte5; + byte reserved_byte6; + // byte 7 + byte sync:1; + byte always_zero_byte7_more:4; + byte always_zero_byte7:3; + // byte 8-15 + char vendor[8]; + // byte 16-31 + char product[16]; + // byte 32-35 + char revision[4]; + // byte 36 + byte release; + // 37-46 + char revision_date[10]; + }; + // raw bytes + byte raw[64]; + }; +}; + +// HDD image +typedef __attribute__((aligned(4))) struct _SCSI_DEVICE +{ + FsFile m_file; // File object + uint64_t m_fileSize; // File size + size_t m_blocksize; // SCSI BLOCK size + SCSI_DEVICE_TYPE m_type; // SCSI device type + uint32_t m_blockcount; // blockcount + SCSI_INQUIRY_DATA inquiry_block; // SCSI information + uint8_t m_senseKey; // Sense key + uint16_t m_additional_sense_code; // ASC/ASCQ + + // Optical + bool m_raw; // Raw disk + bool m_mode2; // MODE2 CDROM +} SCSI_DEVICE; + +SCSI_DEVICE scsi_device_list[NUM_SCSIID][MAX_SCSILUN]; // Maximum number + +static byte onUnimplemented(SCSI_DEVICE *dev, const byte *cdb) +{ + // does nothing! + if(Serial) + { + Serial.print("Unimplemented SCSI command: "); + Serial.println(cdb[0], 16); + } + + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_OPERATION_CODE; + return SCSI_STATUS_CHECK_CONDITION; +} + +static byte onNOP(SCSI_DEVICE *dev, const byte *cdb) +{ + dev->m_senseKey = 0; + dev->m_additional_sense_code = 0; + return SCSI_STATUS_GOOD; +} + +#define MAX_SCSI_COMMAND 0xff + +// function table +byte (*scsi_command_table[MAX_SCSI_COMMAND])(SCSI_DEVICE *dev, const byte *cdb); + +#define SCSI_COMMAND_HANDLER(x) static byte x(SCSI_DEVICE *dev, const byte *cdb) + +// scsi command functions +SCSI_COMMAND_HANDLER(onRequestSense); +SCSI_COMMAND_HANDLER(onRead6); +SCSI_COMMAND_HANDLER(onRead10); +SCSI_COMMAND_HANDLER(onWrite6); +SCSI_COMMAND_HANDLER(onWrite10); +SCSI_COMMAND_HANDLER(onInquiry); +SCSI_COMMAND_HANDLER(onModeSense); +SCSI_COMMAND_HANDLER(onReadCapacity); +SCSI_COMMAND_HANDLER(onModeSense); +SCSI_COMMAND_HANDLER(onModeSelect); +SCSI_COMMAND_HANDLER(onVerify); +SCSI_COMMAND_HANDLER(onReadBuffer); +SCSI_COMMAND_HANDLER(onWriteBuffer); +SCSI_COMMAND_HANDLER(onTestUnitReady); +SCSI_COMMAND_HANDLER(onReZeroUnit); +SCSI_COMMAND_HANDLER(onSendDiagnostic); + void onFalseInit(void); void noSDCardFound(void); void onBusReset(void); @@ -385,31 +502,32 @@ void readSDCardInfo() * Open HDD image file */ -bool hddimageOpen(HDDIMG *h, FsFile file,int id,int lun,int blocksize) +bool hddimageOpen(SCSI_DEVICE *dev, FsFile file,int id,int lun,int blocksize) { - h->m_fileSize = 0; - h->m_blocksize = blocksize; - h->m_file = file; - if(h->m_file.isOpen()) + dev->m_fileSize= 0; + dev->m_blocksize = blocksize; + dev->m_file = file; + dev->m_type = SCSI_DEVICE_HDD; + if(dev->m_file.isOpen()) { - h->m_fileSize = h->m_file.size(); - if(h->m_fileSize>0) + dev->m_fileSize = dev->m_file.size(); + if(dev->m_fileSize>0) { // check blocksize dummy file LOG_FILE.print(" / "); - LOG_FILE.print(h->m_fileSize); + LOG_FILE.print(dev->m_fileSize); LOG_FILE.print("bytes / "); - LOG_FILE.print(h->m_fileSize / 1024); + LOG_FILE.print(dev->m_fileSize / 1024); LOG_FILE.print("KiB / "); - LOG_FILE.print(h->m_fileSize / 1024 / 1024); + LOG_FILE.print(dev->m_fileSize / 1024 / 1024); LOG_FILE.println("MiB"); return true; // File opened } else { LOG_FILE.println(" - file is 0 bytes, can not use."); - h->m_file.close(); - h->m_fileSize = h->m_blocksize = 0; // no file + dev->m_file.close(); + dev->m_fileSize = dev->m_blocksize = 0; // no file } } return false; @@ -430,6 +548,41 @@ void setup() db_bsrr[i] = DBP(i); } + // Default all SCSI command handlers to onUnimplemented + for(unsigned i = 0; i < MAX_SCSI_COMMAND; i++) + { + scsi_command_table[i] = onUnimplemented; + } + + // SCSI commands that just need to return ok + scsi_command_table[SCSI_FORMAT_UNIT4] = onNOP; + scsi_command_table[SCSI_FORMAT_UNIT6] = onNOP; + scsi_command_table[SCSI_REASSIGN_BLOCKS] = onNOP; + scsi_command_table[SCSI_SEEK6] = onNOP; + scsi_command_table[SCSI_SEEK10] = onNOP; + scsi_command_table[SCSI_START_STOP_UNIT] = onNOP; + scsi_command_table[SCSI_PREVENT_ALLOW_REMOVAL] = onNOP; + + // SCSI commands that have handlers + scsi_command_table[SCSI_TEST_UNIT_READY] = onTestUnitReady; + scsi_command_table[SCSI_REZERO_UNIT] = onReZeroUnit; + scsi_command_table[SCSI_REQUEST_SENSE] = onRequestSense; + scsi_command_table[SCSI_READ6] = onRead6; + scsi_command_table[SCSI_READ10] = onRead10; + scsi_command_table[SCSI_WRITE6] = onWrite6; + scsi_command_table[SCSI_WRITE10] = onWrite10; + scsi_command_table[SCSI_INQUIRY] = onInquiry; + scsi_command_table[SCSI_MODE_SELECT6] = onModeSense; + scsi_command_table[SCSI_READ_CAPACITY] = onReadCapacity; + scsi_command_table[SCSI_MODE_SENSE6] = onModeSense; + scsi_command_table[SCSI_MODE_SENSE10] = onModeSense; + scsi_command_table[SCSI_MODE_SELECT6] = onModeSelect; + scsi_command_table[SCSI_MODE_SELECT10] = onModeSelect; + scsi_command_table[SCSI_VERIFY10] = onVerify; + scsi_command_table[SCSI_READ_BUFFER] = onReadBuffer; + scsi_command_table[SCSI_WRITE_BUFFER] = onWriteBuffer; + scsi_command_table[SCSI_SEND_DIAG] = onSendDiagnostic; + // Serial initialization #if DEBUG > 0 Serial.begin(9600); @@ -576,6 +729,7 @@ void findDriveImages(FsFile root) { char path_name[MAX_FILE_PATH+1]; root.getName(path_name, sizeof(path_name)); SD.chdir(path_name); + SCSI_DEVICE *dev = NULL; while (1) { // Directories can not be opened RDWR, so it will fail, but fails the same way with no file/dir, so we need to peek at the file first. @@ -646,10 +800,10 @@ void findDriveImages(FsFile root) { } if(id < NUM_SCSIID && lun < NUM_SCSILUN) { - HDDIMG *h = &img[id][lun]; + dev = &scsi_device_list[id][lun]; LOG_FILE.print(" - "); LOG_FILE.print(name); - image_ready = hddimageOpen(h, file, id, lun, blk); + image_ready = hddimageOpen(dev, file, id, lun, blk); if(image_ready) { // Marked as a responsive ID scsi_id_mask |= 1<m_file)) + SCSI_DEVICE *dev = &scsi_device_list[id][lun]; + if( (lunm_file)) { - LOG_FILE.print((h->m_blocksize<1000) ? ": " : ":"); - LOG_FILE.print(h->m_blocksize); + LOG_FILE.print((dev->m_blocksize<1000) ? ": " : ":"); + LOG_FILE.print(dev->m_blocksize); } else LOG_FILE.print(":----"); @@ -968,23 +1122,23 @@ void writeDataPhase(int len, const byte* p) * Data in phase. * Send len block while reading from SD card. */ -void writeDataPhaseSD(uint32_t adds, uint32_t len) +void writeDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len) { LOGN("DATAIN PHASE(SD)"); SCSI_PHASE_CHANGE(SCSI_PHASE_DATAIN); //Bus settle delay 400ns, file.seek() measured at over 1000ns. - uint64_t pos = (uint64_t)adds * m_img->m_blocksize; - m_img->m_file.seekSet(pos); + uint64_t pos = (uint64_t)adds * dev->m_blocksize; + dev->m_file.seekSet(pos); SCSI_DB_OUTPUT() for(uint32_t i = 0; i < len; i++) { // Asynchronous reads will make it faster ... m_resetJmp = false; - m_img->m_file.read(m_buf, m_img->m_blocksize); + m_img->m_file.read(m_buf, dev->m_blocksize); enableResetJmp(); - writeDataLoop(m_img->m_blocksize, m_buf); + writeDataLoop(dev->m_blocksize, m_buf); } SCSI_DB_INPUT() #ifdef XCVR @@ -1050,25 +1204,25 @@ void readDataPhase(int len, byte* p) * Data out phase. * Write to SD card while reading len block. */ -void readDataPhaseSD(uint32_t adds, uint32_t len) +void readDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len) { LOGN("DATAOUT PHASE(SD)"); SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT); //Bus settle delay 400ns, file.seek() measured at over 1000ns. - uint64_t pos = (uint64_t)adds * m_img->m_blocksize; - m_img->m_file.seekSet(pos); + uint64_t pos = (uint64_t)adds * dev->m_blocksize; + dev->m_file.seekSet(pos); for(uint32_t i = 0; i < len; i++) { m_resetJmp = true; - readDataLoop(m_img->m_blocksize, m_buf); + readDataLoop(dev->m_blocksize, m_buf); m_resetJmp = false; - m_img->m_file.write(m_buf, m_img->m_blocksize); + dev->m_file.write(m_buf, dev->m_blocksize); // If a reset happened while writing, break and let the flush happen before it is handled. if (m_isBusReset) { break; } } - m_img->m_file.flush(); + dev->m_file.flush(); enableResetJmp(); } @@ -1076,16 +1230,16 @@ void readDataPhaseSD(uint32_t adds, uint32_t len) * Data out phase. * Compare to SD card while reading len block. */ -void verifyDataPhaseSD(uint32_t adds, uint32_t len) +void verifyDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len) { LOGN("DATAOUT PHASE(SD)"); SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT); //Bus settle delay 400ns, file.seek() measured at over 1000ns. - uint64_t pos = (uint64_t)adds * m_img->m_blocksize; - m_img->m_file.seekSet(pos); + uint64_t pos = (uint64_t)adds * dev->m_blocksize; + dev->m_file.seekSet(pos); for(uint32_t i = 0; i < len; i++) { - readDataLoop(m_img->m_blocksize, m_buf); + readDataLoop(dev->m_blocksize, m_buf); // This has just gone through the transfer to make things work, a compare would go here. } } @@ -1093,46 +1247,48 @@ void verifyDataPhaseSD(uint32_t adds, uint32_t len) /* * INQUIRY command processing. */ -byte onInquiryCommand(byte len) +byte onInquiry(SCSI_DEVICE *dev, const byte *cdb) { - writeDataPhase(len < 36 ? len : 36, SCSI_INFO_BUF); + writeDataPhase(cdb[4] < 36 ? cdb[4] : 36, dev->inquiry_block.raw); return SCSI_STATUS_GOOD; } /* * REQUEST SENSE command processing. */ -void onRequestSenseCommand(byte len) +byte onRequestSense(SCSI_DEVICE *dev, const byte *cdb) { byte buf[18] = { 0x70, //CheckCondition 0, //Segment number - m_senseKey, //Sense key + dev->m_senseKey, //Sense key 0, 0, 0, 0, //information 10, //Additional data length 0, 0, 0, 0, // command specific information bytes - (byte)(m_addition_sense >> 8), - (byte)m_addition_sense, + (byte)(dev->m_additional_sense_code >> 8), + (byte)dev->m_additional_sense_code, 0, 0, 0, 0, }; - m_senseKey = 0; - m_addition_sense = 0; - writeDataPhase(len < 18 ? len : 18, buf); + dev->m_senseKey = 0; + dev->m_additional_sense_code = 0; + writeDataPhase(cdb[4] < 18 ? cdb[4] : 18, buf); + + return SCSI_STATUS_GOOD; } /* * READ CAPACITY command processing. */ -byte onReadCapacityCommand(byte pmi) +byte onReadCapacity(SCSI_DEVICE *dev, const byte *cdb) { - if(!m_img) { + if(!dev->m_file.isOpen()) { m_senseKey = SCSI_SENSE_NOT_READY; m_addition_sense = SCSI_ASC_LUN_NOT_READY_MANUAL_INTERVENTION_REQUIRED; return SCSI_STATUS_CHECK_CONDITION; } - uint32_t bl = m_img->m_blocksize; - uint32_t bc = m_img->m_fileSize / bl - 1; // Points to last LBA + uint32_t bl = dev->m_blocksize; + uint32_t bc = dev->m_fileSize / bl - 1; // Points to last LBA uint8_t buf[8] = { bc >> 24, bc >> 16, bc >> 8, bc, bl >> 24, bl >> 16, bl >> 8, bl @@ -1144,16 +1300,16 @@ byte onReadCapacityCommand(byte pmi) /* * Check that the image file is present and the block range is valid. */ -byte checkBlockCommand(uint32_t adds, uint32_t len) +byte checkBlockCommand(SCSI_DEVICE *dev, uint32_t adds, uint32_t len) { // Check that image file is present - if(!m_img) { + if(!dev->m_file.isOpen()) { m_senseKey = SCSI_SENSE_NOT_READY; m_addition_sense = SCSI_ASC_LUN_NOT_READY_MANUAL_INTERVENTION_REQUIRED; return SCSI_STATUS_CHECK_CONDITION; } // Check block range is valid - uint32_t bc = m_img->m_fileSize / m_img->m_blocksize; + uint32_t bc = dev->m_fileSize / dev->m_blocksize; if (adds >= bc || (adds + len) > bc) { m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; m_addition_sense = SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; @@ -1165,59 +1321,126 @@ byte checkBlockCommand(uint32_t adds, uint32_t len) /* * READ6 / 10 Command processing. */ -byte onReadCommand(uint32_t adds, uint32_t len) +static byte onRead6(SCSI_DEVICE *dev, const byte *cdb) { - LOGN("-R"); - LOGHEXN(adds); + unsigned adds = (((uint32_t)cdb[1] & 0x1F) << 16) | ((uint32_t)cdb[2] << 8) | cdb[3]; + unsigned len = (cdb[4] == 0) ? 0x100 : cdb[4]; + /* + LOGN("onRead6"); + LOG("-R "); + LOGHEX(adds); + LOG(":"); LOGHEXN(len); + */ + + byte sts = checkBlockCommand(dev, adds, len); + if (sts) { + return sts; + } + + writeDataPhaseSD(dev, adds, len); + return SCSI_STATUS_GOOD; +} - byte sts = checkBlockCommand(adds, len); +static byte onRead10(SCSI_DEVICE *dev, const byte *cdb) +{ + unsigned adds = ((uint32_t)cdb[2] << 24) | ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5]; + unsigned len = ((uint32_t)cdb[7] << 8) | cdb[8]; + /* + LOGN("onRead10"); + LOG("-R "); + LOGHEX(adds); + LOG(":"); + LOGHEXN(len); + */ + byte sts = checkBlockCommand(dev, adds, len); if (sts) { return sts; } - LED_ON(); - writeDataPhaseSD(adds, len); - LED_OFF(); + + writeDataPhaseSD(dev, adds, len); return SCSI_STATUS_GOOD; } /* * WRITE6 / 10 Command processing. */ -byte onWriteCommand(uint32_t adds, uint32_t len) +static byte onWrite6(SCSI_DEVICE *dev, const byte *cdb) { - LOGN("-W"); - LOGHEXN(adds); + unsigned adds = (((uint32_t)cdb[1] & 0x1F) << 16) | ((uint32_t)cdb[2] << 8) | cdb[3]; + unsigned len = (cdb[4] == 0) ? 0x100 : cdb[4]; + /* + LOGN("onWrite6"); + LOG("-W "); + LOGHEX(adds); + LOG(":"); LOGHEXN(len); + */ + + if(dev->m_type == SCSI_DEVICE_OPTICAL) + { + dev->m_senseKey = SCSI_SENSE_HARDWARE_ERROR; + dev->m_additional_sense_code = SCSI_ASC_WRITE_PROTECTED; // Write Protect + return SCSI_STATUS_CHECK_CONDITION; + } - byte sts = checkBlockCommand(adds, len); + byte sts = checkBlockCommand(dev, adds, len); if (sts) { return sts; } - LED_ON(); - readDataPhaseSD(adds, len); - LED_OFF(); + readDataPhaseSD(dev, adds, len); return SCSI_STATUS_GOOD; } +static byte onWrite10(SCSI_DEVICE *dev, const byte *cdb) +{ + unsigned adds = ((uint32_t)cdb[2] << 24) | ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5]; + unsigned len = ((uint32_t)cdb[7] << 8) | cdb[8]; + /* + LOGN("onWrite10"); + LOG("-W "); + LOGHEX(adds); + LOG(":"); + LOGHEXN(len); + */ + + if(dev->m_type == SCSI_DEVICE_OPTICAL) + { + dev->m_senseKey = SCSI_SENSE_HARDWARE_ERROR; + dev->m_additional_sense_code = SCSI_ASC_WRITE_PROTECTED; // Write Protect + return SCSI_STATUS_CHECK_CONDITION; + } + + byte sts = checkBlockCommand(dev, adds, len); + if (sts) { + return sts; + } + + readDataPhaseSD(dev, adds, len); + return SCSI_STATUS_GOOD; +} /* * VERIFY10 Command processing. */ -byte onVerifyCommand(byte flags, uint32_t adds, uint32_t len) +//byte onVerifyCommand(byte flags, uint32_t adds, uint32_t len) +byte onVerify(SCSI_DEVICE *dev, const byte *cdb) { - byte sts = checkBlockCommand(adds, len); + unsigned adds = ((uint32_t)cdb[2] << 24) | ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5]; + unsigned len = ((uint32_t)cdb[7] << 8) | cdb[8]; + + byte sts = checkBlockCommand(dev, adds, len); if (sts) { return sts; } - int bytchk = (flags >> 1) & 0x03; + int bytchk = (cdb[1] >> 1) & 0x03; if (bytchk != 0) { if (bytchk == 3) { // Data-Out buffer is single logical block for repeated verification. - len = m_img->m_blocksize; + len = dev->m_blocksize; } LED_ON(); - verifyDataPhaseSD(adds, len); + verifyDataPhaseSD(dev, adds, len); LED_OFF(); } return SCSI_STATUS_GOOD; @@ -1226,22 +1449,24 @@ byte onVerifyCommand(byte flags, uint32_t adds, uint32_t len) /* * MODE SENSE command processing. */ -byte onModeSenseCommand(byte scsi_cmd, byte dbd, byte cmd2, uint32_t len) +byte onModeSense(SCSI_DEVICE *dev, const byte *cdb) { - if(!m_img) { + if(!dev->m_file.isOpen()) { m_senseKey = SCSI_SENSE_NOT_READY; m_addition_sense = SCSI_ASC_LUN_NOT_READY_MANUAL_INTERVENTION_REQUIRED; return SCSI_STATUS_CHECK_CONDITION; } - uint32_t bl = m_img->m_blocksize; - uint32_t bc = m_img->m_fileSize / bl; + uint32_t bl = dev->m_blocksize; + uint32_t bc = dev->m_fileSize / bl; memset(m_buf, 0, sizeof(m_buf)); - int pageCode = cmd2 & 0x3F; - int pageControl = cmd2 >> 6; + int pageCode = cdb[2] & 0x3F; + int pageControl = cdb[2] >> 6; int a = 4; - if(scsi_cmd == SCSI_MODE_SENSE10) a = 8; + byte dbd = cdb[1] * 0x80; + + if(cdb[0] == SCSI_MODE_SENSE10) a = 8; if(dbd == 0) { byte c[8] = { @@ -1272,8 +1497,8 @@ byte onModeSenseCommand(byte scsi_cmd, byte dbd, byte cmd2, uint32_t len) m_buf[a + 1] = 0x16; // Page length if(pageControl != 1) { m_buf[a + 11] = 0x3F;//Number of sectors / track - m_buf[a + 12] = (byte)(m_img->m_blocksize >> 8); - m_buf[a + 13] = (byte)m_img->m_blocksize; + m_buf[a + 12] = (byte)(dev->m_blocksize >> 8); + m_buf[a + 13] = (byte)dev->m_blocksize; m_buf[a + 15] = 0x1; // Interleave } a += 0x18; @@ -1319,7 +1544,7 @@ byte onModeSenseCommand(byte scsi_cmd, byte dbd, byte cmd2, uint32_t len) return SCSI_STATUS_CHECK_CONDITION; break; } - if(scsi_cmd == SCSI_MODE_SENSE10) + if(cdb[0] == SCSI_MODE_SENSE10) { m_buf[1] = a - 2; m_buf[7] = 0x08; @@ -1329,18 +1554,40 @@ byte onModeSenseCommand(byte scsi_cmd, byte dbd, byte cmd2, uint32_t len) m_buf[0] = a - 1; m_buf[3] = 0x08; } - writeDataPhase(len < a ? len : a, m_buf); + writeDataPhase(cdb[4] < a ? cdb[4] : a, m_buf); return SCSI_STATUS_GOOD; } -byte onModeSelectCommand(byte scsi_cmd, byte flags, uint32_t len) +byte onModeSelect(SCSI_DEVICE *dev, const byte *cdb) { - if (len > MAX_BLOCKSIZE) { + unsigned length = 0; + LOGN("onModeSelect"); + + if (cdb[4] > MAX_BLOCKSIZE) { m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; m_addition_sense = SCSI_ASC_INVALID_FIELD_IN_CDB; return SCSI_STATUS_CHECK_CONDITION; } - readDataPhase(len, m_buf); + + if(dev->m_type != SCSI_DEVICE_HDD && (cdb[1] & 0x01)) + { + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; + return SCSI_STATUS_CHECK_CONDITION; + } + + if(cdb[0] == SCSI_MODE_SELECT6) + { + length = cdb[4]; + } + else /* SCSI_MODE_SELECT10 */ + { + length = cdb[7] << 8; + length |= cdb[8]; + if(length > 0x800) { length = 0x800; } + } + + readDataPhase(length, m_buf); //Apple HD SC Setup sends: //0 0 0 8 0 0 0 0 0 0 2 0 0 2 10 0 1 6 24 10 8 0 0 0 //I believe mode page 0 set to 10 00 is Disable Unit Attention @@ -1357,10 +1604,10 @@ byte onModeSelectCommand(byte scsi_cmd, byte flags, uint32_t len) /* * Test Unit Ready command processing. */ -byte onTestUnitReady() +byte onTestUnitReady(SCSI_DEVICE *dev, const byte *cdb) { // Check that image file is present - if(!m_img) { + if(!dev->m_file.isOpen()) { m_senseKey = SCSI_SENSE_NOT_READY; m_addition_sense = SCSI_ASC_MEDIUM_NOT_PRESENT; return SCSI_STATUS_CHECK_CONDITION; @@ -1370,22 +1617,27 @@ byte onTestUnitReady() /* * ReZero Unit - Move to Logical Block Zero in file. */ -byte onReZeroUnit() { +byte onReZeroUnit(SCSI_DEVICE *dev, const byte *cdb) { LOGN("-ReZeroUnit"); // Make sure we have an image with atleast a first byte. // Actually seeking to the position wont do anything, so dont. - return checkBlockCommand(0, 0); + return checkBlockCommand(dev, 0, 0); } /* * WriteBuffer - Used for testing buffer, no change to medium */ -byte onWriteBuffer(byte mode, uint32_t allocLength) +byte onWriteBuffer(SCSI_DEVICE *dev, const byte *cdb) { + byte mode = cdb[1] & 7; + uint32_t allocLength = ((uint32_t)cdb[6] << 16) | ((uint32_t)cdb[7] << 8) | cdb[8]; + LOGN("-WriteBuffer"); LOGHEXN(mode); LOGHEXN(allocLength); + + if (mode == MODE_COMBINED_HEADER_DATA && (allocLength - 4) <= SCSI_BUF_SIZE) { byte tmp[allocLength]; @@ -1413,8 +1665,8 @@ byte onWriteBuffer(byte mode, uint32_t allocLength) } else { - m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; - m_addition_sense = SCSI_ASC_INVALID_FIELD_IN_CDB; + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; return SCSI_STATUS_CHECK_CONDITION; } } @@ -1422,8 +1674,11 @@ byte onWriteBuffer(byte mode, uint32_t allocLength) /* * ReadBuffer - Used for testing buffer, no change to medium */ -byte onReadBuffer(byte mode, uint32_t allocLength) +byte onReadBuffer(SCSI_DEVICE *dev, const byte *cdb) { + byte mode = cdb[1] & 7; + uint32_t allocLength = ((uint32_t)cdb[6] << 16) | ((uint32_t)cdb[7] << 8) | cdb[8]; + LOGN("-ReadBuffer"); LOGHEXN(mode); LOGHEXN(allocLength); @@ -1462,8 +1717,8 @@ byte onReadBuffer(byte mode, uint32_t allocLength) } else { - m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; - m_addition_sense = SCSI_ASC_INVALID_FIELD_IN_CDB; + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; return SCSI_STATUS_CHECK_CONDITION; } } @@ -1471,11 +1726,12 @@ byte onReadBuffer(byte mode, uint32_t allocLength) /* * On Send Diagnostic */ -byte onSendDiagnostic(byte flags) +byte onSendDiagnostic(SCSI_DEVICE *dev, const byte *cdb) { - int self_test = flags & 0x4; + int self_test = cdb[1] & 0x4; LOGN("-SendDiagnostic"); - LOGHEXN(flags); + LOGHEXN(cdb[1]); + if(self_test) { // Don't actually do a test, we're good. @@ -1516,6 +1772,7 @@ void loop() //int msg = 0; m_msg = 0; + SCSI_DEVICE *dev = (SCSI_DEVICE *)0; // HDD image for current SCSI-ID, LUN // Wait until RST = H, BSY = H, SEL = L do {} while( SCSI_IN(vBSY) || !SCSI_IN(vSEL) || SCSI_IN(vRST)); @@ -1633,14 +1890,42 @@ void loop() // LUN confirmation m_sts = cmd[1]&0xe0; // Preset LUN in status byte m_lun = m_sts>>5; + + dev = &(scsi_device_list[m_id][m_lun]); // HDD Image selection - m_img = (HDDIMG *)0; // None - if( (m_lun <= NUM_SCSILUN) ) + if(m_lun >= NUM_SCSILUN || !dev->m_file.isOpen()) { - m_img = &(img[m_id][m_lun]); // There is an image - if(!(m_img->m_file.isOpen())) - m_img = (HDDIMG *)0; // Image absent + // REQUEST SENSE and INQUIRY are handled different with invalid LUNs + if(cmd[0] != SCSI_REQUEST_SENSE || cmd[0] != SCSI_INQUIRY) + { + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED; + m_sts = SCSI_STATUS_CHECK_CONDITION; + goto Status; + } + + if(cmd[0] == SCSI_INQUIRY) + { + // Special INQUIRY handling for invalid LUNs + LOGN("onInquiry - InvalidLUN"); + dev = &(scsi_device_list[m_id][0]); + + byte temp = dev->inquiry_block.raw[0]; + + // If the LUN is invalid byte 0 of inquiry block needs to be 7fh + dev->inquiry_block.raw[0] = 0x7f; + + // only write back what was asked for + writeDataPhase(cmd[4], dev->inquiry_block.raw); + + // return it back to normal if it was altered + dev->inquiry_block.raw[0] = temp; + + m_sts = SCSI_STATUS_GOOD; + goto Status; + } } + // if(!m_img) m_sts |= 0x02; // Missing image file for LUN //LOGHEX(((uint32_t)m_img)); @@ -1650,106 +1935,9 @@ void loop() LOG(m_lun); LOGN(""); - switch(cmd[0]) { - case SCSI_TEST_UNIT_READY: - LOGN("[Test Unit Ready]"); - m_sts |= onTestUnitReady(); - break; - case SCSI_REZERO_UNIT: - LOGN("[Rezero Unit]"); - m_sts |= onReZeroUnit(); - break; - case SCSI_REQUEST_SENSE: - LOGN("[RequestSense]"); - onRequestSenseCommand(cmd[4]); - break; - case SCSI_FORMAT_UNIT4: // TODO: Implement me! - LOGN("[FormatUnit4]"); - break; - case SCSI_FORMAT_UNIT6: // TODO: Implement me! - LOGN("[FormatUnit6]"); - break; - case SCSI_REASSIGN_BLOCKS: // TODO: Implement me! - LOGN("[ReassignBlocks]"); - break; - case SCSI_READ6: - LOGN("[Read6]"); - m_sts |= onReadCommand((((uint32_t)cmd[1] & 0x1F) << 16) | ((uint32_t)cmd[2] << 8) | cmd[3], (cmd[4] == 0) ? 0x100 : cmd[4]); - break; - case SCSI_WRITE6: - LOGN("[Write6]"); - m_sts |= onWriteCommand((((uint32_t)cmd[1] & 0x1F) << 16) | ((uint32_t)cmd[2] << 8) | cmd[3], (cmd[4] == 0) ? 0x100 : cmd[4]); - break; - case SCSI_SEEK6: // TODO: Implement me! - LOGN("[Seek6]"); - break; - case SCSI_INQUIRY: - LOGN("[Inquiry]"); - m_sts |= onInquiryCommand(cmd[4]); - break; - case SCSI_MODE_SELECT6: - LOGN("[ModeSelect6]"); - m_sts |= onModeSelectCommand(cmd[0], cmd[1], cmd[4]); - break; - case SCSI_MODE_SENSE6: - LOGN("[ModeSense6]"); - m_sts |= onModeSenseCommand(cmd[0], cmd[1]&0x80, cmd[2], cmd[4]); - break; - case SCSI_START_STOP_UNIT: // TODO: Implement me! - LOGN("[StartStopUnit]"); - break; - case SCSI_PREVENT_ALLOW_REMOVAL: // TODO: Implement me! - LOGN("[PreAllowMed.Removal]"); - break; - case SCSI_READ_CAPACITY: - LOGN("[ReadCapacity]"); - m_sts |= onReadCapacityCommand(cmd[8]); - break; - case SCSI_READ10: - LOGN("[Read10]"); - m_sts |= onReadCommand(((uint32_t)cmd[2] << 24) | ((uint32_t)cmd[3] << 16) | ((uint32_t)cmd[4] << 8) | cmd[5], ((uint32_t)cmd[7] << 8) | cmd[8]); - break; - case SCSI_WRITE10: - LOGN("[Write10]"); - m_sts |= onWriteCommand(((uint32_t)cmd[2] << 24) | ((uint32_t)cmd[3] << 16) | ((uint32_t)cmd[4] << 8) | cmd[5], ((uint32_t)cmd[7] << 8) | cmd[8]); - break; - case SCSI_SEEK10: // TODO: Implement me! - LOGN("[Seek10]"); - break; - case SCSI_VERIFY10: - LOGN("[Verify10]"); - m_sts |= onVerifyCommand(cmd[1], ((uint32_t)cmd[2] << 24) | ((uint32_t)cmd[3] << 16) | ((uint32_t)cmd[4] << 8) | cmd[5], ((uint32_t)cmd[7] << 8) | cmd[8]); - break; - case SCSI_SYNCHRONIZE_CACHE: // TODO: Implement me! - LOGN("[SynchronizeCache10]"); - break; - case SCSI_MODE_SELECT10: - LOGN("[ModeSelect10"); - m_sts |= onModeSelectCommand(cmd[0], cmd[1], ((uint32_t)cmd[7] << 8) | cmd[8]); - break; - case SCSI_MODE_SENSE10: - LOGN("[ModeSense10]"); - m_sts |= onModeSenseCommand(cmd[0], cmd[1] & 0x80, cmd[2], ((uint32_t)cmd[7] << 8) | cmd[8]); - break; - case SCSI_WRITE_BUFFER: - LOGN("[WriteBuffer]"); - m_sts |= onWriteBuffer(cmd[1] & 7, ((uint32_t)cmd[6] << 16) | ((uint32_t)cmd[7] << 8) | cmd[8]); - break; - case SCSI_READ_BUFFER: - LOGN("[ReadBuffer]"); - m_sts |= onReadBuffer(cmd[1] & 7, ((uint32_t)cmd[6] << 16) | ((uint32_t)cmd[7] << 8) | cmd[8]); - break; - case SCSI_SEND_DIAG: - m_sts |= onSendDiagnostic(cmd[1]); - break; - default: - LOGN("[*Unknown]"); - m_sts |= SCSI_STATUS_CHECK_CONDITION; - m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; - m_addition_sense = SCSI_ASC_INVALID_OPERATION_CODE; - break; - } + m_sts = scsi_command_table[cmd[0]](dev, cmd); +Status: LOGN("Sts"); SCSI_PHASE_CHANGE(SCSI_PHASE_STATUS); // Bus settle delay 400ns built in to writeHandshake