Give the Gigabeat S bootloader the ability to untar a tarball.
To test a build, use 'make tar' and send the tar to the unit. The bootloader will unarchive it and delete it before loading the main binary.
This is a temporary hack to make testing possible until we have a better way of sending a complete build.
Also enable writing to the disk by disabling the optimised write stubs.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16338 a1c6a512-1295-4272-9138-f99709370657
diff --git a/bootloader/gigabeat-s.c b/bootloader/gigabeat-s.c
index f3e2917..eba3cd7 100644
--- a/bootloader/gigabeat-s.c
+++ b/bootloader/gigabeat-s.c
@@ -47,10 +47,11 @@
 #include "avic-imx31.h"
 #include <stdarg.h>
 
+#define TAR_CHUNK 512
+#define TAR_HEADER_SIZE 157
+
 char version[] = APPSVERSION;
-char buf[MAX_PATH];
 char basedir[] = "/Content/0b00/00/"; /* Where files sent via MTP are stored */
-char model[5];
 int (*kernel_entry)(void);
 extern void reference_system_c(void);
 
@@ -61,11 +62,89 @@
     reference_system_c();
 }
 
+void untar(int tar_fd)
+{
+    char header[TAR_HEADER_SIZE];
+    char copybuf[TAR_CHUNK];
+    char path[102];
+    int fd, i, size = 0, pos = 0;
+
+    while (1)
+    {
+        read(tar_fd, header, TAR_HEADER_SIZE);
+
+        if (*header == '\0')  /* Check for EOF */
+            break;
+
+        /* Parse the size field */
+        size = 0;
+        for (i = 124 ; i < 124 + 11 ; i++) {
+            size = (8 * size) + header[i] - '0';
+        }
+
+        /* Skip rest of header */
+        pos = lseek(tar_fd, TAR_CHUNK - TAR_HEADER_SIZE, SEEK_CUR);
+
+        /* Make the path absolute */
+        strcpy(path, "/");
+        strcat(path, header);
+
+        if (header[156] == '0')  /* file */
+        {
+            int rc, wc, total = 0;
+
+            fd = creat(path);
+            if (fd < 0)
+            {
+                printf("failed to create file (%d)", fd);
+                /* Skip the file */
+                lseek(tar_fd, (size + 511) & (~511), SEEK_CUR);
+            }
+            else
+            {
+                /* Copy the file over 512 bytes at a time */
+                while (total < size)
+                {
+                    rc = read(tar_fd, copybuf, TAR_CHUNK);
+                    pos += rc;
+
+                    wc = write(fd, copybuf, MIN(rc, size - total));
+                    if (wc < 0)
+                    {
+                        printf("write failed (%d)", wc);
+                        break;
+                    }
+                    total += wc;
+                }
+                close(fd);
+            }
+        }
+        else if (header[156] == '5')  /* directory */
+        {
+            int ret;
+
+            /* Remove the trailing slash */
+            if (path[strlen(path) - 1] == '/')
+                path[strlen(path) - 1] = '\0';
+
+            /* Create the dir */
+            ret = mkdir(path);
+            if (ret < 0 && ret != -4)
+            {
+                printf("failed to create dir (%d)", ret);
+            }
+        }
+    }
+}
+
 void main(void)
 {
+    char buf[MAX_PATH];
+    char tarstring[6];
+
     lcd_clear_display();
     printf("Hello world!");
-    printf("Gigabeat S Rockbox Bootloader v.00000003");
+    printf("Gigabeat S Rockbox Bootloader v.00000004");
     system_init();
     kernel_init();
     printf("kernel init done");
@@ -90,7 +169,7 @@
         error(EDISK,rc);
     }
 
-    /* Look for the first valid firmware file */
+    /* Look for a tar file */
     struct dirent_uncached* entry;
     DIR_UNCACHED* dir;
     int fd;
@@ -103,26 +182,31 @@
             fd = open(buf, O_RDONLY);
             if (fd >= 0)
             {
-                lseek(fd, 4, SEEK_SET);
-                rc = read(fd, model, 4);
-                close(fd);
-                if (rc == 4)
+                lseek(fd, 257, SEEK_SET);
+                rc = read(fd, tarstring, 5);
+                if (rc == 5)
                 {
-                    model[4] = 0;
-                    if (strcmp(model, "gigs") == 0)
+                    tarstring[5] = 0;
+                    if (strcmp(tarstring, "ustar") == 0)
+                    {
+                        printf("Found tar file. Unarchiving...");
+                        lseek(fd, 0, SEEK_SET);
+                        untar(fd);
+                        close(fd);
+                        printf("Removing tar file");
+                        remove(buf);
                         break;
+                    }
                 }
+                close(fd);
             }
         }
     }
 
-    printf("Firmware file: %s", buf);
-    printf("Loading firmware");
-
     unsigned char *loadbuffer = (unsigned char *)0x0;
     int buffer_size = 31*1024*1024;
 
-    rc = load_firmware(loadbuffer, buf, buffer_size);
+    rc = load_firmware(loadbuffer, "/.rockbox/rockbox.gigabeat", buffer_size);
     if(rc < 0)
         error((int)buf, rc);
 
diff --git a/firmware/target/arm/imx31/gigabeat-s/ata-imx31.c b/firmware/target/arm/imx31/gigabeat-s/ata-imx31.c
index 19e4407..2272b2c 100644
--- a/firmware/target/arm/imx31/gigabeat-s/ata-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/ata-imx31.c
@@ -128,9 +128,11 @@
     ATA_TIME_9 = (T + 20)/T;
 }
 
+#if 0
 #if !defined(BOOTLOADER)
 void copy_write_sectors(const unsigned char* buf, int wordcount)
 {
     (void)buf; (void)wordcount;
 }
 #endif
+#endif
diff --git a/firmware/target/arm/imx31/gigabeat-s/ata-target.h b/firmware/target/arm/imx31/gigabeat-s/ata-target.h
index 8b37c37..a172064 100644
--- a/firmware/target/arm/imx31/gigabeat-s/ata-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/ata-target.h
@@ -22,10 +22,12 @@
 /* Plain C read & write loops */
 #define PREFER_C_READING
 #define PREFER_C_WRITING
+#if 0
 #if !defined(BOOTLOADER)
 #define ATA_OPTIMIZED_WRITING
 void copy_write_sectors(const unsigned char* buf, int wordcount);
 #endif
+#endif
 
 #define ATA_DATA        ATA_DRIVE_DATA
 #define ATA_ERROR       ATA_DRIVE_FEATURES