|
@@ -6,7 +6,7 @@
|
|
|
#include "config.h"
|
|
|
#include "scsi.h"
|
|
|
// Log File
|
|
|
-#define VERSION "0.xx-SNAPSHOT-BLACKSASI-2022-03-20-F4"
|
|
|
+#define VERSION "0.xx-SNAPSHOT-BLACKSASI-2022-09-28-F4"
|
|
|
|
|
|
#define LOG_FILENAME "LOG.txt"
|
|
|
|
|
@@ -114,6 +114,7 @@
|
|
|
#define GPIOREG(VPIN) ((VPIN) >= 16 ? ((VPIN) >= 32 ? ((VPIN) >= 48 ? ((VPIN) >= 48 ? PEREG : PDREG) : PCREG) : PBREG) : PAREG)
|
|
|
#define BITMASK(VPIN) (1 << ((VPIN % 16) & 15))
|
|
|
|
|
|
+
|
|
|
#define vATN PB(14) // SCSI:ATN
|
|
|
#define vBSY PB(6) // SCSI:BSY
|
|
|
#define vACK PB(7) // SCSI:ACK
|
|
@@ -159,4 +160,296 @@ void switchImage(void);
|
|
|
void initFileLog(void);
|
|
|
void finalizeFileLog(void);
|
|
|
|
|
|
+
|
|
|
+// SCSI config
|
|
|
+#define MAX_SCSIID 7 // Maximum number of supported SCSI-IDs (The minimum is 0)
|
|
|
+#define MAX_SCSILUN 8 // Maximum number of LUNs supported (The minimum is 0)
|
|
|
+#define NUM_SCSIID MAX_SCSIID // Number of enabled SCSI IDs
|
|
|
+#define NUM_SCSILUN 1 // Number of enabled LUNs
|
|
|
+#define READ_PARITY_CHECK 0 // Perform read parity check (unverified)
|
|
|
+#define DEFAULT_SCSI_ID 1
|
|
|
+#define DEFAULT_SCSI_LUN 0
|
|
|
+#define SCSI_BUF_SIZE 512 // Size of the SCSI Buffer
|
|
|
+#define HDD_BLOCK_SIZE 512
|
|
|
+#define OPTICAL_BLOCK_SIZE 2048
|
|
|
+
|
|
|
+// HDD format
|
|
|
+#define MAX_BLOCKSIZE 4096 // Maximum BLOCK size
|
|
|
+
|
|
|
+// LED ERRORS
|
|
|
+#define ERROR_FALSE_INIT 3
|
|
|
+#define ERROR_NO_SDCARD 5
|
|
|
+
|
|
|
+enum SCSI_DEVICE_TYPE
|
|
|
+{
|
|
|
+ SCSI_DEVICE_HDD,
|
|
|
+ SCSI_DEVICE_OPTICAL,
|
|
|
+};
|
|
|
+
|
|
|
+#define CDROM_RAW_SECTORSIZE 2352
|
|
|
+#define CDROM_COMMON_SECTORSIZE 2048
|
|
|
+
|
|
|
+#define MAX_SCSI_COMMAND 0xff
|
|
|
+#define SCSI_COMMAND_HANDLER(x) static byte x(SCSI_DEVICE *dev, const byte *cdb)
|
|
|
+
|
|
|
+
|
|
|
+#define ATN PB14 // SCSI:ATN
|
|
|
+#define BSY PB6 // SCSI:BSY
|
|
|
+#define ACK PB7 // SCSI:ACK
|
|
|
+#define RST PA15 // SCSI:RST
|
|
|
+#define MSG PE2 // SCSI:MSG
|
|
|
+#define SEL PE3 // SCSI:SEL
|
|
|
+#define CD PE4 // SCSI:C/D
|
|
|
+#define REQ PE5 // SCSI:REQ
|
|
|
+#define IO PB1 // SCSI:I/O
|
|
|
+
|
|
|
+#define LED1 PA4 // LED
|
|
|
+#define LED2 PA5 // Driven LED
|
|
|
+#define LED3 PA6 // Driven LED
|
|
|
+// Image Set Selector
|
|
|
+#define IMAGE_SELECT1 PC4
|
|
|
+#define IMAGE_SELECT2 PC5
|
|
|
+
|
|
|
+
|
|
|
+#define NOP(x) for(unsigned _nopcount = x; _nopcount; _nopcount--) { asm("NOP"); }
|
|
|
+
|
|
|
+/* SCSI Timing delays */
|
|
|
+// Due to limitations in timing granularity all of these are "very" rough estimates
|
|
|
+#define SCSI_BUS_SETTLE() NOP(30); // spec 400ns ours ~420us
|
|
|
+#define SCSI_DATA_RELEASE() NOP(30); // spec 400ns ours ~420us
|
|
|
+#define SCSI_HOLD_TIME() asm("NOP"); asm("NOP"); asm("NOP"); // spec 45ns ours ~42ns
|
|
|
+#define SCSI_DESKEW() // asm("NOP"); asm("NOP"); asm("NOP"); // spec 45ns ours ~42ns
|
|
|
+#define SCSI_CABLE_SKEW() // asm("NOP"); // spec 10ns ours ~14ns
|
|
|
+#define SCSI_RESET_HOLD() asm("NOP"); asm("NOP"); // spec 25ns ours ~28ns
|
|
|
+#define SCSI_DISCONNECTION_DELAY() NOP(15); // spec 200ns ours ~210ns
|
|
|
+
|
|
|
+/* SCSI phases
|
|
|
++=============-===============-==================================-============+
|
|
|
+| Signal | Phase name | Direction of transfer | Comment |
|
|
|
+|-------------| | | |
|
|
|
+| MSG|C/D|I/O | | | |
|
|
|
+|----+---+----+---------------+----------------------------------+------------|
|
|
|
+| 0 | 0 | 0 | DATA OUT | Initiator to target \ | Data |
|
|
|
+| 0 | 0 | 1 | DATA IN | Initiator from target / | phase |
|
|
|
+| 0 | 1 | 0 | COMMAND | Initiator to target | |
|
|
|
+| 0 | 1 | 1 | STATUS | Initiator from target | |
|
|
|
+| 1 | 0 | 0 | * | | |
|
|
|
+| 1 | 0 | 1 | * | | |
|
|
|
+| 1 | 1 | 0 | MESSAGE OUT | Initiator to target \ | Message |
|
|
|
+| 1 | 1 | 1 | MESSAGE IN | Initiator from target / | phase |
|
|
|
+|-----------------------------------------------------------------------------|
|
|
|
+| Key: 0 = False, 1 = True, * = Reserved for future standardization |
|
|
|
++=============================================================================+
|
|
|
+*/
|
|
|
+// SCSI phase change as single write to port B
|
|
|
+#define SCSIPHASEMASK(MSGACTIVE, CDACTIVE, IOACTIVE) ((BITMASK(vMSG)<<((MSGACTIVE)?16:0)) | (BITMASK(vCD)<<((CDACTIVE)?16:0)) | (BITMASK(vIO)<<((IOACTIVE)?16:0)))
|
|
|
+
|
|
|
+#define SCSI_PHASE_DATAOUT SCSIPHASEMASK(inactive, inactive, inactive)
|
|
|
+#define SCSI_PHASE_DATAIN SCSIPHASEMASK(inactive, inactive, active)
|
|
|
+#define SCSI_PHASE_COMMAND SCSIPHASEMASK(inactive, active, inactive)
|
|
|
+#define SCSI_PHASE_STATUS SCSIPHASEMASK(inactive, active, active)
|
|
|
+#define SCSI_PHASE_MESSAGEOUT SCSIPHASEMASK(active, active, inactive)
|
|
|
+#define SCSI_PHASE_MESSAGEIN SCSIPHASEMASK(active, active, active)
|
|
|
+
|
|
|
+#define SCSI_PHASE_CHANGE(MASK) { PBREG->BSRR = (MASK); }
|
|
|
+//BLACKSASI clean this up and use defines
|
|
|
+// Data pins
|
|
|
+// 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
|
|
+static const uint32_t scsiDbOutputRegOr_PDREG = 0b00000000000000000101010101010101;
|
|
|
+static const uint32_t scsiDbInputOutputAnd_PDREG = 0b00000000000000000000000000000000;
|
|
|
+static const uint32_t scsiDbInputOutputPullAnd_PDREG = 0b00000000000000000101010101010101;
|
|
|
+// Control pins
|
|
|
+// 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
|
|
+static const uint32_t scsiDbOutputRegOr_PEREG = 0b00000000000000000000000000000001;
|
|
|
+static const uint32_t scsiDbInputOutputAnd_PEREG = 0b00000000000000000011111111110000;
|
|
|
+static const uint32_t scsiDbInputOutputPullAnd_PEREG = 0b00000000000000000101010101010001;
|
|
|
+
|
|
|
+// Put DB and DP in output mode and control buffers
|
|
|
+#define SCSI_DB_OUTPUT() { PDREG->MODER = (PDREG->MODER & scsiDbInputOutputAnd_PDREG) | scsiDbOutputRegOr_PDREG; PEREG->MODER = (PEREG->MODER & scsiDbInputOutputAnd_PEREG) | scsiDbOutputRegOr_PEREG; SCSI_DATABUS_OUT() ;}
|
|
|
+
|
|
|
+// Put DB and DP in input mode and control buffers
|
|
|
+#define SCSI_DB_INPUT() { PDREG->MODER = (PDREG->MODER & scsiDbInputOutputAnd_PDREG); PEREG->MODER = (PEREG->MODER & scsiDbInputOutputAnd_PEREG); SCSI_DATABUS_IN();}
|
|
|
+#define SCSI_SET_PULL() { PDREG->PUPDR |= scsiDbInputOutputPullAnd_PDREG; PEREG->PUPDR |= scsiDbInputOutputPullAnd_PEREG; }
|
|
|
+
|
|
|
+/*
|
|
|
+static const uint32_t scsiDbOutputRegOr = 0x55150011;
|
|
|
+static const uint32_t scsiDbInputOutputAnd = 0x00C0FFCC;
|
|
|
+// Put DB and DP in output mode
|
|
|
+#define SCSI_DB_OUTPUT() { PBREG->MODER = (PBREG->MODER & scsiDbInputOutputAnd) | scsiDbOutputRegOr; }
|
|
|
+
|
|
|
+// Put DB and DP in input mode
|
|
|
+#define SCSI_DB_INPUT() { PBREG->MODER = (PBREG->MODER & scsiDbInputOutputAnd); }
|
|
|
+*/
|
|
|
+
|
|
|
+#if XCVR == 1
|
|
|
+
|
|
|
+#define TR_TARGET PC2 // Target Transceiver Control Pin
|
|
|
+#define TR_DBP PC0 // Data Pins Transceiver Control Pin
|
|
|
+#define TR_INITIATOR PC1 // Initiator Transciever Control Pin
|
|
|
+
|
|
|
+#define vTR_TARGET PC(2) // Target Transceiver Control Pin
|
|
|
+#define vTR_DBP PC(0) // Data Pins Transceiver Control Pin
|
|
|
+#define vTR_INITIATOR PC(1) // Initiator Transciever Control Pin
|
|
|
+
|
|
|
+#define TR_INPUT 1
|
|
|
+#define TR_OUTPUT 0
|
|
|
+
|
|
|
+// Transceiver control definitions
|
|
|
+#define TRANSCEIVER_IO_SET(VPIN,TR_INPUT) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN) << ((TR_INPUT) ? 16 : 0); }
|
|
|
+
|
|
|
+static const uint32_t SCSI_TARGET_PORTA_AND = 0xFFF3FFFF; // Sets input mode when AND-ed against MODER
|
|
|
+static const uint32_t SCSI_TARGET_PORTA_OR = 0x00040000; // Sets output mode when AND+OR against MODER
|
|
|
+static const uint32_t SCSI_TARGET_PORTB_AND = 0xFFFF033F; // Sets input mode when AND-ed against MODER
|
|
|
+static const uint32_t SCSI_TARGET_PORTB_OR = 0x00005440; // Sets output mode when AND+OR against MODER
|
|
|
+
|
|
|
+// Turn on the output only for BSY
|
|
|
+#define SCSI_BSY_ACTIVE() { PAREG->MODER = (PAREG->MODER & SCSI_TARGET_PORTA_AND) | SCSI_TARGET_PORTA_OR; PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND) | SCSI_TARGET_PORTB_OR; SCSI_OUT(vBSY, active) }
|
|
|
+
|
|
|
+// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
|
|
|
+#define SCSI_TARGET_INACTIVE() { PAREG->MODER = (PAREG->MODER & SCSI_TARGET_PORTA_AND); PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND); TRANSCEIVER_IO_SET(vTR_TARGET,TR_INPUT); }
|
|
|
+
|
|
|
+#define SCSI_TARGET_ACTIVE() { PAREG->MODER = (PAREG->MODER & SCSI_TARGET_PORTA_AND) | SCSI_TARGET_PORTA_OR; PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND) | SCSI_TARGET_PORTB_OR; }
|
|
|
+
|
|
|
+#else
|
|
|
+
|
|
|
+// Turn on the output only for BSY
|
|
|
+#define SCSI_BSY_ACTIVE() { pinMode(BSY, OUTPUT_OPEN_DRAIN); SCSI_OUT(vBSY, active) }
|
|
|
+
|
|
|
+// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
|
|
|
+#define SCSI_TARGET_INACTIVE() { SCSI_OUT(vREQ,inactive); SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT); SCSI_OUT(vBSY,inactive); pinMode(BSY, INPUT); }
|
|
|
+
|
|
|
+// BSY,REQ,MSG,CD,IO Turn on the output (no change required for OD)
|
|
|
+#define SCSI_TARGET_ACTIVE() { }
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+// HDDimage file
|
|
|
+#define HDIMG_ID_POS 2 // Position to embed ID number
|
|
|
+#define HDIMG_LUN_POS 3 // Position to embed LUN numbers
|
|
|
+#define HDIMG_BLK_POS 5 // Position to embed block size numbers
|
|
|
+#define MAX_FILE_PATH 64 // Maximum file name length
|
|
|
+
|
|
|
+/*
|
|
|
+ * Data byte to BSRR register setting value and parity table
|
|
|
+*/
|
|
|
+/**
|
|
|
+ * BSRR register generator
|
|
|
+ * Totally configurable for which pin is each data bit, which pin is PTY, and which pin is REQ.
|
|
|
+ * The only requirement is that data and parity pins are in the same GPIO block.
|
|
|
+ * REQ can be specified as -1 to ignore, as it doesn't have to be in the same GPIO block.
|
|
|
+ * This is dramatically slower than the original static array, but is easier to configure
|
|
|
+ */
|
|
|
+static uint32_t genBSRR(uint32_t data) {
|
|
|
+ uint8_t masks[] = {0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL};
|
|
|
+ // Positions array indicates which bit position each data bit goes in
|
|
|
+ // positions[0] is for data bit 0, position[1] for data bit 1, etc
|
|
|
+ // DB0, DB1, DB2, DB4, DB5, DB6, DB7 in order
|
|
|
+ uint8_t positions[] = {8UL, 9UL, 10UL, 2UL, 12UL, 13UL, 14UL, 15UL};
|
|
|
+ uint8_t dbpPosition = 0UL;
|
|
|
+ int reqPosition = 6;
|
|
|
+ uint8_t bitsAsserted = 0;
|
|
|
+
|
|
|
+ uint32_t output = 0x00000000;
|
|
|
+ for (int i = 0; i < 8; i++) {
|
|
|
+ if (data & (0x1 << masks[i])) {
|
|
|
+ // There's a one in this bit position, BSRR reset
|
|
|
+ output |= 0x1 << (positions[i] + 16);
|
|
|
+ bitsAsserted++;
|
|
|
+ } else {
|
|
|
+ // There's a 0 in this bit position, BSRR set high
|
|
|
+ output |= (0x1 << positions[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the parity bit
|
|
|
+ if (bitsAsserted % 2 == 0) {
|
|
|
+ // Even number of bits asserted, Parity asserted (0, low, BSRR reset)
|
|
|
+ output |= 0x01 << (dbpPosition + 16);
|
|
|
+ } else {
|
|
|
+ // Odd number of bits asserted, Parity deasserted (1, high, BSRR set)
|
|
|
+ output |= 0x01 << dbpPosition;
|
|
|
+ }
|
|
|
+
|
|
|
+ // BSRR set REQ if specified
|
|
|
+ // Only set > 0 if it's in the same GPIO block as DB and DBP
|
|
|
+ if (reqPosition >= 0) {
|
|
|
+ output |= 0x01 << reqPosition;
|
|
|
+ }
|
|
|
+
|
|
|
+ return output;
|
|
|
+}
|
|
|
+
|
|
|
+// BSRR register control value that simultaneously performs DB set, DP set, and REQ = H (inactrive)
|
|
|
+//uint32_t db_bsrr[256];
|
|
|
+// Parity bit acquisition
|
|
|
+#define PARITY(DB) (db_bsrr[DB]&1)
|
|
|
+
|
|
|
+// #define GET_CDB6_LBA(x) ((x[2] & 01f) << 16) | (x[3] << 8) | x[4]
|
|
|
+#define READ_DATA_BUS() (byte)((~(uint32_t)GPIOB->regs->IDR)>>8)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+struct SCSI_INQUIRY_DATA
|
|
|
+{
|
|
|
+ union
|
|
|
+ {
|
|
|
+ struct {
|
|
|
+ // bitfields are in REVERSE order for ARM
|
|
|
+ // byte 0
|
|
|
+ byte peripheral_device_type:5;
|
|
|
+ byte peripheral_qualifier:3;
|
|
|
+ // byte 1
|
|
|
+ byte reserved_byte2:7;
|
|
|
+ byte rmb:1;
|
|
|
+ // byte 2
|
|
|
+ byte ansi_version:3;
|
|
|
+ byte always_zero_byte3:5;
|
|
|
+ // byte 3
|
|
|
+ byte response_format:4;
|
|
|
+ byte reserved_byte4:2;
|
|
|
+ byte tiop:1;
|
|
|
+ byte always_zero_byte4:1;
|
|
|
+ // byte 4
|
|
|
+ byte additional_length;
|
|
|
+ // byte 5-6
|
|
|
+ byte reserved_byte5;
|
|
|
+ byte reserved_byte6;
|
|
|
+ // byte 7
|
|
|
+ byte sync:1;
|
|
|
+ byte always_zero_byte7_more:4;
|
|
|
+ byte always_zero_byte7:3;
|
|
|
+ // byte 8-15
|
|
|
+ char vendor[8];
|
|
|
+ // byte 16-31
|
|
|
+ char product[16];
|
|
|
+ // byte 32-35
|
|
|
+ char revision[4];
|
|
|
+ // byte 36
|
|
|
+ byte release;
|
|
|
+ // 37-46
|
|
|
+ char revision_date[10];
|
|
|
+ };
|
|
|
+ // raw bytes
|
|
|
+ byte raw[64];
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+// HDD image
|
|
|
+typedef __attribute__((aligned(4))) struct _SCSI_DEVICE
|
|
|
+{
|
|
|
+ FsFile *m_file; // File object
|
|
|
+ uint64_t m_fileSize; // File size
|
|
|
+ uint16_t m_blocksize; // SCSI BLOCK size
|
|
|
+ uint16_t m_rawblocksize; // OPTICAL raw sector size
|
|
|
+ uint8_t m_type; // SCSI device type
|
|
|
+ uint32_t m_blockcount; // blockcount
|
|
|
+ bool m_raw; // Raw disk
|
|
|
+ SCSI_INQUIRY_DATA *inquiry_block; // SCSI information
|
|
|
+ uint8_t m_senseKey; // Sense key
|
|
|
+ uint16_t m_additional_sense_code; // ASC/ASCQ
|
|
|
+ bool m_mode2; // MODE2 CDROM
|
|
|
+ uint8_t m_sector_offset; // optical sector offset for missing sync header
|
|
|
+} SCSI_DEVICE;
|
|
|
+
|
|
|
+
|
|
|
#endif // __BLACKSASI_H__
|