Переглянути джерело

Merge pull request #43 from ZuluSCSI/v1.0.7-release

Disable raw fallback when image is specified through .ini
Show warning about undivisible image size only when host queries disk
Fix pauses during transfer in PIO mode
Avoid SysTick interrupts during transfers
Alex Perez 3 роки тому
батько
коміт
dc30e557de

+ 24 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp

@@ -78,6 +78,30 @@ void SysTick_Handler(void)
         "b SysTick_Handler_inner": : : "r0");
 }
 
+// This function is called by scsiPhy.cpp.
+// It resets the systick counter to give 1 millisecond of uninterrupted transfer time.
+// The total number of skips is kept track of to keep the correct time on average.
+void SysTick_Handle_PreEmptively()
+{
+    static int skipped_clocks = 0;
+
+    __disable_irq();
+    uint32_t loadval = SysTick->LOAD;
+    skipped_clocks += loadval - SysTick->VAL;
+    SysTick->VAL = 0;
+
+    if (skipped_clocks > loadval)
+    {
+        // We have skipped enough ticks that it is time to fake a call
+        // to SysTick interrupt handler.
+        skipped_clocks -= loadval;
+        uint32_t stack_frame[8] = {0};
+        stack_frame[6] = (uint32_t)__builtin_return_address(0);
+        SysTick_Handler_inner(stack_frame);
+    }
+    __enable_irq();
+}
+
 /***************/
 /* GPIO init   */
 /***************/

+ 9 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h

@@ -73,6 +73,11 @@ void azplatform_emergency_log_save();
 typedef void (*sd_callback_t)(uint32_t bytes_complete);
 void azplatform_set_sd_callback(sd_callback_t func, const uint8_t *buffer);
 
+// This function is called by scsiPhy.cpp.
+// It resets the systick counter to give 1 millisecond of uninterrupted transfer time.
+// The total number of skips is kept track of to keep the correct time on average.
+void SysTick_Handle_PreEmptively();
+
 // Reprogram firmware in main program area.
 #define AZPLATFORM_BOOTLOADER_SIZE 32768
 #define AZPLATFORM_FLASH_TOTAL_SIZE (256 * 1024)
@@ -145,4 +150,8 @@ extern SdioConfig g_sd_sdio_config_crash;
 
 #endif
 
+// Check if a DMA request for SD card read has completed.
+// This is used to optimize the timing of data transfers on SCSI bus.
+bool check_sd_read_done();
+
 #endif

+ 16 - 3
lib/ZuluSCSI_platform_GD32F205/scsiPhy.cpp

@@ -418,13 +418,24 @@ static bool isPollingWriteFinished(const uint8_t *data)
 extern "C" bool scsiIsWriteFinished(const uint8_t *data)
 {
     // Check if there is still a polling transfer in progress
-    if (!isPollingWriteFinished(data))
+    if (!isPollingWriteFinished(data) && !check_sd_read_done())
     {
         // Process the transfer piece-by-piece while waiting
         // for SD card to react.
         int max_count = g_scsi_writereq.count / 8;
-        max_count &= ~255;
-        if (max_count < 256) max_count = 256;
+        
+        // Always transfer whole sectors without pause to avoid problems with some SCSI hosts.
+        int bytesPerSector = 512;
+        if (scsiDev.target)
+        {
+            bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
+        }
+        if (max_count % bytesPerSector != 0) max_count -= (max_count % bytesPerSector);
+        if (max_count < bytesPerSector) max_count = bytesPerSector;
+        
+        // Avoid SysTick interrupt pauses during the transfer
+        SysTick_Handle_PreEmptively();
+
         processPollingWrite(max_count);
         return isPollingWriteFinished(data);
     }
@@ -483,6 +494,8 @@ extern "C" void scsiRead(uint8_t* data, uint32_t count, int* parityError)
     uint32_t count_words = count / 4;
     bool use_greenpak = (g_scsi_phy_mode == PHY_MODE_GREENPAK_DMA || g_scsi_phy_mode == PHY_MODE_GREENPAK_PIO);
 
+    SysTick_Handle_PreEmptively();
+
     if (g_scsi_phase == DATA_OUT && scsiDev.target->syncOffset > 0)
     {
         // Synchronous data transfer

+ 7 - 0
lib/ZuluSCSI_platform_GD32F205/sd_card_sdio.cpp

@@ -282,6 +282,13 @@ bool SdioCard::readSectors(uint32_t sector, uint8_t* dst, size_t n)
         get_stream_callback(dst, n * 512)));
 }
 
+// Check if a DMA request for SD card read has completed.
+// This is used to optimize the timing of data transfers on SCSI bus.
+bool check_sd_read_done()
+{
+    return (DMA_CHCTL(DMA1, DMA_CH3) & DMA_CHXCTL_CHEN)
+        && (DMA_INTF(DMA1) & DMA_FLAG_ADD(DMA_FLAG_FTF, DMA_CH3));
+}
 
 // These functions are not used for SDIO mode but are needed to avoid build error.
 void sdCsInit(SdCsPin_t pin) {}

+ 8 - 0
lib/ZuluSCSI_platform_GD32F205/sd_card_spi.cpp

@@ -263,4 +263,12 @@ void azplatform_set_sd_callback(sd_callback_t func, const uint8_t *buffer)
     g_sd_spi_port.set_sd_callback(func, buffer);    
 }
 
+// Check if a DMA request for SD card read has completed.
+// This is used to optimize the timing of data transfers on SCSI bus.
+bool check_sd_read_done()
+{
+    return (DMA_CHCTL(DMA0, SD_SPI_RX_DMA_CHANNEL) & DMA_CHXCTL_CHEN)
+        && (DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_FTF, SD_SPI_RX_DMA_CHANNEL));
+}
+
 #endif

+ 6 - 8
src/ZuluSCSI.cpp

@@ -329,12 +329,6 @@ bool findHDDImages()
   }
   root.close();
 
-  // Error if there are 0 image files
-  if(!foundImage) {
-    azlog("ERROR: No valid images found!");
-    blinkStatus(BLINK_ERROR_NO_IMAGES);
-  }
-
   // Print SCSI drive map
   for (int i = 0; i < NUM_SCSIID; i++)
   {
@@ -381,9 +375,10 @@ static void reinitSCSI()
 {
   scsiDiskResetImages();
   readSCSIDeviceConfig();
-  bool foundImage = findHDDImages();
+  findHDDImages();
 
-  if (foundImage)
+  // Error if there are 0 image files
+  if (scsiDiskCheckAnyImagesConfigured())
   {
     // Ok, there is an image
     blinkStatus(BLINK_STATUS_OK);
@@ -394,7 +389,10 @@ static void reinitSCSI()
     azlog("No images found, enabling RAW fallback partition");
     scsiDiskOpenHDDImage(RAW_FALLBACK_SCSI_ID, "RAW:0:0xFFFFFFFF", RAW_FALLBACK_SCSI_ID, 0,
                          RAW_FALLBACK_BLOCKSIZE, false, false);
+#else
+    azlog("No valid image files found!");
 #endif
+    blinkStatus(BLINK_ERROR_NO_IMAGES);
   }
 
   scsiPhyReset();

+ 1 - 1
src/ZuluSCSI_config.h

@@ -4,7 +4,7 @@
 #pragma once
 
 // Use variables for version number
-#define FW_VER_NUM      "1.0.6"
+#define FW_VER_NUM      "1.0.7"
 #define FW_VER_SUFFIX   "release"
 #define ZULU_FW_VERSION FW_VER_NUM "-" FW_VER_SUFFIX
 

+ 49 - 9
src/ZuluSCSI_disk.cpp

@@ -230,6 +230,9 @@ struct image_config_t: public S2S_TargetCfg
 
     // Maximum amount of bytes to prefetch
     int prefetchbytes;
+
+    // Warning about geometry settings
+    bool geometrywarningprinted;
 };
 
 static image_config_t g_DiskImages[S2S_MAX_TARGETS];
@@ -393,15 +396,6 @@ bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int
             azlog("---- WARNING: file ", filename, " is not contiguous. This will increase read latency.");
         }
 
-        uint32_t sectorsPerHeadTrack = img.sectorsPerTrack * img.headsPerCylinder;
-        if (img.scsiSectors % sectorsPerHeadTrack != 0)
-        {
-            azlog("---- NOTE: Drive geometry is ",
-                (int)img.sectorsPerTrack, "x", (int)img.headsPerCylinder, "=",
-                (int)sectorsPerHeadTrack, " but image size of ", (int)img.scsiSectors,
-                " is not divisible.");
-        }
-
         if (is_cd)
         {
             azlog("---- Configuring as CD-ROM drive based on image name");
@@ -434,6 +428,23 @@ bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int
     return false;
 }
 
+static void checkDiskGeometryDivisible(image_config_t &img)
+{
+    if (!img.geometrywarningprinted)
+    {
+        uint32_t sectorsPerHeadTrack = img.sectorsPerTrack * img.headsPerCylinder;
+        if (img.scsiSectors % sectorsPerHeadTrack != 0)
+        {
+            azlog("WARNING: Host used command ", scsiDev.cdb[0],
+                " which is affected by drive geometry. Current settings are ",
+                (int)img.sectorsPerTrack, " sectors x ", (int)img.headsPerCylinder, " heads = ",
+                (int)sectorsPerHeadTrack, " but image size of ", (int)img.scsiSectors,
+                " sectors is not divisible. This can cause error messages in diagnostics tools.");
+            img.geometrywarningprinted = true;
+        }
+    }
+}
+
 // Set target configuration to default values
 static void scsiDiskConfigDefaults(int target_idx)
 {
@@ -521,6 +532,19 @@ void scsiDiskLoadConfig(int target_idx)
     }
 }
 
+bool scsiDiskCheckAnyImagesConfigured()
+{
+    for (int i = 0; i < S2S_MAX_TARGETS; i++)
+    {
+        if (g_DiskImages[i].file.isOpen() && (g_DiskImages[i].scsiId & S2S_CFG_TARGET_ENABLED))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 /*******************************/
 /* Config handling for SCSI2SD */
 /*******************************/
@@ -1590,6 +1614,22 @@ void scsiDiskPoll()
     {
         diskDataOut();
     }
+
+    if (scsiDev.phase == STATUS && scsiDev.target)
+    {
+        // Check if the command is affected by drive geometry.
+        // Affected commands are:
+        // 0x1A MODE SENSE command of pages 0x03 (device format), 0x04 (disk geometry) or 0x3F (all pages)
+        // 0x1C RECEIVE DIAGNOSTICS RESULTS
+        uint8_t command = scsiDev.cdb[0];
+        uint8_t pageCode = scsiDev.cdb[2] & 0x3F;
+        if ((command == 0x1A && (pageCode == 0x03 || pageCode == 0x04 || pageCode == 0x3F)) ||
+            command == 0x1C)
+        {
+            image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
+            checkDiskGeometryDivisible(img);
+        }
+    }
 }
 
 extern "C"

+ 3 - 0
src/ZuluSCSI_disk.h

@@ -16,3 +16,6 @@ extern "C" {
 void scsiDiskResetImages();
 bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int scsi_lun, int blocksize, bool is_cd, bool is_fd);
 void scsiDiskLoadConfig(int target_idx);
+
+// Returns true if there is at least one image active
+bool scsiDiskCheckAnyImagesConfigured();