mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-13 10:07:38 -04:00
The new code gets closer to an actual bootloader: - it provides a menu with three options: Sony, Rockox, tools with icons (and extracts the Sony logo from the NVP) - the dualboot install script now creates a symlink from /.rockbox to /contents/.rockox which is necessary to run rockbox - more text drawing / framebuffer functions In the long run, we will move this under bootloader/ and rbutil/ and also use firmware/ drawing facilities, at the moment we use OF display program which is slow and creates some flickering. The logo extraction/placement code was tested with resolution 240x320 and I guessed some reasonable values for 240x400, but those will probably need some tweaking. Change-Id: I0319be902d21a7d33c1dee0fffdb4797065dbf8a
302 lines
9.8 KiB
C
302 lines
9.8 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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;
|
|
}
|