|
|
@@ -0,0 +1,451 @@
|
|
|
+// Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
|
|
|
+//
|
|
|
+// This file is part of SCSI2SD.
|
|
|
+//
|
|
|
+// SCSI2SD is free software: you can redistribute it and/or modify
|
|
|
+// it under the terms of the GNU General Public License as published by
|
|
|
+// the Free Software Foundation, either version 3 of the License, or
|
|
|
+// (at your option) any later version.
|
|
|
+//
|
|
|
+// SCSI2SD is distributed in the hope that it will be useful,
|
|
|
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+// GNU General Public License for more details.
|
|
|
+//
|
|
|
+// You should have received a copy of the GNU General Public License
|
|
|
+// along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+
|
|
|
+#include "device.h"
|
|
|
+#include "scsi.h"
|
|
|
+#include "disk.h"
|
|
|
+#include "sd.h"
|
|
|
+
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+// Global
|
|
|
+SdDevice sdDev;
|
|
|
+
|
|
|
+static uint8 sdCrc7(uint8* chr, uint8 cnt, uint8 crc)
|
|
|
+{
|
|
|
+ uint8 a;
|
|
|
+ for(a = 0; a < cnt; a++)
|
|
|
+ {
|
|
|
+ uint8 Data = chr[a];
|
|
|
+ uint8 i;
|
|
|
+ for(i = 0; i < 8; i++)
|
|
|
+ {
|
|
|
+ crc <<= 1;
|
|
|
+ if( (Data & 0x80) ^ (crc & 0x80) ) {crc ^= 0x09;}
|
|
|
+ Data <<= 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return crc & 0x7F;
|
|
|
+}
|
|
|
+
|
|
|
+// Read and write 1 byte.
|
|
|
+static uint8 sdSpiByte(uint8 value)
|
|
|
+{
|
|
|
+ SDCard_WriteTxData(value);
|
|
|
+ while(!(SDCard_ReadTxStatus() & SDCard_STS_SPI_DONE))
|
|
|
+ {}
|
|
|
+ while (!SDCard_GetRxBufferSize()) {}
|
|
|
+ return SDCard_ReadRxData();
|
|
|
+}
|
|
|
+
|
|
|
+static void sdSendCommand(uint8 cmd, uint32 param)
|
|
|
+{
|
|
|
+ uint8 send[6];
|
|
|
+
|
|
|
+ send[0] = cmd | 0x40;
|
|
|
+ send[1] = param >> 24;
|
|
|
+ send[2] = param >> 16;
|
|
|
+ send[3] = param >> 8;
|
|
|
+ send[4] = param;
|
|
|
+ send[5] = (sdCrc7(send, 5, 0) << 1) | 1;
|
|
|
+
|
|
|
+ for(cmd = 0; cmd < sizeof(send); cmd++)
|
|
|
+ {
|
|
|
+ sdSpiByte(send[cmd]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static uint8 sdReadResp()
|
|
|
+{
|
|
|
+ uint8 v;
|
|
|
+ uint8 i = 128;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ v = sdSpiByte(0xFF);
|
|
|
+ } while(i-- && (v == 0xFF));
|
|
|
+ return v;
|
|
|
+}
|
|
|
+
|
|
|
+static uint8 sdWaitResp()
|
|
|
+{
|
|
|
+ uint8 v;
|
|
|
+ uint8 i = 255;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ v = sdSpiByte(0xFF);
|
|
|
+ } while(i-- && (v != 0xFE));
|
|
|
+ return v;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static uint8 sdCommandAndResponse(uint8 cmd, uint32 param)
|
|
|
+{
|
|
|
+ SDCard_ClearRxBuffer();
|
|
|
+ sdSpiByte(0xFF);
|
|
|
+ sdSendCommand(cmd, param);
|
|
|
+ return sdReadResp();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void sdPrepareRead(int nextBlockOffset)
|
|
|
+{
|
|
|
+ uint32 len = (transfer.lba + transfer.currentBlock + nextBlockOffset);
|
|
|
+ if (!sdDev.ccs)
|
|
|
+ {
|
|
|
+ len = len * SCSI_BLOCK_SIZE;
|
|
|
+ }
|
|
|
+ uint8 v = sdCommandAndResponse(17, len);
|
|
|
+ if (v)
|
|
|
+ {
|
|
|
+ scsiDiskReset();
|
|
|
+
|
|
|
+ scsiDev.status = CHECK_CONDITION;
|
|
|
+ scsiDev.sense.code = HARDWARE_ERROR;
|
|
|
+ scsiDev.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
|
|
+ scsiDev.phase = STATUS;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int sdIsReadReady()
|
|
|
+{
|
|
|
+ uint8 v = sdWaitResp();
|
|
|
+ if (v == 0xFF)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ else if (v == 0xFE)
|
|
|
+ {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ scsiDiskReset();
|
|
|
+ scsiDev.status = CHECK_CONDITION;
|
|
|
+ scsiDev.sense.code = HARDWARE_ERROR;
|
|
|
+ scsiDev.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
|
|
+ scsiDev.phase = STATUS;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void sdReadSector()
|
|
|
+{
|
|
|
+ // We have a spi FIFO of 4 bytes. use it.
|
|
|
+ // This is much better, byut after 4 bytes we're still
|
|
|
+ // blocking a bit.
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < SCSI_BLOCK_SIZE; i+=4)
|
|
|
+ {
|
|
|
+ SDCard_WriteTxData(0xFF);
|
|
|
+ SDCard_WriteTxData(0xFF);
|
|
|
+ SDCard_WriteTxData(0xFF);
|
|
|
+ SDCard_WriteTxData(0xFF);
|
|
|
+
|
|
|
+ while(!(SDCard_ReadTxStatus() & SDCard_STS_SPI_DONE))
|
|
|
+ {}
|
|
|
+ scsiDev.data[i] = SDCard_ReadRxData();
|
|
|
+ scsiDev.data[i+1] = SDCard_ReadRxData();
|
|
|
+ scsiDev.data[i+2] = SDCard_ReadRxData();
|
|
|
+ scsiDev.data[i+3] = SDCard_ReadRxData();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ sdSpiByte(0xFF); // CRC
|
|
|
+ sdSpiByte(0xFF); // CRC
|
|
|
+ scsiDev.dataLen = SCSI_BLOCK_SIZE;
|
|
|
+ scsiDev.dataPtr = 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void sdWaitWriteBusy()
|
|
|
+{
|
|
|
+ uint8 val;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ val = sdSpiByte(0xFF);
|
|
|
+ } while (val != 0xFF);
|
|
|
+}
|
|
|
+
|
|
|
+int sdWriteSector()
|
|
|
+{
|
|
|
+ int result;
|
|
|
+ // Wait for a previously-written sector to complete.
|
|
|
+ sdWaitWriteBusy();
|
|
|
+
|
|
|
+ sdSpiByte(0xFC); // MULTIPLE byte start token
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < SCSI_BLOCK_SIZE; i+=4)
|
|
|
+ {
|
|
|
+ SDCard_WriteTxData(scsiDev.data[i]);
|
|
|
+ SDCard_WriteTxData(scsiDev.data[i+1]);
|
|
|
+ SDCard_WriteTxData(scsiDev.data[i+2]);
|
|
|
+ SDCard_WriteTxData(scsiDev.data[i+3]);
|
|
|
+
|
|
|
+ while(!(SDCard_ReadTxStatus() & SDCard_STS_SPI_DONE))
|
|
|
+ {}
|
|
|
+
|
|
|
+ SDCard_ReadRxData();
|
|
|
+ SDCard_ReadRxData();
|
|
|
+ SDCard_ReadRxData();
|
|
|
+ SDCard_ReadRxData();
|
|
|
+ }
|
|
|
+
|
|
|
+ sdSpiByte(0x00); // CRC
|
|
|
+ sdSpiByte(0x00); // CRC
|
|
|
+ uint8 dataToken = sdSpiByte(0xFF); // Response
|
|
|
+ if (((dataToken & 0x1F) >> 1) != 0x2) // Accepted.
|
|
|
+ {
|
|
|
+ sdWaitWriteBusy();
|
|
|
+ sdSpiByte(0xFD); // STOP TOKEN
|
|
|
+ // Wait for the card to come out of busy.
|
|
|
+ sdWaitWriteBusy();
|
|
|
+
|
|
|
+ scsiDiskReset();
|
|
|
+
|
|
|
+ scsiDev.status = CHECK_CONDITION;
|
|
|
+ scsiDev.sense.code = HARDWARE_ERROR;
|
|
|
+ scsiDev.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
|
|
+ scsiDev.phase = STATUS;
|
|
|
+ result = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // The card is probably in the busy state.
|
|
|
+ // Don't wait, as we could read the SCSI interface instead.
|
|
|
+ result = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+void sdCompleteWrite()
|
|
|
+{
|
|
|
+ // Wait for a previously-written sector to complete.
|
|
|
+ sdWaitWriteBusy();
|
|
|
+
|
|
|
+ sdSpiByte(0xFD); // STOP TOKEN
|
|
|
+ // Wait for the card to come out of busy.
|
|
|
+ sdWaitWriteBusy();
|
|
|
+
|
|
|
+ uint8 r1 = sdCommandAndResponse(13, 0); // send status
|
|
|
+ uint8 r2 = sdSpiByte(0xFF);
|
|
|
+ if (r1 || r2)
|
|
|
+ {
|
|
|
+ scsiDev.status = CHECK_CONDITION;
|
|
|
+ scsiDev.sense.code = HARDWARE_ERROR;
|
|
|
+ scsiDev.sense.asc = WRITE_ERROR_AUTO_REALLOCATION_FAILED;
|
|
|
+ scsiDev.phase = STATUS;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// SD Version 2 (SDHC) support
|
|
|
+static int sendIfCond()
|
|
|
+{
|
|
|
+ int retries = 50;
|
|
|
+
|
|
|
+ do
|
|
|
+ {
|
|
|
+ uint8 status = sdCommandAndResponse(SD_SEND_IF_COND, 0x000001AA);
|
|
|
+
|
|
|
+ if (status == SD_R1_IDLE)
|
|
|
+ {
|
|
|
+ // Version 2 card.
|
|
|
+ sdDev.version = 2;
|
|
|
+ // Read 32bit response. Should contain the same bytes that
|
|
|
+ // we sent in the command parameter.
|
|
|
+ sdSpiByte(0xFF);
|
|
|
+ sdSpiByte(0xFF);
|
|
|
+ sdSpiByte(0xFF);
|
|
|
+ sdSpiByte(0xFF);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else if (status & SD_R1_ILLEGAL)
|
|
|
+ {
|
|
|
+ // Version 1 card.
|
|
|
+ sdDev.version = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } while (--retries > 0);
|
|
|
+
|
|
|
+ return retries > 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdOpCond()
|
|
|
+{
|
|
|
+ int retries = 50;
|
|
|
+
|
|
|
+ uint8 status;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ CyDelay(33); // Spec says to retry for 1 second.
|
|
|
+
|
|
|
+ sdCommandAndResponse(SD_APP_CMD, 0);
|
|
|
+ // Host Capacity Support = 1 (SDHC/SDXC supported)
|
|
|
+ status = sdCommandAndResponse(SD_APP_SEND_OP_COND, 0x40000000);
|
|
|
+ } while ((status != 0) && (--retries > 0));
|
|
|
+
|
|
|
+ return retries > 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdReadOCR()
|
|
|
+{
|
|
|
+ uint8 status = sdCommandAndResponse(SD_READ_OCR, 0);
|
|
|
+ if(status){goto bad;}
|
|
|
+
|
|
|
+ uint8 buf[4];
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < 4; ++i)
|
|
|
+ {
|
|
|
+ buf[i] = sdSpiByte(0xFF);
|
|
|
+ }
|
|
|
+
|
|
|
+ sdDev.ccs = (buf[0] & 0x40) ? 1 : 0;
|
|
|
+
|
|
|
+ return 1;
|
|
|
+bad:
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdReadCSD()
|
|
|
+{
|
|
|
+ uint8 status = sdCommandAndResponse(SD_SEND_CSD, 0);
|
|
|
+ if(status){goto bad;}
|
|
|
+ status = sdWaitResp();
|
|
|
+ if (status != 0xFE) { goto bad; }
|
|
|
+
|
|
|
+ uint8 buf[16];
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < 16; ++i)
|
|
|
+ {
|
|
|
+ buf[i] = sdSpiByte(0xFF);
|
|
|
+ }
|
|
|
+ sdSpiByte(0xFF); // CRC
|
|
|
+ sdSpiByte(0xFF); // CRC
|
|
|
+
|
|
|
+ if ((buf[0] >> 6) == 0x00)
|
|
|
+ {
|
|
|
+ // CSD version 1
|
|
|
+ // C_SIZE in bits [73:62]
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ else if ((buf[0] >> 6) == 0x01)
|
|
|
+ {
|
|
|
+ // CSD version 2
|
|
|
+ // C_SIZE in bits [69:48]
|
|
|
+
|
|
|
+ uint32 c_size =
|
|
|
+ ((((uint32)buf[7]) & 0x3F) << 16) |
|
|
|
+ (((uint32)buf[8]) << 8) |
|
|
|
+ ((uint32)buf[7]);
|
|
|
+ sdDev.capacity = (c_size + 1) * 1024;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ goto bad;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+bad:
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int sdInit()
|
|
|
+{
|
|
|
+ sdDev.version = 0;
|
|
|
+ sdDev.ccs = 0;
|
|
|
+ sdDev.capacity = 0;
|
|
|
+
|
|
|
+ int result = 0;
|
|
|
+ SD_CS_Write(1); // Set CS inactive (active low)
|
|
|
+ SD_Init_Clk_Start(); // Turn on the slow 400KHz clock
|
|
|
+ SD_Clk_Ctl_Write(0); // Select the 400KHz clock source.
|
|
|
+ SDCard_Start(); // Enable SPI hardware
|
|
|
+
|
|
|
+ // Power on sequence. 74 clock cycles of a "1" while CS unasserted.
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < 10; ++i)
|
|
|
+ {
|
|
|
+ sdSpiByte(0xFF);
|
|
|
+ }
|
|
|
+
|
|
|
+ SD_CS_Write(0); // Set CS active (active low)
|
|
|
+ CyDelayUs(1);
|
|
|
+
|
|
|
+ uint8 v = sdCommandAndResponse(SD_GO_IDLE_STATE, 0);
|
|
|
+ if(v != 1){goto bad;}
|
|
|
+
|
|
|
+ if (!sendIfCond()) goto bad; // Sets V1 or V2 flag
|
|
|
+ if (!sdOpCond()) goto bad;
|
|
|
+ if (!sdReadOCR()) goto bad;
|
|
|
+
|
|
|
+ // This command will be ignored if sdDev.ccs is set.
|
|
|
+ // SDHC and SDXC are always 512bytes.
|
|
|
+ v = sdCommandAndResponse(SD_SET_BLOCKLEN, SCSI_BLOCK_SIZE); //Force sector size
|
|
|
+ if(v){goto bad;}
|
|
|
+ v = sdCommandAndResponse(SD_CRC_ON_OFF, 0); //crc off
|
|
|
+ if(v){goto bad;}
|
|
|
+
|
|
|
+ // now set the sd card up for full speed
|
|
|
+ SD_Data_Clk_Start(); // Turn on the fast clock
|
|
|
+ SD_Clk_Ctl_Write(1); // Select the fast clock source.
|
|
|
+ SD_Init_Clk_Stop(); // Stop the slow clock.
|
|
|
+
|
|
|
+ if (!sdReadCSD()) goto bad;
|
|
|
+
|
|
|
+ result = 1;
|
|
|
+ goto out;
|
|
|
+
|
|
|
+bad:
|
|
|
+ sdDev.capacity = 0;
|
|
|
+
|
|
|
+out:
|
|
|
+ return result;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void sdPrepareWrite()
|
|
|
+{
|
|
|
+ // 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;
|
|
|
+ sdCommandAndResponse(SD_APP_CMD, 0);
|
|
|
+ sdCommandAndResponse(SD_APP_SET_WR_BLK_ERASE_COUNT, blocks);
|
|
|
+
|
|
|
+ uint32 len = (transfer.lba + transfer.currentBlock);
|
|
|
+ if (!sdDev.ccs)
|
|
|
+ {
|
|
|
+ len = len * SCSI_BLOCK_SIZE;
|
|
|
+ }
|
|
|
+ uint8 v = sdCommandAndResponse(25, len);
|
|
|
+ if (v)
|
|
|
+ {
|
|
|
+ scsiDiskReset();
|
|
|
+
|
|
|
+ scsiDev.status = CHECK_CONDITION;
|
|
|
+ scsiDev.sense.code = HARDWARE_ERROR;
|
|
|
+ scsiDev.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
|
|
+ scsiDev.phase = STATUS;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|