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