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