Przeglądaj źródła

Added configurable block-size support

Michael McMaster 11 lat temu
rodzic
commit
581fe59d48

+ 1 - 0
lib/SCSI2SD/CHANGELOG

@@ -5,6 +5,7 @@
 		- scsi2sd-config can be used to disable it for those systems that
 		truely require it (eg. Mac Plus).
 	- Added Linked commands support.
+	- Added support for configurable sector sizes between 64 and 2048 bytes.
 	- Powerbook firmware added
 
 20140214		3.2

+ 52 - 8
lib/SCSI2SD/software/SCSI2SD/src/config.c

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

+ 4 - 2
lib/SCSI2SD/software/SCSI2SD/src/config.h

@@ -28,15 +28,17 @@ typedef struct
 	uint8 enableParity;
 	uint8 enableUnitAttention;
 	uint8 reserved1; // Unused. Ensures maxBlocks is aligned.
-	uint32 maxBlocks;
+	uint32 maxSectors;
+	uint16 bytesPerSector;
 
 	// Pad to 64 bytes, which is what we can fit into a USB HID packet.
-	char reserved[28]; 
+	char reserved[26]; 
 } Config;
 
 extern Config* config;
 
 void configInit(void);
 void configPoll(void);
+void configSave(void);
 
 #endif

+ 18 - 22
lib/SCSI2SD/software/SCSI2SD/src/disk.c

@@ -34,11 +34,6 @@ static int doSdInit()
 	if (result)
 	{
 		blockDev.state = blockDev.state | DISK_INITIALISED;
-
-		// artificially limit this value according to EEPROM config.
-		blockDev.capacity =
-			(config->maxBlocks && (sdDev.capacity > config->maxBlocks))
-				? config->maxBlocks : sdDev.capacity;
 	}
 	return result;
 }
@@ -52,12 +47,14 @@ static void doFormatUnit()
 
 static void doReadCapacity()
 {
-	uint32 lba = (((uint32) scsiDev.cdb[2]) << 24) +
+	uint32_t lba = (((uint32) scsiDev.cdb[2]) << 24) +
 		(((uint32) scsiDev.cdb[3]) << 16) +
 		(((uint32) scsiDev.cdb[4]) << 8) +
 		scsiDev.cdb[5];
 	int pmi = scsiDev.cdb[8] & 1;
 
+	uint32_t capacity = getScsiCapacity();
+
 	if (!pmi && lba)
 	{
 		// error.
@@ -69,19 +66,19 @@ static void doReadCapacity()
 		scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
 		scsiDev.phase = STATUS;
 	}
-	else if (blockDev.capacity > 0)
+	else if (capacity > 0)
 	{
-		uint32 highestBlock = blockDev.capacity - 1;
+		uint32_t highestBlock = capacity - 1;
 
 		scsiDev.data[0] = highestBlock >> 24;
 		scsiDev.data[1] = highestBlock >> 16;
 		scsiDev.data[2] = highestBlock >> 8;
 		scsiDev.data[3] = highestBlock;
 
-		scsiDev.data[4] = blockDev.bs >> 24;
-		scsiDev.data[5] = blockDev.bs >> 16;
-		scsiDev.data[6] = blockDev.bs >> 8;
-		scsiDev.data[7] = blockDev.bs;
+		scsiDev.data[4] = config->bytesPerSector >> 24;
+		scsiDev.data[5] = config->bytesPerSector >> 16;
+		scsiDev.data[6] = config->bytesPerSector >> 8;
+		scsiDev.data[7] = config->bytesPerSector;
 		scsiDev.dataLen = 8;
 		scsiDev.phase = DATA_IN;
 	}
@@ -103,7 +100,7 @@ static void doWrite(uint32 lba, uint32 blocks)
 		scsiDev.sense.asc = WRITE_PROTECTED;
 		scsiDev.phase = STATUS;
 	}
-	else if (((uint64) lba) + blocks > blockDev.capacity)
+	else if (((uint64) lba) + blocks > getScsiCapacity())
 	{
 		scsiDev.status = CHECK_CONDITION;
 		scsiDev.sense.code = ILLEGAL_REQUEST;
@@ -117,11 +114,11 @@ static void doWrite(uint32 lba, uint32 blocks)
 		transfer.blocks = blocks;
 		transfer.currentBlock = 0;
 		scsiDev.phase = DATA_OUT;
-		scsiDev.dataLen = SCSI_BLOCK_SIZE;
-		scsiDev.dataPtr = SCSI_BLOCK_SIZE; // TODO FIX scsiDiskPoll()
+		scsiDev.dataLen = config->bytesPerSector;
+		scsiDev.dataPtr = config->bytesPerSector; // TODO FIX scsiDiskPoll()
 
-		// No need for single-block reads atm.  Overhead of the
-		// multi-block read is minimal.
+		// No need for single-block writes atm.  Overhead of the
+		// multi-block write is minimal.
 		transfer.multiBlock = 1;
 		sdPrepareWrite();
 	}
@@ -130,7 +127,8 @@ static void doWrite(uint32 lba, uint32 blocks)
 
 static void doRead(uint32 lba, uint32 blocks)
 {
-	if (((uint64) lba) + blocks > blockDev.capacity)
+	uint32_t capacity = getScsiCapacity();
+	if (((uint64) lba) + blocks > capacity)
 	{
 		scsiDev.status = CHECK_CONDITION;
 		scsiDev.sense.code = ILLEGAL_REQUEST;
@@ -147,7 +145,7 @@ static void doRead(uint32 lba, uint32 blocks)
 		scsiDev.dataLen = 0; // No data yet
 
 		if ((blocks == 1) ||
-			(((uint64) lba) + blocks == blockDev.capacity)
+			(((uint64) lba) + blocks == capacity)
 			)
 		{
 			// We get errors on reading the last sector using a multi-sector
@@ -164,7 +162,7 @@ static void doRead(uint32 lba, uint32 blocks)
 
 static void doSeek(uint32 lba)
 {
-	if (lba >= blockDev.capacity)
+	if (lba >= getScsiCapacity())
 	{
 		scsiDev.status = CHECK_CONDITION;
 		scsiDev.sense.code = ILLEGAL_REQUEST;
@@ -455,8 +453,6 @@ void scsiDiskReset()
 
 void scsiDiskInit()
 {
-	blockDev.bs = SCSI_BLOCK_SIZE;
-	blockDev.capacity = 0;
 	transfer.inProgress = 0;
 	scsiDiskReset();
 

+ 0 - 3
lib/SCSI2SD/software/SCSI2SD/src/disk.h

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

+ 22 - 6
lib/SCSI2SD/software/SCSI2SD/src/geometry.c

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

+ 10 - 4
lib/SCSI2SD/software/SCSI2SD/src/geometry.h

@@ -19,10 +19,8 @@
 
 #include "device.h"
 
-// We make some assumptions that the block size and sector size
-// are always equal.
-#define SCSI_BLOCK_SIZE 512
-#define SCSI_SECTOR_SIZE 512
+#include "config.h"
+#include "sd.h"
 
 // Max allowed by legacy IBM-PC Bios (6 bits)
 #define SCSI_SECTORS_PER_TRACK 63
@@ -37,6 +35,14 @@ typedef enum
 	ADDRESS_PHYSICAL_SECTOR = 5
 } SCSI_ADDRESS_FORMAT;
 
+static inline int SDSectorsPerSCSISector()
+{
+	return (config->bytesPerSector + SD_SECTOR_SIZE - 1) / SD_SECTOR_SIZE;
+}
+
+uint32_t getScsiCapacity();
+
+uint32_t SCSISector2SD(uint32_t scsiSector);
 
 uint64 CHS2LBA(uint32 c, uint8 h, uint32 s);
 void LBA2CHS(uint32 lba, uint32* c, uint8* h, uint32* s);

+ 116 - 13
lib/SCSI2SD/software/SCSI2SD/src/mode.c

@@ -54,14 +54,14 @@ static const uint8 DisconnectReconnectPage[] =
 
 static const uint8 FormatDevicePage[] =
 {
-0x03, // Page code 
+0x03 | 0x80, // Page code | PS (persist) bit.
 0x16, // Page length
 0x00, 0x00, // Single zone
 0x00, 0x00, // No alternate sectors
 0x00, 0x00, // No alternate tracks
 0x00, 0x00, // No alternate tracks per lun
 0x00, SCSI_SECTORS_PER_TRACK, // Sectors per track
-SCSI_SECTOR_SIZE >> 8, SCSI_SECTOR_SIZE & 0xFF, // Data bytes per physical sector
+0xFF, 0xFF, // Data bytes per physical sector. Configurable.
 0x00, 0x01, // Interleave
 0x00, 0x00, // Track skew factor
 0x00, 0x00, // Cylinder skew factor
@@ -111,7 +111,6 @@ static const uint8 ControlModePage[] =
 
 // Allow Apple 68k Drive Setup to format this drive.
 // Code
-// TODO make this string configurable.
 static const uint8 AppleVendorPage[] =
 {
 0x30, // Page code
@@ -143,7 +142,7 @@ static void doModeSense(
 	else
 	{
 		int pageFound = 1;
-		
+
 		////////////// Mode Parameter Header
 		////////////////////////////////////
 
@@ -201,9 +200,9 @@ static void doModeSense(
 			scsiDev.data[idx++] = 0; // reserved
 
 			// Block length
-			scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 16;
-			scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 8;
-			scsiDev.data[idx++] = SCSI_BLOCK_SIZE & 0xFF;
+			scsiDev.data[idx++] = config->bytesPerSector >> 16;
+			scsiDev.data[idx++] = config->bytesPerSector >> 8;
+			scsiDev.data[idx++] = config->bytesPerSector & 0xFF;
 		}
 
 		switch (pageCode)
@@ -223,6 +222,19 @@ static void doModeSense(
 
 		case 0x03:
 			pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));
+			if (pc != 0x01)
+			{
+				// Fill out the configured bytes-per-sector
+				scsiDev.data[idx+12] = config->bytesPerSector >> 8;
+				scsiDev.data[idx+13] = config->bytesPerSector & 0xFF;
+			}
+			else
+			{
+				// Set a mask for the changeable values.
+				scsiDev.data[idx+12] = 0xFF;
+				scsiDev.data[idx+13] = 0xFF;
+			}
+
 			idx += sizeof(FormatDevicePage);
 			if (pageCode != 0x3f) break;
 
@@ -236,7 +248,7 @@ static void doModeSense(
 				uint32 cyl;
 				uint8 head;
 				uint32 sector;
-				LBA2CHS(blockDev.capacity, &cyl, &head, &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
 	{

+ 12 - 1
lib/SCSI2SD/software/SCSI2SD/src/scsi.c

@@ -178,7 +178,14 @@ static void process_DataIn()
 	if ((scsiDev.dataPtr >= scsiDev.dataLen) &&
 		(transfer.currentBlock == transfer.blocks))
 	{
-		enter_Status(GOOD);
+		if (scsiDev.postDataOutHook != NULL)
+		{
+			scsiDev.postDataOutHook();
+		}
+		else
+		{
+			enter_Status(GOOD);
+		}
 	}
 }
 
@@ -439,6 +446,8 @@ static void scsiReset()
 	scsiDev.sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
 	scsiDiskReset();
 
+	scsiDev.postDataOutHook = NULL;
+
 	// Sleep to allow the bus to settle down a bit.
 	// We must be ready again within the "Reset to selection time" of
 	// 250ms.
@@ -463,6 +472,8 @@ static void enter_SelectionPhase()
 
 	transfer.blocks = 0;
 	transfer.currentBlock = 0;
+
+	scsiDev.postDataOutHook = NULL;
 }
 
 static void process_SelectionPhase()

+ 7 - 1
lib/SCSI2SD/software/SCSI2SD/src/scsi.h

@@ -62,6 +62,10 @@ typedef enum
 	MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG = 0x0B
 } SCSI_MESSAGE;
 
+// Maximum value for bytes-per-sector.
+#define MAX_SECTOR_SIZE 2048
+#define MIN_SECTOR_SIZE 64
+
 typedef struct
 {
 	uint8_t scsiIdMask;
@@ -78,7 +82,7 @@ typedef struct
 
 	int phase;
 
-	uint8 data[SCSI_BLOCK_SIZE];
+	uint8 data[MAX_SECTOR_SIZE];
 	int dataPtr; // Index into data, reset on [re]selection to savedDataPtr
 	int savedDataPtr; // Index into data, initially 0.
 	int dataLen;
@@ -103,6 +107,8 @@ typedef struct
 	uint8 msgIn;
 	uint8 msgOut;
 
+	void (*postDataOutHook)(void);
+
 #ifdef MM_DEBUG
 	uint8 cmdCount;
 	uint8 selCount;

+ 117 - 29
lib/SCSI2SD/software/SCSI2SD/src/sd.c

@@ -129,12 +129,14 @@ static void sdClearStatus()
 void sdPrepareRead()
 {
 	uint8 v;
-	uint32 len = (transfer.lba + transfer.currentBlock);
+	uint32 scsiLBA = (transfer.lba + transfer.currentBlock);
+	uint32 sdLBA = SCSISector2SD(scsiLBA);
+	
 	if (!sdDev.ccs)
 	{
-		len = len * SCSI_BLOCK_SIZE;
+		sdLBA = sdLBA * SD_SECTOR_SIZE;
 	}
-	v = sdCommandAndResponse(SD_READ_MULTIPLE_BLOCK, len);
+	v = sdCommandAndResponse(SD_READ_MULTIPLE_BLOCK, sdLBA);
 	if (v)
 	{
 		scsiDiskReset();
@@ -151,7 +153,7 @@ void sdPrepareRead()
 	}
 }
 
-static void doReadSector()
+static void doReadSector(uint32_t numBytes)
 {
 	int prep, i, guard;
 
@@ -184,12 +186,9 @@ static void doReadSector()
 	// Don't do a bus settle delay if we're already in the correct phase.
 	if (transfer.currentBlock == 0)
 	{
-		//scsiEnterPhase(DATA_OUT);
-		//CyDelayUs(200);
 		scsiEnterPhase(DATA_IN);
-		//CyDelayUs(200); // TODO BLOODY SLOW INTERLEAVE
 	}
-	
+
 	// Quickly seed the FIFO
 	prep = 4;
 	CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO
@@ -204,7 +203,7 @@ static void doReadSector()
 	// We stream data straight from the SDCard fifos into the SCSI component
 	// FIFO's. If the loop isn't fast enough, the transmit FIFO's will empty,
 	// and performance will suffer. Every clock cycle counts.
-	while (i < SCSI_BLOCK_SIZE && !scsiDev.resetFlag)
+	while (i < numBytes && !scsiDev.resetFlag)
 	{
 		uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);
 		uint8_t scsiStatus = CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG);
@@ -232,7 +231,25 @@ static void doReadSector()
 		// "combined" SPIM TX and RX FIFOS to the individual FIFO size.
 		// Unlike the SCSI component, SPIM doesn't check if there's room in
 		// the output FIFO before starting to transmit.
-		if ((prep - guard < 4) && (prep < SCSI_BLOCK_SIZE))
+		if ((prep - guard < 4) && (prep < numBytes))
+		{
+			CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO
+			prep++;
+		}
+	}
+
+	// Read and discard remaining bytes.
+	while (i < SD_SECTOR_SIZE)
+	{
+		uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);
+		if(sdRxStatus & SDCard_STS_RX_FIFO_NOT_EMPTY)
+		{
+			CY_GET_REG8(SDCard_RXDATA_PTR);
+			guard++;
+			i++;
+		}
+
+		if ((prep - guard < 4) && (prep < SD_SECTOR_SIZE))
 		{
 			CY_SET_REG8(SDCard_TXDATA_PTR, 0xFF); // Put a byte in the FIFO
 			prep++;
@@ -241,21 +258,20 @@ static void doReadSector()
 
 	sdSpiByte(0xFF); // CRC
 	sdSpiByte(0xFF); // CRC
-	scsiDev.dataLen = SCSI_BLOCK_SIZE;
-	scsiDev.dataPtr = SCSI_BLOCK_SIZE;
+	scsiDev.dataLen = numBytes;
+	scsiDev.dataPtr = numBytes;
 	
 	while (SCSI_ReadPin(SCSI_In_ACK) && !scsiDev.resetFlag) {}
 }
 
-void sdReadSectorSingle()
+static void doReadSectorSingle(uint32 sdBlock, int sdBytes)
 {
 	uint8 v;
-	uint32 len = (transfer.lba + transfer.currentBlock);
 	if (!sdDev.ccs)
 	{
-		len = len * SCSI_BLOCK_SIZE;
+		sdBlock = sdBlock * SD_SECTOR_SIZE;
 	}	
-	v = sdCommandAndResponse(SD_READ_SINGLE_BLOCK, len);
+	v = sdCommandAndResponse(SD_READ_SINGLE_BLOCK, sdBlock);
 	if (v)
 	{
 		scsiDiskReset();
@@ -268,15 +284,47 @@ void sdReadSectorSingle()
 	}
 	else
 	{
-		doReadSector();
+		doReadSector(sdBytes);
+	}
+}
+
+
+void sdReadSectorSingle()
+{
+	uint32 scsiLBA = (transfer.lba + transfer.currentBlock);
+	uint32 sdLBA = SCSISector2SD(scsiLBA);
+	
+	int sdSectors = SDSectorsPerSCSISector();
+	int i;
+	for (i = 0; (i < sdSectors - 1) && (scsiDev.status != CHECK_CONDITION); ++i)
+	{
+		doReadSectorSingle(sdLBA + i, SD_SECTOR_SIZE);
+	}
+
+	if (scsiDev.status != CHECK_CONDITION)
+	{
+		int remaining = config->bytesPerSector % SD_SECTOR_SIZE;
+		if (remaining == 0) remaining = SD_SECTOR_SIZE; // Full sector needed.
+		doReadSectorSingle(sdLBA + i, remaining);
 	}
 }
 
 void sdReadSectorMulti()
 {
 	// Pre: sdPrepareRead called.
-	
-	doReadSector();
+	int sdSectors = SDSectorsPerSCSISector();
+	int i;
+	for (i = 0; (i < sdSectors - 1) && (scsiDev.status != CHECK_CONDITION); ++i)
+	{
+		doReadSector(SD_SECTOR_SIZE);
+	}
+
+	if (scsiDev.status != CHECK_CONDITION)
+	{
+		int remaining = config->bytesPerSector % SD_SECTOR_SIZE;
+		if (remaining == 0) remaining = SD_SECTOR_SIZE; // Full sector needed.
+		doReadSector(remaining);
+	}
 }
 
 
@@ -329,7 +377,7 @@ static void sdWaitWriteBusy()
 	} while (val != 0xFF);
 }
 
-int sdWriteSector()
+static int doWriteSector(uint32_t numBytes)
 {
 	int prep, i, guard;
 	int result, maxWait;
@@ -351,7 +399,7 @@ int sdWriteSector()
 	// We stream data straight from the SCSI fifos into the SPIM component
 	// FIFO's. If the loop isn't fast enough, the transmit FIFO's will empty,
 	// and performance will suffer. Every clock cycle counts.	
-	while (i < SCSI_BLOCK_SIZE)
+	while (i < numBytes && !scsiDev.resetFlag)
 	{
 		uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);
 		uint8_t scsiStatus = CY_GET_REG8(scsiTarget_StatusReg__STATUS_REG);
@@ -375,7 +423,7 @@ int sdWriteSector()
 			++i;
 		}
 
-		if (prep < SCSI_BLOCK_SIZE &&
+		if (prep < numBytes &&
 			(scsiDev.resetFlag || (scsiStatus & 1)) // SCSI TX FIFO NOT FULL
 			)
 		{
@@ -385,6 +433,25 @@ int sdWriteSector()
 		}
 	}
 	
+	// Write remaining bytes as 0x00
+	while (i < SD_SECTOR_SIZE)
+	{
+		uint8_t sdRxStatus = CY_GET_REG8(SDCard_RX_STATUS_PTR);
+
+		if(guard - i < 4)
+		{
+			CY_SET_REG8(SDCard_TXDATA_PTR, 0x00);
+			guard++;
+		}
+
+		// Byte has been sent out the SPIM interface.
+		if (sdRxStatus & SDCard_STS_RX_FIFO_NOT_EMPTY)
+		{
+			 CY_GET_REG8(SDCard_RXDATA_PTR);
+			++i;
+		}
+	}
+	
 	sdSpiByte(0x00); // CRC
 	sdSpiByte(0x00); // CRC
 
@@ -438,6 +505,26 @@ int sdWriteSector()
 	return result;
 }
 
+int sdWriteSector()
+{
+	int result = 1;
+	// Pre: sdPrepareWrite called.
+	int sdSectors = SDSectorsPerSCSISector();
+	int i;
+	for (i = 0; result && (i < sdSectors - 1) && (scsiDev.status != CHECK_CONDITION); ++i)
+	{
+		result = doWriteSector(SD_SECTOR_SIZE);
+	}
+
+	if (result && scsiDev.status != CHECK_CONDITION)
+	{
+		int remaining = config->bytesPerSector % SD_SECTOR_SIZE;
+		if (remaining == 0) remaining = SD_SECTOR_SIZE; // Full sector needed.
+		result = doWriteSector(remaining);
+	}
+	return result;
+}
+
 void sdCompleteWrite()
 {
 	transfer.inProgress = 0;
@@ -565,7 +652,7 @@ static int sdReadCSD()
 		uint32 c_size = (((((uint32)buf[6]) & 0x3) << 16) | (((uint32)buf[7]) << 8) | buf[8]) >> 6;
 		uint32 c_mult = (((((uint32)buf[9]) & 0x3) << 8) | ((uint32)buf[0xa])) >> 7;
 		uint32 sectorSize = buf[5] & 0x0F;
-		sdDev.capacity = ((c_size+1) * ((uint64)1 << (c_mult+2)) * ((uint64)1 << sectorSize)) / SCSI_BLOCK_SIZE;
+		sdDev.capacity = ((c_size+1) * ((uint64)1 << (c_mult+2)) * ((uint64)1 << sectorSize)) / SD_SECTOR_SIZE;
 	}
 	else if ((buf[0] >> 6) == 0x01)
 	{
@@ -622,7 +709,7 @@ int sdInit()
 
 	// This command will be ignored if sdDev.ccs is set.
 	// SDHC and SDXC are always 512bytes.
-	v = sdCRCCommandAndResponse(SD_SET_BLOCKLEN, SCSI_BLOCK_SIZE); //Force sector size
+	v = sdCRCCommandAndResponse(SD_SET_BLOCKLEN, SD_SECTOR_SIZE); //Force sector size
 	if(v){goto bad;}
 	v = sdCRCCommandAndResponse(SD_CRC_ON_OFF, 0); //crc off
 	if(v){goto bad;}
@@ -671,23 +758,24 @@ out:
 
 void sdPrepareWrite()
 {
-	uint32 len;
 	uint8 v;
 	
 	// Set the number of blocks to pre-erase by the multiple block write command
 	// We don't care about the response - if the command is not accepted, writes
 	// will just be a bit slower.
 	// Max 22bit parameter.
-	uint32 blocks = transfer.blocks > 0x7FFFFF ? 0x7FFFFF : transfer.blocks;
+	uint32_t sdBlocks = transfer.blocks * SDSectorsPerSCSISector();
+	uint32 blocks = sdBlocks > 0x7FFFFF ? 0x7FFFFF : sdBlocks;
 	sdCommandAndResponse(SD_APP_CMD, 0);
 	sdCommandAndResponse(SD_APP_SET_WR_BLK_ERASE_COUNT, blocks);
 
-	len = (transfer.lba + transfer.currentBlock);
+	uint32 scsiLBA = (transfer.lba + transfer.currentBlock);
+	uint32 sdLBA = SCSISector2SD(scsiLBA);
 	if (!sdDev.ccs)
 	{
-		len = len * SCSI_BLOCK_SIZE;
+		sdLBA = sdLBA * SD_SECTOR_SIZE;
 	}
-	v = sdCommandAndResponse(25, len);
+	v = sdCommandAndResponse(25, sdLBA);
 	if (v)
 	{
 		scsiDiskReset();

+ 2 - 0
lib/SCSI2SD/software/SCSI2SD/src/sd.h

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

+ 38 - 12
lib/SCSI2SD/software/scsi2sd-config/main.c

@@ -45,7 +45,8 @@ enum
 	PARAM_APPLE,
 	PARAM_VENDOR,
 	PARAM_PRODID,
-	PARAM_REV
+	PARAM_REV,
+	PARAM_BYTESPERSECTOR
 };
 
 // Must be consistent with the structure defined in the SCSI2SD config.h header.
@@ -59,7 +60,9 @@ typedef struct __attribute((packed))
 	uint8_t enableParity;
 	uint8_t enableUnitAttention;
 	uint8_t reserved1; // Unused. Ensures maxBlocks is aligned.
-	uint32_t maxBlocks;
+	uint32_t maxSectors;
+	uint16_t bytesPerSector;
+
 
 	// Pad to 64 bytes, which is what we can fit into a USB HID packet.
 	char reserved[28];
@@ -74,10 +77,11 @@ static void printConfig(ConfigPacket* packet)
 	printf("\n");
 	printf("Parity Checking:\t\t%s\n", packet->enableParity ? "enabled" : "disabled");
 	printf("Unit Attention Condition:\t%s\n", packet->enableUnitAttention ? "enabled" : "disabled");
-	if (packet->maxBlocks)
+	printf("Bytes per sector:\t\t%d\n", packet->bytesPerSector);
+	if (packet->maxSectors)
 	{
 		char sizeBuf[64];
-		uint64_t maxBytes = packet->maxBlocks * (uint64_t) 512;
+		uint64_t maxBytes = packet->maxSectors * (uint64_t) packet->bytesPerSector;
 		if (maxBytes > (1024*1024*1024))
 		{
 			sprintf(sizeBuf, "%.02fGB", maxBytes / (1024.0*1024.0*1024.0));
@@ -95,7 +99,7 @@ static void printConfig(ConfigPacket* packet)
 			sprintf(sizeBuf, "%" PRIu64 " bytes", maxBytes);
 		}
 
-		printf("Maximum Size:\t\t\t%s (%d blocks)\n", sizeBuf, packet->maxBlocks);
+		printf("Maximum Size:\t\t\t%s (%d sectors)\n", sizeBuf, packet->maxSectors);
 	}
 	else
 	{
@@ -117,7 +121,8 @@ static int readConfig(hid_device* handle, ConfigPacket* packet)
 	}
 
 	memcpy(packet, buf, result);
-	packet->maxBlocks = ntohl(packet->maxBlocks);
+	packet->maxSectors = ntohl(packet->maxSectors);
+	packet->bytesPerSector = ntohs(packet->bytesPerSector);
 
 	return result;
 }
@@ -127,9 +132,11 @@ static int writeConfig(hid_device* handle, ConfigPacket* packet)
 	unsigned char buf[1 + sizeof(ConfigPacket)];
 	buf[0] = 0; // report ID
 
-	packet->maxBlocks = htonl(packet->maxBlocks);
+	packet->maxSectors = htonl(packet->maxSectors);
+	packet->bytesPerSector = htons(packet->bytesPerSector);
 	memcpy(buf + 1, packet, sizeof(ConfigPacket));
-	packet->maxBlocks = ntohl(packet->maxBlocks);
+	packet->maxSectors = ntohl(packet->maxSectors);
+	packet->bytesPerSector = ntohs(packet->bytesPerSector);
 
 	int result = hid_write(handle, buf, sizeof(buf));
 
@@ -159,6 +166,8 @@ static void usage()
 	printf("\t\tEach block is 512 bytes. The maximum possible size is 2TB.\n");
 	printf("\t\tThe reported size will be the lower of this value and the SD\n");
 	printf("\t\tcard size. 0 disables the limit.\n\n");
+	printf("--sector={64-2048}\n\t\tSet the bytes-per-sector. Normally 512 bytes.\n");
+	printf("\t\tCan also be set with a SCSI MODE SELECT command.\n\n");
 	printf("--apple\t\tSet the vendor, product ID and revision fields to simulate an \n");
 	printf("\t\tapple-suppled disk. Provides support for the Apple Drive Setup\n");
 	printf("\t\tutility.\n\n");
@@ -250,6 +259,9 @@ int main(int argc, char* argv[])
 		{
 			"rev", required_argument, NULL, PARAM_REV
 		},
+		{
+			"sector", required_argument, NULL, PARAM_BYTESPERSECTOR
+		},
 		{
 			NULL, 0, NULL, 0
 		}
@@ -295,11 +307,11 @@ int main(int argc, char* argv[])
 
 		case PARAM_MAXBLOCKS:
 		{
-			int64_t maxBlocks = -1;
-			if (sscanf(optarg, "%" PRId64, &maxBlocks) == 1 &&
-				maxBlocks >= 0 && maxBlocks <= UINT32_MAX)
+			int64_t maxSectors = -1;
+			if (sscanf(optarg, "%" PRId64, &maxSectors) == 1 &&
+				maxSectors >= 0 && maxSectors <= UINT32_MAX)
 			{
-				packet.maxBlocks = maxBlocks;
+				packet.maxSectors = maxSectors;
 			}
 			else
 			{
@@ -329,6 +341,20 @@ int main(int argc, char* argv[])
 			memcpy(packet.revision, optarg, MIN(strlen(optarg), 4));
 			break;
 
+		case PARAM_BYTESPERSECTOR:
+		{
+			int64_t bytesPerSector = -1;
+			if (sscanf(optarg, "%" PRId64, &bytesPerSector) == 1 &&
+				bytesPerSector >= 64 && bytesPerSector <= 2048)
+			{
+				packet.bytesPerSector = bytesPerSector;
+			}
+			else
+			{
+				usage();
+			}
+			break;
+		}
 		case '?':
 			usage();
 		}