diff options
Diffstat (limited to 'bootloader/x1000/recovery.c')
-rw-r--r-- | bootloader/x1000/recovery.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/bootloader/x1000/recovery.c b/bootloader/x1000/recovery.c new file mode 100644 index 0000000000..371500826f --- /dev/null +++ b/bootloader/x1000/recovery.c @@ -0,0 +1,208 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021-2022 Aidan MacDonald + * + * 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 "x1000bootloader.h" +#include "lcd.h" +#include "font.h" +#include "button.h" +#include "kernel.h" +#include <string.h> + +enum { + MENUITEM_HEADING, + MENUITEM_ACTION, +}; + +struct menuitem { + int type; + const char* text; + void(*action)(void); +}; + +static void to_main_menu(void); +static void to_debug_menu(void); + +/* Defines the recovery menu contents */ +static const struct menuitem recovery_items[] = { + {MENUITEM_HEADING, "Boot select", NULL}, + {MENUITEM_ACTION, "Rockbox", &boot_rockbox}, +#ifdef OF_PLAYER_NAME + {MENUITEM_ACTION, OF_PLAYER_NAME, &boot_of_player}, +#endif +#ifdef OF_RECOVERY_NAME + {MENUITEM_ACTION, OF_RECOVERY_NAME, &boot_of_recovery}, +#endif + {MENUITEM_ACTION, "Linux", &boot_linux}, + {MENUITEM_HEADING, "System", NULL}, + {MENUITEM_ACTION, "USB mode", &usb_mode}, + {MENUITEM_ACTION, "Shutdown", &shutdown}, + {MENUITEM_ACTION, "Reboot", &reboot}, + {MENUITEM_HEADING, "Bootloader", NULL}, + {MENUITEM_ACTION, "Install or update", &bootloader_install}, + {MENUITEM_ACTION, "Backup", &bootloader_backup}, + {MENUITEM_ACTION, "Restore", &bootloader_restore}, + {MENUITEM_HEADING, "Advanced", NULL}, + {MENUITEM_ACTION, "Debug tools", &to_debug_menu}, +}; + +static const struct menuitem debug_menu_items[] = { + {MENUITEM_HEADING, "Debug tools", NULL}, +#ifdef HAVE_SCREENDUMP + {MENUITEM_ACTION, "Enable screenshots", &screenshot_enable}, +#endif + {MENUITEM_ACTION, "Flash info", &show_flash_info}, + {MENUITEM_ACTION, "Flash ONFI info", &show_flash_onfi_info}, +#ifdef OF_PLAYER_ADDR + {MENUITEM_ACTION, "Dump OF player", &dump_of_player}, +#endif +#ifdef OF_RECOVERY_ADDR + {MENUITEM_ACTION, "Dump OF recovery", &dump_of_recovery}, +#endif + {MENUITEM_ACTION, "Dump entire flash", &dump_entire_flash}, + {MENUITEM_ACTION, "Main menu", &to_main_menu}, +}; + +static const struct menuitem* current_menu = NULL; +static struct bl_list recmenu_list; + +static void to_main_menu(void) +{ + current_menu = recovery_items; + recmenu_list.num_items = ARRAYLEN(recovery_items); + recmenu_list.selected_item = 1; +} + +static void to_debug_menu(void) +{ + current_menu = debug_menu_items; + recmenu_list.num_items = ARRAYLEN(debug_menu_items); + recmenu_list.selected_item = 1; +} + +static void recmenu_draw_item(const struct bl_listitem* item) +{ + const struct menuitem* mu = ¤t_menu[item->index]; + const char* fmt; + + switch(mu->type) { + case MENUITEM_HEADING: + fmt = "[%s]"; + break; + + case MENUITEM_ACTION: + default: + if(item->index == item->list->selected_item) + fmt = "=> %s"; + else + fmt = " %s"; + break; + } + + lcd_putsxyf(item->x, item->y, fmt, mu->text); +} + +static void recmenu_scroll(struct bl_list* list, int dir) +{ + int start, end, step; + + if(dir < 0) { + start = list->selected_item - 1; + end = -1; + step = -1; + } else if(dir > 0) { + start = list->selected_item + 1; + end = list->num_items; + step = 1; + } else { + return; + } + + for(int i = start; i != end; i += step) { + if(current_menu[i].action) { + gui_list_select(list, i); + + /* always show one item above the selection to ensure + * the topmost heading is visible */ + if(list->selected_item == list->top_item && list->top_item > 0) + list->top_item--; + + break; + } + } +} + +static void put_help_line(int y, int line, const char* str1, const char* str2) +{ + y += line*SYSFONT_HEIGHT; + lcd_putsxy(0, y, str1); + lcd_putsxy(LCD_WIDTH - strlen(str2)*SYSFONT_WIDTH, y, str2); +} + +void recovery_menu(void) +{ + struct viewport vp = { + .x = 0, .y = SYSFONT_HEIGHT, + .width = LCD_WIDTH, + .height = LCD_HEIGHT - SYSFONT_HEIGHT*5, + }; + lcd_init_viewport(&vp); + + struct bl_list* list = &recmenu_list; + gui_list_init(list, &vp); + list->draw_item = recmenu_draw_item; + to_main_menu(); + + while(1) { + clearscreen(); + putcenter_y(0, "Rockbox recovery menu"); + + /* draw the help text */ + int ypos = LCD_HEIGHT - 4*SYSFONT_HEIGHT; + put_help_line(ypos, 0, BL_DOWN_NAME "/" BL_UP_NAME, "move cursor"); + put_help_line(ypos, 1, BL_SELECT_NAME, "select item"); + put_help_line(ypos, 2, BL_QUIT_NAME, "power off"); + + /* draw the list */ + gui_list_draw(list); + + lcd_update(); + + /* handle input */ + switch(get_button(TIMEOUT_BLOCK)) { + case BL_SELECT: { + if(current_menu[list->selected_item].action) + current_menu[list->selected_item].action(); + } break; + + case BL_UP: + recmenu_scroll(list, -1); + break; + + case BL_DOWN: + recmenu_scroll(list, 1); + break; + + case BL_QUIT: + shutdown(); + break; + } + } +} |