Răsfoiți Sursa

Merge pull request #80 from BlueSCSI/InitiatorModeSupport

Initiator mode support
Eric Helgeson 2 ani în urmă
părinte
comite
e6e8479913

+ 120 - 104
lib/BlueSCSI_platform_RP2040/BlueSCSI_platform.cpp

@@ -20,14 +20,38 @@
 #endif
 #include <pico/multicore.h>
 #include "scsi_accel_rp2040.h"
+#include "hardware/i2c.h"
 
 extern "C" {
 #include <pico/cyw43_arch.h>
 
 const char *g_platform_name = PLATFORM_NAME;
 static bool g_scsi_initiator = false;
+static bool g_supports_initiator = false;
 static uint32_t g_flash_chip_size = 0;
 static bool g_uart_initialized = false;
+SCSI_PINS scsi_pins = {  // Default values, to be tweaked later as needed
+    .OUT_IO = SCSI_OUT_IO,
+    .OUT_CD = SCSI_OUT_CD,
+    .OUT_REQ = SCSI_OUT_REQ,
+    .OUT_SEL = SCSI_OUT_SEL,
+    .OUT_MSG = SCSI_OUT_MSG,
+    .OUT_RST = SCSI_OUT_RST,
+    .OUT_BSY = SCSI_OUT_BSY,
+    .OUT_ACK = SCSI_OUT_ACK,
+
+    .IN_IO = SCSI_IN_IO,
+    .IN_CD = SCSI_IN_CD,
+    .IN_MSG = SCSI_IN_MSG,
+    .IN_REQ = SCSI_IN_REQ,
+    .IN_SEL = SCSI_IN_SEL,
+    .IN_BSY = SCSI_IN_BSY,
+    .IN_RST = SCSI_IN_RST,
+    .IN_ACK = SCSI_IN_ACK,
+    .IN_ATN = SCSI_IN_ATN,
+
+    .SCSI_ACCEL_PINMASK = SCSI_ACCEL_SETPINS
+};
 
 #ifdef MBED
 void mbed_error_hook(const mbed_error_ctx * error_context);
@@ -93,46 +117,69 @@ void platform_init()
     // Make sure second core is stopped
     multicore_reset_core1();
 
+    // Default debug logging to disabled
+    g_log_debug = false;
+
+    // Report platform and firmware version
+    log("Platform: ", g_platform_name);
+    log("FW Version: ", g_log_firmwareversion);
+
     /* First configure the pins that affect external buffer directions.
      * RP2040 defaults to pulldowns, while these pins have external pull-ups.
      */
     //        pin             function       pup   pdown  out    state fast
     gpio_conf(SCSI_DATA_DIR,  GPIO_FUNC_SIO, false,false, true,  false, true);
-    //gpio_conf(SCSI_OUT_RST,   GPIO_FUNC_SIO, false,false, true,  true, true);
-    gpio_conf(SCSI_OUT_BSY,   GPIO_FUNC_SIO, false,false, true,  true, false);   
+    
+    gpio_conf(scsi_pins.OUT_BSY,   GPIO_FUNC_SIO, false,false, true,  true, false);   
     //gpio_set_drive_strength(SCSI_OUT_BSY, GPIO_DRIVE_STRENGTH_8MA);
-    gpio_conf(SCSI_OUT_SEL,   GPIO_FUNC_SIO, false,false, true,  true, false);
-
-    /* Check dip switch settings */
-    // Option switches: S1 is iATN, S2 is iACK
-    gpio_conf(SCSI_IN_ACK,    GPIO_FUNC_SIO, false, false, false, false, false);
-    gpio_conf(SCSI_IN_ATN,    GPIO_FUNC_SIO, false, false, false, false, false);
-    delay(10); /// Settle time
-    (void)!gpio_get(SCSI_IN_ATN); // S1
-    (void)!gpio_get(SCSI_IN_ACK); // S2
-
-    /* Initialize logging to SWO pin (UART0) */
-    gpio_conf(SWO_PIN,        GPIO_FUNC_UART,false,false, true,  false, true);
-    uart_init(uart0, 115200);
-    g_uart_initialized = true;
+    gpio_conf(scsi_pins.OUT_SEL,   GPIO_FUNC_SIO, true, false, true,  true, false);
+    gpio_conf(scsi_pins.OUT_ACK,   GPIO_FUNC_SIO, true, false, true,  true, false);
+    gpio_conf(scsi_pins.OUT_IO,    GPIO_FUNC_SIO, true, false, true,  true, false);
+    gpio_conf(scsi_pins.OUT_REQ,   GPIO_FUNC_SIO, true, false, true,  true, false);
+
+    // Determine whether I2C is supported
+    // If G16 and G17 are high, this is the 2023_09a revision or later desktop board
+    gpio_conf(GPIO_I2C_SCL,   GPIO_FUNC_I2C, false, false, false,  false, true);
+    gpio_conf(GPIO_I2C_SDA,   GPIO_FUNC_I2C, false, false, false,  false, true);
+    delay(10);
+    bool d50_2023_09a = gpio_get(GPIO_I2C_SCL) && gpio_get(GPIO_I2C_SDA);
+
+    if (d50_2023_09a) {
+        log("I2C Supported");
+        g_supports_initiator = true;
+        gpio_conf(GPIO_I2C_SCL,   GPIO_FUNC_I2C, true, false, false,  true, true);
+        gpio_conf(GPIO_I2C_SDA,   GPIO_FUNC_I2C, true, false, false,  true, true);
+
+        // Use Pico SDK methods
+        gpio_set_function(GPIO_I2C_SCL, GPIO_FUNC_I2C);
+        gpio_set_function(GPIO_I2C_SDA, GPIO_FUNC_I2C);
+        // gpio_pull_up(GPIO_I2C_SCL);  // TODO necessary?
+        // gpio_pull_up(GPIO_I2C_SDA);
+    } else {
+        /* Check option switch settings */
+        // Option switches: S1 is iATN, S2 is iACK
+        gpio_conf(scsi_pins.IN_ACK,    GPIO_FUNC_SIO, true, false, false, false, false);
+        gpio_conf(scsi_pins.IN_ATN,    GPIO_FUNC_SIO, false, false, false, false, false);
+        delay(10); /// Settle time
+        // Check option switches
+        bool optionS1 = !gpio_get(scsi_pins.IN_ATN);
+        bool optionS2 = !gpio_get(scsi_pins.IN_ACK);
+
+        // Reset REQ to appropriate pin for older hardware
+        scsi_pins.OUT_REQ = SCSI_OUT_REQ_BEFORE_2023_09a;
+        scsi_pins.SCSI_ACCEL_PINMASK = SCSI_ACCEL_SETPINS_PRE09A;
+
+        // Initialize logging to SWO pin (UART0) 
+        gpio_conf(SWO_PIN,        GPIO_FUNC_UART,false,false, true,  false, true);
+        uart_init(uart0, 1000000);
+        g_uart_initialized = true;
     #ifdef MBED
-    mbed_set_error_hook(mbed_error_hook);
+        mbed_set_error_hook(mbed_error_hook);
     #endif
+    }
+    // TODO Disable I2C if debug logging is enabled later?  Switch to Serial output mode?
 
     //log("DIP switch settings: debug log ", (int)dbglog, ", termination ", (int)termination);
-    log("Platform: ", g_platform_name);
-    log("FW Version: ", g_log_firmwareversion);
-
-    g_log_debug = false; // Debug logging can be handled with a debug firmware, very easy to reflash
-
-    // if (termination)  // Termination is handled by hardware jumper
-    // {
-    //     log("SCSI termination is enabled");
-    // }
-    // else
-    // {
-    //     log("NOTE: SCSI termination is disabled");
-    // }
 
 #ifdef ENABLE_AUDIO_OUTPUT
     log("SP/DIF audio to expansion header enabled");
@@ -179,57 +226,10 @@ void platform_init()
 #endif
 }
 
-static bool read_initiator_dip_switch()
-{
-    /* Revision 2022d hardware has problems reading initiator DIP switch setting.
-     * The 74LVT245 hold current is keeping the GPIO_ACK state too strongly.
-     * Detect this condition by toggling the pin up and down and seeing if it sticks.
-     */
-
-    // Strong output high, then pulldown
-    //        pin             function       pup   pdown   out    state  fast
-    //gpio_conf(DIP_INITIATOR,  GPIO_FUNC_SIO, false, false, true,  true,  false);
-    //gpio_conf(DIP_INITIATOR,  GPIO_FUNC_SIO, false, true,  false, true,  false);
-    //delay(1);
-    //bool initiator_state1 = gpio_get(DIP_INITIATOR);
-
-    // Strong output low, then pullup
-    //        pin             function       pup   pdown   out    state  fast
-    //gpio_conf(DIP_INITIATOR,  GPIO_FUNC_SIO, false, false, true,  false, false);
-    //gpio_conf(DIP_INITIATOR,  GPIO_FUNC_SIO, true,  false, false, false, false);
-    //delay(1);
-    //bool initiator_state2 = gpio_get(DIP_INITIATOR);
-
-    //if (initiator_state1 == initiator_state2)
-    //{
-        // Ok, was able to read the state directly
-        //return !initiator_state1;
-    //}
-
-    // Enable OUT_BSY for a short time.
-    // If in target mode, this will force GPIO_ACK high.
-    gpio_put(SCSI_OUT_BSY, 0);
-    delay_100ns();
-    gpio_put(SCSI_OUT_BSY, 1);
-
-    //return !gpio_get(DIP_INITIATOR);
-    return false;
-}
 
 // late_init() only runs in main application, SCSI not needed in bootloader
 void platform_late_init()
 {
-    if (read_initiator_dip_switch())
-    {
-        g_scsi_initiator = true;
-        log("SCSI initiator mode selected by DIP switch, expecting SCSI disks on the bus");
-    }
-    else
-    {
-        g_scsi_initiator = false;
-        // log("SCSI target/disk mode selected by DIP switch, acting as a SCSI disk");
-    }
-
     /* Initialize SCSI pins to required modes.
      * SCSI pins should be inactive / input at this point.
      */
@@ -252,31 +252,31 @@ void platform_late_init()
         // Act as SCSI device / target
 
         // SCSI control outputs
-        //        pin             function       pup   pdown  out    state fast
-        gpio_conf(SCSI_OUT_IO,    GPIO_FUNC_SIO, false,false, true,  true, true);
-        gpio_conf(SCSI_OUT_MSG,   GPIO_FUNC_SIO, false,false, true,  true, true);
+        //        pin                   function       pup   pdown  out    state fast
+        gpio_conf(scsi_pins.OUT_IO,    GPIO_FUNC_SIO, false,false, true,  true, true);
+        gpio_conf(scsi_pins.OUT_MSG,   GPIO_FUNC_SIO, false,false, true,  true, true);
 
         // REQ pin is switched between PIO and SIO, pull-up makes sure no glitches
-        gpio_conf(SCSI_OUT_REQ,   GPIO_FUNC_SIO, true ,false, true,  true, true);
+        gpio_conf(scsi_pins.OUT_REQ,   GPIO_FUNC_SIO, true ,false, true,  true, true);
 
         // Shared pins are changed to input / output depending on communication phase
-        gpio_conf(SCSI_IN_SEL,    GPIO_FUNC_SIO, true, false, false, true, true);
-        if (SCSI_OUT_CD != SCSI_IN_SEL)
+        gpio_conf(scsi_pins.IN_SEL,    GPIO_FUNC_SIO, true, false, false, true, true);
+        if (scsi_pins.OUT_CD != scsi_pins.IN_SEL)
         {
-            gpio_conf(SCSI_OUT_CD,    GPIO_FUNC_SIO, false,false, true,  true, true);
+            gpio_conf(scsi_pins.OUT_CD,    GPIO_FUNC_SIO, false,false, true,  true, true);
         }
 
-        gpio_conf(SCSI_IN_BSY,    GPIO_FUNC_SIO, true, false, false, true, true);
-        if (SCSI_OUT_MSG != SCSI_IN_BSY)
+        gpio_conf(scsi_pins.IN_BSY,    GPIO_FUNC_SIO, true, false, false, true, true);
+        if (scsi_pins.OUT_MSG != scsi_pins.IN_BSY)
         {
-            gpio_conf(SCSI_OUT_MSG,    GPIO_FUNC_SIO, false,false, true,  true, true);
+            gpio_conf(scsi_pins.OUT_MSG,    GPIO_FUNC_SIO, false,false, true,  true, true);
         }
 
         // SCSI control inputs
-        //        pin             function       pup   pdown  out    state fast
-        gpio_conf(SCSI_IN_ACK,    GPIO_FUNC_SIO, false, false, false, true, false);
-        gpio_conf(SCSI_IN_ATN,    GPIO_FUNC_SIO, false, false, false, true, false);
-        gpio_conf(SCSI_IN_RST,    GPIO_FUNC_SIO, true, false, false, true, false);
+        //        pin                   function       pup   pdown  out    state fast
+        gpio_conf(scsi_pins.IN_ACK,    GPIO_FUNC_SIO, true, false, false, true, false);
+        gpio_conf(scsi_pins.IN_ATN,    GPIO_FUNC_SIO, false, false, false, true, false);
+        gpio_conf(scsi_pins.IN_RST,    GPIO_FUNC_SIO, true, false, false, true, false);
 
 #ifdef ENABLE_AUDIO_OUTPUT
         // one-time control setup for DMA channels and second core
@@ -285,18 +285,27 @@ void platform_late_init()
     }
     else
     {
-        // Act as SCSI initiator
-
-        //        pin             function       pup   pdown  out    state fast
-        gpio_conf(SCSI_IN_IO,     GPIO_FUNC_SIO, true ,false, false, true, false);
-        gpio_conf(SCSI_IN_MSG,    GPIO_FUNC_SIO, true ,false, false, true, false);
-        gpio_conf(SCSI_IN_CD,     GPIO_FUNC_SIO, true ,false, false, true, false);
-        gpio_conf(SCSI_IN_REQ,    GPIO_FUNC_SIO, true ,false, false, true, false);
-        gpio_conf(SCSI_IN_BSY,    GPIO_FUNC_SIO, true, false, false, true, false);
-        gpio_conf(SCSI_IN_RST,    GPIO_FUNC_SIO, true, false, false, true, false);
-        gpio_conf(SCSI_OUT_SEL,   GPIO_FUNC_SIO, false,false, true,  true, true);
-        gpio_conf(SCSI_OUT_ACK,   GPIO_FUNC_SIO, false,false, true,  true, true);
-        gpio_conf(SCSI_OUT_ATN,   GPIO_FUNC_SIO, false,false, true,  true, true);
+        // Act as SCSI Initiator
+
+        //        pin                   function       pup   pdown  out    state fast
+        gpio_conf(scsi_pins.IN_IO,     GPIO_FUNC_SIO, true ,false, false, true, false);
+        gpio_conf(scsi_pins.IN_MSG,    GPIO_FUNC_SIO, true ,false, false, true, false);
+        gpio_conf(scsi_pins.IN_CD,     GPIO_FUNC_SIO, true ,false, false, true, false);
+        gpio_conf(scsi_pins.IN_REQ,    GPIO_FUNC_SIO, true ,false, false, true, false);
+        gpio_conf(scsi_pins.IN_BSY,    GPIO_FUNC_SIO, true, false, false, true, false);
+        gpio_conf(scsi_pins.IN_RST,    GPIO_FUNC_SIO, true, false, false, true, false);
+        gpio_conf(scsi_pins.OUT_SEL,   GPIO_FUNC_SIO, false,false, true,  true, true);
+        gpio_conf(scsi_pins.OUT_ACK,   GPIO_FUNC_SIO, true,false, true,  true, true);
+        //gpio_conf(SCSI_OUT_ATN,   GPIO_FUNC_SIO, false,false, true,  true, true);  // ATN output is unused
+    }
+}
+
+void platform_enable_initiator_mode() {
+    if (g_supports_initiator) {
+        g_scsi_initiator = true;
+        log("SCSI Initiator Mode.  Will scan the bus for drives to image.");
+    } else {
+        log("SCSI Initiator Mode requested, but not supported.");
     }
 }
 
@@ -621,7 +630,9 @@ void platform_poll()
 #endif
 }
 
-uint8_t platform_get_buttons()
+uint8_t platform_get_buttons() {return 0;}
+// TODO figure this out
+/*uint8_t platform_get_buttons()
 {
     uint8_t buttons = 0;
 
@@ -649,6 +660,11 @@ uint8_t platform_get_buttons()
     }
 
     return buttons_debounced;
+}*/
+
+// Used by setup methods to determine which hardware version is in use
+bool is202309a() {
+    return scsi_pins.OUT_REQ == SCSI_OUT_REQ;
 }
 
 /*****************************************/

+ 34 - 18
lib/BlueSCSI_platform_RP2040/BlueSCSI_platform.h

@@ -26,6 +26,8 @@ extern const char *g_platform_name;
 #define PLATFORM_VDD_WARNING_LIMIT_mV 2800
 #endif
 
+extern SCSI_PINS scsi_pins;
+
 // NOTE: The driver supports synchronous speeds higher than 10MB/s, but this
 // has not been tested due to lack of fast enough SCSI adapter.
 // #define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_TURBO
@@ -61,6 +63,9 @@ void platform_late_init();
 // Disable the status LED
 void platform_disable_led(void);
 
+// Enables initiator mode
+void platform_enable_initiator_mode();
+
 // Query whether initiator mode is enabled on targets with PLATFORM_HAS_INITIATOR_MODE
 bool platform_is_initiator_mode_enabled();
 
@@ -79,6 +84,9 @@ void platform_poll();
 // This function should return without significantly delay.
 uint8_t platform_get_buttons();
 
+// Platform method to determine whether this is a certain hardware version
+bool is202309a();
+
 // Set callback that will be called during data transfer to/from SD card.
 // This can be used to implement simultaneous transfer to SCSI bus.
 typedef void (*sd_callback_t)(uint32_t bytes_complete);
@@ -131,26 +139,34 @@ int platform_network_wifi_channel();
 // Write a single SCSI pin.
 // Example use: SCSI_OUT(ATN, 1) sets SCSI_ATN to low (active) state.
 #define SCSI_OUT(pin, state) \
-    *(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (SCSI_OUT_ ## pin)
+    *(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (scsi_pins.OUT_ ## pin)
 
 // Read a single SCSI pin.
 // Example use: SCSI_IN(ATN), returns 1 for active low state.
 #define SCSI_IN(pin) \
-    ((sio_hw->gpio_in & (1 << (SCSI_IN_ ## pin))) ? 0 : 1)
+    ((sio_hw->gpio_in & (1 << (scsi_pins.IN_ ## pin))) ? 0 : 1)
 
 // Set pin directions for initiator vs. target mode
 #define SCSI_ENABLE_INITIATOR() \
     (sio_hw->gpio_oe_set = (1 << SCSI_OUT_ACK) | \
-                           (1 << SCSI_OUT_ATN)), \
+                           (1 << SCSI_OUT_SEL)), \
     (sio_hw->gpio_oe_clr = (1 << SCSI_IN_IO) | \
                            (1 << SCSI_IN_CD) | \
                            (1 << SCSI_IN_MSG) | \
-                           (1 << SCSI_IN_REQ))
+                           (1 << SCSI_OUT_REQ))
+
+#define SCSI_RELEASE_INITIATOR() \
+    (sio_hw->gpio_oe_clr = (1 << scsi_pins.OUT_ACK) | \
+                           (1 << scsi_pins.OUT_SEL)), \
+    (sio_hw->gpio_oe_set = (1 << scsi_pins.IN_IO) | \
+                           (1 << scsi_pins.IN_CD) | \
+                           (1 << scsi_pins.IN_MSG) | \
+                           (1 << scsi_pins.IN_REQ))
 
 // Enable driving of shared control pins
 #define SCSI_ENABLE_CONTROL_OUT() \
-    (sio_hw->gpio_oe_set = (1 << SCSI_OUT_CD) | \
-                           (1 << SCSI_OUT_MSG))
+    (sio_hw->gpio_oe_set = (1 << scsi_pins.OUT_CD) | \
+                           (1 << scsi_pins.OUT_MSG))
 
 // Set SCSI data bus to output
 #define SCSI_ENABLE_DATA_OUT() \
@@ -159,29 +175,29 @@ int platform_network_wifi_channel();
 
 // Write SCSI data bus, also sets REQ to inactive.
 #define SCSI_OUT_DATA(data) \
-    gpio_put_masked(SCSI_IO_DATA_MASK | (1 << SCSI_OUT_REQ), \
-                    g_scsi_parity_lookup[(uint8_t)(data)] | (1 << SCSI_OUT_REQ)), \
+    gpio_put_masked(SCSI_IO_DATA_MASK | (1 << scsi_pins.OUT_REQ), \
+                    g_scsi_parity_lookup[(uint8_t)(data)] | (1 << scsi_pins.OUT_REQ)), \
     SCSI_ENABLE_DATA_OUT()
 
 // Release SCSI data bus and REQ signal
 #define SCSI_RELEASE_DATA_REQ() \
     (sio_hw->gpio_oe_clr = SCSI_IO_DATA_MASK, \
      sio_hw->gpio_clr = (1 << SCSI_DATA_DIR), \
-     sio_hw->gpio_set = ((1 << SCSI_OUT_REQ)))
+     sio_hw->gpio_set = ((1 << scsi_pins.OUT_REQ)))
 
 // Release all SCSI outputs
 #define SCSI_RELEASE_OUTPUTS() \
     SCSI_RELEASE_DATA_REQ(), \
-    sio_hw->gpio_set = (1 << SCSI_OUT_IO) | \
-                       (1 << SCSI_OUT_CD) | \
-                       (1 << SCSI_OUT_MSG) | \
-                       (1 << SCSI_OUT_RST) | \
-                       (1 << SCSI_OUT_BSY) | \
-                       (1 << SCSI_OUT_REQ) | \
-                       (1 << SCSI_OUT_SEL), \
+    sio_hw->gpio_set = (1 << scsi_pins.OUT_IO) | \
+                       (1 << scsi_pins.OUT_CD) | \
+                       (1 << scsi_pins.OUT_MSG) | \
+                       (1 << scsi_pins.OUT_RST) | \
+                       (1 << scsi_pins.OUT_BSY) | \
+                       (1 << scsi_pins.OUT_REQ) | \
+                       (1 << scsi_pins.OUT_SEL), \
                        delay(1), \
-    sio_hw->gpio_oe_clr = (1 << SCSI_OUT_CD) | \
-                          (1 << SCSI_OUT_MSG)
+    sio_hw->gpio_oe_clr = (1 << scsi_pins.OUT_CD) | \
+                          (1 << scsi_pins.OUT_MSG)
 
 // Read SCSI data bus
 #define SCSI_IN_DATA() \

+ 43 - 19
lib/BlueSCSI_platform_RP2040/BlueSCSI_platform_gpio.h

@@ -22,35 +22,37 @@
 
 // Data direction control
 #define SCSI_DATA_DIR 9
+#define SCSI_OUT_DIRECTIONPIN 9
 
 // SCSI control lines
-#define SCSI_OUT_IO   22  // Used to be 16
-#define SCSI_OUT_REQ  17
+#define SCSI_OUT_IO   22
+#define SCSI_OUT_REQ  19
+#define SCSI_OUT_REQ_BEFORE_2023_09a  17
 
-#define SCSI_OUT_CD   18  // TODO hardware design
+#define SCSI_OUT_CD   18
 #define SCSI_IN_SEL  18
 
-#define SCSI_OUT_SEL  19
+#define SCSI_OUT_SEL  21
 
 #define SCSI_OUT_MSG  20
-#define SCSI_IN_BSY  20  // TODO hardware design
+#define SCSI_IN_BSY  20
 
 #define SCSI_IN_RST  21
-#define SCSI_OUT_RST  22  // Same as IO currently, not initialized or used
+#define SCSI_OUT_RST  22  // No RST pin, manual or expander only
 
 #define SCSI_IN_ACK  26
 #define SCSI_OUT_BSY  27
 #define SCSI_IN_ATN  28
 
 // Status line outputs for initiator mode
-#define SCSI_OUT_ACK  10
-#define SCSI_OUT_ATN  29
+#define SCSI_OUT_ACK  26
+//#define SCSI_OUT_ATN  29  // ATN output is unused
 
 // Status line inputs for initiator mode
-#define SCSI_IN_IO    12
-#define SCSI_IN_CD    11
-#define SCSI_IN_MSG   13
-#define SCSI_IN_REQ   9
+#define SCSI_IN_IO    22
+#define SCSI_IN_CD    18
+#define SCSI_IN_MSG   28
+#define SCSI_IN_REQ   19
 
 // Status LED pins
 #define LED_PIN      25
@@ -75,13 +77,35 @@
 #define SD_SPI_CS    15
 
 // IO expander I2C
-// #define GPIO_I2C_SDA 14
-// #define GPIO_I2C_SCL 15
-
-// DIP switch pins
-// #define DIP_INITIATOR 10
-// #define DIP_DBGLOG 28
-// #define DIP_TERM 9
+#define GPIO_I2C_SDA 16
+#define GPIO_I2C_SCL 17
 
 // Other pins
 #define SWO_PIN 16
+
+#define SCSI_ACCEL_SETPINS 0x801FF
+#define SCSI_ACCEL_SETPINS_PRE09A 0x201FF
+
+typedef struct __attribute__((packed))
+{
+	uint8_t OUT_IO;
+	uint8_t OUT_CD;
+	uint8_t OUT_REQ;
+	uint8_t OUT_SEL;
+	uint8_t OUT_MSG;
+	uint8_t OUT_RST;
+	uint8_t OUT_BSY;
+	uint8_t OUT_ACK;
+
+	uint8_t IN_IO;
+	uint8_t IN_CD;
+	uint8_t IN_MSG;
+	uint8_t IN_REQ;
+	uint8_t IN_SEL;
+	uint8_t IN_BSY;
+	uint8_t IN_RST;
+	uint8_t IN_ACK;
+	uint8_t IN_ATN;
+
+	uint32_t SCSI_ACCEL_PINMASK;
+} SCSI_PINS;

+ 5 - 7
lib/BlueSCSI_platform_RP2040/scsiHostPhy.cpp

@@ -31,12 +31,14 @@ void scsiHostPhyReset(void)
 // Returns true if the target answers to selection request.
 bool scsiHostPhySelect(int target_id)
 {
+    SCSI_ENABLE_INITIATOR();
     SCSI_RELEASE_OUTPUTS();
 
     // We can't write individual data bus bits, so use a bit modified
     // arbitration scheme. We always yield to any other initiator on
     // the bus.
     scsiLogInitiatorPhaseChange(BUS_BUSY);
+    SCSI_OUT(REQ, 0);
     SCSI_OUT(BSY, 1);
     for (int wait = 0; wait < 10; wait++)
     {
@@ -80,10 +82,9 @@ bool scsiHostPhySelect(int target_id)
         return false;
     }
 
-    // We need to assert OUT_BSY to enable IO buffer U105 to read status signals.
     SCSI_RELEASE_DATA_REQ();
-    SCSI_OUT(BSY, 1);
     SCSI_OUT(SEL, 0);
+    SCSI_ENABLE_INITIATOR();
     return true;
 }
 
@@ -107,8 +108,7 @@ int scsiHostPhyGetPhase()
 
     if (phase == 0 && absolute_time_diff_us(last_online_time, get_absolute_time()) > 100)
     {
-        // Disable OUT_BSY for a short time to see if the target is still on line
-        SCSI_OUT(BSY, 0);
+        // BlueSCSI doesn't need to assert OUT_BSY to check whether the bus is in use
         delayMicroseconds(1);
 
         if (!SCSI_IN(BSY))
@@ -116,9 +116,6 @@ int scsiHostPhyGetPhase()
             scsiLogInitiatorPhaseChange(BUS_FREE);
             return BUS_FREE;
         }
-
-        // Still online, re-enable OUT_BSY to enable IO buffers
-        SCSI_OUT(BSY, 1);
         last_online_time = get_absolute_time();
     }
     else if (phase != 0)
@@ -266,4 +263,5 @@ void scsiHostPhyRelease()
 {
     scsiLogInitiatorPhaseChange(BUS_FREE);
     SCSI_RELEASE_OUTPUTS();
+    SCSI_RELEASE_DATA_REQ();
 }

+ 13 - 12
lib/BlueSCSI_platform_RP2040/scsiPhy.cpp

@@ -117,17 +117,18 @@ static void scsi_rst_assert_interrupt()
 
 static void scsiPhyIRQ(uint gpio, uint32_t events)
 {
-    if (gpio == SCSI_IN_BSY || gpio == SCSI_IN_SEL)
+    if (gpio == scsi_pins.IN_BSY || gpio == scsi_pins.IN_SEL)
     {
         // Note BSY / SEL interrupts only when we are not driving OUT_BSY low ourselves.
         // The BSY input pin may be shared with other signals.
-        if (sio_hw->gpio_out & (1 << SCSI_OUT_BSY))
+        if (sio_hw->gpio_out & (1 << scsi_pins.OUT_BSY))
         {
             scsi_bsy_deassert_interrupt();
         }
     }
-    else if (gpio == SCSI_IN_RST)
+    else if (gpio == scsi_pins.IN_RST && ((~sio_hw->gpio_oe) & (1 << scsi_pins.OUT_SEL)))
     {
+        // If oSEL is in input mode, this is a real reset.  Otherwise ignore.
         scsi_rst_assert_interrupt();
     }
 }
@@ -144,14 +145,14 @@ extern "C" void scsiPhyReset(void)
 
     // Enable BSY, RST and SEL interrupts
     // Note: RP2040 library currently supports only one callback,
-    // so it has to be same for both pins.
-    gpio_set_irq_enabled_with_callback(SCSI_IN_BSY, GPIO_IRQ_EDGE_RISE, true, scsiPhyIRQ);
-    gpio_set_irq_enabled(SCSI_IN_RST, GPIO_IRQ_EDGE_FALL, true);
+    // so it has to be same for all pins.
+    gpio_set_irq_enabled_with_callback(scsi_pins.IN_BSY, GPIO_IRQ_EDGE_RISE, true, scsiPhyIRQ);
+    gpio_set_irq_enabled(scsi_pins.IN_RST, GPIO_IRQ_EDGE_FALL, true);
 
     // Check BSY line status when SEL goes active.
     // This is needed to handle SCSI-1 hosts that use the single initiator mode.
     // The host will just assert the SEL directly, without asserting BSY first.
-    gpio_set_irq_enabled(SCSI_IN_SEL, GPIO_IRQ_EDGE_FALL, true);
+    gpio_set_irq_enabled(scsi_pins.IN_SEL, GPIO_IRQ_EDGE_FALL, true);
 }
 
 /************************/
@@ -223,11 +224,11 @@ extern "C" uint32_t scsiEnterPhaseImmediate(int phase)
             // To avoid unnecessary delays, precalculate an XOR mask and then apply it
             // simultaneously to all three signals.
             uint32_t gpio_new = 0;
-            if (!(phase & __scsiphase_msg)) { gpio_new |= (1 << SCSI_OUT_MSG); }
-            if (!(phase & __scsiphase_cd)) { gpio_new |= (1 << SCSI_OUT_CD); }
-            if (!(phase & __scsiphase_io)) { gpio_new |= (1 << SCSI_OUT_IO); }
+            if (!(phase & __scsiphase_msg)) { gpio_new |= (1 << scsi_pins.OUT_MSG); }
+            if (!(phase & __scsiphase_cd)) { gpio_new |= (1 << scsi_pins.OUT_CD); }
+            if (!(phase & __scsiphase_io)) { gpio_new |= (1 << scsi_pins.OUT_IO); }
 
-            uint32_t mask = (1 << SCSI_OUT_MSG) | (1 << SCSI_OUT_CD) | (1 << SCSI_OUT_IO);
+            uint32_t mask = (1 << scsi_pins.OUT_MSG) | (1 << scsi_pins.OUT_CD) | (1 << scsi_pins.OUT_IO);
             uint32_t gpio_xor = (sio_hw->gpio_out ^ gpio_new) & mask;
             sio_hw->gpio_togl = gpio_xor;
             SCSI_ENABLE_CONTROL_OUT();
@@ -297,7 +298,7 @@ static inline void scsiWriteOneByte(uint8_t value)
 {
     SCSI_OUT_DATA(value);
     delay_100ns(); // DB setup time before REQ
-    gpio_acknowledge_irq(SCSI_IN_ACK, GPIO_IRQ_EDGE_FALL);
+    gpio_acknowledge_irq(scsi_pins.IN_ACK, GPIO_IRQ_EDGE_FALL);
     SCSI_OUT(REQ, 1);
     SCSI_WAIT_ACTIVE_EDGE(ACK);
     SCSI_RELEASE_DATA_REQ();

+ 1 - 1
lib/BlueSCSI_platform_RP2040/scsi_accel.pio

@@ -5,7 +5,7 @@
 ; -   8: DBP
 ; Side set is REQ pin
 
-.define REQ 17  ; was 9
+.define REQ 19  ; was 17
 .define ACK 26  ; was 10
 
 ; Delay from data setup to REQ assertion.

+ 9 - 3
lib/BlueSCSI_platform_RP2040/scsi_accel_host.cpp

@@ -36,14 +36,19 @@ static void scsi_accel_host_config_gpio()
         iobank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_SIO;
+        iobank0_hw->io[SCSI_IN_REQ].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_OUT_ACK].ctrl = GPIO_FUNC_SIO;
     }
     else if (g_scsi_host_state == SCSIHOST_READ)
     {
         // Data bus and REQ as input, ACK pin as output
-        pio_sm_set_pins(SCSI_PIO, SCSI_SM, 0x7FF);
-        pio_sm_set_consecutive_pindirs(SCSI_PIO, SCSI_SM, 0, 10, false);
-        pio_sm_set_consecutive_pindirs(SCSI_PIO, SCSI_SM, 10, 1, true);
+        // 100000010000000000111111111
+        // ACK    REQ        PDB
+        //
+        pio_sm_set_pins(SCSI_PIO, SCSI_SM, 0x40801FF);
+        pio_sm_set_consecutive_pindirs(SCSI_PIO, SCSI_SM, 0, 9, false);  // DBP Input
+        pio_sm_set_consecutive_pindirs(SCSI_PIO, SCSI_SM, SCSI_IN_REQ, 1, false);  // REQ Input
+        pio_sm_set_consecutive_pindirs(SCSI_PIO, SCSI_SM, SCSI_OUT_ACK, 1, true);  // ACK Output
 
         iobank0_hw->io[SCSI_IO_DB0].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DB1].ctrl  = GPIO_FUNC_SIO;
@@ -54,6 +59,7 @@ static void scsi_accel_host_config_gpio()
         iobank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_SIO;
+        //iobank0_hw->io[SCSI_IN_REQ].ctrl = GPIO_FUNC_PIO0;
         iobank0_hw->io[SCSI_OUT_ACK].ctrl = GPIO_FUNC_PIO0;
     }
 }

+ 2 - 2
lib/BlueSCSI_platform_RP2040/scsi_accel_host.pio

@@ -5,8 +5,8 @@
 ; -   8: DBP
 ; Side set is ACK pin
 
-.define REQ 9
-.define ACK 10
+.define REQ 19
+.define ACK 26
 
 ; Read from SCSI bus using asynchronous handshake.
 ; Data is returned as 16-bit words that contain the 8 data bits + 1 parity bit.

+ 2 - 3
lib/BlueSCSI_platform_RP2040/scsi_accel_host.pio.h

@@ -19,10 +19,10 @@ static const uint16_t scsi_host_async_read_program_instructions[] = {
             //     .wrap_target
     0x90a0, //  0: pull   block           side 1     
     0xb027, //  1: mov    x, osr          side 1     
-    0x3009, //  2: wait   0 gpio, 9       side 1     
+    0x3013, //  2: wait   0 gpio, 19      side 1     
     0x4009, //  3: in     pins, 9         side 0     
     0x4067, //  4: in     null, 7         side 0     
-    0x2089, //  5: wait   1 gpio, 9       side 0     
+    0x2093, //  5: wait   1 gpio, 19      side 0     
     0x1042, //  6: jmp    x--, 2          side 1     
             //     .wrap
 };
@@ -41,4 +41,3 @@ static inline pio_sm_config scsi_host_async_read_program_get_default_config(uint
     return c;
 }
 #endif
-

+ 14 - 14
lib/BlueSCSI_platform_RP2040/scsi_accel_rp2040.cpp

@@ -762,20 +762,20 @@ static void scsidma_config_gpio()
         iobank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_SIO;
+        iobank0_hw->io[scsi_pins.OUT_REQ].ctrl = GPIO_FUNC_SIO;
     }
     else if (g_scsi_dma_state == SCSIDMA_WRITE)
     {
         // Make sure the initial state of all pins is high and output
-        pio_sm_set_pins(SCSI_DMA_PIO, SCSI_DATA_SM, 0x201FF);  // 3FF
+        pio_sm_set_pins(SCSI_DMA_PIO, SCSI_DATA_SM, scsi_pins.SCSI_ACCEL_PINMASK);
         // Binary of 0x3FF is is 0 0 1 1 11111111
         //                       ? A R P DBP
         // A = ACK, R = REQ, DBP are the data pins
         // REQ internal state needs to be set 'high'
-        // 100000000111111111
+        // 100000010000000000111111111
         // Probably right to left here, so 0 - 9 are set 'high' and 10/11 are set 'low'
         pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, 0, 9, true);
-        pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, 17, 1, true);
+        pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, scsi_pins.OUT_REQ, 1, true);
 
         iobank0_hw->io[SCSI_IO_DB0].ctrl  = GPIO_FUNC_PIO0;
         iobank0_hw->io[SCSI_IO_DB1].ctrl  = GPIO_FUNC_PIO0;
@@ -786,7 +786,7 @@ static void scsidma_config_gpio()
         iobank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_PIO0;
         iobank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_PIO0;
         iobank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
+        iobank0_hw->io[scsi_pins.OUT_REQ].ctrl = GPIO_FUNC_PIO0;
     }
     else if (g_scsi_dma_state == SCSIDMA_READ)
     {
@@ -794,16 +794,16 @@ static void scsidma_config_gpio()
         {
             // Asynchronous read
             // Data bus as input, REQ pin as output
-            pio_sm_set_pins(SCSI_DMA_PIO, SCSI_DATA_SM, 0x201FF);
+            pio_sm_set_pins(SCSI_DMA_PIO, SCSI_DATA_SM, scsi_pins.SCSI_ACCEL_PINMASK);
             pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, 0, 9, false);
-            pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, 17, 1, true);
+            pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, scsi_pins.OUT_REQ, 1, true);
         }
         else
         {
             // Synchronous read, REQ pin is written by SYNC_SM
-            pio_sm_set_pins(SCSI_DMA_PIO, SCSI_SYNC_SM, 0x201FF);
+            pio_sm_set_pins(SCSI_DMA_PIO, SCSI_SYNC_SM, scsi_pins.SCSI_ACCEL_PINMASK);
             pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, 0, 9, false);
-            pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_SYNC_SM, 17, 1, true);
+            pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_SYNC_SM, scsi_pins.OUT_REQ, 1, true);
         }
 
         iobank0_hw->io[SCSI_IO_DB0].ctrl  = GPIO_FUNC_SIO;
@@ -815,7 +815,7 @@ static void scsidma_config_gpio()
         iobank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_SIO;
         iobank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
+        iobank0_hw->io[scsi_pins.OUT_REQ].ctrl = GPIO_FUNC_PIO0;
     }
 }
 
@@ -870,7 +870,7 @@ void scsi_accel_rp2040_init()
     g_scsi_dma.pio_offset_async_write = pio_add_program(SCSI_DMA_PIO, &scsi_accel_async_write_program);
     g_scsi_dma.pio_cfg_async_write = scsi_accel_async_write_program_get_default_config(g_scsi_dma.pio_offset_async_write);
     sm_config_set_out_pins(&g_scsi_dma.pio_cfg_async_write, SCSI_IO_DB0, 9);
-    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_async_write, SCSI_OUT_REQ);
+    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_async_write, scsi_pins.OUT_REQ);
     sm_config_set_fifo_join(&g_scsi_dma.pio_cfg_async_write, PIO_FIFO_JOIN_TX);
     sm_config_set_out_shift(&g_scsi_dma.pio_cfg_async_write, true, false, 32);
 
@@ -883,7 +883,7 @@ void scsi_accel_rp2040_init()
     g_scsi_dma.pio_offset_sync_write = pio_add_program(SCSI_DMA_PIO, &scsi_sync_write_program);
     g_scsi_dma.pio_cfg_sync_write = scsi_sync_write_program_get_default_config(g_scsi_dma.pio_offset_sync_write);
     sm_config_set_out_pins(&g_scsi_dma.pio_cfg_sync_write, SCSI_IO_DB0, 9);
-    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_sync_write, SCSI_OUT_REQ);
+    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_sync_write, scsi_pins.OUT_REQ);
     sm_config_set_out_shift(&g_scsi_dma.pio_cfg_sync_write, true, true, 32);
     sm_config_set_in_shift(&g_scsi_dma.pio_cfg_sync_write, true, true, 1);
 
@@ -891,14 +891,14 @@ void scsi_accel_rp2040_init()
     g_scsi_dma.pio_offset_read = pio_add_program(SCSI_DMA_PIO, &scsi_accel_read_program);
     g_scsi_dma.pio_cfg_read = scsi_accel_read_program_get_default_config(g_scsi_dma.pio_offset_read);
     sm_config_set_in_pins(&g_scsi_dma.pio_cfg_read, SCSI_IO_DB0);
-    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_read, SCSI_OUT_REQ);
+    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_read, scsi_pins.OUT_REQ);
     sm_config_set_out_shift(&g_scsi_dma.pio_cfg_read, true, false, 32);
     sm_config_set_in_shift(&g_scsi_dma.pio_cfg_read, true, true, 32);
 
     // Synchronous SCSI read pacer
     g_scsi_dma.pio_offset_sync_read_pacer = pio_add_program(SCSI_DMA_PIO, &scsi_sync_read_pacer_program);
     g_scsi_dma.pio_cfg_sync_read_pacer = scsi_sync_read_pacer_program_get_default_config(g_scsi_dma.pio_offset_sync_read_pacer);
-    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_sync_read_pacer, SCSI_OUT_REQ);
+    sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_sync_read_pacer, scsi_pins.OUT_REQ);
 
     // Read parity check
     g_scsi_dma.pio_offset_read_parity = pio_add_program(SCSI_DMA_PIO, &scsi_read_parity_program);

+ 2 - 0
platformio.ini

@@ -33,6 +33,8 @@ build_flags =
     -DHAS_SDIO_CLASS
     -DUSE_ARDUINO=1
     -DNO_USB=1
+    -DPICO_DEFAULT_I2C_SDA_PIN=16
+    -DPICO_DEFAULT_I2C_SCL_PIN=17
 
 ; Experimental Audio build
 ; Rquires seperate hardware and overclock. 

+ 59 - 1
src/BlueSCSI.cpp

@@ -496,6 +496,10 @@ static void reinitSCSI()
   {
     g_log_debug = true;
   }
+  if (ini_getbool("SCSI", "TestMode", 0, CONFIGFILE))
+  {
+    g_test_mode = true;
+  }
 
 #ifdef PLATFORM_HAS_INITIATOR_MODE
   if (platform_is_initiator_mode_enabled())
@@ -550,7 +554,6 @@ extern "C" void bluescsi_setup(void)
   pio_clear_instruction_memory(pio0);
   pio_clear_instruction_memory(pio1);
   platform_init();
-  platform_late_init();
 
   g_sdcard_present = mountSDCard();
 
@@ -581,6 +584,11 @@ extern "C" void bluescsi_setup(void)
 
   if (g_sdcard_present)
   {
+    platform_late_init();
+    if (ini_getbool("SCSI", "InitiatorMode", false, CONFIGFILE))
+    {
+      platform_enable_initiator_mode();
+    }
     if (SD.clusterCount() == 0)
     {
       log("SD card without filesystem!");
@@ -607,8 +615,58 @@ extern "C" void bluescsi_setup(void)
   LED_OFF();
 }
 
+void bluescsi_test_loop(void) {
+  while(g_test_mode) {
+    // Initiator Mode Pin Test First (BSY not asserted)
+    SCSI_ENABLE_INITIATOR();
+    SCSI_OUT(SEL, 1);
+    delay(500);
+    SCSI_OUT(SEL, 0);
+    SCSI_OUT(ACK, 1);
+    delay(500);
+    SCSI_OUT(ACK, 0);
+    delay(500);
+    // ATN not tested here, there's no ATN output
+    SCSI_RELEASE_INITIATOR();
+
+    delay(1000);
+    // Switch to target mode with BSY_OUT
+    SCSI_OUT(BSY, 1);
+    SCSI_ENABLE_CONTROL_OUT();
+    delay(500);
+    SCSI_OUT(IO, 1);
+    delay(500);
+    SCSI_OUT(IO, 0);
+    SCSI_OUT(REQ, 1);
+    delay(500);
+    SCSI_OUT(REQ, 0);
+    SCSI_OUT(CD, 1);
+    delay(500);
+    SCSI_OUT(CD, 0);
+    SCSI_OUT(SEL, 1);
+    delay(500);
+    SCSI_OUT(SEL, 0);
+    SCSI_OUT(MSG, 1);
+    delay(500);
+    SCSI_OUT(MSG, 0);
+    SCSI_OUT(ACK, 1);
+    delay(500);
+    SCSI_OUT(ACK, 0);
+    // SCSI_OUT(ATN, 1);
+    // delay(500);
+    // SCSI_OUT(ATN, 0);
+    delay(500);
+    SCSI_OUT(BSY, 0);
+    SCSI_RELEASE_OUTPUTS();
+  }
+}
+
 extern "C" void bluescsi_main_loop(void)
 {
+  if (unlikely(g_test_mode)) {
+    bluescsi_test_loop();
+  }
+
   static uint32_t sd_card_check_time = 0;
   static uint32_t last_request_time = 0;
 

+ 35 - 12
src/BlueSCSI_initiator.cpp

@@ -121,6 +121,8 @@ void delay_with_poll(uint32_t ms)
 // High level logic of the initiator mode
 void scsiInitiatorMainLoop()
 {
+    SCSI_RELEASE_OUTPUTS();
+    SCSI_ENABLE_INITIATOR();
     if (g_scsiHostPhyReset)
     {
         log("Executing BUS RESET after aborted command");
@@ -155,29 +157,29 @@ void scsiInitiatorMainLoop()
                 scsiInquiry(g_initiator_state.target_id, inquiry_data);
             LED_OFF();
 
+            uint64_t total_bytes = 0;
             if (readcapok)
             {
-                log("SCSI id ", g_initiator_state.target_id,
+                log("SCSI ID ", g_initiator_state.target_id,
                     " capacity ", (int)g_initiator_state.sectorcount,
                     " sectors x ", (int)g_initiator_state.sectorsize, " bytes");
 
                 g_initiator_state.sectorcount_all = g_initiator_state.sectorcount;
 
-                uint64_t total_bytes = (uint64_t)g_initiator_state.sectorcount * g_initiator_state.sectorsize;
+                total_bytes = (uint64_t)g_initiator_state.sectorcount * g_initiator_state.sectorsize;
                 log("Drive total size is ", (int)(total_bytes / (1024 * 1024)), " MiB");
                 if (total_bytes >= 0xFFFFFFFF && SD.fatType() != FAT_TYPE_EXFAT)
                 {
                     // Note: the FAT32 limit is 4 GiB - 1 byte
                     log("Image files equal or larger than 4 GiB are only possible on exFAT filesystem");
-                    log("Please reformat the SD card with exFAT format to image this drive fully");
-
-                    g_initiator_state.sectorcount = (uint32_t)0xFFFFFFFF / g_initiator_state.sectorsize;
-                    log("Will image first 4 GiB - 1 = ", (int)g_initiator_state.sectorcount, " sectors");
+                    log("Please reformat the SD card with exFAT format to image this drive.");
+                    g_initiator_state.sectorsize = 0;
+                    g_initiator_state.sectorcount = g_initiator_state.sectorcount_all = 0;
                 }
             }
             else if (startstopok)
             {
-                log("SCSI id ", g_initiator_state.target_id, " responds but ReadCapacity command failed");
+                log("SCSI ID ", g_initiator_state.target_id, " responds but ReadCapacity command failed");
                 log("Possibly SCSI-1 drive? Attempting to read up to 1 GB.");
                 g_initiator_state.sectorsize = 512;
                 g_initiator_state.sectorcount = g_initiator_state.sectorcount_all = 2097152;
@@ -185,7 +187,7 @@ void scsiInitiatorMainLoop()
             }
             else
             {
-                debuglog("Failed to connect to SCSI id ", g_initiator_state.target_id);
+                debuglog("No response from SCSI ID ", g_initiator_state.target_id);
                 g_initiator_state.sectorsize = 0;
                 g_initiator_state.sectorcount = g_initiator_state.sectorcount_all = 0;
             }
@@ -202,10 +204,27 @@ void scsiInitiatorMainLoop()
             if (g_initiator_state.sectorcount > 0)
             {
                 char filename[32] = {0};
+                int lun = 0;
+
                 strncpy(filename, filename_format, sizeof(filename) - 1);
                 filename[2] += g_initiator_state.target_id;
 
-                SD.remove(filename);
+                uint64_t sd_card_free_bytes = (uint64_t)SD.vol()->freeClusterCount() * SD.vol()->bytesPerCluster();
+                if(sd_card_free_bytes < total_bytes)
+                {
+                    log("SD Card only has ", (int)(sd_card_free_bytes / (1024 * 1024)), " MiB - not enough free space to image this drive!");
+                    g_initiator_state.imaging = false;
+                    return;
+                }
+
+                while(SD.exists(filename))
+                {
+                    filename[3] = lun++ + '0';
+                }
+                if(lun != 0)
+                {
+                    log(filename_format, " already exists, using ", filename);
+                }
                 g_initiator_state.target_file = SD.open(filename, O_RDWR | O_CREAT | O_TRUNC);
                 if (!g_initiator_state.target_file.isOpen())
                 {
@@ -440,7 +459,7 @@ bool scsiRequestSense(int target_id, uint8_t *sense_key)
                                          response, sizeof(response),
                                          NULL, 0);
 
-    debuglog("RequestSense response: ", bytearray(response, 18));
+    log("RequestSense response: ", bytearray(response, 18));
 
     *sense_key = response[2];
     return status == 0;
@@ -449,10 +468,14 @@ bool scsiRequestSense(int target_id, uint8_t *sense_key)
 // Execute UNIT START STOP command to load/unload media
 bool scsiStartStopUnit(int target_id, bool start)
 {
-    uint8_t command[6] = {0x1B, 0, 0, 0, 0, 0};
+    uint8_t command[6] = {0x1B, 0x1, 0, 0, 0, 0};
     uint8_t response[4] = {0};
 
-    if (start) command[4] |= 1;
+    if (start)
+    {
+        command[4] |= 1; // Start
+        command[1] = 0;  // Immediate
+    }
 
     int status = scsiInitiatorRunCommand(target_id,
                                          command, sizeof(command),

+ 2 - 1
src/BlueSCSI_log.cpp

@@ -3,7 +3,8 @@
 #include "BlueSCSI_platform.h"
 
 const char *g_log_firmwareversion = BLUESCSI_FW_VERSION " " __DATE__ " " __TIME__;
-bool g_log_debug = true;
+bool g_log_debug = false;
+bool g_test_mode = false;
 
 // This memory buffer can be read by debugger and is also saved to log.txt
 #define LOGBUFMASK (LOGBUFSIZE - 1)

+ 3 - 0
src/BlueSCSI_log.h

@@ -16,6 +16,9 @@ const char *log_get_buffer(uint32_t *startpos, uint32_t *available = nullptr);
 // Whether to enable debug messages
 extern bool g_log_debug;
 
+// Enables output test mode
+extern bool g_test_mode;
+
 // Firmware version string
 extern const char *g_log_firmwareversion;