mirror of
https://github.com/fhgwright/SCSI2SD.git
synced 2024-12-31 23:31:20 +00:00
Added configurable block-size support
This commit is contained in:
parent
7231a4e210
commit
0bb5ed8349
@ -5,6 +5,7 @@
|
|||||||
- scsi2sd-config can be used to disable it for those systems that
|
- scsi2sd-config can be used to disable it for those systems that
|
||||||
truely require it (eg. Mac Plus).
|
truely require it (eg. Mac Plus).
|
||||||
- Added Linked commands support.
|
- Added Linked commands support.
|
||||||
|
- Added support for configurable sector sizes between 64 and 2048 bytes.
|
||||||
- Powerbook firmware added
|
- Powerbook firmware added
|
||||||
|
|
||||||
20140214 3.2
|
20140214 3.2
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// CYDEV_EEPROM_ROW_SIZE == 16.
|
// 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)
|
// Config shadow RAM (copy of EEPROM)
|
||||||
static Config shadow =
|
static Config shadow =
|
||||||
@ -38,7 +38,9 @@ static Config shadow =
|
|||||||
" 3.3", // revision (68k Apple Drive Setup: Set to "1.0 ")
|
" 3.3", // revision (68k Apple Drive Setup: Set to "1.0 ")
|
||||||
1, // enable parity
|
1, // enable parity
|
||||||
1, // enable unit attention,
|
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.
|
// reserved bytes will be initialised to 0.
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,6 +70,12 @@ static uint32_t ntohl(uint32_t val)
|
|||||||
((val >> 8) & 0xFF00) |
|
((val >> 8) & 0xFF00) |
|
||||||
((val >> 24) & 0xFF);
|
((val >> 24) & 0xFF);
|
||||||
}
|
}
|
||||||
|
static uint16_t ntohs(uint16_t val)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
((val & 0xFF) << 8) |
|
||||||
|
((val >> 8) & 0xFF);
|
||||||
|
}
|
||||||
static uint32_t htonl(uint32_t val)
|
static uint32_t htonl(uint32_t val)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@ -76,6 +84,12 @@ static uint32_t htonl(uint32_t val)
|
|||||||
((val >> 8) & 0xFF00) |
|
((val >> 8) & 0xFF00) |
|
||||||
((val >> 24) & 0xFF);
|
((val >> 24) & 0xFF);
|
||||||
}
|
}
|
||||||
|
static uint16_t htons(uint16_t val)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
((val & 0xFF) << 8) |
|
||||||
|
((val >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
static void saveConfig()
|
static void saveConfig()
|
||||||
{
|
{
|
||||||
@ -111,6 +125,15 @@ void configInit()
|
|||||||
|
|
||||||
if (memcmp(eeprom + shadowBytes, magic, sizeof(magic)))
|
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();
|
saveConfig();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -161,7 +184,17 @@ void configPoll()
|
|||||||
// shadow should be padded out to 64bytes, which is the largest
|
// shadow should be padded out to 64bytes, which is the largest
|
||||||
// possible HID transfer.
|
// possible HID transfer.
|
||||||
USBFS_ReadOutEP(USB_EP_OUT, (uint8 *)&shadow, byteCount);
|
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();
|
CFG_EEPROM_Start();
|
||||||
saveConfig(); // write to eeprom
|
saveConfig(); // write to eeprom
|
||||||
@ -173,14 +206,18 @@ void configPoll()
|
|||||||
// Allow the host to send us another updated config.
|
// Allow the host to send us another updated config.
|
||||||
USBFS_EnableOutEP(USB_EP_OUT);
|
USBFS_EnableOutEP(USB_EP_OUT);
|
||||||
|
|
||||||
|
// Set unt attention as the block size may have changed.
|
||||||
|
scsiDev.unitAttention = MODE_PARAMETERS_CHANGED;
|
||||||
|
|
||||||
ledOff();
|
ledOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (usbInEpState)
|
switch (usbInEpState)
|
||||||
{
|
{
|
||||||
case USB_IDLE:
|
case USB_IDLE:
|
||||||
shadow.maxBlocks = htonl(shadow.maxBlocks);
|
shadow.maxSectors = htonl(shadow.maxSectors);
|
||||||
|
shadow.bytesPerSector = htons(shadow.bytesPerSector);
|
||||||
|
|
||||||
#ifdef MM_DEBUG
|
#ifdef MM_DEBUG
|
||||||
memcpy(&shadow.reserved, &scsiDev.cdb, 12);
|
memcpy(&shadow.reserved, &scsiDev.cdb, 12);
|
||||||
shadow.reserved[12] = scsiDev.msgIn;
|
shadow.reserved[12] = scsiDev.msgIn;
|
||||||
@ -197,12 +234,11 @@ void configPoll()
|
|||||||
shadow.reserved[23] = scsiDev.msgCount;
|
shadow.reserved[23] = scsiDev.msgCount;
|
||||||
shadow.reserved[24] = scsiDev.cmdCount;
|
shadow.reserved[24] = scsiDev.cmdCount;
|
||||||
shadow.reserved[25] = scsiDev.watchdogTick;
|
shadow.reserved[25] = scsiDev.watchdogTick;
|
||||||
shadow.reserved[26] = blockDev.state;
|
|
||||||
shadow.reserved[27] = scsiReadDBxPins();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
USBFS_LoadInEP(USB_EP_IN, (uint8 *)&shadow, sizeof(shadow));
|
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;
|
usbInEpState = USB_DATA_SENT;
|
||||||
break;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -28,15 +28,17 @@ typedef struct
|
|||||||
uint8 enableParity;
|
uint8 enableParity;
|
||||||
uint8 enableUnitAttention;
|
uint8 enableUnitAttention;
|
||||||
uint8 reserved1; // Unused. Ensures maxBlocks is aligned.
|
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.
|
// Pad to 64 bytes, which is what we can fit into a USB HID packet.
|
||||||
char reserved[28];
|
char reserved[26];
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
extern Config* config;
|
extern Config* config;
|
||||||
|
|
||||||
void configInit(void);
|
void configInit(void);
|
||||||
void configPoll(void);
|
void configPoll(void);
|
||||||
|
void configSave(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,11 +34,6 @@ static int doSdInit()
|
|||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
blockDev.state = blockDev.state | DISK_INITIALISED;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
@ -52,12 +47,14 @@ static void doFormatUnit()
|
|||||||
|
|
||||||
static void doReadCapacity()
|
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[3]) << 16) +
|
||||||
(((uint32) scsiDev.cdb[4]) << 8) +
|
(((uint32) scsiDev.cdb[4]) << 8) +
|
||||||
scsiDev.cdb[5];
|
scsiDev.cdb[5];
|
||||||
int pmi = scsiDev.cdb[8] & 1;
|
int pmi = scsiDev.cdb[8] & 1;
|
||||||
|
|
||||||
|
uint32_t capacity = getScsiCapacity();
|
||||||
|
|
||||||
if (!pmi && lba)
|
if (!pmi && lba)
|
||||||
{
|
{
|
||||||
// error.
|
// error.
|
||||||
@ -69,19 +66,19 @@ static void doReadCapacity()
|
|||||||
scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
|
scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
|
||||||
scsiDev.phase = STATUS;
|
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[0] = highestBlock >> 24;
|
||||||
scsiDev.data[1] = highestBlock >> 16;
|
scsiDev.data[1] = highestBlock >> 16;
|
||||||
scsiDev.data[2] = highestBlock >> 8;
|
scsiDev.data[2] = highestBlock >> 8;
|
||||||
scsiDev.data[3] = highestBlock;
|
scsiDev.data[3] = highestBlock;
|
||||||
|
|
||||||
scsiDev.data[4] = blockDev.bs >> 24;
|
scsiDev.data[4] = config->bytesPerSector >> 24;
|
||||||
scsiDev.data[5] = blockDev.bs >> 16;
|
scsiDev.data[5] = config->bytesPerSector >> 16;
|
||||||
scsiDev.data[6] = blockDev.bs >> 8;
|
scsiDev.data[6] = config->bytesPerSector >> 8;
|
||||||
scsiDev.data[7] = blockDev.bs;
|
scsiDev.data[7] = config->bytesPerSector;
|
||||||
scsiDev.dataLen = 8;
|
scsiDev.dataLen = 8;
|
||||||
scsiDev.phase = DATA_IN;
|
scsiDev.phase = DATA_IN;
|
||||||
}
|
}
|
||||||
@ -103,7 +100,7 @@ static void doWrite(uint32 lba, uint32 blocks)
|
|||||||
scsiDev.sense.asc = WRITE_PROTECTED;
|
scsiDev.sense.asc = WRITE_PROTECTED;
|
||||||
scsiDev.phase = STATUS;
|
scsiDev.phase = STATUS;
|
||||||
}
|
}
|
||||||
else if (((uint64) lba) + blocks > blockDev.capacity)
|
else if (((uint64) lba) + blocks > getScsiCapacity())
|
||||||
{
|
{
|
||||||
scsiDev.status = CHECK_CONDITION;
|
scsiDev.status = CHECK_CONDITION;
|
||||||
scsiDev.sense.code = ILLEGAL_REQUEST;
|
scsiDev.sense.code = ILLEGAL_REQUEST;
|
||||||
@ -117,11 +114,11 @@ static void doWrite(uint32 lba, uint32 blocks)
|
|||||||
transfer.blocks = blocks;
|
transfer.blocks = blocks;
|
||||||
transfer.currentBlock = 0;
|
transfer.currentBlock = 0;
|
||||||
scsiDev.phase = DATA_OUT;
|
scsiDev.phase = DATA_OUT;
|
||||||
scsiDev.dataLen = SCSI_BLOCK_SIZE;
|
scsiDev.dataLen = config->bytesPerSector;
|
||||||
scsiDev.dataPtr = SCSI_BLOCK_SIZE; // TODO FIX scsiDiskPoll()
|
scsiDev.dataPtr = config->bytesPerSector; // TODO FIX scsiDiskPoll()
|
||||||
|
|
||||||
// No need for single-block reads atm. Overhead of the
|
// No need for single-block writes atm. Overhead of the
|
||||||
// multi-block read is minimal.
|
// multi-block write is minimal.
|
||||||
transfer.multiBlock = 1;
|
transfer.multiBlock = 1;
|
||||||
sdPrepareWrite();
|
sdPrepareWrite();
|
||||||
}
|
}
|
||||||
@ -130,7 +127,8 @@ static void doWrite(uint32 lba, uint32 blocks)
|
|||||||
|
|
||||||
static void doRead(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.status = CHECK_CONDITION;
|
||||||
scsiDev.sense.code = ILLEGAL_REQUEST;
|
scsiDev.sense.code = ILLEGAL_REQUEST;
|
||||||
@ -147,7 +145,7 @@ static void doRead(uint32 lba, uint32 blocks)
|
|||||||
scsiDev.dataLen = 0; // No data yet
|
scsiDev.dataLen = 0; // No data yet
|
||||||
|
|
||||||
if ((blocks == 1) ||
|
if ((blocks == 1) ||
|
||||||
(((uint64) lba) + blocks == blockDev.capacity)
|
(((uint64) lba) + blocks == capacity)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// We get errors on reading the last sector using a multi-sector
|
// 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)
|
static void doSeek(uint32 lba)
|
||||||
{
|
{
|
||||||
if (lba >= blockDev.capacity)
|
if (lba >= getScsiCapacity())
|
||||||
{
|
{
|
||||||
scsiDev.status = CHECK_CONDITION;
|
scsiDev.status = CHECK_CONDITION;
|
||||||
scsiDev.sense.code = ILLEGAL_REQUEST;
|
scsiDev.sense.code = ILLEGAL_REQUEST;
|
||||||
@ -455,8 +453,6 @@ void scsiDiskReset()
|
|||||||
|
|
||||||
void scsiDiskInit()
|
void scsiDiskInit()
|
||||||
{
|
{
|
||||||
blockDev.bs = SCSI_BLOCK_SIZE;
|
|
||||||
blockDev.capacity = 0;
|
|
||||||
transfer.inProgress = 0;
|
transfer.inProgress = 0;
|
||||||
scsiDiskReset();
|
scsiDiskReset();
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ typedef enum
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32 bs; // Block size.
|
|
||||||
uint32 capacity; // In blocks.
|
|
||||||
|
|
||||||
int state;
|
int state;
|
||||||
} BlockDevice;
|
} BlockDevice;
|
||||||
|
|
||||||
|
@ -20,6 +20,22 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#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
|
// Standard mapping according to ECMA-107 and ISO/IEC 9293:1994
|
||||||
// Sector always starts at 1. There is no 0 sector.
|
// Sector always starts at 1. There is no 0 sector.
|
||||||
uint64 CHS2LBA(uint32 c, uint8 h, uint32 s)
|
uint64 CHS2LBA(uint32 c, uint8 h, uint32 s)
|
||||||
@ -51,7 +67,7 @@ uint64 scsiByteAddress(int format, const uint8* addr)
|
|||||||
(((uint32) addr[2]) << 8) +
|
(((uint32) addr[2]) << 8) +
|
||||||
addr[3];
|
addr[3];
|
||||||
|
|
||||||
result = (uint64) SCSI_BLOCK_SIZE * lba;
|
result = (uint64_t) config->bytesPerSector * lba;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ADDRESS_PHYSICAL_BYTE:
|
case ADDRESS_PHYSICAL_BYTE:
|
||||||
@ -69,7 +85,7 @@ uint64 scsiByteAddress(int format, const uint8* addr)
|
|||||||
(((uint32) addr[6]) << 8) +
|
(((uint32) addr[6]) << 8) +
|
||||||
addr[7];
|
addr[7];
|
||||||
|
|
||||||
result = CHS2LBA(cyl, head, 1) * (uint64) SCSI_SECTOR_SIZE + bytes;
|
result = CHS2LBA(cyl, head, 1) * (uint64_t) config->bytesPerSector + bytes;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ADDRESS_PHYSICAL_SECTOR:
|
case ADDRESS_PHYSICAL_SECTOR:
|
||||||
@ -87,7 +103,7 @@ uint64 scsiByteAddress(int format, const uint8* addr)
|
|||||||
(((uint32) addr[6]) << 8) +
|
(((uint32) addr[6]) << 8) +
|
||||||
addr[7];
|
addr[7];
|
||||||
|
|
||||||
result = CHS2LBA(cyl, head, sector) * (uint64) SCSI_SECTOR_SIZE;
|
result = CHS2LBA(cyl, head, sector) * (uint64_t) config->bytesPerSector;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -100,8 +116,8 @@ uint64 scsiByteAddress(int format, const uint8* addr)
|
|||||||
|
|
||||||
void scsiSaveByteAddress(int format, uint64 byteAddr, uint8* buf)
|
void scsiSaveByteAddress(int format, uint64 byteAddr, uint8* buf)
|
||||||
{
|
{
|
||||||
uint32 lba = byteAddr / SCSI_BLOCK_SIZE;
|
uint32 lba = byteAddr / config->bytesPerSector;
|
||||||
uint32 byteOffset = byteAddr % SCSI_BLOCK_SIZE;
|
uint32 byteOffset = byteAddr % config->bytesPerSector;
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
@ -127,7 +143,7 @@ void scsiSaveByteAddress(int format, uint64 byteAddr, uint8* buf)
|
|||||||
|
|
||||||
LBA2CHS(lba, &cyl, &head, §or);
|
LBA2CHS(lba, &cyl, &head, §or);
|
||||||
|
|
||||||
bytes = sector * SCSI_SECTOR_SIZE + byteOffset;
|
bytes = sector * config->bytesPerSector + byteOffset;
|
||||||
|
|
||||||
buf[0] = cyl >> 16;
|
buf[0] = cyl >> 16;
|
||||||
buf[1] = cyl >> 8;
|
buf[1] = cyl >> 8;
|
||||||
|
@ -19,10 +19,8 @@
|
|||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
|
||||||
// We make some assumptions that the block size and sector size
|
#include "config.h"
|
||||||
// are always equal.
|
#include "sd.h"
|
||||||
#define SCSI_BLOCK_SIZE 512
|
|
||||||
#define SCSI_SECTOR_SIZE 512
|
|
||||||
|
|
||||||
// Max allowed by legacy IBM-PC Bios (6 bits)
|
// Max allowed by legacy IBM-PC Bios (6 bits)
|
||||||
#define SCSI_SECTORS_PER_TRACK 63
|
#define SCSI_SECTORS_PER_TRACK 63
|
||||||
@ -37,6 +35,14 @@ typedef enum
|
|||||||
ADDRESS_PHYSICAL_SECTOR = 5
|
ADDRESS_PHYSICAL_SECTOR = 5
|
||||||
} SCSI_ADDRESS_FORMAT;
|
} 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);
|
uint64 CHS2LBA(uint32 c, uint8 h, uint32 s);
|
||||||
void LBA2CHS(uint32 lba, uint32* c, uint8* h, uint32* s);
|
void LBA2CHS(uint32 lba, uint32* c, uint8* h, uint32* s);
|
||||||
|
@ -54,14 +54,14 @@ static const uint8 DisconnectReconnectPage[] =
|
|||||||
|
|
||||||
static const uint8 FormatDevicePage[] =
|
static const uint8 FormatDevicePage[] =
|
||||||
{
|
{
|
||||||
0x03, // Page code
|
0x03 | 0x80, // Page code | PS (persist) bit.
|
||||||
0x16, // Page length
|
0x16, // Page length
|
||||||
0x00, 0x00, // Single zone
|
0x00, 0x00, // Single zone
|
||||||
0x00, 0x00, // No alternate sectors
|
0x00, 0x00, // No alternate sectors
|
||||||
0x00, 0x00, // No alternate tracks
|
0x00, 0x00, // No alternate tracks
|
||||||
0x00, 0x00, // No alternate tracks per lun
|
0x00, 0x00, // No alternate tracks per lun
|
||||||
0x00, SCSI_SECTORS_PER_TRACK, // Sectors per track
|
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, 0x01, // Interleave
|
||||||
0x00, 0x00, // Track skew factor
|
0x00, 0x00, // Track skew factor
|
||||||
0x00, 0x00, // Cylinder skew factor
|
0x00, 0x00, // Cylinder skew factor
|
||||||
@ -111,7 +111,6 @@ static const uint8 ControlModePage[] =
|
|||||||
|
|
||||||
// Allow Apple 68k Drive Setup to format this drive.
|
// Allow Apple 68k Drive Setup to format this drive.
|
||||||
// Code
|
// Code
|
||||||
// TODO make this string configurable.
|
|
||||||
static const uint8 AppleVendorPage[] =
|
static const uint8 AppleVendorPage[] =
|
||||||
{
|
{
|
||||||
0x30, // Page code
|
0x30, // Page code
|
||||||
@ -143,7 +142,7 @@ static void doModeSense(
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int pageFound = 1;
|
int pageFound = 1;
|
||||||
|
|
||||||
////////////// Mode Parameter Header
|
////////////// Mode Parameter Header
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
@ -201,9 +200,9 @@ static void doModeSense(
|
|||||||
scsiDev.data[idx++] = 0; // reserved
|
scsiDev.data[idx++] = 0; // reserved
|
||||||
|
|
||||||
// Block length
|
// Block length
|
||||||
scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 16;
|
scsiDev.data[idx++] = config->bytesPerSector >> 16;
|
||||||
scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 8;
|
scsiDev.data[idx++] = config->bytesPerSector >> 8;
|
||||||
scsiDev.data[idx++] = SCSI_BLOCK_SIZE & 0xFF;
|
scsiDev.data[idx++] = config->bytesPerSector & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (pageCode)
|
switch (pageCode)
|
||||||
@ -223,6 +222,19 @@ static void doModeSense(
|
|||||||
|
|
||||||
case 0x03:
|
case 0x03:
|
||||||
pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));
|
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);
|
idx += sizeof(FormatDevicePage);
|
||||||
if (pageCode != 0x3f) break;
|
if (pageCode != 0x3f) break;
|
||||||
|
|
||||||
@ -236,7 +248,7 @@ static void doModeSense(
|
|||||||
uint32 cyl;
|
uint32 cyl;
|
||||||
uint8 head;
|
uint8 head;
|
||||||
uint32 sector;
|
uint32 sector;
|
||||||
LBA2CHS(blockDev.capacity, &cyl, &head, §or);
|
LBA2CHS(getScsiCapacity(), &cyl, &head, §or);
|
||||||
|
|
||||||
scsiDev.data[idx+2] = cyl >> 16;
|
scsiDev.data[idx+2] = cyl >> 16;
|
||||||
scsiDev.data[idx+3] = cyl >> 8;
|
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 scsiModeCommand()
|
||||||
{
|
{
|
||||||
int commandHandled = 1;
|
int commandHandled = 1;
|
||||||
@ -343,16 +423,39 @@ int scsiModeCommand()
|
|||||||
{
|
{
|
||||||
// MODE SELECT(6)
|
// MODE SELECT(6)
|
||||||
int len = scsiDev.cdb[4];
|
int len = scsiDev.cdb[4];
|
||||||
if (len == 0) len = 256;
|
if (len == 0)
|
||||||
scsiDev.dataLen = len;
|
{
|
||||||
scsiDev.phase = DATA_OUT;
|
// 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)
|
else if (command == 0x55)
|
||||||
{
|
{
|
||||||
// MODE SELECT(10)
|
// MODE SELECT(10)
|
||||||
int allocLength = (((uint16) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];
|
int allocLength = (((uint16) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];
|
||||||
scsiDev.dataLen = allocLength;
|
if (allocLength == 0)
|
||||||
scsiDev.phase = DATA_OUT;
|
{
|
||||||
|
// 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
|
else
|
||||||
{
|
{
|
||||||
|
@ -178,7 +178,14 @@ static void process_DataIn()
|
|||||||
if ((scsiDev.dataPtr >= scsiDev.dataLen) &&
|
if ((scsiDev.dataPtr >= scsiDev.dataLen) &&
|
||||||
(transfer.currentBlock == transfer.blocks))
|
(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;
|
scsiDev.sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
|
||||||
scsiDiskReset();
|
scsiDiskReset();
|
||||||
|
|
||||||
|
scsiDev.postDataOutHook = NULL;
|
||||||
|
|
||||||
// Sleep to allow the bus to settle down a bit.
|
// Sleep to allow the bus to settle down a bit.
|
||||||
// We must be ready again within the "Reset to selection time" of
|
// We must be ready again within the "Reset to selection time" of
|
||||||
// 250ms.
|
// 250ms.
|
||||||
@ -463,6 +472,8 @@ static void enter_SelectionPhase()
|
|||||||
|
|
||||||
transfer.blocks = 0;
|
transfer.blocks = 0;
|
||||||
transfer.currentBlock = 0;
|
transfer.currentBlock = 0;
|
||||||
|
|
||||||
|
scsiDev.postDataOutHook = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_SelectionPhase()
|
static void process_SelectionPhase()
|
||||||
|
@ -62,6 +62,10 @@ typedef enum
|
|||||||
MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG = 0x0B
|
MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG = 0x0B
|
||||||
} SCSI_MESSAGE;
|
} SCSI_MESSAGE;
|
||||||
|
|
||||||
|
// Maximum value for bytes-per-sector.
|
||||||
|
#define MAX_SECTOR_SIZE 2048
|
||||||
|
#define MIN_SECTOR_SIZE 64
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t scsiIdMask;
|
uint8_t scsiIdMask;
|
||||||
@ -78,7 +82,7 @@ typedef struct
|
|||||||
|
|
||||||
int phase;
|
int phase;
|
||||||
|
|
||||||
uint8 data[SCSI_BLOCK_SIZE];
|
uint8 data[MAX_SECTOR_SIZE];
|
||||||
int dataPtr; // Index into data, reset on [re]selection to savedDataPtr
|
int dataPtr; // Index into data, reset on [re]selection to savedDataPtr
|
||||||
int savedDataPtr; // Index into data, initially 0.
|
int savedDataPtr; // Index into data, initially 0.
|
||||||
int dataLen;
|
int dataLen;
|
||||||
@ -103,6 +107,8 @@ typedef struct
|
|||||||
uint8 msgIn;
|
uint8 msgIn;
|
||||||
uint8 msgOut;
|
uint8 msgOut;
|
||||||
|
|
||||||
|
void (*postDataOutHook)(void);
|
||||||
|
|
||||||
#ifdef MM_DEBUG
|
#ifdef MM_DEBUG
|
||||||
uint8 cmdCount;
|
uint8 cmdCount;
|
||||||
uint8 selCount;
|
uint8 selCount;
|
||||||
|
@ -129,12 +129,14 @@ static void sdClearStatus()
|
|||||||
void sdPrepareRead()
|
void sdPrepareRead()
|
||||||
{
|
{
|
||||||
uint8 v;
|
uint8 v;
|
||||||
uint32 len = (transfer.lba + transfer.currentBlock);
|
uint32 scsiLBA = (transfer.lba + transfer.currentBlock);
|
||||||
|
uint32 sdLBA = SCSISector2SD(scsiLBA);
|
||||||
|
|
||||||
if (!sdDev.ccs)
|
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)
|
if (v)
|
||||||
{
|
{
|
||||||
scsiDiskReset();
|
scsiDiskReset();
|
||||||
@ -151,7 +153,7 @@ void sdPrepareRead()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void doReadSector()
|
static void doReadSector(uint32_t numBytes)
|
||||||
{
|
{
|
||||||
int prep, i, guard;
|
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.
|
// Don't do a bus settle delay if we're already in the correct phase.
|
||||||
if (transfer.currentBlock == 0)
|
if (transfer.currentBlock == 0)
|
||||||
{
|
{
|
||||||
//scsiEnterPhase(DATA_OUT);
|
|
||||||
//CyDelayUs(200);
|
|
||||||
scsiEnterPhase(DATA_IN);
|
scsiEnterPhase(DATA_IN);
|
||||||
//CyDelayUs(200); // TODO BLOODY SLOW INTERLEAVE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quickly seed the FIFO
|
// Quickly seed the FIFO
|
||||||
prep = 4;
|
prep = 4;
|
||||||
CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO
|
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
|
// 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,
|
// FIFO's. If the loop isn't fast enough, the transmit FIFO's will empty,
|
||||||
// and performance will suffer. Every clock cycle counts.
|
// 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 sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);
|
||||||
uint8_t scsiStatus = CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG);
|
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.
|
// "combined" SPIM TX and RX FIFOS to the individual FIFO size.
|
||||||
// Unlike the SCSI component, SPIM doesn't check if there's room in
|
// Unlike the SCSI component, SPIM doesn't check if there's room in
|
||||||
// the output FIFO before starting to transmit.
|
// 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
|
CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO
|
||||||
prep++;
|
prep++;
|
||||||
@ -241,21 +258,20 @@ static void doReadSector()
|
|||||||
|
|
||||||
sdSpiByte(0xFF); // CRC
|
sdSpiByte(0xFF); // CRC
|
||||||
sdSpiByte(0xFF); // CRC
|
sdSpiByte(0xFF); // CRC
|
||||||
scsiDev.dataLen = SCSI_BLOCK_SIZE;
|
scsiDev.dataLen = numBytes;
|
||||||
scsiDev.dataPtr = SCSI_BLOCK_SIZE;
|
scsiDev.dataPtr = numBytes;
|
||||||
|
|
||||||
while (SCSI_ReadPin(SCSI_In_ACK) && !scsiDev.resetFlag) {}
|
while (SCSI_ReadPin(SCSI_In_ACK) && !scsiDev.resetFlag) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sdReadSectorSingle()
|
static void doReadSectorSingle(uint32 sdBlock, int sdBytes)
|
||||||
{
|
{
|
||||||
uint8 v;
|
uint8 v;
|
||||||
uint32 len = (transfer.lba + transfer.currentBlock);
|
|
||||||
if (!sdDev.ccs)
|
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)
|
if (v)
|
||||||
{
|
{
|
||||||
scsiDiskReset();
|
scsiDiskReset();
|
||||||
@ -268,15 +284,47 @@ void sdReadSectorSingle()
|
|||||||
}
|
}
|
||||||
else
|
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()
|
void sdReadSectorMulti()
|
||||||
{
|
{
|
||||||
// Pre: sdPrepareRead called.
|
// Pre: sdPrepareRead called.
|
||||||
|
int sdSectors = SDSectorsPerSCSISector();
|
||||||
doReadSector();
|
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);
|
} while (val != 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sdWriteSector()
|
static int doWriteSector(uint32_t numBytes)
|
||||||
{
|
{
|
||||||
int prep, i, guard;
|
int prep, i, guard;
|
||||||
int result, maxWait;
|
int result, maxWait;
|
||||||
@ -351,7 +399,7 @@ int sdWriteSector()
|
|||||||
// We stream data straight from the SCSI fifos into the SPIM component
|
// 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,
|
// FIFO's. If the loop isn't fast enough, the transmit FIFO's will empty,
|
||||||
// and performance will suffer. Every clock cycle counts.
|
// 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 sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);
|
||||||
uint8_t scsiStatus = CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG);
|
uint8_t scsiStatus = CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG);
|
||||||
@ -375,7 +423,7 @@ int sdWriteSector()
|
|||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prep < SCSI_BLOCK_SIZE &&
|
if (prep < numBytes &&
|
||||||
(scsiDev.resetFlag || (scsiStatus & 1)) // SCSI TX FIFO NOT FULL
|
(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
|
||||||
sdSpiByte(0x00); // CRC
|
sdSpiByte(0x00); // CRC
|
||||||
|
|
||||||
@ -438,6 +505,26 @@ int sdWriteSector()
|
|||||||
return result;
|
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()
|
void sdCompleteWrite()
|
||||||
{
|
{
|
||||||
transfer.inProgress = 0;
|
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_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 c_mult = (((((uint32)buf[9]) & 0x3) << 8) | ((uint32)buf[0xa])) >> 7;
|
||||||
uint32 sectorSize = buf[5] & 0x0F;
|
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)
|
else if ((buf[0] >> 6) == 0x01)
|
||||||
{
|
{
|
||||||
@ -622,7 +709,7 @@ int sdInit()
|
|||||||
|
|
||||||
// This command will be ignored if sdDev.ccs is set.
|
// This command will be ignored if sdDev.ccs is set.
|
||||||
// SDHC and SDXC are always 512bytes.
|
// 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;}
|
if(v){goto bad;}
|
||||||
v = sdCRCCommandAndResponse(SD_CRC_ON_OFF, 0); //crc off
|
v = sdCRCCommandAndResponse(SD_CRC_ON_OFF, 0); //crc off
|
||||||
if(v){goto bad;}
|
if(v){goto bad;}
|
||||||
@ -671,23 +758,24 @@ out:
|
|||||||
|
|
||||||
void sdPrepareWrite()
|
void sdPrepareWrite()
|
||||||
{
|
{
|
||||||
uint32 len;
|
|
||||||
uint8 v;
|
uint8 v;
|
||||||
|
|
||||||
// Set the number of blocks to pre-erase by the multiple block write command
|
// 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
|
// We don't care about the response - if the command is not accepted, writes
|
||||||
// will just be a bit slower.
|
// will just be a bit slower.
|
||||||
// Max 22bit parameter.
|
// 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_CMD, 0);
|
||||||
sdCommandAndResponse(SD_APP_SET_WR_BLK_ERASE_COUNT, blocks);
|
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)
|
if (!sdDev.ccs)
|
||||||
{
|
{
|
||||||
len = len * SCSI_BLOCK_SIZE;
|
sdLBA = sdLBA * SD_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
v = sdCommandAndResponse(25, len);
|
v = sdCommandAndResponse(25, sdLBA);
|
||||||
if (v)
|
if (v)
|
||||||
{
|
{
|
||||||
scsiDiskReset();
|
scsiDiskReset();
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
#ifndef SD_H
|
#ifndef SD_H
|
||||||
#define SD_H
|
#define SD_H
|
||||||
|
|
||||||
|
#define SD_SECTOR_SIZE 512
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
SD_GO_IDLE_STATE = 0,
|
SD_GO_IDLE_STATE = 0,
|
||||||
|
@ -45,7 +45,8 @@ enum
|
|||||||
PARAM_APPLE,
|
PARAM_APPLE,
|
||||||
PARAM_VENDOR,
|
PARAM_VENDOR,
|
||||||
PARAM_PRODID,
|
PARAM_PRODID,
|
||||||
PARAM_REV
|
PARAM_REV,
|
||||||
|
PARAM_BYTESPERSECTOR
|
||||||
};
|
};
|
||||||
|
|
||||||
// Must be consistent with the structure defined in the SCSI2SD config.h header.
|
// 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 enableParity;
|
||||||
uint8_t enableUnitAttention;
|
uint8_t enableUnitAttention;
|
||||||
uint8_t reserved1; // Unused. Ensures maxBlocks is aligned.
|
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.
|
// Pad to 64 bytes, which is what we can fit into a USB HID packet.
|
||||||
char reserved[28];
|
char reserved[28];
|
||||||
@ -74,10 +77,11 @@ static void printConfig(ConfigPacket* packet)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Parity Checking:\t\t%s\n", packet->enableParity ? "enabled" : "disabled");
|
printf("Parity Checking:\t\t%s\n", packet->enableParity ? "enabled" : "disabled");
|
||||||
printf("Unit Attention Condition:\t%s\n", packet->enableUnitAttention ? "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];
|
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))
|
if (maxBytes > (1024*1024*1024))
|
||||||
{
|
{
|
||||||
sprintf(sizeBuf, "%.02fGB", maxBytes / (1024.0*1024.0*1024.0));
|
sprintf(sizeBuf, "%.02fGB", maxBytes / (1024.0*1024.0*1024.0));
|
||||||
@ -95,7 +99,7 @@ static void printConfig(ConfigPacket* packet)
|
|||||||
sprintf(sizeBuf, "%" PRIu64 " bytes", maxBytes);
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -117,7 +121,8 @@ static int readConfig(hid_device* handle, ConfigPacket* packet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
memcpy(packet, buf, result);
|
memcpy(packet, buf, result);
|
||||||
packet->maxBlocks = ntohl(packet->maxBlocks);
|
packet->maxSectors = ntohl(packet->maxSectors);
|
||||||
|
packet->bytesPerSector = ntohs(packet->bytesPerSector);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -127,9 +132,11 @@ static int writeConfig(hid_device* handle, ConfigPacket* packet)
|
|||||||
unsigned char buf[1 + sizeof(ConfigPacket)];
|
unsigned char buf[1 + sizeof(ConfigPacket)];
|
||||||
buf[0] = 0; // report ID
|
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));
|
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));
|
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\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\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("\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("--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\tapple-suppled disk. Provides support for the Apple Drive Setup\n");
|
||||||
printf("\t\tutility.\n\n");
|
printf("\t\tutility.\n\n");
|
||||||
@ -250,6 +259,9 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
"rev", required_argument, NULL, PARAM_REV
|
"rev", required_argument, NULL, PARAM_REV
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"sector", required_argument, NULL, PARAM_BYTESPERSECTOR
|
||||||
|
},
|
||||||
{
|
{
|
||||||
NULL, 0, NULL, 0
|
NULL, 0, NULL, 0
|
||||||
}
|
}
|
||||||
@ -295,11 +307,11 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
case PARAM_MAXBLOCKS:
|
case PARAM_MAXBLOCKS:
|
||||||
{
|
{
|
||||||
int64_t maxBlocks = -1;
|
int64_t maxSectors = -1;
|
||||||
if (sscanf(optarg, "%" PRId64, &maxBlocks) == 1 &&
|
if (sscanf(optarg, "%" PRId64, &maxSectors) == 1 &&
|
||||||
maxBlocks >= 0 && maxBlocks <= UINT32_MAX)
|
maxSectors >= 0 && maxSectors <= UINT32_MAX)
|
||||||
{
|
{
|
||||||
packet.maxBlocks = maxBlocks;
|
packet.maxSectors = maxSectors;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -329,6 +341,20 @@ int main(int argc, char* argv[])
|
|||||||
memcpy(packet.revision, optarg, MIN(strlen(optarg), 4));
|
memcpy(packet.revision, optarg, MIN(strlen(optarg), 4));
|
||||||
break;
|
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 '?':
|
case '?':
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user