| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691 | /*   *  BlueSCSI *  Copyright (c) 2021  Eric Helgeson, Androda *   *  This file is free software: you may copy, redistribute and/or modify it   *  under the terms of the GNU General Public License as published by the   *  Free Software Foundation, either version 2 of the License, or (at your   *  option) any later version.   *   *  This file is distributed in the hope that it will be useful, but   *  WITHOUT ANY WARRANTY; without even the implied warranty of   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   *  General Public License for more details.   *   *  You should have received a copy of the GNU General Public License   *  along with this program.  If not, see https://github.com/erichelgeson/bluescsi.   *   * This file incorporates work covered by the following copyright and   * permission notice:   *   *     Copyright (c) 2019 komatsu    *   *     Permission to use, copy, modify, and/or distribute this software   *     for any purpose with or without fee is hereby granted, provided   *     that the above copyright notice and this permission notice appear   *     in all copies.   *   *     THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL   *     WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED   *     WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE   *     AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR   *     CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS   *     OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,   *     NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN   *     CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   */#include <Arduino.h> // For Platform.IO#include <SdFat.h>#include <setjmp.h>#define DEBUG            0      // 0:No debug information output                                // 1: Debug information output to USB Serial                                // 2: Debug information output to LOG.txt (slow)// Log File#define VERSION "1.1-SNAPSHOT-20220622"#define LOG_FILENAME "LOG.txt"#include "BlueSCSI.h"#include "scsi_cmds.h"#include "scsi_sense.h"#include "scsi_status.h"#include "scsi_mode.h"#ifdef USE_STM32_DMA#warning "warning USE_STM32_DMA"#endif// SDFATSdFs SD;FsFile LOG_FILE;volatile bool m_isBusReset = false;   // Bus resetvolatile bool m_resetJmp = false;     // Call longjmp on resetjmp_buf       m_resetJmpBuf;byte          scsi_id_mask;              // Mask list of responding SCSI IDsbyte          m_id;                      // Currently responding SCSI-IDbyte          m_lun;                     // Logical unit number currently respondingbyte          m_sts;                     // Status bytebyte          m_msg;                     // Message bytesbyte          m_buf[MAX_BLOCKSIZE];      // General purpose bufferbyte          m_scsi_buf[SCSI_BUF_SIZE]; // Buffer for SCSI READ/WRITE Bufferbyte          m_msb[256];                // Command storage bytesSCSI_DEVICE scsi_device_list[NUM_SCSIID][MAX_SCSILUN]; // Maximum numberstatic byte onUnimplemented(SCSI_DEVICE *dev, const byte *cdb){  // does nothing!  if(Serial)  {    Serial.print("Unimplemented SCSI command: ");    Serial.println(cdb[0], 16);  }  dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;  dev->m_additional_sense_code = SCSI_ASC_INVALID_OPERATION_CODE;  return SCSI_STATUS_CHECK_CONDITION;}static byte onNOP(SCSI_DEVICE *dev, const byte *cdb){  dev->m_senseKey = 0;  dev->m_additional_sense_code = 0;  return SCSI_STATUS_GOOD;}#define MAX_SCSI_COMMAND  0xff// function tablebyte (*scsi_command_table[MAX_SCSI_COMMAND])(SCSI_DEVICE *dev, const byte *cdb);#define SCSI_COMMAND_HANDLER(x) static byte x(SCSI_DEVICE *dev, const byte *cdb)// scsi command functionsSCSI_COMMAND_HANDLER(onRequestSense);SCSI_COMMAND_HANDLER(onRead6);SCSI_COMMAND_HANDLER(onRead10);SCSI_COMMAND_HANDLER(onWrite6);SCSI_COMMAND_HANDLER(onWrite10);SCSI_COMMAND_HANDLER(onInquiry);SCSI_COMMAND_HANDLER(onReadCapacity);SCSI_COMMAND_HANDLER(onModeSense);SCSI_COMMAND_HANDLER(onModeSelect);SCSI_COMMAND_HANDLER(onVerify);SCSI_COMMAND_HANDLER(onReadBuffer);SCSI_COMMAND_HANDLER(onWriteBuffer);SCSI_COMMAND_HANDLER(onTestUnitReady);SCSI_COMMAND_HANDLER(onReZeroUnit);SCSI_COMMAND_HANDLER(onSendDiagnostic);SCSI_COMMAND_HANDLER(onReadDefectData);static void flashError(const unsigned error);void onBusReset(void);void initFileLog(int);void finalizeFileLog(void);void findDriveImages(FsFile root);/* * IO read. */inline byte readIO(void){  // Port input data register  uint32_t ret = GPIOB->regs->IDR;  byte bret = (byte)(~(ret>>8));#if READ_PARITY_CHECK  if((db_bsrr[bret]^ret)&1)    m_sts |= 0x01; // parity error#endif  return bret;}// If config file exists, read the first three lines and copy the contents.// File must be well formed or you will get junk in the SCSI Vendor fields.void readSCSIDeviceConfig(SCSI_DEVICE *dev) {  FsFile config_file = SD.open("scsi-config.txt", O_RDONLY);  if (!config_file.isOpen()) {    return;  }  SCSI_INQUIRY_DATA *iq = &dev->inquiry_block;  char vendor[9];  memset(vendor, 0, sizeof(vendor));  config_file.readBytes(vendor, sizeof(vendor));  LOG_FILE.print("SCSI VENDOR: ");  LOG_FILE.println(vendor);  memcpy(&iq->vendor, vendor, 8);  char product[17];  memset(product, 0, sizeof(product));  config_file.readBytes(product, sizeof(product));  LOG_FILE.print("SCSI PRODUCT: ");  LOG_FILE.println(product);  memcpy(&iq->product, product, 16);  char version[5];  memset(version, 0, sizeof(version));  config_file.readBytes(version, sizeof(version));  LOG_FILE.print("SCSI VERSION: ");  LOG_FILE.println(version);  memcpy(&iq->revision, version, 4);  config_file.close();}// read SD information and print to logfilevoid readSDCardInfo(){  cid_t sd_cid;  if(SD.card()->readCID(&sd_cid))  {    LOG_FILE.print("Sd MID:");    LOG_FILE.print(sd_cid.mid, 16);    LOG_FILE.print(" OID:");    LOG_FILE.print(sd_cid.oid[0]);    LOG_FILE.println(sd_cid.oid[1]);    LOG_FILE.print("Sd Name:");    LOG_FILE.print(sd_cid.pnm[0]);    LOG_FILE.print(sd_cid.pnm[1]);    LOG_FILE.print(sd_cid.pnm[2]);    LOG_FILE.print(sd_cid.pnm[3]);    LOG_FILE.println(sd_cid.pnm[4]);    LOG_FILE.print("Sd Date:");    LOG_FILE.print(sd_cid.mdt_month);    LOG_FILE.print("/20"); // CID year is 2000 + high/low    LOG_FILE.print(sd_cid.mdt_year_high);    LOG_FILE.println(sd_cid.mdt_year_low);    LOG_FILE.print("Sd Serial:");    LOG_FILE.println(sd_cid.psn);    LOG_FILE.sync();  }}/* * Open HDD image file */bool hddimageOpen(SCSI_DEVICE *dev, FsFile *file,int id,int lun,int blocksize){  dev->m_fileSize= 0;  dev->m_blocksize = blocksize;  dev->m_file = file;  dev->m_type = SCSI_DEVICE_HDD;  if(dev->m_file->isOpen())  {    dev->m_fileSize = dev->m_file->size();    dev->m_blockcount = dev->m_fileSize / dev->m_blocksize;    if(dev->m_fileSize>0)    {      // check blocksize dummy file      LOG_FILE.print(" / ");      LOG_FILE.print(dev->m_fileSize);      LOG_FILE.print("bytes / ");      LOG_FILE.print(dev->m_fileSize / 1024);      LOG_FILE.print("KiB / ");      LOG_FILE.print(dev->m_fileSize / 1024 / 1024);      LOG_FILE.println("MiB");      return true; // File opened    }    else    {      LOG_FILE.println(" - file is 0 bytes, can not use.");      dev->m_file->close();      dev->m_fileSize = dev->m_blocksize = 0; // no file    }  }  return false;}/* * Initialization. *  Initialize the bus and set the PIN orientation */void setup(){  // PA15 / PB3 / PB4 Cannot be used  // JTAG Because it is used for debugging.  enableDebugPorts();  // Setup BSRR table  for (unsigned i = 0; i <= 255; i++) {    db_bsrr[i] = DBP(i);  }  // Default all SCSI command handlers to onUnimplemented  for(unsigned i = 0; i < MAX_SCSI_COMMAND; i++)  {    scsi_command_table[i] = onUnimplemented;  }  // SCSI commands that just need to return ok  scsi_command_table[SCSI_FORMAT_UNIT4] = onNOP;  scsi_command_table[SCSI_FORMAT_UNIT6] = onNOP;  scsi_command_table[SCSI_REASSIGN_BLOCKS] = onNOP;  scsi_command_table[SCSI_SEEK6] = onNOP;  scsi_command_table[SCSI_SEEK10] = onNOP;  scsi_command_table[SCSI_START_STOP_UNIT] = onNOP;  scsi_command_table[SCSI_PREVENT_ALLOW_REMOVAL] = onNOP;  scsi_command_table[SCSI_RELEASE] = onNOP;  scsi_command_table[SCSI_RESERVE] = onNOP;  scsi_command_table[SCSI_TEST_UNIT_READY] = onNOP;    // SCSI commands that have handlers  scsi_command_table[SCSI_REZERO_UNIT] = onReZeroUnit;  scsi_command_table[SCSI_REQUEST_SENSE] = onRequestSense;  scsi_command_table[SCSI_READ6] = onRead6;  scsi_command_table[SCSI_READ10] = onRead10;  scsi_command_table[SCSI_WRITE6] = onWrite6;  scsi_command_table[SCSI_WRITE10] = onWrite10;  scsi_command_table[SCSI_INQUIRY] = onInquiry;  scsi_command_table[SCSI_READ_CAPACITY] = onReadCapacity;  scsi_command_table[SCSI_MODE_SENSE6] =  onModeSense;  scsi_command_table[SCSI_MODE_SENSE10] = onModeSense;  scsi_command_table[SCSI_MODE_SELECT6] = onModeSelect;  scsi_command_table[SCSI_MODE_SELECT10] = onModeSelect;  scsi_command_table[SCSI_VERIFY10] = onVerify;  scsi_command_table[SCSI_READ_BUFFER] = onReadBuffer;  scsi_command_table[SCSI_WRITE_BUFFER] = onWriteBuffer;  scsi_command_table[SCSI_SEND_DIAG] = onSendDiagnostic;  scsi_command_table[SCSI_READ_DEFECT_DATA] = onReadDefectData;  // Serial initialization#if DEBUG > 0  Serial.begin(9600);  // If using a USB->TTL monitor instead of USB serial monitor - you can uncomment this.  //while (!Serial);#endif  // PIN initialization  gpio_mode(LED2, GPIO_OUTPUT_PP);  gpio_mode(LED, GPIO_OUTPUT_OD);  // Image Set Select Init  gpio_mode(IMAGE_SELECT1, GPIO_INPUT_PU);  gpio_mode(IMAGE_SELECT2, GPIO_INPUT_PU);  pinMode(IMAGE_SELECT1, INPUT);  pinMode(IMAGE_SELECT2, INPUT);  int image_file_set = ((digitalRead(IMAGE_SELECT1) == LOW) ? 1 : 0) | ((digitalRead(IMAGE_SELECT2) == LOW) ? 2 : 0);  LED_OFF();#ifdef XCVR  // Transceiver Pin Initialization  pinMode(TR_TARGET, OUTPUT);  pinMode(TR_INITIATOR, OUTPUT);  pinMode(TR_DBP, OUTPUT);    TRANSCEIVER_IO_SET(vTR_INITIATOR,TR_INPUT);#endif  //GPIO(SCSI BUS)Initialization  //Port setting register (lower)//  GPIOB->regs->CRL |= 0x000000008; // SET INPUT W/ PUPD on PAB-PB0  //Port setting register (upper)  //GPIOB->regs->CRH = 0x88888888; // SET INPUT W/ PUPD on PB15-PB8//  GPIOB->regs->ODR = 0x0000FF00; // SET PULL-UPs on PB15-PB8  // DB and DP are input modes  SCSI_DB_INPUT()#ifdef XCVR  TRANSCEIVER_IO_SET(vTR_DBP,TR_INPUT);    // Initiator port  pinMode(ATN, INPUT);  pinMode(BSY, INPUT);  pinMode(ACK, INPUT);  pinMode(RST, INPUT);  pinMode(SEL, INPUT);  TRANSCEIVER_IO_SET(vTR_INITIATOR,TR_INPUT);  // Target port  pinMode(MSG, INPUT);  pinMode(CD, INPUT);  pinMode(REQ, INPUT);  pinMode(IO, INPUT);  TRANSCEIVER_IO_SET(vTR_TARGET,TR_INPUT);#else  // Input port  gpio_mode(ATN, GPIO_INPUT_PU);  gpio_mode(BSY, GPIO_INPUT_PU);  gpio_mode(ACK, GPIO_INPUT_PU);  gpio_mode(RST, GPIO_INPUT_PU);  gpio_mode(SEL, GPIO_INPUT_PU);  // Output port  gpio_mode(MSG, GPIO_OUTPUT_OD);  gpio_mode(CD,  GPIO_OUTPUT_OD);  gpio_mode(REQ, GPIO_OUTPUT_OD);  gpio_mode(IO,  GPIO_OUTPUT_OD);  // Turn off the output port  SCSI_TARGET_INACTIVE()#endif  //Occurs when the RST pin state changes from HIGH to LOW  //attachInterrupt(RST, onBusReset, FALLING);  // Try Full and half clock speeds.  LED_ON();  int mhz = 0;  if (SD.begin(SdSpiConfig(PA4, DEDICATED_SPI, SD_SCK_MHZ(50), &SPI)))  {    mhz = 50;  }  else if (SD.begin(SdSpiConfig(PA4, DEDICATED_SPI, SD_SCK_MHZ(25), &SPI)))  {    mhz = 25;  }  LED_OFF();  if(mhz == 0) {#if DEBUG > 0    Serial.println("SD initialization failed!");#endif    flashError(ERROR_NO_SDCARD);  }  initFileLog(mhz);  readSDCardInfo();  //HD image file open  scsi_id_mask = 0x00;  // Iterate over the root path in the SD card looking for candidate image files.  FsFile root;  char image_set_dir_name[] = "/ImageSetX/";  image_set_dir_name[9] = char(image_file_set) + 0x30;  root.open(image_set_dir_name);  if (root.isDirectory()) {    LOG_FILE.print("Looking for images in: ");    LOG_FILE.println(image_set_dir_name);    LOG_FILE.sync();  } else {    root.close();    root.open("/");  }  findDriveImages(root);  root.close();  FsFile images_all_dir;  images_all_dir.open("/ImageSetAll/");  if (images_all_dir.isDirectory()) {    LOG_FILE.println("Looking for images in: /ImageSetAll/");    LOG_FILE.sync();    findDriveImages(images_all_dir);  }  images_all_dir.close();  // Error if there are 0 image files  if(scsi_id_mask==0) {    LOG_FILE.println("ERROR: No valid images found!");    flashError(ERROR_FALSE_INIT);  }  finalizeFileLog();  LED_OFF();  //Occurs when the RST pin state changes from HIGH to LOW  attachInterrupt(RST, onBusReset, FALLING);}void findDriveImages(FsFile root) {  bool image_ready;  FsFile *file = NULL;  char path_name[MAX_FILE_PATH+1];  root.getName(path_name, sizeof(path_name));  SD.chdir(path_name);  SCSI_DEVICE *dev = NULL;  while (1) {    // Directories can not be opened RDWR, so it will fail, but fails the same way with no file/dir, so we need to peek at the file first.    FsFile file_test = root.openNextFile(O_RDONLY);    char name[MAX_FILE_PATH+1];    file_test.getName(name, MAX_FILE_PATH+1);    // Skip directories and already open files.    if(file_test.isDir() || strncmp(name, "LOG.txt", 7) == 0) {      file_test.close();      continue;    }    // If error there is no next file to open.    if(file_test.getError() > 0) {      file_test.close();      break;    }    // Valid file, open for reading/writing.    file = new FsFile(SD.open(name, O_RDWR));    if(file && file->isFile()) {      if(tolower(name[0]) == 'h' && tolower(name[1]) == 'd') {        // Defaults for Hard Disks        int id  = 1; // 0 and 3 are common in Macs for physical HD and CD, so avoid them.        int lun = 0;        int blk = 512;        // Positionally read in and coerase the chars to integers.        // We only require the minimum and read in the next if provided.        int file_name_length = strlen(name);        if(file_name_length > 2) { // HD[N]          int tmp_id = name[HDIMG_ID_POS] - '0';          // If valid id, set it, else use default          if(tmp_id > -1 && tmp_id < 8) {            id = tmp_id;          } else {            LOG_FILE.print(name);            LOG_FILE.println(" - bad SCSI id in filename, Using default ID 1");          }        }        if(file_name_length > 3) { // HDN[N]          int tmp_lun = name[HDIMG_LUN_POS] - '0';          // If valid lun, set it, else use default          if(tmp_lun == 0 || tmp_lun == 1) {            lun = tmp_lun;          } else {            LOG_FILE.print(name);            LOG_FILE.println(" - bad SCSI LUN in filename, Using default LUN ID 0");          }        }        int blk1 = 0, blk2, blk3, blk4 = 0;        if(file_name_length > 8) { // HD00_[111]          blk1 = name[HDIMG_BLK_POS] - '0';          blk2 = name[HDIMG_BLK_POS+1] - '0';          blk3 = name[HDIMG_BLK_POS+2] - '0';          if(file_name_length > 9) // HD00_NNN[1]            blk4 = name[HDIMG_BLK_POS+3] - '0';        }        if(blk1 == 2 && blk2 == 5 && blk3 == 6) {          blk = 256;        } else if(blk1 == 1 && blk2 == 0 && blk3 == 2 && blk4 == 4) {          blk = 1024;        } else if(blk1 == 2 && blk2 == 0 && blk3 == 4 && blk4 == 8) {          blk  = 2048;        }        if(id < NUM_SCSIID && lun < NUM_SCSILUN) {          dev = &scsi_device_list[id][lun];          LOG_FILE.print(" - ");          LOG_FILE.print(name);          image_ready = hddimageOpen(dev, file, id, lun, blk);          if(image_ready) { // Marked as a responsive ID            scsi_id_mask |= 1<<id;                        switch(dev->m_type)            {              case SCSI_DEVICE_HDD:              // default SCSI HDD              dev->inquiry_block.ansi_version = 1;              dev->inquiry_block.response_format = 1;              dev->inquiry_block.additional_length = 31;              memcpy(dev->inquiry_block.vendor, "QUANTUM", 7);              memcpy(dev->inquiry_block.product, "FIREBALL1", 9);              memcpy(dev->inquiry_block.revision, "1.0", 3);              break;                            case SCSI_DEVICE_OPTICAL:              // default SCSI CDROM              dev->inquiry_block.peripheral_device_type = 5;              dev->inquiry_block.rmb = 1;              dev->inquiry_block.ansi_version = 1;              dev->inquiry_block.response_format = 1;              dev->inquiry_block.additional_length = 42;              dev->inquiry_block.sync = 1;              memcpy(dev->inquiry_block.vendor, "BLUESCSI", 8);              memcpy(dev->inquiry_block.product, "CD-ROM CDU-55S", 14);              memcpy(dev->inquiry_block.revision, "1.9a", 4);              dev->inquiry_block.release = 0x20;              memcpy(dev->inquiry_block.revision_date, "1995", 4);              break;            }            readSCSIDeviceConfig(dev);          }        }      }    } else {      file->close();      delete file;      LOG_FILE.print("Not an image: ");      LOG_FILE.println(name);    }    LOG_FILE.sync();  }  // cd .. before going back.  SD.chdir("/");}/* * Setup initialization logfile */void initFileLog(int success_mhz) {  LOG_FILE = SD.open(LOG_FILENAME, O_WRONLY | O_CREAT | O_TRUNC);  LOG_FILE.println("BlueSCSI <-> SD - https://github.com/erichelgeson/BlueSCSI");  LOG_FILE.print("VERSION: ");  LOG_FILE.print(VERSION);  LOG_FILE.println(BUILD_TAGS);  LOG_FILE.print("DEBUG:");  LOG_FILE.print(DEBUG);  LOG_FILE.print(" SDFAT_FILE_TYPE:");  LOG_FILE.println(SDFAT_FILE_TYPE);  LOG_FILE.print("SdFat version: ");  LOG_FILE.println(SD_FAT_VERSION_STR);  LOG_FILE.print("Sd Format: ");  switch(SD.vol()->fatType()) {    case FAT_TYPE_EXFAT:    LOG_FILE.println("exFAT");    break;    case FAT_TYPE_FAT32:    LOG_FILE.print("FAT32");    case FAT_TYPE_FAT16:    LOG_FILE.print("FAT16");    default:    LOG_FILE.println(" - Consider formatting the SD Card with exFAT for improved performance.");  }  LOG_FILE.print("SPI speed: ");  LOG_FILE.print(success_mhz);  LOG_FILE.println("Mhz");  if(success_mhz == 25) {    LOG_FILE.println("SPI running at half speed - read https://github.com/erichelgeson/BlueSCSI/wiki/Slow-SPI");  }  LOG_FILE.print("SdFat Max FileName Length: ");  LOG_FILE.println(MAX_FILE_PATH);  LOG_FILE.println("Initialized SD Card - lets go!");  LOG_FILE.sync();}/* * Finalize initialization logfile */void finalizeFileLog() {  // View support drive map  LOG_FILE.print("ID");  for(int lun=0;lun<NUM_SCSILUN;lun++)  {    LOG_FILE.print(":LUN");    LOG_FILE.print(lun);  }  LOG_FILE.println(":");  //  for(int id=0;id<NUM_SCSIID;id++)  {    LOG_FILE.print(" ");    LOG_FILE.print(id);    for(int lun=0;lun<NUM_SCSILUN;lun++)    {      SCSI_DEVICE *dev = &scsi_device_list[id][lun];      if( (lun<NUM_SCSILUN) && (dev->m_file))      {        LOG_FILE.print((dev->m_blocksize<1000) ? ": " : ":");        LOG_FILE.print(dev->m_blocksize);      }      else              LOG_FILE.print(":----");    }    LOG_FILE.println(":");  }  LOG_FILE.println("Finished initialization of SCSI Devices - Entering main loop.");  LOG_FILE.sync();  #if DEBUG < 2  LOG_FILE.close();  #endif}static void flashError(const unsigned error){  while(true) {    for(uint8_t i = 0; i < error; i++) {      LED_ON();      delay(250);      LED_OFF();      delay(250);    }    delay(3000);  }}/* * Return from exception and call longjmp */void __attribute__ ((noinline)) longjmpFromInterrupt(jmp_buf jmpb, int retval) __attribute__ ((noreturn));void longjmpFromInterrupt(jmp_buf jmpb, int retval) {  // Address of longjmp with the thumb bit cleared  const uint32_t longjmpaddr = ((uint32_t)longjmp) & 0xfffffffe;  const uint32_t zero = 0;  // Default PSR value, function calls don't require any particular value  const uint32_t PSR = 0x01000000;  // For documentation on what this is doing, see:  // https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/exception-entry-and-return  // Stack frame needs to have R0-R3, R12, LR, PC, PSR (from bottom to top)  // This is being set up to have R0 and R1 contain the parameters passed to longjmp, and PC is the address of the longjmp function.  // This is using existing stack space, rather than allocating more, as longjmp is just going to unroll the stack even further.  // 0xfffffff9 is the EXC_RETURN value to return to thread mode.  asm (      "str %0, [sp];\      str %1, [sp, #4];\      str %2, [sp, #8];\      str %2, [sp, #12];\      str %2, [sp, #16];\      str %2, [sp, #20];\      str %3, [sp, #24];\      str %4, [sp, #28];\      ldr lr, =0xfffffff9;\      bx lr"       :: "r"(jmpb),"r"(retval),"r"(zero), "r"(longjmpaddr), "r"(PSR)  );}/* * Bus reset interrupt. */void onBusReset(void){  if(isHigh(gpio_read(RST))) {    delayMicroseconds(20);    if(isHigh(gpio_read(RST))) {  // BUS FREE is done in the main process//      gpio_mode(MSG, GPIO_OUTPUT_OD);//      gpio_mode(CD,  GPIO_OUTPUT_OD);//      gpio_mode(REQ, GPIO_OUTPUT_OD);//      gpio_mode(IO,  GPIO_OUTPUT_OD);      // Should I enter DB and DBP once?      SCSI_DB_INPUT()      LOGN("BusReset!");      if (m_resetJmp) {        m_resetJmp = false;        // Jumping out of the interrupt handler, so need to clear the interupt source.        uint8 exti = PIN_MAP[RST].gpio_bit;        EXTI_BASE->PR = (1U << exti);        longjmpFromInterrupt(m_resetJmpBuf, 1);      } else {        m_isBusReset = true;      }    }  }}    /* * Enable the reset longjmp, and check if reset fired while it was disabled. */void enableResetJmp(void) {  m_resetJmp = true;  if (m_isBusReset) {    longjmp(m_resetJmpBuf, 1);  }}/* * Read by handshake. */inline byte readHandshake(void){  SCSI_OUT(vREQ,active)  //SCSI_DB_INPUT()  while( ! SCSI_IN(vACK));  byte r = readIO();  SCSI_OUT(vREQ,inactive)  while( SCSI_IN(vACK));  return r;  }/* * Write with a handshake. */inline void writeHandshake(byte d){  // This has a 400ns bus settle delay built in. Not optimal for multi-byte transfers.  GPIOB->regs->BSRR = db_bsrr[d]; // setup DB,DBP (160ns)#ifdef XCVR  TRANSCEIVER_IO_SET(vTR_DBP,TR_OUTPUT)#endif  SCSI_DB_OUTPUT() // (180ns)  // ACK.Fall to DB output delay 100ns(MAX)  (DTC-510B)  SCSI_OUT(vREQ,inactive) // setup wait (30ns)  SCSI_OUT(vREQ,inactive) // setup wait (30ns)  SCSI_OUT(vREQ,inactive) // setup wait (30ns)  SCSI_OUT(vREQ,active)   // (30ns)  //while(!SCSI_IN(vACK)) { if(m_isBusReset){ SCSI_DB_INPUT() return; }}  while(!SCSI_IN(vACK));  // ACK.Fall to REQ.Raise delay 500ns(typ.) (DTC-510B)  GPIOB->regs->BSRR = DBP(0xff);  // DB=0xFF , SCSI_OUT(vREQ,inactive)  // REQ.Raise to DB hold time 0ns  SCSI_DB_INPUT() // (150ns)#ifdef XCVR  TRANSCEIVER_IO_SET(vTR_DBP,TR_INPUT)#endif  while( SCSI_IN(vACK));}#pragma GCC push_options#pragma GCC optimize ("-Os")/* * This loop is tuned to repeat the following pattern: * 1) Set REQ * 2) 5 cycles of work/delay * 3) Wait for ACK * Cycle time tunings are for 72MHz STM32F103 * Alignment matters. For the 3 instruction wait loops,it looks like crossing * an 8 byte prefetch buffer can add 2 cycles of wait every branch taken. */void writeDataLoop(uint32_t blocksize, const byte* srcptr) __attribute__ ((aligned(8)));void writeDataLoop(uint32_t blocksize, const byte* srcptr){#define REQ_ON() (port_b->BRR = req_bit);#define FETCH_BSRR_DB() (bsrr_val = bsrr_tbl[*srcptr++])#define REQ_OFF_DB_SET(BSRR_VAL) port_b->BSRR = BSRR_VAL;#define WAIT_ACK_ACTIVE()   while((*port_a_idr>>(vACK&15)&1))#define WAIT_ACK_INACTIVE() while(!(*port_a_idr>>(vACK&15)&1))  register const byte *endptr= srcptr + blocksize;  // End pointer  register const uint32_t *bsrr_tbl = db_bsrr;      // Table to convert to BSRR  register uint32_t bsrr_val;                       // BSRR value to output (DB, DBP, REQ = ACTIVE)  register uint32_t req_bit = BITMASK(vREQ);  register gpio_reg_map *port_b = PBREG;  register volatile uint32_t *port_a_idr = &(GPIOA->regs->IDR);  // Start the first bus cycle.  FETCH_BSRR_DB();  REQ_OFF_DB_SET(bsrr_val);  REQ_ON();  FETCH_BSRR_DB();  WAIT_ACK_ACTIVE();  REQ_OFF_DB_SET(bsrr_val);  // Align the starts of the do/while and WAIT loops to an 8 byte prefetch.  asm("nop.w;nop");  do{    WAIT_ACK_INACTIVE();    REQ_ON();    // 4 cycles of work    FETCH_BSRR_DB();    // Extra 1 cycle delay while keeping the loop within an 8 byte prefetch.    asm("nop");    WAIT_ACK_ACTIVE();    REQ_OFF_DB_SET(bsrr_val);    // Extra 1 cycle delay, plus 4 cycles for the branch taken with prefetch.    asm("nop");  }while(srcptr < endptr);  WAIT_ACK_INACTIVE();  // Finish the last bus cycle, byte is already on DB.  REQ_ON();  WAIT_ACK_ACTIVE();  REQ_OFF_DB_SET(bsrr_val);  WAIT_ACK_INACTIVE();}#pragma GCC pop_options/* * Data in phase. *  Send len bytes of data array p. */void writeDataPhase(int len, const byte* p){  LOGN("DATAIN PHASE");  SCSI_PHASE_CHANGE(SCSI_PHASE_DATAIN);  // Bus settle delay 400ns. Following code was measured at 800ns before REQ asserted. STM32F103.#ifdef XCVR  TRANSCEIVER_IO_SET(vTR_DBP,TR_OUTPUT)#endif  SCSI_DB_OUTPUT()  writeDataLoop(len, p);}/* * Data in phase. *  Send len block while reading from SD card. */void writeDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len){  LOGN("DATAIN PHASE(SD)");  SCSI_PHASE_CHANGE(SCSI_PHASE_DATAIN);  //Bus settle delay 400ns, file.seek() measured at over 1000ns.  uint64_t pos = (uint64_t)adds * dev->m_blocksize;  dev->m_file->seekSet(pos);#ifdef XCVR  TRANSCEIVER_IO_SET(vTR_DBP,TR_OUTPUT)#endif  SCSI_DB_OUTPUT()  for(uint32_t i = 0; i < len; i++) {      // Asynchronous reads will make it faster ...    m_resetJmp = false;    dev->m_file->read(m_buf, dev->m_blocksize);    enableResetJmp();    writeDataLoop(dev->m_blocksize, m_buf);  }}#pragma GCC push_options#pragma GCC optimize ("-Os")    /* * See writeDataLoop for optimization info. */void readDataLoop(uint32_t blockSize, byte* dstptr) __attribute__ ((aligned(16)));void readDataLoop(uint32_t blockSize, byte* dstptr){  register byte *endptr= dstptr + blockSize - 1;#define REQ_ON() (port_b->BRR = req_bit);#define REQ_OFF() (port_b->BSRR = req_bit);#define WAIT_ACK_ACTIVE()   while((*port_a_idr>>(vACK&15)&1))#define WAIT_ACK_INACTIVE() while(!(*port_a_idr>>(vACK&15)&1))  register uint32_t req_bit = BITMASK(vREQ);  register gpio_reg_map *port_b = PBREG;  register volatile uint32_t *port_a_idr = &(GPIOA->regs->IDR);  REQ_ON();  // Fastest alignment obtained by trial and error.  // Wait loop is within an 8 byte prefetch buffer.  asm("nop");  do {    WAIT_ACK_ACTIVE();    uint32_t ret = port_b->IDR;    REQ_OFF();    *dstptr++ = ~(ret >> 8);    // Move wait loop in to a single 8 byte prefetch buffer    asm("nop;nop;nop");    WAIT_ACK_INACTIVE();    REQ_ON();    // Extra 1 cycle delay    asm("nop");  } while(dstptr<endptr);  WAIT_ACK_ACTIVE();  uint32_t ret = GPIOB->regs->IDR;  REQ_OFF();  *dstptr = ~(ret >> 8);  WAIT_ACK_INACTIVE();}#pragma GCC pop_options/* * Data out phase. *  len block read */void readDataPhase(int len, byte* p){  LOGN("DATAOUT PHASE");  SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT);  // Bus settle delay 400ns. The following code was measured at 450ns before REQ asserted. STM32F103.  readDataLoop(len, p);}/* * Data out phase. *  Write to SD card while reading len block. */void readDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len){  LOGN("DATAOUT PHASE(SD)");  SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT);  //Bus settle delay 400ns, file.seek() measured at over 1000ns.  uint64_t pos = (uint64_t)adds * dev->m_blocksize;  dev->m_file->seekSet(pos);  for(uint32_t i = 0; i < len; i++) {    m_resetJmp = true;    readDataLoop(dev->m_blocksize, m_buf);    m_resetJmp = false;    dev->m_file->write(m_buf, dev->m_blocksize);    // If a reset happened while writing, break and let the flush happen before it is handled.    if (m_isBusReset) {      break;    }  }  dev->m_file->flush();  enableResetJmp();}/* * Data out phase. * Compare to SD card while reading len block. */void verifyDataPhaseSD(SCSI_DEVICE *dev, uint32_t adds, uint32_t len){  LOGN("DATAOUT PHASE(SD)");  SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT);  //Bus settle delay 400ns, file.seek() measured at over 1000ns.  uint64_t pos = (uint64_t)adds * dev->m_blocksize;  dev->m_file->seekSet(pos);  for(uint32_t i = 0; i < len; i++) {    readDataLoop(dev->m_blocksize, m_buf);    // This has just gone through the transfer to make things work, a compare would go here.  }}/* * INQUIRY command processing. */byte onInquiry(SCSI_DEVICE *dev, const byte *cdb){  writeDataPhase(cdb[4] < 36 ? cdb[4] : 36, dev->inquiry_block.raw);  return SCSI_STATUS_GOOD;}/* * REQUEST SENSE command processing. */byte onRequestSense(SCSI_DEVICE *dev, const byte *cdb){  byte buf[18] = {    0x70,   //CheckCondition    0,      //Segment number    dev->m_senseKey,   //Sense key    0, 0, 0, 0,  //information    10,   //Additional data length    0, 0, 0, 0, // command specific information bytes    (byte)(dev->m_additional_sense_code >> 8),    (byte)dev->m_additional_sense_code,    0, 0, 0, 0,  };  dev->m_senseKey = 0;  dev->m_additional_sense_code = 0;  writeDataPhase(cdb[4] < 18 ? cdb[4] : 18, buf);    return SCSI_STATUS_GOOD;}/* * READ CAPACITY command processing. */byte onReadCapacity(SCSI_DEVICE *dev, const byte *cdb){  uint32_t lastlba = dev->m_blockcount - 1; // Points to last LBA  uint8_t buf[8] = {    lastlba >> 24,    lastlba >> 16,    lastlba >> 8,    lastlba,    dev->m_blocksize >> 24,    dev->m_blocksize >> 16,    dev->m_blocksize >> 8,    dev->m_blocksize  };  writeDataPhase(sizeof(buf), buf);  return SCSI_STATUS_GOOD;}/* * Check that the image file is present and the block range is valid. */byte checkBlockCommand(SCSI_DEVICE *dev, uint32_t adds, uint32_t len){  // Check block range is valid  if (adds >= dev->m_blockcount || (adds + len) > dev->m_blockcount) {        dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;    dev->m_additional_sense_code = SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;    return SCSI_STATUS_CHECK_CONDITION;  }  return SCSI_STATUS_GOOD;}/* * READ6 / 10 Command processing. */static byte onRead6(SCSI_DEVICE *dev, const byte *cdb){  unsigned adds = (((uint32_t)cdb[1] & 0x1F) << 16) | ((uint32_t)cdb[2] << 8) | cdb[3];  unsigned len = (cdb[4] == 0) ? 0x100 : cdb[4];  /*  LOGN("onRead6");  LOG("-R ");  LOGHEX(adds);  LOG(":");  LOGHEXN(len);  */     byte sts = checkBlockCommand(dev, adds, len);  if (sts) {    return sts;  }    writeDataPhaseSD(dev, adds, len);  return SCSI_STATUS_GOOD;}static byte onRead10(SCSI_DEVICE *dev, const byte *cdb){  unsigned adds = ((uint32_t)cdb[2] << 24) | ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5];  unsigned len = ((uint32_t)cdb[7] << 8) | cdb[8];  /*  LOGN("onRead10");  LOG("-R ");  LOGHEX(adds);  LOG(":");  LOGHEXN(len);  */  byte sts = checkBlockCommand(dev, adds, len);  if (sts) {    return sts;  }    writeDataPhaseSD(dev, adds, len);  return SCSI_STATUS_GOOD;}/* * WRITE6 / 10 Command processing. */static byte onWrite6(SCSI_DEVICE *dev, const byte *cdb){  unsigned adds = (((uint32_t)cdb[1] & 0x1F) << 16) | ((uint32_t)cdb[2] << 8) | cdb[3];  unsigned len = (cdb[4] == 0) ? 0x100 : cdb[4];  /*  LOGN("onWrite6");  LOG("-W ");  LOGHEX(adds);  LOG(":");  LOGHEXN(len);  */  if(dev->m_type == SCSI_DEVICE_OPTICAL)  {    dev->m_senseKey = SCSI_SENSE_HARDWARE_ERROR;    dev->m_additional_sense_code = SCSI_ASC_WRITE_PROTECTED; // Write Protect    return SCSI_STATUS_CHECK_CONDITION;  }    byte sts = checkBlockCommand(dev, adds, len);  if (sts) {    return sts;  }  readDataPhaseSD(dev, adds, len);  return SCSI_STATUS_GOOD;}static byte onWrite10(SCSI_DEVICE *dev, const byte *cdb){  unsigned adds = ((uint32_t)cdb[2] << 24) | ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5];  unsigned len = ((uint32_t)cdb[7] << 8) | cdb[8];  /*  LOGN("onWrite10");  LOG("-W ");  LOGHEX(adds);  LOG(":");  LOGHEXN(len);  */  if(dev->m_type == SCSI_DEVICE_OPTICAL)  {    dev->m_senseKey = SCSI_SENSE_HARDWARE_ERROR;    dev->m_additional_sense_code = SCSI_ASC_WRITE_PROTECTED; // Write Protect    return SCSI_STATUS_CHECK_CONDITION;  }  byte sts = checkBlockCommand(dev, adds, len);  if (sts) {    return sts;  }  readDataPhaseSD(dev, adds, len);  return SCSI_STATUS_GOOD;}/* * VERIFY10 Command processing. */byte onVerify(SCSI_DEVICE *dev, const byte *cdb){  unsigned adds = ((uint32_t)cdb[2] << 24) | ((uint32_t)cdb[3] << 16) | ((uint32_t)cdb[4] << 8) | cdb[5];  unsigned len = ((uint32_t)cdb[7] << 8) | cdb[8];  byte sts = checkBlockCommand(dev, adds, len);  if (sts) {    return sts;  }  int bytchk = (cdb[1] >> 1) & 0x03;  if (bytchk != 0) {    if (bytchk == 3) {      // Data-Out buffer is single logical block for repeated verification.      len = dev->m_blocksize;    }    LED_ON();    verifyDataPhaseSD(dev, adds, len);    LED_OFF();  }  return SCSI_STATUS_GOOD;}/* * MODE SENSE command processing. */byte onModeSense(SCSI_DEVICE *dev, const byte *cdb){  memset(m_buf, 0, sizeof(m_buf));  int pageCode = cdb[2] & 0x3F;  int pageControl = cdb[2] >> 6;  int a = 4;  byte dbd = cdb[1] & 0x08;  if(cdb[0] == SCSI_MODE_SENSE10) a = 8;  if(dbd == 0) {    byte c[8] = {      0,//Density code      dev->m_blockcount >> 16,      dev->m_blockcount >> 8,      dev->m_blockcount,      0, //Reserve      dev->m_blocksize >> 16,      dev->m_blocksize >> 8,      dev->m_blocksize,        };    memcpy(&m_buf[a], c, 8);    a += 8;  }  switch(pageCode) {  case SCSI_SENSE_MODE_ALL:  case SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY:    m_buf[a + 0] = SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY;    m_buf[a + 1] = 0x0A;    a += 0x0C;    if(pageCode != SCSI_SENSE_MODE_ALL) break;  case SCSI_SENSE_MODE_DISCONNECT_RECONNECT:    m_buf[a + 0] = SCSI_SENSE_MODE_DISCONNECT_RECONNECT;    m_buf[a + 1] = 0x0A;    a += 0x0C;    if(pageCode != SCSI_SENSE_MODE_ALL) break;  case SCSI_SENSE_MODE_FORMAT_DEVICE:  //Drive parameters    m_buf[a + 0] = SCSI_SENSE_MODE_FORMAT_DEVICE; //Page code    m_buf[a + 1] = 0x16; // Page length    if(pageControl != 1) {      m_buf[a + 11] = 0x3F;//Number of sectors / track      m_buf[a + 12] = (byte)(dev->m_blocksize >> 8);      m_buf[a + 13] = (byte)dev->m_blocksize;      m_buf[a + 15] = 0x1; // Interleave    }    a += 0x18;    if(pageCode != SCSI_SENSE_MODE_ALL) break;  case SCSI_SENSE_MODE_DISK_GEOMETRY:  //Drive parameters    m_buf[a + 0] = SCSI_SENSE_MODE_DISK_GEOMETRY; //Page code    m_buf[a + 1] = 0x16; // Page length    if(pageControl != 1) {      unsigned cylinders = dev->m_blockcount / (16 * 63);      m_buf[a + 2] = (byte)(cylinders >> 16); // Cylinders      m_buf[a + 3] = (byte)(cylinders >> 8);      m_buf[a + 4] = (byte)cylinders;      m_buf[a + 5] = 16;   //Number of heads    }    a += 0x18;    if(pageCode != SCSI_SENSE_MODE_ALL) break;  case SCSI_SENSE_MODE_FLEXABLE_GEOMETRY:    m_buf[a + 0] = SCSI_SENSE_MODE_FLEXABLE_GEOMETRY;    m_buf[a + 1] = 0x1E;  // Page length    if(pageControl != 1) {      m_buf[a + 2] = 0x03;       m_buf[a + 3] = 0xE8; // Transfer rate 1 mbit/s      m_buf[a + 4] = 16; // Number of heads      m_buf[a + 5] = 18; // Sectors per track      m_buf[a + 6] = (byte)dev->m_blocksize >> 8;      m_buf[a + 7] = (byte)dev->m_blocksize & 0xff;  // Data bytes per sector    }    a += 0x20;    if(pageCode != SCSI_SENSE_MODE_ALL) break;  case SCSI_SENSE_MODE_CACHING:    m_buf[a + 0] = SCSI_SENSE_MODE_CACHING;    m_buf[a + 1] = 0x0A;  // Page length    if(pageControl != 1) {      m_buf[a + 2] = 0x01; // Disalbe Read Cache so no one asks for Cache Stats page.    }    a += 0x08;    if(pageCode != SCSI_SENSE_MODE_ALL) break;  case SCSI_SENSE_MODE_VENDOR_APPLE:    {      const byte page30[0x14] = {0x41, 0x50, 0x50, 0x4C, 0x45, 0x20, 0x43, 0x4F, 0x4D, 0x50, 0x55, 0x54, 0x45, 0x52, 0x2C, 0x20, 0x49, 0x4E, 0x43, 0x20};      m_buf[a + 0] = SCSI_SENSE_MODE_VENDOR_APPLE; // Page code      m_buf[a + 1] = sizeof(page30); // Page length      if(pageControl != 1) {        memcpy(&m_buf[a + 2], page30, sizeof(page30));      }      a += 2 + sizeof(page30);      if(pageCode != SCSI_SENSE_MODE_ALL) break;    }    break; // Don't want SCSI_SENSE_MODE_ALL falling through to error condition  default:    dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;    dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB;    return SCSI_STATUS_CHECK_CONDITION;    break;  }  if(cdb[0] == SCSI_MODE_SENSE10)  {    m_buf[1] = a - 2;    m_buf[7] = 0x08;  }  else  {    m_buf[0] = a - 1;    m_buf[3] = 0x08;  }  writeDataPhase(cdb[4] < a ? cdb[4] : a, m_buf);  return SCSI_STATUS_GOOD;}    byte onModeSelect(SCSI_DEVICE *dev, const byte *cdb){  unsigned length = 0;  LOGN("onModeSelect");  if(dev->m_type != SCSI_DEVICE_HDD && (cdb[1] & 0x01))  {    dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;    dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB;    return SCSI_STATUS_CHECK_CONDITION;  }  if(cdb[0] == SCSI_MODE_SELECT6)  {    length = cdb[4];  }  else /* SCSI_MODE_SELECT10 */  {    length = cdb[7] << 8;    length |= cdb[8];    if(length > 0x800) { length = 0x800; }  }  readDataPhase(length, m_buf);  //Apple HD SC Setup sends:  //0 0 0 8 0 0 0 0 0 0 2 0 0 2 10 0 1 6 24 10 8 0 0 0  //I believe mode page 0 set to 10 00 is Disable Unit Attention  //Mode page 1 set to 24 10 08 00 00 00 is TB and PER set, read retry count 16, correction span 8  #if DEBUG > 0  for (unsigned i = 0; i < length; i++) {    LOGHEX(m_buf[i]);LOG(" ");  }  LOGN("");  #endif  return SCSI_STATUS_GOOD;}/* * ReZero Unit - Move to Logical Block Zero in file. */byte onReZeroUnit(SCSI_DEVICE *dev, const byte *cdb) {  LOGN("-ReZeroUnit");  // Make sure we have an image with atleast a first byte.  // Actually seeking to the position wont do anything, so dont.  return checkBlockCommand(dev, 0, 0);}/* * WriteBuffer - Used for testing buffer, no change to medium */byte onWriteBuffer(SCSI_DEVICE *dev, const byte *cdb){  byte mode = cdb[1] & 7;   uint32_t allocLength = ((uint32_t)cdb[6] << 16) | ((uint32_t)cdb[7] << 8) | cdb[8];  LOGN("-WriteBuffer");  LOGHEXN(mode);  LOGHEXN(allocLength);  if (mode == MODE_COMBINED_HEADER_DATA && (allocLength - 4) <= SCSI_BUF_SIZE)  {    byte tmp[allocLength];    readDataPhase(allocLength, tmp);    // Drop header    memcpy(m_scsi_buf, (&tmp[4]), allocLength - 4);    #if DEBUG > 0    for (unsigned i = 0; i < allocLength; i++) {      LOGHEX(tmp[i]);LOG(" ");    }    LOGN("");    #endif    return SCSI_STATUS_GOOD;  }  else if ( mode == MODE_DATA && allocLength <= SCSI_BUF_SIZE)  {    readDataPhase(allocLength, m_scsi_buf);    #if DEBUG > 0    for (unsigned i = 0; i < allocLength; i++) {      LOGHEX(m_scsi_buf[i]);LOG(" ");    }    LOGN("");    #endif    return SCSI_STATUS_GOOD;  }  else  {    dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;    dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB;    return SCSI_STATUS_CHECK_CONDITION;  }}/* * ReadBuffer - Used for testing buffer, no change to medium */byte onReadBuffer(SCSI_DEVICE *dev, const byte *cdb){  byte mode = cdb[1] & 7;   uint32_t allocLength = ((uint32_t)cdb[6] << 16) | ((uint32_t)cdb[7] << 8) | cdb[8];    LOGN("-ReadBuffer");  LOGHEXN(mode);  LOGHEXN(allocLength);  if (mode == MODE_COMBINED_HEADER_DATA)  {    byte scsi_buf_response[SCSI_BUF_SIZE + 4];    // four byte read buffer header    scsi_buf_response[0] = 0;    scsi_buf_response[1] = (SCSI_BUF_SIZE >> 16) & 0xff;    scsi_buf_response[2] = (SCSI_BUF_SIZE >> 8) & 0xff;    scsi_buf_response[3] = SCSI_BUF_SIZE & 0xff;    // actual data    memcpy((&scsi_buf_response[4]), m_scsi_buf, SCSI_BUF_SIZE);    writeDataPhase(SCSI_BUF_SIZE + 4, scsi_buf_response);    #if DEBUG > 0    for (unsigned i = 0; i < allocLength; i++) {      LOGHEX(m_scsi_buf[i]);LOG(" ");    }    LOGN("");    #endif    return SCSI_STATUS_GOOD;  }  else if (mode == MODE_DATA)  {    writeDataPhase(allocLength, m_scsi_buf);    #if DEBUG > 0    for (unsigned i = 0; i < allocLength; i++) {      LOGHEX(m_scsi_buf[i]);LOG(" ");    }    LOGN("");    #endif    return SCSI_STATUS_GOOD;  }  else  {    dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;    dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB;    return SCSI_STATUS_CHECK_CONDITION;  }}/* * On Send Diagnostic */byte onSendDiagnostic(SCSI_DEVICE *dev, const byte *cdb){  int self_test = cdb[1] & 0x4;  LOGN("-SendDiagnostic");  LOGHEXN(cdb[1]);  if(self_test)  {    // Don't actually do a test, we're good.    return SCSI_STATUS_GOOD;  }  else  {    dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;    dev->m_additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_CDB;    return SCSI_STATUS_CHECK_CONDITION;  }}/* * Read Defect Data */byte onReadDefectData(SCSI_DEVICE *dev, const byte *cdb){  byte response[4] = {    0x0, // Reserved    cdb[2], // echo back Reserved, Plist, Glist, Defect list format    cdb[7], cdb[8] // echo back defect list length  };  writeDataPhase(4, response);  return SCSI_STATUS_GOOD;}/* * MsgIn2. */void MsgIn2(int msg){  LOGN("MsgIn2");  SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEIN);  // Bus settle delay 400ns built in to writeHandshake  writeHandshake(msg);}/* * Main loop. */void loop() {#ifdef XCVR  // Reset all DB and Target pins, switch transceivers to input  // Precaution against bugs or jumps which don't clean up properly  SCSI_DB_INPUT();  TRANSCEIVER_IO_SET(vTR_DBP,TR_INPUT)  SCSI_TARGET_INACTIVE();  TRANSCEIVER_IO_SET(vTR_INITIATOR,TR_INPUT)#endif  //int msg = 0;  m_msg = 0;  m_lun = 0xff;  SCSI_DEVICE *dev = (SCSI_DEVICE *)0; // HDD image for current SCSI-ID, LUN  // Wait until RST = H, BSY = H, SEL = L  do {} while( SCSI_IN(vBSY) || !SCSI_IN(vSEL) || SCSI_IN(vRST));  // BSY+ SEL-  // If the ID to respond is not driven, wait for the next  //byte db = readIO();  //byte scsiid = db & scsi_id_mask;  byte scsiid = readIO() & scsi_id_mask;  if((scsiid) == 0) {    delayMicroseconds(1);    return;  }  LOGN("Selection");  m_isBusReset = false;  if (setjmp(m_resetJmpBuf) == 1) {    LOGN("Reset, going to BusFree");    goto BusFree;  }  enableResetJmp();    // Set BSY to-when selected  SCSI_BSY_ACTIVE();     // Turn only BSY output ON, ACTIVE  // Ask for a TARGET-ID to respond  m_id = 31 - __builtin_clz(scsiid);  // Wait until SEL becomes inactive  while(isHigh(gpio_read(SEL)) && isLow(gpio_read(BSY))) {  }  #ifdef XCVR  // Reconfigure target pins to output mode, after resetting their values  GPIOB->regs->BSRR = 0x000000E8; // MSG, CD, REQ, IO//  GPIOA->regs->BSRR = 0x00000200; // BSY#endif  SCSI_TARGET_ACTIVE()  // (BSY), REQ, MSG, CD, IO output turned on  //    if(isHigh(gpio_read(ATN))) {    SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEOUT);    // Bus settle delay 400ns. Following code was measured at 350ns before REQ asserted. Added another 50ns. STM32F103.    SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEOUT);// 28ns delay STM32F103    SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEOUT);// 28ns delay STM32F103    bool syncenable = false;    int syncperiod = 50;    int syncoffset = 0;    int msc = 0;    while(isHigh(gpio_read(ATN)) && msc < 255) {      m_msb[msc++] = readHandshake();    }    for(int i = 0; i < msc; i++) {      // ABORT      if (m_msb[i] == 0x06) {        goto BusFree;      }      // BUS DEVICE RESET      if (m_msb[i] == 0x0C) {        syncoffset = 0;        goto BusFree;      }      // IDENTIFY      if (m_msb[i] >= 0x80) {        m_lun = m_msb[i] & 0x1f;        if(m_lun >= NUM_SCSILUN)        {          SCSI_DEVICE *d = &scsi_device_list[m_id][m_lun];          d->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;          d->m_additional_sense_code = SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED;          m_sts |= SCSI_STATUS_CHECK_CONDITION;          goto Status;        }      }      // Extended message      if (m_msb[i] == 0x01) {        // Check only when synchronous transfer is possible        if (!syncenable || m_msb[i + 2] != 0x01) {          MsgIn2(0x07);          break;        }        // Transfer period factor(50 x 4 = Limited to 200ns)        syncperiod = m_msb[i + 3];        if (syncperiod > 50) {          syncperiod = 50;        }        // REQ/ACK offset(Limited to 16)        syncoffset = m_msb[i + 4];        if (syncoffset > 16) {          syncoffset = 16;        }        // STDR response message generation        MsgIn2(0x01);        MsgIn2(0x03);        MsgIn2(0x01);        MsgIn2(syncperiod);        MsgIn2(syncoffset);        break;      }    }  }  LOG("Command:");  SCSI_PHASE_CHANGE(SCSI_PHASE_COMMAND);  // Bus settle delay 400ns. The following code was measured at 20ns before REQ asserted. Added another 380ns. STM32F103.  asm("nop;nop;nop;nop;nop;nop;nop;nop");// This asm causes some code reodering, which adds 270ns, plus 8 nop cycles for an additional 110ns. STM32F103  int len;  byte cmd[12];  cmd[0] = readHandshake();  LOGHEX(cmd[0]);  // Command length selection, reception  static const int cmd_class_len[8]={6,10,10,6,6,12,6,6};  len = cmd_class_len[cmd[0] >> 5];  cmd[1] = readHandshake(); LOG(":");LOGHEX(cmd[1]);  cmd[2] = readHandshake(); LOG(":");LOGHEX(cmd[2]);  cmd[3] = readHandshake(); LOG(":");LOGHEX(cmd[3]);  cmd[4] = readHandshake(); LOG(":");LOGHEX(cmd[4]);  cmd[5] = readHandshake(); LOG(":");LOGHEX(cmd[5]);  // Receive the remaining commands  for(int i = 6; i < len; i++ ) {    cmd[i] = readHandshake();    LOG(":");    LOGHEX(cmd[i]);  }  // LUN confirmation  m_sts = cmd[1]&0xe0;      // Preset LUN in status byte  // if it wasn't set in the IDENTIFY then grab it from the CDB  if(m_lun > NUM_SCSILUN)  {      m_lun = m_sts>>5;  }  LOG(":ID ");  LOG(m_id);  LOG(":LUN ");  LOG(m_lun);  LOGN("");  dev = &(scsi_device_list[m_id][m_lun]);  // HDD Image selection  if(m_lun >= NUM_SCSILUN || !dev->m_file)  {    // REQUEST SENSE and INQUIRY are handled different with invalid LUNs    if(cmd[0] != SCSI_REQUEST_SENSE || cmd[0] != SCSI_INQUIRY)    {      dev->m_senseKey = SCSI_SENSE_ILLEGAL_REQUEST;      dev->m_additional_sense_code = SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED;      m_sts = SCSI_STATUS_CHECK_CONDITION;      goto Status;    }    if(cmd[0] == SCSI_INQUIRY)    {      // Special INQUIRY handling for invalid LUNs      LOGN("onInquiry - InvalidLUN");      dev = &(scsi_device_list[m_id][0]);      byte temp = dev->inquiry_block.raw[0];      // If the LUN is invalid byte 0 of inquiry block needs to be 7fh      dev->inquiry_block.raw[0] = 0x7f;      // only write back what was asked for      writeDataPhase(cmd[4], dev->inquiry_block.raw);      // return it back to normal if it was altered      dev->inquiry_block.raw[0] = temp;      m_sts = SCSI_STATUS_GOOD;      goto Status;    }  }  LED_ON();  m_sts = scsi_command_table[cmd[0]](dev, cmd);  LED_OFF();Status:  LOGN("Sts");  SCSI_PHASE_CHANGE(SCSI_PHASE_STATUS);  // Bus settle delay 400ns built in to writeHandshake  writeHandshake(m_sts);  LOGN("MsgIn");  SCSI_PHASE_CHANGE(SCSI_PHASE_MESSAGEIN);  // Bus settle delay 400ns built in to writeHandshake  writeHandshake(m_msg);BusFree:  LOGN("BusFree");  m_isBusReset = false;  //SCSI_OUT(vREQ,inactive) // gpio_write(REQ, low);  //SCSI_OUT(vMSG,inactive) // gpio_write(MSG, low);  //SCSI_OUT(vCD ,inactive) // gpio_write(CD, low);  //SCSI_OUT(vIO ,inactive) // gpio_write(IO, low);  //SCSI_OUT(vBSY,inactive)  SCSI_TARGET_INACTIVE() // Turn off BSY, REQ, MSG, CD, IO output#ifdef XCVR  TRANSCEIVER_IO_SET(vTR_TARGET,TR_INPUT);  // Something in code linked after this function is performing better with a +4 alignment.  // Adding this nop is causing the next function (_GLOBAL__sub_I_SD) to have an address with a last digit of 0x4.  // Last digit of 0xc also works.  // This affects both with and without XCVR, currently without XCVR doesn't need any padding.  // Until the culprit can be tracked down and fixed, it may be necessary to do manual adjustment.  asm("nop.w");#endif}
 |