blob: 499d9c218c7ac9e8e49662ce5b507cfd23c55fcb [file] [log] [blame]
Will Robertson590501c2007-09-21 15:51:53 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Greg White
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Will Robertson590501c2007-09-21 15:51:53 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
Will Robertson590501c2007-09-21 15:51:53 +000022#include "system.h"
Frank Gevaertsab4a1622010-05-06 22:13:54 +000023#include <stdio.h>
Michael Sevakis7d1a47c2013-08-05 22:02:45 -040024#include <errno.h>
Michael Sevakis4ea4cdf2014-08-08 02:28:11 -040025#include "../kernel-internal.h"
Thomas Martitz0e2286f2010-08-12 13:38:25 +000026#include "gcc_extensions.h"
Michael Sevakisc41caca2008-05-07 07:14:39 +000027#include "string.h"
Michael Sevakis80278e42008-05-10 18:00:11 +000028#include "adc.h"
29#include "powermgmt.h"
Frank Gevaerts2f8a0082008-11-01 16:14:28 +000030#include "storage.h"
Michael Sevakis7d1a47c2013-08-05 22:02:45 -040031#include "file_internal.h"
Nicolas Pennequinc2ca8c72007-11-27 15:40:29 +000032#include "dir.h"
Michael Sevakis7d1a47c2013-08-05 22:02:45 -040033#include "file.h"
Will Robertson590501c2007-09-21 15:51:53 +000034#include "disk.h"
Will Robertson590501c2007-09-21 15:51:53 +000035#include "common.h"
Marcin Bukat0b296912012-03-04 15:34:29 +010036#include "rb-loader.h"
37#include "loader_strerror.h"
Michael Sevakis863c03f2011-01-21 07:05:51 +000038#include "power.h"
Michael Sevakis80278e42008-05-10 18:00:11 +000039#include "backlight.h"
Will Robertson590501c2007-09-21 15:51:53 +000040#include "usb.h"
Michael Sevakis80278e42008-05-10 18:00:11 +000041#include "button.h"
Michael Sevakisc41caca2008-05-07 07:14:39 +000042#include "font.h"
43#include "lcd.h"
Rafaël Carré5d236b22010-05-27 09:41:46 +000044#include "version.h"
Will Robertson590501c2007-09-21 15:51:53 +000045
Michael Sevakis863c03f2011-01-21 07:05:51 +000046/* Show the Rockbox logo - in show_logo.c */
Bertrik Sikken7e3a3f42011-09-08 18:31:15 +000047extern void show_logo(void);
Michael Sevakis863c03f2011-01-21 07:05:51 +000048
Nicolas Pennequine2f5f212008-02-17 23:17:08 +000049#define TAR_CHUNK 512
50#define TAR_HEADER_SIZE 157
51
Michael Sevakisc41caca2008-05-07 07:14:39 +000052/* Where files sent via MTP are stored */
53static const char basedir[] = "/Content/0b00/00/";
54/* Can use memory after vector table up to 0x01f00000 */
55static char * const tarbuf = (char *)0x00000040;
56static const size_t tarbuf_size = 0x01f00000 - 0x00000040;
Michael Sevakis80278e42008-05-10 18:00:11 +000057/* Firmware data */
58static void * const load_buf = 0x00000000;
59static const size_t load_buf_size = 0x20000000 - 0x100000;
60static const void * const start_addr = 0x00000000;
Michael Sevakisa07c0342008-02-08 02:20:05 +000061
Michael Sevakis863c03f2011-01-21 07:05:51 +000062/* Show a message + "Shutting down...", then power off the device */
63static void display_message_and_power_off(int timeout, const char *msg)
Michael Sevakis94f7d0f2008-04-18 16:42:50 +000064{
Michael Sevakis863c03f2011-01-21 07:05:51 +000065 verbose = true;
66 printf(msg);
67 printf("Shutting down...");
Michael Sevakis94f7d0f2008-04-18 16:42:50 +000068 sleep(timeout);
Michael Sevakis863c03f2011-01-21 07:05:51 +000069 power_off();
Michael Sevakis94f7d0f2008-04-18 16:42:50 +000070}
71
Michael Sevakis863c03f2011-01-21 07:05:51 +000072static void check_battery_safe(void)
Michael Sevakis80278e42008-05-10 18:00:11 +000073{
Michael Sevakis863c03f2011-01-21 07:05:51 +000074 if (battery_level_safe())
75 return;
Michael Sevakis80278e42008-05-10 18:00:11 +000076
Michael Sevakis863c03f2011-01-21 07:05:51 +000077 display_message_and_power_off(HZ, "Battery low");
Michael Sevakis80278e42008-05-10 18:00:11 +000078}
79
80/* TODO: Handle charging while connected */
Michael Sevakis863c03f2011-01-21 07:05:51 +000081static void handle_usb(int connect_timeout)
Michael Sevakis80278e42008-05-10 18:00:11 +000082{
Michael Sevakis863c03f2011-01-21 07:05:51 +000083 long end_tick = 0;
Michael Sevakis80278e42008-05-10 18:00:11 +000084 int button;
85
Michael Sevakis80278e42008-05-10 18:00:11 +000086 /* We need full button and backlight handling now */
87 backlight_init();
88 button_init();
Michael Sevakis863c03f2011-01-21 07:05:51 +000089 backlight_on();
Michael Sevakis80278e42008-05-10 18:00:11 +000090
91 /* Start the USB driver */
92 usb_init();
93 usb_start_monitoring();
94
95 /* Wait for threads to connect or cable is pulled */
Michael Sevakis863c03f2011-01-21 07:05:51 +000096 printf("USB: Connecting");
97
98 if (connect_timeout != TIMEOUT_BLOCK)
99 end_tick = current_tick + connect_timeout;
Michael Sevakis80278e42008-05-10 18:00:11 +0000100
101 while (1)
102 {
103 button = button_get_w_tmo(HZ/2);
104
105 if (button == SYS_USB_CONNECTED)
106 break; /* Hit */
107
Michael Sevakis863c03f2011-01-21 07:05:51 +0000108 if (connect_timeout != TIMEOUT_BLOCK &&
109 TIME_AFTER(current_tick, end_tick))
110 {
111 /* Timed out waiting for the connect - will happen when connected
112 * to a charger through the USB port */
113 printf("USB: Timed out");
114 break;
115 }
116
Michael Sevakis36bc6a12012-01-01 03:19:52 +0000117 if (usb_plugged() == USB_EXTRACTED)
Michael Sevakis80278e42008-05-10 18:00:11 +0000118 break; /* Cable pulled */
119 }
120
121 if (button == SYS_USB_CONNECTED)
122 {
Michael Sevakis863c03f2011-01-21 07:05:51 +0000123 /* Switch to verbose mode if not in it so that the status updates
124 * are shown */
125 verbose = true;
Michael Sevakis80278e42008-05-10 18:00:11 +0000126 /* Got the message - wait for disconnect */
Michael Sevakis863c03f2011-01-21 07:05:51 +0000127 printf("Bootloader USB mode");
Michael Sevakis80278e42008-05-10 18:00:11 +0000128
129 usb_acknowledge(SYS_USB_CONNECTED_ACK);
130
131 while (1)
132 {
Michael Sevakis863c03f2011-01-21 07:05:51 +0000133 button = button_get_w_tmo(HZ/2);
Michael Sevakis80278e42008-05-10 18:00:11 +0000134 if (button == SYS_USB_DISCONNECTED)
Michael Sevakis80278e42008-05-10 18:00:11 +0000135 break;
Michael Sevakis863c03f2011-01-21 07:05:51 +0000136
137 check_battery_safe();
Michael Sevakis80278e42008-05-10 18:00:11 +0000138 }
Michael Sevakis863c03f2011-01-21 07:05:51 +0000139
140 backlight_on();
141 /* Sleep a little to let the backlight ramp up */
142 sleep(HZ*5/4);
Michael Sevakis80278e42008-05-10 18:00:11 +0000143 }
144
145 /* Put drivers initialized for USB connection into a known state */
Michael Sevakis80278e42008-05-10 18:00:11 +0000146 usb_close();
147 button_close();
148 backlight_close();
Michael Sevakis80278e42008-05-10 18:00:11 +0000149}
150
Michael Sevakisc41caca2008-05-07 07:14:39 +0000151static void untar(int tar_fd)
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000152{
153 char header[TAR_HEADER_SIZE];
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000154 char *ptr;
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000155 char path[102];
Michael Sevakisc41caca2008-05-07 07:14:39 +0000156 int fd, i;
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000157 int ret;
Michael Sevakisc41caca2008-05-07 07:14:39 +0000158 size_t size = filesize(tar_fd);
159
Michael Sevakis80278e42008-05-10 18:00:11 +0000160 if (size > tarbuf_size)
161 {
Michael Sevakisc41caca2008-05-07 07:14:39 +0000162 printf("tar file too large"); /* Paranoid but proper */
163 return;
164 }
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000165
166 ret = read(tar_fd, tarbuf, filesize(tar_fd));
Michael Sevakis80278e42008-05-10 18:00:11 +0000167 if (ret < 0)
168 {
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000169 printf("couldn't read tar file (%d)", ret);
170 return;
171 }
172 ptr = tarbuf;
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000173
174 while (1)
175 {
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000176 memcpy(header, ptr, TAR_HEADER_SIZE);
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000177
178 if (*header == '\0') /* Check for EOF */
179 break;
180
181 /* Parse the size field */
182 size = 0;
183 for (i = 124 ; i < 124 + 11 ; i++) {
184 size = (8 * size) + header[i] - '0';
185 }
186
187 /* Skip rest of header */
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000188 ptr += TAR_CHUNK;
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000189
190 /* Make the path absolute */
191 strcpy(path, "/");
192 strcat(path, header);
193
194 if (header[156] == '0') /* file */
195 {
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000196 int wc;
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000197
Thomas Martitzc61e89c2010-05-06 17:35:04 +0000198 fd = creat(path, 0666);
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000199 if (fd < 0)
200 {
201 printf("failed to create file (%d)", fd);
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000202 }
203 else
204 {
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000205 wc = write(fd, ptr, size);
206 if (wc < 0)
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000207 {
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000208 printf("write failed (%d)", wc);
209 break;
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000210 }
211 close(fd);
212 }
Nicolas Pennequin90a3d582008-04-16 00:38:06 +0000213 ptr += (size + TAR_CHUNK-1) & (~(TAR_CHUNK-1));
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000214 }
215 else if (header[156] == '5') /* directory */
216 {
217 int ret;
218
219 /* Remove the trailing slash */
220 if (path[strlen(path) - 1] == '/')
221 path[strlen(path) - 1] = '\0';
222
223 /* Create the dir */
224 ret = mkdir(path);
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400225 if (ret < 0 && errno != EEXIST)
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000226 {
227 printf("failed to create dir (%d)", ret);
228 }
229 }
230 }
231}
232
Michael Sevakis80278e42008-05-10 18:00:11 +0000233/* Look for a tar file or rockbox binary in the MTP directory */
234static void handle_untar(void)
Will Robertson590501c2007-09-21 15:51:53 +0000235{
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000236 char buf[MAX_PATH];
237 char tarstring[6];
Nicolas Pennequin620e6b42008-04-15 23:17:03 +0000238 char model[5];
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400239 struct dirent* entry;
240 DIR* dir;
Nicolas Pennequinc2ca8c72007-11-27 15:40:29 +0000241 int fd;
Michael Sevakis80278e42008-05-10 18:00:11 +0000242 int rc;
243
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400244 dir = opendir(basedir);
Michael Sevakis80278e42008-05-10 18:00:11 +0000245
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400246 while ((entry = readdir(dir)))
Nicolas Pennequinc2ca8c72007-11-27 15:40:29 +0000247 {
Michael Sevakis80278e42008-05-10 18:00:11 +0000248 if (*entry->d_name == '.')
249 continue;
Nicolas Pennequin620e6b42008-04-15 23:17:03 +0000250
Michael Sevakis80278e42008-05-10 18:00:11 +0000251 snprintf(buf, sizeof(buf), "%s%s", basedir, entry->d_name);
252 fd = open(buf, O_RDONLY);
253
254 if (fd < 0)
255 continue;
256
257 /* Check whether the file is a rockbox binary. */
258 lseek(fd, 4, SEEK_SET);
259 rc = read(fd, model, 4);
260 if (rc == 4)
261 {
262 model[4] = 0;
263 if (strcmp(model, "gigs") == 0)
264 {
Michael Sevakis863c03f2011-01-21 07:05:51 +0000265 verbose = true;
Michael Sevakis80278e42008-05-10 18:00:11 +0000266 printf("Found rockbox binary. Moving...");
Nicolas Pennequine2f5f212008-02-17 23:17:08 +0000267 close(fd);
Björn Stenbergb69be102008-11-23 22:07:48 +0000268 int ret = rename(buf, BOOTDIR "/" BOOTFILE);
Michael Sevakis80278e42008-05-10 18:00:11 +0000269 printf("returned %d", ret);
270 sleep(HZ);
271 break;
Nicolas Pennequinc2ca8c72007-11-27 15:40:29 +0000272 }
273 }
Will Robertson590501c2007-09-21 15:51:53 +0000274
Michael Sevakis80278e42008-05-10 18:00:11 +0000275 /* Check whether the file is a tar file. */
276 lseek(fd, 257, SEEK_SET);
277 rc = read(fd, tarstring, 5);
278 if (rc == 5)
Michael Sevakis94f7d0f2008-04-18 16:42:50 +0000279 {
Michael Sevakis80278e42008-05-10 18:00:11 +0000280 tarstring[5] = 0;
281 if (strcmp(tarstring, "ustar") == 0)
282 {
Michael Sevakis863c03f2011-01-21 07:05:51 +0000283 verbose = true;
Michael Sevakis80278e42008-05-10 18:00:11 +0000284 printf("Found tar file. Unarchiving...");
285 lseek(fd, 0, SEEK_SET);
286 untar(fd);
287 close(fd);
288 printf("Removing tar file");
289 remove(buf);
290 break;
291 }
Michael Sevakis94f7d0f2008-04-18 16:42:50 +0000292 }
293
Michael Sevakis80278e42008-05-10 18:00:11 +0000294 close(fd);
Michael Sevakis94f7d0f2008-04-18 16:42:50 +0000295 }
Michael Sevakis80278e42008-05-10 18:00:11 +0000296}
Michael Sevakis94f7d0f2008-04-18 16:42:50 +0000297
Michael Sevakis80278e42008-05-10 18:00:11 +0000298/* Try to load the firmware and run it */
Thomas Martitz0e2286f2010-08-12 13:38:25 +0000299static void NORETURN_ATTR handle_firmware_load(void)
Michael Sevakis80278e42008-05-10 18:00:11 +0000300{
Michael Sevakis863c03f2011-01-21 07:05:51 +0000301 int rc = load_firmware(load_buf, BOOTFILE, load_buf_size);
Will Robertson590501c2007-09-21 15:51:53 +0000302
Marcin Bukat0b296912012-03-04 15:34:29 +0100303 if(rc <= EFILE_EMPTY)
Rafaël Carré1ec82122010-06-23 05:08:36 +0000304 error(EBOOTFILE, rc, true);
Michael Sevakisa07c0342008-02-08 02:20:05 +0000305
Michael Sevakis80278e42008-05-10 18:00:11 +0000306 /* Pause to look at messages */
Michael Sevakis863c03f2011-01-21 07:05:51 +0000307 while (1)
308 {
309 int button = button_read_device();
310
311 /* Ignore settings reset */
312 if (button == BUTTON_NONE || button == BUTTON_MENU)
313 break;
314
315 sleep(HZ/5);
316
317 check_battery_safe();
318
319 /* If the disk powers off, the firmware will lock at startup */
320 storage_spin();
321 }
Michael Sevakis80278e42008-05-10 18:00:11 +0000322
323 /* Put drivers into a known state */
324 button_close_device();
Frank Gevaerts2f8a0082008-11-01 16:14:28 +0000325 storage_close();
Michael Sevakisa07c0342008-02-08 02:20:05 +0000326 system_prepare_fw_start();
Will Robertson590501c2007-09-21 15:51:53 +0000327
Marcin Bukat0b296912012-03-04 15:34:29 +0100328 commit_discard_idcache();
329 asm volatile ("bx %0": : "r"(start_addr));
Nicolas Pennequinc2ca8c72007-11-27 15:40:29 +0000330
Michael Sevakis94f7d0f2008-04-18 16:42:50 +0000331 /* Halt */
332 while (1)
333 core_idle();
Will Robertson590501c2007-09-21 15:51:53 +0000334}
335
Michael Sevakis80278e42008-05-10 18:00:11 +0000336void main(void)
337{
338 int rc;
Michael Sevakis863c03f2011-01-21 07:05:51 +0000339 int batt;
Michael Sevakis80278e42008-05-10 18:00:11 +0000340
Michael Sevakis80278e42008-05-10 18:00:11 +0000341 system_init();
342 kernel_init();
343
Michael Sevakis863c03f2011-01-21 07:05:51 +0000344 /* Keep button_device_init early to delay calls to button_read_device */
345 button_init_device();
346
347 lcd_init();
348 font_init();
349 show_logo();
Michael Sevakis4d3a0202009-02-07 10:09:13 +0000350 lcd_clear_display();
351
Michael Sevakis863c03f2011-01-21 07:05:51 +0000352 if (button_hold())
353 display_message_and_power_off(HZ, "Hold switch on");
354
355 if (button_read_device() != BUTTON_NONE)
356 verbose = true;
357
Michael Sevakis4d3a0202009-02-07 10:09:13 +0000358 printf("Gigabeat S Rockbox Bootloader");
Michael Sevakis95a4c3a2014-08-28 10:26:45 -0400359 printf("Version %s", rbversion);
Michael Sevakis4d3a0202009-02-07 10:09:13 +0000360
Michael Sevakis80278e42008-05-10 18:00:11 +0000361 adc_init();
Thomas Martitzc1bd9b02012-01-03 23:44:38 +0000362 batt = _battery_voltage();
Michael Sevakis863c03f2011-01-21 07:05:51 +0000363 printf("Battery: %d.%03d V", batt / 1000, batt % 1000);
364 check_battery_safe();
Michael Sevakis80278e42008-05-10 18:00:11 +0000365
Frank Gevaerts2f8a0082008-11-01 16:14:28 +0000366 rc = storage_init();
Michael Sevakis80278e42008-05-10 18:00:11 +0000367 if(rc)
Rafaël Carré1ec82122010-06-23 05:08:36 +0000368 error(EATA, rc, true);
Michael Sevakis80278e42008-05-10 18:00:11 +0000369
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400370 filesystem_init();
Michael Sevakis80278e42008-05-10 18:00:11 +0000371
372 rc = disk_mount_all();
Michael Sevakis863c03f2011-01-21 07:05:51 +0000373 if (rc <= 0)
Rafaël Carré1ec82122010-06-23 05:08:36 +0000374 error(EDISK, rc, true);
Michael Sevakis80278e42008-05-10 18:00:11 +0000375
376 printf("Init complete");
377
378 /* Do USB first since a tar or binary could be added to the MTP directory
379 * at the time and we can untar or move after unplugging. */
Michael Sevakis36bc6a12012-01-01 03:19:52 +0000380 if (usb_plugged() == USB_INSERTED)
Michael Sevakis863c03f2011-01-21 07:05:51 +0000381 handle_usb(HZ*2);
382
Michael Sevakis80278e42008-05-10 18:00:11 +0000383 handle_untar();
384 handle_firmware_load(); /* No return */
385}