Added configurable block-size support
authorMichael McMaster <michael@codesrc.com>
Thu, 27 Mar 2014 14:24:33 +0000 (00:24 +1000)
committerMichael McMaster <michael@codesrc.com>
Thu, 27 Mar 2014 14:24:33 +0000 (00:24 +1000)
13 files changed:
CHANGELOG
software/SCSI2SD/src/config.c
software/SCSI2SD/src/config.h
software/SCSI2SD/src/disk.c
software/SCSI2SD/src/disk.h
software/SCSI2SD/src/geometry.c
software/SCSI2SD/src/geometry.h
software/SCSI2SD/src/mode.c
software/SCSI2SD/src/scsi.c
software/SCSI2SD/src/scsi.h
software/SCSI2SD/src/sd.c
software/SCSI2SD/src/sd.h
software/scsi2sd-config/main.c

index ef65147..0e6de11 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,7 @@
                - scsi2sd-config can be used to disable it for those systems that
                truely require it (eg. Mac Plus).
        - Added Linked commands support.
+       - Added support for configurable sector sizes between 64 and 2048 bytes.
        - Powerbook firmware added
 
 20140214               3.2
index 9abdc67..37dbc55 100755 (executable)
@@ -27,7 +27,7 @@
 #include <string.h>\r
 \r
 // CYDEV_EEPROM_ROW_SIZE == 16.\r
-static char magic[CYDEV_EEPROM_ROW_SIZE] = "codesrc_00000001";\r
+static char magic[CYDEV_EEPROM_ROW_SIZE] = "codesrc_00000002";\r
 \r
 // Config shadow RAM (copy of EEPROM)\r
 static Config shadow =\r
@@ -38,7 +38,9 @@ static Config shadow =
        " 3.3", // revision (68k Apple Drive Setup: Set to "1.0 ")\r
        1, // enable parity\r
        1, // enable unit attention,\r
-       0 // Max blocks (0 == disabled)\r
+       0, // RESERVED\r
+       0, // Max sectors (0 == disabled)\r
+       512 // Sector size\r
        // reserved bytes will be initialised to 0.\r
 };\r
 \r
@@ -68,6 +70,12 @@ static uint32_t ntohl(uint32_t val)
                ((val >> 8) & 0xFF00) |\r
                ((val >> 24) & 0xFF);\r
 }\r
+static uint16_t ntohs(uint16_t val)\r
+{\r
+       return\r
+               ((val & 0xFF) << 8) |\r
+               ((val >> 8) & 0xFF);\r
+}\r
 static uint32_t htonl(uint32_t val)\r
 {\r
        return\r
@@ -76,6 +84,12 @@ static uint32_t htonl(uint32_t val)
                ((val >> 8) & 0xFF00) |\r
                ((val >> 24) & 0xFF);\r
 }\r
+static uint16_t htons(uint16_t val)\r
+{\r
+       return\r
+               ((val & 0xFF) << 8) |\r
+               ((val >> 8) & 0xFF);\r
+}\r
 \r
 static void saveConfig()\r
 {\r
@@ -111,6 +125,15 @@ void configInit()
 \r
        if (memcmp(eeprom + shadowBytes, magic, sizeof(magic)))\r
        {\r
+               // Initial state, invalid, or upgrade required.\r
+               if (!memcmp(eeprom + shadowBytes, magic, sizeof(magic) - 1) &&\r
+                       ((eeprom + shadowBytes)[sizeof(magic) - 2] == '1'))\r
+               {\r
+                       // Upgrade from version 1.\r
+                       memcpy(&shadow, eeprom, sizeof(shadow));\r
+                       shadow.bytesPerSector = 512;\r
+               }\r
+\r
                saveConfig();\r
        }\r
        else\r
@@ -161,7 +184,17 @@ void configPoll()
                // shadow should be padded out to 64bytes, which is the largest\r
                // possible HID transfer.\r
                USBFS_ReadOutEP(USB_EP_OUT, (uint8 *)&shadow, byteCount);\r
-               shadow.maxBlocks = htonl(shadow.maxBlocks);\r
+               shadow.maxSectors = ntohl(shadow.maxSectors);\r
+               shadow.bytesPerSector = ntohs(shadow.bytesPerSector);\r
+\r
+               if (shadow.bytesPerSector > MAX_SECTOR_SIZE)\r
+               {\r
+                       shadow.bytesPerSector = MAX_SECTOR_SIZE;\r
+               }\r
+               else if (shadow.bytesPerSector < MIN_SECTOR_SIZE)\r
+               {\r
+                       shadow.bytesPerSector = MIN_SECTOR_SIZE;\r
+               }\r
 \r
                CFG_EEPROM_Start();\r
                saveConfig(); // write to eeprom\r
@@ -173,14 +206,18 @@ void configPoll()
                // Allow the host to send us another updated config.\r
                USBFS_EnableOutEP(USB_EP_OUT);\r
 \r
+               // Set unt attention as the block size may have changed.\r
+               scsiDev.unitAttention = MODE_PARAMETERS_CHANGED;\r
+\r
                ledOff();\r
        }\r
 \r
        switch (usbInEpState)\r
        {\r
        case USB_IDLE:\r
-               shadow.maxBlocks = htonl(shadow.maxBlocks);\r
-               \r
+               shadow.maxSectors = htonl(shadow.maxSectors);\r
+               shadow.bytesPerSector = htons(shadow.bytesPerSector);\r
+\r
                #ifdef MM_DEBUG\r
                memcpy(&shadow.reserved, &scsiDev.cdb, 12);\r
                shadow.reserved[12] = scsiDev.msgIn;\r
@@ -197,12 +234,11 @@ void configPoll()
                shadow.reserved[23] = scsiDev.msgCount;\r
                shadow.reserved[24] = scsiDev.cmdCount;\r
                shadow.reserved[25] = scsiDev.watchdogTick;\r
-               shadow.reserved[26] = blockDev.state;\r
-               shadow.reserved[27] = scsiReadDBxPins();\r
                #endif\r
 \r
                USBFS_LoadInEP(USB_EP_IN, (uint8 *)&shadow, sizeof(shadow));\r
-               shadow.maxBlocks = ntohl(shadow.maxBlocks);\r
+               shadow.maxSectors = ntohl(shadow.maxSectors);\r
+               shadow.bytesPerSector = ntohs(shadow.bytesPerSector);\r
                usbInEpState = USB_DATA_SENT;\r
                break;\r
 \r
@@ -216,3 +252,11 @@ void configPoll()
        }\r
 }\r
 \r
+// Public method for storing MODE SELECT results.\r
+void configSave()\r
+{\r
+       CFG_EEPROM_Start();\r
+       saveConfig(); // write to eeprom\r
+       CFG_EEPROM_Stop();\r
+}\r
+\r
index 6b048bd..8867038 100755 (executable)
@@ -28,15 +28,17 @@ typedef struct
        uint8 enableParity;\r
        uint8 enableUnitAttention;\r
        uint8 reserved1; // Unused. Ensures maxBlocks is aligned.\r
-       uint32 maxBlocks;\r
+       uint32 maxSectors;\r
+       uint16 bytesPerSector;\r
 \r
        // Pad to 64 bytes, which is what we can fit into a USB HID packet.\r
-       char reserved[28]; \r
+       char reserved[26]; \r
 } Config;\r
 \r
 extern Config* config;\r
 \r
 void configInit(void);\r
 void configPoll(void);\r
+void configSave(void);\r
 \r
 #endif\r
index 32b3bc3..fb76ff2 100755 (executable)
@@ -34,11 +34,6 @@ static int doSdInit()
        if (result)\r
        {\r
                blockDev.state = blockDev.state | DISK_INITIALISED;\r
-\r
-               // artificially limit this value according to EEPROM config.\r
-               blockDev.capacity =\r
-                       (config->maxBlocks && (sdDev.capacity > config->maxBlocks))\r
-                               ? config->maxBlocks : sdDev.capacity;\r
        }\r
        return result;\r
 }\r
@@ -52,12 +47,14 @@ static void doFormatUnit()
 \r
 static void doReadCapacity()\r
 {\r
-       uint32 lba = (((uint32) scsiDev.cdb[2]) << 24) +\r
+       uint32_t lba = (((uint32) scsiDev.cdb[2]) << 24) +\r
                (((uint32) scsiDev.cdb[3]) << 16) +\r
                (((uint32) scsiDev.cdb[4]) << 8) +\r
                scsiDev.cdb[5];\r
        int pmi = scsiDev.cdb[8] & 1;\r
 \r
+       uint32_t capacity = getScsiCapacity();\r
+\r
        if (!pmi && lba)\r
        {\r
                // error.\r
@@ -69,19 +66,19 @@ static void doReadCapacity()
                scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
                scsiDev.phase = STATUS;\r
        }\r
-       else if (blockDev.capacity > 0)\r
+       else if (capacity > 0)\r
        {\r
-               uint32 highestBlock = blockDev.capacity - 1;\r
+               uint32_t highestBlock = capacity - 1;\r
 \r
                scsiDev.data[0] = highestBlock >> 24;\r
                scsiDev.data[1] = highestBlock >> 16;\r
                scsiDev.data[2] = highestBlock >> 8;\r
                scsiDev.data[3] = highestBlock;\r
 \r
-               scsiDev.data[4] = blockDev.bs >> 24;\r
-               scsiDev.data[5] = blockDev.bs >> 16;\r
-               scsiDev.data[6] = blockDev.bs >> 8;\r
-               scsiDev.data[7] = blockDev.bs;\r
+               scsiDev.data[4] = config->bytesPerSector >> 24;\r
+               scsiDev.data[5] = config->bytesPerSector >> 16;\r
+               scsiDev.data[6] = config->bytesPerSector >> 8;\r
+               scsiDev.data[7] = config->bytesPerSector;\r
                scsiDev.dataLen = 8;\r
                scsiDev.phase = DATA_IN;\r
        }\r
@@ -103,7 +100,7 @@ static void doWrite(uint32 lba, uint32 blocks)
                scsiDev.sense.asc = WRITE_PROTECTED;\r
                scsiDev.phase = STATUS;\r
        }\r
-       else if (((uint64) lba) + blocks > blockDev.capacity)\r
+       else if (((uint64) lba) + blocks > getScsiCapacity())\r
        {\r
                scsiDev.status = CHECK_CONDITION;\r
                scsiDev.sense.code = ILLEGAL_REQUEST;\r
@@ -117,11 +114,11 @@ static void doWrite(uint32 lba, uint32 blocks)
                transfer.blocks = blocks;\r
                transfer.currentBlock = 0;\r
                scsiDev.phase = DATA_OUT;\r
-               scsiDev.dataLen = SCSI_BLOCK_SIZE;\r
-               scsiDev.dataPtr = SCSI_BLOCK_SIZE; // TODO FIX scsiDiskPoll()\r
+               scsiDev.dataLen = config->bytesPerSector;\r
+               scsiDev.dataPtr = config->bytesPerSector; // TODO FIX scsiDiskPoll()\r
 \r
-               // No need for single-block reads atm.  Overhead of the\r
-               // multi-block read is minimal.\r
+               // No need for single-block writes atm.  Overhead of the\r
+               // multi-block write is minimal.\r
                transfer.multiBlock = 1;\r
                sdPrepareWrite();\r
        }\r
@@ -130,7 +127,8 @@ static void doWrite(uint32 lba, uint32 blocks)
 \r
 static void doRead(uint32 lba, uint32 blocks)\r
 {\r
-       if (((uint64) lba) + blocks > blockDev.capacity)\r
+       uint32_t capacity = getScsiCapacity();\r
+       if (((uint64) lba) + blocks > capacity)\r
        {\r
                scsiDev.status = CHECK_CONDITION;\r
                scsiDev.sense.code = ILLEGAL_REQUEST;\r
@@ -147,7 +145,7 @@ static void doRead(uint32 lba, uint32 blocks)
                scsiDev.dataLen = 0; // No data yet\r
 \r
                if ((blocks == 1) ||\r
-                       (((uint64) lba) + blocks == blockDev.capacity)\r
+                       (((uint64) lba) + blocks == capacity)\r
                        )\r
                {\r
                        // We get errors on reading the last sector using a multi-sector\r
@@ -164,7 +162,7 @@ static void doRead(uint32 lba, uint32 blocks)
 \r
 static void doSeek(uint32 lba)\r
 {\r
-       if (lba >= blockDev.capacity)\r
+       if (lba >= getScsiCapacity())\r
        {\r
                scsiDev.status = CHECK_CONDITION;\r
                scsiDev.sense.code = ILLEGAL_REQUEST;\r
@@ -455,8 +453,6 @@ void scsiDiskReset()
 \r
 void scsiDiskInit()\r
 {\r
-       blockDev.bs = SCSI_BLOCK_SIZE;\r
-       blockDev.capacity = 0;\r
        transfer.inProgress = 0;\r
        scsiDiskReset();\r
 \r
index c7c2c80..b9367a4 100755 (executable)
@@ -33,9 +33,6 @@ typedef enum
 
 typedef struct
 {
-       uint32 bs; // Block size.
-       uint32 capacity; // In blocks.
-
        int state;
 } BlockDevice;
 
index f42152b..01478e3 100755 (executable)
 \r
 #include <string.h>\r
 \r
+uint32_t getScsiCapacity()\r
+{\r
+       uint32_t capacity = sdDev.capacity / SDSectorsPerSCSISector();\r
+       if (config->maxSectors && (capacity > config->maxSectors))\r
+       {\r
+               capacity = config->maxSectors;\r
+       }\r
+       return capacity;\r
+}\r
+\r
+\r
+uint32_t SCSISector2SD(uint32_t scsiSector)\r
+{\r
+       return scsiSector * SDSectorsPerSCSISector();\r
+}\r
+\r
 // Standard mapping according to ECMA-107 and ISO/IEC 9293:1994\r
 // Sector always starts at 1. There is no 0 sector.\r
 uint64 CHS2LBA(uint32 c, uint8 h, uint32 s)\r
@@ -51,7 +67,7 @@ uint64 scsiByteAddress(int format, const uint8* addr)
                        (((uint32) addr[2]) << 8) +\r
                        addr[3];\r
 \r
-               result = (uint64) SCSI_BLOCK_SIZE * lba;\r
+               result = (uint64_t) config->bytesPerSector * lba;\r
        } break;\r
 \r
        case ADDRESS_PHYSICAL_BYTE:\r
@@ -69,7 +85,7 @@ uint64 scsiByteAddress(int format, const uint8* addr)
                        (((uint32) addr[6]) << 8) +\r
                        addr[7];\r
 \r
-               result = CHS2LBA(cyl, head, 1) * (uint64) SCSI_SECTOR_SIZE + bytes;\r
+               result = CHS2LBA(cyl, head, 1) * (uint64_t) config->bytesPerSector + bytes;\r
        } break;\r
 \r
        case ADDRESS_PHYSICAL_SECTOR:\r
@@ -87,7 +103,7 @@ uint64 scsiByteAddress(int format, const uint8* addr)
                        (((uint32) addr[6]) << 8) +\r
                        addr[7];\r
 \r
-               result = CHS2LBA(cyl, head, sector) * (uint64) SCSI_SECTOR_SIZE;\r
+               result = CHS2LBA(cyl, head, sector) * (uint64_t) config->bytesPerSector;\r
        } break;\r
 \r
        default:\r
@@ -100,8 +116,8 @@ uint64 scsiByteAddress(int format, const uint8* addr)
 \r
 void scsiSaveByteAddress(int format, uint64 byteAddr, uint8* buf)\r
 {\r
-       uint32 lba = byteAddr / SCSI_BLOCK_SIZE;\r
-       uint32 byteOffset = byteAddr % SCSI_BLOCK_SIZE;\r
+       uint32 lba = byteAddr / config->bytesPerSector;\r
+       uint32 byteOffset = byteAddr % config->bytesPerSector;\r
 \r
        switch (format)\r
        {\r
@@ -127,7 +143,7 @@ void scsiSaveByteAddress(int format, uint64 byteAddr, uint8* buf)
 \r
                LBA2CHS(lba, &cyl, &head, &sector);\r
 \r
-               bytes = sector * SCSI_SECTOR_SIZE + byteOffset;\r
+               bytes = sector * config->bytesPerSector + byteOffset;\r
 \r
                buf[0] = cyl >> 16;\r
                buf[1] = cyl >> 8;\r
index daf0fdd..c965870 100755 (executable)
 
 #include "device.h"
 
-// We make some assumptions that the block size and sector size
-// are always equal.
-#define SCSI_BLOCK_SIZE 512
-#define SCSI_SECTOR_SIZE 512
+#include "config.h"
+#include "sd.h"
 
 // Max allowed by legacy IBM-PC Bios (6 bits)
 #define SCSI_SECTORS_PER_TRACK 63
@@ -37,6 +35,14 @@ typedef enum
        ADDRESS_PHYSICAL_SECTOR = 5
 } SCSI_ADDRESS_FORMAT;
 
+static inline int SDSectorsPerSCSISector()
+{
+       return (config->bytesPerSector + SD_SECTOR_SIZE - 1) / SD_SECTOR_SIZE;
+}
+
+uint32_t getScsiCapacity();
+
+uint32_t SCSISector2SD(uint32_t scsiSector);
 
 uint64 CHS2LBA(uint32 c, uint8 h, uint32 s);
 void LBA2CHS(uint32 lba, uint32* c, uint8* h, uint32* s);
index 0f5b38c..385fdf8 100755 (executable)
@@ -54,14 +54,14 @@ static const uint8 DisconnectReconnectPage[] =
 \r
 static const uint8 FormatDevicePage[] =\r
 {\r
-0x03, // Page code \r
+0x03 | 0x80, // Page code | PS (persist) bit.\r
 0x16, // Page length\r
 0x00, 0x00, // Single zone\r
 0x00, 0x00, // No alternate sectors\r
 0x00, 0x00, // No alternate tracks\r
 0x00, 0x00, // No alternate tracks per lun\r
 0x00, SCSI_SECTORS_PER_TRACK, // Sectors per track\r
-SCSI_SECTOR_SIZE >> 8, SCSI_SECTOR_SIZE & 0xFF, // Data bytes per physical sector\r
+0xFF, 0xFF, // Data bytes per physical sector. Configurable.\r
 0x00, 0x01, // Interleave\r
 0x00, 0x00, // Track skew factor\r
 0x00, 0x00, // Cylinder skew factor\r
@@ -111,7 +111,6 @@ static const uint8 ControlModePage[] =
 \r
 // Allow Apple 68k Drive Setup to format this drive.\r
 // Code\r
-// TODO make this string configurable.\r
 static const uint8 AppleVendorPage[] =\r
 {\r
 0x30, // Page code\r
@@ -143,7 +142,7 @@ static void doModeSense(
        else\r
        {\r
                int pageFound = 1;\r
-               \r
+\r
                ////////////// Mode Parameter Header\r
                ////////////////////////////////////\r
 \r
@@ -201,9 +200,9 @@ static void doModeSense(
                        scsiDev.data[idx++] = 0; // reserved\r
 \r
                        // Block length\r
-                       scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 16;\r
-                       scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 8;\r
-                       scsiDev.data[idx++] = SCSI_BLOCK_SIZE & 0xFF;\r
+                       scsiDev.data[idx++] = config->bytesPerSector >> 16;\r
+                       scsiDev.data[idx++] = config->bytesPerSector >> 8;\r
+                       scsiDev.data[idx++] = config->bytesPerSector & 0xFF;\r
                }\r
 \r
                switch (pageCode)\r
@@ -223,6 +222,19 @@ static void doModeSense(
 \r
                case 0x03:\r
                        pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));\r
+                       if (pc != 0x01)\r
+                       {\r
+                               // Fill out the configured bytes-per-sector\r
+                               scsiDev.data[idx+12] = config->bytesPerSector >> 8;\r
+                               scsiDev.data[idx+13] = config->bytesPerSector & 0xFF;\r
+                       }\r
+                       else\r
+                       {\r
+                               // Set a mask for the changeable values.\r
+                               scsiDev.data[idx+12] = 0xFF;\r
+                               scsiDev.data[idx+13] = 0xFF;\r
+                       }\r
+\r
                        idx += sizeof(FormatDevicePage);\r
                        if (pageCode != 0x3f) break;\r
 \r
@@ -236,7 +248,7 @@ static void doModeSense(
                                uint32 cyl;\r
                                uint8 head;\r
                                uint32 sector;\r
-                               LBA2CHS(blockDev.capacity, &cyl, &head, &sector);\r
+                               LBA2CHS(getScsiCapacity(), &cyl, &head, &sector);\r
 \r
                                scsiDev.data[idx+2] = cyl >> 16;\r
                                scsiDev.data[idx+3] = cyl >> 8;\r
@@ -309,6 +321,74 @@ static void doModeSense(
        }\r
 }\r
 \r
+// Callback after the DATA OUT phase is complete.\r
+static void doModeSelect(void)\r
+{\r
+       if (scsiDev.status == GOOD) // skip if we've already encountered an error\r
+       {\r
+               // scsiDev.dataLen bytes are in scsiDev.data\r
+\r
+               int idx;\r
+               if (scsiDev.cdb[0] == 0x15)\r
+               {\r
+                       int blockDescLen =\r
+                               (((uint16_t)scsiDev.data[6]) << 8) |scsiDev.data[7];\r
+                       idx = 8 + blockDescLen;\r
+               }\r
+               else\r
+               {\r
+                       int blockDescLen = scsiDev.data[3];\r
+                       idx = 4 + blockDescLen;\r
+               }\r
+               if (idx > scsiDev.dataLen) goto bad;\r
+\r
+               while (idx < scsiDev.dataLen)\r
+               {\r
+                       int pageLen = scsiDev.data[idx + 1];\r
+                       if (idx + 2 + pageLen > scsiDev.dataLen) goto bad;\r
+\r
+                       int pageCode = scsiDev.data[idx] & 0x3F;\r
+                       switch (pageCode)\r
+                       {\r
+                       case 0x03: // Format Device Page\r
+                       {\r
+                               if (pageLen != 0x16) goto bad;\r
+\r
+                               // Fill out the configured bytes-per-sector\r
+                               uint16_t bytesPerSector =\r
+                                       (((uint16_t)scsiDev.data[idx+12]) << 8) |\r
+                                       scsiDev.data[idx+13];\r
+\r
+                               // Sane values only, ok ?\r
+                               if ((bytesPerSector < MIN_SECTOR_SIZE) ||\r
+                                       (bytesPerSector > MAX_SECTOR_SIZE))\r
+                               {\r
+                                       goto bad;\r
+                               }\r
+\r
+                               config->bytesPerSector = bytesPerSector;\r
+                               if (scsiDev.cdb[1] & 1) // SP Save Pages flag\r
+                               {\r
+                                       configSave();\r
+                               }\r
+                       }\r
+                       break;\r
+                       default:\r
+                               goto bad;\r
+                       }\r
+               }\r
+       }\r
+\r
+       goto out;\r
+bad:\r
+       scsiDev.status = CHECK_CONDITION;\r
+       scsiDev.sense.code = ILLEGAL_REQUEST;\r
+       scsiDev.sense.asc = INVALID_FIELD_IN_PARAMETER_LIST;\r
+\r
+out:\r
+       scsiDev.phase = STATUS;\r
+}\r
+\r
 int scsiModeCommand()\r
 {\r
        int commandHandled = 1;\r
@@ -343,16 +423,39 @@ int scsiModeCommand()
        {\r
                // MODE SELECT(6)\r
                int len = scsiDev.cdb[4];\r
-               if (len == 0) len = 256;\r
-               scsiDev.dataLen = len;\r
-               scsiDev.phase = DATA_OUT;\r
+               if (len == 0)\r
+               {\r
+                       // If len == 0, then transfer no data. From the SCSI 2 standard:\r
+                       //      A parameter list length of zero indicates that no data shall\r
+                       //      be transferred. This condition shall not be considered as an\r
+                       //              error.\r
+                       scsiDev.phase = STATUS;\r
+               }\r
+               else\r
+               {\r
+                       scsiDev.dataLen = len;\r
+                       scsiDev.phase = DATA_OUT;\r
+                       scsiDev.postDataOutHook = doModeSelect;\r
+               }\r
        }\r
        else if (command == 0x55)\r
        {\r
                // MODE SELECT(10)\r
                int allocLength = (((uint16) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];\r
-               scsiDev.dataLen = allocLength;\r
-               scsiDev.phase = DATA_OUT;\r
+               if (allocLength == 0)\r
+               {\r
+                       // If len == 0, then transfer no data. From the SCSI 2 standard:\r
+                       //      A parameter list length of zero indicates that no data shall\r
+                       //      be transferred. This condition shall not be considered as an\r
+                       //              error.\r
+                       scsiDev.phase = STATUS;\r
+               }\r
+               else\r
+               {\r
+                       scsiDev.dataLen = allocLength;\r
+                       scsiDev.phase = DATA_OUT;\r
+                       scsiDev.postDataOutHook = doModeSelect;\r
+               }\r
        }\r
        else\r
        {\r
index af7af32..581c17a 100755 (executable)
@@ -178,7 +178,14 @@ static void process_DataIn()
        if ((scsiDev.dataPtr >= scsiDev.dataLen) &&\r
                (transfer.currentBlock == transfer.blocks))\r
        {\r
-               enter_Status(GOOD);\r
+               if (scsiDev.postDataOutHook != NULL)\r
+               {\r
+                       scsiDev.postDataOutHook();\r
+               }\r
+               else\r
+               {\r
+                       enter_Status(GOOD);\r
+               }\r
        }\r
 }\r
 \r
@@ -439,6 +446,8 @@ static void scsiReset()
        scsiDev.sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
        scsiDiskReset();\r
 \r
+       scsiDev.postDataOutHook = NULL;\r
+\r
        // Sleep to allow the bus to settle down a bit.\r
        // We must be ready again within the "Reset to selection time" of\r
        // 250ms.\r
@@ -463,6 +472,8 @@ static void enter_SelectionPhase()
 \r
        transfer.blocks = 0;\r
        transfer.currentBlock = 0;\r
+\r
+       scsiDev.postDataOutHook = NULL;\r
 }\r
 \r
 static void process_SelectionPhase()\r
index 4ef2499..dd0c33a 100755 (executable)
@@ -62,6 +62,10 @@ typedef enum
        MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG = 0x0B
 } SCSI_MESSAGE;
 
+// Maximum value for bytes-per-sector.
+#define MAX_SECTOR_SIZE 2048
+#define MIN_SECTOR_SIZE 64
+
 typedef struct
 {
        uint8_t scsiIdMask;
@@ -78,7 +82,7 @@ typedef struct
 
        int phase;
 
-       uint8 data[SCSI_BLOCK_SIZE];
+       uint8 data[MAX_SECTOR_SIZE];
        int dataPtr; // Index into data, reset on [re]selection to savedDataPtr
        int savedDataPtr; // Index into data, initially 0.
        int dataLen;
@@ -103,6 +107,8 @@ typedef struct
        uint8 msgIn;
        uint8 msgOut;
 
+       void (*postDataOutHook)(void);
+
 #ifdef MM_DEBUG
        uint8 cmdCount;
        uint8 selCount;
index 28980c6..fb290f1 100755 (executable)
@@ -129,12 +129,14 @@ static void sdClearStatus()
 void sdPrepareRead()\r
 {\r
        uint8 v;\r
-       uint32 len = (transfer.lba + transfer.currentBlock);\r
+       uint32 scsiLBA = (transfer.lba + transfer.currentBlock);\r
+       uint32 sdLBA = SCSISector2SD(scsiLBA);\r
+       \r
        if (!sdDev.ccs)\r
        {\r
-               len = len * SCSI_BLOCK_SIZE;\r
+               sdLBA = sdLBA * SD_SECTOR_SIZE;\r
        }\r
-       v = sdCommandAndResponse(SD_READ_MULTIPLE_BLOCK, len);\r
+       v = sdCommandAndResponse(SD_READ_MULTIPLE_BLOCK, sdLBA);\r
        if (v)\r
        {\r
                scsiDiskReset();\r
@@ -151,7 +153,7 @@ void sdPrepareRead()
        }\r
 }\r
 \r
-static void doReadSector()\r
+static void doReadSector(uint32_t numBytes)\r
 {\r
        int prep, i, guard;\r
 \r
@@ -184,12 +186,9 @@ static void doReadSector()
        // Don't do a bus settle delay if we're already in the correct phase.\r
        if (transfer.currentBlock == 0)\r
        {\r
-               //scsiEnterPhase(DATA_OUT);\r
-               //CyDelayUs(200);\r
                scsiEnterPhase(DATA_IN);\r
-               //CyDelayUs(200); // TODO BLOODY SLOW INTERLEAVE\r
        }\r
-       \r
+\r
        // Quickly seed the FIFO\r
        prep = 4;\r
        CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO\r
@@ -204,7 +203,7 @@ static void doReadSector()
        // We stream data straight from the SDCard fifos into the SCSI component\r
        // FIFO's. If the loop isn't fast enough, the transmit FIFO's will empty,\r
        // and performance will suffer. Every clock cycle counts.\r
-       while (i < SCSI_BLOCK_SIZE && !scsiDev.resetFlag)\r
+       while (i < numBytes && !scsiDev.resetFlag)\r
        {\r
                uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);\r
                uint8_t scsiStatus = CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG);\r
@@ -232,7 +231,25 @@ static void doReadSector()
                // "combined" SPIM TX and RX FIFOS to the individual FIFO size.\r
                // Unlike the SCSI component, SPIM doesn't check if there's room in\r
                // the output FIFO before starting to transmit.\r
-               if ((prep - guard < 4) && (prep < SCSI_BLOCK_SIZE))\r
+               if ((prep - guard < 4) && (prep < numBytes))\r
+               {\r
+                       CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO\r
+                       prep++;\r
+               }\r
+       }\r
+\r
+       // Read and discard remaining bytes.\r
+       while (i < SD_SECTOR_SIZE)\r
+       {\r
+               uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);\r
+               if(sdRxStatus & SDCard_STS_RX_FIFO_NOT_EMPTY)\r
+               {\r
+                       CY_GET_REG8(SDCard_RXDATA_PTR);\r
+                       guard++;\r
+                       i++;\r
+               }\r
+\r
+               if ((prep - guard < 4) && (prep < SD_SECTOR_SIZE))\r
                {\r
                        CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO\r
                        prep++;\r
@@ -241,21 +258,20 @@ static void doReadSector()
 \r
        sdSpiByte(0xFF); // CRC\r
        sdSpiByte(0xFF); // CRC\r
-       scsiDev.dataLen = SCSI_BLOCK_SIZE;\r
-       scsiDev.dataPtr = SCSI_BLOCK_SIZE;\r
+       scsiDev.dataLen = numBytes;\r
+       scsiDev.dataPtr = numBytes;\r
        \r
        while (SCSI_ReadPin(SCSI_In_ACK) && !scsiDev.resetFlag) {}\r
 }\r
 \r
-void sdReadSectorSingle()\r
+static void doReadSectorSingle(uint32 sdBlock, int sdBytes)\r
 {\r
        uint8 v;\r
-       uint32 len = (transfer.lba + transfer.currentBlock);\r
        if (!sdDev.ccs)\r
        {\r
-               len = len * SCSI_BLOCK_SIZE;\r
+               sdBlock = sdBlock * SD_SECTOR_SIZE;\r
        }       \r
-       v = sdCommandAndResponse(SD_READ_SINGLE_BLOCK, len);\r
+       v = sdCommandAndResponse(SD_READ_SINGLE_BLOCK, sdBlock);\r
        if (v)\r
        {\r
                scsiDiskReset();\r
@@ -268,15 +284,47 @@ void sdReadSectorSingle()
        }\r
        else\r
        {\r
-               doReadSector();\r
+               doReadSector(sdBytes);\r
+       }\r
+}\r
+\r
+\r
+void sdReadSectorSingle()\r
+{\r
+       uint32 scsiLBA = (transfer.lba + transfer.currentBlock);\r
+       uint32 sdLBA = SCSISector2SD(scsiLBA);\r
+       \r
+       int sdSectors = SDSectorsPerSCSISector();\r
+       int i;\r
+       for (i = 0; (i < sdSectors - 1) && (scsiDev.status != CHECK_CONDITION); ++i)\r
+       {\r
+               doReadSectorSingle(sdLBA + i, SD_SECTOR_SIZE);\r
+       }\r
+\r
+       if (scsiDev.status != CHECK_CONDITION)\r
+       {\r
+               int remaining = config->bytesPerSector % SD_SECTOR_SIZE;\r
+               if (remaining == 0) remaining = SD_SECTOR_SIZE; // Full sector needed.\r
+               doReadSectorSingle(sdLBA + i, remaining);\r
        }\r
 }\r
 \r
 void sdReadSectorMulti()\r
 {\r
        // Pre: sdPrepareRead called.\r
-       \r
-       doReadSector();\r
+       int sdSectors = SDSectorsPerSCSISector();\r
+       int i;\r
+       for (i = 0; (i < sdSectors - 1) && (scsiDev.status != CHECK_CONDITION); ++i)\r
+       {\r
+               doReadSector(SD_SECTOR_SIZE);\r
+       }\r
+\r
+       if (scsiDev.status != CHECK_CONDITION)\r
+       {\r
+               int remaining = config->bytesPerSector % SD_SECTOR_SIZE;\r
+               if (remaining == 0) remaining = SD_SECTOR_SIZE; // Full sector needed.\r
+               doReadSector(remaining);\r
+       }\r
 }\r
 \r
 \r
@@ -329,7 +377,7 @@ static void sdWaitWriteBusy()
        } while (val != 0xFF);\r
 }\r
 \r
-int sdWriteSector()\r
+static int doWriteSector(uint32_t numBytes)\r
 {\r
        int prep, i, guard;\r
        int result, maxWait;\r
@@ -351,7 +399,7 @@ int sdWriteSector()
        // We stream data straight from the SCSI fifos into the SPIM component\r
        // FIFO's. If the loop isn't fast enough, the transmit FIFO's will empty,\r
        // and performance will suffer. Every clock cycle counts.       \r
-       while (i < SCSI_BLOCK_SIZE)\r
+       while (i < numBytes && !scsiDev.resetFlag)\r
        {\r
                uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);\r
                uint8_t scsiStatus = CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG);\r
@@ -375,7 +423,7 @@ int sdWriteSector()
                        ++i;\r
                }\r
 \r
-               if (prep < SCSI_BLOCK_SIZE &&\r
+               if (prep < numBytes &&\r
                        (scsiDev.resetFlag || (scsiStatus & 1)) // SCSI TX FIFO NOT FULL\r
                        )\r
                {\r
@@ -385,6 +433,25 @@ int sdWriteSector()
                }\r
        }\r
        \r
+       // Write remaining bytes as 0x00\r
+       while (i < SD_SECTOR_SIZE)\r
+       {\r
+               uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);\r
+\r
+               if(guard - i < 4)\r
+               {\r
+                       CY_SET_REG8(SDCard_TXDATA_PTR, 0x00);\r
+                       guard++;\r
+               }\r
+\r
+               // Byte has been sent out the SPIM interface.\r
+               if (sdRxStatus & SDCard_STS_RX_FIFO_NOT_EMPTY)\r
+               {\r
+                        CY_GET_REG8(SDCard_RXDATA_PTR);\r
+                       ++i;\r
+               }\r
+       }\r
+       \r
        sdSpiByte(0x00); // CRC\r
        sdSpiByte(0x00); // CRC\r
 \r
@@ -438,6 +505,26 @@ int sdWriteSector()
        return result;\r
 }\r
 \r
+int sdWriteSector()\r
+{\r
+       int result = 1;\r
+       // Pre: sdPrepareWrite called.\r
+       int sdSectors = SDSectorsPerSCSISector();\r
+       int i;\r
+       for (i = 0; result && (i < sdSectors - 1) && (scsiDev.status != CHECK_CONDITION); ++i)\r
+       {\r
+               result = doWriteSector(SD_SECTOR_SIZE);\r
+       }\r
+\r
+       if (result && scsiDev.status != CHECK_CONDITION)\r
+       {\r
+               int remaining = config->bytesPerSector % SD_SECTOR_SIZE;\r
+               if (remaining == 0) remaining = SD_SECTOR_SIZE; // Full sector needed.\r
+               result = doWriteSector(remaining);\r
+       }\r
+       return result;\r
+}\r
+\r
 void sdCompleteWrite()\r
 {\r
        transfer.inProgress = 0;\r
@@ -565,7 +652,7 @@ static int sdReadCSD()
                uint32 c_size = (((((uint32)buf[6]) & 0x3) << 16) | (((uint32)buf[7]) << 8) | buf[8]) >> 6;\r
                uint32 c_mult = (((((uint32)buf[9]) & 0x3) << 8) | ((uint32)buf[0xa])) >> 7;\r
                uint32 sectorSize = buf[5] & 0x0F;\r
-               sdDev.capacity = ((c_size+1) * ((uint64)1 << (c_mult+2)) * ((uint64)1 << sectorSize)) / SCSI_BLOCK_SIZE;\r
+               sdDev.capacity = ((c_size+1) * ((uint64)1 << (c_mult+2)) * ((uint64)1 << sectorSize)) / SD_SECTOR_SIZE;\r
        }\r
        else if ((buf[0] >> 6) == 0x01)\r
        {\r
@@ -622,7 +709,7 @@ int sdInit()
 \r
        // This command will be ignored if sdDev.ccs is set.\r
        // SDHC and SDXC are always 512bytes.\r
-       v = sdCRCCommandAndResponse(SD_SET_BLOCKLEN, SCSI_BLOCK_SIZE); //Force sector size\r
+       v = sdCRCCommandAndResponse(SD_SET_BLOCKLEN, SD_SECTOR_SIZE); //Force sector size\r
        if(v){goto bad;}\r
        v = sdCRCCommandAndResponse(SD_CRC_ON_OFF, 0); //crc off\r
        if(v){goto bad;}\r
@@ -671,23 +758,24 @@ out:
 \r
 void sdPrepareWrite()\r
 {\r
-       uint32 len;\r
        uint8 v;\r
        \r
        // Set the number of blocks to pre-erase by the multiple block write command\r
        // We don't care about the response - if the command is not accepted, writes\r
        // will just be a bit slower.\r
        // Max 22bit parameter.\r
-       uint32 blocks = transfer.blocks > 0x7FFFFF ? 0x7FFFFF : transfer.blocks;\r
+       uint32_t sdBlocks = transfer.blocks * SDSectorsPerSCSISector();\r
+       uint32 blocks = sdBlocks > 0x7FFFFF ? 0x7FFFFF : sdBlocks;\r
        sdCommandAndResponse(SD_APP_CMD, 0);\r
        sdCommandAndResponse(SD_APP_SET_WR_BLK_ERASE_COUNT, blocks);\r
 \r
-       len = (transfer.lba + transfer.currentBlock);\r
+       uint32 scsiLBA = (transfer.lba + transfer.currentBlock);\r
+       uint32 sdLBA = SCSISector2SD(scsiLBA);\r
        if (!sdDev.ccs)\r
        {\r
-               len = len * SCSI_BLOCK_SIZE;\r
+               sdLBA = sdLBA * SD_SECTOR_SIZE;\r
        }\r
-       v = sdCommandAndResponse(25, len);\r
+       v = sdCommandAndResponse(25, sdLBA);\r
        if (v)\r
        {\r
                scsiDiskReset();\r
index 7eb8fcf..4f1e9db 100755 (executable)
@@ -17,6 +17,8 @@
 #ifndef SD_H
 #define SD_H
 
+#define SD_SECTOR_SIZE 512
+
 typedef enum
 {
        SD_GO_IDLE_STATE = 0,
index 25b66bf..6784d1b 100644 (file)
@@ -45,7 +45,8 @@ enum
        PARAM_APPLE,
        PARAM_VENDOR,
        PARAM_PRODID,
-       PARAM_REV
+       PARAM_REV,
+       PARAM_BYTESPERSECTOR
 };
 
 // Must be consistent with the structure defined in the SCSI2SD config.h header.
@@ -59,7 +60,9 @@ typedef struct __attribute((packed))
        uint8_t enableParity;
        uint8_t enableUnitAttention;
        uint8_t reserved1; // Unused. Ensures maxBlocks is aligned.
-       uint32_t maxBlocks;
+       uint32_t maxSectors;
+       uint16_t bytesPerSector;
+
 
        // Pad to 64 bytes, which is what we can fit into a USB HID packet.
        char reserved[28];
@@ -74,10 +77,11 @@ static void printConfig(ConfigPacket* packet)
        printf("\n");
        printf("Parity Checking:\t\t%s\n", packet->enableParity ? "enabled" : "disabled");
        printf("Unit Attention Condition:\t%s\n", packet->enableUnitAttention ? "enabled" : "disabled");
-       if (packet->maxBlocks)
+       printf("Bytes per sector:\t\t%d\n", packet->bytesPerSector);
+       if (packet->maxSectors)
        {
                char sizeBuf[64];
-               uint64_t maxBytes = packet->maxBlocks * (uint64_t) 512;
+               uint64_t maxBytes = packet->maxSectors * (uint64_t) packet->bytesPerSector;
                if (maxBytes > (1024*1024*1024))
                {
                        sprintf(sizeBuf, "%.02fGB", maxBytes / (1024.0*1024.0*1024.0));
@@ -95,7 +99,7 @@ static void printConfig(ConfigPacket* packet)
                        sprintf(sizeBuf, "%" PRIu64 " bytes", maxBytes);
                }
 
-               printf("Maximum Size:\t\t\t%s (%d blocks)\n", sizeBuf, packet->maxBlocks);
+               printf("Maximum Size:\t\t\t%s (%d sectors)\n", sizeBuf, packet->maxSectors);
        }
        else
        {
@@ -117,7 +121,8 @@ static int readConfig(hid_device* handle, ConfigPacket* packet)
        }
 
        memcpy(packet, buf, result);
-       packet->maxBlocks = ntohl(packet->maxBlocks);
+       packet->maxSectors = ntohl(packet->maxSectors);
+       packet->bytesPerSector = ntohs(packet->bytesPerSector);
 
        return result;
 }
@@ -127,9 +132,11 @@ static int writeConfig(hid_device* handle, ConfigPacket* packet)
        unsigned char buf[1 + sizeof(ConfigPacket)];
        buf[0] = 0; // report ID
 
-       packet->maxBlocks = htonl(packet->maxBlocks);
+       packet->maxSectors = htonl(packet->maxSectors);
+       packet->bytesPerSector = htons(packet->bytesPerSector);
        memcpy(buf + 1, packet, sizeof(ConfigPacket));
-       packet->maxBlocks = ntohl(packet->maxBlocks);
+       packet->maxSectors = ntohl(packet->maxSectors);
+       packet->bytesPerSector = ntohs(packet->bytesPerSector);
 
        int result = hid_write(handle, buf, sizeof(buf));
 
@@ -159,6 +166,8 @@ static void usage()
        printf("\t\tEach block is 512 bytes. The maximum possible size is 2TB.\n");
        printf("\t\tThe reported size will be the lower of this value and the SD\n");
        printf("\t\tcard size. 0 disables the limit.\n\n");
+       printf("--sector={64-2048}\n\t\tSet the bytes-per-sector. Normally 512 bytes.\n");
+       printf("\t\tCan also be set with a SCSI MODE SELECT command.\n\n");
        printf("--apple\t\tSet the vendor, product ID and revision fields to simulate an \n");
        printf("\t\tapple-suppled disk. Provides support for the Apple Drive Setup\n");
        printf("\t\tutility.\n\n");
@@ -250,6 +259,9 @@ int main(int argc, char* argv[])
                {
                        "rev", required_argument, NULL, PARAM_REV
                },
+               {
+                       "sector", required_argument, NULL, PARAM_BYTESPERSECTOR
+               },
                {
                        NULL, 0, NULL, 0
                }
@@ -295,11 +307,11 @@ int main(int argc, char* argv[])
 
                case PARAM_MAXBLOCKS:
                {
-                       int64_t maxBlocks = -1;
-                       if (sscanf(optarg, "%" PRId64, &maxBlocks) == 1 &&
-                               maxBlocks >= 0 && maxBlocks <= UINT32_MAX)
+                       int64_t maxSectors = -1;
+                       if (sscanf(optarg, "%" PRId64, &maxSectors) == 1 &&
+                               maxSectors >= 0 && maxSectors <= UINT32_MAX)
                        {
-                               packet.maxBlocks = maxBlocks;
+                               packet.maxSectors = maxSectors;
                        }
                        else
                        {
@@ -329,6 +341,20 @@ int main(int argc, char* argv[])
                        memcpy(packet.revision, optarg, MIN(strlen(optarg), 4));
                        break;
 
+               case PARAM_BYTESPERSECTOR:
+               {
+                       int64_t bytesPerSector = -1;
+                       if (sscanf(optarg, "%" PRId64, &bytesPerSector) == 1 &&
+                               bytesPerSector >= 64 && bytesPerSector <= 2048)
+                       {
+                               packet.bytesPerSector = bytesPerSector;
+                       }
+                       else
+                       {
+                               usage();
+                       }
+                       break;
+               }
                case '?':
                        usage();
                }