From 80278e45aa79cee66596c257c5d3870765233e00 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 10 May 2008 18:00:11 +0000 Subject: Bring Gigabeat S bootloader one step close to a release version. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17442 a1c6a512-1295-4272-9138-f99709370657 --- bootloader/gigabeat-s.c | 329 ++++++++++++++++++++++++++++++------------------ 1 file changed, 209 insertions(+), 120 deletions(-) (limited to 'bootloader/gigabeat-s.c') diff --git a/bootloader/gigabeat-s.c b/bootloader/gigabeat-s.c index 30c2955c19..dfe13e4540 100644 --- a/bootloader/gigabeat-s.c +++ b/bootloader/gigabeat-s.c @@ -21,11 +21,15 @@ #include #include "kernel.h" #include "string.h" +#include "adc.h" +#include "powermgmt.h" #include "ata.h" #include "dir.h" #include "disk.h" #include "common.h" +#include "backlight.h" #include "usb.h" +#include "button.h" #include "font.h" #include "lcd.h" #include "usb-target.h" @@ -39,11 +43,15 @@ static const char basedir[] = "/Content/0b00/00/"; /* Can use memory after vector table up to 0x01f00000 */ static char * const tarbuf = (char *)0x00000040; static const size_t tarbuf_size = 0x01f00000 - 0x00000040; -/* Queue to get notifications when in USB mode */ -static struct event_queue usb_wait_queue; +/* Firmware data */ +static void * const load_buf = 0x00000000; +static const size_t load_buf_size = 0x20000000 - 0x100000; +static const void * const start_addr = 0x00000000; static void show_splash(int timeout, const char *msg) { + backlight_on(); + reset_screen(); lcd_putsxy( (LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg))) / 2, (LCD_HEIGHT - SYSFONT_HEIGHT) / 2, msg); lcd_update(); @@ -51,6 +59,95 @@ static void show_splash(int timeout, const char *msg) sleep(timeout); } +static bool pause_if_button_pressed(bool pre_usb) +{ + while (1) + { + int button = button_read_device(); + + if (pre_usb && !usb_plugged()) + return false; + + /* Exit if no button or only the menu (settings reset) button */ + switch (button) + { + case BUTTON_MENU: + case BUTTON_NONE: + return true; + } + + sleep(HZ/5); + + /* If the disk powers off, the firmware will lock at startup */ + ata_spin(); + } +} + +/* TODO: Handle charging while connected */ +static void handle_usb(void) +{ + int button; + + /* Check if plugged and pause to look at messages. If the cable was pulled + * while waiting, proceed as if it never was plugged. */ + if (!usb_plugged() || !pause_if_button_pressed(true)) + { + /* Bang on the controller */ + usb_init_device(); + return; + } + + /** Enter USB mode **/ + + /* We need full button and backlight handling now */ + backlight_init(); + button_init(); + + /* Start the USB driver */ + usb_init(); + usb_start_monitoring(); + + /* Wait for threads to connect or cable is pulled */ + show_splash(HZ/2, "Waiting for USB"); + + while (1) + { + button = button_get_w_tmo(HZ/2); + + if (button == SYS_USB_CONNECTED) + break; /* Hit */ + + if (!usb_plugged()) + break; /* Cable pulled */ + } + + if (button == SYS_USB_CONNECTED) + { + /* Got the message - wait for disconnect */ + show_splash(0, "Bootloader USB mode"); + + usb_acknowledge(SYS_USB_CONNECTED_ACK); + + while (1) + { + button = button_get(true); + if (button == SYS_USB_DISCONNECTED) + { + usb_acknowledge(SYS_USB_DISCONNECTED_ACK); + break; + } + } + } + + /* Put drivers initialized for USB connection into a known state */ + backlight_on(); + usb_close(); + button_close(); + backlight_close(); + + reset_screen(); +} + static void untar(int tar_fd) { char header[TAR_HEADER_SIZE]; @@ -60,13 +157,15 @@ static void untar(int tar_fd) int ret; size_t size = filesize(tar_fd); - if (size > tarbuf_size) { + if (size > tarbuf_size) + { printf("tar file too large"); /* Paranoid but proper */ return; } ret = read(tar_fd, tarbuf, filesize(tar_fd)); - if (ret < 0) { + if (ret < 0) + { printf("couldn't read tar file (%d)", ret); return; } @@ -131,153 +230,91 @@ static void untar(int tar_fd) } } -void main(void) +/* Look for a tar file or rockbox binary in the MTP directory */ +static void handle_untar(void) { char buf[MAX_PATH]; char tarstring[6]; char model[5]; - - /* Flush and invalidate all caches (because vectors were written) */ - invalidate_icache(); - - lcd_clear_display(); - printf("Gigabeat S Rockbox Bootloader v.00000013"); - system_init(); - kernel_init(); - printf("kernel init done"); + struct dirent_uncached* entry; + DIR_UNCACHED* dir; + int fd; int rc; - enable_interrupt(IRQ_FIQ_STATUS); + dir = opendir_uncached(basedir); - rc = ata_init(); - if(rc) + while ((entry = readdir_uncached(dir))) { - reset_screen(); - error(EATA, rc); - } - printf("ata init done"); + if (*entry->d_name == '.') + continue; - disk_init(); - printf("disk init done"); + snprintf(buf, sizeof(buf), "%s%s", basedir, entry->d_name); + fd = open(buf, O_RDONLY); - rc = disk_mount_all(); - if (rc<=0) - { - error(EDISK,rc); - } + if (fd < 0) + continue; - /* Look for a tar file */ - struct dirent_uncached* entry; - DIR_UNCACHED* dir; - int fd; - dir = opendir_uncached(basedir); - while ((entry = readdir_uncached(dir))) - { - if (*entry->d_name != '.') + /* Check whether the file is a rockbox binary. */ + lseek(fd, 4, SEEK_SET); + rc = read(fd, model, 4); + if (rc == 4) { - snprintf(buf, sizeof(buf), "%s%s", basedir, entry->d_name); - fd = open(buf, O_RDONLY); - if (fd >= 0) + model[4] = 0; + if (strcmp(model, "gigs") == 0) { - /* Check whether the file is a rockbox binary. */ - lseek(fd, 4, SEEK_SET); - rc = read(fd, model, 4); - if (rc == 4) - { - model[4] = 0; - if (strcmp(model, "gigs") == 0) - { - printf("Found rockbox binary. Moving..."); - close(fd); - remove("/.rockbox/rockbox.gigabeat"); - int ret = rename(buf, "/.rockbox/rockbox.gigabeat"); - printf("returned %d", ret); - sleep(HZ); - break; - } - } - - /* Check whether the file is a tar file. */ - lseek(fd, 257, SEEK_SET); - rc = read(fd, tarstring, 5); - if (rc == 5) - { - 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; - } - } + printf("Found rockbox binary. Moving..."); close(fd); + remove("/.rockbox/rockbox.gigabeat"); + int ret = rename(buf, "/.rockbox/rockbox.gigabeat"); + printf("returned %d", ret); + sleep(HZ); + break; } } - } - - 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) + /* Check whether the file is a tar file. */ + lseek(fd, 257, SEEK_SET); + rc = read(fd, tarstring, 5); + if (rc == 5) { - /* 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); + 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; + } } - /* No more monitoring */ - usb_stop_monitoring(); - - reset_screen(); - } - else - { - /* Bang on the controller */ - usb_init_device(); + close(fd); } +} - unsigned char *loadbuffer = (unsigned char *)0x0; - int buffer_size = 31*1024*1024; +/* Try to load the firmware and run it */ +static void __attribute__((noreturn)) handle_firmware_load(void) +{ + int rc = load_firmware(load_buf, "/.rockbox/rockbox.gigabeat", + load_buf_size); - rc = load_firmware(loadbuffer, "/.rockbox/rockbox.gigabeat", buffer_size); if(rc < 0) error(EBOOTFILE, rc); + /* Pause to look at messages */ + pause_if_button_pressed(false); + + /* Put drivers into a known state */ + button_close_device(); + ata_close(); system_prepare_fw_start(); if (rc == EOK) { invalidate_icache(); - asm volatile ("bx %0": : "r"(loadbuffer)); + asm volatile ("bx %0": : "r"(start_addr)); } /* Halt */ @@ -285,3 +322,55 @@ void main(void) core_idle(); } +static void check_battery(void) +{ + int batt = battery_adc_voltage(); + printf("Battery: %d.%03d V", batt / 1000, batt % 1000); + /* TODO: warn on low battery or shut down */ +} + +void main(void) +{ + int rc; + + /* Flush and invalidate all caches (because vectors were written) */ + invalidate_icache(); + + lcd_clear_display(); + printf("Gigabeat S Rockbox Bootloader"); + printf("Version %s", version); + system_init(); + kernel_init(); + + enable_interrupt(IRQ_FIQ_STATUS); + + /* Initialize KPP so we can poll the button states */ + button_init_device(); + + adc_init(); + + check_battery(); + + rc = ata_init(); + if(rc) + { + reset_screen(); + error(EATA, rc); + } + + disk_init(); + + rc = disk_mount_all(); + if (rc<=0) + { + error(EDISK,rc); + } + + printf("Init complete"); + + /* Do USB first since a tar or binary could be added to the MTP directory + * at the time and we can untar or move after unplugging. */ + handle_usb(); + handle_untar(); + handle_firmware_load(); /* No return */ +} -- cgit v1.2.3