GSoC/Buflib: Enable compaction in buflib.
[kugel-rb.git] / bootloader / gigabeat-s.c
blob2500496fd43f088a09976d9e189b2f8b3fe85dca
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
21 #include "config.h"
22 #include "system.h"
23 #include <stdio.h>
24 #include "kernel.h"
25 #include "gcc_extensions.h"
26 #include "string.h"
27 #include "adc.h"
28 #include "powermgmt.h"
29 #include "storage.h"
30 #include "dir.h"
31 #include "disk.h"
32 #include "common.h"
33 #include "power.h"
34 #include "backlight.h"
35 #include "usb.h"
36 #include "button.h"
37 #include "font.h"
38 #include "lcd.h"
39 #include "usb-target.h"
40 #include "version.h"
42 /* Show the Rockbox logo - in show_logo.c */
43 extern int show_logo(void);
45 #define TAR_CHUNK 512
46 #define TAR_HEADER_SIZE 157
48 /* Where files sent via MTP are stored */
49 static const char basedir[] = "/Content/0b00/00/";
50 /* Can use memory after vector table up to 0x01f00000 */
51 static char * const tarbuf = (char *)0x00000040;
52 static const size_t tarbuf_size = 0x01f00000 - 0x00000040;
53 /* Firmware data */
54 static void * const load_buf = 0x00000000;
55 static const size_t load_buf_size = 0x20000000 - 0x100000;
56 static const void * const start_addr = 0x00000000;
58 /* Show a message + "Shutting down...", then power off the device */
59 static void display_message_and_power_off(int timeout, const char *msg)
61 verbose = true;
62 printf(msg);
63 printf("Shutting down...");
64 sleep(timeout);
65 power_off();
68 static void check_battery_safe(void)
70 if (battery_level_safe())
71 return;
73 display_message_and_power_off(HZ, "Battery low");
76 /* TODO: Handle charging while connected */
77 static void handle_usb(int connect_timeout)
79 long end_tick = 0;
80 int button;
82 /* We need full button and backlight handling now */
83 backlight_init();
84 button_init();
85 backlight_on();
87 /* Start the USB driver */
88 usb_init();
89 usb_start_monitoring();
91 /* Wait for threads to connect or cable is pulled */
92 printf("USB: Connecting");
94 if (connect_timeout != TIMEOUT_BLOCK)
95 end_tick = current_tick + connect_timeout;
97 while (1)
99 button = button_get_w_tmo(HZ/2);
101 if (button == SYS_USB_CONNECTED)
102 break; /* Hit */
104 if (connect_timeout != TIMEOUT_BLOCK &&
105 TIME_AFTER(current_tick, end_tick))
107 /* Timed out waiting for the connect - will happen when connected
108 * to a charger through the USB port */
109 printf("USB: Timed out");
110 break;
113 if (!usb_plugged())
114 break; /* Cable pulled */
117 if (button == SYS_USB_CONNECTED)
119 /* Switch to verbose mode if not in it so that the status updates
120 * are shown */
121 verbose = true;
122 /* Got the message - wait for disconnect */
123 printf("Bootloader USB mode");
125 usb_acknowledge(SYS_USB_CONNECTED_ACK);
127 while (1)
129 button = button_get_w_tmo(HZ/2);
130 if (button == SYS_USB_DISCONNECTED)
131 break;
133 check_battery_safe();
136 backlight_on();
137 /* Sleep a little to let the backlight ramp up */
138 sleep(HZ*5/4);
141 /* Put drivers initialized for USB connection into a known state */
142 usb_close();
143 button_close();
144 backlight_close();
147 static void untar(int tar_fd)
149 char header[TAR_HEADER_SIZE];
150 char *ptr;
151 char path[102];
152 int fd, i;
153 int ret;
154 size_t size = filesize(tar_fd);
156 if (size > tarbuf_size)
158 printf("tar file too large"); /* Paranoid but proper */
159 return;
162 ret = read(tar_fd, tarbuf, filesize(tar_fd));
163 if (ret < 0)
165 printf("couldn't read tar file (%d)", ret);
166 return;
168 ptr = tarbuf;
170 while (1)
172 memcpy(header, ptr, TAR_HEADER_SIZE);
174 if (*header == '\0') /* Check for EOF */
175 break;
177 /* Parse the size field */
178 size = 0;
179 for (i = 124 ; i < 124 + 11 ; i++) {
180 size = (8 * size) + header[i] - '0';
183 /* Skip rest of header */
184 ptr += TAR_CHUNK;
186 /* Make the path absolute */
187 strcpy(path, "/");
188 strcat(path, header);
190 if (header[156] == '0') /* file */
192 int wc;
194 fd = creat(path, 0666);
195 if (fd < 0)
197 printf("failed to create file (%d)", fd);
199 else
201 wc = write(fd, ptr, size);
202 if (wc < 0)
204 printf("write failed (%d)", wc);
205 break;
207 close(fd);
209 ptr += (size + TAR_CHUNK-1) & (~(TAR_CHUNK-1));
211 else if (header[156] == '5') /* directory */
213 int ret;
215 /* Remove the trailing slash */
216 if (path[strlen(path) - 1] == '/')
217 path[strlen(path) - 1] = '\0';
219 /* Create the dir */
220 ret = mkdir(path);
221 if (ret < 0 && ret != -4)
223 printf("failed to create dir (%d)", ret);
229 /* Look for a tar file or rockbox binary in the MTP directory */
230 static void handle_untar(void)
232 char buf[MAX_PATH];
233 char tarstring[6];
234 char model[5];
235 struct dirent_uncached* entry;
236 DIR_UNCACHED* dir;
237 int fd;
238 int rc;
240 dir = opendir_uncached(basedir);
242 while ((entry = readdir_uncached(dir)))
244 if (*entry->d_name == '.')
245 continue;
247 snprintf(buf, sizeof(buf), "%s%s", basedir, entry->d_name);
248 fd = open(buf, O_RDONLY);
250 if (fd < 0)
251 continue;
253 /* Check whether the file is a rockbox binary. */
254 lseek(fd, 4, SEEK_SET);
255 rc = read(fd, model, 4);
256 if (rc == 4)
258 model[4] = 0;
259 if (strcmp(model, "gigs") == 0)
261 verbose = true;
262 printf("Found rockbox binary. Moving...");
263 close(fd);
264 remove( BOOTDIR "/" BOOTFILE);
265 int ret = rename(buf, BOOTDIR "/" BOOTFILE);
266 printf("returned %d", ret);
267 sleep(HZ);
268 break;
272 /* Check whether the file is a tar file. */
273 lseek(fd, 257, SEEK_SET);
274 rc = read(fd, tarstring, 5);
275 if (rc == 5)
277 tarstring[5] = 0;
278 if (strcmp(tarstring, "ustar") == 0)
280 verbose = true;
281 printf("Found tar file. Unarchiving...");
282 lseek(fd, 0, SEEK_SET);
283 untar(fd);
284 close(fd);
285 printf("Removing tar file");
286 remove(buf);
287 break;
291 close(fd);
295 /* Try to load the firmware and run it */
296 static void NORETURN_ATTR handle_firmware_load(void)
298 int rc = load_firmware(load_buf, BOOTFILE, load_buf_size);
300 if(rc < 0)
301 error(EBOOTFILE, rc, true);
303 /* Pause to look at messages */
304 while (1)
306 int button = button_read_device();
308 /* Ignore settings reset */
309 if (button == BUTTON_NONE || button == BUTTON_MENU)
310 break;
312 sleep(HZ/5);
314 check_battery_safe();
316 /* If the disk powers off, the firmware will lock at startup */
317 storage_spin();
320 /* Put drivers into a known state */
321 button_close_device();
322 storage_close();
323 system_prepare_fw_start();
325 if (rc == EOK)
327 cpucache_commit_discard();
328 asm volatile ("bx %0": : "r"(start_addr));
331 /* Halt */
332 while (1)
333 core_idle();
336 void main(void)
338 int rc;
339 int batt;
341 system_init();
342 kernel_init();
344 /* Keep button_device_init early to delay calls to button_read_device */
345 button_init_device();
347 lcd_init();
348 font_init();
349 show_logo();
350 lcd_clear_display();
352 if (button_hold())
353 display_message_and_power_off(HZ, "Hold switch on");
355 if (button_read_device() != BUTTON_NONE)
356 verbose = true;
358 printf("Gigabeat S Rockbox Bootloader");
359 printf("Version " RBVERSION);
361 adc_init();
362 batt = battery_adc_voltage();
363 printf("Battery: %d.%03d V", batt / 1000, batt % 1000);
364 check_battery_safe();
366 rc = storage_init();
367 if(rc)
368 error(EATA, rc, true);
370 disk_init();
372 rc = disk_mount_all();
373 if (rc <= 0)
374 error(EDISK, rc, true);
376 printf("Init complete");
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. */
380 if (usb_plugged())
381 handle_usb(HZ*2);
383 handle_untar();
384 handle_firmware_load(); /* No return */