Added configurable block-size support

This commit is contained in:
Michael McMaster 2014-03-28 00:24:33 +10:00
parent 7231a4e210
commit 0bb5ed8349
13 changed files with 399 additions and 101 deletions

View File

@ -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

View File

@ -27,7 +27,7 @@
#include <string.h>
// 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();
}

View File

@ -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

View File

@ -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();

View File

@ -33,9 +33,6 @@ typedef enum
typedef struct
{
uint32 bs; // Block size.
uint32 capacity; // In blocks.
int state;
} BlockDevice;

View File

@ -20,6 +20,22 @@
#include <string.h>
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, &sector);
bytes = sector * SCSI_SECTOR_SIZE + byteOffset;
bytes = sector * config->bytesPerSector + byteOffset;
buf[0] = cyl >> 16;
buf[1] = cyl >> 8;

View File

@ -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);

View File

@ -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, &sector);
LBA2CHS(getScsiCapacity(), &cyl, &head, &sector);
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
{

View File

@ -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()

View File

@ -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;

View File

@ -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();

View File

@ -17,6 +17,8 @@
#ifndef SD_H
#define SD_H
#define SD_SECTOR_SIZE 512
typedef enum
{
SD_GO_IDLE_STATE = 0,

View 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();
}