587 lines
21 KiB
C
Executable File
587 lines
21 KiB
C
Executable File
/*
|
|
* reset.c -- System Tools (Aigo Eros Q platform)
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*
|
|
* Multi-function system tools module for the Aigo Eros Q hardware
|
|
* family (HiFiWalker H2, Surfans F20, Phinistec Z6, Agptek H3).
|
|
*
|
|
* OreBolt OS, Hiby Player (stock firmware), and Rockbox all boot
|
|
* from the FAT32 MicroSD card -- the SPI flash bootloader is never
|
|
* modified. Switching between them is a matter of which files live
|
|
* on the SD card.
|
|
*
|
|
* Menu options:
|
|
* 1. PURGE -- Operational cleanup. Kills child processes
|
|
* (dosbox, emulators), brings usb0 down, wipes
|
|
* /data/loot_drop/, calls sync(). Does NOT
|
|
* touch any firmware files.
|
|
* 2. HIBY -- Restore stock firmware (Hiby Player). Removes
|
|
* all OreBolt OS files from the SD card and
|
|
* restores the stock backup from /data/stock_backup/.
|
|
* Reboots into the original music player.
|
|
* 3. ROCKBOX -- If the user has separately installed Rockbox on
|
|
* the SD card (detected by presence of .rockbox/
|
|
* or rockbox/ directory), reboots the device.
|
|
* OreBolt OS does NOT ship, bundle, or install
|
|
* Rockbox. This is detection-only.
|
|
* 4. REBOOT -- Warm reboot the device.
|
|
*
|
|
* STOCK BACKUP CONVENTION:
|
|
* When OreBolt OS is first deployed (build.sh --deploy), the
|
|
* deploy script snapshots the stock firmware files into
|
|
* /data/stock_backup/ before writing any OreBolt files. The
|
|
* expected layout is:
|
|
*
|
|
* /data/stock_backup/
|
|
* usr/bin/hiby_player (stock music player binary)
|
|
* etc/inittab (stock init configuration)
|
|
* etc/init.d/S* (stock init scripts)
|
|
* ...any other stock files...
|
|
*
|
|
* If /data/stock_backup/ does not exist, the HIBY option shows
|
|
* a warning that no backup is available.
|
|
*
|
|
* INPUT:
|
|
* ROTATE = scroll menu
|
|
* PLAY = select / confirm
|
|
* BACK = cancel / exit
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <linux/input.h>
|
|
#include <linux/reboot.h>
|
|
#include <sys/reboot.h>
|
|
#include "h2_ui.h"
|
|
#include "log_manager.h"
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Menu items */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
typedef enum {
|
|
OPT_PURGE = 0,
|
|
OPT_HIBY,
|
|
OPT_ROCKBOX,
|
|
OPT_REBOOT,
|
|
OPT_COUNT
|
|
} menu_opt_t;
|
|
|
|
static const char *opt_labels[] = {
|
|
"PURGE - Clear data & connections",
|
|
"HIBY PLAYER - Restore stock FW",
|
|
"ROCKBOX - Reboot to Rockbox",
|
|
"REBOOT - Restart device",
|
|
};
|
|
#define NUM_OPTS ((int)(sizeof(opt_labels) / sizeof(opt_labels[0])))
|
|
|
|
static const char *opt_descs[] = {
|
|
"Kills child procs, wipes loot_drop,\nbrings usb0 down. Safe, non-destructive.",
|
|
"Removes OreBolt OS files, restores\nstock Hiby Player from backup, reboots.",
|
|
"Reboots so the Rockbox bootloader\ncan chainload .rockbox/ on the SD card.",
|
|
"Standard warm reboot via kernel.",
|
|
};
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* State */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static int input_fd = -1;
|
|
static int selection = 0;
|
|
static lv_obj_t *menu_list = NULL;
|
|
static lv_obj_t *desc_label = NULL;
|
|
static lv_obj_t *status_lbl = NULL;
|
|
static lv_obj_t *list_btns[OPT_COUNT];
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Filesystem helpers */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/*
|
|
* Recursively remove all files and directories under `path`.
|
|
* Does not follow symlinks. Skips `.` and `..`.
|
|
* Returns 0 on success, -1 on error.
|
|
*/
|
|
static int rm_rf(const char *path) {
|
|
DIR *d = opendir(path);
|
|
if (!d) return -1;
|
|
|
|
struct dirent *ent;
|
|
int ret = 0;
|
|
while ((ent = readdir(d)) != NULL) {
|
|
if (ent->d_name[0] == '.') continue;
|
|
|
|
char fullpath[512];
|
|
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, ent->d_name);
|
|
|
|
struct stat st;
|
|
if (lstat(fullpath, &st) != 0) continue;
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
ret |= rm_rf(fullpath);
|
|
rmdir(fullpath);
|
|
} else {
|
|
ret |= unlink(fullpath);
|
|
}
|
|
}
|
|
closedir(d);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Check if a directory exists and is non-empty.
|
|
* Returns 1 if directory exists with at least one non-dot entry.
|
|
*/
|
|
static int dir_exists(const char *path) {
|
|
DIR *d = opendir(path);
|
|
if (!d) return 0;
|
|
|
|
struct dirent *ent;
|
|
int found = 0;
|
|
while ((ent = readdir(d)) != NULL) {
|
|
if (ent->d_name[0] == '.') continue;
|
|
found = 1;
|
|
break;
|
|
}
|
|
closedir(d);
|
|
return found;
|
|
}
|
|
|
|
/*
|
|
* Check if a file exists and is executable.
|
|
*/
|
|
static int bin_exists(const char *path) {
|
|
return access(path, X_OK) == 0;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Actions */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void action_purge(void) {
|
|
LOG_INF("executing operational purge");
|
|
|
|
/* Kill known child processes */
|
|
const char *procs[] = { "dosbox", "snes9x", "fceux", "mednafen",
|
|
"dgen", "gambatte", "gpsp", "stella", NULL };
|
|
for (int i = 0; procs[i]; i++) {
|
|
char cmd[128];
|
|
snprintf(cmd, sizeof(cmd), "pidof %s 2>/dev/null", procs[i]);
|
|
FILE *pgrep = popen(cmd, "r");
|
|
if (pgrep) {
|
|
char pidbuf[16];
|
|
if (fgets(pidbuf, sizeof(pidbuf), pgrep)) {
|
|
long pid = atol(pidbuf);
|
|
if (pid > 0) {
|
|
kill(pid, SIGTERM);
|
|
LOG_INF("killed %s (pid %ld)", procs[i], pid);
|
|
}
|
|
}
|
|
pclose(pgrep);
|
|
}
|
|
}
|
|
|
|
/* Bring usb0 down */
|
|
int fd = open("/sys/class/net/usb0/operstate", O_WRONLY);
|
|
if (fd >= 0) { write(fd, "down", 4); close(fd); }
|
|
|
|
/* Wipe loot_drop */
|
|
rm_rf("/data/loot_drop");
|
|
mkdir("/data/loot_drop", 0755);
|
|
|
|
sync();
|
|
LOG_INF("operational purge complete");
|
|
}
|
|
|
|
static void action_hiby(void) {
|
|
LOG_INF("initiating stock firmware restore (Hiby Player)");
|
|
|
|
if (!dir_exists("/data/stock_backup")) {
|
|
LOG_ERR("no stock backup found at /data/stock_backup/");
|
|
return;
|
|
}
|
|
|
|
/* Step 1: Stop OreBolt init scripts */
|
|
system("/etc/init.d/S98retro-input stop 2>/dev/null");
|
|
system("/etc/init.d/S99broker stop 2>/dev/null");
|
|
system("/etc/init.d/bt_input_daemon.sh stop 2>/dev/null");
|
|
|
|
/* Step 2: Remove all OreBolt module binaries */
|
|
rm_rf("/apps");
|
|
mkdir("/apps", 0755);
|
|
|
|
/* Step 3: Remove OreBolt-specific binaries */
|
|
unlink("/usr/bin/h2_test");
|
|
unlink("/usr/bin/retro_input_mapper");
|
|
|
|
/* Step 4: Remove OreBolt init scripts */
|
|
unlink("/etc/init.d/S98retro-input");
|
|
unlink("/etc/init.d/S99broker");
|
|
unlink("/etc/init.d/bt_input_daemon.sh");
|
|
|
|
/* Step 5: Remove OreBolt shared libraries */
|
|
unlink("/usr/lib/liblvgl.so");
|
|
|
|
/* Step 6: Restore stock files from backup */
|
|
if (dir_exists("/data/stock_backup/usr")) {
|
|
system("cp -a /data/stock_backup/usr/bin/* /usr/bin/ 2>/dev/null");
|
|
system("cp -a /data/stock_backup/usr/lib/* /usr/lib/ 2>/dev/null");
|
|
}
|
|
if (dir_exists("/data/stock_backup/etc")) {
|
|
system("cp -a /data/stock_backup/etc/inittab /etc/inittab 2>/dev/null");
|
|
system("cp -a /data/stock_backup/etc/init.d/* /etc/init.d/ 2>/dev/null");
|
|
}
|
|
|
|
sync();
|
|
LOG_INF("stock firmware restored, rebooting to Hiby Player");
|
|
sleep(1);
|
|
sync();
|
|
reboot(RB_AUTOBOOT);
|
|
}
|
|
|
|
static void action_rockbox(void) {
|
|
if (!dir_exists("/.rockbox") && !dir_exists("/rockbox")) {
|
|
LOG_ERR("no .rockbox/ or rockbox/ directory found on SD card");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* OreBolt OS does NOT ship or install Rockbox. If the user
|
|
* has installed Rockbox on their SD card independently, a
|
|
* reboot is sufficient for the Rockbox bootloader (or the
|
|
* stock bootloader with Rockbox boot files) to load it.
|
|
*
|
|
* We do NOT modify any files -- Rockbox manages its own boot
|
|
* presence. A reboot is sufficient.
|
|
*/
|
|
LOG_INF("Rockbox detected, rebooting to Rockbox");
|
|
sleep(1);
|
|
sync();
|
|
reboot(RB_AUTOBOOT);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* UI */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void build_menu(void) {
|
|
lv_obj_clean(menu_list);
|
|
|
|
for (int i = 0; i < NUM_OPTS; i++) {
|
|
list_btns[i] = lv_list_add_btn(menu_list, LV_SYMBOL_LIST,
|
|
opt_labels[i]);
|
|
lv_obj_set_style_text_color(list_btns[i],
|
|
COLOR_TEXT, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(list_btns[i],
|
|
&lv_font_montserrat_10, LV_PART_MAIN);
|
|
}
|
|
}
|
|
|
|
static void update_desc(void) {
|
|
lv_label_set_text(desc_label, opt_descs[selection]);
|
|
|
|
/* Color-code destructive options */
|
|
lv_color_t desc_color = COLOR_TEXT;
|
|
if (selection == OPT_HIBY)
|
|
desc_color = COLOR_PRIMARY; /* red-orange: destructive */
|
|
else if (selection == OPT_ROCKBOX)
|
|
desc_color = lv_color_make(100, 180, 255); /* blue: external */
|
|
|
|
lv_obj_set_style_text_color(desc_label, desc_color, LV_PART_MAIN);
|
|
}
|
|
|
|
static void scroll_menu(int delta) {
|
|
selection += delta;
|
|
if (selection < 0) selection = NUM_OPTS - 1;
|
|
if (selection >= NUM_OPTS) selection = 0;
|
|
|
|
if (selection < NUM_OPTS && list_btns[selection]) {
|
|
lv_obj_scroll_to_view(list_btns[selection], LV_ANIM_ON);
|
|
}
|
|
update_desc();
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Confirmation overlay */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static int show_confirm(const char *warning, const char *action_name) {
|
|
lv_obj_clean(lv_scr_act());
|
|
|
|
lv_obj_t *scr = lv_scr_act();
|
|
|
|
lv_obj_t *box = lv_obj_create(scr);
|
|
lv_obj_set_size(box, 296, 180);
|
|
lv_obj_align(box, LV_ALIGN_CENTER, 0, 0);
|
|
lv_obj_set_style_bg_color(box, lv_color_make(24, 18, 18), LV_PART_MAIN);
|
|
lv_obj_set_style_border_color(box, COLOR_PRIMARY, LV_PART_MAIN);
|
|
lv_obj_set_style_border_width(box, 2, LV_PART_MAIN);
|
|
|
|
lv_obj_t *title = lv_label_create(box);
|
|
lv_label_set_text(title, action_name);
|
|
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 12);
|
|
lv_obj_set_style_text_color(title, COLOR_PRIMARY, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(title, &lv_font_montserrat_14, LV_PART_MAIN);
|
|
|
|
lv_obj_t *warn = lv_label_create(box);
|
|
lv_label_set_text(warn, warning);
|
|
lv_obj_align(warn, LV_ALIGN_TOP_MID, 0, 38);
|
|
lv_obj_set_style_text_color(warn, COLOR_TEXT, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(warn, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
lv_obj_set_style_text_align(warn, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
|
|
|
|
lv_obj_t *prompt = lv_label_create(box);
|
|
lv_label_set_text(prompt, "[PLAY] CONFIRM [BACK] CANCEL");
|
|
lv_obj_align(prompt, LV_ALIGN_BOTTOM_MID, 0, -16);
|
|
lv_obj_set_style_text_color(prompt, lv_color_make(120, 120, 140),
|
|
LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(prompt, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
|
|
/* Wait for PLAY or BACK */
|
|
struct input_event ev;
|
|
while (1) {
|
|
lv_timer_handler();
|
|
|
|
if (input_fd >= 0 &&
|
|
read(input_fd, &ev, sizeof(struct input_event)) > 0) {
|
|
if (ev.type == EV_KEY && ev.value == 1) {
|
|
if (ev.code == H2_KEY_PLAY) {
|
|
lv_label_set_text(prompt, "EXECUTING...");
|
|
lv_obj_set_style_text_color(prompt, COLOR_ACCENT,
|
|
LV_PART_MAIN);
|
|
lv_timer_handler();
|
|
return 1; /* confirmed */
|
|
}
|
|
if (ev.code == H2_KEY_BACK) return 0; /* cancelled */
|
|
}
|
|
}
|
|
usleep(15000);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Status overlay (result message + BACK to dismiss) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void show_status(const char *msg, int is_error) {
|
|
lv_obj_clean(lv_scr_act());
|
|
|
|
lv_obj_t *scr = lv_scr_act();
|
|
|
|
lv_obj_t *box = lv_obj_create(scr);
|
|
lv_obj_set_size(box, 296, 120);
|
|
lv_obj_align(box, LV_ALIGN_CENTER, 0, 0);
|
|
|
|
lv_obj_t *lbl = lv_label_create(box);
|
|
lv_label_set_text(lbl, msg);
|
|
lv_obj_align(lbl, LV_ALIGN_CENTER, 0, 0);
|
|
lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(lbl, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
|
|
if (is_error)
|
|
lv_obj_set_style_text_color(lbl, COLOR_PRIMARY, LV_PART_MAIN);
|
|
else
|
|
lv_obj_set_style_text_color(lbl, COLOR_ACCENT, LV_PART_MAIN);
|
|
|
|
lv_obj_t *hint = lv_label_create(scr);
|
|
lv_label_set_text(hint, "[BACK] Return to menu");
|
|
lv_obj_align(hint, LV_ALIGN_BOTTOM_MID, 0, -4);
|
|
lv_obj_set_style_text_color(hint, lv_color_make(80, 85, 100),
|
|
LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(hint, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
|
|
/* Wait for BACK */
|
|
struct input_event ev;
|
|
while (1) {
|
|
lv_timer_handler();
|
|
if (input_fd >= 0 &&
|
|
read(input_fd, &ev, sizeof(struct input_event)) > 0) {
|
|
if (ev.type == EV_KEY && ev.value == 1 &&
|
|
ev.code == H2_KEY_BACK)
|
|
break;
|
|
}
|
|
usleep(15000);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Rebuild the main menu after a confirmation/status overlay */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void rebuild_main_ui(void) {
|
|
lv_obj_clean(lv_scr_act());
|
|
lv_obj_t *scr = lv_scr_act();
|
|
|
|
lv_obj_t *title = lv_label_create(scr);
|
|
lv_label_set_text(title, "SYSTEM TOOLS");
|
|
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 8);
|
|
lv_obj_set_style_text_color(title, COLOR_PRIMARY, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(title, &lv_font_montserrat_14, LV_PART_MAIN);
|
|
|
|
lv_obj_t *sub = lv_label_create(scr);
|
|
lv_label_set_text(sub, "Aigo Eros Q | X1000E");
|
|
lv_obj_align(sub, LV_ALIGN_TOP_MID, 0, 26);
|
|
lv_obj_set_style_text_color(sub, COLOR_MUTED, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(sub, &lv_font_montserrat_10, LV_PART_MAIN);
|
|
|
|
menu_list = lv_list_create(scr);
|
|
lv_obj_set_size(menu_list, 296, 140);
|
|
lv_obj_align(menu_list, LV_ALIGN_TOP_MID, 0, 42);
|
|
lv_obj_set_style_bg_color(menu_list,
|
|
lv_color_make(18, 22, 32), LV_PART_MAIN);
|
|
lv_obj_set_style_border_width(menu_list, 1, LV_PART_MAIN);
|
|
lv_obj_set_style_border_color(menu_list,
|
|
lv_color_make(50, 55, 70), LV_PART_MAIN);
|
|
|
|
desc_label = lv_label_create(scr);
|
|
lv_obj_set_size(desc_label, 296, 36);
|
|
lv_obj_align(desc_label, LV_ALIGN_TOP_MID, 0, 186);
|
|
lv_obj_set_style_text_color(desc_label, COLOR_TEXT, LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(desc_label, &lv_font_montserrat_10,
|
|
LV_PART_MAIN);
|
|
lv_obj_set_style_text_align(desc_label, LV_TEXT_ALIGN_CENTER,
|
|
LV_PART_MAIN);
|
|
|
|
status_lbl = lv_label_create(scr);
|
|
lv_label_set_text(status_lbl, "ROTATE=scroll PLAY=select BACK=exit");
|
|
lv_obj_align(status_lbl, LV_ALIGN_BOTTOM_MID, 0, -4);
|
|
lv_obj_set_style_text_color(status_lbl, lv_color_make(80, 85, 100),
|
|
LV_PART_MAIN);
|
|
lv_obj_set_style_text_font(status_lbl, &lv_font_montserrat_10,
|
|
LV_PART_MAIN);
|
|
|
|
build_menu();
|
|
update_desc();
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* main */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
int main(void) {
|
|
init_h2_graphics_runtime("SYSTEM TOOLS");
|
|
LOG_INF("reset/system-tools module started");
|
|
|
|
input_fd = open("/dev/input/event0", O_RDONLY | O_NONBLOCK);
|
|
|
|
/* Build initial UI */
|
|
rebuild_main_ui();
|
|
|
|
/* Detect available options */
|
|
int has_stock_backup = dir_exists("/data/stock_backup");
|
|
int has_rockbox = dir_exists("/.rockbox") || dir_exists("/rockbox");
|
|
|
|
if (has_stock_backup)
|
|
LOG_INF("stock backup detected: /data/stock_backup/");
|
|
if (has_rockbox)
|
|
LOG_INF("Rockbox detected on SD card");
|
|
|
|
/* Input loop */
|
|
struct input_event ev;
|
|
while (1) {
|
|
lv_timer_handler();
|
|
|
|
if (input_fd >= 0 &&
|
|
read(input_fd, &ev, sizeof(struct input_event)) > 0) {
|
|
|
|
if (ev.type == EV_REL && ev.code == 0 && ev.value != 0) {
|
|
scroll_menu(ev.value > 0 ? 1 : -1);
|
|
}
|
|
|
|
if (ev.type == EV_KEY && ev.value == 1) {
|
|
if (ev.code == H2_KEY_BACK) break;
|
|
|
|
if (ev.code == H2_KEY_PLAY) {
|
|
switch ((menu_opt_t)selection) {
|
|
case OPT_PURGE:
|
|
if (show_confirm(
|
|
"Clears all captured data, kills\n"
|
|
"child processes, drops network.\n"
|
|
"OreBolt OS files are NOT removed.",
|
|
"OPERATIONAL PURGE")) {
|
|
action_purge();
|
|
show_status("PURGE COMPLETE.\n\n"
|
|
"All captured data cleared.", 0);
|
|
}
|
|
rebuild_main_ui();
|
|
break;
|
|
|
|
case OPT_HIBY:
|
|
if (!has_stock_backup) {
|
|
show_status(
|
|
"No stock backup found.\n\n"
|
|
"/data/stock_backup/ does not\n"
|
|
"exist. Was OreBolt OS deployed\n"
|
|
"with --backup-stock?", 1);
|
|
rebuild_main_ui();
|
|
break;
|
|
}
|
|
if (show_confirm(
|
|
"This will REMOVE OreBolt OS from\n"
|
|
"the SD card and restore the stock\n"
|
|
"Hiby Player firmware. All OreBolt\n"
|
|
"modules, data, and configs will be\n"
|
|
"permanently deleted.\n\n"
|
|
"The device will reboot into the\n"
|
|
"original Hiby music player.",
|
|
"RESTORE HIBY PLAYER")) {
|
|
/* This reboots -- does not return */
|
|
action_hiby();
|
|
}
|
|
rebuild_main_ui();
|
|
break;
|
|
|
|
case OPT_ROCKBOX:
|
|
if (!has_rockbox) {
|
|
show_status(
|
|
"No Rockbox installation found.\n\n"
|
|
"Install .rockbox/ on the SD card\n"
|
|
"or use a Rockboot-flashed SD card.", 1);
|
|
rebuild_main_ui();
|
|
break;
|
|
}
|
|
if (show_confirm(
|
|
"Reboot to Rockbox.\n\n"
|
|
"The Rockbox bootloader will\n"
|
|
"chainload from the SD card.\n"
|
|
"OreBolt OS files are NOT removed.",
|
|
"BOOT ROCKBOX")) {
|
|
/* This reboots -- does not return */
|
|
action_rockbox();
|
|
}
|
|
rebuild_main_ui();
|
|
break;
|
|
|
|
case OPT_REBOOT:
|
|
if (show_confirm(
|
|
"Standard warm reboot.\n"
|
|
"Device will restart normally.",
|
|
"REBOOT DEVICE")) {
|
|
LOG_INF("user-initiated reboot");
|
|
sync();
|
|
reboot(RB_AUTOBOOT);
|
|
}
|
|
rebuild_main_ui();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
usleep(15000);
|
|
}
|
|
|
|
if (input_fd >= 0) close(input_fd);
|
|
LOG_INF("system-tools module exited");
|
|
return 0;
|
|
} |