Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id:$ |
| 9 | * |
| 10 | * Copyright (C) 2015 by Cástor Muñoz |
| 11 | * |
| 12 | * This program is free software; you can redistribute it and/or |
| 13 | * modify it under the terms of the GNU General Public License |
| 14 | * as published by the Free Software Foundation; either version 2 |
| 15 | * of the License, or (at your option) any later version. |
| 16 | * |
| 17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 18 | * KIND, either express or implied. |
| 19 | * |
| 20 | ****************************************************************************/ |
| 21 | |
| 22 | #include <stdio.h> |
| 23 | #include <stdlib.h> |
| 24 | #include <string.h> |
| 25 | #include <unistd.h> |
| 26 | #include <fcntl.h> |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 27 | #include <sys/types.h> |
| 28 | #include <sys/stat.h> |
| 29 | |
| 30 | #include "mks5lboot.h" |
| 31 | |
| 32 | /* Win32 compatibility */ |
| 33 | #ifndef O_BINARY |
| 34 | #define O_BINARY 0 |
| 35 | #endif |
| 36 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 37 | #ifdef WIN32 |
| 38 | #include <windows.h> |
| 39 | #define sleep_ms(ms) Sleep(ms) |
| 40 | #else |
| 41 | #include <time.h> |
| 42 | static void sleep_ms(unsigned int ms) |
| 43 | { |
| 44 | struct timespec req; |
| 45 | req.tv_sec = ms / 1000; |
| 46 | req.tv_nsec = (ms % 1000) * 1000000; |
| 47 | nanosleep(&req, NULL); |
| 48 | } |
| 49 | #endif |
| 50 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 51 | #define DEFAULT_LOOP_PERIOD 1 /* seconds */ |
| 52 | |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 53 | #define _ERR(format, ...) \ |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 54 | do { \ |
| 55 | snprintf(errstr, errstrsize, "[ERR] "format, __VA_ARGS__); \ |
| 56 | goto error; \ |
| 57 | } while(0) |
| 58 | |
| 59 | static int write_file(char *outfile, unsigned char* buf, |
| 60 | int bufsize, char* errstr, int errstrsize) |
| 61 | { |
| 62 | int fd = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666); |
| 63 | if (fd < 0) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 64 | _ERR("Could not open %s for writing", outfile); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 65 | |
| 66 | if (write(fd, buf, bufsize) != bufsize) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 67 | _ERR("Could not write file %s", outfile); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 68 | |
| 69 | return 1; |
| 70 | |
| 71 | error: |
| 72 | return 0; |
| 73 | } |
| 74 | |
| 75 | static unsigned char *read_file(char *infile, int *bufsize, |
| 76 | char* errstr, int errstrsize) |
| 77 | { |
| 78 | unsigned char *buf; |
| 79 | int fd; |
| 80 | struct stat s; |
| 81 | |
| 82 | fd = open(infile, O_RDONLY|O_BINARY); |
| 83 | if (fd < 0) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 84 | _ERR("Could not open %s for reading", infile); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 85 | |
| 86 | if (fstat(fd, &s) < 0) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 87 | _ERR("Checking size of input file %s", infile); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 88 | |
| 89 | *bufsize = s.st_size; |
| 90 | |
| 91 | buf = malloc(*bufsize); |
| 92 | if (buf == NULL) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 93 | _ERR("Could not allocate memory for %s", infile); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 94 | |
| 95 | if (read(fd, buf, *bufsize) != *bufsize) |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 96 | _ERR("Could not read file %s", infile); |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 97 | |
| 98 | return buf; |
| 99 | |
| 100 | error: |
| 101 | return NULL; |
| 102 | } |
| 103 | |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 104 | static void usage(void) |
| 105 | { |
| 106 | fprintf(stderr, |
| 107 | "Usage:\n" |
| 108 | " mks5lboot --bl-inst <bootloader.ipod> [-p <pid>] [--single]\n" |
| 109 | " --bl-uninst <platform> [-p <pid>]\n" |
| 110 | " --dfuscan [--loop [<sec>]] [-p <pid>]\n" |
| 111 | " --dfusend <infile.dfu> [-p <pid>]\n" |
| 112 | " --dfureset [--loop [<sec>]] [-p <pid>]\n" |
| 113 | " --mkdfu-inst <bootloader.ipod> <outfile.dfu> [--single]\n" |
| 114 | " --mkdfu-uninst <platform> <outfile.dfu>\n" |
| 115 | " --mkdfu-raw <filename.bin> <outfile.dfu>\n" |
| 116 | "\n" |
| 117 | "Commands:\n" |
| 118 | " --bl-inst Install file <bootloader.ipod> into an iPod device\n" |
| 119 | " (same as --mkdfu-inst and --dfusend).\n" |
| 120 | " --bl-uninst Remove a bootloader from an iPod device (same as\n" |
| 121 | " --mkdfu-uninst and --dfusend).\n" |
| 122 | "\n" |
| 123 | " --dfuscan scan for DFU USB devices and outputs the status.\n" |
| 124 | " --dfusend send DFU image <infile.dfu> to the device.\n" |
| 125 | " --dfureset reset DFU USB device bus.\n" |
| 126 | "\n" |
| 127 | " --mkdfu-inst Build a DFU image containing an installer for\n" |
| 128 | " <bootloader.ipod>, save it as <outfile.dfu>.\n" |
| 129 | " --mkdfu-uninst Build a DFU image containing an uninstaler for\n" |
| 130 | " <platform> devices, save it as <outfile.dfu>.\n" |
| 131 | " --mkdfu-raw Build a DFU image containing raw executable\n" |
| 132 | " code, save it ass <outfile.dfu>. <infile.bin>\n" |
| 133 | " is the code you want to run, it is loaded at\n" |
| 134 | " address 0x%08x and executed.\n" |
| 135 | "\n" |
| 136 | " <bootloader.ipod> is the rockbox bootloader that you want to\n" |
| 137 | " install (previously scrambled with tools/scramble utility).\n" |
| 138 | "\n" |
| 139 | " <platform> is the name of the platform (type of device) for\n" |
| 140 | " which the DFU uninstaller will be built. Currently supported\n" |
| 141 | " platform names are:\n" |
| 142 | " ipod6g: iPod Classic 6G\n" |
| 143 | "\n" |
| 144 | "Options:\n" |
| 145 | " -p, --pid <pid> Use a specific <pid> (Product Id) USB device,\n" |
| 146 | " if this option is ommited then it uses the\n" |
| 147 | " first USB DFU device found.\n" |
| 148 | " -l, --loop <sec> Run the command every <sec> seconds, default\n" |
| 149 | " period (<sec> ommited) is %d seconds.\n" |
| 150 | " -S, --single Be careful using this option. The bootloader\n" |
| 151 | " is installed for single boot, the original\n" |
| 152 | " Apple NOR boot is destroyed (if it exists),\n" |
| 153 | " and only Rockbox can be used.\n" |
| 154 | , DFU_LOADADDR + BIN_OFFSET |
| 155 | , DEFAULT_LOOP_PERIOD); |
| 156 | |
| 157 | exit(1); |
| 158 | } |
| 159 | |
| 160 | int main(int argc, char* argv[]) |
| 161 | { |
| 162 | char *dfuoutfile = NULL; |
| 163 | char *dfuinfile = NULL; |
| 164 | char *dfu_arg = NULL; |
| 165 | int dfu_type = DFU_NONE; |
| 166 | int n_cmds = 0; |
| 167 | int scan = 0; |
| 168 | int pid = 0; |
| 169 | int reset = 0; |
| 170 | int loop = 0; |
| 171 | int single = 0; |
| 172 | char errstr[200]; |
| 173 | unsigned char *dfubuf; |
| 174 | int dfusize; |
| 175 | |
| 176 | fprintf(stderr, |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 177 | #if defined(WIN32) && defined(USE_LIBUSBAPI) |
| 178 | "mks5lboot Version " VERSION " (libusb)\n" |
| 179 | #else |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 180 | "mks5lboot Version " VERSION "\n" |
Cástor Muñoz | fbbba92 | 2017-05-04 10:52:03 +0200 | [diff] [blame] | 181 | #endif |
Cástor Muñoz | 346423c | 2016-02-04 23:05:17 +0100 | [diff] [blame] | 182 | "This is free software; see the source for copying conditions. There is NO\n" |
| 183 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" |
| 184 | "\n"); |
| 185 | fflush(stderr); |
| 186 | |
| 187 | while (--argc) |
| 188 | { |
| 189 | argv++; |
| 190 | if (!memcmp(*argv, "--bl", 4)) { |
| 191 | if (!strcmp(*argv+4, "-inst")) dfu_type = DFU_INST; |
| 192 | else if (!strcmp(*argv+4, "-uninst")) dfu_type = DFU_UNINST; |
| 193 | else usage(); |
| 194 | if (!--argc) usage(); |
| 195 | dfu_arg = *++argv; |
| 196 | n_cmds++; |
| 197 | } |
| 198 | else if (!memcmp(*argv, "--mkdfu", 7)) { |
| 199 | if (!strcmp(*argv+7, "-inst")) dfu_type = DFU_INST; |
| 200 | else if (!strcmp(*argv+7, "-uninst")) dfu_type = DFU_UNINST; |
| 201 | else if (!strcmp(*argv+7, "-raw")) dfu_type = DFU_RAW; |
| 202 | else usage(); |
| 203 | if (!--argc) usage(); |
| 204 | dfu_arg = *++argv; |
| 205 | if (!--argc) usage(); |
| 206 | dfuoutfile = *++argv; |
| 207 | n_cmds++; |
| 208 | } |
| 209 | else if (!strcmp(*argv, "--dfusend")) { |
| 210 | if (!--argc) usage(); |
| 211 | dfuinfile = *++argv; |
| 212 | n_cmds++; |
| 213 | } |
| 214 | else if (!strcmp(*argv, "--dfuscan")) { |
| 215 | scan = 1; |
| 216 | n_cmds++; |
| 217 | } |
| 218 | else if (!strcmp(*argv, "--dfureset")) { |
| 219 | scan = 1; |
| 220 | reset = 1; |
| 221 | n_cmds++; |
| 222 | } |
| 223 | else if (!strcmp(*argv, "--pid") || !strcmp(*argv, "-p")) { |
| 224 | if (!--argc) usage(); |
| 225 | if (sscanf(*++argv, "%x", &pid) != 1) usage(); |
| 226 | } |
| 227 | else if (!strcmp(*argv, "--loop") || !strcmp (*argv, "-l")) { |
| 228 | if (!(argc-1) || *(argv+1)[0] == '-') { |
| 229 | loop = DEFAULT_LOOP_PERIOD; |
| 230 | } |
| 231 | else { |
| 232 | if ((sscanf(*++argv, "%d", &loop) != 1) || !loop) usage(); |
| 233 | argc--; |
| 234 | } |
| 235 | } |
| 236 | else if (!strcmp(*argv, "--single") || !strcmp(*argv, "-S")) { |
| 237 | single = 1; |
| 238 | } |
| 239 | else if (!strcmp(*argv, "--debug")) { |
| 240 | ipoddfu_debug(1); |
| 241 | } |
| 242 | else |
| 243 | usage(); |
| 244 | } |
| 245 | |
| 246 | if (n_cmds != 1) |
| 247 | usage(); |
| 248 | |
| 249 | if ((dfu_type == DFU_INST) && single) |
| 250 | dfu_type = DFU_INST_SINGLE; |
| 251 | |
| 252 | if (scan) { |
| 253 | int cnt = 0; |
| 254 | while (1) { |
| 255 | int state, res; |
| 256 | if (loop) printf("[%d] ", cnt); |
| 257 | else printf("[INFO] "); |
| 258 | printf("DFU %s:\n", reset ? "reset":"scan"); |
| 259 | res = ipoddfu_scan(pid, &state, reset, errstr, sizeof(errstr)); |
| 260 | if (res == 0) |
| 261 | printf("%s\n", errstr); |
| 262 | else |
| 263 | printf("[INFO] DFU device state: %d\n", state); |
| 264 | if (!loop) |
| 265 | exit(!res); |
| 266 | fflush(stdout); |
| 267 | sleep_ms(loop*1000); |
| 268 | cnt += loop; |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | if (dfuinfile) |
| 273 | dfubuf = read_file(dfuinfile, &dfusize, errstr, sizeof(errstr)); |
| 274 | else |
| 275 | dfubuf = mkdfu(dfu_type, dfu_arg, &dfusize, errstr, sizeof(errstr)); |
| 276 | |
| 277 | if (!dfubuf) |
| 278 | goto error; |
| 279 | |
| 280 | if (dfuoutfile) { |
| 281 | if (write_file(dfuoutfile, dfubuf, dfusize, errstr, sizeof(errstr))) { |
| 282 | printf("[INFO] Created file %s (%d bytes)\n", dfuoutfile, dfusize); |
| 283 | exit(0); |
| 284 | } |
| 285 | } |
| 286 | else { |
| 287 | if (ipoddfu_send(pid, dfubuf, dfusize, errstr, sizeof(errstr))) { |
| 288 | printf("[INFO] DFU image sent successfully (%d bytes)\n", dfusize); |
| 289 | exit(0); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | error: |
| 294 | printf("%s\n", errstr); |
| 295 | exit(1); |
| 296 | } |