diff options
-rw-r--r-- | bootloader/SOURCES | 2 | ||||
-rw-r--r-- | bootloader/fiiom3k.c | 360 | ||||
-rw-r--r-- | bootloader/x1000.c | 490 |
3 files changed, 491 insertions, 361 deletions
diff --git a/bootloader/SOURCES b/bootloader/SOURCES index 57c23b115c..f72c58a0b7 100644 --- a/bootloader/SOURCES +++ b/bootloader/SOURCES | |||
@@ -90,5 +90,5 @@ show_logo.c | |||
90 | sansaconnect.c | 90 | sansaconnect.c |
91 | show_logo.c | 91 | show_logo.c |
92 | #elif defined(FIIO_M3K) | 92 | #elif defined(FIIO_M3K) |
93 | fiiom3k.c | 93 | x1000.c |
94 | #endif | 94 | #endif |
diff --git a/bootloader/fiiom3k.c b/bootloader/fiiom3k.c deleted file mode 100644 index 42b3260e07..0000000000 --- a/bootloader/fiiom3k.c +++ /dev/null | |||
@@ -1,360 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "system.h" | ||
23 | #include "core_alloc.h" | ||
24 | #include "kernel/kernel-internal.h" | ||
25 | #include "i2c.h" | ||
26 | #include "power.h" | ||
27 | #include "lcd.h" | ||
28 | #include "font.h" | ||
29 | #include "backlight.h" | ||
30 | #include "backlight-target.h" | ||
31 | #include "button.h" | ||
32 | #include "storage.h" | ||
33 | #include "file_internal.h" | ||
34 | #include "disk.h" | ||
35 | #include "usb.h" | ||
36 | #include "rb-loader.h" | ||
37 | #include "loader_strerror.h" | ||
38 | #include "version.h" | ||
39 | #include "installer-fiiom3k.h" | ||
40 | #include "boot-x1000.h" | ||
41 | #include "x1000/cpm.h" | ||
42 | |||
43 | /* Load address where the binary needs to be placed */ | ||
44 | extern unsigned char loadaddress[]; | ||
45 | |||
46 | /* Fixed buffer to contain the loaded binary in memory */ | ||
47 | extern unsigned char loadbuffer[]; | ||
48 | extern unsigned char loadbufferend[]; | ||
49 | #define MAX_LOAD_SIZE (loadbufferend - loadbuffer) | ||
50 | |||
51 | void exec(void* dst, const void* src, int bytes) | ||
52 | __attribute__((noreturn, section(".icode"))); | ||
53 | |||
54 | void exec(void* dst, const void* src, int bytes) | ||
55 | { | ||
56 | memcpy(dst, src, bytes); | ||
57 | commit_discard_idcache(); | ||
58 | __asm__ __volatile__ ("jr %0\n" | ||
59 | "nop\n" | ||
60 | :: "r"(dst)); | ||
61 | __builtin_unreachable(); | ||
62 | } | ||
63 | |||
64 | static bool lcd_inited = false; | ||
65 | static bool usb_inited = false; | ||
66 | static bool disk_inited = false; | ||
67 | |||
68 | static void init_lcd(void) | ||
69 | { | ||
70 | if(lcd_inited) | ||
71 | return; | ||
72 | |||
73 | lcd_init(); | ||
74 | font_init(); | ||
75 | lcd_setfont(FONT_SYSFIXED); | ||
76 | |||
77 | /* Clear screen before turning backlight on, otherwise we might | ||
78 | * display random garbage on the screen */ | ||
79 | lcd_clear_display(); | ||
80 | lcd_update(); | ||
81 | |||
82 | backlight_init(); | ||
83 | |||
84 | lcd_inited = true; | ||
85 | } | ||
86 | |||
87 | static void put_version(void) | ||
88 | { | ||
89 | lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(rbversion))) / 2, | ||
90 | (LCD_HEIGHT - SYSFONT_HEIGHT), rbversion); | ||
91 | } | ||
92 | |||
93 | static void do_splash2(int delay, const char* msg, const char* msg2) | ||
94 | { | ||
95 | init_lcd(); | ||
96 | lcd_clear_display(); | ||
97 | lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg))) / 2, | ||
98 | (LCD_HEIGHT - SYSFONT_HEIGHT) / 2, msg); | ||
99 | if(msg2) { | ||
100 | lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg2))) / 2, | ||
101 | (LCD_HEIGHT + 2*SYSFONT_HEIGHT) / 2, msg2); | ||
102 | } | ||
103 | |||
104 | put_version(); | ||
105 | lcd_update(); | ||
106 | sleep(delay); | ||
107 | } | ||
108 | |||
109 | static void do_splash(int delay, const char* msg) | ||
110 | { | ||
111 | do_splash2(delay, msg, NULL); | ||
112 | } | ||
113 | |||
114 | static void do_usb(void) | ||
115 | { | ||
116 | if(!usb_inited) { | ||
117 | usb_init(); | ||
118 | usb_start_monitoring(); | ||
119 | usb_inited = true; | ||
120 | } | ||
121 | |||
122 | do_splash2(0, "Waiting for USB", "Press POWER to go back"); | ||
123 | |||
124 | int btn; | ||
125 | while(1) { | ||
126 | btn = button_get(true); | ||
127 | if(btn == SYS_USB_CONNECTED) | ||
128 | break; | ||
129 | else if(btn == BUTTON_POWER) | ||
130 | return; | ||
131 | } | ||
132 | |||
133 | do_splash(0, "USB mode"); | ||
134 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
135 | while(button_get(true) != SYS_USB_DISCONNECTED); | ||
136 | |||
137 | do_splash(3*HZ, "USB disconnected"); | ||
138 | } | ||
139 | |||
140 | static int init_disk(void) | ||
141 | { | ||
142 | if(disk_inited) | ||
143 | return 0; | ||
144 | |||
145 | while(!storage_present(0)) { | ||
146 | do_splash2(0, "Insert SD card", "Press POWER for recovery"); | ||
147 | int btn = button_get_w_tmo(HZ); | ||
148 | if(btn == BUTTON_POWER) | ||
149 | return 1; | ||
150 | } | ||
151 | |||
152 | if(disk_mount_all() <= 0) { | ||
153 | do_splash(5*HZ, "Cannot mount filesystem"); | ||
154 | return 1; | ||
155 | } | ||
156 | |||
157 | disk_inited = true; | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static void do_boot(void) | ||
162 | { | ||
163 | if(init_disk() != 0) | ||
164 | return; | ||
165 | |||
166 | int loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE); | ||
167 | if(loadsize <= 0) { | ||
168 | do_splash2(5*HZ, "Error loading Rockbox", | ||
169 | loader_strerror(loadsize)); | ||
170 | do_usb(); | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | if(lcd_inited) | ||
175 | backlight_hw_off(); | ||
176 | |||
177 | disable_irq(); | ||
178 | exec(loadaddress, loadbuffer, loadsize); | ||
179 | } | ||
180 | |||
181 | #define INSTALL 0 | ||
182 | #define BACKUP 1 | ||
183 | #define RESTORE 2 | ||
184 | |||
185 | static void do_install(int which) | ||
186 | { | ||
187 | int rc = init_disk(); | ||
188 | if(rc != 0) { | ||
189 | do_splash2(5*HZ, "Install aborted", "No SD card present"); | ||
190 | return; | ||
191 | } | ||
192 | |||
193 | const char* msg; | ||
194 | if(rc == INSTALL) | ||
195 | msg = "Installing"; | ||
196 | else if(rc == BACKUP) | ||
197 | msg = "Backing up"; | ||
198 | else | ||
199 | msg = "Restoring backup"; | ||
200 | |||
201 | do_splash(0, msg); | ||
202 | |||
203 | if(which == INSTALL) | ||
204 | rc = install_boot("/bootloader.m3k"); | ||
205 | else if(which == BACKUP) | ||
206 | rc = backup_boot("/fiiom3k-boot.bin"); | ||
207 | else | ||
208 | rc = restore_boot("/fiiom3k-boot.bin"); | ||
209 | |||
210 | char buf[32]; | ||
211 | snprintf(buf, sizeof(buf), "Failed! Error: %d", rc); | ||
212 | const char* msg1 = rc == 0 ? "Success" : buf; | ||
213 | const char* msg2 = "Press POWER to continue"; | ||
214 | do_splash2(0, msg1, msg2); | ||
215 | |||
216 | button_clear_queue(); | ||
217 | while(button_get(true) != BUTTON_POWER); | ||
218 | } | ||
219 | |||
220 | static void recovery_menu(void) | ||
221 | { | ||
222 | static const char* items[] = { | ||
223 | "--- Rockbox recovery menu ---", | ||
224 | "[System]", | ||
225 | " Start Rockbox", | ||
226 | " USB mode", | ||
227 | " Shutdown", | ||
228 | " Reboot", | ||
229 | "[Bootloader]", | ||
230 | " Install or update", | ||
231 | " Backup", | ||
232 | " Restore", | ||
233 | "", | ||
234 | "", | ||
235 | "", | ||
236 | "", | ||
237 | "", | ||
238 | "VOL+/VOL- move cursor", | ||
239 | "PLAY select item", | ||
240 | "POWER power off", | ||
241 | }; | ||
242 | |||
243 | static const int nitems = sizeof(items) / sizeof(char*); | ||
244 | |||
245 | init_lcd(); | ||
246 | |||
247 | int selection = 2; | ||
248 | do { | ||
249 | /* Draw menu */ | ||
250 | lcd_clear_display(); | ||
251 | |||
252 | for(int i = 0; i < nitems; ++i) | ||
253 | lcd_puts(0, i, items[i]); | ||
254 | |||
255 | if(items[selection][0] == ' ') | ||
256 | lcd_puts(0, selection, "=>"); | ||
257 | |||
258 | put_version(); | ||
259 | lcd_update(); | ||
260 | |||
261 | /* Clear queue to avoid accidental input */ | ||
262 | button_clear_queue(); | ||
263 | |||
264 | /* Get the button */ | ||
265 | int btn = button_get(true); | ||
266 | |||
267 | /* Process user input */ | ||
268 | if(btn == BUTTON_VOL_UP) { | ||
269 | for(int i = selection-1; i >= 0; --i) { | ||
270 | if(items[i][0] == ' ') { | ||
271 | selection = i; | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | continue; | ||
277 | } else if(btn == BUTTON_VOL_DOWN) { | ||
278 | for(int i = selection+1; i < nitems; ++i) { | ||
279 | if(items[i][0] == ' ') { | ||
280 | selection = i; | ||
281 | break; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | continue; | ||
286 | } else if(btn == BUTTON_POWER) { | ||
287 | selection = 4; /* Shutdown */ | ||
288 | } else if(btn != BUTTON_PLAY) { | ||
289 | continue; | ||
290 | } | ||
291 | |||
292 | /* User pressed PLAY so decide what action to take */ | ||
293 | switch(selection) { | ||
294 | case 2: /* Start rockbox */ | ||
295 | do_boot(); | ||
296 | break; | ||
297 | |||
298 | case 3: /* USB mode */ | ||
299 | do_usb(); | ||
300 | break; | ||
301 | |||
302 | case 4: /* Shutdown */ | ||
303 | do_splash(HZ, "Shutting down"); | ||
304 | power_off(); | ||
305 | break; | ||
306 | |||
307 | case 5: /* Reboot */ | ||
308 | do_splash(HZ, "Rebooting"); | ||
309 | system_reboot(); | ||
310 | break; | ||
311 | |||
312 | case 7: /* Install bootloader */ | ||
313 | do_install(INSTALL); | ||
314 | break; | ||
315 | |||
316 | case 8: /* Backup bootloader */ | ||
317 | do_install(BACKUP); | ||
318 | break; | ||
319 | |||
320 | case 9: /* Restore bootloader */ | ||
321 | do_install(RESTORE); | ||
322 | break; | ||
323 | |||
324 | default: | ||
325 | break; | ||
326 | } | ||
327 | } while(1); | ||
328 | } | ||
329 | |||
330 | void main(void) | ||
331 | { | ||
332 | bool recovery_mode = false; | ||
333 | |||
334 | if(get_boot_flag(BOOT_FLAG_USB_BOOT)) | ||
335 | recovery_mode = true; | ||
336 | |||
337 | system_init(); | ||
338 | core_allocator_init(); | ||
339 | kernel_init(); | ||
340 | i2c_init(); | ||
341 | power_init(); | ||
342 | button_init(); | ||
343 | enable_irq(); | ||
344 | |||
345 | if(storage_init() < 0) { | ||
346 | do_splash(3*HZ, "Failed to init storage"); | ||
347 | power_off(); | ||
348 | } | ||
349 | |||
350 | filesystem_init(); | ||
351 | |||
352 | if(button_read_device() & BUTTON_VOL_UP) | ||
353 | recovery_mode = true; | ||
354 | |||
355 | if(!recovery_mode) | ||
356 | do_boot(); | ||
357 | |||
358 | /* If boot fails or user holds Vol+, go to recovery menu */ | ||
359 | recovery_menu(); | ||
360 | } | ||
diff --git a/bootloader/x1000.c b/bootloader/x1000.c new file mode 100644 index 0000000000..0b28b7449d --- /dev/null +++ b/bootloader/x1000.c | |||
@@ -0,0 +1,490 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /* Unified bootloader for all X1000 targets. This is a bit messy. | ||
23 | * | ||
24 | * Features: | ||
25 | * - Text based user interface | ||
26 | * - USB mass storage access | ||
27 | * - Bootloader installation / backup / restore | ||
28 | * | ||
29 | * Possible future improvements: | ||
30 | * - Allow booting original firmware from the UI | ||
31 | */ | ||
32 | |||
33 | #include "system.h" | ||
34 | #include "core_alloc.h" | ||
35 | #include "kernel/kernel-internal.h" | ||
36 | #include "i2c.h" | ||
37 | #include "power.h" | ||
38 | #include "lcd.h" | ||
39 | #include "font.h" | ||
40 | #include "backlight.h" | ||
41 | #include "backlight-target.h" | ||
42 | #include "button.h" | ||
43 | #include "storage.h" | ||
44 | #include "file_internal.h" | ||
45 | #include "disk.h" | ||
46 | #include "usb.h" | ||
47 | #include "rb-loader.h" | ||
48 | #include "loader_strerror.h" | ||
49 | #include "version.h" | ||
50 | #include "boot-x1000.h" | ||
51 | #include <stddef.h> | ||
52 | #include <stdbool.h> | ||
53 | #include <string.h> | ||
54 | #include <stdio.h> | ||
55 | #include <stdarg.h> | ||
56 | |||
57 | #ifdef FIIO_M3K | ||
58 | # include "installer-fiiom3k.h" | ||
59 | #endif | ||
60 | |||
61 | #if defined(FIIO_M3K) | ||
62 | # define BL_RECOVERY BUTTON_VOL_UP | ||
63 | # define BL_UP BUTTON_VOL_UP | ||
64 | # define BL_DOWN BUTTON_VOL_DOWN | ||
65 | # define BL_SELECT BUTTON_PLAY | ||
66 | # define BL_QUIT BUTTON_POWER | ||
67 | # define BL_UP_NAME "VOL+" | ||
68 | # define BL_DOWN_NAME "VOL-" | ||
69 | # define BL_SELECT_NAME "PLAY" | ||
70 | # define BL_QUIT_NAME "POWER" | ||
71 | #else | ||
72 | # error "Missing keymap!" | ||
73 | #endif | ||
74 | |||
75 | enum { | ||
76 | MENUITEM_HEADING, | ||
77 | MENUITEM_ACTION, | ||
78 | }; | ||
79 | |||
80 | struct menuitem { | ||
81 | int type; | ||
82 | const char* text; | ||
83 | void(*action)(void); | ||
84 | }; | ||
85 | |||
86 | void clearscreen(void); | ||
87 | void putversion(void); | ||
88 | void putcenter_y(int y, const char* msg); | ||
89 | void putcenter_line(int line, const char* msg); | ||
90 | void splash2(long delay, const char* msg, const char* msg2); | ||
91 | void splash(long delay, const char* msg); | ||
92 | |||
93 | void init_lcd(void); | ||
94 | void init_usb(void); | ||
95 | int init_disk(void); | ||
96 | |||
97 | void recovery_menu(void) __attribute__((noreturn)); | ||
98 | |||
99 | void boot_rockbox(void); | ||
100 | void usb_mode(void); | ||
101 | void shutdown(void); | ||
102 | void reboot(void); | ||
103 | void bootloader_install(void); | ||
104 | void bootloader_backup(void); | ||
105 | void bootloader_restore(void); | ||
106 | |||
107 | /* Defines the recovery menu contents */ | ||
108 | const struct menuitem recovery_items[] = { | ||
109 | {MENUITEM_HEADING, "System", NULL}, | ||
110 | {MENUITEM_ACTION, "Start Rockbox", &boot_rockbox}, | ||
111 | {MENUITEM_ACTION, "USB mode", &usb_mode}, | ||
112 | {MENUITEM_ACTION, "Shutdown", &shutdown}, | ||
113 | {MENUITEM_ACTION, "Reboot", &reboot}, | ||
114 | {MENUITEM_HEADING, "Bootloader", NULL}, | ||
115 | {MENUITEM_ACTION, "Install or update", &bootloader_install}, | ||
116 | {MENUITEM_ACTION, "Backup", &bootloader_backup}, | ||
117 | {MENUITEM_ACTION, "Restore", &bootloader_restore}, | ||
118 | }; | ||
119 | |||
120 | /* Final load address of rockbox binary. | ||
121 | * NOTE: this is really the load address of the bootloader... it relies | ||
122 | * on the fact that bootloader and app are linked at the same address. */ | ||
123 | extern unsigned char loadaddress[]; | ||
124 | |||
125 | /* Temp buffer to contain the binary in memory */ | ||
126 | extern unsigned char loadbuffer[]; | ||
127 | extern unsigned char loadbufferend[]; | ||
128 | #define MAX_LOAD_SIZE (loadbufferend - loadbuffer) | ||
129 | |||
130 | /* Flags to indicate if hardware was already initialized */ | ||
131 | bool lcd_inited = false; | ||
132 | bool usb_inited = false; | ||
133 | bool disk_inited = false; | ||
134 | |||
135 | /* Jump to loaded binary */ | ||
136 | void exec(void* dst, const void* src, size_t bytes) | ||
137 | __attribute__((noinline, noreturn, section(".icode"))); | ||
138 | |||
139 | void exec(void* dst, const void* src, size_t bytes) | ||
140 | { | ||
141 | memcpy(dst, src, bytes); | ||
142 | commit_discard_idcache(); | ||
143 | |||
144 | typedef void(*entry_fn)(void) __attribute__((noreturn)); | ||
145 | entry_fn fn = (entry_fn)dst; | ||
146 | fn(); | ||
147 | } | ||
148 | |||
149 | void clearscreen(void) | ||
150 | { | ||
151 | init_lcd(); | ||
152 | lcd_clear_display(); | ||
153 | putversion(); | ||
154 | } | ||
155 | |||
156 | void putversion(void) | ||
157 | { | ||
158 | int x = (LCD_WIDTH - SYSFONT_WIDTH*strlen(rbversion)) / 2; | ||
159 | int y = LCD_HEIGHT - SYSFONT_HEIGHT; | ||
160 | lcd_putsxy(x, y, rbversion); | ||
161 | } | ||
162 | |||
163 | void putcenter_y(int y, const char* msg) | ||
164 | { | ||
165 | int x = (LCD_WIDTH - SYSFONT_WIDTH*strlen(msg)) / 2; | ||
166 | lcd_putsxy(x, y, msg); | ||
167 | } | ||
168 | |||
169 | void putcenter_line(int line, const char* msg) | ||
170 | { | ||
171 | int y = LCD_HEIGHT/2 + (line - 1)*SYSFONT_HEIGHT; | ||
172 | putcenter_y(y, msg); | ||
173 | } | ||
174 | |||
175 | void splash2(long delay, const char* msg, const char* msg2) | ||
176 | { | ||
177 | clearscreen(); | ||
178 | putcenter_line(0, msg); | ||
179 | if(msg2) | ||
180 | putcenter_line(1, msg2); | ||
181 | lcd_update(); | ||
182 | sleep(delay); | ||
183 | } | ||
184 | |||
185 | void splash(long delay, const char* msg) | ||
186 | { | ||
187 | splash2(delay, msg, NULL); | ||
188 | } | ||
189 | |||
190 | void init_lcd(void) | ||
191 | { | ||
192 | if(lcd_inited) | ||
193 | return; | ||
194 | |||
195 | lcd_init(); | ||
196 | font_init(); | ||
197 | lcd_setfont(FONT_SYSFIXED); | ||
198 | |||
199 | /* Clear screen before turning backlight on, otherwise we might | ||
200 | * display random garbage on the screen */ | ||
201 | lcd_clear_display(); | ||
202 | lcd_update(); | ||
203 | |||
204 | backlight_init(); | ||
205 | |||
206 | lcd_inited = true; | ||
207 | } | ||
208 | |||
209 | /* TODO: This does not work properly after a USB boot. | ||
210 | * | ||
211 | * The USB core is not properly reset by just re-initializing it, and I can't | ||
212 | * figure out how to make it work. Setting the DWC_DCTL.SDIS bit will force a | ||
213 | * disconnect (the usb-designware driver does this already as part of its init | ||
214 | * but it doesn't seem to cause a disconnect). | ||
215 | * | ||
216 | * But the host still doesn't detect us properly when we reconnect. Linux gives | ||
217 | * messages "usb 1-3: config 1 has no interfaces?" in dmesg and no mass storage | ||
218 | * interfaces show up. | ||
219 | * | ||
220 | * Re-plugging the cable seems to reset everything to a working state and there | ||
221 | * are no issues, but that's annoying. | ||
222 | */ | ||
223 | void init_usb(void) | ||
224 | { | ||
225 | if(usb_inited) | ||
226 | return; | ||
227 | |||
228 | usb_init(); | ||
229 | usb_start_monitoring(); | ||
230 | usb_inited = true; | ||
231 | } | ||
232 | |||
233 | int init_disk(void) | ||
234 | { | ||
235 | if(disk_inited) | ||
236 | return 0; | ||
237 | |||
238 | button_clear_queue(); | ||
239 | while(!storage_present(IF_MD(0))) { | ||
240 | splash2(0, "Insert SD card", "Press " BL_QUIT_NAME " for recovery"); | ||
241 | if(button_get_w_tmo(HZ/4) == BL_QUIT) | ||
242 | return -1; | ||
243 | } | ||
244 | |||
245 | if(disk_mount_all() <= 0) { | ||
246 | splash(5*HZ, "Cannot mount disk"); | ||
247 | return -1; | ||
248 | } | ||
249 | |||
250 | disk_inited = true; | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | void put_help_line(int line, const char* str1, const char* str2) | ||
255 | { | ||
256 | int width = LCD_WIDTH / SYSFONT_WIDTH; | ||
257 | lcd_puts(0, line, str1); | ||
258 | lcd_puts(width - strlen(str2), line, str2); | ||
259 | } | ||
260 | |||
261 | void recovery_menu(void) | ||
262 | { | ||
263 | const int n_items = sizeof(recovery_items)/sizeof(struct menuitem); | ||
264 | |||
265 | int selection = 0; | ||
266 | while(recovery_items[selection].type != MENUITEM_ACTION) | ||
267 | ++selection; | ||
268 | |||
269 | while(1) { | ||
270 | clearscreen(); | ||
271 | putcenter_y(0, "Rockbox recovery menu"); | ||
272 | |||
273 | int top_line = 2; | ||
274 | |||
275 | /* draw the menu */ | ||
276 | for(int i = 0; i < n_items; ++i) { | ||
277 | switch(recovery_items[i].type) { | ||
278 | case MENUITEM_HEADING: | ||
279 | lcd_putsf(0, top_line+i, "[%s]", recovery_items[i].text); | ||
280 | break; | ||
281 | |||
282 | case MENUITEM_ACTION: | ||
283 | lcd_puts(3, top_line+i, recovery_items[i].text); | ||
284 | break; | ||
285 | |||
286 | default: | ||
287 | break; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | /* draw the selection marker */ | ||
292 | lcd_puts(0, top_line+selection, "=>"); | ||
293 | |||
294 | /* draw the help text */ | ||
295 | int line = (LCD_HEIGHT - SYSFONT_HEIGHT)/SYSFONT_HEIGHT - 3; | ||
296 | put_help_line(line++, BL_DOWN_NAME "/" BL_UP_NAME, "move cursor"); | ||
297 | put_help_line(line++, BL_SELECT_NAME, "select item"); | ||
298 | put_help_line(line++, BL_QUIT_NAME, "power off"); | ||
299 | |||
300 | lcd_update(); | ||
301 | |||
302 | /* handle input */ | ||
303 | button_clear_queue(); | ||
304 | switch(button_get(true)) { | ||
305 | case BL_SELECT: { | ||
306 | if(recovery_items[selection].action) | ||
307 | recovery_items[selection].action(); | ||
308 | } break; | ||
309 | |||
310 | case BL_UP: | ||
311 | for(int i = selection-1; i >= 0; --i) { | ||
312 | if(recovery_items[i].action) { | ||
313 | selection = i; | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | break; | ||
318 | |||
319 | case BL_DOWN: | ||
320 | for(int i = selection+1; i < n_items; ++i) { | ||
321 | if(recovery_items[i].action) { | ||
322 | selection = i; | ||
323 | break; | ||
324 | } | ||
325 | } | ||
326 | break; | ||
327 | |||
328 | case BL_QUIT: | ||
329 | shutdown(); | ||
330 | break; | ||
331 | |||
332 | default: | ||
333 | break; | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | |||
338 | void boot_rockbox(void) | ||
339 | { | ||
340 | if(init_disk() != 0) | ||
341 | return; | ||
342 | |||
343 | int rc = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE); | ||
344 | if(rc <= 0) { | ||
345 | splash2(5*HZ, "Error loading Rockbox", loader_strerror(rc)); | ||
346 | return; | ||
347 | } | ||
348 | |||
349 | if(lcd_inited) | ||
350 | backlight_hw_off(); | ||
351 | |||
352 | disable_irq(); | ||
353 | exec(loadaddress, loadbuffer, rc); | ||
354 | } | ||
355 | |||
356 | void usb_mode(void) | ||
357 | { | ||
358 | init_usb(); | ||
359 | splash2(0, "Waiting for USB", "Press " BL_QUIT_NAME " to go back"); | ||
360 | |||
361 | while(1) { | ||
362 | int btn = button_get(true); | ||
363 | if(btn == SYS_USB_CONNECTED) | ||
364 | break; | ||
365 | else if(btn == BL_QUIT) | ||
366 | return; | ||
367 | } | ||
368 | |||
369 | splash(0, "USB mode"); | ||
370 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
371 | while(button_get(true) != SYS_USB_DISCONNECTED); | ||
372 | |||
373 | splash(3*HZ, "USB disconnected"); | ||
374 | } | ||
375 | |||
376 | void shutdown(void) | ||
377 | { | ||
378 | splash(HZ, "Shutting down"); | ||
379 | power_off(); | ||
380 | while(1); | ||
381 | } | ||
382 | |||
383 | void reboot(void) | ||
384 | { | ||
385 | splash(HZ, "Rebooting"); | ||
386 | system_reboot(); | ||
387 | while(1); | ||
388 | } | ||
389 | |||
390 | /* TODO: clean this up, make the installer generic as well */ | ||
391 | enum { | ||
392 | INSTALL, | ||
393 | BACKUP, | ||
394 | RESTORE, | ||
395 | }; | ||
396 | |||
397 | #ifdef FIIO_M3K | ||
398 | void bootloader_action(int which) | ||
399 | { | ||
400 | if(init_disk() != 0) { | ||
401 | splash2(5*HZ, "Install aborted", "Cannot access SD card"); | ||
402 | return; | ||
403 | } | ||
404 | |||
405 | const char* msg; | ||
406 | switch(which) { | ||
407 | case INSTALL: msg = "Installing"; break; | ||
408 | case BACKUP: msg = "Backing up"; break; | ||
409 | case RESTORE: msg = "Restoring"; break; | ||
410 | default: return; /* can't happen */ | ||
411 | } | ||
412 | |||
413 | splash(0, msg); | ||
414 | |||
415 | int rc; | ||
416 | switch(which) { | ||
417 | case INSTALL: rc = install_boot("/bootloader.m3k"); break; | ||
418 | case BACKUP: rc = backup_boot("/fiiom3k-boot.bin"); break; | ||
419 | case RESTORE: rc = restore_boot("/fiiom3k-boot.bin"); break; | ||
420 | default: return; | ||
421 | } | ||
422 | |||
423 | static char buf[64]; | ||
424 | snprintf(buf, sizeof(buf), "Failed! Error: %d", rc); | ||
425 | const char* msg1 = rc == 0 ? "Success" : buf; | ||
426 | const char* msg2 = "Press " BL_QUIT_NAME " to continue"; | ||
427 | splash2(0, msg1, msg2); | ||
428 | |||
429 | button_clear_queue(); | ||
430 | while(button_get(true) != BL_QUIT); | ||
431 | } | ||
432 | #else | ||
433 | void bootloader_action(int which) | ||
434 | { | ||
435 | (void)which; | ||
436 | splash(5*HZ, "Not implemented!"); | ||
437 | } | ||
438 | #endif | ||
439 | |||
440 | void bootloader_install(void) | ||
441 | { | ||
442 | bootloader_action(INSTALL); | ||
443 | } | ||
444 | |||
445 | void bootloader_backup(void) | ||
446 | { | ||
447 | bootloader_action(BACKUP); | ||
448 | } | ||
449 | |||
450 | void bootloader_restore(void) | ||
451 | { | ||
452 | bootloader_action(RESTORE); | ||
453 | } | ||
454 | |||
455 | void main(void) | ||
456 | { | ||
457 | system_init(); | ||
458 | core_allocator_init(); | ||
459 | kernel_init(); | ||
460 | i2c_init(); | ||
461 | power_init(); | ||
462 | button_init(); | ||
463 | enable_irq(); | ||
464 | |||
465 | if(storage_init() < 0) { | ||
466 | splash(5*HZ, "storage_init() failed"); | ||
467 | power_off(); | ||
468 | } | ||
469 | |||
470 | filesystem_init(); | ||
471 | |||
472 | /* If USB booting, the user probably needs to enter recovery mode; | ||
473 | * let's not force them to hold down the recovery key. */ | ||
474 | bool recovery_mode = get_boot_flag(BOOT_FLAG_USB_BOOT); | ||
475 | |||
476 | #ifdef HAVE_BUTTON_DATA | ||
477 | int bdata; | ||
478 | if(button_read_device(&bdata) & BL_RECOVERY) | ||
479 | #else | ||
480 | if(button_read_device() & BL_RECOVERY) | ||
481 | #endif | ||
482 | recovery_mode = true; | ||
483 | |||
484 | /* If boot fails, it will return and continue on below */ | ||
485 | if(!recovery_mode) | ||
486 | boot_rockbox(); | ||
487 | |||
488 | /* This function does not return. */ | ||
489 | recovery_menu(); | ||
490 | } | ||