فهرست منبع

Feature for generating image files.

Allows the firmware to create new image files when it detects
a text file with name such as "Create 1024M HD50.txt".
Petteri Aimonen 2 سال پیش
والد
کامیت
5fc17e889e
3فایلهای تغییر یافته به همراه172 افزوده شده و 6 حذف شده
  1. 14 0
      README.md
  2. 155 6
      src/ZuluSCSI.cpp
  3. 3 0
      src/ZuluSCSI_config.h

+ 14 - 0
README.md

@@ -15,6 +15,20 @@ In addition to the simplified filenames style above, the ZuluSCSI firmware also
 The media type can be set in `zuluscsi.ini`, or directly by the file name prefix.
 Supported prefixes are `HD` (hard drive), `CD` (cd-rom), `FD` (floppy), `MO` (magneto-optical), `RE` (generic removeable media), `TP` (sequential tape drive).
 
+Creating new image files
+------------------------
+Empty image files can be created using operating system tools:
+
+* Windows: `fsutil file createnew HD1.img 1073741824` (1 GB)
+* Linux: `fallocate -l 1G HD1.img`
+* Mac OS X: `mkfile -n 1g HD1.img`
+
+ZuluSCSI firmware can also create image files itself.
+To do this, create a text file with filename such as `Create 1024M HD40.txt`.
+The special filename must start with "Create" and be followed by file size and the name of resulting image file.
+The file will be created next time the SD card is inserted.
+The status LED will flash rapidly while image file generation is in progress.
+
 Log files and error indications
 -------------------------------
 Log messages are stored in `zululog.txt`, which is cleared on every boot.

+ 155 - 6
src/ZuluSCSI.cpp

@@ -152,6 +152,140 @@ void print_sd_info()
 /* Harddisk image file handling  */
 /*********************************/
 
+// When a file is called e.g. "Create_1024M_HD40.txt",
+// create image file with specified size.
+// Returns true if image file creation succeeded.
+//
+// Parsing rules:
+// - Filename must start with "Create", case-insensitive
+// - Separator can be either underscore, dash or space
+// - Size must start with a number. Unit of k, kb, m, mb, g, gb is supported,
+//   case-insensitive, with 1024 as the base. If no unit, assume MB.
+// - If target filename does not have extension (just .txt), use ".bin"
+bool createImage(const char *cmd_filename, char imgname[MAX_FILE_PATH + 1])
+{
+  if (strncasecmp(cmd_filename, CREATEFILE, strlen(CREATEFILE)) != 0)
+  {
+    return false;
+  }
+
+  const char *p = cmd_filename + strlen(CREATEFILE);
+
+  // Skip separator if any
+  while (isspace(*p) || *p == '-' || *p == '_')
+  {
+    p++;
+  }
+
+  char *unit = nullptr;
+  uint64_t size = strtoul(p, &unit, 10);
+
+  if (size <= 0 || unit <= p)
+  {
+    logmsg("---- Could not parse size in filename '", cmd_filename, "'");
+    return false;
+  }
+
+  // Parse k/M/G unit
+  char unitchar = tolower(*unit);
+  if (unitchar == 'k')
+  {
+    size *= 1024;
+    p = unit + 1;
+  }
+  else if (unitchar == 'm')
+  {
+    size *= 1024 * 1024;
+    p = unit + 1;
+  }
+  else if (unitchar == 'g')
+  {
+    size *= 1024 * 1024 * 1024;
+    p = unit + 1;
+  }
+  else
+  {
+    size *= 1024 * 1024;
+    p = unit;
+  }
+
+  // Skip i and B if part of unit
+  if (tolower(*p) == 'i') p++;
+  if (tolower(*p) == 'b') p++;
+
+  // Skip separator if any
+  while (isspace(*p) || *p == '-' || *p == '_')
+  {
+    p++;
+  }
+
+  // Copy target filename to new buffer
+  strncpy(imgname, p, MAX_FILE_PATH);
+  imgname[MAX_FILE_PATH] = '\0';
+  int namelen = strlen(imgname);
+
+  // Strip .txt extension if any
+  if (namelen >= 4 && strncasecmp(imgname + namelen - 4, ".txt", 4) == 0)
+  {
+    namelen -= 4;
+    imgname[namelen] = '\0';
+  }
+
+  // Add .bin if no extension
+  if (!strchr(imgname, '.') && namelen < MAX_FILE_PATH - 4)
+  {
+    namelen += 4;
+    strcat(imgname, ".bin");
+  }
+
+  // Check if file exists
+  if (namelen <= 5 || SD.exists(imgname))
+  {
+    logmsg("---- Image file already exists, skipping '", cmd_filename, "'");
+    return false;
+  }
+
+  // Create file, try to preallocate contiguous sectors
+  LED_ON();
+  FsFile file = SD.open(imgname, O_WRONLY | O_CREAT);
+
+  if (!file.preAllocate(size))
+  {
+    logmsg("---- Preallocation didn't find contiguous set of clusters, continuing anyway");
+  }
+
+  // Write zeros to fill the file
+  uint32_t start = millis();
+  memset(scsiDev.data, 0, sizeof(scsiDev.data));
+  uint64_t remain = size;
+  while (remain > 0)
+  {
+    if (millis() & 128) { LED_ON(); } else { LED_OFF(); }
+    platform_reset_watchdog();
+
+    size_t to_write = sizeof(scsiDev.data);
+    if (to_write > remain) to_write = remain;
+    if (file.write(scsiDev.data, to_write) != to_write)
+    {
+      logmsg("---- File writing to '", imgname, "' failed with ", (int)remain, " bytes remaining");
+      file.close();
+      LED_OFF();
+      return false;
+    }
+
+    remain -= to_write;
+  }
+
+  file.close();
+  uint32_t time = millis() - start;
+  int kb_per_s = size / time;
+  logmsg("---- Image creation successful, write speed ", kb_per_s, " kB/s, removing '", cmd_filename, "'");
+  SD.remove(cmd_filename);
+
+  LED_OFF();
+  return true;
+}
+
 // Iterate over the root path in the SD card looking for candidate image files.
 bool findHDDImages()
 {
@@ -208,19 +342,34 @@ bool findHDDImages()
     if(!file.isDir()) {
       file.getName(name, MAX_FILE_PATH+1);
       file.close();
-      bool is_hd = (tolower(name[0]) == 'h' && tolower(name[1]) == 'd');
-      bool is_cd = (tolower(name[0]) == 'c' && tolower(name[1]) == 'd');
-      bool is_fd = (tolower(name[0]) == 'f' && tolower(name[1]) == 'd');
-      bool is_mo = (tolower(name[0]) == 'm' && tolower(name[1]) == 'o');
-      bool is_re = (tolower(name[0]) == 'r' && tolower(name[1]) == 'e');
-      bool is_tp = (tolower(name[0]) == 't' && tolower(name[1]) == 'p');
 
+      // Special filename for clearing any previously programmed ROM drive
       if(strcasecmp(name, "CLEAR_ROM") == 0)
       {
+        logmsg("-- Special filename: '", name, "'");
         scsiDiskClearRomDrive();
         continue;
       }
 
+      // Special filename for creating new empty image files
+      if (strncasecmp(name, CREATEFILE, strlen(CREATEFILE)) == 0)
+      {
+        logmsg("-- Special filename: '", name, "'");
+        char imgname[MAX_FILE_PATH+1];
+        if (createImage(name, imgname))
+        {
+          // Created new image file, use its name instead of the name of the command file
+          strncpy(name, imgname, MAX_FILE_PATH);
+          name[MAX_FILE_PATH] = '\0';
+        }
+      }
+
+      bool is_hd = (tolower(name[0]) == 'h' && tolower(name[1]) == 'd');
+      bool is_cd = (tolower(name[0]) == 'c' && tolower(name[1]) == 'd');
+      bool is_fd = (tolower(name[0]) == 'f' && tolower(name[1]) == 'd');
+      bool is_mo = (tolower(name[0]) == 'm' && tolower(name[1]) == 'o');
+      bool is_re = (tolower(name[0]) == 'r' && tolower(name[1]) == 'e');
+      bool is_tp = (tolower(name[0]) == 't' && tolower(name[1]) == 'p');
       if (is_hd || is_cd || is_fd || is_mo || is_re || is_tp)
       {
         // Check file extension

+ 3 - 0
src/ZuluSCSI_config.h

@@ -36,6 +36,9 @@
 #define LOGFILE     "zululog.txt"
 #define CRASHFILE   "zuluerr.txt"
 
+// Prefix for command file to create new image (case-insensitive)
+#define CREATEFILE "create"
+
 // Log buffer size in bytes, must be a power of 2
 #ifndef LOGBUFSIZE
 #define LOGBUFSIZE 16384