|
@@ -419,6 +419,20 @@ static void doSeek(uint32_t lba)
|
|
|
|
|
|
|
|
BlockDevice blockDev = {DISK_PRESENT | DISK_INITIALISED};
|
|
BlockDevice blockDev = {DISK_PRESENT | DISK_INITIALISED};
|
|
|
Transfer transfer;
|
|
Transfer transfer;
|
|
|
|
|
+static struct {
|
|
|
|
|
+ uint8_t *buffer;
|
|
|
|
|
+ uint32_t bytes_sd; // Number of bytes that have been scheduled for transfer on SD card side
|
|
|
|
|
+ uint32_t bytes_scsi; // Number of bytes that have been scheduled for transfer on SCSI side
|
|
|
|
|
+} g_disk_transfer;
|
|
|
|
|
+
|
|
|
|
|
+#ifdef PREFETCH_BUFFER_SIZE
|
|
|
|
|
+static struct {
|
|
|
|
|
+ uint8_t buffer[PREFETCH_BUFFER_SIZE];
|
|
|
|
|
+ uint32_t sector;
|
|
|
|
|
+ uint32_t bytes;
|
|
|
|
|
+ uint8_t scsiId;
|
|
|
|
|
+} g_scsi_prefetch;
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
/*****************/
|
|
/*****************/
|
|
|
/* Write command */
|
|
/* Write command */
|
|
@@ -579,7 +593,24 @@ static void doRead(uint32_t lba, uint32_t blocks)
|
|
|
scsiDev.dataLen = 0;
|
|
scsiDev.dataLen = 0;
|
|
|
scsiDev.dataPtr = 0;
|
|
scsiDev.dataPtr = 0;
|
|
|
|
|
|
|
|
- if (!img.file.seek(transfer.lba * bytesPerSector))
|
|
|
|
|
|
|
+#ifdef PREFETCH_BUFFER_SIZE
|
|
|
|
|
+ uint32_t sectors_in_prefetch = g_scsi_prefetch.bytes / bytesPerSector;
|
|
|
|
|
+ if (img.scsiId == g_scsi_prefetch.scsiId &&
|
|
|
|
|
+ transfer.lba >= g_scsi_prefetch.sector &&
|
|
|
|
|
+ transfer.lba < g_scsi_prefetch.sector + sectors_in_prefetch)
|
|
|
|
|
+ {
|
|
|
|
|
+ // We have the some sectors already in prefetch cache
|
|
|
|
|
+ scsiEnterPhase(DATA_IN);
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t start_offset = transfer.lba - g_scsi_prefetch.sector;
|
|
|
|
|
+ uint32_t count = sectors_in_prefetch - start_offset;
|
|
|
|
|
+ if (count > transfer.blocks) count = transfer.blocks;
|
|
|
|
|
+ scsiStartWrite(g_scsi_prefetch.buffer + start_offset * bytesPerSector, count * bytesPerSector);
|
|
|
|
|
+ transfer.currentBlock += count;
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ if (!img.file.seek((transfer.lba + transfer.currentBlock) * bytesPerSector))
|
|
|
{
|
|
{
|
|
|
azlog("Seek to ", transfer.lba, " failed for SCSI ID", (int)scsiDev.target->targetId);
|
|
azlog("Seek to ", transfer.lba, " failed for SCSI ID", (int)scsiDev.target->targetId);
|
|
|
scsiDev.status = CHECK_CONDITION;
|
|
scsiDev.status = CHECK_CONDITION;
|
|
@@ -593,39 +624,49 @@ static void doRead(uint32_t lba, uint32_t blocks)
|
|
|
void diskDataIn_callback(uint32_t bytes_complete)
|
|
void diskDataIn_callback(uint32_t bytes_complete)
|
|
|
{
|
|
{
|
|
|
// For best performance, do writes in blocks of 4 or more bytes
|
|
// For best performance, do writes in blocks of 4 or more bytes
|
|
|
- if (bytes_complete < scsiDev.dataLen)
|
|
|
|
|
|
|
+ if (bytes_complete < g_disk_transfer.bytes_sd)
|
|
|
{
|
|
{
|
|
|
bytes_complete &= ~3;
|
|
bytes_complete &= ~3;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (bytes_complete > scsiDev.dataPtr)
|
|
|
|
|
|
|
+ if (bytes_complete > g_disk_transfer.bytes_scsi)
|
|
|
{
|
|
{
|
|
|
// DMA is reading from SD card, bytes_complete bytes have already been read.
|
|
// DMA is reading from SD card, bytes_complete bytes have already been read.
|
|
|
// Send them to SCSI bus now.
|
|
// Send them to SCSI bus now.
|
|
|
-
|
|
|
|
|
- uint32_t len = bytes_complete - scsiDev.dataPtr;
|
|
|
|
|
- scsiWrite(scsiDev.data + scsiDev.dataPtr, len);
|
|
|
|
|
- scsiDev.dataPtr += len;
|
|
|
|
|
|
|
+ uint32_t len = bytes_complete - g_disk_transfer.bytes_scsi;
|
|
|
|
|
+ scsiStartWrite(g_disk_transfer.buffer + g_disk_transfer.bytes_scsi, len);
|
|
|
|
|
+ g_disk_transfer.bytes_scsi += len;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Provide a chance for polling request processing
|
|
|
|
|
+ scsiIsWriteFinished(NULL);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static void diskDataIn()
|
|
|
|
|
|
|
+// Start a data in transfer using given temporary buffer.
|
|
|
|
|
+// diskDataIn() below divides the scsiDev.data buffer to two halves for double buffering.
|
|
|
|
|
+static void start_dataInTransfer(uint8_t *buffer, uint32_t count)
|
|
|
{
|
|
{
|
|
|
- scsiEnterPhase(DATA_IN);
|
|
|
|
|
-
|
|
|
|
|
- // Figure out how many blocks we can fit in buffer
|
|
|
|
|
- uint32_t blockcount = (transfer.blocks - transfer.currentBlock);
|
|
|
|
|
- uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
|
|
|
|
|
- uint32_t maxblocks = sizeof(scsiDev.data) / bytesPerSector;
|
|
|
|
|
- if (blockcount > maxblocks) blockcount = maxblocks;
|
|
|
|
|
- uint32_t transferlen = blockcount * bytesPerSector;
|
|
|
|
|
|
|
+ g_disk_transfer.buffer = buffer;
|
|
|
|
|
+ g_disk_transfer.bytes_scsi = 0;
|
|
|
|
|
+ g_disk_transfer.bytes_sd = count;
|
|
|
|
|
+
|
|
|
|
|
+ // Verify that previous write using this buffer has finished
|
|
|
|
|
+ uint32_t start = millis();
|
|
|
|
|
+ while (!scsiIsWriteFinished(buffer + count - 1) && !scsiDev.resetFlag)
|
|
|
|
|
+ {
|
|
|
|
|
+ if ((uint32_t)(millis() - start) > 5000)
|
|
|
|
|
+ {
|
|
|
|
|
+ azlog("start_dataInTransfer() timeout waiting for previous to finish");
|
|
|
|
|
+ scsiDev.resetFlag = 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (scsiDev.resetFlag) return;
|
|
|
|
|
|
|
|
- // Start reading from SD card.
|
|
|
|
|
- // The callback will write to SCSI bus.
|
|
|
|
|
|
|
+ // Start transferring from SD card
|
|
|
image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
|
|
image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
|
|
|
- azplatform_set_sd_callback(&diskDataIn_callback, scsiDev.data);
|
|
|
|
|
|
|
+ azplatform_set_sd_callback(&diskDataIn_callback, buffer);
|
|
|
|
|
|
|
|
- if (img.file.read(scsiDev.data, transferlen) != transferlen)
|
|
|
|
|
|
|
+ if (img.file.read(buffer, count) != count)
|
|
|
{
|
|
{
|
|
|
azlog("SD card read failed: ", SD.sdErrorCode());
|
|
azlog("SD card read failed: ", SD.sdErrorCode());
|
|
|
scsiDev.status = CHECK_CONDITION;
|
|
scsiDev.status = CHECK_CONDITION;
|
|
@@ -634,11 +675,67 @@ static void diskDataIn()
|
|
|
scsiDev.phase = STATUS;
|
|
scsiDev.phase = STATUS;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- diskDataIn_callback(transferlen);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ diskDataIn_callback(count);
|
|
|
azplatform_set_sd_callback(NULL, NULL);
|
|
azplatform_set_sd_callback(NULL, NULL);
|
|
|
- transfer.currentBlock += blockcount;
|
|
|
|
|
- scsiDev.dataPtr = scsiDev.dataLen = 0;
|
|
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void diskDataIn()
|
|
|
|
|
+{
|
|
|
|
|
+ scsiEnterPhase(DATA_IN);
|
|
|
|
|
+
|
|
|
|
|
+ // Figure out how many blocks we can fit in buffer
|
|
|
|
|
+ uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
|
|
|
|
|
+ uint32_t maxblocks = sizeof(scsiDev.data) / bytesPerSector;
|
|
|
|
|
+ uint32_t maxblocks_half = maxblocks / 2;
|
|
|
|
|
+
|
|
|
|
|
+ // Start transfer in first half of buffer
|
|
|
|
|
+ // Waits for the previous first half transfer to finish first.
|
|
|
|
|
+ uint32_t remain = (transfer.blocks - transfer.currentBlock);
|
|
|
|
|
+ if (remain > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ uint32_t transfer_blocks = std::min(remain, maxblocks_half);
|
|
|
|
|
+ uint32_t transfer_bytes = transfer_blocks * bytesPerSector;
|
|
|
|
|
+ start_dataInTransfer(&scsiDev.data[0], transfer_bytes);
|
|
|
|
|
+ transfer.currentBlock += transfer_blocks;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Start transfer in second half of buffer
|
|
|
|
|
+ // Waits for the previous second half transfer to finish first
|
|
|
|
|
+ remain = (transfer.blocks - transfer.currentBlock);
|
|
|
|
|
+ if (remain > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ uint32_t transfer_blocks = std::min(remain, maxblocks_half);
|
|
|
|
|
+ uint32_t transfer_bytes = transfer_blocks * bytesPerSector;
|
|
|
|
|
+ start_dataInTransfer(&scsiDev.data[maxblocks_half * bytesPerSector], transfer_bytes);
|
|
|
|
|
+ transfer.currentBlock += transfer_blocks;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (transfer.currentBlock == transfer.blocks)
|
|
|
|
|
+ {
|
|
|
|
|
+ // This was the last block, verify that everything finishes
|
|
|
|
|
+
|
|
|
|
|
+#ifdef PREFETCH_BUFFER_SIZE
|
|
|
|
|
+ uint32_t prefetch_sectors = PREFETCH_BUFFER_SIZE / bytesPerSector;
|
|
|
|
|
+ g_scsi_prefetch.sector = transfer.lba + transfer.blocks;
|
|
|
|
|
+ g_scsi_prefetch.bytes = 0;
|
|
|
|
|
+ g_scsi_prefetch.scsiId = scsiDev.target->cfg->scsiId;
|
|
|
|
|
+ while (!scsiIsWriteFinished(NULL) && prefetch_sectors > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ // We still have time, prefetch next sectors in case this SCSI request
|
|
|
|
|
+ // is part of a longer linear read.
|
|
|
|
|
+ g_disk_transfer.buffer = g_scsi_prefetch.buffer + g_scsi_prefetch.bytes;
|
|
|
|
|
+ g_disk_transfer.bytes_sd = bytesPerSector;
|
|
|
|
|
+ g_disk_transfer.bytes_scsi = bytesPerSector; // Tell callback not to send to SCSI
|
|
|
|
|
+ image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
|
|
|
|
|
+ azplatform_set_sd_callback(&diskDataIn_callback, g_disk_transfer.buffer);
|
|
|
|
|
+ g_scsi_prefetch.bytes += img.file.read(g_disk_transfer.buffer, bytesPerSector);
|
|
|
|
|
+ azplatform_set_sd_callback(NULL, NULL);
|
|
|
|
|
+ prefetch_sectors--;
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ scsiFinishWrite();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -884,6 +981,11 @@ void scsiDiskReset()
|
|
|
transfer.blocks = 0;
|
|
transfer.blocks = 0;
|
|
|
transfer.currentBlock = 0;
|
|
transfer.currentBlock = 0;
|
|
|
transfer.multiBlock = 0;
|
|
transfer.multiBlock = 0;
|
|
|
|
|
+
|
|
|
|
|
+#ifdef PREFETCH_BUFFER_SIZE
|
|
|
|
|
+ g_scsi_prefetch.bytes = 0;
|
|
|
|
|
+ g_scsi_prefetch.sector = 0;
|
|
|
|
|
+#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
extern "C"
|
|
extern "C"
|