blob: c6d07c7508e652b4856f6a9e175a42e7c48e3426 [file] [log] [blame]
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2011 by Amaury Pouly
*
* Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
* and the ipodlinux bootloader by Daniel Palffy and Bernard Leach
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "nwz_lib.h"
#include "nwz_plattools.h"
#include <time.h>
#include <errno.h>
/* all images must have the following size */
#define ICON_WIDTH 130
#define ICON_HEIGHT 130
/* images */
#include "data/rockbox_icon.h"
#if BMPWIDTH_rockbox_icon != ICON_WIDTH || BMPHEIGHT_rockbox_icon != ICON_HEIGHT
#error rockbox_icon has the wrong resolution
#endif
#include "data/tools_icon.h"
#if BMPWIDTH_tools_icon != ICON_WIDTH || BMPHEIGHT_tools_icon != ICON_HEIGHT
#error tools_icon has the wrong resolution
#endif
/* buffer for Sony image, filled from NVP */
unsigned short sony_icon[ICON_WIDTH * ICON_HEIGHT];
/* resolution */
static int width, height, bpp;
/* return icon y position (x is always centered) */
int get_icon_y(void)
{
/* adjust so that this contains the Sony logo and produces a nice logo
* when used with rockbox */
if(height == 320)
return 70;
else if(height == 320)
return 100;
else
return height / 2 - ICON_HEIGHT + 30; /* guess, probably won't work */
}
/* Sony logo extraction */
bool extract_sony_logo(void)
{
/* only support bpp of 16 */
if(bpp != 16)
return false;
/* load the entire image from the nvp */
int bti_size = nwz_nvp_read(NWZ_NVP_BTI, NULL);
if(bti_size < 0)
return false;
unsigned short *bti = malloc(bti_size);
if(nwz_nvp_read(NWZ_NVP_BTI, bti) != bti_size)
return false;
/* compute the offset in the image of the logo itself */
int x_off = (width - ICON_WIDTH) / 2; /* logo is centered horizontally */
int y_off = get_icon_y();
/* extract part of the image */
for(int y = 0; y < ICON_HEIGHT; y++)
{
memcpy(sony_icon + ICON_WIDTH * y,
bti + width * (y + y_off) + x_off, ICON_WIDTH * sizeof(unsigned short));
}
free(bti);
return true;
}
/* Important Note: this bootloader is carefully written so that in case of
* error, the OF is run. This seems like the safest option since the OF is
* always there and might do magic things. */
enum boot_mode
{
BOOT_ROCKBOX,
BOOT_TOOLS,
BOOT_OF
};
void draw_icon(int left, int top, const unsigned short *icon, unsigned short *fb_mmap)
{
for(int y = 0; y < ICON_HEIGHT; y++)
{
memcpy(fb_mmap + width * (y + top) + left, icon + ICON_WIDTH * y,
ICON_WIDTH * sizeof(unsigned short));
}
}
enum boot_mode get_boot_mode(void)
{
if(bpp != 16)
{
nwz_lcdmsg(true, 0, 2, "Unsupported bpp");
sleep(2);
return BOOT_OF;
}
/* open framebuffer */
int fb_fd = nwz_fb_open(true);
if(fb_fd < 0)
{
nwz_lcdmsg(true, 0, 2, "Cannot open input device");
sleep(2);
return BOOT_OF;
}
/* open input device */
int input_fd = nwz_key_open();
if(input_fd < 0)
{
nwz_fb_close(fb_fd);
nwz_lcdmsg(true, 0, 2, "Cannot open input device");
sleep(2);
return BOOT_OF;
}
int fb_size = width * height * bpp / 2;
void *fb_mmap = nwz_fb_mmap(fb_fd, 0, fb_size);
void *fb_mmap_p1 = nwz_fb_mmap(fb_fd, NWZ_FB_LCD_PAGE_OFFSET, fb_size);
if(fb_mmap == NULL || fb_mmap_p1 == NULL)
{
nwz_fb_close(fb_fd);
nwz_key_close(input_fd);
nwz_lcdmsg(true, 0, 2, "Cannot map framebuffer");
sleep(2);
return BOOT_OF;
}
/* wait for user action */
enum boot_mode mode = BOOT_OF;
/* NOTE on drawing: since screen is redrawn automatically, and we invoke
* external programs to draw, we can't hope to fit it in the frame time
* and it will flicker. To avoid this, we use the fact that all programs
* only write to page 0. So we setup the lcd to update from page 1. When
* we need to update the screen, we ask it to draw from page 0, then copy
* page 0 to page 1 and then switch back to page 1 */
memset(fb_mmap_p1, 0xff, fb_size); /* clear page 1 */
nwz_fb_set_page(fb_fd, 1);
bool redraw = true;
while(true)
{
if(redraw)
{
/* redraw screen on page 0: clear screen */
memset(fb_mmap, 0, fb_size);
/* display top text */
nwz_display_text_center(width, 0, true, NWZ_COLOR(255, 201, 0),
NWZ_COLOR(0, 0, 0), 0, "SELECT PLAYER");
/* display icon */
const unsigned short *icon = (mode == BOOT_OF) ? sony_icon :
(mode == BOOT_ROCKBOX) ? rockbox_icon : tools_icon;
draw_icon((width - ICON_WIDTH) / 2, get_icon_y(), icon, fb_mmap);
/* display bottom description */
const char *desc = (mode == BOOT_OF) ? "SONY" :
(mode == BOOT_ROCKBOX) ? "ROCKBOX" : "DEBUG TOOLS";
nwz_display_text_center(width, get_icon_y() + ICON_HEIGHT + 30, true,
NWZ_COLOR(255, 201, 0), NWZ_COLOR(0, 0, 0), 0, desc);
/* display arrows */
int arrow_y = get_icon_y() + ICON_HEIGHT / 2 - NWZ_FONT_H(true) / 2;
nwz_display_text(NWZ_FONT_W(true) / 2, arrow_y, true,
NWZ_COLOR(255, 201, 0), NWZ_COLOR(0, 0, 0), 0, "<");
nwz_display_text(width - 3 * NWZ_FONT_W(true) / 2, arrow_y, true,
NWZ_COLOR(255, 201, 0), NWZ_COLOR(0, 0, 0), 0, ">");
/* switch to page 1 */
nwz_fb_set_page(fb_fd, 0);
/* copy page 0 to page 1 */
memcpy(fb_mmap_p1, fb_mmap, fb_size);
/* switch back to page 1 */
nwz_fb_set_page(fb_fd, 1);
redraw = false;
}
/* wait for a key */
int ret = nwz_key_wait_event(input_fd, -1);
if(ret != 1)
continue;
struct input_event evt;
if(nwz_key_read_event(input_fd, &evt) != 1)
continue;
/* only act on release */
if(nwz_key_event_is_press(&evt))
continue;
int key_code = nwz_key_event_get_keycode(&evt);
/* play -> stop loop and return mode */
if(key_code == NWZ_KEY_PLAY)
break;
/* left/right/up/down: change mode */
if(key_code == NWZ_KEY_LEFT || key_code == NWZ_KEY_DOWN)
{
if(mode == BOOT_ROCKBOX)
mode = BOOT_OF;
else if(mode == BOOT_OF)
mode = BOOT_TOOLS;
else
mode = BOOT_ROCKBOX;
redraw = true;
}
if(key_code == NWZ_KEY_RIGHT || key_code == NWZ_KEY_UP)
{
if(mode == BOOT_ROCKBOX)
mode = BOOT_TOOLS;
else if(mode == BOOT_OF)
mode = BOOT_ROCKBOX;
else
mode = BOOT_OF;
redraw = true;
}
}
/* switch back to page 0 */
nwz_fb_set_page(fb_fd, 0);
nwz_key_close(input_fd);
nwz_fb_close(fb_fd);
return mode;
}
static char *boot_rb_argv[] =
{
"rockbox.sony",
NULL
};
int NWZ_TOOL_MAIN(all_tools)(int argc, char **argv);
void error_screen(const char *msg)
{
nwz_lcdmsg(true, 0, 0, msg);
sleep(3);
}
void create_sony_logo(void)
{
for(int y = 0; y < ICON_HEIGHT; y++)
for(int x = 0; x < ICON_WIDTH; x++)
sony_icon[y * ICON_WIDTH + x] = 0xf81f;
}
int main(int argc, char **argv)
{
/* make sure backlight is on and we are running the standard lcd mode */
int fb_fd = nwz_fb_open(true);
if(fb_fd >= 0)
{
struct nwz_fb_brightness bl;
nwz_fb_get_brightness(fb_fd, &bl);
bl.level = NWZ_FB_BL_MAX_LEVEL;
nwz_fb_set_brightness(fb_fd, &bl);
nwz_fb_set_standard_mode(fb_fd);
/* get resolution */
/* we also need to get the native resolution */
if(nwz_fb_get_resolution(fb_fd, &width, &height, &bpp) != 0)
{
/* safe one */
width = 240;
height = 320;
bpp = 16;
}
nwz_fb_close(fb_fd);
}
/* extract logo */
if(!extract_sony_logo())
create_sony_logo();
/* run all tools menu */
enum boot_mode mode = get_boot_mode();
if(mode == BOOT_TOOLS)
{
/* run tools and then run OF */
NWZ_TOOL_MAIN(all_tools)(argc, argv);
}
else if(mode == BOOT_ROCKBOX)
{
/* Rockbox expects /.rockbox to contain themes, rocks, etc, but we
* cannot easily create this symlink because the root filesystem is
* mounted read-only. Although we could remount it read-write temporarily,
* this is neededlessly complicated and we defer this job to the dualboot
* install script */
execvp("/contents/.rockbox/rockbox.sony", boot_rb_argv);
/* fallback to OF in case of failure */
error_screen("Cannot boot Rockbox");
sleep(5);
}
/* boot OF */
execvp("/usr/local/bin/SpiderApp.of", argv);
error_screen("Cannot boot OF");
sleep(5);
/* if we reach this point, everything failed, so return an error so that
* sysmgrd knows something is wrong */
return 1;
}