A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

Optional dual-boot support in iAudio X5 and M5 bootloader, based on FS#5289.

In order to enable it, #define HAVE_DUALBOOT when building the bootloader.
Do not use the automatically created x5_fw.bin or m5_fw.bin, but use mkboot
to create a new firmware file from an OF x5_fw.bin resp. m5_fw.bin and
bootloader.bin.

The dual-boot bootloader boots the OF when pressing Play (main or remote) for
more than 3 seconds. Hold it a bit longer because the OF also checks buttons.
Short press boots rockbox.

As a bonus, the Play button read (for hold check) is done a bit earlier for
single-boot mode as well.



git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30018 a1c6a512-1295-4272-9138-f99709370657

+220 -35
+2 -1
bootloader/bootloader.make
··· 10 10 INCLUDES += -I$(APPSDIR) 11 11 SRC += $(call preprocess, $(APPSDIR)/SOURCES) 12 12 13 + CONFIGFILE := $(FIRMDIR)/export/config/$(MODELNAME).h 13 14 BOOTLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/boot.lds 14 15 BOOTLINK := $(BUILDDIR)/boot.link 15 16 ··· 17 18 18 19 .SECONDEXPANSION: 19 20 20 - $(BOOTLINK): $(BOOTLDS) 21 + $(BOOTLINK): $(BOOTLDS) $(CONFIGFILE) 21 22 $(call PRINTS,PP $(@F)) 22 23 $(call preprocess2file,$<,$@,-DLOADADDRESS=$(LOADADDRESS)) 23 24
+8 -8
bootloader/iaudio_coldfire.c
··· 123 123 } 124 124 } 125 125 126 + #if defined(IAUDIO_M5) || defined(IAUDIO_X5) 127 + int initial_gpio_read; 128 + #endif 129 + 126 130 void main(void) 127 131 { 128 132 int i; ··· 144 148 if ((GPIO_READ & 0x80000000) == 0) 145 149 rc_on_button = true; 146 150 #elif defined(IAUDIO_M5) || defined(IAUDIO_X5) 147 - int data; 148 - 149 151 or_l(0x0e000000, &GPIO_FUNCTION); /* main Hold & Power, remote Play */ 150 152 and_l(~0x0e000000, &GPIO_ENABLE); 151 - 152 - data = GPIO_READ; 153 - 154 - if ((data & 0x04000000) == 0) 153 + 154 + if ((initial_gpio_read & 0x04000000) == 0) 155 155 on_button = true; 156 - 157 - if ((data & 0x02000000) == 0) 156 + 157 + if ((initial_gpio_read & 0x02000000) == 0) 158 158 rc_on_button = true; 159 159 #endif 160 160
+51 -2
firmware/target/coldfire/crt0.S
··· 26 26 .global start 27 27 start: 28 28 29 + #if defined(BOOTLOADER) && defined(HAVE_DUALBOOT) \ 30 + && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) 31 + 32 + /* 8 byte dualboot signature */ 33 + bra.b 1f /* 0x6006 */ 34 + .short 0x4442 /* DB */ 35 + #if defined(IAUDIO_X5) 36 + .long 0x69617835 /* iax5 */ 37 + #elif defined(IAUDIO_M5) 38 + .long 0x69616d35 /* iam5 */ 39 + #else 40 + #error Dualboot signature not defined 41 + #endif 42 + 1: 43 + /* As the control registers are write-only, we're relying on MBAR2 being */ 44 + /* set up correctly by the preloader for button check */ 45 + /* Only use scratch regs until we're sure that we will boot rockbox */ 46 + lea MBAR2, %a1 47 + move.l (%a1), %a0 /* store GPIO_READ result for button check in main() */ 48 + 49 + /* Wait ~3 seconds for ON-button release. We need roughly 300ns per 50 + iteration, so we check 10000000 times to reach the desired delay */ 51 + move.l #10000000, %d0 52 + .on_button_test: 53 + move.l (%a1), %d1 /* GPIO_READ */ 54 + and.l #0x06000000, %d1 /* Check main (bit 25=0) and remote (bit 26=0) */ 55 + cmp.l #0x06000000, %d1 /* ON buttons simultaneously */ 56 + beq.b .loadrockbox 57 + subq.l #1, %d0 58 + bne.b .on_button_test 59 + 60 + .loadoriginal: 61 + jmp 0x10010 62 + 63 + .loadrockbox: 64 + move.l %a0, %d7 /* keep initial GPIO_READ value in %d7 for now */ 65 + 66 + #endif /* defined(BOOTLOADER && defined(HAVE_DUALBOOT) 67 + && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) */ 68 + 29 69 move.w #0x2700,%sr 30 70 31 71 move.l #vectors,%d0 32 72 movec.l %d0,%vbr 33 - 73 + 34 74 move.l #MBAR+1,%d0 35 75 movec.l %d0,%mbar 36 76 ··· 39 79 40 80 lea MBAR,%a0 41 81 lea MBAR2,%a1 42 - 82 + 83 + #if defined(BOOTLOADER) && !defined(HAVE_DUALBOOT) \ 84 + && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) 85 + move.l (%a1), %d7 /* store GPIO_READ result for button check in main() */ 86 + #endif 87 + 43 88 clr.l (0x180,%a1) /* PLLCR = 0 */ 44 89 45 90 /* 64K DMA-capable SRAM at 0x10000000 ··· 318 363 move.l %d0,(%a2)+ 319 364 cmp.l %a2,%a4 320 365 bhi.b .mungeloop 366 + 367 + #if defined(BOOTLOADER) && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) 368 + move.l %d7, initial_gpio_read 369 + #endif 321 370 322 371 jsr main 323 372 .hoo:
+5 -1
firmware/target/coldfire/iaudio/boot.lds
··· 14 14 #define IRAMSIZE 0x18000 15 15 #endif 16 16 #define DRAMORIG 0x31000000 17 + #ifdef HAVE_DUALBOOT 18 + #define FLASHORIG 0x00150000 19 + #else 17 20 #define FLASHORIG 0x00010000 18 - #define FLASHSIZE 4M 21 + #endif 22 + #define FLASHSIZE 4M - FLASHORIG 19 23 20 24 MEMORY 21 25 {
+1 -1
rbutil/rbutilqt/base/bootloaderinstallhex.cpp
··· 162 162 163 163 // iriver decode already done in stage 1 164 164 int result; 165 - if((result = mkboot(descrambledName.toLocal8Bit().constData(), 165 + if((result = mkboot_iriver(descrambledName.toLocal8Bit().constData(), 166 166 tempfileName.toLocal8Bit().constData(), 167 167 tempbinName.toLocal8Bit().constData(), origin)) < 0) 168 168 {
+151 -21
tools/mkboot.c
··· 26 26 #ifndef RBUTIL 27 27 static void usage(void) 28 28 { 29 - printf("usage: mkboot [-h300] <firmware file> <boot file> <output file>\n"); 29 + printf("usage: mkboot <target> <firmware file> <boot file> <output file>\n"); 30 + printf("available targets:\n" 31 + "\t-h100 Iriver H1x0\n" 32 + "\t-h300 Iriver H3x0\n" 33 + "\t-iax5 iAudio X5\n" 34 + "\t-iam5 iAudio M5\n"); 30 35 31 36 exit(1); 32 37 } 33 38 #endif 34 - 35 - static unsigned char image[0x400000 + 0x220 + 0x400000/0x200]; 36 39 37 40 #ifndef RBUTIL 38 41 int main(int argc, char *argv[]) 39 42 { 40 - char *infile, *bootfile, *outfile; 41 - int origin = 0x1f0000; /* H1x0 bootloader address */ 42 - 43 - if(argc < 3) { 43 + if(argc != 5) 44 + { 44 45 usage(); 46 + return 1; 45 47 } 46 48 47 - if(!strcmp(argv[1], "-h300")) { 48 - infile = argv[2]; 49 - bootfile = argv[3]; 50 - outfile = argv[4]; 49 + if ( ! strcmp(argv[1], "-h100")) 50 + return mkboot_iriver(argv[2], argv[3], argv[4], 0x1f0000); 51 + 52 + if ( ! strcmp(argv[1], "-h300")) 53 + return mkboot_iriver(argv[2], argv[3], argv[4], 0x3f0000); 54 + 55 + if ( ! strcmp(argv[1], "-iax5")) 56 + return mkboot_iaudio(argv[2], argv[3], argv[4], 0); 57 + 58 + if ( ! strcmp(argv[1], "-iam5")) 59 + return mkboot_iaudio(argv[2], argv[3], argv[4], 1); 51 60 52 - origin = 0x3f0000; /* H3x0 bootloader address */ 53 - } 54 - else 55 - { 56 - infile = argv[1]; 57 - bootfile = argv[2]; 58 - outfile = argv[3]; 59 - } 60 - return mkboot(infile, bootfile, outfile, origin); 61 + usage(); 62 + return 1; 61 63 } 62 64 #endif 63 65 64 - int mkboot(const char* infile, const char* bootfile, const char* outfile, int origin) 66 + static unsigned char image[0x400000 + 0x220 + 0x400000/0x200]; 67 + 68 + int mkboot_iriver(const char* infile, const char* bootfile, const char* outfile, int origin) 65 69 { 66 70 FILE *f; 67 71 int i; ··· 187 191 188 192 return 0; 189 193 } 194 + 195 + /* iAudio firmware update file header size */ 196 + #define HEADER_SIZE 0x1030 197 + /* Address of flash contents that get overwritten by a firmware update. 198 + * Contents before this address contain the preloader and are not affected 199 + * by a firmware update. 200 + * -> Firmware update file contents starting at offset HEADER_SIZE end up 201 + * in flash at address FLASH_START 202 + */ 203 + #define FLASH_START 0x00010000 204 + /* Start of unused space in original firmware (flash address, not file 205 + * offset!) where we patch in the Rockbox loader */ 206 + #define ROCKBOX_BOOTLOADER 0x00150000 207 + /* End of unused space in original firmware */ 208 + #define BOOTLOADER_LIMIT 0x00170000 209 + 210 + /* Patch the Rockbox bootloader into free space in the original firmware 211 + * (starting at 0x150000). The preloader starts execution of the OF at 212 + * 0x10000 which normally contains a jsr 0x10010. We also patch this to 213 + * do a jsr 0x150000 to the Rockbox dual boot loader instead. If it then 214 + * decides to start the OF instead of Rockbox, it simply does a jmp 215 + * 0x10010 instead of loading Rockbox from disk. 216 + */ 217 + int mkboot_iaudio(const char* infile, const char* bootfile, const char* outfile, int model_nr) 218 + { 219 + size_t flength, blength; 220 + unsigned char *bbuf, *fbuf, *p; 221 + const unsigned char fsig[] = { 222 + 0x4e, 0xb9, 0x00, 0x01, 0x00, 0x10 }; /* jsr 0x10010 */ 223 + unsigned char bsig[2][8] = { 224 + /* dualboot signatures */ 225 + { 0x60, 0x06, 0x44, 0x42, 0x69, 0x61, 0x78, 0x35 }, /* X5 */ 226 + { 0x60, 0x06, 0x44, 0x42, 0x69, 0x61, 0x6d, 0x35 }, /* M5 */ }; 227 + FILE *ffile, *bfile, *ofile; 228 + unsigned char sum = 0; 229 + int i; 230 + 231 + /* read input files */ 232 + if ((bfile = fopen(bootfile, "rb")) == NULL) { 233 + perror("Cannot open Rockbox bootloader file.\n"); 234 + return 1; 235 + } 236 + 237 + fseek(bfile, 0, SEEK_END); 238 + blength = ftell(bfile); 239 + fseek(bfile, 0, SEEK_SET); 240 + 241 + if (blength + ROCKBOX_BOOTLOADER >= BOOTLOADER_LIMIT) { 242 + fprintf(stderr, "Rockbox bootloader is too big.\n"); 243 + return 1; 244 + } 245 + 246 + if ((ffile = fopen(infile, "rb")) == NULL) { 247 + perror("Cannot open original firmware file."); 248 + return 1; 249 + } 250 + 251 + fseek(ffile, 0, SEEK_END); 252 + flength = ftell(ffile); 253 + fseek(ffile, 0, SEEK_SET); 254 + 255 + bbuf = malloc(blength); 256 + fbuf = malloc(flength); 257 + 258 + if (!bbuf || !fbuf) { 259 + fprintf(stderr, "Out of memory.\n"); 260 + return 1; 261 + } 262 + 263 + if ( fread(bbuf, 1, blength, bfile) < blength 264 + || fread(fbuf, 1, flength, ffile) < flength) { 265 + fprintf(stderr, "Read error.\n"); 266 + return 1; 267 + } 268 + fclose(bfile); 269 + fclose(ffile); 270 + 271 + /* verify format of input files */ 272 + if (blength < 0x10 || memcmp(bbuf, bsig[model_nr], sizeof(bsig[0]))) { 273 + fprintf(stderr, "Rockbox bootloader format error (is it bootloader.bin?).\n"); 274 + return 1; 275 + } 276 + if (flength < HEADER_SIZE-FLASH_START+BOOTLOADER_LIMIT 277 + || memcmp(fbuf+HEADER_SIZE, fsig, sizeof(fsig))) { 278 + fprintf(stderr, "Original firmware format error.\n"); 279 + return 1; 280 + } 281 + 282 + /* verify firmware is not overrun */ 283 + for (i = ROCKBOX_BOOTLOADER; i < BOOTLOADER_LIMIT; i++) { 284 + if (fbuf[HEADER_SIZE-FLASH_START+i] != 0xff) { 285 + fprintf(stderr, "Original firmware has grown too much.\n"); 286 + return 1; 287 + } 288 + } 289 + 290 + /* change jsr 0x10010 to jsr DUAL_BOOTLOADER */ 291 + p = fbuf + HEADER_SIZE + 2; 292 + *p++ = (ROCKBOX_BOOTLOADER >> 24) & 0xff; 293 + *p++ = (ROCKBOX_BOOTLOADER >> 16) & 0xff; 294 + *p++ = (ROCKBOX_BOOTLOADER >> 8) & 0xff; 295 + *p++ = (ROCKBOX_BOOTLOADER ) & 0xff; 296 + 297 + p = fbuf + HEADER_SIZE + ROCKBOX_BOOTLOADER - FLASH_START; 298 + memcpy(p, bbuf, blength); 299 + 300 + /* recalc checksum */ 301 + for (i = HEADER_SIZE; (size_t)i < flength; i++) 302 + sum += fbuf[i]; 303 + fbuf[0x102b] = sum; 304 + 305 + /* write output */ 306 + if ((ofile = fopen(outfile, "wb")) == NULL) { 307 + perror("Cannot open output file"); 308 + return 1; 309 + } 310 + if (fwrite(fbuf, 1, flength, ofile) < flength) { 311 + fprintf(stderr, "Write error.\n"); 312 + return 1; 313 + } 314 + fclose(ofile); 315 + free(bbuf); 316 + free(fbuf); 317 + 318 + return 0; 319 + }
+2 -1
tools/mkboot.h
··· 26 26 extern "C" { 27 27 #endif 28 28 29 - int mkboot(const char* infile, const char* bootfile, const char* outfile, int origin); 29 + int mkboot_iriver(const char* infile, const char* bootfile, const char* outfile, int origin); 30 + int mkboot_iaudio(const char* infile, const char* bootfile, const char* outfile, int model_nr); 30 31 31 32 #ifdef __cplusplus 32 33 }