diff options
Diffstat (limited to 'utils/ingenic_tools/usbboot.c')
-rw-r--r-- | utils/ingenic_tools/usbboot.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/utils/ingenic_tools/usbboot.c b/utils/ingenic_tools/usbboot.c new file mode 100644 index 0000000000..0001711605 --- /dev/null +++ b/utils/ingenic_tools/usbboot.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * Directly adapted from jz4760_tools/usbboot.c, | ||
13 | * Copyright (C) 2015 by Amaury Pouly | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | |||
25 | #include <libusb.h> | ||
26 | #include <getopt.h> | ||
27 | #include <unistd.h> | ||
28 | #include <stdlib.h> | ||
29 | #include <stdbool.h> | ||
30 | #include <string.h> | ||
31 | #include <stdio.h> | ||
32 | #include <stdarg.h> | ||
33 | |||
34 | #define VR_GET_CPU_INFO 0 | ||
35 | #define VR_SET_DATA_ADDRESS 1 | ||
36 | #define VR_SET_DATA_LENGTH 2 | ||
37 | #define VR_FLUSH_CACHES 3 | ||
38 | #define VR_PROGRAM_START1 4 | ||
39 | #define VR_PROGRAM_START2 5 | ||
40 | |||
41 | /* Global variables */ | ||
42 | bool g_verbose = false; | ||
43 | libusb_device_handle* g_usb_dev = NULL; | ||
44 | int g_vid = 0, g_pid = 0; | ||
45 | |||
46 | /* Utility functions */ | ||
47 | void die(const char* msg, ...) | ||
48 | { | ||
49 | va_list ap; | ||
50 | va_start(ap, msg); | ||
51 | vfprintf(stderr, msg, ap); | ||
52 | fprintf(stderr, "\n"); | ||
53 | va_end(ap); | ||
54 | exit(1); | ||
55 | } | ||
56 | |||
57 | void verbose(const char* msg, ...) | ||
58 | { | ||
59 | if(!g_verbose) | ||
60 | return; | ||
61 | |||
62 | va_list ap; | ||
63 | va_start(ap, msg); | ||
64 | vprintf(msg, ap); | ||
65 | printf("\n"); | ||
66 | va_end(ap); | ||
67 | } | ||
68 | |||
69 | void open_usb(void) | ||
70 | { | ||
71 | if(g_usb_dev) { | ||
72 | verbose("Closing USB device"); | ||
73 | libusb_close(g_usb_dev); | ||
74 | } | ||
75 | |||
76 | if(g_vid == 0 || g_pid == 0) | ||
77 | die("Can't open USB device: vendor/product ID not specified"); | ||
78 | |||
79 | verbose("Opening USB device %04x:%04x", g_vid, g_pid); | ||
80 | g_usb_dev = libusb_open_device_with_vid_pid(NULL, g_vid, g_pid); | ||
81 | if(!g_usb_dev) | ||
82 | die("Could not open USB device"); | ||
83 | |||
84 | int ret = libusb_claim_interface(g_usb_dev, 0); | ||
85 | if(ret != 0) { | ||
86 | libusb_close(g_usb_dev); | ||
87 | die("Could not claim interface: %d", ret); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | void ensure_usb(void) | ||
92 | { | ||
93 | if(!g_usb_dev) | ||
94 | open_usb(); | ||
95 | } | ||
96 | |||
97 | /* USB communication functions */ | ||
98 | void jz_get_cpu_info(void) | ||
99 | { | ||
100 | ensure_usb(); | ||
101 | verbose("Issue GET_CPU_INFO"); | ||
102 | |||
103 | uint8_t buf[9]; | ||
104 | int ret = libusb_control_transfer(g_usb_dev, | ||
105 | LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, | ||
106 | VR_GET_CPU_INFO, 0, 0, buf, 8, 1000); | ||
107 | if(ret != 0) | ||
108 | die("Can't get CPU info: %d", ret); | ||
109 | |||
110 | buf[8] = 0; | ||
111 | printf("CPU info: %s\n", buf); | ||
112 | } | ||
113 | |||
114 | void jz_upload(const char* filename, int length) | ||
115 | { | ||
116 | if(length < 0) | ||
117 | die("invalid upload length: %d", length); | ||
118 | |||
119 | ensure_usb(); | ||
120 | verbose("Transfer %d bytes from device to host", length); | ||
121 | |||
122 | void* data = malloc(length); | ||
123 | int xfered = 0; | ||
124 | int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | 1, | ||
125 | data, length, &xfered, 10000); | ||
126 | if(ret != 0) | ||
127 | die("Transfer failed: %d", ret); | ||
128 | if(xfered != length) | ||
129 | die("Transfer error: got %d bytes, expected %d", xfered, length); | ||
130 | |||
131 | FILE* f = fopen(filename, "wb"); | ||
132 | if(f == NULL) | ||
133 | die("Can't open file '%s' for writing", filename); | ||
134 | |||
135 | if(fwrite(data, length, 1, f) != 1) | ||
136 | die("Error writing transfered data to file"); | ||
137 | |||
138 | fclose(f); | ||
139 | free(data); | ||
140 | } | ||
141 | |||
142 | void jz_download(const char* filename) | ||
143 | { | ||
144 | FILE* f = fopen(filename, "rb"); | ||
145 | if(f == NULL) | ||
146 | die("Can't open file '%s' for reading", filename); | ||
147 | |||
148 | fseek(f, 0, SEEK_END); | ||
149 | int length = ftell(f); | ||
150 | fseek(f, 0, SEEK_SET); | ||
151 | |||
152 | void* data = malloc(length); | ||
153 | if(fread(data, length, 1, f) != 1) | ||
154 | die("Error reading data from file"); | ||
155 | fclose(f); | ||
156 | |||
157 | verbose("Transfer %d bytes from host to device", length); | ||
158 | int xfered = 0; | ||
159 | int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_OUT | 1, | ||
160 | data, length, &xfered, 10000); | ||
161 | if(ret != 0) | ||
162 | die("Transfer failed: %d", ret); | ||
163 | if(xfered != length) | ||
164 | die("Transfer error: %d bytes recieved, expected %d", xfered, length); | ||
165 | |||
166 | free(data); | ||
167 | } | ||
168 | |||
169 | #define jz_vendor_out_func(name, type, fmt) \ | ||
170 | void name(unsigned long param) { \ | ||
171 | ensure_usb(); \ | ||
172 | verbose("Issue " #type fmt, param); \ | ||
173 | int ret = libusb_control_transfer(g_usb_dev, \ | ||
174 | LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, \ | ||
175 | VR_##type, param >> 16, param & 0xffff, NULL, 0, 1000); \ | ||
176 | if(ret != 0) \ | ||
177 | die("Request " #type " failed: %d", ret); \ | ||
178 | } | ||
179 | |||
180 | jz_vendor_out_func(jz_set_data_address, SET_DATA_ADDRESS, " 0x%08lx") | ||
181 | jz_vendor_out_func(jz_set_data_length, SET_DATA_LENGTH, " 0x%0lx") | ||
182 | jz_vendor_out_func(_jz_flush_caches, FLUSH_CACHES, "") | ||
183 | jz_vendor_out_func(jz_program_start1, PROGRAM_START1, " 0x%08lx") | ||
184 | jz_vendor_out_func(jz_program_start2, PROGRAM_START2, " 0x%08lx") | ||
185 | #define jz_flush_caches() _jz_flush_caches(0) | ||
186 | |||
187 | /* Default settings */ | ||
188 | struct cpu_profile { | ||
189 | const char* name; | ||
190 | int vid, pid; | ||
191 | unsigned long s1_load_addr, s1_exec_addr; | ||
192 | unsigned long s2_load_addr, s2_exec_addr; | ||
193 | }; | ||
194 | |||
195 | static const struct cpu_profile cpu_profiles[] = { | ||
196 | {"x1000", | ||
197 | 0xa108, 0x1000, | ||
198 | 0xf4001000, 0xf4001800, | ||
199 | 0x80004000, 0x80004000}, | ||
200 | {"jz4760", | ||
201 | 0x601a, 0x4760, | ||
202 | 0x80000000, 0x80000000, | ||
203 | 0x80000000, 0x80000000}, | ||
204 | {NULL} | ||
205 | }; | ||
206 | |||
207 | /* Simple "download and run" functions for dev purposes */ | ||
208 | unsigned long s1_load_addr = 0, s1_exec_addr = 0; | ||
209 | unsigned long s2_load_addr = 0, s2_exec_addr = 0; | ||
210 | |||
211 | void apply_cpu_profile(const char* name) | ||
212 | { | ||
213 | const struct cpu_profile* p = &cpu_profiles[0]; | ||
214 | for(p = &cpu_profiles[0]; p->name != NULL; ++p) { | ||
215 | if(strcmp(p->name, name) != 0) | ||
216 | continue; | ||
217 | |||
218 | g_vid = p->vid; | ||
219 | g_pid = p->pid; | ||
220 | s1_load_addr = p->s1_load_addr; | ||
221 | s1_exec_addr = p->s1_exec_addr; | ||
222 | s2_load_addr = p->s2_load_addr; | ||
223 | s2_exec_addr = p->s2_exec_addr; | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | die("CPU '%s' not known", name); | ||
228 | } | ||
229 | |||
230 | void run_stage1(const char* filename) | ||
231 | { | ||
232 | if(s1_load_addr == 0 || s1_exec_addr == 0) | ||
233 | die("No stage1 binary settings -- did you specify --cpu?"); | ||
234 | jz_set_data_address(s1_load_addr); | ||
235 | jz_download(filename); | ||
236 | jz_program_start1(s1_exec_addr); | ||
237 | } | ||
238 | |||
239 | void run_stage2(const char* filename) | ||
240 | { | ||
241 | if(s2_load_addr == 0 || s2_exec_addr == 0) | ||
242 | die("No stage2 binary settings -- did you specify --cpu?"); | ||
243 | jz_set_data_address(s2_load_addr); | ||
244 | jz_download(filename); | ||
245 | jz_flush_caches(); | ||
246 | jz_program_start2(s2_exec_addr); | ||
247 | } | ||
248 | |||
249 | /* Main functions */ | ||
250 | void usage() | ||
251 | { | ||
252 | printf("\ | ||
253 | Usage: usbboot [options]\n\ | ||
254 | \n\ | ||
255 | Basic options:\n\ | ||
256 | --cpu <cpu> Select device CPU type\n\ | ||
257 | --stage1 <file> Download and execute stage1 binary\n\ | ||
258 | --stage2 <file> Download and execute stage2 binary\n\ | ||
259 | \n\ | ||
260 | Advanced options:\n\ | ||
261 | --vid <vid> Specify USB vendor ID\n\ | ||
262 | --pid <pid> Specify USB product ID\n\ | ||
263 | --cpuinfo Ask device for CPU info\n\ | ||
264 | --addr <addr> Set data address\n\ | ||
265 | --length <len> Set data length\n\ | ||
266 | --upload <file> Transfer data from device (needs prior --length)\n\ | ||
267 | --download <file> Transfer data to device\n\ | ||
268 | --start1 <addr> Execute stage1 code at address\n\ | ||
269 | --start2 <addr> Execute stage2 code at address\n\ | ||
270 | --flush-caches Flush device CPU caches\n\ | ||
271 | --renumerate Close and re-open the USB device\n\ | ||
272 | --wait <time> Wait for <time> seconds\n\ | ||
273 | -v, --verbose Be verbose\n\ | ||
274 | \n\ | ||
275 | Known CPU types and default stage1/stage2 binary settings:\n"); | ||
276 | const struct cpu_profile* p = &cpu_profiles[0]; | ||
277 | for(p = &cpu_profiles[0]; p->name != NULL; ++p) { | ||
278 | printf("* %s\n", p->name); | ||
279 | printf(" - USB ID: %04x:%04x\n", p->vid, p->pid); | ||
280 | printf(" - Stage1: load %#08lx, exec %#08lx\n", | ||
281 | p->s1_load_addr, p->s1_exec_addr); | ||
282 | printf(" - Stage2: load %#08lx, exec %#08lx\n", | ||
283 | p->s2_load_addr, p->s2_exec_addr); | ||
284 | } | ||
285 | |||
286 | exit(1); | ||
287 | } | ||
288 | |||
289 | void cleanup() | ||
290 | { | ||
291 | if(g_usb_dev == NULL) | ||
292 | libusb_close(g_usb_dev); | ||
293 | libusb_exit(NULL); | ||
294 | } | ||
295 | |||
296 | int main(int argc, char* argv[]) | ||
297 | { | ||
298 | if(argc <= 1) | ||
299 | usage(); | ||
300 | |||
301 | libusb_init(NULL); | ||
302 | atexit(cleanup); | ||
303 | |||
304 | enum { | ||
305 | OPT_VID = 0x100, OPT_PID, | ||
306 | OPT_CPUINFO, | ||
307 | OPT_START1, OPT_START2, OPT_FLUSH_CACHES, | ||
308 | OPT_RENUMERATE, OPT_WAIT, | ||
309 | }; | ||
310 | |||
311 | static const struct option long_options[] = { | ||
312 | {"cpu", required_argument, 0, 'c'}, | ||
313 | {"stage1", required_argument, 0, '1'}, | ||
314 | {"stage2", required_argument, 0, '2'}, | ||
315 | {"vid", required_argument, 0, OPT_VID}, | ||
316 | {"pid", required_argument, 0, OPT_PID}, | ||
317 | {"cpuinfo", no_argument, 0, OPT_CPUINFO}, | ||
318 | {"addr", required_argument, 0, 'a'}, | ||
319 | {"length", required_argument, 0, 'l'}, | ||
320 | {"upload", required_argument, 0, 'u'}, | ||
321 | {"download", required_argument, 0, 'd'}, | ||
322 | {"start1", required_argument, 0, OPT_START1}, | ||
323 | {"start2", required_argument, 0, OPT_START2}, | ||
324 | {"flush-caches", no_argument, 0, OPT_FLUSH_CACHES}, | ||
325 | {"renumerate", no_argument, 0, OPT_RENUMERATE}, | ||
326 | {"wait", required_argument, 0, OPT_WAIT}, | ||
327 | {"help", no_argument, 0, 'h'}, | ||
328 | {"verbose", no_argument, 0, 'v'}, | ||
329 | {0, 0, 0, 0} | ||
330 | }; | ||
331 | |||
332 | int opt; | ||
333 | int data_length = -1; | ||
334 | while((opt = getopt_long(argc, argv, "hvc:1:2:a:l:u:d:", long_options, NULL)) != -1) { | ||
335 | unsigned long param; | ||
336 | char* end; | ||
337 | switch(opt) { | ||
338 | case OPT_VID: | ||
339 | case OPT_PID: | ||
340 | case 'a': | ||
341 | case 'l': | ||
342 | case OPT_START1: | ||
343 | case OPT_START2: | ||
344 | case OPT_WAIT: | ||
345 | param = strtoul(optarg, &end, 0); | ||
346 | if(*end) | ||
347 | die("Invalid argument '%s'", optarg); | ||
348 | break; | ||
349 | default: | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | switch(opt) { | ||
354 | case 'h': | ||
355 | usage(); | ||
356 | break; | ||
357 | case 'v': | ||
358 | g_verbose = true; | ||
359 | break; | ||
360 | case 'c': | ||
361 | apply_cpu_profile(optarg); | ||
362 | break; | ||
363 | case '1': | ||
364 | run_stage1(optarg); | ||
365 | break; | ||
366 | case '2': | ||
367 | run_stage2(optarg); | ||
368 | break; | ||
369 | case OPT_VID: | ||
370 | g_vid = param & 0xffff; | ||
371 | break; | ||
372 | case OPT_PID: | ||
373 | g_pid = param & 0xffff; | ||
374 | break; | ||
375 | case OPT_CPUINFO: | ||
376 | jz_get_cpu_info(); | ||
377 | break; | ||
378 | case 'a': | ||
379 | jz_set_data_address(param); | ||
380 | break; | ||
381 | case 'l': | ||
382 | data_length = param; | ||
383 | jz_set_data_length(param); | ||
384 | break; | ||
385 | case 'u': | ||
386 | if(data_length < 0) | ||
387 | die("Need to specify --length before --upload"); | ||
388 | jz_upload(optarg, data_length); | ||
389 | break; | ||
390 | case 'd': | ||
391 | jz_download(optarg); | ||
392 | break; | ||
393 | case OPT_START1: | ||
394 | jz_program_start1(param); | ||
395 | break; | ||
396 | case OPT_START2: | ||
397 | jz_program_start2(param); | ||
398 | break; | ||
399 | case OPT_FLUSH_CACHES: | ||
400 | jz_flush_caches(); | ||
401 | break; | ||
402 | case OPT_RENUMERATE: | ||
403 | open_usb(); | ||
404 | break; | ||
405 | case OPT_WAIT: | ||
406 | verbose("Wait %lu seconds", param); | ||
407 | sleep(param); | ||
408 | break; | ||
409 | default: | ||
410 | /* should only happen due to a bug */ | ||
411 | die("Bad option"); | ||
412 | break; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | if(optind != argc) | ||
417 | die("Extra arguments on command line"); | ||
418 | |||
419 | return 0; | ||
420 | } | ||