Selaa lähdekoodia

Merge pull request #534 from ZuluSCSI/feature/tape-markers

Basic tape file marking with Read support
Alex Perez 7 kuukautta sitten
vanhempi
sitoutus
953c7ce57c

+ 19 - 6
lib/SCSI2SD/src/firmware/scsi.c

@@ -537,12 +537,25 @@ static void process_Command()
 			memset(scsiDev.data, 0, 256); // Max possible alloc length
 			scsiDev.data[0] = 0xF0;
 			scsiDev.data[2] = scsiDev.target->sense.code & 0x0F;
-
-			scsiDev.data[3] = transfer.lba >> 24;
-			scsiDev.data[4] = transfer.lba >> 16;
-			scsiDev.data[5] = transfer.lba >> 8;
-			scsiDev.data[6] = transfer.lba;
-
+			if (cfg->deviceType == S2S_CFG_SEQUENTIAL)
+			{
+				scsiDev.data[2] |= scsiDev.target->sense.filemark ? 1 << 7 : 0;
+				scsiDev.data[3] |= scsiDev.target->sense.eom ? 1 << 6 : 0;
+			}
+			if (cfg->deviceType == S2S_CFG_SEQUENTIAL)
+			{
+				scsiDev.data[3] = scsiDev.target->sense.info >> 24;
+				scsiDev.data[4] = scsiDev.target->sense.info >> 16;
+				scsiDev.data[5] = scsiDev.target->sense.info >> 8;
+				scsiDev.data[6] = scsiDev.target->sense.info;
+			}
+			else
+			{
+				scsiDev.data[3] = transfer.lba >> 24;
+				scsiDev.data[4] = transfer.lba >> 16;
+				scsiDev.data[5] = transfer.lba >> 8;
+				scsiDev.data[6] = transfer.lba;
+			}
 			// Additional bytes if there are errors to report
 			scsiDev.data[7] = 10; // additional length
 			scsiDev.data[12] = scsiDev.target->sense.asc >> 8;

+ 3 - 0
lib/SCSI2SD/src/firmware/sense.h

@@ -171,6 +171,9 @@ typedef enum
 
 typedef struct
 {
+	bool filemark;
+	bool eom;
+	uint32_t info;
 	uint8_t code;
 	uint16_t asc;
 } ScsiSense;

+ 1 - 1
src/ZuluSCSI.cpp

@@ -443,7 +443,7 @@ bool findHDDImages()
     }
 
     char name[MAX_FILE_PATH+1];
-    if(!file.isDir() || scsiDiskFolderContainsCueSheet(&file)) {
+    if(!file.isDir() || scsiDiskFolderContainsCueSheet(&file) || scsiDiskFolderIsTapeFolder(&file)) {
       file.getName(name, MAX_FILE_PATH+1);
       file.close();
 

+ 2 - 0
src/ZuluSCSI_config.h

@@ -131,6 +131,8 @@
 #define ZIP100_DISK_SIZE    100663296 // bytes
 #define ZIP250_DISK_SIZE    250640384 // bytes
 
+#define TAPE_DEFAULT_NAME  "tape.000"
+
 // Settings for rebooting
 #define REBOOT_INTO_MASS_STORAGE_MAGIC_NUM 0x5eeded
 

+ 49 - 8
src/ZuluSCSI_disk.cpp

@@ -480,8 +480,11 @@ bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_lun, in
         {
             logmsg("---- Configuring as tape drive");
             img.deviceType = S2S_CFG_SEQUENTIAL;
+            img.tape_mark_count = 0;
+            scsiDev.target->sense.filemark = false;
+            scsiDev.target->sense.eom = false;
         }
-                else if (type == S2S_CFG_ZIP100)
+        else if (type == S2S_CFG_ZIP100)
         {
             logmsg("---- Configuration as Iomega Zip100");
             img.deviceType = S2S_CFG_ZIP100;
@@ -576,6 +579,34 @@ bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_lun, in
                 img.cuesheetfile.close();
             }
         }
+        else if (img.deviceType == S2S_CFG_SEQUENTIAL && img.file.isFolder())
+        {
+            // multi file tape that implements tape markers
+            char name[MAX_FILE_PATH + 1] = {0};
+            img.file.getFoldername(name, sizeof(name));
+            img.bin_container.open(name);
+            FsFile file;
+            bool valid = false;
+
+            while(file.openNext(&img.bin_container))
+            {
+                file.getName(name, sizeof(name));
+                if(!file.isDir() && !file.isHidden() && scsiDiskFilenameValid(name))
+                {
+                    valid = true;
+                    img.tape_mark_count++;
+                }
+            }
+            if (!valid)
+            {
+                // if there are no valid image files, create one
+                file.open(&img.bin_container, TAPE_DEFAULT_NAME, O_CREAT);
+                file.close();
+            }
+            img.tape_mark_index = 0;
+            img.tape_mark_block_offset = 0;
+            img.tape_load_next_file = false;
+        }
 
         img.use_prefix = use_prefix;
         img.file.getFilename(img.current_image, sizeof(img.current_image));
@@ -665,6 +696,20 @@ bool scsiDiskFolderContainsCueSheet(FsFile *dir)
     return false;
 }
 
+bool scsiDiskFolderIsTapeFolder(FsFile *dir)
+{
+    char filename[MAX_FILE_PATH + 1];
+    dir->getName(filename, sizeof(filename));
+    // string starts with 'tp', the 3rd character is a SCSI ID, and it has more 3 charters
+    // e.g. "tp0 - tape 01"
+    if (strlen(filename) > 3 && strncasecmp("tp", filename, 2) == 0 
+        && filename[2] >= '0' && filename[2] - '0' < NUM_SCSIID)
+    {
+        return true;
+    }
+    return false;
+}
+
 static void scsiDiskCheckDir(char * dir_name, int target_idx, image_config_t* img, S2S_CFG_TYPE type, const char* type_name)
 {
     if (SD.exists(dir_name))
@@ -788,13 +833,9 @@ static void doPerformEject(image_config_t &img)
     }
 }
 
-
-// Finds filename with the lowest lexical order _after_ the given filename in
-// the given folder. If there is no file after the given one, or if there is
-// no current file, this will return the lowest filename encountered.
-static int findNextImageAfter(image_config_t &img,
+int findNextImageAfter(image_config_t &img,
         const char* dirname, const char* filename,
-        char* buf, size_t buflen)
+        char* buf, size_t buflen, bool ignore_prefix)
 {
     FsFile dir;
     if (dirname[0] == '\0')
@@ -837,7 +878,7 @@ static int findNextImageAfter(image_config_t &img,
             continue;
         }
 
-        if (img.use_prefix && !compare_prefix(filename, buf)) continue;
+        if (!ignore_prefix && img.use_prefix && !compare_prefix(filename, buf)) continue;
 
         // keep track of the first item to allow wrapping
         // without having to iterate again

+ 14 - 3
src/ZuluSCSI_disk.h

@@ -59,9 +59,12 @@ struct image_config_t: public S2S_TargetCfg
     // default option of '0' disables this functionality
     uint8_t ejectButton;
 
-    // For tape drive emulation, current position in blocks
-    uint32_t tape_pos;
-
+    // For tape drive emulation
+    uint32_t tape_pos; // current position in blocks
+    uint32_t tape_mark_index; // a direct relationship to the file in a multi image file tape 
+    uint32_t tape_mark_count; // the number of marks
+    uint32_t tape_mark_block_offset; // Sum of the the previous image file sizes at the current mark
+    bool     tape_load_next_file;
     // True if there is a subdirectory of images for this target
     bool image_directory;
 
@@ -141,6 +144,9 @@ bool scsiDiskFilenameValid(const char* name);
 // This is used when single .cue sheet references multiple .bin files.
 bool scsiDiskFolderContainsCueSheet(FsFile *dir);
 
+// Checks if the directory name is for multi tagged tapes
+bool scsiDiskFolderIsTapeFolder(FsFile *dir);
+
 // Clear the ROM drive header from flash
 bool scsiDiskClearRomDrive();
 // Program ROM drive and rename image file
@@ -153,6 +159,11 @@ bool scsiDiskActivateRomDrive();
 // Returns true if there is at least one image active
 bool scsiDiskCheckAnyImagesConfigured();
 
+// Finds filename with the lowest lexical order _after_ the given filename in
+// the given folder. If there is no file after the given one, or if there is
+// no current file, this will return the lowest filename encountered.
+int findNextImageAfter(image_config_t &img, const char* dirname, const char* filename, char* buf, size_t buflen, bool ignore_prefix = false);
+
 // Gets the next image filename for the target, if configured for multiple
 // images. As a side effect this advances image tracking to the next image.
 // Returns the length of the new image filename, or 0 if the target is not

+ 111 - 3
src/ZuluSCSI_tape.cpp

@@ -32,6 +32,9 @@ extern "C" {
 #include <scsi.h>
 }
 
+#ifdef PREFETCH_BUFFER_SIZE
+
+#endif
 static void doSeek(uint32_t lba)
 {
     image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
@@ -57,6 +60,112 @@ static void doSeek(uint32_t lba)
     }
 }
 
+static void doTapeRead(uint32_t blocks)
+{
+    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
+    uint32_t bytesPerSector = 0;
+    uint32_t capacity = 0;
+    bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
+
+    if (img.bin_container.isOpen())
+    {
+        // multifile tape - multiple file markers
+        char dir_name[MAX_FILE_PATH + 1];
+        char current_filename[MAX_FILE_PATH + 1] = {0};
+        char next_filename[MAX_FILE_PATH + 1] = {0};
+        int filename_len = 0;
+        img.bin_container.getName(dir_name, sizeof(dir_name));
+        img.file.getFilename(current_filename, sizeof(current_filename));
+        if (current_filename[0] == '\0' || img.tape_load_next_file)
+        {
+            // load first file in directory or load next file
+            if (img.tape_load_next_file)
+            {
+                capacity = img.file.size() / bytesPerSector;
+            }
+            filename_len = findNextImageAfter(img, dir_name, current_filename, next_filename, sizeof(next_filename), true);
+            if (filename_len > 0 && img.file.selectImageFile(next_filename))
+            {
+                if (img.tape_load_next_file)
+                {
+                    img.tape_mark_block_offset += capacity;
+                    img.tape_mark_index++;
+                }
+                capacity = img.file.size() / bytesPerSector;
+                dbgmsg("------ Read tape loaded file ", next_filename, " has ", (int) capacity, " sectors with filemark ", (int) img.tape_mark_index ," at the end");
+                img.tape_load_next_file = false;
+            }
+            else
+            {
+                img.tape_load_next_file = false;
+                logmsg("No tape element images found or openable in tape directory ", dir_name);
+                scsiDev.target->sense.filemark = true;
+                scsiDev.status = CHECK_CONDITION;
+                scsiDev.target->sense.code = MEDIUM_ERROR;
+                scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT;
+                scsiDev.phase = STATUS;
+                return;
+            }
+        }
+        else
+            capacity = img.file.size() / bytesPerSector;
+    }
+    else
+    {
+        capacity = img.file.size() / bytesPerSector;
+    }
+
+    bool passed_filemarker = false;
+    bool end_of_tape = false;
+    if (unlikely(((uint64_t) img.tape_pos) - img.tape_mark_block_offset + blocks >= capacity))
+    {
+        // reading past a file, set blocks to end of file
+        uint32_t blocks_till_eof =  capacity - (img.tape_pos - img.tape_mark_block_offset);
+        dbgmsg("------ Read tape went past file marker, blocks left to be read ", (int) blocks_till_eof, " out of ", (int) blocks);
+        passed_filemarker = true;
+        // SCSI-2 Spec: "If the fixed bit is one, the information field shall be set to the requested transfer length minus the
+        //               actual number of blocks read (not including the filemark)"
+        scsiDev.target->sense.info = blocks - blocks_till_eof;
+        blocks = blocks_till_eof;
+        if (img.tape_mark_index < img.tape_mark_count - 1)
+        {
+            img.tape_load_next_file = true;
+        }
+        else
+            end_of_tape = true;
+    }
+
+    dbgmsg("------ Read tape ", (int)blocks, "x", (int)bytesPerSector, " tape position ",(int)img.tape_pos, " file position ", (int)(img.tape_pos - img.tape_mark_block_offset), " ends with file mark ", (int)img.tape_mark_index);
+    if (blocks > 0)
+        scsiDiskStartRead(img.tape_pos - img.tape_mark_block_offset, blocks);
+
+    if (passed_filemarker)
+    {
+        scsiFinishWrite();
+        scsiDev.target->sense.filemark = true;
+        scsiDev.status = CHECK_CONDITION;
+        scsiDev.target->sense.code = NO_SENSE;
+        scsiDev.target->sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
+        scsiDev.phase = STATUS;
+    }
+    img.tape_pos += blocks;
+}
+
+static void doRewind()
+{
+    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
+    img.tape_mark_block_offset = 0;
+    img.tape_mark_index = 0;
+    img.tape_pos = 0;
+    img.tape_load_next_file = false;
+    if (img.bin_container.isOpen())
+    {
+        // multifile tape - multiple
+        char emptyfile[] = "";
+        img.file.selectImageFile(emptyfile);
+    }
+}
+
 extern "C" int scsiTapeCommand()
 {
     image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
@@ -103,8 +212,7 @@ extern "C" int scsiTapeCommand()
 
         if (blocks_to_read > 0)
         {
-            scsiDiskStartRead(img.tape_pos, blocks_to_read);
-            img.tape_pos += blocks_to_read;
+            doTapeRead(blocks_to_read);
         }
     }
     else if (command == 0x0A)
@@ -194,7 +302,7 @@ extern "C" int scsiTapeCommand()
     {
         // REWIND
         // Set tape position back to 0.
-        img.tape_pos = 0;
+        doRewind();
     }
     else if (command == 0x05)
     {