BlueSCSI.cpp 37 KB

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