ZuluSCSI.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. /*
  2. * ZuluSCSI™
  3. * Copyright (c) 2022-2024 Rabbit Hole Computing™
  4. *
  5. * This project is based on BlueSCSI:
  6. *
  7. * BlueSCSI
  8. * Copyright (c) 2021 Eric Helgeson, Androda
  9. *
  10. * This work incorporates work by following
  11. * Copyright (c) 2023 joshua stein <jcs@jcs.org>
  12. * Copyright (c) 2023 zigzagjoe
  13. *
  14. * This file is free software: you may copy, redistribute and/or modify it
  15. * under the terms of the GNU General Public License as published by the
  16. * Free Software Foundation, either version 2 of the License, or (at your
  17. * option) any later version.
  18. *
  19. * This file is distributed in the hope that it will be useful, but
  20. * WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  22. * General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program. If not, see https://github.com/erichelgeson/bluescsi.
  26. *
  27. * This file incorporates work covered by the following copyright and
  28. * permission notice:
  29. *
  30. * Copyright (c) 2019 komatsu
  31. *
  32. * Permission to use, copy, modify, and/or distribute this software
  33. * for any purpose with or without fee is hereby granted, provided
  34. * that the above copyright notice and this permission notice appear
  35. * in all copies.
  36. *
  37. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  38. * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  39. * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
  40. * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
  41. * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  42. * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  43. * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  44. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  45. */
  46. #include <SdFat.h>
  47. #include <minIni.h>
  48. #include <minIni_cache.h>
  49. #include <string.h>
  50. #include <strings.h>
  51. #include <ctype.h>
  52. #include <zip_parser.h>
  53. #include "ZuluSCSI_config.h"
  54. #include "ZuluSCSI_platform.h"
  55. #include "ZuluSCSI_log.h"
  56. #include "ZuluSCSI_log_trace.h"
  57. #include "ZuluSCSI_settings.h"
  58. #include "ZuluSCSI_disk.h"
  59. #include "ZuluSCSI_initiator.h"
  60. #include "ZuluSCSI_msc.h"
  61. #include "ROMDrive.h"
  62. SdFs SD;
  63. FsFile g_logfile;
  64. bool g_rawdrive_active;
  65. static bool g_romdrive_active;
  66. static bool g_sdcard_present;
  67. #ifndef SD_SPEED_CLASS_WARN_BELOW
  68. #define SD_SPEED_CLASS_WARN_BELOW 10
  69. #endif
  70. /************************************/
  71. /* Status reporting by blinking led */
  72. /************************************/
  73. #define BLINK_STATUS_OK 1
  74. #define BLINK_ERROR_NO_IMAGES 3
  75. #define BLINK_DIRECT_MODE 4
  76. #define BLINK_ERROR_NO_SD_CARD 5
  77. void blinkStatus(int count)
  78. {
  79. uint8_t blink_delay = 250;
  80. if (count == BLINK_DIRECT_MODE)
  81. blink_delay = 100;
  82. for (int i = 0; i < count; i++)
  83. {
  84. LED_ON();
  85. delay(blink_delay);
  86. LED_OFF();
  87. delay(blink_delay);
  88. }
  89. }
  90. extern "C" void s2s_ledOn()
  91. {
  92. LED_ON();
  93. }
  94. extern "C" void s2s_ledOff()
  95. {
  96. LED_OFF();
  97. }
  98. /**************/
  99. /* Log saving */
  100. /**************/
  101. void save_logfile(bool always = false)
  102. {
  103. #ifdef ZULUSCSI_HARDWARE_CONFIG
  104. // Disable logging to the SD card when in direct mode
  105. if (g_hw_config.is_active())
  106. return;
  107. #endif
  108. static uint32_t prev_log_pos = 0;
  109. static uint32_t prev_log_len = 0;
  110. static uint32_t prev_log_save = 0;
  111. uint32_t loglen = log_get_buffer_len();
  112. if (loglen != prev_log_len && g_sdcard_present)
  113. {
  114. // When debug is off, save log at most every LOG_SAVE_INTERVAL_MS
  115. // When debug is on, save after every SCSI command.
  116. if (always || g_log_debug || (LOG_SAVE_INTERVAL_MS > 0 && (uint32_t)(millis() - prev_log_save) > LOG_SAVE_INTERVAL_MS))
  117. {
  118. g_logfile.write(log_get_buffer(&prev_log_pos));
  119. g_logfile.flush();
  120. prev_log_len = loglen;
  121. prev_log_save = millis();
  122. }
  123. }
  124. }
  125. void init_logfile()
  126. {
  127. #ifdef ZULUSCSI_HARDWARE_CONFIG
  128. // Disable logging to the SD card when in direct mode
  129. if (g_hw_config.is_active())
  130. return;
  131. #endif
  132. if (g_rawdrive_active)
  133. return;
  134. static bool first_open_after_boot = true;
  135. bool truncate = first_open_after_boot;
  136. int flags = O_WRONLY | O_CREAT | (truncate ? O_TRUNC : O_APPEND);
  137. g_logfile = SD.open(LOGFILE, flags);
  138. if (!g_logfile.isOpen())
  139. {
  140. logmsg("Failed to open log file: ", SD.sdErrorCode());
  141. }
  142. save_logfile(true);
  143. first_open_after_boot = false;
  144. }
  145. void print_sd_info()
  146. {
  147. uint64_t size = (uint64_t)SD.vol()->clusterCount() * SD.vol()->bytesPerCluster();
  148. logmsg("SD card detected, FAT", (int)SD.vol()->fatType(),
  149. " volume size: ", (int)(size / 1024 / 1024), " MB");
  150. cid_t sd_cid;
  151. if(SD.card()->readCID(&sd_cid))
  152. {
  153. logmsg("SD MID: ", (uint8_t)sd_cid.mid, ", OID: ", (uint8_t)sd_cid.oid[0], " ", (uint8_t)sd_cid.oid[1]);
  154. char sdname[6] = {sd_cid.pnm[0], sd_cid.pnm[1], sd_cid.pnm[2], sd_cid.pnm[3], sd_cid.pnm[4], 0};
  155. logmsg("SD Name: ", sdname);
  156. logmsg("SD Date: ", (int)sd_cid.mdtMonth(), "/", sd_cid.mdtYear());
  157. logmsg("SD Serial: ", sd_cid.psn());
  158. }
  159. sds_t sds = {0};
  160. if (SD.card()->readSDS(&sds) && sds.speedClass() < SD_SPEED_CLASS_WARN_BELOW)
  161. {
  162. logmsg("-- WARNING: Your SD Card Speed Class is ", (int)sds.speedClass(), ". Class ", (int) SD_SPEED_CLASS_WARN_BELOW," or better is recommended for best performance.");
  163. }
  164. }
  165. /*********************************/
  166. /* Harddisk image file handling */
  167. /*********************************/
  168. // When a file is called e.g. "Create_1024M_HD40.txt",
  169. // create image file with specified size.
  170. // Returns true if image file creation succeeded.
  171. //
  172. // Parsing rules:
  173. // - Filename must start with "Create", case-insensitive
  174. // - Separator can be either underscore, dash or space
  175. // - Size must start with a number. Unit of k, kb, m, mb, g, gb is supported,
  176. // case-insensitive, with 1024 as the base. If no unit, assume MB.
  177. // - If target filename does not have extension (just .txt), use ".bin"
  178. bool createImage(const char *cmd_filename, char imgname[MAX_FILE_PATH + 1])
  179. {
  180. if (strncasecmp(cmd_filename, CREATEFILE, strlen(CREATEFILE)) != 0)
  181. {
  182. return false;
  183. }
  184. const char *p = cmd_filename + strlen(CREATEFILE);
  185. // Skip separator if any
  186. while (isspace(*p) || *p == '-' || *p == '_')
  187. {
  188. p++;
  189. }
  190. char *unit = nullptr;
  191. uint64_t size = strtoul(p, &unit, 10);
  192. if (size <= 0 || unit <= p)
  193. {
  194. logmsg("---- Could not parse size in filename '", cmd_filename, "'");
  195. return false;
  196. }
  197. // Parse k/M/G unit
  198. char unitchar = tolower(*unit);
  199. if (unitchar == 'k')
  200. {
  201. size *= 1024;
  202. p = unit + 1;
  203. }
  204. else if (unitchar == 'm')
  205. {
  206. size *= 1024 * 1024;
  207. p = unit + 1;
  208. }
  209. else if (unitchar == 'g')
  210. {
  211. size *= 1024 * 1024 * 1024;
  212. p = unit + 1;
  213. }
  214. else
  215. {
  216. size *= 1024 * 1024;
  217. p = unit;
  218. }
  219. // Skip i and B if part of unit
  220. if (tolower(*p) == 'i') p++;
  221. if (tolower(*p) == 'b') p++;
  222. // Skip separator if any
  223. while (isspace(*p) || *p == '-' || *p == '_')
  224. {
  225. p++;
  226. }
  227. // Copy target filename to new buffer
  228. strncpy(imgname, p, MAX_FILE_PATH);
  229. imgname[MAX_FILE_PATH] = '\0';
  230. int namelen = strlen(imgname);
  231. // Strip .txt extension if any
  232. if (namelen >= 4 && strncasecmp(imgname + namelen - 4, ".txt", 4) == 0)
  233. {
  234. namelen -= 4;
  235. imgname[namelen] = '\0';
  236. }
  237. // Add .bin if no extension
  238. if (!strchr(imgname, '.') && namelen < MAX_FILE_PATH - 4)
  239. {
  240. namelen += 4;
  241. strcat(imgname, ".bin");
  242. }
  243. // Check if file exists
  244. if (namelen <= 5 || SD.exists(imgname))
  245. {
  246. logmsg("---- Image file already exists, skipping '", cmd_filename, "'");
  247. return false;
  248. }
  249. // Create file, try to preallocate contiguous sectors
  250. LED_ON();
  251. FsFile file = SD.open(imgname, O_WRONLY | O_CREAT);
  252. if (!file.preAllocate(size))
  253. {
  254. logmsg("---- Preallocation didn't find contiguous set of clusters, continuing anyway");
  255. }
  256. // Write zeros to fill the file
  257. uint32_t start = millis();
  258. memset(scsiDev.data, 0, sizeof(scsiDev.data));
  259. uint64_t remain = size;
  260. while (remain > 0)
  261. {
  262. if (millis() & 128) { LED_ON(); } else { LED_OFF(); }
  263. platform_reset_watchdog();
  264. size_t to_write = sizeof(scsiDev.data);
  265. if (to_write > remain) to_write = remain;
  266. if (file.write(scsiDev.data, to_write) != to_write)
  267. {
  268. logmsg("---- File writing to '", imgname, "' failed with ", (int)remain, " bytes remaining");
  269. file.close();
  270. LED_OFF();
  271. return false;
  272. }
  273. remain -= to_write;
  274. }
  275. file.close();
  276. uint32_t time = millis() - start;
  277. int kb_per_s = size / time;
  278. logmsg("---- Image creation successful, write speed ", kb_per_s, " kB/s, removing '", cmd_filename, "'");
  279. SD.remove(cmd_filename);
  280. LED_OFF();
  281. return true;
  282. }
  283. static bool typeIsRemovable(S2S_CFG_TYPE type)
  284. {
  285. switch (type)
  286. {
  287. case S2S_CFG_OPTICAL:
  288. case S2S_CFG_MO:
  289. case S2S_CFG_FLOPPY_14MB:
  290. case S2S_CFG_ZIP100:
  291. case S2S_CFG_REMOVABLE:
  292. case S2S_CFG_SEQUENTIAL:
  293. return true;
  294. default:
  295. return false;
  296. }
  297. }
  298. // Iterate over the root path in the SD card looking for candidate image files.
  299. bool findHDDImages()
  300. {
  301. #ifdef ZULUSCSI_HARDWARE_CONFIG
  302. if (g_hw_config.is_active())
  303. {
  304. return false;
  305. }
  306. #endif // ZULUSCSI_HARDWARE_CONFIG
  307. char imgdir[MAX_FILE_PATH];
  308. ini_gets("SCSI", "Dir", "/", imgdir, sizeof(imgdir), CONFIGFILE);
  309. int dirindex = 0;
  310. logmsg("Finding images in directory ", imgdir, ":");
  311. FsFile root;
  312. root.open(imgdir);
  313. if (!root.isOpen())
  314. {
  315. logmsg("Could not open directory: ", imgdir);
  316. }
  317. FsFile file;
  318. bool imageReady;
  319. bool foundImage = false;
  320. int usedDefaultId = 0;
  321. uint8_t removable_count = 0;
  322. uint8_t eject_btn_set = 0;
  323. uint8_t last_removable_device = 255;
  324. while (1)
  325. {
  326. if (!file.openNext(&root, O_READ))
  327. {
  328. // Check for additional directories with ini keys Dir1..Dir9
  329. while (dirindex < 10)
  330. {
  331. dirindex++;
  332. char key[5] = "Dir0";
  333. key[3] += dirindex;
  334. if (ini_gets("SCSI", key, "", imgdir, sizeof(imgdir), CONFIGFILE) != 0)
  335. {
  336. break;
  337. }
  338. }
  339. if (imgdir[0] != '\0')
  340. {
  341. logmsg("Finding images in additional directory Dir", (int)dirindex, " = \"", imgdir, "\":");
  342. root.open(imgdir);
  343. if (!root.isOpen())
  344. {
  345. logmsg("-- Could not open directory: ", imgdir);
  346. }
  347. continue;
  348. }
  349. else
  350. {
  351. break;
  352. }
  353. }
  354. char name[MAX_FILE_PATH+1];
  355. if(!file.isDir() || scsiDiskFolderContainsCueSheet(&file)) {
  356. file.getName(name, MAX_FILE_PATH+1);
  357. file.close();
  358. // Special filename for clearing any previously programmed ROM drive
  359. if(strcasecmp(name, "CLEAR_ROM") == 0)
  360. {
  361. logmsg("-- Special filename: '", name, "'");
  362. romDriveClear();
  363. continue;
  364. }
  365. // Special filename for creating new empty image files
  366. if (strncasecmp(name, CREATEFILE, strlen(CREATEFILE)) == 0)
  367. {
  368. logmsg("-- Special filename: '", name, "'");
  369. char imgname[MAX_FILE_PATH+1];
  370. if (createImage(name, imgname))
  371. {
  372. // Created new image file, use its name instead of the name of the command file
  373. strncpy(name, imgname, MAX_FILE_PATH);
  374. name[MAX_FILE_PATH] = '\0';
  375. }
  376. }
  377. bool use_prefix = false;
  378. bool is_hd = (tolower(name[0]) == 'h' && tolower(name[1]) == 'd');
  379. bool is_cd = (tolower(name[0]) == 'c' && tolower(name[1]) == 'd');
  380. bool is_fd = (tolower(name[0]) == 'f' && tolower(name[1]) == 'd');
  381. bool is_mo = (tolower(name[0]) == 'm' && tolower(name[1]) == 'o');
  382. bool is_re = (tolower(name[0]) == 'r' && tolower(name[1]) == 'e');
  383. bool is_tp = (tolower(name[0]) == 't' && tolower(name[1]) == 'p');
  384. bool is_zp = (tolower(name[0]) == 'z' && tolower(name[1]) == 'p');
  385. #ifdef ZULUSCSI_NETWORK
  386. bool is_ne = (tolower(name[0]) == 'n' && tolower(name[1]) == 'e');
  387. #endif // ZULUSCSI_NETWORK
  388. if (is_hd || is_cd || is_fd || is_mo || is_re || is_tp || is_zp
  389. #ifdef ZULUSCSI_NETWORK
  390. || is_ne
  391. #endif // ZULUSCSI_NETWORK
  392. )
  393. {
  394. // Check if the image should be loaded to microcontroller flash ROM drive
  395. bool is_romdrive = false;
  396. const char *extension = strrchr(name, '.');
  397. if (extension && strcasecmp(extension, ".rom") == 0)
  398. {
  399. is_romdrive = true;
  400. }
  401. // skip file if the name indicates it is not a valid image container
  402. if (!is_romdrive && !scsiDiskFilenameValid(name)) continue;
  403. // Defaults for Hard Disks
  404. int id = 1; // 0 and 3 are common in Macs for physical HD and CD, so avoid them.
  405. int lun = 0;
  406. // Parse SCSI device ID
  407. int file_name_length = strlen(name);
  408. if(file_name_length > 2) { // HD[N]
  409. int tmp_id = name[HDIMG_ID_POS] - '0';
  410. if(tmp_id > -1 && tmp_id < 8)
  411. {
  412. id = tmp_id; // If valid id, set it, else use default
  413. use_prefix = true;
  414. }
  415. else
  416. {
  417. id = usedDefaultId++;
  418. }
  419. }
  420. // Parse SCSI LUN number
  421. if(file_name_length > 3) { // HD0[N]
  422. int tmp_lun = name[HDIMG_LUN_POS] - '0';
  423. if(tmp_lun > -1 && tmp_lun < NUM_SCSILUN) {
  424. lun = tmp_lun; // If valid id, set it, else use default
  425. }
  426. }
  427. // Add the directory name to get the full file path
  428. char fullname[MAX_FILE_PATH * 2 + 2] = {0};
  429. strncpy(fullname, imgdir, MAX_FILE_PATH);
  430. if (fullname[strlen(fullname) - 1] != '/') strcat(fullname, "/");
  431. strcat(fullname, name);
  432. // Check whether this SCSI ID has been configured yet
  433. if (s2s_getConfigById(id))
  434. {
  435. logmsg("-- Ignoring ", fullname, ", SCSI ID ", id, " is already in use!");
  436. continue;
  437. }
  438. // set the default block size now that we know the device type
  439. if (g_scsi_settings.getDevice(id)->blockSize == 0)
  440. {
  441. g_scsi_settings.getDevice(id)->blockSize = is_cd ? DEFAULT_BLOCKSIZE_OPTICAL : DEFAULT_BLOCKSIZE;
  442. }
  443. int blk = getBlockSize(name, id);
  444. #ifdef ZULUSCSI_NETWORK
  445. if (is_ne && !platform_network_supported())
  446. {
  447. logmsg("-- Ignoring ", fullname, ", networking is not supported on this hardware");
  448. continue;
  449. }
  450. #endif // ZULUSCSI_NETWORK
  451. // Type mapping based on filename.
  452. // If type is FIXED, the type can still be overridden in .ini file.
  453. S2S_CFG_TYPE type = S2S_CFG_FIXED;
  454. if (is_cd) type = S2S_CFG_OPTICAL;
  455. if (is_fd) type = S2S_CFG_FLOPPY_14MB;
  456. if (is_mo) type = S2S_CFG_MO;
  457. #ifdef ZULUSCSI_NETWORK
  458. if (is_ne) type = S2S_CFG_NETWORK;
  459. #endif // ZULUSCSI_NETWORK
  460. if (is_re) type = S2S_CFG_REMOVABLE;
  461. if (is_tp) type = S2S_CFG_SEQUENTIAL;
  462. if (is_zp) type = S2S_CFG_ZIP100;
  463. g_scsi_settings.initDevice(id & 7, type);
  464. // Open the image file
  465. if (id < NUM_SCSIID && is_romdrive)
  466. {
  467. logmsg("-- Loading ROM drive from ", fullname, " for id:", id);
  468. imageReady = scsiDiskProgramRomDrive(fullname, id, blk, type);
  469. if (imageReady)
  470. {
  471. foundImage = true;
  472. }
  473. }
  474. else if(id < NUM_SCSIID && lun < NUM_SCSILUN) {
  475. logmsg("-- Opening ", fullname, " for id:", id, " lun:", lun);
  476. if (g_scsi_settings.getDevicePreset(id) != DEV_PRESET_NONE)
  477. {
  478. logmsg("---- Using device preset: ", g_scsi_settings.getDevicePresetName(id));
  479. }
  480. imageReady = scsiDiskOpenHDDImage(id, fullname, lun, blk, type, use_prefix);
  481. if(imageReady)
  482. {
  483. foundImage = true;
  484. }
  485. else
  486. {
  487. logmsg("---- Failed to load image");
  488. }
  489. } else {
  490. logmsg("-- Invalid lun or id for image ", fullname);
  491. }
  492. }
  493. }
  494. }
  495. if(usedDefaultId > 0) {
  496. logmsg("Some images did not specify a SCSI ID. Last file will be used at ID ", usedDefaultId);
  497. }
  498. root.close();
  499. g_romdrive_active = scsiDiskActivateRomDrive();
  500. // Print SCSI drive map
  501. for (int i = 0; i < NUM_SCSIID; i++)
  502. {
  503. const S2S_TargetCfg* cfg = s2s_getConfigByIndex(i);
  504. if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))
  505. {
  506. int capacity_kB = ((uint64_t)cfg->scsiSectors * cfg->bytesPerSector) / 1024;
  507. if (cfg->deviceType == S2S_CFG_NETWORK)
  508. {
  509. logmsg("SCSI ID: ", (int)(cfg->scsiId & 7),
  510. ", Type: ", (int)cfg->deviceType,
  511. ", Quirks: ", (int)cfg->quirks);
  512. }
  513. else
  514. {
  515. logmsg("SCSI ID: ", (int)(cfg->scsiId & S2S_CFG_TARGET_ID_BITS),
  516. ", BlockSize: ", (int)cfg->bytesPerSector,
  517. ", Type: ", (int)cfg->deviceType,
  518. ", Quirks: ", (int)cfg->quirks,
  519. ", Size: ", capacity_kB, "kB",
  520. typeIsRemovable((S2S_CFG_TYPE)cfg->deviceType) ? ", Removable" : ""
  521. );
  522. }
  523. }
  524. }
  525. // count the removable drives and drive with eject enabled
  526. for (uint8_t id = 0; id < S2S_MAX_TARGETS; id++)
  527. {
  528. const S2S_TargetCfg* cfg = s2s_getConfigByIndex(id);
  529. if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED ))
  530. {
  531. if (typeIsRemovable((S2S_CFG_TYPE)cfg->deviceType))
  532. {
  533. removable_count++;
  534. last_removable_device = id;
  535. if ( getEjectButton(id) !=0 )
  536. {
  537. eject_btn_set++;
  538. }
  539. }
  540. }
  541. }
  542. if (removable_count == 1)
  543. {
  544. // If there is a removable device
  545. if (eject_btn_set == 1)
  546. logmsg("Eject set to device with ID: ", last_removable_device);
  547. else if (eject_btn_set == 0)
  548. {
  549. logmsg("Found 1 removable device, to set an eject button see EjectButton in the '", CONFIGFILE,"', or the http://zuluscsi.com/manual");
  550. }
  551. }
  552. else if (removable_count > 1)
  553. {
  554. if (removable_count >= eject_btn_set && eject_btn_set > 0)
  555. {
  556. if (eject_btn_set == removable_count)
  557. logmsg("Eject set on all removable devices:");
  558. else
  559. logmsg("Eject set on the following SCSI IDs:");
  560. for (uint8_t id = 0; id < S2S_MAX_TARGETS; id++)
  561. {
  562. if( getEjectButton(id) != 0)
  563. {
  564. logmsg("-- SCSI ID: ", (int)id, " type: ", (int) s2s_getConfigById(id)->deviceType, " button mask: ", getEjectButton(id));
  565. }
  566. }
  567. }
  568. else
  569. {
  570. logmsg("Multiple removable devices, to set an eject button see EjectButton in the '", CONFIGFILE,"', or the http://zuluscsi.com/manual");
  571. }
  572. }
  573. return foundImage;
  574. }
  575. /************************/
  576. /* Config file loading */
  577. /************************/
  578. void readSCSIDeviceConfig()
  579. {
  580. s2s_configInit(&scsiDev.boardCfg);
  581. for (int i = 0; i < NUM_SCSIID; i++)
  582. {
  583. scsiDiskLoadConfig(i);
  584. }
  585. }
  586. /*********************************/
  587. /* Main SCSI handling loop */
  588. /*********************************/
  589. static bool mountSDCard()
  590. {
  591. // Prepare for mounting new SD card by closing all old files.
  592. // When switching between FAT and exFAT cards the pointers
  593. // are invalidated and accessing old files results in crash.
  594. invalidate_ini_cache();
  595. g_logfile.close();
  596. scsiDiskCloseSDCardImages();
  597. // Check for the common case, FAT filesystem as first partition
  598. if (SD.begin(SD_CONFIG))
  599. {
  600. reload_ini_cache(CONFIGFILE);
  601. return true;
  602. }
  603. // Do we have any kind of card?
  604. if (!SD.card() || SD.sdErrorCode() != 0)
  605. return false;
  606. // Try to mount the whole card as FAT (without partition table)
  607. if (static_cast<FsVolume*>(&SD)->begin(SD.card(), true, 0))
  608. return true;
  609. // Failed to mount FAT filesystem, but card can still be accessed as raw image
  610. return true;
  611. }
  612. static void reinitSCSI()
  613. {
  614. #if defined(ZULUSCSI_HARDWARE_CONFIG)
  615. if (!g_hw_config.is_active() && ini_getbool("SCSI", "Debug", 0, CONFIGFILE))
  616. {
  617. g_log_debug = true;
  618. }
  619. #else
  620. if (ini_getbool("SCSI", "Debug", 0, CONFIGFILE))
  621. {
  622. g_log_debug = true;
  623. }
  624. #endif
  625. if (g_log_debug)
  626. {
  627. g_scsi_log_mask = ini_getl("SCSI", "DebugLogMask", 0xFF, CONFIGFILE) & 0xFF;
  628. if (g_scsi_log_mask == 0)
  629. {
  630. dbgmsg("DebugLogMask set to 0x00, this will silence all debug messages when a SCSI ID has been selected");
  631. }
  632. else if (g_scsi_log_mask != 0xFF)
  633. {
  634. dbgmsg("DebugLogMask set to ", (uint8_t) g_scsi_log_mask, " only SCSI ID's matching the bit mask will be logged");
  635. }
  636. g_log_ignore_busy_free = ini_getbool("SCSI", "DebugIgnoreBusyFree", 0, CONFIGFILE);
  637. if (g_log_ignore_busy_free)
  638. {
  639. dbgmsg("DebugIgnoreBusyFree enabled, BUS_FREE/BUS_BUSY messages suppressed");
  640. }
  641. }
  642. #ifdef PLATFORM_HAS_INITIATOR_MODE
  643. if (platform_is_initiator_mode_enabled())
  644. {
  645. // Initialize scsiDev to zero values even though it is not used
  646. scsiInit();
  647. // Initializer initiator mode state machine
  648. scsiInitiatorInit();
  649. blinkStatus(BLINK_STATUS_OK);
  650. return;
  651. }
  652. #endif
  653. scsiDiskResetImages();
  654. #if defined(ZULUSCSI_HARDWARE_CONFIG)
  655. if (g_hw_config.is_active())
  656. {
  657. bool success;
  658. uint8_t scsiId = g_hw_config.scsi_id();
  659. g_scsi_settings.initDevice(scsiId, g_hw_config.device_type());
  660. logmsg("Direct/Raw mode enabled, using hardware switches for configuration");
  661. logmsg("-- SCSI ID set via DIP switch to ", (int) g_hw_config.scsi_id());
  662. char raw_filename[32];
  663. uint32_t start = g_scsi_settings.getDevice(scsiId)->sectorSDBegin;
  664. uint32_t end = g_scsi_settings.getDevice(scsiId)->sectorSDEnd;
  665. if (start == end && end == 0)
  666. {
  667. strcpy(raw_filename, "RAW:0:0xFFFFFFFF");
  668. }
  669. else
  670. {
  671. snprintf(raw_filename, sizeof(raw_filename), "RAW:0x%X:0x%X", start, end);
  672. }
  673. success = scsiDiskOpenHDDImage(scsiId, raw_filename, 0,
  674. g_hw_config.blocksize(), g_hw_config.device_type());
  675. if (success)
  676. {
  677. if (g_scsi_settings.getDevicePreset(scsiId) != DEV_PRESET_NONE)
  678. {
  679. logmsg("---- Using device preset: ", g_scsi_settings.getDevicePresetName(scsiId));
  680. }
  681. blinkStatus(BLINK_STATUS_OK);
  682. }
  683. delay(250);
  684. blinkStatus(BLINK_DIRECT_MODE);
  685. }
  686. else
  687. #endif // ZULUSCSI_HARDWARE_CONFIG
  688. {
  689. readSCSIDeviceConfig();
  690. findHDDImages();
  691. // Error if there are 0 image files
  692. if (scsiDiskCheckAnyImagesConfigured())
  693. {
  694. // Ok, there is an image, turn LED on for the time it takes to perform init
  695. LED_ON();
  696. delay(100);
  697. }
  698. else
  699. {
  700. #ifdef RAW_FALLBACK_ENABLE
  701. logmsg("No images found, enabling RAW fallback partition");
  702. g_scsi_settings.initDevice(RAW_FALLBACK_SCSI_ID, S2S_CFG_FIXED);
  703. scsiDiskOpenHDDImage(RAW_FALLBACK_SCSI_ID, "RAW:0:0xFFFFFFFF", 0,
  704. RAW_FALLBACK_BLOCKSIZE);
  705. #else
  706. logmsg("No valid image files found!");
  707. #endif // RAW_FALLBACK_ENABLE
  708. blinkStatus(BLINK_ERROR_NO_IMAGES);
  709. }
  710. }
  711. scsiPhyReset();
  712. scsiDiskInit();
  713. scsiInit();
  714. #ifdef ZULUSCSI_NETWORK
  715. if (scsiDiskCheckAnyNetworkDevicesConfigured())
  716. {
  717. platform_network_init(scsiDev.boardCfg.wifiMACAddress);
  718. platform_network_wifi_join(scsiDev.boardCfg.wifiSSID, scsiDev.boardCfg.wifiPassword);
  719. }
  720. #endif // ZULUSCSI_NETWORK
  721. }
  722. // Update firmware by unzipping the firmware package
  723. static void firmware_update()
  724. {
  725. const char firmware_prefix[] = FIRMWARE_PREFIX;
  726. FsFile root = SD.open("/");
  727. FsFile file;
  728. char name[MAX_FILE_PATH + 1];
  729. while (1)
  730. {
  731. if (!file.openNext(&root, O_RDONLY))
  732. {
  733. file.close();
  734. root.close();
  735. return;
  736. }
  737. if (file.isDir())
  738. continue;
  739. file.getName(name, sizeof(name));
  740. if (strlen(name) + 1 < sizeof(firmware_prefix))
  741. continue;
  742. if ( strncasecmp(firmware_prefix, name, sizeof(firmware_prefix) -1) == 0)
  743. {
  744. break;
  745. }
  746. }
  747. logmsg("Found firmware package ", name);
  748. zipparser::Parser parser = zipparser::Parser(FIRMWARE_NAME_PREFIX, sizeof(FIRMWARE_NAME_PREFIX) - 1);
  749. uint8_t buf[512];
  750. int32_t parsed_length;
  751. int bytes_read = 0;
  752. while ((bytes_read = file.read(buf, sizeof(buf))) > 0)
  753. {
  754. parsed_length = parser.Parse(buf, bytes_read);
  755. if (parsed_length == sizeof(buf))
  756. continue;
  757. if (parsed_length >= 0)
  758. {
  759. if (!parser.FoundMatch())
  760. {
  761. parser.Reset();
  762. file.seekSet(file.position() - (sizeof(buf) - parsed_length) + parser.GetCompressedSize());
  763. }
  764. else
  765. {
  766. // seek to start of compressed data in matching file
  767. file.seekSet(file.position() - (sizeof(buf) - parsed_length));
  768. break;
  769. }
  770. }
  771. if (parsed_length < 0)
  772. {
  773. file.close();
  774. root.close();
  775. return;
  776. }
  777. }
  778. if (parser.FoundMatch())
  779. {
  780. logmsg("Unzipping matching firmware with prefix: ", FIRMWARE_NAME_PREFIX);
  781. FsFile target_firmware;
  782. target_firmware.open(&root, "ZuluSCSI.bin", O_BINARY | O_WRONLY | O_CREAT | O_TRUNC);
  783. uint32_t position = 0;
  784. while ((bytes_read = file.read(buf, sizeof(buf))) > 0)
  785. {
  786. if (bytes_read > parser.GetCompressedSize() - position)
  787. bytes_read = parser.GetCompressedSize() - position;
  788. target_firmware.write(buf, bytes_read);
  789. position += bytes_read;
  790. if (position >= parser.GetCompressedSize())
  791. {
  792. break;
  793. }
  794. }
  795. // zip file has a central directory at the end of the file,
  796. // so the compressed data should never hit the end of the file
  797. // so bytes read should always be greater than 0 for a valid datastream
  798. if (bytes_read > 0)
  799. {
  800. target_firmware.close();
  801. file.close();
  802. root.remove(name);
  803. root.close();
  804. logmsg("Update extracted from package, rebooting MCU");
  805. platform_reset_mcu();
  806. }
  807. else
  808. {
  809. target_firmware.close();
  810. logmsg("Error reading firmware package file");
  811. root.remove("ZuluSCSI.bin");
  812. }
  813. }
  814. else
  815. logmsg("Updater did not find matching file in package: ", name);
  816. file.close();
  817. root.close();
  818. }
  819. // Place all the setup code that requires the SD card to be initialized here
  820. // Which is pretty much everything after platform_init and and platform_late_init
  821. static void zuluscsi_setup_sd_card()
  822. {
  823. g_sdcard_present = mountSDCard();
  824. if(!g_sdcard_present)
  825. {
  826. logmsg("SD card init failed, sdErrorCode: ", (int)SD.sdErrorCode(),
  827. " sdErrorData: ", (int)SD.sdErrorData());
  828. if (romDriveCheckPresent())
  829. {
  830. reinitSCSI();
  831. if (g_romdrive_active)
  832. {
  833. logmsg("Enabled ROM drive without SD card");
  834. return;
  835. }
  836. }
  837. do
  838. {
  839. blinkStatus(BLINK_ERROR_NO_SD_CARD);
  840. delay(1000);
  841. platform_reset_watchdog();
  842. g_sdcard_present = mountSDCard();
  843. } while (!g_sdcard_present);
  844. logmsg("SD card init succeeded after retry");
  845. }
  846. firmware_update();
  847. static const char sg_default[] = "Default";
  848. if (g_sdcard_present)
  849. {
  850. char speed_grade_str[10];
  851. ini_gets("SCSI", "SpeedGrade", sg_default, speed_grade_str, sizeof(speed_grade_str), CONFIGFILE);
  852. zuluscsi_speed_grade_t grade = platform_string_to_speed_grade(speed_grade_str, sizeof(speed_grade_str));
  853. if (grade != SPEED_GRADE_DEFAULT)
  854. {
  855. zuluscsi_reclock_status_t status = platform_reclock(grade);
  856. switch (status)
  857. {
  858. case ZULUSCSI_RECLOCK_NOT_SUPPORTED:
  859. logmsg("Reclocking this board is not supported");
  860. break;
  861. case ZULUSCSI_RECLOCK_FAILED:
  862. logmsg("Reclocking failed");
  863. break;
  864. case ZULUSCSI_RECLOCK_SUCCESS:
  865. logmsg("Reclocking was successful");
  866. break;
  867. case ZULUSCSI_RECLOCK_CUSTOM:
  868. logmsg("Custom reclocking timings used");
  869. break;
  870. }
  871. g_sdcard_present = mountSDCard();
  872. reinitSCSI();
  873. }
  874. if (SD.clusterCount() == 0)
  875. {
  876. logmsg("SD card without filesystem!");
  877. }
  878. print_sd_info();
  879. char presetName[32];
  880. ini_gets("SCSI", "System", "", presetName, sizeof(presetName), CONFIGFILE);
  881. scsi_system_settings_t *cfg = g_scsi_settings.initSystem(presetName);
  882. int boot_delay_ms = cfg->initPreDelay;
  883. if (boot_delay_ms > 0)
  884. {
  885. logmsg("Pre SCSI init boot delay in millis: ", boot_delay_ms);
  886. delay(boot_delay_ms);
  887. }
  888. platform_post_sd_card_init();
  889. reinitSCSI();
  890. boot_delay_ms = cfg->initPostDelay;
  891. if (boot_delay_ms > 0)
  892. {
  893. logmsg("Post SCSI init boot delay in millis: ", boot_delay_ms);
  894. delay(boot_delay_ms);
  895. }
  896. }
  897. if (g_sdcard_present)
  898. {
  899. init_logfile();
  900. if (ini_getbool("SCSI", "DisableStatusLED", false, CONFIGFILE))
  901. {
  902. platform_disable_led();
  903. }
  904. }
  905. // Counterpart for the LED_ON in reinitSCSI().
  906. LED_OFF();
  907. }
  908. extern "C" void zuluscsi_setup(void)
  909. {
  910. platform_init();
  911. platform_late_init();
  912. zuluscsi_setup_sd_card();
  913. #ifdef PLATFORM_MASS_STORAGE
  914. static bool check_mass_storage = true;
  915. if ((check_mass_storage && g_scsi_settings.getSystem()->enableUSBMassStorage)
  916. || platform_rebooted_into_mass_storage())
  917. {
  918. check_mass_storage = false;
  919. // perform checks to see if a computer is attached and return true if we should enter MSC mode.
  920. if (platform_sense_msc())
  921. {
  922. zuluscsi_msc_loop();
  923. logmsg("Re-processing filenames and zuluscsi.ini config parameters");
  924. zuluscsi_setup_sd_card();
  925. }
  926. }
  927. #endif
  928. logmsg("Clock set to: ", (int) platform_sys_clock_in_hz(), "Hz");
  929. logmsg("Initialization complete!");
  930. }
  931. extern "C" void zuluscsi_main_loop(void)
  932. {
  933. static uint32_t sd_card_check_time = 0;
  934. static uint32_t last_request_time = 0;
  935. platform_reset_watchdog();
  936. platform_poll();
  937. diskEjectButtonUpdate(true);
  938. #ifdef ZULUSCSI_NETWORK
  939. platform_network_poll();
  940. #endif // ZULUSCSI_NETWORK
  941. #ifdef PLATFORM_HAS_INITIATOR_MODE
  942. if (platform_is_initiator_mode_enabled())
  943. {
  944. scsiInitiatorMainLoop();
  945. save_logfile();
  946. }
  947. else
  948. #endif
  949. {
  950. scsiPoll();
  951. scsiDiskPoll();
  952. scsiLogPhaseChange(scsiDev.phase);
  953. // Save log periodically during status phase if there are new messages.
  954. // In debug mode, also save every 2 seconds if no SCSI requests come in.
  955. // SD card writing takes a while, during which the code can't handle new
  956. // SCSI requests, so normally we only want to save during a phase where
  957. // the host is waiting for us. But for debugging issues where no requests
  958. // come through or a request hangs, it's useful to force saving of log.
  959. if (scsiDev.phase == STATUS || (g_log_debug && (uint32_t)(millis() - last_request_time) > 2000))
  960. {
  961. save_logfile();
  962. last_request_time = millis();
  963. }
  964. }
  965. if (g_sdcard_present)
  966. {
  967. // Check SD card status for hotplug
  968. if (scsiDev.phase == BUS_FREE &&
  969. (uint32_t)(millis() - sd_card_check_time) > 5000)
  970. {
  971. sd_card_check_time = millis();
  972. uint32_t ocr;
  973. if (!SD.card()->readOCR(&ocr))
  974. {
  975. if (!SD.card()->readOCR(&ocr))
  976. {
  977. g_sdcard_present = false;
  978. logmsg("SD card removed, trying to reinit");
  979. }
  980. }
  981. }
  982. }
  983. if (!g_sdcard_present)
  984. {
  985. // Try to remount SD card
  986. do
  987. {
  988. g_sdcard_present = mountSDCard();
  989. if (g_sdcard_present)
  990. {
  991. logmsg("SD card reinit succeeded");
  992. print_sd_info();
  993. reinitSCSI();
  994. init_logfile();
  995. }
  996. else if (!g_romdrive_active)
  997. {
  998. blinkStatus(BLINK_ERROR_NO_SD_CARD);
  999. delay(1000);
  1000. platform_reset_watchdog();
  1001. platform_poll();
  1002. }
  1003. } while (!g_sdcard_present && !g_romdrive_active);
  1004. }
  1005. }