BlueSCSI/src/BlueSCSI.h

372 lines
14 KiB
C

#ifndef __BLUESCSI_H__
#define __BLUESCSI_H__
#include <Arduino.h> // For Platform.IO
#include <SdFat.h>
// 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
#define BLUESCSI_INI "bluescsi.ini"
#define SCSI_VENDOR_LENGTH 8
#define SCSI_PRODUCT_LENGTH 16
#define SCSI_REVISION_LENGTH 4
// HDD format
#define MAX_BLOCKSIZE 4096 // Maximum BLOCK size
// SDFAT
#define SD1_CONFIG SdSpiConfig(PA4, DEDICATED_SPI, SD_SCK_MHZ(SPI_FULL_SPEED), &SPI)
// LED ERRORS
#define ERROR_FALSE_INIT 3
#define ERROR_NO_SDCARD 5
enum SCSI_DEVICE_TYPE
{
SCSI_DEVICE_HDD,
SCSI_DEVICE_OPTICAL,
};
#define SCSI_DEVICE_FLAG_OPTICAL_MODE2 0x1
#define SCSI_DEVICE_FLAG_OPTICAL_RAW 0x2
#define SET_DEVICE_FLAG(var, flag) (var |= flag)
#define UNSET_DEVICE_FLAG(var, flag) (var &= ~flag)
#define IS_DEVICE_FLAG_SET(var, flag) ((var & flag) == flag)
#define IS_RAW(var) IS_DEVICE_FLAG_SET(var, SCSI_DEVICE_FLAG_OPTICAL_RAW)
#define IS_MODE2(var) IS_DEVICE_FLAG_SET(var, SCSI_DEVICE_FLAG_OPTICAL_MODE2)
#define INT_TO_CHAR(var) var+'0'
#define CHAR_TO_INT(var) var-'0'
#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)
#if DEBUG
#define LOG(XX) Serial.print(XX)
#define LOGHEX(XX) Serial.print(XX, HEX)
#define LOGN(XX) Serial.println(XX)
#define LOGHEXN(XX) Serial.println(XX, HEX)
#else
#define LOG(XX) //Serial.print(XX)
#define LOGHEX(XX) //Serial.print(XX, HEX)
#define LOGN(XX) //Serial.println(XX)
#define LOGHEXN(XX) //Serial.println(XX, HEX)
#endif
#define active 1
#define inactive 0
#define high 0
#define low 1
#define isHigh(XX) ((XX) == high)
#define isLow(XX) ((XX) != high)
#define gpio_mode(pin,val) gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val);
#define gpio_write(pin,val) gpio_write_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val)
#define gpio_read(pin) gpio_read_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit)
//#define DB0 PB8 // SCSI:DB0
//#define DB1 PB9 // SCSI:DB1
//#define DB2 PB10 // SCSI:DB2
//#define DB3 PB11 // SCSI:DB3
//#define DB4 PB12 // SCSI:DB4
//#define DB5 PB13 // SCSI:DB5
//#define DB6 PB14 // SCSI:DB6
//#define DB7 PB15 // SCSI:DB7
//#define DBP PB0 // SCSI:DBP
#define ATN PA8 // SCSI:ATN
#define BSY PA9 // SCSI:BSY
#define ACK PA10 // SCSI:ACK
#define RST PA15 // SCSI:RST
#define MSG PB3 // SCSI:MSG
#define SEL PB4 // SCSI:SEL
#define CD PB5 // SCSI:C/D
#define REQ PB6 // SCSI:REQ
#define IO PB7 // SCSI:I/O
#define SD_CS PA4 // SDCARD:CS
// Image Set Selector
#ifdef XCVR
#define IMAGE_SELECT1 PC14
#define IMAGE_SELECT2 PC15
#else
#define IMAGE_SELECT1 PA1
#define IMAGE_SELECT2 PB1
#endif
// GPIO register port
#define PAREG GPIOA->regs
#define PBREG GPIOB->regs
#define PCREG GPIOC->regs
// Virtual pin (Arduio compatibility is slow, so make it MCU-dependent)
#define PA(BIT) (BIT)
#define PB(BIT) (BIT+16)
#define PC(BIT) (BIT+32)
// Virtual pin decoding
#define GPIOREG(VPIN) ((VPIN)>=16?((VPIN)>=32?PCREG:PBREG):PAREG)
#define BITMASK(VPIN) (1<<((VPIN)&15))
// Built-in LED
#define LED PC13
#define vLED PC(13)
#define LED_MODE GPIO_OUTPUT_OD
// External LED
#define LED2 PA0
#define vLED2 PA(0)
#define LED2_MODE GPIO_OUTPUT_PP
// LED control
#define LED_ON() GPIOREG(vLED)->BSRR = BITMASK(vLED) << (LED_MODE == GPIO_OUTPUT_PP ? 0 : 16); GPIOREG(vLED2)->BSRR = BITMASK(vLED2) << (LED2_MODE == GPIO_OUTPUT_PP ? 0 : 16);
#define LED_OFF() GPIOREG(vLED)->BSRR = BITMASK(vLED) << (LED_MODE == GPIO_OUTPUT_PP ? 16 : 0); GPIOREG(vLED2)->BSRR = BITMASK(vLED2) << (LED2_MODE == GPIO_OUTPUT_PP ? 16 : 0);
#define vATN PA(8) // SCSI:ATN
#define vBSY PA(9) // SCSI:BSY
#define vACK PA(10) // SCSI:ACK
#define vRST PA(15) // SCSI:RST
#define vMSG PB(3) // SCSI:MSG
#define vSEL PB(4) // SCSI:SEL
#define vCD PB(5) // SCSI:C/D
#define vREQ PB(6) // SCSI:REQ
#define vIO PB(7) // SCSI:I/O
#define vSD_CS PA(4) // SDCARD:CS
// SCSI output pin control: opendrain active LOW (direct pin drive)
#define SCSI_OUT(VPIN,ACTIVE) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN)<<((ACTIVE)?16:0); }
// SCSI input pin check (inactive=0,active=1)
#define SCSI_IN(VPIN) ((~GPIOREG(VPIN)->IDR>>(VPIN&15))&1)
#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); }
#ifdef XCVR
#define TR_TARGET PA1 // Target Transceiver Control Pin
#define TR_DBP PA2 // Data Pins Transceiver Control Pin
#define TR_INITIATOR PA3 // Initiator Transciever Control Pin
#define vTR_TARGET PA(1) // Target Transceiver Control Pin
#define vTR_DBP PA(2) // Data Pins Transceiver Control Pin
#define vTR_INITIATOR PA(3) // 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); }
// Turn on the output only for BSY
#define SCSI_BSY_ACTIVE() { gpio_mode(BSY, GPIO_OUTPUT_PP); SCSI_OUT(vBSY, active) }
#define SCSI_TARGET_ACTIVE() { gpio_mode(REQ, GPIO_OUTPUT_PP); gpio_mode(MSG, GPIO_OUTPUT_PP); gpio_mode(CD, GPIO_OUTPUT_PP); gpio_mode(IO, GPIO_OUTPUT_PP); gpio_mode(BSY, GPIO_OUTPUT_PP); TRANSCEIVER_IO_SET(vTR_TARGET,TR_OUTPUT);}
// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
#define SCSI_TARGET_INACTIVE() { gpio_mode(REQ, GPIO_INPUT_FLOATING); gpio_mode(MSG, GPIO_INPUT_FLOATING); gpio_mode(CD, GPIO_INPUT_FLOATING); gpio_mode(IO, GPIO_INPUT_FLOATING); gpio_mode(BSY, GPIO_INPUT_FLOATING); TRANSCEIVER_IO_SET(vTR_TARGET,TR_INPUT); }
#define DB_MODE_OUT 1 // push-pull mode
#define DB_MODE_IN 4 // floating inputs
#else
// GPIO mode
// IN , FLOAT : 4
// IN , PU/PD : 8
// OUT, PUSH/PULL : 3
// OUT, OD : 1
#define DB_MODE_OUT 3
//#define DB_MODE_OUT 7
#define DB_MODE_IN 8
// Turn on the output only for BSY
#define SCSI_BSY_ACTIVE() { gpio_mode(BSY, GPIO_OUTPUT_OD); SCSI_OUT(vBSY, active) }
// BSY,REQ,MSG,CD,IO Turn on the output (no change required for OD)
#define SCSI_TARGET_ACTIVE() { if (DB_MODE_OUT != 7) gpio_mode(REQ, GPIO_OUTPUT_PP); }
// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
#define SCSI_TARGET_INACTIVE() { if (DB_MODE_OUT == 7) SCSI_OUT(vREQ,inactive) else { if (DB_MODE_IN == 8) gpio_mode(REQ, GPIO_INPUT_PU) else gpio_mode(REQ, GPIO_INPUT_FLOATING)} PBREG->BSRR = 0b000000000000000011101000; SCSI_OUT(vBSY,inactive); gpio_mode(BSY, GPIO_INPUT_PU); }
#endif
// Put DB and DP in output mode
#define SCSI_DB_OUTPUT() { PBREG->CRL=(PBREG->CRL &0xfffffff0)|DB_MODE_OUT; PBREG->CRH = 0x11111111*DB_MODE_OUT; }
// Put DB and DP in input mode
#define SCSI_DB_INPUT() { PBREG->CRL=(PBREG->CRL &0xfffffff0)|DB_MODE_IN ; PBREG->CRH = (uint32_t)0x11111111*DB_MODE_IN; }
// HDDiamge 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
#define MAX_MAC_PATH 32 // Maximum file name length on vintage macs
#define MAC_BLK_SIZE 4096
/*
* Data byte to BSRR register setting value and parity table
*/
// Parity bit generation
//#define PTY(V) (1^((V)^((V)>>1)^((V)>>2)^((V)>>3)^((V)>>4)^((V)>>5)^((V)>>6)^((V)>>7))&1)
#define PTY(n) ((1 ^ (n) ^ ((n)>>1) ^ ((n)>>2) ^ ((n)>>3) ^ ((n)>>4) ^ ((n)>>5) ^ ((n)>>6) ^ ((n)>>7)) & 1)
// Data byte to BSRR register setting value conversion table
// BSRR[31:24] = DB[7:0]
// BSRR[ 16] = PTY(DB)
// BSRR[15: 8] = ~DB[7:0]
// BSRR[ 0] = ~PTY(DB)
// Set DBP, set REQ = inactive
#define DBP(D) ((((((uint32_t)(D)<<8)|PTY(D))*0x00010001)^0x0000ff01)|BITMASK(vREQ))
//#define DBP(D) ((((((uint32_t)(D)<<8)|PTY(D))*0x00010001)^0x0000ff01))
//#define DBP8(D) DBP(D),DBP(D+1),DBP(D+2),DBP(D+3),DBP(D+4),DBP(D+5),DBP(D+6),DBP(D+7)
//#define DBP32(D) DBP8(D),DBP8(D+8),DBP8(D+16),DBP8(D+24)
// 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)
// Macro cleaning
//#undef DBP32
//#undef DBP8
//#undef DBP
//#undef PTY
// #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)
typedef 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];
};
} SCSI_INQUIRY_DATA;
// 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
SCSI_INQUIRY_DATA inquiry_block; // SCSI information
uint8_t m_senseKey; // Sense key
uint16_t m_additional_sense_code; // ASC/ASCQ
uint8_t m_sector_offset; // optical sector offset for missing sync header
uint8_t flags; // various device flags
} SCSI_DEVICE;
static byte cdb_len_lookup[] = {
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,12,12,12,12,12,12,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10
};
#endif