/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2005 by Dave Chapman * * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include #include #include #include "cpu.h" #include "system.h" #include "lcd.h" #include "kernel.h" #include "thread.h" #include "ata.h" #include "fat.h" #include "disk.h" #include "font.h" #include "adc.h" #include "backlight.h" #include "panic.h" #include "power.h" #include "file.h" #define XSC(X) #X #define SC(X) XSC(X) #if (CONFIG_CPU == PP5020) #define DRAM_START 0x10000000 #else #define IPOD_LCD_BASE 0xc0001000 #define DRAM_START 0x28000000 #endif #define IPOD_HW_REVISION (*((volatile unsigned long*)(0x00002084))) /* We copy the hardware revision to the last four bytes of SDRAM and then re-read it after we have re-mapped SDRAM to 0x0 in Rockbox */ #define TMP_IPOD_HW_REVISION (*((volatile unsigned long*)(0x11fffffc))) #define BUTTON_LEFT 1 #define BUTTON_MENU 2 #define BUTTON_RIGHT 3 #define BUTTON_PLAY 4 #define BUTTON_HOLD 5 /* Size of the buffer to store the loaded Rockbox/Linux image */ #define MAX_LOADSIZE (4*1024*1024) char version[] = APPSVERSION; int line=0; #if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI) /* check if number of seconds has past */ int timer_check(int clock_start, unsigned int usecs) { if ((USEC_TIMER - clock_start) >= usecs) { return 1; } else { return 0; } } static void ser_opto_keypad_cfg(int val) { int start_time; outl(inl(0x6000d004) & ~0x80, 0x6000d004); outl(inl(0x7000c104) | 0xc000000, 0x7000c104); outl(val, 0x7000c120); outl(inl(0x7000c100) | 0x80000000, 0x7000c100); outl(inl(0x6000d024) & ~0x10, 0x6000d024); outl(inl(0x6000d014) | 0x10, 0x6000d014); start_time = USEC_TIMER; do { if ((inl(0x7000c104) & 0x80000000) == 0) { break; } } while (timer_check(start_time, 1500) != 0); outl(inl(0x7000c100) & ~0x80000000, 0x7000c100); outl(inl(0x6000d004) | 0x80, 0x6000d004); outl(inl(0x6000d024) | 0x10, 0x6000d024); outl(inl(0x6000d014) & ~0x10, 0x6000d014); outl(inl(0x7000c104) | 0xc000000, 0x7000c104); outl(inl(0x7000c100) | 0x60000000, 0x7000c100); } int opto_keypad_read(void) { int loop_cnt, had_io = 0; for (loop_cnt = 5; loop_cnt != 0;) { int key_pressed = 0; int start_time; unsigned int key_pad_val; ser_opto_keypad_cfg(0x8000023a); start_time = USEC_TIMER; do { if (inl(0x7000c104) & 0x4000000) { had_io = 1; break; } if (had_io != 0) { break; } } while (timer_check(start_time, 1500) != 0); key_pad_val = inl(0x7000c140); if ((key_pad_val & ~0x7fff0000) != 0x8000023a) { loop_cnt--; } else { key_pad_val = (key_pad_val << 11) >> 27; key_pressed = 1; } outl(inl(0x7000c100) | 0x60000000, 0x7000c100); outl(inl(0x7000c104) | 0xc000000, 0x7000c104); if (key_pressed != 0) { return key_pad_val ^ 0x1f; } } return 0; } #endif static int key_pressed(void) { unsigned char state; #if CONFIG_KEYPAD == IPOD_4G_PAD #ifdef IPOD_MINI /* mini 1G only */ state = GPIOA_INPUT_VAL & 0x3f; if ((state & 0x10) == 0) return BUTTON_LEFT; if ((state & 0x2) == 0) return BUTTON_MENU; if ((state & 0x4) == 0) return BUTTON_PLAY; if ((state & 0x8) == 0) return BUTTON_RIGHT; #else state = opto_keypad_read(); if ((state & 0x4) == 0) return BUTTON_LEFT; if ((state & 0x10) == 0) return BUTTON_MENU; if ((state & 0x8) == 0) return BUTTON_PLAY; if ((state & 0x2) == 0) return BUTTON_RIGHT; #endif #elif CONFIG_KEYPAD == IPOD_3G_PAD state = inb(0xcf000030); if (((state & 0x20) == 0)) return BUTTON_HOLD; /* hold on */ if ((state & 0x08) == 0) return BUTTON_LEFT; if ((state & 0x10) == 0) return BUTTON_MENU; if ((state & 0x04) == 0) return BUTTON_PLAY; if ((state & 0x01) == 0) return BUTTON_RIGHT; #endif return 0; } int load_rockbox(unsigned char* buf) { int fd; int rc; int len; unsigned long chksum; char model[5]; unsigned long sum; int i; char str[80]; fd = open("/.rockbox/" BOOTFILE, O_RDONLY); if(fd < 0) { fd = open("/" BOOTFILE, O_RDONLY); if(fd < 0) return -1; } len = filesize(fd) - 8; if (len > MAX_LOADSIZE) return -6; lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET); rc = read(fd, &chksum, 4); chksum=betoh32(chksum); /* Rockbox checksums are big-endian */ if(rc < 4) return -2; rc = read(fd, model, 4); if(rc < 4) return -3; model[4] = 0; snprintf(str, 80, "Model: %s", model); lcd_puts(0, line++, str); snprintf(str, 80, "Checksum: %x", chksum); lcd_puts(0, line++, str); lcd_update(); lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET); rc = read(fd, buf, len); if(rc < len) return -4; close(fd); sum = MODEL_NUMBER; for(i = 0;i < len;i++) { sum += buf[i]; } snprintf(str, 80, "Sum: %x", sum); lcd_puts(0, line++, str); lcd_update(); if(sum != chksum) return -5; return len; } int load_linux(unsigned char* buf) { int fd; int rc; int len; char str[80]; fd=open("/linux.bin",O_RDONLY); if (fd < 0) return -1; len=filesize(fd); if (len > MAX_LOADSIZE) return -6; rc=read(fd,buf,len); if (rc < len) return -4; snprintf(str, 80, "Loaded Linux: %d bytes", len); lcd_puts(0, line++, str); lcd_update(); return len; } /* A buffer to load the Linux kernel or Rockbox into */ unsigned char loadbuffer[MAX_LOADSIZE]; void* main(void) { char buf[256]; int i; int rc; struct partinfo* pinfo; unsigned short* identify_info; /* Turn on the backlight */ #if CONFIG_BACKLIGHT==BL_IPOD4G /* brightness full */ outl(0x80000000 | (0xff << 16), 0x7000a010); /* set port B03 on */ outl(((0x100 | 1) << 3), 0x6000d824); #elif CONFIG_BACKLIGHT==BL_IPODMINI /* set port B03 on */ outl(((0x100 | 1) << 3), 0x6000d824); #elif CONFIG_BACKLIGHT==BL_IPODNANO /* set port B03 on */ outl(((0x100 | 1) << 3), 0x6000d824); /* set port L07 on */ outl(((0x100 | 1) << 7), 0x6000d12c); #elif CONFIG_BACKLIGHT==BL_IPOD3G outl(inl(IPOD_LCD_BASE) | 0x2, IPOD_LCD_BASE); #endif TMP_IPOD_HW_REVISION = IPOD_HW_REVISION; ipod_hw_rev = IPOD_HW_REVISION; system_init(); kernel_init(); lcd_init(); font_init(); #if 0 /* ADC and button drivers are not yet implemented */ adc_init(); button_init(); #endif line=0; lcd_setfont(FONT_SYSFIXED); lcd_puts(0, line++, "Rockbox boot loader"); snprintf(buf, sizeof(buf), "Version: 20%s", version); lcd_puts(0, line++, buf); snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION); lcd_puts(0, line++, buf); lcd_update(); i=ata_init(); if (i==0) { identify_info=ata_get_identify(); /* Show model */ for (i=0; i < 20; i++) { ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]); } buf[40]=0; for (i=39; i && buf[i]==' '; i--) { buf[i]=0; } lcd_puts(0, line++, buf); lcd_update(); } else { snprintf(buf, sizeof(buf), "ATA: %d", i); lcd_puts(0, line++, buf); lcd_update(); } disk_init(); rc = disk_mount_all(); if (rc<=0) { lcd_puts(0, line++, "No partition found"); lcd_update(); // while(button_get(true) != SYS_USB_CONNECTED) {}; } pinfo = disk_partinfo(1); snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB", pinfo->type, pinfo->size / 2048); lcd_puts(0, line++, buf); lcd_update(); /* Check for a keypress */ i=key_pressed(); if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) { lcd_puts(0, line, "Loading Rockbox..."); lcd_update(); rc=load_rockbox(loadbuffer); if (rc < 0) { snprintf(buf, sizeof(buf), "Rockbox error: %d",rc); lcd_puts(0, line++, buf); lcd_update(); } else { lcd_puts(0, line++, "Rockbox loaded."); lcd_update(); memcpy((void*)DRAM_START,loadbuffer,rc); return (void*)DRAM_START; } } if (i==BUTTON_PLAY) { lcd_puts(0, line, "Loading Linux..."); lcd_update(); rc=load_linux(loadbuffer); if (rc < 0) { snprintf(buf, sizeof(buf), "Linux error: %d",rc); lcd_puts(0, line++, buf); lcd_update(); } else { memcpy((void*)DRAM_START,loadbuffer,rc); return (void*)DRAM_START; } } /* If everything else failed, try the original firmware */ lcd_puts(0, line, "Loading original firmware..."); lcd_update(); /* The original firmware should already be at the correct location in RAM - the Rockbox bootloader has been appended to the end of it, and the "entryOffset" in the firmware header modified to tell the Apple bootloader to pass execution to our bootloader, rather than the start of the original firmware - which is always at the start of RAM. */ return (void*)DRAM_START; } /* These functions are present in the firmware library, but we reimplement them here because the originals do a lot more than we want */ void reset_poweroff_timer(void) { } int dbg_ports(void) { return 0; } void mpeg_stop(void) { } void usb_acknowledge(void) { } void usb_wait_for_disconnect(void) { } void sys_poweroff(void) { }