From 061f38096a6990f1b3de2fd2dc4f06ecb29e363f Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Sun, 13 Nov 2005 20:59:30 +0000 Subject: Implement (unreliable) button detection using code from ipodlinux bootloader - this needs replacing by a better button driver in firmware/ and triple-boot bootloader functionality. Current button mappings: MENU loads rockbox.ipod and checks the checksum (but doesn't execute it), PLAY will load and execute a Linux kernel (linux.bin). An error in either of those two actions, or no keypress will load the original firmware git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7848 a1c6a512-1295-4272-9138-f99709370657 --- bootloader/ipod.c | 213 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 173 insertions(+), 40 deletions(-) (limited to 'bootloader') diff --git a/bootloader/ipod.c b/bootloader/ipod.c index 840b95be9e..591b7608aa 100644 --- a/bootloader/ipod.c +++ b/bootloader/ipod.c @@ -23,6 +23,7 @@ #include #include +#include #include "cpu.h" #include "system.h" #include "lcd.h" @@ -34,7 +35,6 @@ #include "font.h" #include "adc.h" #include "backlight.h" -#include "button.h" #include "panic.h" #include "power.h" #include "file.h" @@ -44,6 +44,14 @@ #define IPOD_HW_REVISION (*((volatile unsigned long*)(0x00002084))) +#define BUTTON_LEFT 1 +#define BUTTON_MENU 2 +#define BUTTON_RIGHT 3 +#define BUTTON_PLAY 4 + +/* Size of the buffer to store the loaded Rockbox/Linux image */ +#define MAX_LOADSIZE (4*1024*1024) + char version[] = APPSVERSION; #include "rockbox-16bit.h" @@ -115,7 +123,93 @@ int usleep(unsigned int usecs) return 0; } -int load_firmware(void) + +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 = timer_get_current(); + 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 = timer_get_current(); + 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; +} + +static int key_pressed(void) +{ + unsigned char state; + + 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; + return 0; +} + +int load_rockbox(unsigned char* buf) { int fd; int rc; @@ -124,36 +218,31 @@ int load_firmware(void) char model[5]; unsigned long sum; int i; - unsigned char *buf = (unsigned char *)DRAM_START; char str[80]; fd = open("/rockbox.ipod", O_RDONLY); if(fd < 0) - return -1; + return -1; len = filesize(fd) - 8; - snprintf(str, 80, "Length: %x", len); - lcd_puts(0, line++, str); - lcd_update(); + 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; - - snprintf(str, 80, "Checksum: %x", chksum); - lcd_puts(0, line++, str); - lcd_update(); + return -2; rc = read(fd, model, 4); if(rc < 4) - return -3; + return -3; model[4] = 0; - snprintf(str, 80, "Model name: %s", model); + snprintf(str, 80, "Model: %s, Checksum: %x", model, chksum); lcd_puts(0, line++, str); lcd_update(); @@ -161,14 +250,14 @@ int load_firmware(void) rc = read(fd, buf, len); if(rc < len) - return -4; + return -4; close(fd); sum = MODEL_NUMBER; for(i = 0;i < len;i++) { - sum += buf[i]; + sum += buf[i]; } snprintf(str, 80, "Sum: %x", sum); @@ -178,9 +267,40 @@ int load_firmware(void) if(sum != chksum) return -5; - return 0; + 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]; @@ -196,7 +316,6 @@ void* main(void) /* Turn on the backlight */ #if CONFIG_BACKLIGHT==BL_IPOD4G - /* brightness full */ outl(0x80000000 | (0xff << 16), 0x7000a010); @@ -277,32 +396,50 @@ void* main(void) lcd_puts(0, line++, buf); lcd_update(); + /* Check for a keypress */ + i=key_pressed(); + + if (i==BUTTON_MENU) { + 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(); #if 0 - /* The following code will load and run an ipodlinux kernel - we will - enable it once the button driver is written and we can detect key - presses */ - int fd=open("/linux.bin",O_RDONLY); - if (fd >= 0) { - i=filesize(fd); - int n=read(fd,(void*)DRAM_START,i); - if (n==i) { - /* We return the entry point for the loaded kernel */ - return DRAM_START; - } else { - /* What do we do now? We may have overwritten the copy of the - original firmware with our incomplete copy of the Linux - kernel... */ - } - } + /* Rockbox is not yet runable, so we disable this */ + memcpy((void*)DRAM_START,loadbuffer,rc); + return (void*)DRAM_START; #endif + } + } - /* Pause for 5 seconds so we can see what's happened*/ - usleep(5000000); + 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(); + /* Pause for 5 seconds so we can see what's happened */ + usleep(5000000); + entry = tblp->addr + tblp->entryOffset; if (imageno || ((int)tblp->addr & 0xffffff) != 0) { memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding, @@ -320,10 +457,6 @@ void reset_poweroff_timer(void) { } -void screen_dump(void) -{ -} - int dbg_ports(void) { return 0; -- cgit v1.2.3