Sfoglia il codice sorgente

ISO-9660 raw image support for CD/DVD emulation

Troy 3 anni fa
parent
commit
953309be30
3 ha cambiato i file con 546 aggiunte e 210 eliminazioni
  1. 530 203
      src/BlueSCSI.cpp
  2. 13 7
      src/BlueSCSI.h
  3. 3 0
      src/scsi_sense.h

+ 530 - 203
src/BlueSCSI.cpp

@@ -39,7 +39,7 @@
 #include <SdFat.h>
 #include <setjmp.h>
 
-#define DEBUG            0      // 0:No debug information output
+#define DEBUG            1      // 0:No debug information output
                                 // 1: Debug information output to USB Serial
                                 // 2: Debug information output to LOG.txt (slow)
 
@@ -98,6 +98,12 @@ SCSI_COMMAND_HANDLER(onWriteBuffer);
 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;
 
   // clear and initialize default inquiry blocks
   // default SCSI HDD
@@ -462,85 +539,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<<id;
+      if(id < NUM_SCSIID && lun < NUM_SCSILUN) {
+        dev = &scsi_device_list[id][lun];
+        LOG_FILE.print(" - ");
+        LOG_FILE.print(name);
+        dev->m_type = device_type;
+        image_ready = hddimageOpen(dev, file, id, lun, blk);
+        if(image_ready) { // Marked as a responsive ID
+          scsi_id_mask |= 1<<id;
+          
+          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;
             
-            switch(dev->m_type)
-            {
-              case SCSI_DEVICE_HDD:
-              // default SCSI HDD
-              dev->inquiry_block = &default_hdd;        
-              break;
-              
-              case SCSI_DEVICE_OPTICAL:
-              // default SCSI CDROM
-              dev->inquiry_block = &default_optical;
-              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();
   }
@@ -815,7 +923,7 @@ void writeDataLoop(uint32_t blocksize, const byte* srcptr)
  */
 void writeDataPhase(int len, const byte* p)
 {
-  LOGN("DATAIN PHASE");
+  LOG(" DI ");
   SCSI_PHASE_CHANGE(SCSI_PHASE_DATAIN);
   // Bus settle delay 400ns. Following code was measured at 800ns before REQ asserted. STM32F103.
 #ifdef XCVR
@@ -831,17 +939,16 @@ void writeDataPhase(int len, const byte* p)
  */
 void writeDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len)
 {
-  LOGN("DATAIN PHASE(SD)");
+  LOG (" DI(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;
@@ -900,7 +1007,7 @@ void readDataLoop(uint32_t blockSize, byte* dstptr)
  */
 void readDataPhase(int len, byte* p)
 {
-  LOGN("DATAOUT PHASE");
+  LOG(" DO ");
   SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT);
   // Bus settle delay 400ns. The following code was measured at 450ns before REQ asserted. STM32F103.
   readDataLoop(len, p);
@@ -912,7 +1019,7 @@ void readDataPhase(int len, byte* p)
  */
 void readDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len)
 {
-  LOGN("DATAOUT PHASE(SD)");
+  LOG(" DO(SD) ");
   SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT);
   //Bus settle delay 400ns, file.seek() measured at over 1000ns.
 
@@ -938,7 +1045,7 @@ void readDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len)
  */
 void verifyDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len)
 {
-  LOGN("DATAOUT PHASE(SD)");
+  LOG(" DO(SD) ");
   SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT);
   //Bus settle delay 400ns, file.seek() measured at over 1000ns.
 
@@ -956,7 +1063,7 @@ void verifyDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len)
  */
 void MsgIn2(int msg)
 {
-  LOGN("MsgIn2");
+  LOG(" MI:"); LOGHEX(msg); LOG(" ");
   SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEIN);
   // Bus settle delay 400ns built in to writeHandshake
   writeHandshake(msg);
@@ -993,7 +1100,7 @@ void loop()
     delayMicroseconds(1);
     return;
   }
-  LOGN("Selection");
+  LOG(" S ");
   m_isBusReset = false;
   if (setjmp(m_resetJmpBuf) == 1) {
     LOGN("Reset, going to BusFree");
@@ -1020,6 +1127,7 @@ void loop()
 
   //  
   if(isHigh(gpio_read(ATN))) {
+    LOG(" MO:");
     SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEOUT);
     // Bus settle delay 400ns. Following code was measured at 350ns before REQ asserted. Added another 50ns. STM32F103.
     SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEOUT);// 28ns delay STM32F103
@@ -1032,6 +1140,7 @@ void loop()
       m_msb[msc++] = readHandshake();
     }
     for(int i = 0; i < msc; i++) {
+      LOGHEX(m_msb[i]); LOG(":");
       // ABORT
       if (m_msb[i] == 0x06) {
         goto BusFree;
@@ -1081,7 +1190,7 @@ void loop()
     }
   }
 
-  LOG("Command:");
+  LOG(" CMD:");
   SCSI_PHASE_CHANGE(SCSI_PHASE_COMMAND);
   // Bus settle delay 400ns. The following code was measured at 20ns before REQ asserted. Added another 380ns. STM32F103.
   asm("nop;nop;nop;nop;nop;nop;nop;nop");// This asm causes some code reodering, which adds 270ns, plus 8 nop cycles for an additional 110ns. STM32F103
@@ -1115,7 +1224,7 @@ void loop()
   LOG(m_id);
   LOG(":LUN ");
   LOG(m_lun);
-  LOGN("");
+  LOG(" ");
 
   dev = &(scsi_device_list[m_id][m_lun]);
   // HDD Image selection
@@ -1133,7 +1242,7 @@ void loop()
     if(cmd[0] == SCSI_INQUIRY)
     {
       // Special INQUIRY handling for invalid LUNs
-      LOGN("onInquiry - InvalidLUN");
+      LOG(" onInquiry-InvalidLUN ");
       dev = &(scsi_device_list[m_id][0]);
 
       byte temp = dev->inquiry_block.raw[0];
@@ -1157,18 +1266,17 @@ void loop()
   LED_OFF();
 
 Status:
-  LOGN("Sts");
+  LOG(" S:"); LOGHEX(m_sts);
   SCSI_PHASE_CHANGE(SCSI_PHASE_STATUS);
   // Bus settle delay 400ns built in to writeHandshake
   writeHandshake(m_sts);
 
-  LOGN("MsgIn");
+  LOG(" MI:"); LOGHEX(m_msg);
   SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEIN);
   // Bus settle delay 400ns built in to writeHandshake
   writeHandshake(m_msg);
-
 BusFree:
-  LOGN("BusFree");
+  LOGN(" BF");
   m_isBusReset = false;
   //SCSI_OUT(vREQ,inactive) // gpio_write(REQ, low);
   //SCSI_OUT(vMSG,inactive) // gpio_write(MSG, low);
@@ -1213,7 +1321,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;
 }
 
@@ -1267,6 +1375,9 @@ byte checkBlockCommand(SCSI_DEVICE *dev, uint32_t adds, uint32_t len)
 {
   // Check block range is valid
   if (adds >= dev->m_blockcount || (adds + len) > dev->m_blockcount) {    
+    LOG(dev->m_blockcount);
+    if(adds >= dev->m_additional_sense_code) { LOGN(" first "); }
+    if((adds + len) > dev->m_blockcount) { LOGN(" second "); }
     dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;
     dev->m_additional_sense_code = SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
     return SCSI_STATUS_CHECK_CONDITION;
@@ -1302,13 +1413,14 @@ 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 ");
+  
+  LOG (" Read10 ");
+  LOG("A:");
   LOGHEX(adds);
   LOG(":");
-  LOGHEXN(len);
-  */
+  LOGHEX(len);
+  LOG(" ");
+  
   byte sts = checkBlockCommand(dev, adds, len);
   if (sts) {
     return sts;
@@ -1403,15 +1515,34 @@ 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;
+
+  // saving parameters is not allowed...yet!
+  if(pageControl == 3)
+  {
+    dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;
+    dev->m_additional_sense_code = SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED;
+    return SCSI_STATUS_CHECK_CONDITION;
+  }
 
-  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,
@@ -1425,95 +1556,183 @@ 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
-    if(pageControl != 1) {
-      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 += 0x20;
-    if(pageCode != SCSI_SENSE_MODE_ALL) break;
-  case SCSI_SENSE_MODE_CACHING:
-    m_buf[a + 0] = SCSI_SENSE_MODE_CACHING;
-    m_buf[a + 1] = 0x0A;  // Page length
-    if(pageControl != 1) {
-      m_buf[a + 2] = 0x01; // Disalbe Read Cache so no one asks for Cache Stats page.
-    }
-    a += 0x08;
-    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) {
+        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] = 63; // 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 += 0x20;
+      if(pageCode != SCSI_SENSE_MODE_ALL) break;
+    case SCSI_SENSE_MODE_CACHING:
+      m_buf[a + 0] = SCSI_SENSE_MODE_CACHING;
+      m_buf[a + 1] = 0x0A;  // Page length
       if(pageControl != 1) {
-        memcpy(&m_buf[a + 2], page30, sizeof(page30));
+        m_buf[a + 2] = 0x01; // Disalbe Read Cache so no one asks for Cache Stats page.
       }
-      a += 2 + sizeof(page30);
+      a += 0x0C;
       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;
 }
     
@@ -1522,7 +1741,8 @@ byte onModeSelect(SCSI_DEVICE *dev, const byte *cdb)
   unsigned length = 0;
   LOGN("onModeSelect");
 
-  if(dev->m_type != SCSI_DEVICE_HDD && (cdb[1] & 0x01))
+  // saving mode pages isn't supported yet
+  if(cdb[1] & 0x01)
   {
     dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;
     dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB;
@@ -1540,6 +1760,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
@@ -1697,6 +1918,7 @@ byte onReadDefectData(SCSI_DEVICE *dev, const byte *cdb)
   return SCSI_STATUS_GOOD;
 }
 
+<<<<<<< HEAD
 <<<<<<< HEAD
 /*
  * MsgIn2.
@@ -1955,3 +2177,108 @@ BusFree:
 }
 =======
 >>>>>>> faed60f (code layout adjustments)
+=======
+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;
+}

+ 13 - 7
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
 	uint16_t      m_blocksize;            // SCSI BLOCK size
+  uint16_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;
 
 

+ 3 - 0
src/scsi_sense.h

@@ -27,6 +27,7 @@
 #define SCSI_ASC_WRITE_PROTECTED                                0x2700
 #define SCSI_ASC_CANNOT_READ_MEDIUM_UNKNOWN_FORMAT              0x3001
 #define SCSI_ASC_CANNOT_READ_MEDIUM_INCOMPATIBLE_FORMAT         0x3002
+#define SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED                0x3900
 #define SCSI_ASC_MEDIUM_NOT_PRESENT                             0x3A00
 #define SCSI_ASC_LUN_NOT_READY_MANUAL_INTERVENTION_REQUIRED     0x0403
 
@@ -39,6 +40,8 @@
 #define SCSI_SENSE_MODE_DISK_GEOMETRY               0x04
 #define SCSI_SENSE_MODE_FLEXABLE_GEOMETRY           0x05
 #define SCSI_SENSE_MODE_CACHING                     0x08
+#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