UMS for the Gigabeat S. Bootloader USB mode. Has a couple quirks 1) First plug has problems if cold. Replug if it doesn't connect or not at high speed 2) Linux doesn't like the odd bootable flag value used in the partitions so it won't mount but Windows works. Fix minor OTG driver bugs and clean up device memory handling. Generic name for ARC controller driver.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17154 a1c6a512-1295-4272-9138-f99709370657
diff --git a/bootloader/gigabeat-s.c b/bootloader/gigabeat-s.c
index 3ca41d9..dbacaaa 100644
--- a/bootloader/gigabeat-s.c
+++ b/bootloader/gigabeat-s.c
@@ -46,6 +46,7 @@
 #include "lcd-target.h"
 #include "avic-imx31.h"
 #include <stdarg.h>
+#include "usb-target.h"
 
 #define TAR_CHUNK 512
 #define TAR_HEADER_SIZE 157
@@ -55,6 +56,7 @@
 int (*kernel_entry)(void);
 char *tarbuf = (char *)0x00000040;
 extern void reference_system_c(void);
+static struct event_queue usb_wait_queue;
 
 /* Dummy stub that creates C references for C functions only used by
    assembly - never called */
@@ -63,6 +65,15 @@
     reference_system_c();
 }
 
+void show_splash(int timeout, const char *msg)
+{
+    lcd_putsxy( (LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg))) / 2,
+                (LCD_HEIGHT - SYSFONT_HEIGHT) / 2, msg);
+    lcd_update();
+
+    sleep(timeout);
+}
+
 void untar(int tar_fd)
 {
     char header[TAR_HEADER_SIZE];
@@ -145,7 +156,7 @@
 
     lcd_clear_display();
     printf("Hello world!");
-    printf("Gigabeat S Rockbox Bootloader v.00000006");
+    printf("Gigabeat S Rockbox Bootloader v.00000007");
     system_init();
     kernel_init();
     printf("kernel init done");
@@ -223,6 +234,47 @@
         }
     }
 
+    if (usb_plugged())
+    {
+        /* Enter USB mode  */
+        struct queue_event ev;
+        queue_init(&usb_wait_queue, true);
+
+        /* Start the USB driver */
+        usb_init();
+        usb_start_monitoring();
+
+        /* Wait for threads to connect or cable is pulled */
+        reset_screen();
+        show_splash(0, "Waiting for USB");
+
+        while (1)
+        {
+            queue_wait_w_tmo(&usb_wait_queue, &ev, HZ/2);
+
+            if (ev.id == SYS_USB_CONNECTED)
+                break; /* Hit */
+
+            if (!usb_plugged())
+                break; /* Cable pulled */
+        }
+
+        if (ev.id == SYS_USB_CONNECTED)
+        {
+            /* Got the message - wait for disconnect */
+            reset_screen();
+            show_splash(0, "Bootloader USB mode");
+
+            usb_acknowledge(SYS_USB_CONNECTED_ACK);
+            usb_wait_for_disconnect(&usb_wait_queue);
+        }
+
+        /* No more monitoring */
+        usb_stop_monitoring();
+
+        reset_screen();
+    }
+
     unsigned char *loadbuffer = (unsigned char *)0x0;
     int buffer_size = 31*1024*1024;
 
@@ -239,6 +291,8 @@
         rc = kernel_entry();
     }
 
-    while (1);
+    /* Halt */
+    while (1)
+        core_idle();
 }