Răsfoiți Sursa

Use single zip file to update any ZuluSCSI

This will update the ZuluSCSI by placing a zip file with the prefix
`zuluscsi-fw` in the root directory of the SD card.

The firmware will extract the appropriate bin file from the container
zip, delete the container zip and then reboot the board. Once the
board has been rebooted, the bootloader installs the bin file.

This is a initial attempt. Currently if the zip file is compressed
with deflate, the code will install the compressed data which will
crash the board.
J. Morio Sakaguchi 11 luni în urmă
părinte
comite
0d41ffebd7

+ 28 - 18
.github/workflows/firmware_build.yml

@@ -1,6 +1,6 @@
 name: Build ZuluSCSI firmware
 
-on: 
+on:
   push:
   workflow_dispatch:
   pull_request:
@@ -11,46 +11,56 @@ jobs:
 #    runs-on: self-hosted
     name: Build firmware on GitHub using latest Ubuntu
     runs-on: ubuntu-latest
-    
+
     steps:
       - name: Check out code from GitHub
         uses: actions/checkout@v4
         with:
           path: ZuluSCSI
           fetch-depth: "0"
-      
+
+      - name: Install dependencies
+        run: |
+          sudo apt-get update
+          sudo apt-get install zip
+
       - name: Install platformio
         run: |
           pip install -U platformio
-      
+
       - name: Build firmware
         run: |
           cd ZuluSCSI
           pio run -v -j8
-    
+
       - name: Rename firmware files
         run: |
           cd ZuluSCSI
           utils/rename_binaries.sh
 
+      - name: Create bin package zip file
+        run: |
+          cd ZuluSCSI
+          utils/create_bin_package_zip.sh
+
       - name: Upload binaries into build artifacts
         uses: actions/upload-artifact@v4
         with:
           path: ZuluSCSI/distrib/*
           name: ZuluSCSI binaries
-      
-      # - name: Upload to latest release
-      #   env:
-      #     GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      #   if: github.ref == 'refs/heads/main'
-      #   run: |
-      #     cd ZuluSCSI
-      #     git tag -d latest
-      #     git tag latest
-      #     git push origin --force latest
-      #     cd distrib
-      #     gh api repos/${GITHUB_REPOSITORY}/releases/tags/latest | jq -r '.assets[] | [.url] | @tsv' | xargs -n 1 gh api -X DELETE || true
-      #     gh release upload --repo ${GITHUB_REPOSITORY} --clobber latest *
+
+      - name: Upload to latest release
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        if: github.ref == 'refs/heads/main'
+        run: |
+          cd ZuluSCSI
+          git tag -d latest
+          git tag latest
+          git push origin --force latest
+          cd distrib
+          gh api repos/${GITHUB_REPOSITORY}/releases/tags/latest | jq -r '.assets[] | [.url] | @tsv' | xargs -n 1 gh api -X DELETE || true
+          gh release upload --repo ${GITHUB_REPOSITORY} --clobber latest *
 
       - name: Upload to newly created release
         env:

+ 239 - 0
lib/ZipParser/zip_parser.cpp

@@ -0,0 +1,239 @@
+/**
+ * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details. 
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+
+#include "zip_parser.h"
+
+
+namespace zipparser
+{
+
+    Parser::Parser()
+    {
+        filename_len = 0;
+        Reset();
+    }
+    Parser::Parser(char const *filename, const size_t length)
+    {
+        Reset();
+        SetMatchingFilename(filename, length);
+    }
+
+    void Parser::Reset()
+    {
+        target = parsing_target::signature;
+        position = 0;
+        deflate = false;
+        filename_match = false;
+        crc = 0;
+    }
+
+    void Parser::SetMatchingFilename(char const *filename, const size_t length)
+    {
+        if (filename[0] == '\0')
+            filename_len = 0;
+        else
+        {
+            this->filename = filename;
+            filename_len = length;
+        }
+    }
+
+
+    int32_t Parser::Parse(uint8_t const *buf, const size_t size)
+    {
+        if (filename_len == 0)
+            return PARSE_ERROR;
+
+        static bool matching = true;
+        static bool central_dir = false;
+        for (size_t idx = 0; idx < size; idx++)
+        {
+            switch (target)
+            {
+                case parsing_target::signature:
+                    if (++position == 1 && buf[idx] == 'P')
+                        break;
+                    if (position == 2 && buf[idx] == 'K')
+                    {
+                        central_dir = false;
+                        break;
+                    }
+                    if (position == 3 && buf[idx] == 0x03)
+                        break;
+                    if (position == 3 && buf[idx] == 0x01)
+                    {
+                        central_dir = true;
+                        break;
+                    }
+                    if (position == 4 && central_dir && buf[idx] == 0x2)
+                    {
+                        return PARSE_CENTRAL_DIR;
+                    }
+                    if (position == 4 && buf[idx] == 0x04)
+                    {
+                        position = 0;
+                        target = parsing_target::version;
+                        break;
+                    }
+                    return PARSE_ERROR;
+                break;
+                case parsing_target::version:
+                    if (++position == 2)
+                    {
+                        position = 0;
+                        target = parsing_target::flag;
+                    }
+                break;
+                case parsing_target::flag:
+                    if (++position == 2)
+                    {
+                        position = 0;
+                        target = parsing_target::method;
+                    }
+                break;
+                case parsing_target::method:
+                    if (++position == 1)
+                    {
+                        if (buf[idx] == 0x08 || buf[idx] == 0x00)
+                            deflate = buf[idx] == 0x08;
+                        else
+                            return PARSE_UNSUPPORTED_COMPRESSION;
+                    }
+                    if (position == 2)
+                    {
+                        if (buf[idx] == 0)
+                        {
+                            position = 0;
+                            target = parsing_target::modify_time;
+                        }
+                        else
+                            return PARSE_UNSUPPORTED_COMPRESSION;
+                    }
+                break;
+                case parsing_target::modify_time:
+                    if (++position == 2)
+                    {
+                        position = 0;
+                        target = parsing_target::modify_date;
+                    }
+                break;
+                case parsing_target::modify_date:
+                    if (++position == 2)
+                    {
+                        position = 0;
+                        target = parsing_target::crc;
+                        crc = 0;
+                    }
+                break;
+                case parsing_target::crc:
+                    crc |= buf[idx] << (8 * position++);
+                    if (position == 4)
+                    {
+                        target = parsing_target::size_compressed;
+                        compressed_data_size = 0;
+                        position = 0;
+                    }
+                break;
+                case parsing_target::size_compressed:
+                    compressed_data_size |= buf[idx] << (8 * position++);
+                    if (position == 4)
+                    {
+                        target = parsing_target::size_uncompressed;
+                        uncompressed_data_size = 0;
+                        position = 0;
+                    }
+                break;
+                case parsing_target::size_uncompressed:
+                    uncompressed_data_size |= buf[idx] << (8 * position++);
+                    if (position == 4)
+                    {
+                        target = parsing_target::filename_len;
+                        current_zip_filename_len = 0;
+                        position = 0;
+                    }
+                break;
+                case parsing_target::filename_len:
+                    current_zip_filename_len |= buf[idx] << (8 * position++);
+                    if (position == 2)
+                    {
+                        target = parsing_target::extra_field_len;
+                        extra_field_len = 0;
+                        position = 0;
+                    }
+                break;
+                case parsing_target::extra_field_len:
+                    extra_field_len |= buf[idx] << (8 * position++);
+                    if (position == 2)
+                    {
+                        target = parsing_target::filename;
+                        position = 0;
+                        filename_match = false;
+                        matching = true;
+                    }
+                break;
+                case parsing_target::filename:
+                    if (position <= current_zip_filename_len - 1)
+                    {    
+                        if (matching && position < filename_len && filename[position] != buf[idx])
+                            matching = false;
+                        if (position == filename_len - 1 && matching)
+                            filename_match = true;
+                        if (position == current_zip_filename_len -1)
+                        {
+                            target = parsing_target::extra_field;
+                            matching = true;
+                            position = 0;
+                            if (extra_field_len == 0)
+                            {
+                                target = parsing_target::end;
+                                return idx + 1;
+                            }
+                        }
+                        else
+                            position++;
+                    }
+                break;
+                case parsing_target::extra_field:
+                    // extra_field_len should be at least 1 by this time
+                    if (++position == extra_field_len)
+                    {
+                        target = parsing_target::end;
+                        return idx + 1;
+                    }
+                break;
+                case parsing_target::end:
+                    return 0;
+                break;
+            }
+        }
+        return size;
+    }
+    bool Parser::FoundMatch()
+    {
+        return filename_match;
+    }
+
+    uint32_t Parser::GetCompressedSize()
+    {
+        return compressed_data_size;
+    }
+}
+

+ 64 - 0
lib/ZipParser/zip_parser.h

@@ -0,0 +1,64 @@
+/**
+ * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details. 
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+
+#pragma once
+
+#include <stdint.h>
+#include <strings.h>
+namespace zipparser
+{
+    enum class parsing_target {signature, version, flag, method, modify_time, modify_date, 
+                                crc, size_compressed, size_uncompressed, filename_len, 
+                                extra_field_len, filename, extra_field, end};
+
+    class Parser
+    {
+        public:
+            Parser();
+            Parser(char const *filename, const size_t length);
+            void SetMatchingFilename(char const *filename, const size_t length);
+            void Reset();
+            static const int32_t PARSE_ERROR = -1;
+            static const int32_t PARSE_CENTRAL_DIR = -2;
+            static const int32_t PARSE_UNSUPPORTED_COMPRESSION = -3;
+            // A state machine that per byte processes the incoming buffer
+            // \param buf a pointer to binary data to be processed
+            // \param size of data to be processed, can by 1 for a single byte at a time
+            // \returns the number of bytes processed or -1 if an error ocurred
+            int32_t Parse(uint8_t const *buf, const size_t size);
+            bool FoundMatch();
+            uint32_t GetCompressedSize();
+            
+        protected:
+            bool filename_match;
+            char const *filename;
+            size_t filename_len;
+            size_t current_zip_filename_len;
+            size_t extra_field_len;
+            uint32_t compressed_data_size;
+            uint32_t uncompressed_data_size;
+            parsing_target target;
+            size_t position;
+            bool deflate;
+            uint32_t crc;
+
+    };
+}

+ 10 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp

@@ -22,6 +22,7 @@
 #include "ZuluSCSI_platform.h"
 #include "gd32f20x_sdio.h"
 #include "gd32f20x_fmc.h"
+#include "gd32f20x_fwdgt.h"
 #include "ZuluSCSI_log.h"
 #include "ZuluSCSI_config.h"
 #include "usbd_conf.h"
@@ -392,6 +393,7 @@ static bool get_direct_mode(uint32_t port, uint32_t pin, const char *switch_name
 
 void platform_late_init()
 {
+
     // Initialize usb for CDC serial output
     usb_serial_init();
 
@@ -716,6 +718,14 @@ void platform_reset_watchdog()
     usb_log_poll();
 }
 
+void platform_reset_mcu()
+{
+    // reset in 2 sec ( 1 / (40KHz / 32) * 2500 == 2sec)
+    fwdgt_config(2500, FWDGT_PSC_DIV32);
+    fwdgt_enable();
+
+}
+
 // Poll function that is called every few milliseconds.
 // Can be left empty or used for platform-specific processing.
 void platform_poll()

+ 3 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h

@@ -90,6 +90,9 @@ void platform_disable_led(void);
 // Setup soft watchdog
 void platform_reset_watchdog();
 
+// Reset MCU after a certain amount of time
+void platform_reset_mcu();
+
 // Poll function that is called every few milliseconds.
 // The SD card is free to access during this time, and pauses up to
 // few milliseconds shouldn't disturb SCSI communication.

+ 9 - 0
lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform.cpp

@@ -22,6 +22,7 @@
 #include "ZuluSCSI_platform.h"
 #include "gd32f4xx_sdio.h"
 #include "gd32f4xx_fmc.h"
+#include "gd32f4xx_fwdgt.h"
 #include "ZuluSCSI_log.h"
 #include "ZuluSCSI_config.h"
 #include "usb_hs.h"
@@ -511,6 +512,14 @@ void platform_reset_watchdog()
     g_watchdog_timeout = WATCHDOG_CRASH_TIMEOUT;
 }
 
+void platform_reset_mcu()
+{
+    // reset in 2 sec ( 1 / (32KHz / 32) * 2000 == 2sec)
+    fwdgt_config(2000, FWDGT_PSC_DIV32);
+    fwdgt_enable();
+
+}
+
 // Poll function that is called every few milliseconds.
 // Can be left empty or used for platform-specific processing.
 void platform_poll()

+ 3 - 0
lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform.h

@@ -82,6 +82,9 @@ void platform_disable_led(void);
 // Setup soft watchdog
 void platform_reset_watchdog();
 
+// Reset MCU
+void platform_reset_mcu();
+
 // Poll function that is called every few milliseconds.
 // The SD card is free to access during this time, and pauses up to
 // few milliseconds shouldn't disturb SCSI communication.

+ 5 - 0
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.cpp

@@ -863,6 +863,11 @@ void platform_poll()
 #endif // ENABLE_AUDIO_OUTPUT
 }
 
+void platform_reset_mcu()
+{
+    watchdog_reboot(0, 0, 2000);
+}
+
 uint8_t platform_get_buttons()
 {
     uint8_t buttons = 0;

+ 4 - 0
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.h

@@ -99,6 +99,10 @@ bool platform_is_initiator_mode_enabled();
 // Setup soft watchdog if supported
 void platform_reset_watchdog();
 
+// Reset MCU
+void platform_reset_mcu();
+
+
 // Poll function that is called every few milliseconds.
 // The SD card is free to access during this time, and pauses up to
 // few milliseconds shouldn't disturb SCSI communication.

+ 13 - 1
platformio.ini

@@ -3,12 +3,17 @@
 [platformio]
 default_envs = ZuluSCSIv1_0, ZuluSCSIv1_0_mini, ZuluSCSIv1_1_plus, ZuluSCSI_RP2040, ZuluSCSI_RP2040_Audio, ZuluSCSI_Pico, ZuluSCSI_Pico_DaynaPORT, ZuluSCSI_BS2, ZuluSCSI_Pico_2, ZuluSCSI_Pico_2_DaynaPORT
 
+[env]
+build_flags =
+    -DBUILD_ENV=$PIOENV
+
 ; Example platform to serve as a base for porting efforts
 [env:template]
 platform = ststm32
 framework = arduino
 board = bluepill_f103c8
 build_flags =
+    ${env.build_flags}
     -Os -Isrc
     -DLOGBUFSIZE=512
     -DPREFETCH_BUFFER_SIZE=0
@@ -49,7 +54,9 @@ extra_scripts = src/build_bootloader.py
 debug_build_flags = 
      -Os -Wall -Wno-sign-compare -ggdb -g3
 build_flags = 
+    ${env.build_flags}
      -Os -Wall -Wno-sign-compare -ggdb -g3 -Isrc
+     -DBUILD_ENV=$PIOENV
      -D__SYSTEM_CLOCK_120M_PLL_IRC8M=120000000
      -DSPI_DRIVER_SELECT=3
      -DSD_CHIP_SELECT_MODE=2
@@ -64,6 +71,7 @@ build_flags =
 [env:ZuluSCSIv1_0_mini]
 extends = env:ZuluSCSIv1_0
 build_flags = 
+    ${env.build_flags}
      -Os -Wall -Wno-sign-compare -ggdb -g3 -Isrc
      -D__SYSTEM_CLOCK_120M_PLL_IRC8M=120000000
      -DSPI_DRIVER_SELECT=3
@@ -79,7 +87,8 @@ build_flags =
 ; ZuluSCSI V1.1+ hardware platforms, this support v1.1, v1.1 ODE, and vl.2
 [env:ZuluSCSIv1_1_plus]
 extends = env:ZuluSCSIv1_0
-build_flags = 
+build_flags =
+    ${env.build_flags}
      -Os -Wall -Wno-sign-compare -ggdb -g3 -Isrc
      -D__SYSTEM_CLOCK_120M_PLL_IRC8M=120000000
      -DSPI_DRIVER_SELECT=3
@@ -106,6 +115,7 @@ board_build.ldscript = ${BUILD_DIR}/rp_linker.ld ; created by src/process-linker
 framework = arduino
 lib_deps =
     SdFat=https://github.com/rabbitholecomputing/SdFat#2.2.3-gpt
+    uzlib=https://github.com/pfalcon/uzlib
     minIni
     SCSI2SD
     CUEParser=https://github.com/rabbitholecomputing/CUEParser
@@ -115,6 +125,7 @@ debug_tool = cmsis-dap
 debug_build_flags =
     -O2 -ggdb -g3
 build_flags =
+    ${env.build_flags}
     -O2 -Isrc -ggdb -g3
     -Wall -Wno-sign-compare -Wno-ignored-qualifiers
     -DSPI_DRIVER_SELECT=3
@@ -275,6 +286,7 @@ extra_scripts = src/build_bootloader.py
 debug_tool = stlink
 debug_build_flags = -Os -ggdb -g3
 build_flags = 
+    ${env.build_flags}
      -Os -Wall -Wno-sign-compare -ggdb -g3 -Isrc
      -D__SYSTEM_CLOCK_200M_PLL_IRC16M=200000000
      -DSPI_DRIVER_SELECT=3

+ 111 - 2
src/ZuluSCSI.cpp

@@ -50,6 +50,7 @@
 #include <string.h>
 #include <strings.h>
 #include <ctype.h>
+#include <zip_parser.h>
 #include "ZuluSCSI_config.h"
 #include "ZuluSCSI_platform.h"
 #include "ZuluSCSI_log.h"
@@ -816,6 +817,112 @@ static void reinitSCSI()
 #endif // ZULUSCSI_NETWORK
 
 }
+
+// Update firmware by unzipping the firmware package
+static void firmware_update()
+{
+  const char firmware_prefix[] = FIRMWARE_PREFIX;
+  FsFile root = SD.open("/");
+  FsFile file;
+  char name[MAX_FILE_PATH + 1];
+  while (1)
+  {
+    if (!file.openNext(&root, O_RDONLY))
+    {
+      file.close();
+      root.close();
+      return;
+    }
+    if (file.isDir())
+      continue;
+
+    file.getName(name, sizeof(name));
+    if (strlen(name) + 1 < sizeof(firmware_prefix))
+      continue;
+    if ( strncasecmp(firmware_prefix, name, sizeof(firmware_prefix) -1) == 0)
+    {
+      break;
+    }
+  }
+
+  logmsg("Found firmware package ", name);
+
+  zipparser::Parser parser = zipparser::Parser(FIRMWARE_NAME_PREFIX, sizeof(FIRMWARE_NAME_PREFIX) - 1);
+  uint8_t buf[512];
+  int32_t parsed_length;
+  int bytes_read = 0;
+  while ((bytes_read = file.read(buf, sizeof(buf))) > 0)
+  {
+    parsed_length = parser.Parse(buf, bytes_read);
+    if (parsed_length == sizeof(buf))
+       continue;
+    if (parsed_length >= 0)
+    {
+      if (!parser.FoundMatch())
+      {
+        parser.Reset();
+        file.seekSet(file.position() - (sizeof(buf) - parsed_length) + parser.GetCompressedSize());
+      }
+      else
+      {
+        // seek to start of compressed data in matching file
+        file.seekSet(file.position() - (sizeof(buf) - parsed_length));
+        break;
+      }
+    }
+    if (parsed_length < 0)
+    {
+      file.close();
+      root.close();
+      return;
+    }
+  }
+
+
+  if (parser.FoundMatch())
+  {
+
+    logmsg("Unzipping matching firmware with prefix: ", FIRMWARE_NAME_PREFIX);
+    FsFile target_firmware;
+    target_firmware.open(&root, "zuluscsi.bin", O_BINARY | O_WRONLY | O_CREAT | O_TRUNC);
+    uint32_t position = 0;
+    while ((bytes_read = file.read(buf, sizeof(buf))) > 0)
+    {
+      if (bytes_read > parser.GetCompressedSize() - position)
+        bytes_read =  parser.GetCompressedSize() - position;
+      target_firmware.write(buf, bytes_read);
+      position += bytes_read;
+      if (position >= parser.GetCompressedSize())
+      {
+        break;
+      }
+    }
+    // zip file has a central directory at the end of the file,
+    // so the compressed data should never hit the end of the file
+    // so bytes read should always be greater than 0 for a valid datastream
+    if (bytes_read > 0)
+    {
+      target_firmware.close();
+      file.close();
+      root.remove(name);
+      root.close();
+      logmsg("Update extracted from package, rebooting MCU");
+      platform_reset_mcu();
+    }
+    else
+    {
+      target_firmware.close();
+      logmsg("Error reading firmware package file");
+      root.remove("zuluscsi.bin");
+    }
+  }
+  else
+    logmsg("Updater did not find matching file in package: ", name);
+  file.close();
+  root.close();
+}
+
+
 // Place all the setup code that requires the SD card to be initialized here
 // Which is pretty much everything after platform_init and and platform_late_init
 static void zuluscsi_setup_sd_card()
@@ -847,8 +954,10 @@ static void zuluscsi_setup_sd_card()
     } while (!g_sdcard_present);
     logmsg("SD card init succeeded after retry");
   }
-  
-  static const char sg_default[] = "Default"; 
+
+  firmware_update();
+
+  static const char sg_default[] = "Default";
   if (g_sdcard_present)
   {
     char speed_grade_str[10];

+ 6 - 1
src/ZuluSCSI_config.h

@@ -28,9 +28,13 @@
 #include <ZuluSCSI_platform_config.h>
 
 // Use variables for version number
-#define FW_VER_NUM      "24.11.26"
+#define FW_VER_NUM      "24.11.29"
 #define FW_VER_SUFFIX   "devel"
 
+#define DEF_STRINGFY(DEF) STRINGFY(DEF)
+#define STRINGFY(STR) #STR
+#define FIRMWARE_NAME_PREFIX DEF_STRINGFY(BUILD_ENV)
+
 #define ZULU_FW_VERSION FW_VER_NUM "-" FW_VER_SUFFIX
 #define INQUIRY_NAME  PLATFORM_NAME " v" ZULU_FW_VERSION
 #define TOOLBOX_API 0
@@ -39,6 +43,7 @@
 #define CONFIGFILE  "zuluscsi.ini"
 #define LOGFILE     "zululog.txt"
 #define CRASHFILE   "zuluerr.txt"
+#define FIRMWARE_PREFIX "zuluscsi-fw"
 
 // Prefix for command file to create new image (case-insensitive)
 #define CREATEFILE "create"

+ 30 - 0
utils/create_bin_package_zip.sh

@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+#
+# ZuluSCSI™ file is licensed under the GPL version 3 or any later version. 
+#
+# https://www.gnu.org/licenses/gpl-3.0.html
+# ----
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version. 
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details. 
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+
+# This script renames the built binaries according to version
+# number and platform.
+
+cd distrib
+DATE=$(date +%Y-%m-%d)
+VERSION=$(git describe --always)
+FWPACKAGE=zuluscsi-fw_"$DATE"_"$VERSION".zip
+zip -0 "$FWPACKAGE" *.bin