/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2008 by Maurus Cuelenaere * * 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 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include "jz4740.h" #include "../kernel-internal.h" #include "backlight.h" #include "font.h" #include "lcd.h" #include "file.h" #include "usb.h" #include "system.h" #include "button.h" #include "common.h" #include "rb-loader.h" #include "loader_strerror.h" #include "storage.h" #include "file_internal.h" #include "disk.h" #include "string.h" #include "adc.h" #include "version.h" extern void show_logo(void); extern void power_off(void); static void show_splash(int timeout, const char *msg) { reset_screen(); lcd_putsxy( (LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg))) / 2, (LCD_HEIGHT - SYSFONT_HEIGHT) / 2, msg); lcd_update(); sleep(timeout); } static void usb_mode(void) { int button; /* Init USB */ usb_init(); usb_start_monitoring(); /* Wait for threads to connect */ show_splash(HZ/2, "Waiting for USB"); while (1) { button = button_get_w_tmo(HZ/2); if (button == SYS_USB_CONNECTED) break; /* Hit */ } 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) break; } } } static int boot_of(void) { int fd, rc, len, i, checksum = 0; void (*kernel_entry)(int, void*, void*); printf("Mounting disk..."); rc = disk_mount_all(); if (rc <= 0) error(EDISK, rc, true); /* TODO: get this from the NAND flash instead of SD */ fd = open("/ccpmp.bin", O_RDONLY); if(fd < 0) return EFILE_NOT_FOUND; lseek(fd, 4, SEEK_SET); rc = read(fd, (char*)&len, 4); /* CPU is LE */ if(rc < 4) return EREAD_IMAGE_FAILED; len += 8; printf("Reading %d bytes...", len); lseek(fd, 0, SEEK_SET); rc = read(fd, (void*)0x80004000, len); if(rc < len) return EREAD_IMAGE_FAILED; close(fd); for(i=0; i0; i--) asm volatile("nop\n"); kernel_entry = (void*) 0x80004008; kernel_entry(0, "Jan 10 2008", "15:34:42"); /* Reversed from the SPL */ return 0; /* Shouldn't happen */ } static int boot_rockbox(void) { int rc; void (*kernel_entry)(void); printf("Mounting disk..."); rc = disk_mount_all(); if (rc <= 0) error(EDISK,rc, true); printf("Loading firmware..."); rc = load_firmware((unsigned char *)CONFIG_SDRAM_START, BOOTFILE, 0x400000); if(rc <= EFILE_EMPTY) return rc; else { printf("Starting Rockbox..."); adc_close(); /* Disable SADC, seems to fix the re-init Rockbox does */ disable_interrupt(); kernel_entry = (void*) CONFIG_SDRAM_START; kernel_entry(); return 0; /* Shouldn't happen */ } } static void reset_configuration(void) { int rc; rc = disk_mount_all(); if (rc <= 0) error(EDISK,rc, true); if(rename(ROCKBOX_DIR "/config.cfg", ROCKBOX_DIR "/config.old") == 0) show_splash(HZ/2, "Configuration reset successfully!"); else show_splash(HZ/2, "Couldn't reset configuration!"); } #define RECT_X (LCD_WIDTH/8) #define RECT_Y(i) (LCD_HEIGHT/20 + LCD_HEIGHT/10*i + RECT_HEIGHT*i) #define RECT_WIDTH (LCD_WIDTH*3/4) #define RECT_HEIGHT (LCD_HEIGHT/ARRAYLEN(strings) - LCD_HEIGHT/10) #define TEXT_X(i) (RECT_X + RECT_WIDTH/2 - strlen(strings[i])*SYSFONT_WIDTH/2) #define TEXT_Y(i) (RECT_Y(i) + RECT_HEIGHT/2 - SYSFONT_HEIGHT/2) static int boot_menu(void) { const char* strings[] = {"Boot Rockbox", "Boot OF", "USB mode", "Reset Rockbox configuration"}; int button, touch, poweroff_repeat = 0; unsigned int i; verbose = true; adc_init(); redraw: lcd_clear_display(); for(i=0; i> 16; int found = -1; for(i=0; i RECT_X && x < RECT_X+RECT_WIDTH && y > RECT_Y(i) && y < RECT_Y(i)+RECT_HEIGHT) { found = i; break; } } switch(found) { case 0: reset_screen(); boot_rockbox(); break; case 1: reset_screen(); boot_of(); break; case 2: usb_mode(); break; case 3: reset_configuration(); break; } if(found != -1) goto redraw; } else if(button & BUTTON_POWER) { if(poweroff_repeat++ > 8) power_off(); } else poweroff_repeat = 0; } } int main(void) { int rc; #ifdef HAVE_TOUCHSCREEN int dummy; #endif kernel_init(); lcd_init(); font_init(); lcd_setfont(FONT_SYSFIXED); button_init(); backlight_init(); show_logo(); filesystem_init(); rc = storage_init(); if(rc) error(EATA, rc, true); /* Don't mount the disks yet, there could be file system/partition errors which are fixable in USB mode */ #ifdef HAVE_TOUCHSCREEN rc = button_read_device(&dummy); #else rc = button_read_device(); #endif if(rc) verbose = true; #ifdef BUTTON_VOL_UP if(rc & BUTTON_VOL_UP || #endif #ifdef BUTTON_POWER rc & BUTTON_POWER || #endif 0) rc = boot_menu(); if(verbose) reset_screen(); printf(MODEL_NAME" Rockbox Bootloader"); printf("Version %s", rbversion); #ifdef HAS_BUTTON_HOLD if(button_hold()) rc = boot_of(); else #endif rc = boot_rockbox(); if(rc <= EFILE_EMPTY) printf("Error: %s", loader_strerror(rc)); /* Halt */ while (1) core_idle(); return 0; }