From 3f26fcf34001197ed267fa1ad549095aae49c88e Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Tue, 11 May 2021 13:28:43 +0100 Subject: FiiO M3K: New bootloader SPL and UCL-compressed bootloader are now packed into one output, bootloader.m3k, eliminating the separate SPL build phase. The Rockbox bootloader now has a recovery menu, accessible by holding VOL+ when booting, that lets you back up, restore, and update the bootloader from the device. Change-Id: I642c6e5fb83587a013ab2fbfd1adab439561ced2 --- bootloader/fiiom3k.c | 264 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 233 insertions(+), 31 deletions(-) (limited to 'bootloader/fiiom3k.c') diff --git a/bootloader/fiiom3k.c b/bootloader/fiiom3k.c index 9f169755ae..3d42ba7314 100644 --- a/bootloader/fiiom3k.c +++ b/bootloader/fiiom3k.c @@ -20,6 +20,7 @@ ****************************************************************************/ #include "system.h" +#include "core_alloc.h" #include "kernel/kernel-internal.h" #include "i2c.h" #include "power.h" @@ -35,6 +36,9 @@ #include "rb-loader.h" #include "loader_strerror.h" #include "version.h" +#include "installer-fiiom3k.h" +#include "spl-x1000.h" +#include "x1000/cpm.h" /* Load address where the binary needs to be placed */ extern unsigned char loadaddress[]; @@ -59,6 +63,7 @@ void exec(void* dst, const void* src, int bytes) static bool lcd_inited = false; static bool usb_inited = false; +static bool disk_inited = false; static void init_lcd(void) { @@ -79,6 +84,12 @@ static void init_lcd(void) lcd_inited = true; } +static void put_version(void) +{ + lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(rbversion))) / 2, + (LCD_HEIGHT - SYSFONT_HEIGHT), rbversion); +} + static void do_splash2(int delay, const char* msg, const char* msg2) { init_lcd(); @@ -90,8 +101,7 @@ static void do_splash2(int delay, const char* msg, const char* msg2) (LCD_HEIGHT + 2*SYSFONT_HEIGHT) / 2, msg2); } - lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(rbversion))) / 2, - (LCD_HEIGHT - SYSFONT_HEIGHT), rbversion); + put_version(); lcd_update(); sleep(delay); } @@ -109,20 +119,232 @@ static void do_usb(void) usb_inited = true; } - do_splash(0, "Waiting for USB"); + do_splash2(0, "Waiting for USB", "Press POWER to go back"); - while(button_get(true) != SYS_USB_CONNECTED); - do_splash(0, "USB mode"); + int btn; + while(1) { + btn = button_get(true); + if(btn == SYS_USB_CONNECTED) + break; + else if(btn == BUTTON_POWER) + return; + } + do_splash(0, "USB mode"); usb_acknowledge(SYS_USB_CONNECTED_ACK); while(button_get(true) != SYS_USB_DISCONNECTED); do_splash(3*HZ, "USB disconnected"); } +static int init_disk(void) +{ + if(disk_inited) + return 0; + + while(!storage_present(0)) { + do_splash2(0, "Insert SD card", "Press POWER for recovery"); + int btn = button_get_w_tmo(HZ); + if(btn == BUTTON_POWER) + return 1; + } + + if(disk_mount_all() <= 0) { + do_splash(5*HZ, "Cannot mount filesystem"); + return 1; + } + + disk_inited = true; + return 0; +} + +static void do_boot(void) +{ + if(init_disk() != 0) + return; + + int loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE); + if(loadsize <= 0) { + do_splash2(5*HZ, "Error loading Rockbox", + loader_strerror(loadsize)); + do_usb(); + return; + } + + if(lcd_inited) + backlight_hw_off(); + + disable_irq(); + exec(loadaddress, loadbuffer, loadsize); +} + +#define INSTALL 0 +#define BACKUP 1 +#define RESTORE 2 + +static void do_install(int which) +{ + int rc = init_disk(); + if(rc != 0) { + do_splash2(5*HZ, "Install aborted", "No SD card present"); + return; + } + + const char* msg; + if(rc == INSTALL) + msg = "Installing"; + else if(rc == BACKUP) + msg = "Backing up"; + else + msg = "Restoring backup"; + + do_splash(0, msg); + + if(which == INSTALL) + rc = install_boot("/bootloader.m3k"); + else if(which == BACKUP) + rc = backup_boot("/fiiom3k-boot.bin"); + else + rc = restore_boot("/fiiom3k-boot.bin"); + + char buf[32]; + snprintf(buf, sizeof(buf), "Failed! Error: %d", rc); + const char* msg1 = rc == 0 ? "Success" : buf; + const char* msg2 = "Press POWER to continue"; + do_splash2(0, msg1, msg2); + + button_clear_queue(); + while(button_get(true) != BUTTON_POWER); +} + +static void recovery_menu(void) +{ + static const char* items[] = { + "--- Rockbox recovery menu ---", + "[System]", + " Start Rockbox", + " USB mode", + " Shutdown", + " Reboot", + "[Bootloader]", + " Install or update", + " Backup", + " Restore", + "", + "", + "", + "", + "", + "VOL+/VOL- move cursor", + "PLAY select item", + "POWER power off", + }; + + static const int nitems = sizeof(items) / sizeof(char*); + + init_lcd(); + + int selection = 2; + do { + /* Draw menu */ + lcd_clear_display(); + + for(int i = 0; i < nitems; ++i) + lcd_puts(0, i, items[i]); + + if(items[selection][0] == ' ') + lcd_puts(0, selection, "=>"); + + put_version(); + lcd_update(); + + /* Clear queue to avoid accidental input */ + button_clear_queue(); + + /* Get the button */ + int btn = button_get(true); + + /* Process user input */ + if(btn == BUTTON_VOL_UP) { + for(int i = selection-1; i >= 0; --i) { + if(items[i][0] == ' ') { + selection = i; + break; + } + } + + continue; + } else if(btn == BUTTON_VOL_DOWN) { + for(int i = selection+1; i < nitems; ++i) { + if(items[i][0] == ' ') { + selection = i; + break; + } + } + + continue; + } else if(btn == BUTTON_POWER) { + selection = 4; /* Shutdown */ + } else if(btn != BUTTON_PLAY) { + continue; + } + + /* User pressed PLAY so decide what action to take */ + switch(selection) { + case 2: /* Start rockbox */ + do_boot(); + break; + + case 3: /* USB mode */ + do_usb(); + break; + + case 4: /* Shutdown */ + do_splash(HZ, "Shutting down"); + power_off(); + break; + + case 5: /* Reboot */ + do_splash(HZ, "Rebooting"); + system_reboot(); + break; + + case 7: /* Install bootloader */ + do_install(INSTALL); + break; + + case 8: /* Backup bootloader */ + do_install(BACKUP); + break; + + case 9: /* Restore bootloader */ + do_install(RESTORE); + break; + + default: + break; + } + } while(1); +} + void main(void) { + bool recovery_mode = false; + + /* This hack is needed because when USB booting, we cannot initialize + * clocks in the SPL -- it may break the mask ROM's USB code. So if the + * SPL has not already initialized the clocks, we need to do that now. + * + * Also use this as a sign that we should enter the recovery menu since + * this is probably the expected result if the user is USB booting... + */ + if(jz_readf(CPM_MPCR, ENABLE)) { + spl_handle_pre_boot(0); + recovery_mode = true; + } + system_init(); + core_allocator_init(); kernel_init(); i2c_init(); power_init(); @@ -136,32 +358,12 @@ void main(void) filesystem_init(); - int loadsize = 0; - do { - if(!storage_present(0)) { - do_splash(HZ, "Insert SD card"); - continue; - } - - if(disk_mount_all() <= 0) { - do_splash(5*HZ, "Cannot mount filesystem"); - do_usb(); - continue; - } - - loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE); - if(loadsize <= 0) { - do_splash2(5*HZ, "Error loading Rockbox", - loader_strerror(loadsize)); - do_usb(); - continue; - } - } while(loadsize <= 0); + if(button_read_device() & BUTTON_VOL_UP) + recovery_mode = true; - if(lcd_inited) - backlight_hw_off(); + if(!recovery_mode) + do_boot(); - disable_irq(); - - exec(loadaddress, loadbuffer, loadsize); + /* If boot fails or user holds Vol+, go to recovery menu */ + recovery_menu(); } -- cgit v1.2.3