Merge branch 'master' of ssh://webhost.codesrc.com/home/michael/projects/SCSI2SD
[SCSI2SD.git] / software / SCSI2SD / src / cdrom.c
index 6a30938..1c9305f 100755 (executable)
 //
 //     You should have received a copy of the GNU General Public License
 //     along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.
+#pragma GCC push_options
+#pragma GCC optimize("-flto")
 
 #include "device.h"
 #include "scsi.h"
 #include "config.h"
 #include "cdrom.h"
 
-uint8_t SimpleTOC[] =
+static const uint8_t SimpleTOC[] =
 {
        0x00, // toc length, MSB
-       0x0A, // toc length, LSB
+       0x12, // toc length, LSB
        0x01, // First track number
        0x01, // Last track number,
        // TRACK 1 Descriptor
-       0x00, // reservied
-       0x06, // Q sub-channel not supplied, Digital track
+       0x00, // reserved
+       0x14, // Q sub-channel encodes current position, Digital track
        0x01, // Track 1,
        0x00, // Reserved
-       0x00,0x00,0x00,0x00 // Track start sector (LBA)
+       0x00,0x00,0x00,0x00, // Track start sector (LBA)
+       0x00, // reserved
+       0x14, // Q sub-channel encodes current position, Digital track
+       0xAA, // Leadout Track
+       0x00, // Reserved
+       0x00,0x00,0x00,0x00, // Track start sector (LBA)
+};
+
+static const uint8_t SessionTOC[] =
+{
+       0x00, // toc length, MSB
+       0x0A, // toc length, LSB
+       0x01, // First session number
+       0x01, // Last session number,
+       // TRACK 1 Descriptor
+       0x00, // reserved
+       0x14, // Q sub-channel encodes current position, Digital track
+       0x01, // First track number in last complete session
+       0x00, // Reserved
+       0x00,0x00,0x00,0x00 // LBA of first track in last session
+};
+
+
+static const uint8_t FullTOC[] =
+{
+       0x00, // toc length, MSB
+       0x44, // toc length, LSB
+       0x01, // First session number
+       0x01, // Last session number,
+       // A0 Descriptor
+       0x01, // session number
+       0x14, // ADR/Control
+       0x00, // TNO
+       0xA0, // POINT
+       0x00, // Min
+       0x00, // Sec
+       0x00, // Frame
+       0x00, // Zero
+       0x01, // First Track number.
+       0x00, // Disc type 00 = Mode 1
+       0x00,  // PFRAME
+       // A1
+       0x01, // session number
+       0x14, // ADR/Control
+       0x00, // TNO
+       0xA1, // POINT
+       0x00, // Min
+       0x00, // Sec
+       0x00, // Frame
+       0x00, // Zero
+       0x01, // Last Track number
+       0x00, // PSEC
+       0x00,  // PFRAME
+       // A2
+       0x01, // session number
+       0x14, // ADR/Control
+       0x00, // TNO
+       0xA2, // POINT
+       0x00, // Min
+       0x00, // Sec
+       0x00, // Frame
+       0x00, // Zero
+       0x79, // LEADOUT position BCD
+       0x59, // leadout PSEC BCD
+       0x74, // leadout PFRAME BCD
+       // TRACK 1 Descriptor
+       0x01, // session number
+       0x14, // ADR/Control
+       0x00, // TNO
+       0x01, // Point
+       0x00, // Min
+       0x00, // Sec
+       0x00, // Frame
+       0x00, // Zero
+       0x00, // PMIN
+       0x00, // PSEC
+       0x00,  // PFRAME
+       // b0
+       0x01, // session number
+       0x54, // ADR/Control
+       0x00, // TNO
+       0xB1, // POINT
+       0x79, // Min BCD
+       0x59, // Sec BCD
+       0x74, // Frame BCD
+       0x00, // Zero
+       0x79, // PMIN BCD
+       0x59, // PSEC BCD
+       0x74,  // PFRAME BCD
+       // c0
+       0x01, // session number
+       0x54, // ADR/Control
+       0x00, // TNO
+       0xC0, // POINT
+       0x00, // Min
+       0x00, // Sec
+       0x00, // Frame
+       0x00, // Zero
+       0x00, // PMIN
+       0x00, // PSEC
+       0x00  // PFRAME
 };
 
-void doReadTOC(int MSF, uint8_t track, uint16_t allocationLength)
+static void LBA2MSF(uint32_t LBA, uint8_t* MSF)
+{
+       MSF[0] = 0; // reserved.
+       MSF[3] = LBA % 75; // M
+       uint32_t rem = LBA / 75;
+
+       MSF[2] = rem % 60; // S
+       MSF[1] = rem / 60;
+
+}
+
+static void doReadTOC(int MSF, uint8_t track, uint16_t allocationLength)
 {
        // We only support track 1.
        // track 0 means "return all tracks"
@@ -45,18 +158,29 @@ void doReadTOC(int MSF, uint8_t track, uint16_t allocationLength)
                scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
                scsiDev.phase = STATUS;
        }
-       else if (MSF)
-       {
-               // MSF addressing not currently supported.
-               scsiDev.status = CHECK_CONDITION;
-               scsiDev.target->sense.code = ILLEGAL_REQUEST;
-               scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
-               scsiDev.phase = STATUS;
-       }
        else
        {
                uint32_t len = sizeof(SimpleTOC);
                memcpy(scsiDev.data, SimpleTOC, len);
+
+               uint32_t capacity = getScsiCapacity(
+                       scsiDev.target->cfg->sdSectorStart,
+                       scsiDev.target->liveCfg.bytesPerSector,
+                       scsiDev.target->cfg->scsiSectors);
+
+               // Replace start of leadout track
+               if (MSF)
+               {
+                       LBA2MSF(capacity, scsiDev.data + 0x0E);
+               }
+               else
+               {
+                       scsiDev.data[0x0E] = capacity >> 24;
+                       scsiDev.data[0x0F] = capacity >> 16;
+                       scsiDev.data[0x10] = capacity >> 8;
+                       scsiDev.data[0x11] = capacity;
+               }
+
                if (len > allocationLength)
                {
                        len = allocationLength;
@@ -66,20 +190,30 @@ void doReadTOC(int MSF, uint8_t track, uint16_t allocationLength)
        }
 }
 
-uint8_t SimpleHeader[] =
+static void doReadSessionInfo(uint8_t session, uint16_t allocationLength)
 {
-       0x01, // 2048byte user data, L-EC in 288 byte aux field.
-       0x00, // reserved
-       0x00, // reserved
-       0x00, // reserved
-       0x00,0x00,0x00,0x00 // Track start sector (LBA)
-};
+       uint32_t len = sizeof(SessionTOC);
+       memcpy(scsiDev.data, SessionTOC, len);
 
-void doReadHeader(int MSF, uint32_t lba, uint16_t allocationLength)
+       if (len > allocationLength)
+       {
+               len = allocationLength;
+       }
+       scsiDev.dataLen = len;
+       scsiDev.phase = DATA_IN;
+}
+
+static inline uint8_t
+fromBCD(uint8_t val)
 {
-       if (MSF)
+       return ((val >> 4) * 10) + (val & 0xF);
+}
+
+static void doReadFullTOC(int convertBCD, uint8_t session, uint16_t allocationLength)
+{
+       // We only support session 1.
+       if (session > 1)
        {
-               // MSF addressing not currently supported.
                scsiDev.status = CHECK_CONDITION;
                scsiDev.target->sense.code = ILLEGAL_REQUEST;
                scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
@@ -87,8 +221,25 @@ void doReadHeader(int MSF, uint32_t lba, uint16_t allocationLength)
        }
        else
        {
-               uint32_t len = sizeof(SimpleHeader);
-               memcpy(scsiDev.data, SimpleHeader, len);
+               uint32_t len = sizeof(FullTOC);
+               memcpy(scsiDev.data, FullTOC, len);
+
+               if (convertBCD)
+               {
+                       int descriptor = 4;
+                       while (descriptor < len)
+                       {
+                               int i;
+                               for (i = 0; i < 7; ++i)
+                               {
+                                       scsiDev.data[descriptor + i] =
+                                               fromBCD(scsiDev.data[descriptor + 4 + i]);
+                               }
+                               descriptor += 11;
+                       }
+
+               }
+
                if (len > allocationLength)
                {
                        len = allocationLength;
@@ -98,6 +249,27 @@ void doReadHeader(int MSF, uint32_t lba, uint16_t allocationLength)
        }
 }
 
+static uint8_t SimpleHeader[] =
+{
+       0x01, // 2048byte user data, L-EC in 288 byte aux field.
+       0x00, // reserved
+       0x00, // reserved
+       0x00, // reserved
+       0x00,0x00,0x00,0x00 // Track start sector (LBA or MSF)
+};
+
+void doReadHeader(int MSF, uint32_t lba, uint16_t allocationLength)
+{
+       uint32_t len = sizeof(SimpleHeader);
+       memcpy(scsiDev.data, SimpleHeader, len);
+       if (len > allocationLength)
+       {
+               len = allocationLength;
+       }
+       scsiDev.dataLen = len;
+       scsiDev.phase = DATA_IN;
+}
+
 
 // Handle direct-access scsi device commands
 int scsiCDRomCommand()
@@ -105,34 +277,44 @@ int scsiCDRomCommand()
        int commandHandled = 1;
 
        uint8 command = scsiDev.cdb[0];
-       if (scsiDev.target->cfg->deviceType == CONFIG_OPTICAL)
+       if (command == 0x43)
        {
-               if (command == 0x43)
-               {
-                       // CD-ROM Read TOC
-                       int MSF = scsiDev.cdb[1] & 0x02 ? 1 : 0;
-                       uint8_t track = scsiDev.cdb[6];
-                       uint16_t allocationLength =
-                               (((uint32_t) scsiDev.cdb[7]) << 8) +
-                               scsiDev.cdb[8];
-
-                       doReadTOC(MSF, track, allocationLength);
-               }
-               else if (command == 0x44)
-               {
-                       // CD-ROM Read Header
-                       int MSF = scsiDev.cdb[1] & 0x02 ? 1 : 0;
-                       uint32_t lba = 0; // IGNORED for now
-                       uint16_t allocationLength =
-                               (((uint32_t) scsiDev.cdb[7]) << 8) +
-                               scsiDev.cdb[8];
-                       doReadHeader(MSF, lba, allocationLength);
-               }
-               else
+               // CD-ROM Read TOC
+               int MSF = scsiDev.cdb[1] & 0x02 ? 1 : 0;
+               uint8_t track = scsiDev.cdb[6];
+               uint16_t allocationLength =
+                       (((uint32_t) scsiDev.cdb[7]) << 8) +
+                       scsiDev.cdb[8];
+
+               // Reject MMC commands for now, otherwise the TOC data format
+               // won't be understood.
+               // The "format" field is reserved for SCSI-2
+               uint8_t format = scsiDev.cdb[2] & 0x0F;
+               switch (format)
                {
-                       commandHandled = 0;
+                       case 0: doReadTOC(MSF, track, allocationLength); break; // SCSI-2
+                       case 1: doReadSessionInfo(MSF, allocationLength); break; // MMC2
+                       case 2: doReadFullTOC(0, track, allocationLength); break; // MMC2
+                       case 3: doReadFullTOC(1, track, allocationLength); break; // MMC2
+                       default:
+                       {
+                               scsiDev.status = CHECK_CONDITION;
+                               scsiDev.target->sense.code = ILLEGAL_REQUEST;
+                               scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
+                               scsiDev.phase = STATUS;
+                       }
                }
        }
+       else if (command == 0x44)
+       {
+               // CD-ROM Read Header
+               int MSF = scsiDev.cdb[1] & 0x02 ? 1 : 0;
+               uint32_t lba = 0; // IGNORED for now
+               uint16_t allocationLength =
+                       (((uint32_t) scsiDev.cdb[7]) << 8) +
+                       scsiDev.cdb[8];
+               doReadHeader(MSF, lba, allocationLength);
+       }
        else
        {
                commandHandled = 0;
@@ -140,3 +322,5 @@ int scsiCDRomCommand()
 
        return commandHandled;
 }
+
+#pragma GCC pop_options