- 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
#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
" 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
((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
((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
\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
// 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
// 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
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
}\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
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
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
\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
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
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
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
\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
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
\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
\r
void scsiDiskInit()\r
{\r
- blockDev.bs = SCSI_BLOCK_SIZE;\r
- blockDev.capacity = 0;\r
transfer.inProgress = 0;\r
scsiDiskReset();\r
\r
typedef struct
{
- uint32 bs; // Block size.
- uint32 capacity; // In blocks.
-
int state;
} BlockDevice;
\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
(((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
(((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
(((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
\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
\r
LBA2CHS(lba, &cyl, &head, §or);\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
#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
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);
\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
\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
else\r
{\r
int pageFound = 1;\r
- \r
+\r
////////////// Mode Parameter Header\r
////////////////////////////////////\r
\r
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
\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
uint32 cyl;\r
uint8 head;\r
uint32 sector;\r
- LBA2CHS(blockDev.capacity, &cyl, &head, §or);\r
+ LBA2CHS(getScsiCapacity(), &cyl, &head, §or);\r
\r
scsiDev.data[idx+2] = cyl >> 16;\r
scsiDev.data[idx+3] = cyl >> 8;\r
}\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
{\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
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
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
\r
transfer.blocks = 0;\r
transfer.currentBlock = 0;\r
+\r
+ scsiDev.postDataOutHook = NULL;\r
}\r
\r
static void process_SelectionPhase()\r
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;
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;
uint8 msgIn;
uint8 msgOut;
+ void (*postDataOutHook)(void);
+
#ifdef MM_DEBUG
uint8 cmdCount;
uint8 selCount;
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
}\r
}\r
\r
-static void doReadSector()\r
+static void doReadSector(uint32_t numBytes)\r
{\r
int prep, i, guard;\r
\r
// 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
// 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
// "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
\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
}\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
} 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
// 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
++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
}\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
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
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
\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
\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
#ifndef SD_H
#define SD_H
+#define SD_SECTOR_SIZE 512
+
typedef enum
{
SD_GO_IDLE_STATE = 0,
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.
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];
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));
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
{
}
memcpy(packet, buf, result);
- packet->maxBlocks = ntohl(packet->maxBlocks);
+ packet->maxSectors = ntohl(packet->maxSectors);
+ packet->bytesPerSector = ntohs(packet->bytesPerSector);
return result;
}
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));
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");
{
"rev", required_argument, NULL, PARAM_REV
},
+ {
+ "sector", required_argument, NULL, PARAM_BYTESPERSECTOR
+ },
{
NULL, 0, NULL, 0
}
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
{
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();
}