1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006 by Greg White
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
25 #include "gcc_extensions.h"
28 #include "powermgmt.h"
33 #include "backlight.h"
38 #include "usb-target.h"
42 #define TAR_HEADER_SIZE 157
44 /* Where files sent via MTP are stored */
45 static const char basedir
[] = "/Content/0b00/00/";
46 /* Can use memory after vector table up to 0x01f00000 */
47 static char * const tarbuf
= (char *)0x00000040;
48 static const size_t tarbuf_size
= 0x01f00000 - 0x00000040;
50 static void * const load_buf
= 0x00000000;
51 static const size_t load_buf_size
= 0x20000000 - 0x100000;
52 static const void * const start_addr
= 0x00000000;
54 static void show_splash(int timeout
, const char *msg
)
58 lcd_putsxy( (LCD_WIDTH
- (SYSFONT_WIDTH
* strlen(msg
))) / 2,
59 (LCD_HEIGHT
- SYSFONT_HEIGHT
) / 2, msg
);
65 static bool pause_if_button_pressed(bool pre_usb
)
69 int button
= button_read_device();
71 if (pre_usb
&& !usb_plugged())
74 /* Exit if no button or only select buttons that have other
78 case USB_BL_INSTALL_MODE_BTN
:
80 break; /* Only before USB detect */
81 case BUTTON_MENU
: /* Settings reset */
82 case BUTTON_NONE
: /* Nothing pressed */
88 /* If the disk powers off, the firmware will lock at startup */
93 /* TODO: Handle charging while connected */
94 static void handle_usb(void)
98 /* Check if plugged and pause to look at messages. If the cable was pulled
99 * while waiting, proceed as if it never was plugged. */
100 if (!usb_plugged() || !pause_if_button_pressed(true))
103 /** Enter USB mode **/
105 /* We need full button and backlight handling now */
109 /* Start the USB driver */
111 usb_start_monitoring();
113 /* Wait for threads to connect or cable is pulled */
114 show_splash(HZ
/2, "Waiting for USB");
118 button
= button_get_w_tmo(HZ
/2);
120 if (button
== SYS_USB_CONNECTED
)
124 break; /* Cable pulled */
127 if (button
== SYS_USB_CONNECTED
)
129 /* Got the message - wait for disconnect */
130 show_splash(0, "Bootloader USB mode");
132 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
136 button
= button_get(true);
137 if (button
== SYS_USB_DISCONNECTED
)
142 /* Put drivers initialized for USB connection into a known state */
148 /* Sleep a little to let the backlight ramp up */
154 static void untar(int tar_fd
)
156 char header
[TAR_HEADER_SIZE
];
161 size_t size
= filesize(tar_fd
);
163 if (size
> tarbuf_size
)
165 printf("tar file too large"); /* Paranoid but proper */
169 ret
= read(tar_fd
, tarbuf
, filesize(tar_fd
));
172 printf("couldn't read tar file (%d)", ret
);
179 memcpy(header
, ptr
, TAR_HEADER_SIZE
);
181 if (*header
== '\0') /* Check for EOF */
184 /* Parse the size field */
186 for (i
= 124 ; i
< 124 + 11 ; i
++) {
187 size
= (8 * size
) + header
[i
] - '0';
190 /* Skip rest of header */
193 /* Make the path absolute */
195 strcat(path
, header
);
197 if (header
[156] == '0') /* file */
201 fd
= creat(path
, 0666);
204 printf("failed to create file (%d)", fd
);
208 wc
= write(fd
, ptr
, size
);
211 printf("write failed (%d)", wc
);
216 ptr
+= (size
+ TAR_CHUNK
-1) & (~(TAR_CHUNK
-1));
218 else if (header
[156] == '5') /* directory */
222 /* Remove the trailing slash */
223 if (path
[strlen(path
) - 1] == '/')
224 path
[strlen(path
) - 1] = '\0';
228 if (ret
< 0 && ret
!= -4)
230 printf("failed to create dir (%d)", ret
);
236 /* Look for a tar file or rockbox binary in the MTP directory */
237 static void handle_untar(void)
242 struct dirent_uncached
* entry
;
247 dir
= opendir_uncached(basedir
);
249 while ((entry
= readdir_uncached(dir
)))
251 if (*entry
->d_name
== '.')
254 snprintf(buf
, sizeof(buf
), "%s%s", basedir
, entry
->d_name
);
255 fd
= open(buf
, O_RDONLY
);
260 /* Check whether the file is a rockbox binary. */
261 lseek(fd
, 4, SEEK_SET
);
262 rc
= read(fd
, model
, 4);
266 if (strcmp(model
, "gigs") == 0)
268 printf("Found rockbox binary. Moving...");
270 remove( BOOTDIR
"/" BOOTFILE
);
271 int ret
= rename(buf
, BOOTDIR
"/" BOOTFILE
);
272 printf("returned %d", ret
);
278 /* Check whether the file is a tar file. */
279 lseek(fd
, 257, SEEK_SET
);
280 rc
= read(fd
, tarstring
, 5);
284 if (strcmp(tarstring
, "ustar") == 0)
286 printf("Found tar file. Unarchiving...");
287 lseek(fd
, 0, SEEK_SET
);
290 printf("Removing tar file");
300 /* Try to load the firmware and run it */
301 static void NORETURN_ATTR
handle_firmware_load(void)
303 int rc
= load_firmware(load_buf
, BOOTFILE
,
307 error(EBOOTFILE
, rc
, true);
309 /* Pause to look at messages */
310 pause_if_button_pressed(false);
312 /* Put drivers into a known state */
313 button_close_device();
315 system_prepare_fw_start();
319 cpucache_invalidate();
320 asm volatile ("bx %0": : "r"(start_addr
));
328 static void check_battery(void)
330 int batt
= battery_adc_voltage();
331 printf("Battery: %d.%03d V", batt
/ 1000, batt
% 1000);
332 /* TODO: warn on low battery or shut down */
339 /* Flush and invalidate all caches (because vectors were written) */
340 cpucache_invalidate();
345 enable_interrupt(IRQ_FIQ_STATUS
);
350 printf("Gigabeat S Rockbox Bootloader");
351 printf("Version " RBVERSION
);
353 /* Initialize KPP so we can poll the button states */
354 button_init_device();
364 error(EATA
, rc
, true);
369 rc
= disk_mount_all();
372 error(EDISK
, rc
, true);
375 printf("Init complete");
377 /* Do USB first since a tar or binary could be added to the MTP directory
378 * at the time and we can untar or move after unplugging. */
381 handle_firmware_load(); /* No return */