| /*************************************************************************** |
| * __________ __ ___. |
| * 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; |
| } |