summaryrefslogtreecommitdiff
path: root/bootloader/xduoo_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'bootloader/xduoo_linux.c')
-rw-r--r--bootloader/xduoo_linux.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/bootloader/xduoo_linux.c b/bootloader/xduoo_linux.c
new file mode 100644
index 0000000000..1071c6dc9b
--- /dev/null
+++ b/bootloader/xduoo_linux.c
@@ -0,0 +1,555 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 *
10 * Copyright (C) 2016 by Amaury Pouly
11 * 2018 by Marcin Bukat
12 * 2018 by Roman Stolyarov
13 *
14 * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
15 * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
21 *
22 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23 * KIND, either express or implied.
24 *
25 ****************************************************************************/
26
27#include "system.h"
28#include "lcd.h"
29#include "backlight.h"
30#include "button-target.h"
31#include "button.h"
32#include "../kernel/kernel-internal.h"
33#include "core_alloc.h"
34#include "filesystem-app.h"
35#include "lcd.h"
36#include "font.h"
37#include "power.h"
38#include <string.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <fcntl.h>
44#include <dirent.h>
45#include <sys/wait.h>
46#include <stdarg.h>
47#include "version.h"
48
49/* all images must have the following size */
50#define ICON_WIDTH 130
51#define ICON_HEIGHT 130
52
53/* images */
54#include "bitmaps/rockboxicon.h"
55#include "bitmaps/hibyicon.h"
56#include "bitmaps/toolsicon.h"
57
58/* don't issue an error when parsing the file for dependencies */
59#if defined(BMPWIDTH_rockboxicon) && (BMPWIDTH_rockboxicon != ICON_WIDTH || \
60 BMPHEIGHT_rockboxicon != ICON_HEIGHT)
61#error rockboxicon has the wrong resolution
62#endif
63#if defined(BMPWIDTH_hibyicon) && (BMPWIDTH_hibyicon != ICON_WIDTH || \
64 BMPHEIGHT_hibyicon != ICON_HEIGHT)
65#error hibyicon has the wrong resolution
66#endif
67#if defined(BMPWIDTH_toolsicon) && (BMPWIDTH_toolsicon != ICON_WIDTH || \
68 BMPHEIGHT_toolsicon != ICON_HEIGHT)
69#error toolsicon has the wrong resolution
70#endif
71
72#ifndef BUTTON_UP
73#define BUTTON_UP BUTTON_PREV
74#endif
75#ifndef BUTTON_DOWN
76#define BUTTON_DOWN BUTTON_NEXT
77#endif
78#ifndef BUTTON_ENTER
79#define BUTTON_ENTER BUTTON_PLAY
80#endif
81
82/* return icon y position (x is always centered) */
83static int get_icon_y(void)
84{
85 int h;
86 lcd_getstringsize("X", NULL, &h);
87 return ((LCD_HEIGHT - ICON_HEIGHT)/2) - h;
88}
89
90/* Important Note: this bootloader is carefully written so that in case of
91 * error, the OF is run. This seems like the safest option since the OF is
92 * always there and might do magic things. */
93
94enum boot_mode
95{
96 BOOT_ROCKBOX,
97 BOOT_TOOLS,
98 BOOT_OF,
99 BOOT_COUNT,
100 BOOT_USB, /* special */
101 BOOT_STOP, /* power down/suspend */
102};
103
104static void display_text_center(int y, const char *text)
105{
106 int width;
107 lcd_getstringsize(text, &width, NULL);
108 lcd_putsxy(LCD_WIDTH / 2 - width / 2, y, text);
109}
110
111static void display_text_centerf(int y, const char *format, ...)
112{
113 char buf[1024];
114 va_list ap;
115 va_start(ap, format);
116
117 vsnprintf(buf, sizeof(buf), format, ap);
118 display_text_center(y, buf);
119}
120
121/* get timeout before taking action if the user doesn't touch the device */
122static int get_inactivity_tmo(void)
123{
124#if defined(HAS_BUTTON_HOLD)
125 if(button_hold())
126 return 5 * HZ; /* Inactivity timeout when on hold */
127 else
128#endif
129 return 10 * HZ; /* Inactivity timeout when not on hold */
130}
131
132/* return action on idle timeout */
133static enum boot_mode inactivity_action(enum boot_mode cur_selection)
134{
135#if defined(HAS_BUTTON_HOLD)
136 if(button_hold())
137 return BOOT_STOP; /* power down/suspend */
138 else
139#endif
140 return cur_selection; /* return last choice */
141}
142
143/* we store the boot mode in a file in /tmp so we can reload it between 'boots'
144 * (since the mostly suspends instead of powering down) */
145static enum boot_mode load_boot_mode(enum boot_mode mode)
146{
147 int fd = open("/data/rb_bl_mode.txt", O_RDONLY);
148 if(fd >= 0)
149 {
150 read(fd, &mode, sizeof(mode));
151 close(fd);
152 }
153 return mode;
154}
155
156static void save_boot_mode(enum boot_mode mode)
157{
158 int fd = open("/data/rb_bl_mode.txt", O_RDWR | O_CREAT | O_TRUNC);
159 if(fd >= 0)
160 {
161 write(fd, &mode, sizeof(mode));
162 close(fd);
163 }
164}
165
166static enum boot_mode get_boot_mode(void)
167{
168 /* load previous mode, or start with rockbox if none */
169 enum boot_mode init_mode = load_boot_mode(BOOT_ROCKBOX);
170 /* wait for user action */
171 enum boot_mode mode = init_mode;
172 int last_activity = current_tick;
173#if defined(HAS_BUTTON_HOLD)
174 bool hold_status = button_hold();
175#endif
176 while(true)
177 {
178 /* on usb detect, return to usb
179 * FIXME this is a hack, we need proper usb detection */
180 if(power_input_status() & POWER_INPUT_USB_CHARGER)
181 {
182 /* save last choice */
183 save_boot_mode(mode);
184 return BOOT_USB;
185 }
186 /* inactivity detection */
187 int timeout = last_activity + get_inactivity_tmo();
188 if(TIME_AFTER(current_tick, timeout))
189 {
190 /* save last choice */
191 save_boot_mode(mode);
192 return inactivity_action(mode);
193 }
194 /* redraw */
195 lcd_clear_display();
196 /* display top text */
197#if defined(HAS_BUTTON_HOLD)
198 if(button_hold())
199 {
200 lcd_set_foreground(LCD_RGBPACK(255, 0, 0));
201 display_text_center(0, "ON HOLD!");
202 }
203 else
204#endif
205 {
206 lcd_set_foreground(LCD_RGBPACK(255, 201, 0));
207 display_text_center(0, "SELECT PLAYER");
208 }
209 lcd_set_foreground(LCD_RGBPACK(255, 201, 0));
210 /* display icon */
211 const struct bitmap *icon = (mode == BOOT_OF) ? &bm_hibyicon :
212 (mode == BOOT_ROCKBOX) ? &bm_rockboxicon : &bm_toolsicon;
213 lcd_bmp(icon, (LCD_WIDTH - ICON_WIDTH) / 2, get_icon_y());
214 /* display bottom description */
215 const char *desc = (mode == BOOT_OF) ? "HIBY PLAYER" :
216 (mode == BOOT_ROCKBOX) ? "ROCKBOX" : "TOOLS";
217
218 int desc_height;
219 lcd_getstringsize(desc, NULL, &desc_height);
220 display_text_center(LCD_HEIGHT - 3*desc_height, desc);
221
222 /* display arrows */
223 int arrow_width, arrow_height;
224 lcd_getstringsize("<", &arrow_width, &arrow_height);
225 int arrow_y = get_icon_y() + ICON_HEIGHT / 2 - arrow_height / 2;
226 lcd_putsxy(arrow_width / 2, arrow_y, "<");
227 lcd_putsxy(LCD_WIDTH - 3 * arrow_width / 2, arrow_y, ">");
228
229 lcd_set_foreground(LCD_RGBPACK(0, 255, 0));
230 display_text_centerf(LCD_HEIGHT - arrow_height * 3 / 2, "timeout in %d sec",
231 (timeout - current_tick + HZ - 1) / HZ);
232
233 lcd_update();
234
235 /* wait for a key */
236 int btn = button_get_w_tmo(HZ / 10);
237
238#if defined(HAS_BUTTON_HOLD)
239 /* record action, changing HOLD counts as action */
240 if(btn & BUTTON_MAIN || hold_status != button_hold())
241 last_activity = current_tick;
242
243 hold_status = button_hold();
244#else
245 if(btn & BUTTON_MAIN)
246 last_activity = current_tick;
247#endif
248 /* ignore release, allow repeat */
249 if(btn & BUTTON_REL)
250 continue;
251 if(btn & BUTTON_REPEAT)
252 btn &= ~BUTTON_REPEAT;
253 /* play -> stop loop and return mode */
254 if(btn == BUTTON_ENTER)
255 break;
256 /* left/right/up/down: change mode */
257 if(btn == BUTTON_UP || btn == BUTTON_VOL_UP)
258 mode = (mode + BOOT_COUNT - 1) % BOOT_COUNT;
259 if(btn == BUTTON_DOWN || btn == BUTTON_VOL_DOWN)
260 mode = (mode + 1) % BOOT_COUNT;
261 }
262
263 /* save mode */
264 save_boot_mode(mode);
265 return mode;
266}
267
268void error_screen(const char *msg)
269{
270 lcd_clear_display();
271 lcd_putsf(0, 0, msg);
272 lcd_update();
273}
274
275int choice_screen(const char *title, bool center, int nr_choices, const char *choices[])
276{
277 int choice = 0;
278 int max_len = 0;
279 int h;
280 lcd_getstringsize("x", NULL, &h);
281 for(int i = 0; i < nr_choices; i++)
282 {
283 int len = strlen(choices[i]);
284 if(len > max_len)
285 max_len = len;
286 }
287 char *buf = malloc(max_len + 10);
288 int top_y = 2 * h;
289 int nr_lines = (LCD_HEIGHT - top_y) / h;
290 while(true)
291 {
292 /* make sure choice is visible */
293 int offset = choice - nr_lines / 2;
294 if(offset < 0)
295 offset = 0;
296 lcd_clear_display();
297 /* display top text */
298 lcd_set_foreground(LCD_RGBPACK(255, 201, 0));
299 display_text_center(0, title);
300 int line = 0;
301 for(int i = 0; i < nr_choices && line < nr_lines; i++)
302 {
303 if(i < offset)
304 continue;
305 if(i == choice)
306 lcd_set_foreground(LCD_RGBPACK(255, 0, 0));
307 else
308 lcd_set_foreground(LCD_RGBPACK(255, 201, 0));
309 sprintf(buf, "%s", choices[i]);
310 if(center)
311 display_text_center(top_y + h * line, buf);
312 else
313 lcd_putsxy(0, top_y + h * line, buf);
314 line++;
315 }
316
317 lcd_update();
318
319 /* wait for a key */
320 int btn = button_get_w_tmo(HZ / 10);
321 /* ignore release, allow repeat */
322 if(btn & BUTTON_REL)
323 continue;
324 if(btn & BUTTON_REPEAT)
325 btn &= ~BUTTON_REPEAT;
326 /* play -> stop loop and return mode */
327 if(btn == BUTTON_ENTER)
328 {
329 free(buf);
330 return btn == BUTTON_ENTER ? choice : -1;
331 }
332 /* left/right/up/down: change mode */
333 if(btn == BUTTON_UP || btn == BUTTON_VOL_UP)
334 choice = (choice + nr_choices - 1) % nr_choices;
335 if(btn == BUTTON_DOWN || btn == BUTTON_VOL_DOWN)
336 choice = (choice + 1) % nr_choices;
337 }
338}
339
340void run_file(const char *name)
341{
342 char *dirname = "/mnt/sd_0/";
343 char *buf = malloc(strlen(dirname) + strlen(name) + 1);
344 sprintf(buf, "%s%s", dirname, name);
345
346 lcd_clear_display();
347 lcd_set_foreground(LCD_RGBPACK(255, 201, 0));
348 lcd_putsf(0, 0, "Running %s", name);
349 lcd_update();
350
351 pid_t pid = fork();
352 if(pid == 0)
353 {
354 execlp("sh", "sh", buf, NULL);
355 _exit(42);
356 }
357 int status;
358 waitpid(pid, &status, 0);
359 if(WIFEXITED(status))
360 {
361 lcd_set_foreground(LCD_RGBPACK(255, 201, 0));
362 lcd_putsf(0, 1, "program returned %d", WEXITSTATUS(status));
363 }
364 else
365 {
366 lcd_set_foreground(LCD_RGBPACK(255, 0, 0));
367 lcd_putsf(0, 1, "an error occured: %x", status);
368 }
369 lcd_set_foreground(LCD_RGBPACK(255, 0, 0));
370 lcd_putsf(0, 3, "Press any key or wait");
371 lcd_update();
372 /* wait a small time */
373 sleep(HZ);
374 /* ignore event */
375 while(button_get(false) != 0) {}
376 /* wait for any key or timeout */
377 button_get_w_tmo(4 * HZ);
378}
379
380void run_script_menu(void)
381{
382 const char **entries = NULL;
383 int nr_entries = 0;
384 DIR *dir = opendir("/mnt/sd_0");
385 struct dirent *ent;
386 while((ent = readdir(dir)))
387 {
388 if(ent->d_type != DT_REG)
389 continue;
390 entries = realloc(entries, (nr_entries + 1) * sizeof(const char *));
391 entries[nr_entries++] = strdup(ent->d_name);
392 }
393 closedir(dir);
394 int idx = choice_screen("RUN SCRIPT", false, nr_entries, entries);
395 if(idx >= 0)
396 run_file(entries[idx]);
397 for(int i = 0; i < nr_entries; i++)
398 free((char *)entries[i]);
399 free(entries);
400}
401
402static void adb(int start)
403{
404 pid_t pid = fork();
405 if(pid == 0)
406 {
407 execlp("/etc/init.d/K90adb", "K90adb", start ? "start" : "stop", NULL);
408 _exit(42);
409 }
410 int status;
411 waitpid(pid, &status, 0);
412#if 0
413 if(WIFEXITED(status))
414 {
415 lcd_set_foreground(LCD_RGBPACK(255, 201, 0));
416 lcd_putsf(0, 1, "program returned %d", WEXITSTATUS(status));
417 }
418 else
419 {
420 lcd_set_foreground(LCD_RGBPACK(255, 0, 0));
421 lcd_putsf(0, 1, "an error occured: %x", status);
422 }
423#endif
424}
425
426static void tools_screen(void)
427{
428 const char *choices[] = {"ADB start", "ADB stop", "Run script", "Restart", "Shutdown"};
429 int choice = choice_screen("TOOLS MENU", true, 5, choices);
430 if(choice == 0)
431 {
432 /* run service menu */
433 printf("Starting ADB service...\n");
434 fflush(stdout);
435 adb(1);
436 }
437 else if(choice == 1)
438 {
439 printf("Stopping ADB service...\n");
440 fflush(stdout);
441 adb(0);
442 }
443 else if(choice == 2)
444 {
445 run_script_menu();
446 }
447 else if(choice == 3)
448 system_reboot();
449 else if(choice == 4)
450 power_off();
451}
452
453#if 0
454/* open log file */
455static int open_log(void)
456{
457 /* open regular log file */
458 int fd = open("/mnt/sd_0/rockbox.log", O_RDWR | O_CREAT | O_APPEND);
459 /* get its size */
460 struct stat stat;
461 if(fstat(fd, &stat) != 0)
462 return fd; /* on error, don't do anything */
463 /* if file is too large, rename it and start a new log file */
464 if(stat.st_size < 1000000)
465 return fd;
466 close(fd);
467 /* move file */
468 rename("/mnt/sd_0/rockbox.log", "/mnt/sd_0/rockbox.log.1");
469 /* re-open the file, truncate in case the move was unsuccessful */
470 return open("/mnt/sd_0/rockbox.log", O_RDWR | O_CREAT | O_APPEND | O_TRUNC);
471}
472#endif
473
474int main(int argc, char **argv)
475{
476 (void) argc;
477 (void) argv;
478#if 0
479 /* redirect stdout and stderr to have error messages logged somewhere on the
480 * user partition */
481 int fd = open_log();
482 if(fd >= 0)
483 {
484 dup2(fd, fileno(stdout));
485 dup2(fd, fileno(stderr));
486 close(fd);
487 }
488 /* print version */
489 printf("Rockbox boot loader\n");
490 printf("Version: %s\n", rbversion);
491 printf("%s\n", MODEL_NAME);
492#endif
493
494 system_init();
495 core_allocator_init();
496 kernel_init();
497 paths_init();
498 lcd_init();
499 font_init();
500 button_init();
501 backlight_init();
502 backlight_set_brightness(DEFAULT_BRIGHTNESS_SETTING);
503
504 /* try to load the extra font we install on the device */
505 //int font_id = font_load("/usr/rockbox/fonts/20-Terminus-Bold.fnt");
506 //if(font_id >= 0)
507 // lcd_setfont(font_id);
508
509 /* run all tools menu */
510 while(true)
511 {
512 enum boot_mode mode = get_boot_mode();
513 if(mode == BOOT_USB || mode == BOOT_OF)
514 {
515#if 0
516 fflush(stdout);
517 fflush(stderr);
518 close(fileno(stdout));
519 close(fileno(stderr));
520#endif
521 /* for now the only way we have to trigger USB mode it to run the OF */
522 /* boot OF */
523 execvp("/usr/bin/hiby_player", argv);
524 error_screen("Cannot boot OF");
525 sleep(5 * HZ);
526 }
527 else if(mode == BOOT_TOOLS)
528 {
529 tools_screen();
530 }
531 else if(mode == BOOT_ROCKBOX)
532 {
533 fflush(stdout);
534#if defined(XDUOO_X3II)
535 system("/bin/cp /mnt/sd_0/.rockbox/rockbox.x3ii /tmp");
536 execl("/tmp/rockbox.x3ii", "rockbox.x3ii", NULL);
537#elif defined(XDUOO_X20)
538 system("/bin/cp /mnt/sd_0/.rockbox/rockbox.x20 /tmp");
539 execl("/tmp/rockbox.x20", "rockbox.x20", NULL);
540#endif
541 printf("execvp failed: %s\n", strerror(errno));
542 /* fallback to OF in case of failure */
543 error_screen("Cannot boot Rockbox");
544 sleep(5 * HZ);
545 }
546 else
547 {
548 printf("suspend\n");
549// nwz_power_suspend();
550 }
551 }
552 /* if we reach this point, everything failed, so return an error so that
553 * sysmgrd knows something is wrong */
554 return 1;
555}