From 30f684844e63ff81debfc4904fa50e7c38ed5895 Mon Sep 17 00:00:00 2001 From: Troy Date: Sun, 19 Jun 2022 20:02:55 -0400 Subject: [PATCH] ISO-9660 raw image support for CD/DVD emulation --- src/BlueSCSI.cpp | 670 ++++++++++++++++++++++++++++++++++------------- src/BlueSCSI.h | 20 +- src/scsi_sense.h | 2 + 3 files changed, 500 insertions(+), 192 deletions(-) diff --git a/src/BlueSCSI.cpp b/src/BlueSCSI.cpp index 1ec7ad3..307ca28 100644 --- a/src/BlueSCSI.cpp +++ b/src/BlueSCSI.cpp @@ -98,6 +98,12 @@ SCSI_COMMAND_HANDLER(onTestUnitReady); SCSI_COMMAND_HANDLER(onReZeroUnit); SCSI_COMMAND_HANDLER(onSendDiagnostic); SCSI_COMMAND_HANDLER(onReadDefectData); +SCSI_COMMAND_HANDLER(onReadTOC); +SCSI_COMMAND_HANDLER(onReadDVDStructure); +SCSI_COMMAND_HANDLER(onReadDiscInformation); + +static uint32_t MSFtoLBA(const byte *msf); +static void LBAtoMSF(const uint32_t lba, byte *msf); static void flashError(const unsigned error); void onBusReset(void); @@ -185,6 +191,23 @@ void readSDCardInfo() } } +bool VerifyISOPVD(SCSI_DEVICE *dev, unsigned sector_size, bool mode2) +{ + int seek = 16 * sector_size; + if(sector_size > CDROM_COMMON_SECTORSIZE) seek += 16; + if(mode2) seek += 8; + bool ret = false; + + dev->m_file->seekSet(seek); + dev->m_file->read(m_buf, 2048); + + ret = ((m_buf[0] == 1 && !strncmp((char *)&m_buf[1], "CD001", 5) && m_buf[6] == 1) || + (m_buf[8] == 1 && !strncmp((char *)&m_buf[9], "CDROM", 5) && m_buf[14] == 1)); + + dev->m_file->rewind(); + return ret; +} + /* * Open HDD image file */ @@ -192,32 +215,83 @@ void readSDCardInfo() bool hddimageOpen(SCSI_DEVICE *dev, FsFile *file,int id,int lun,int blocksize) { dev->m_fileSize= 0; + dev->m_offset = 0; dev->m_blocksize = blocksize; dev->m_file = file; - dev->m_type = SCSI_DEVICE_HDD; - if(dev->m_file->isOpen()) - { - dev->m_fileSize = dev->m_file->size(); - dev->m_blockcount = dev->m_fileSize / dev->m_blocksize; - if(dev->m_fileSize>0) - { - // check blocksize dummy file - LOG_FILE.print(" / "); - LOG_FILE.print(dev->m_fileSize); - LOG_FILE.print("bytes / "); - LOG_FILE.print(dev->m_fileSize / 1024); - LOG_FILE.print("KiB / "); - 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."); - dev->m_file->close(); - dev->m_fileSize = dev->m_blocksize = 0; // no file + if(!dev->m_file->isOpen()) { goto failed; } + + dev->m_fileSize = dev->m_file->size(); + + if(dev->m_fileSize < 1) { + LOG_FILE.println(" - file is 0 bytes, can not use."); + goto failed; + } + + if(dev->m_type == SCSI_DEVICE_OPTICAL) { + LOG_FILE.print(" CDROM"); + + // Borrowed from PCEM + if(VerifyISOPVD(dev, CDROM_COMMON_SECTORSIZE, false)) { + dev->m_blocksize = CDROM_COMMON_SECTORSIZE; + dev->m_mode2 = false; + } else if(VerifyISOPVD(dev, CDROM_RAW_SECTORSIZE, false)) { + dev->m_blocksize = CDROM_RAW_SECTORSIZE; + dev->m_rawblocksize = CDROM_COMMON_SECTORSIZE; + dev->m_mode2 = false; + dev->m_raw = true; + dev->m_offset = 16; + } else if(VerifyISOPVD(dev, 2336, true)) { + dev->m_blocksize = 2336; + dev->m_mode2 = true; + } else if(VerifyISOPVD(dev, CDROM_RAW_SECTORSIZE, true)) { + dev->m_blocksize = CDROM_RAW_SECTORSIZE; + dev->m_mode2 = true; + dev->m_raw = true; + dev->m_offset = 16; + } else { + // Last ditch effort + // size must be less than 700MB + if(dev->m_fileSize > 912579600) { + goto failed; + } + + dev->m_raw = true; + + if(!(dev->m_fileSize % CDROM_COMMON_SECTORSIZE)) { + // try a multiple of 2048 + dev->m_blocksize = CDROM_COMMON_SECTORSIZE; + } else { + // I give up! + LOG_FILE.println(" InvalidISO"); + goto failed; + } } + } else { + LOG_FILE.print(" HDD"); } + dev->m_blockcount = dev->m_fileSize / dev->m_blocksize; + + // check blocksize dummy file + LOG_FILE.print(" / "); + LOG_FILE.print(dev->m_fileSize); + LOG_FILE.print("bytes / "); + LOG_FILE.print(dev->m_fileSize / 1024); + LOG_FILE.print("KiB / "); + LOG_FILE.print(dev->m_fileSize / 1024 / 1024); + LOG_FILE.println("MiB"); + + if(dev->m_type == SCSI_DEVICE_OPTICAL) { + LOG_FILE.print(" MODE2:");LOG_FILE.print(dev->m_mode2); + LOG_FILE.print(" BlockSize:");LOG_FILE.println(dev->m_blocksize); + } + return true; // File opened + +failed: + + dev->m_file->close(); + dev->m_fileSize = dev->m_blocksize = 0; // no file + delete dev->m_file; + dev->m_file = NULL; return false; } @@ -272,6 +346,9 @@ void setup() scsi_command_table[SCSI_WRITE_BUFFER] = onWriteBuffer; scsi_command_table[SCSI_SEND_DIAG] = onSendDiagnostic; scsi_command_table[SCSI_READ_DEFECT_DATA] = onReadDefectData; + scsi_command_table[SCSI_READ_TOC] = onReadTOC; + scsi_command_table[SCSI_READ_DVD_STRUCTURE] = onReadDVDStructure; + scsi_command_table[SCSI_READ_DISC_INFORMATION] = onReadDiscInformation; // Serial initialization #if DEBUG > 0 @@ -438,100 +515,116 @@ void findDriveImages(FsFile root) { // Valid file, open for reading/writing. file = new FsFile(SD.open(name, O_RDWR)); if(file && file->isFile()) { - if(tolower(name[0]) == 'h' && tolower(name[1]) == 'd') { - // Defaults for Hard Disks - int id = 1; // 0 and 3 are common in Macs for physical HD and CD, so avoid them. - int lun = 0; - int blk = 512; - - // Positionally read in and coerase the chars to integers. - // We only require the minimum and read in the next if provided. - int file_name_length = strlen(name); - if(file_name_length > 2) { // HD[N] - int tmp_id = name[HDIMG_ID_POS] - '0'; - - // If valid id, set it, else use default - if(tmp_id > -1 && tmp_id < 8) { - id = tmp_id; - } else { - LOG_FILE.print(name); - LOG_FILE.println(" - bad SCSI id in filename, Using default ID 1"); - } + SCSI_DEVICE_TYPE device_type; + if(tolower(name[1]) != 'd') { + file->close(); + delete file; + LOG_FILE.print("Not an image: "); + LOG_FILE.println(name); + continue; + } + + switch (tolower(name[0])) { + case 'h': device_type = SCSI_DEVICE_HDD; + break; + case 'c': device_type = SCSI_DEVICE_OPTICAL; + break; + default: + file->close(); + delete file; + LOG_FILE.print("Not an image: "); + LOG_FILE.println(name); + continue; + } + + // Defaults for Hard Disks + int id = 1; // 0 and 3 are common in Macs for physical HD and CD, so avoid them. + int lun = 0; + int blk = 512; + + // Positionally read in and coerase the chars to integers. + // We only require the minimum and read in the next if provided. + int file_name_length = strlen(name); + if(file_name_length > 2) { // HD[N] + int tmp_id = name[HDIMG_ID_POS] - '0'; + + // If valid id, set it, else use default + if(tmp_id > -1 && tmp_id < 8) { + id = tmp_id; + } else { + LOG_FILE.print(name); + LOG_FILE.println(" - bad SCSI id in filename, Using default ID 1"); } + } - if(file_name_length > 3) { // HDN[N] - int tmp_lun = name[HDIMG_LUN_POS] - '0'; + if(file_name_length > 3) { // HDN[N] + int tmp_lun = name[HDIMG_LUN_POS] - '0'; - // If valid lun, set it, else use default - if(tmp_lun == 0 || tmp_lun == 1) { - lun = tmp_lun; - } else { - LOG_FILE.print(name); - LOG_FILE.println(" - bad SCSI LUN in filename, Using default LUN ID 0"); - } + // If valid lun, set it, else use default + if(tmp_lun == 0 || tmp_lun == 1) { + lun = tmp_lun; + } else { + LOG_FILE.print(name); + LOG_FILE.println(" - bad SCSI LUN in filename, Using default LUN ID 0"); } + } - int blk1 = 0, blk2, blk3, blk4 = 0; - if(file_name_length > 8) { // HD00_[111] - blk1 = name[HDIMG_BLK_POS] - '0'; - blk2 = name[HDIMG_BLK_POS+1] - '0'; - blk3 = name[HDIMG_BLK_POS+2] - '0'; - if(file_name_length > 9) // HD00_NNN[1] - blk4 = name[HDIMG_BLK_POS+3] - '0'; - } - if(blk1 == 2 && blk2 == 5 && blk3 == 6) { - blk = 256; - } else if(blk1 == 1 && blk2 == 0 && blk3 == 2 && blk4 == 4) { - blk = 1024; - } else if(blk1 == 2 && blk2 == 0 && blk3 == 4 && blk4 == 8) { - blk = 2048; - } + int blk1 = 0, blk2, blk3, blk4 = 0; + if(file_name_length > 8) { // HD00_[111] + blk1 = name[HDIMG_BLK_POS] - '0'; + blk2 = name[HDIMG_BLK_POS+1] - '0'; + blk3 = name[HDIMG_BLK_POS+2] - '0'; + if(file_name_length > 9) // HD00_NNN[1] + blk4 = name[HDIMG_BLK_POS+3] - '0'; + } + if(blk1 == 2 && blk2 == 5 && blk3 == 6) { + blk = 256; + } else if(blk1 == 1 && blk2 == 0 && blk3 == 2 && blk4 == 4) { + blk = 1024; + } else if(blk1 == 2 && blk2 == 0 && blk3 == 4 && blk4 == 8) { + blk = 2048; + } - if(id < NUM_SCSIID && lun < NUM_SCSILUN) { - dev = &scsi_device_list[id][lun]; - LOG_FILE.print(" - "); - LOG_FILE.print(name); - image_ready = hddimageOpen(dev, file, id, lun, blk); - if(image_ready) { // Marked as a responsive ID - scsi_id_mask |= 1<m_type = device_type; + image_ready = hddimageOpen(dev, file, id, lun, blk); + if(image_ready) { // Marked as a responsive ID + scsi_id_mask |= 1<m_type) + { + case SCSI_DEVICE_HDD: + // default SCSI HDD + dev->inquiry_block.ansi_version = 1; + dev->inquiry_block.response_format = 1; + dev->inquiry_block.additional_length = 31; + memcpy(dev->inquiry_block.vendor, "QUANTUM", 7); + memcpy(dev->inquiry_block.product, "FIREBALL1", 9); + memcpy(dev->inquiry_block.revision, "1.0", 3); + break; - switch(dev->m_type) - { - case SCSI_DEVICE_HDD: - // default SCSI HDD - dev->inquiry_block.ansi_version = 1; - dev->inquiry_block.response_format = 1; - dev->inquiry_block.additional_length = 31; - memcpy(dev->inquiry_block.vendor, "QUANTUM", 7); - memcpy(dev->inquiry_block.product, "FIREBALL1", 9); - memcpy(dev->inquiry_block.revision, "1.0", 3); - break; - - case SCSI_DEVICE_OPTICAL: - // default SCSI CDROM - dev->inquiry_block.peripheral_device_type = 5; - dev->inquiry_block.rmb = 1; - dev->inquiry_block.ansi_version = 1; - dev->inquiry_block.response_format = 1; - dev->inquiry_block.additional_length = 42; - dev->inquiry_block.sync = 1; - memcpy(dev->inquiry_block.vendor, "BLUESCSI", 8); - memcpy(dev->inquiry_block.product, "CD-ROM CDU-55S", 14); - memcpy(dev->inquiry_block.revision, "1.9a", 4); - dev->inquiry_block.release = 0x20; - memcpy(dev->inquiry_block.revision_date, "1995", 4); - break; - } - - readSCSIDeviceConfig(dev); + case SCSI_DEVICE_OPTICAL: + // default SCSI CDROM + dev->inquiry_block.peripheral_device_type = 5; + dev->inquiry_block.rmb = 1; + dev->inquiry_block.ansi_version = 1; + dev->inquiry_block.response_format = 1; + dev->inquiry_block.additional_length = 42; + dev->inquiry_block.sync = 1; + memcpy(dev->inquiry_block.vendor, "BLUESCSI", 8); + memcpy(dev->inquiry_block.product, "CD-ROM CDU-55S", 14); + memcpy(dev->inquiry_block.revision, "1.9a", 4); + dev->inquiry_block.release = 0x20; + memcpy(dev->inquiry_block.revision_date, "1995", 4); + break; } + + readSCSIDeviceConfig(dev); } - } - } else { - file->close(); - delete file; - LOG_FILE.print("Not an image: "); - LOG_FILE.println(name); + } } LOG_FILE.sync(); } @@ -825,14 +918,13 @@ 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 * dev->m_blocksize; dev->m_file->seekSet(pos); - #ifdef XCVR TRANSCEIVER_IO_SET(vTR_DBP,TR_OUTPUT) #endif SCSI_DB_OUTPUT() + for(uint32_t i = 0; i < len; i++) { // Asynchronous reads will make it faster ... m_resetJmp = false; @@ -1204,7 +1296,7 @@ static byte onNOP(SCSI_DEVICE *dev, const byte *cdb) */ byte onInquiry(SCSI_DEVICE *dev, const byte *cdb) { - writeDataPhase(cdb[4] < 36 ? cdb[4] : 36, dev->inquiry_block.raw); + writeDataPhase(cdb[4] < 47 ? cdb[4] : 47, dev->inquiry_block.raw); return SCSI_STATUS_GOOD; } @@ -1397,15 +1489,26 @@ byte onVerify(SCSI_DEVICE *dev, const byte *cdb) */ byte onModeSense(SCSI_DEVICE *dev, const byte *cdb) { - memset(m_buf, 0, sizeof(m_buf)); int pageCode = cdb[2] & 0x3F; int pageControl = cdb[2] >> 6; - int a = 4; - byte dbd = cdb[1] & 0x08; + byte dbd = cdb[1] & 0x8; + byte block_descriptor_length = 8; - if(cdb[0] == SCSI_MODE_SENSE10) a = 8; + // SCSI_MODE_SENSE6 + int a = 4; + int length = cdb[4]; - if(dbd == 0) { + if(cdb[0] == SCSI_MODE_SENSE10) { + a = 8; + length = cdb[7]; + length <<= 8; + length |= cdb[8]; + if(length > 0x800) { length = 0x800; }; + } + + memset(m_buf, 0, length); + + if(!dbd && dev->m_type != SCSI_DEVICE_OPTICAL) { byte c[8] = { 0,//Density code dev->m_blockcount >> 16, @@ -1419,83 +1522,175 @@ byte onModeSense(SCSI_DEVICE *dev, const byte *cdb) memcpy(&m_buf[a], c, 8); a += 8; } - switch(pageCode) { - case SCSI_SENSE_MODE_ALL: - case SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY: - m_buf[a + 0] = SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY; - m_buf[a + 1] = 0x0A; - a += 0x0C; - if(pageCode != SCSI_SENSE_MODE_ALL) break; - - case SCSI_SENSE_MODE_DISCONNECT_RECONNECT: - m_buf[a + 0] = SCSI_SENSE_MODE_DISCONNECT_RECONNECT; - m_buf[a + 1] = 0x0A; - a += 0x0C; - if(pageCode != SCSI_SENSE_MODE_ALL) break; - - case SCSI_SENSE_MODE_FORMAT_DEVICE: //Drive parameters - m_buf[a + 0] = SCSI_SENSE_MODE_FORMAT_DEVICE; //Page code - m_buf[a + 1] = 0x16; // Page length - if(pageControl != 1) { - m_buf[a + 11] = 0x3F;//Number of sectors / track - 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; - if(pageCode != SCSI_SENSE_MODE_ALL) break; - - case SCSI_SENSE_MODE_DISK_GEOMETRY: //Drive parameters - m_buf[a + 0] = SCSI_SENSE_MODE_DISK_GEOMETRY; //Page code - m_buf[a + 1] = 0x16; // Page length - if(pageControl != 1) { - unsigned cylinders = dev->m_blockcount / (16 * 63); - m_buf[a + 2] = (byte)(cylinders >> 16); // Cylinders - m_buf[a + 3] = (byte)(cylinders >> 8); - m_buf[a + 4] = (byte)cylinders; - m_buf[a + 5] = 16; //Number of heads - } - a += 0x18; - if(pageCode != SCSI_SENSE_MODE_ALL) break; - case SCSI_SENSE_MODE_FLEXABLE_GEOMETRY: - m_buf[a + 0] = SCSI_SENSE_MODE_FLEXABLE_GEOMETRY; - m_buf[a + 1] = 0x1E; // Page length - m_buf[a + 2] = 0x03E8; // Transfer rate 1 mbit/s - m_buf[a + 4] = 16; // Number of heads - m_buf[a + 5] = 18; // Sectors per track - m_buf[a + 6] = 0x2000; // Data bytes per sector - a += 0x20; - if(pageCode != SCSI_SENSE_MODE_ALL) break; - case SCSI_SENSE_MODE_VENDOR_APPLE: - { - const byte page30[0x14] = {0x41, 0x50, 0x50, 0x4C, 0x45, 0x20, 0x43, 0x4F, 0x4D, 0x50, 0x55, 0x54, 0x45, 0x52, 0x2C, 0x20, 0x49, 0x4E, 0x43, 0x20}; - m_buf[a + 0] = SCSI_SENSE_MODE_VENDOR_APPLE; // Page code - m_buf[a + 1] = sizeof(page30); // Page length + + // HDD supports page codes 0x1 (Read/Write), 0x2, 0x3, 0x4 + // CDROM supports page codes 0x1 (Read Only), 0x2, 0xD, 0xE, 0x30 + if(dev->m_type == SCSI_DEVICE_HDD) { + switch(pageCode) { + case SCSI_SENSE_MODE_ALL: + case SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY: + m_buf[a + 0] = SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY; + m_buf[a + 1] = 0x0A; + a += 0x0C; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + + case SCSI_SENSE_MODE_DISCONNECT_RECONNECT: + m_buf[a + 0] = SCSI_SENSE_MODE_DISCONNECT_RECONNECT; + m_buf[a + 1] = 0x0A; + a += 0x0C; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + + case SCSI_SENSE_MODE_FORMAT_DEVICE: //Drive parameters + m_buf[a + 0] = SCSI_SENSE_MODE_FORMAT_DEVICE; //Page code + m_buf[a + 1] = 0x16; // Page length + if(pageControl != 1) { + m_buf[a + 11] = 0x3F;//Number of sectors / track + 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; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + + case SCSI_SENSE_MODE_DISK_GEOMETRY: //Drive parameters + m_buf[a + 0] = SCSI_SENSE_MODE_DISK_GEOMETRY; //Page code + m_buf[a + 1] = 0x16; // Page length + if(pageControl != 1) { + unsigned cylinders = dev->m_blockcount / (16 * 63); + if(pageControl != 1) { + m_buf[a + 2] = (byte)(cylinders >> 16); // Cylinders + m_buf[a + 3] = (byte)(cylinders >> 8); + m_buf[a + 4] = (byte)cylinders; + m_buf[a + 5] = 16; //Number of heads + } else { + m_buf[a + 2] = 0xFF; // Cylinder length + m_buf[a + 3] = 0xFF; + m_buf[a + 4] = 0xFF; + m_buf[a + 5] = 16; //Number of heads + } + } + a += 0x18; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + case SCSI_SENSE_MODE_FLEXABLE_GEOMETRY: + m_buf[a + 0] = SCSI_SENSE_MODE_FLEXABLE_GEOMETRY; + m_buf[a + 1] = 0x1E; // Page length if(pageControl != 1) { - memcpy(&m_buf[a + 2], page30, sizeof(page30)); + m_buf[a + 2] = 0x03; + m_buf[a + 3] = 0xE8; // Transfer rate 1 mbit/s + m_buf[a + 4] = 16; // Number of heads + m_buf[a + 5] = 18; // Sectors per track + m_buf[a + 6] = (byte)dev->m_blocksize >> 8; + m_buf[a + 7] = (byte)dev->m_blocksize & 0xff; // Data bytes per sector } - a += 2 + sizeof(page30); + a += 0x20; if(pageCode != SCSI_SENSE_MODE_ALL) break; + case SCSI_SENSE_MODE_VENDOR_APPLE: + { + const byte apple_magic[0x24] = { + 0x23, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x30, 0x16, 0x41, 0x50, 0x50, + 0x4C, 0x45, 0x20, 0x43, 0x4F, 0x4D, 0x50, 0x55, + 0x54, 0x45, 0x52, 0x2C, 0x20, 0x49, 0x4E, 0x43, + 0x20, 0x20, 0x20 + }; + if(pageControl != 1) { + memcpy(&m_buf[0], apple_magic, sizeof(apple_magic)); + } + a = sizeof(apple_magic); + if(pageCode != SCSI_SENSE_MODE_ALL) break; + } + break; // Don't want SCSI_SENSE_MODE_ALL falling through to error condition + + default: + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; + return SCSI_STATUS_CHECK_CONDITION; + break; + } + } else { + // OPTICAL + block_descriptor_length = 0; + if(cdb[0] == SCSI_MODE_SENSE6) { + m_buf[2] = 1 << 7; // WP bit + } else { + m_buf[3] = 1 << 7; // WP bit } - break; // Don't want SCSI_SENSE_MODE_ALL falling through to error condition - default: - dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; - dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; - return SCSI_STATUS_CHECK_CONDITION; - break; - } - if(cdb[0] == SCSI_MODE_SENSE10) - { - m_buf[1] = a - 2; - m_buf[7] = 0x08; + switch(pageCode) { + case SCSI_SENSE_MODE_ALL: + case SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY: + m_buf[a + 0] = SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY; + m_buf[a + 1] = 0x06; + a += 0x08; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + + case SCSI_SENSE_MODE_DISCONNECT_RECONNECT: + m_buf[a + 0] = SCSI_SENSE_MODE_DISCONNECT_RECONNECT; + m_buf[a + 1] = 0x0A; + a += 0x0C; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + + case SCSI_SENSE_MODE_CDROM: + m_buf[a + 0] = SCSI_SENSE_MODE_CDROM; + m_buf[a + 1] = 0x06; + if(pageControl != 1) + { + // 2 seconds for inactive timer + m_buf[a + 3] = 0x05; + // MSF multiples are 60 and 75 + m_buf[a + 5] = 60; + m_buf[a + 7] = 75; + } + a += 0x8; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + + case SCSI_SENSE_MODE_CDROM_AUDIO_CONTROL: + m_buf[a + 0] = SCSI_SENSE_MODE_CDROM_AUDIO_CONTROL; + m_buf[a + 1] = 0x0E; + + a += 0x10; + if(pageCode != SCSI_SENSE_MODE_ALL) break; + + case SCSI_SENSE_MODE_VENDOR_APPLE: + { + const byte apple_magic[0x24] = { + 0x23, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x30, 0x16, 0x41, 0x50, 0x50, + 0x4C, 0x45, 0x20, 0x43, 0x4F, 0x4D, 0x50, 0x55, + 0x54, 0x45, 0x52, 0x2C, 0x20, 0x49, 0x4E, 0x43, + 0x20, 0x20, 0x20 + }; + if(pageControl != 1) { + memcpy(&m_buf[0], apple_magic, sizeof(apple_magic)); + } + a = sizeof(apple_magic); + if(pageCode != SCSI_SENSE_MODE_ALL) break; + } + break; // Don't want SCSI_SENSE_MODE_ALL falling through to error condition + + default: + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; + return SCSI_STATUS_CHECK_CONDITION; + break; + } } - else - { - m_buf[0] = a - 1; - m_buf[3] = 0x08; + if(pageCode != SCSI_SENSE_MODE_VENDOR_APPLE) { + if(cdb[0] == SCSI_MODE_SENSE10) + { + m_buf[1] = a - 2; + m_buf[7] = block_descriptor_length; // block descriptor length + } + else + { + m_buf[0] = a - 1; + m_buf[3] = block_descriptor_length; // block descriptor length + } } - writeDataPhase(cdb[4] < a ? cdb[4] : a, m_buf); + + writeDataPhase(length < a ? length : a, m_buf); return SCSI_STATUS_GOOD; } @@ -1528,6 +1723,7 @@ byte onModeSelect(SCSI_DEVICE *dev, const byte *cdb) if(length > 0x800) { length = 0x800; } } + memset(m_buf, 0, length); 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 @@ -1685,3 +1881,107 @@ byte onReadDefectData(SCSI_DEVICE *dev, const byte *cdb) return SCSI_STATUS_GOOD; } +static byte onReadTOC(SCSI_DEVICE *dev, const byte *cdb) +{ + unsigned lba = 0; + uint8_t msf = cdb[1] & 0x02; + uint8_t track = cdb[6]; + unsigned len = ((uint32_t)cdb[7] << 8) | cdb[8]; + memset(m_buf, 0, len); + + // Doing just the error seemed to make MacOS unhappy +#if 0 + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; + return SCSI_STATUS_CHECK_CONDITION; +#endif + + if(track > 1 || cdb[2] != 0) + { + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB; + return SCSI_STATUS_CHECK_CONDITION; + } + + m_buf[1] = 18; // TOC length LSB + m_buf[2] = 1; // First Track + m_buf[3] = 1; // Last Track + + // first track + m_buf[5] = 0x14; // data track + m_buf[6] = 1; + + // leadout track + m_buf[13] = 0x14; // data track + m_buf[14] = 0xaa; // leadout track + if(msf) + { + LBAtoMSF(dev->m_blockcount, &m_buf[16]); + } + else + { + m_buf[16] = (byte)(dev->m_blockcount >> 24); + m_buf[17] = (byte)(dev->m_blockcount >> 16); + m_buf[18] = (byte)(dev->m_blockcount >> 8); + m_buf[20] = (byte)(dev->m_blockcount); + } + + writeDataPhase(SCSI_TOC_LENGTH > len ? len : SCSI_TOC_LENGTH, m_buf); + return SCSI_STATUS_GOOD; +} + +static byte onReadDiscInformation(SCSI_DEVICE *dev, const byte *cdb) +{ + writeDataPhase((cdb[7] >> 8) | cdb[8], m_buf); + return SCSI_STATUS_GOOD; +} + +static byte onReadDVDStructure(SCSI_DEVICE *dev, const byte *cdb) +{ + dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST; + dev->m_additional_sense_code = SCSI_ASC_CANNOT_READ_MEDIUM_INCOMPATIBLE_FORMAT; + return SCSI_STATUS_CHECK_CONDITION; +} + +// Thanks RaSCSI :D +// LBA→MSF Conversion +static inline void LBAtoMSF(const uint32_t lba, byte *msf) +{ + uint32_t m, s, f; + + // 75 and 75*60 get the remainder + m = lba / (75 * 60); + s = lba % (75 * 60); + f = s % 75; + s /= 75; + + // The base point is M=0, S=2, F=0 + s += 2; + if (s >= 60) { + s -= 60; + m++; + } + + // Store + msf[0] = 0x00; + msf[1] = (byte)m; + msf[2] = (byte)s; + msf[3] = (byte)f; +} + +static inline uint32_t MSFtoLBA(const byte *msf) +{ + uint32_t lba; + + // 1, 75, add up in multiples of 75*60 + lba = msf[1]; + lba *= 60; + lba += msf[2]; + lba *= 75; + lba += msf[3]; + + // Since the base point is M=0, S=2, F=0, subtract 150 + lba -= 150; + + return lba; +} \ No newline at end of file diff --git a/src/BlueSCSI.h b/src/BlueSCSI.h index 9bfec92..3391e9f 100644 --- a/src/BlueSCSI.h +++ b/src/BlueSCSI.h @@ -26,6 +26,17 @@ #define ERROR_FALSE_INIT 3 #define ERROR_NO_SDCARD 5 +enum SCSI_DEVICE_TYPE +{ + SCSI_DEVICE_HDD, + SCSI_DEVICE_OPTICAL, +}; + +#define CDROM_RAW_SECTORSIZE 2352 +#define CDROM_COMMON_SECTORSIZE 2048 + +#define MAX_SCSI_COMMAND 0xff +#define SCSI_COMMAND_HANDLER(x) static byte x(SCSI_DEVICE *dev, const byte *cdb) #if DEBUG #define LOG(XX) Serial.print(XX) @@ -250,14 +261,7 @@ uint32_t db_bsrr[256]; // #define GET_CDB6_LBA(x) ((x[2] & 01f) << 16) | (x[3] << 8) | x[4] #define READ_DATA_BUS() (byte)((~(uint32_t)GPIOB->regs->IDR)>>8) -enum SCSI_DEVICE_TYPE -{ - SCSI_DEVICE_HDD, - SCSI_DEVICE_OPTICAL, -}; -#define CDROM_RAW_SECTORSIZE 2352 -#define CDROM_COMMON_SECTORSIZE 2048 struct SCSI_INQUIRY_DATA { @@ -310,6 +314,7 @@ 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 + size_t m_rawblocksize; uint8_t m_type; // SCSI device type uint32_t m_blockcount; // blockcount bool m_raw; // Raw disk @@ -317,6 +322,7 @@ typedef __attribute__((aligned(4))) struct _SCSI_DEVICE uint8_t m_senseKey; // Sense key uint16_t m_additional_sense_code; // ASC/ASCQ bool m_mode2; // MODE2 CDROM + uint8_t m_offset; // ISO offset for missing sync header } SCSI_DEVICE; diff --git a/src/scsi_sense.h b/src/scsi_sense.h index 3e4e812..bbb8fb9 100644 --- a/src/scsi_sense.h +++ b/src/scsi_sense.h @@ -38,6 +38,8 @@ #define SCSI_SENSE_MODE_FORMAT_DEVICE 0x03 #define SCSI_SENSE_MODE_DISK_GEOMETRY 0x04 #define SCSI_SENSE_MODE_FLEXABLE_GEOMETRY 0x05 +#define SCSI_SENSE_MODE_CDROM 0x0D +#define SCSI_SENSE_MODE_CDROM_AUDIO_CONTROL 0x0E #define SCSI_SENSE_MODE_VENDOR_APPLE 0x30 #define SCSI_SENSE_MODE_ALL 0x3F