diff options
Diffstat (limited to 'utils/imxtools/sbtools')
-rw-r--r-- | utils/imxtools/sbtools/sbloader.c | 382 |
1 files changed, 346 insertions, 36 deletions
diff --git a/utils/imxtools/sbtools/sbloader.c b/utils/imxtools/sbtools/sbloader.c index a5bc8c18fb..657ef5d578 100644 --- a/utils/imxtools/sbtools/sbloader.c +++ b/utils/imxtools/sbtools/sbloader.c | |||
@@ -23,12 +23,20 @@ | |||
23 | #include <string.h> | 23 | #include <string.h> |
24 | #include <libusb.h> | 24 | #include <libusb.h> |
25 | #include <stdint.h> | 25 | #include <stdint.h> |
26 | #include <stdbool.h> | ||
27 | #include <getopt.h> | ||
28 | |||
29 | bool g_debug = false; | ||
26 | 30 | ||
27 | #ifndef MIN | 31 | #ifndef MIN |
28 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 32 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
29 | #endif | 33 | #endif |
30 | 34 | ||
31 | void put32le(uint8_t *buf, uint32_t i) | 35 | #ifndef MAX |
36 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) | ||
37 | #endif | ||
38 | |||
39 | static void put32le(uint8_t *buf, uint32_t i) | ||
32 | { | 40 | { |
33 | *buf++ = i & 0xff; | 41 | *buf++ = i & 0xff; |
34 | *buf++ = (i >> 8) & 0xff; | 42 | *buf++ = (i >> 8) & 0xff; |
@@ -36,7 +44,7 @@ void put32le(uint8_t *buf, uint32_t i) | |||
36 | *buf++ = (i >> 24) & 0xff; | 44 | *buf++ = (i >> 24) & 0xff; |
37 | } | 45 | } |
38 | 46 | ||
39 | void put32be(uint8_t *buf, uint32_t i) | 47 | static void put32be(uint8_t *buf, uint32_t i) |
40 | { | 48 | { |
41 | *buf++ = (i >> 24) & 0xff; | 49 | *buf++ = (i >> 24) & 0xff; |
42 | *buf++ = (i >> 16) & 0xff; | 50 | *buf++ = (i >> 16) & 0xff; |
@@ -46,6 +54,7 @@ void put32be(uint8_t *buf, uint32_t i) | |||
46 | 54 | ||
47 | enum dev_type_t | 55 | enum dev_type_t |
48 | { | 56 | { |
57 | PROBE_DEVICE, | ||
49 | HID_DEVICE, | 58 | HID_DEVICE, |
50 | RECOVERY_DEVICE, | 59 | RECOVERY_DEVICE, |
51 | }; | 60 | }; |
@@ -66,13 +75,10 @@ struct dev_info_t g_dev_info[] = | |||
66 | {0x066f, 0x3600, 4096, RECOVERY_DEVICE}, /* STMP36xx */ | 75 | {0x066f, 0x3600, 4096, RECOVERY_DEVICE}, /* STMP36xx */ |
67 | }; | 76 | }; |
68 | 77 | ||
69 | int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers) | 78 | static int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers) |
70 | { | 79 | { |
71 | libusb_detach_kernel_driver(dev, 0); | 80 | libusb_detach_kernel_driver(dev, 0); |
72 | libusb_detach_kernel_driver(dev, 4); | ||
73 | |||
74 | libusb_claim_interface(dev, 0); | 81 | libusb_claim_interface(dev, 0); |
75 | libusb_claim_interface(dev, 4); | ||
76 | 82 | ||
77 | uint8_t *xfer_buf = malloc(1 + xfer_size); | 83 | uint8_t *xfer_buf = malloc(1 + xfer_size); |
78 | uint8_t *p = xfer_buf; | 84 | uint8_t *p = xfer_buf; |
@@ -131,7 +137,7 @@ int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, | |||
131 | return ret; | 137 | return ret; |
132 | } | 138 | } |
133 | 139 | ||
134 | int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers) | 140 | static int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers) |
135 | { | 141 | { |
136 | (void) nr_xfers; | 142 | (void) nr_xfers; |
137 | // there should be no kernel driver attached but in doubt... | 143 | // there should be no kernel driver attached but in doubt... |
@@ -159,52 +165,356 @@ int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int s | |||
159 | return 0; | 165 | return 0; |
160 | } | 166 | } |
161 | 167 | ||
162 | int main(int argc, char **argv) | 168 | static void usage(void) |
163 | { | 169 | { |
164 | if(argc != 3) | 170 | printf("sbloader [options] file\n"); |
171 | printf("options:\n"); | ||
172 | printf(" -h/-?/--help Display this help\n"); | ||
173 | printf(" -d/--debug Enable debug output\n"); | ||
174 | printf(" -x <size> Force transfer size\n"); | ||
175 | printf(" -u <vid>:<pid> Force USB PID and VID\n"); | ||
176 | printf(" -b <bus>:<dev> Force USB bus and device\n"); | ||
177 | printf(" -p <protocol> Force protocol ('hid' or 'recovery')\n"); | ||
178 | printf("The following devices are known to this tool:\n"); | ||
179 | for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) | ||
165 | { | 180 | { |
166 | printf("usage: %s <xfer size> <file>\n", argv[0]); | 181 | const char *type = "unk"; |
167 | printf("If <xfer size> is set to zero, the preferred one is used.\n"); | 182 | if(g_dev_info[i].dev_type == HID_DEVICE) |
168 | return 1; | 183 | type = "hid"; |
184 | else if(g_dev_info[i].dev_type == RECOVERY_DEVICE) | ||
185 | type = "recovery"; | ||
186 | else if(g_dev_info[i].dev_type == PROBE_DEVICE) | ||
187 | type = "probe"; | ||
188 | printf(" %04x:%04x %s (%d bytes/xfer)\n", g_dev_info[i].vendor_id, | ||
189 | g_dev_info[i].product_id, type, g_dev_info[i].xfer_size); | ||
169 | } | 190 | } |
191 | printf("You can select a particular device by USB PID and VID.\n"); | ||
192 | printf("In case this is ambiguous, use bus and device number.\n"); | ||
193 | printf("Protocol is infered if possible and unspecified.\n"); | ||
194 | printf("Transfer size is infered if possible.\n"); | ||
195 | exit(1); | ||
196 | } | ||
197 | |||
198 | static bool dev_match(libusb_device *dev, struct dev_info_t *arg_di, | ||
199 | int usb_bus, int usb_dev, int *db_idx) | ||
200 | { | ||
201 | // match bus/dev | ||
202 | if(usb_bus != -1) | ||
203 | return libusb_get_bus_number(dev) == usb_bus && libusb_get_device_address(dev) == usb_dev; | ||
204 | // get device descriptor | ||
205 | struct libusb_device_descriptor desc; | ||
206 | if(libusb_get_device_descriptor(dev, &desc)) | ||
207 | return false; | ||
208 | // match command line vid/pid if specified | ||
209 | if(arg_di->vendor_id != 0) | ||
210 | return desc.idVendor == arg_di->vendor_id && desc.idProduct == arg_di->product_id; | ||
211 | // match known vid/pid | ||
212 | for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) | ||
213 | if(desc.idVendor == g_dev_info[i].vendor_id && desc.idProduct == g_dev_info[i].product_id) | ||
214 | { | ||
215 | if(db_idx) | ||
216 | *db_idx = i; | ||
217 | return true; | ||
218 | } | ||
219 | return false; | ||
220 | } | ||
221 | |||
222 | static void print_match(libusb_device *dev) | ||
223 | { | ||
224 | struct libusb_device_descriptor desc; | ||
225 | if(libusb_get_device_descriptor(dev, &desc)) | ||
226 | printf("????:????"); | ||
227 | else | ||
228 | printf("%04x:%04x", desc.idVendor, desc.idProduct); | ||
229 | printf(" @ %d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev)); | ||
230 | } | ||
231 | |||
232 | static bool is_hid_dev(struct libusb_config_descriptor *desc) | ||
233 | { | ||
234 | if(desc->bNumInterfaces != 1) | ||
235 | return false; | ||
236 | if(desc->interface[0].num_altsetting != 1) | ||
237 | return false; | ||
238 | const struct libusb_interface_descriptor *intf = &desc->interface[0].altsetting[0]; | ||
239 | if(intf->bNumEndpoints != 1) | ||
240 | return false; | ||
241 | if(intf->bInterfaceClass != LIBUSB_CLASS_HID || intf->bInterfaceSubClass != 0 || | ||
242 | intf->bInterfaceProtocol != 0) | ||
243 | return false; | ||
244 | return true; | ||
245 | } | ||
170 | 246 | ||
171 | char *end; | 247 | static bool is_recovery_dev(struct libusb_config_descriptor *desc) |
172 | int xfer_size = strtol(argv[1], &end, 0); | 248 | { |
173 | if(end != (argv[1] + strlen(argv[1]))) | 249 | return false; |
250 | } | ||
251 | |||
252 | static enum dev_type_t probe_protocol(libusb_device_handle *dev) | ||
253 | { | ||
254 | struct libusb_config_descriptor *desc; | ||
255 | if(libusb_get_config_descriptor(libusb_get_device(dev), 0, &desc)) | ||
256 | goto Lerr; | ||
257 | if(is_hid_dev(desc)) | ||
258 | return HID_DEVICE; | ||
259 | if(is_recovery_dev(desc)) | ||
260 | return RECOVERY_DEVICE; | ||
261 | Lerr: | ||
262 | printf("Cannot probe protocol, please specify it on command line.\n"); | ||
263 | exit(11); | ||
264 | return PROBE_DEVICE; | ||
265 | } | ||
266 | |||
267 | struct hid_item_t | ||
268 | { | ||
269 | int tag; | ||
270 | int type; | ||
271 | int total_size; | ||
272 | int data_offset; | ||
273 | int data_size; | ||
274 | }; | ||
275 | |||
276 | static bool hid_parse_short_item(uint8_t *buf, int size, struct hid_item_t *item) | ||
277 | { | ||
278 | if(size == 0) | ||
279 | return false; | ||
280 | item->tag = buf[0] >> 4; | ||
281 | item->data_size = buf[0] & 3; | ||
282 | item->type = (buf[0] >> 2) & 3; | ||
283 | item->data_offset = 1; | ||
284 | item->total_size = 1 + item->data_size; | ||
285 | return size >= item->total_size; | ||
286 | } | ||
287 | |||
288 | static bool hid_parse_item(uint8_t *buf, int size, struct hid_item_t *item) | ||
289 | { | ||
290 | if(!hid_parse_short_item(buf, size, item)) | ||
291 | return false; | ||
292 | /* long item ? */ | ||
293 | if(item->data_size == 2 && item->type == 3 && item->tag == 15) | ||
174 | { | 294 | { |
175 | printf("Invalid transfer size !\n"); | 295 | item->tag = buf[2]; |
176 | return 1; | 296 | item->data_size = buf[1]; |
297 | item->total_size = 3 + item->data_size; | ||
298 | return size >= item->total_size; | ||
177 | } | 299 | } |
300 | else | ||
301 | return true; | ||
302 | } | ||
303 | |||
304 | static int probe_hid_xfer_size(libusb_device_handle *dev) | ||
305 | { | ||
306 | // FIXME detahc kernel and claim interface here ? | ||
307 | /* get HID descriptor */ | ||
308 | uint8_t buffer[1024]; | ||
309 | int ret = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE, | ||
310 | LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8) | 0, 0, buffer, | ||
311 | sizeof(buffer), 1000); | ||
312 | if(ret <= 0) | ||
313 | goto Lerr; | ||
314 | /* this is not a real parse, since the HID descriptor of the device is | ||
315 | * is mostly trivial, we assume that all reports are made up of one item | ||
316 | * and simply compute the maximum of report size * report count */ | ||
317 | int xfer_size = 0; | ||
318 | int report_size = 0; | ||
319 | int report_count = 0; | ||
320 | uint8_t *buf = buffer; | ||
321 | int size = ret; | ||
322 | while(true) | ||
323 | { | ||
324 | struct hid_item_t item; | ||
325 | if(!hid_parse_item(buf, size, &item)) | ||
326 | break; | ||
327 | if(item.type == /*global*/1) | ||
328 | { | ||
329 | if(item.tag == /*report count*/9) | ||
330 | report_count = buf[item.data_offset]; | ||
331 | if(item.tag == /*report size*/7) | ||
332 | report_size = buf[item.data_offset]; | ||
333 | } | ||
334 | else if(item.type == /*main*/0) | ||
335 | { | ||
336 | if(item.tag == /*output*/9) | ||
337 | xfer_size = MAX(xfer_size, report_count * report_size); | ||
338 | } | ||
339 | buf += item.total_size; | ||
340 | size -= item.total_size; | ||
341 | } | ||
342 | return xfer_size / 8; | ||
178 | 343 | ||
179 | libusb_device_handle *dev; | 344 | Lerr: |
180 | 345 | printf("Cannot probe transfer size, please specify it on command line.\n"); | |
346 | exit(11); | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static int probe_xfer_size(enum dev_type_t prot, libusb_device_handle *dev) | ||
351 | { | ||
352 | if(prot == HID_DEVICE) | ||
353 | return probe_hid_xfer_size(dev); | ||
354 | printf("Cannot probe transfer size, please specify it on command line.\n"); | ||
355 | exit(10); | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | int main(int argc, char **argv) | ||
360 | { | ||
361 | if(argc <= 1) | ||
362 | usage(); | ||
363 | struct dev_info_t di = {.vendor_id = 0, .product_id = 0, .xfer_size = 0, | ||
364 | .dev_type = PROBE_DEVICE}; | ||
365 | int usb_bus = -1; | ||
366 | int usb_dev = -1; | ||
367 | /* parse command line */ | ||
368 | while(1) | ||
369 | { | ||
370 | static struct option long_options[] = | ||
371 | { | ||
372 | {"help", no_argument, 0, '?'}, | ||
373 | {"debug", no_argument, 0, 'd'}, | ||
374 | {0, 0, 0, 0} | ||
375 | }; | ||
376 | |||
377 | int c = getopt_long(argc, argv, "?dx:u:b:p:", long_options, NULL); | ||
378 | if(c == -1) | ||
379 | break; | ||
380 | switch(c) | ||
381 | { | ||
382 | case -1: | ||
383 | break; | ||
384 | case 'd': | ||
385 | g_debug = true; | ||
386 | break; | ||
387 | case '?': | ||
388 | usage(); | ||
389 | break; | ||
390 | case 'x': | ||
391 | { | ||
392 | char *end; | ||
393 | di.xfer_size = strtoul(optarg, &end, 0); | ||
394 | if(*end) | ||
395 | { | ||
396 | printf("Invalid transfer size!\n"); | ||
397 | exit(2); | ||
398 | } | ||
399 | break; | ||
400 | } | ||
401 | case 'u': | ||
402 | { | ||
403 | char *end; | ||
404 | di.vendor_id = strtoul(optarg, &end, 16); | ||
405 | if(*end != ':') | ||
406 | { | ||
407 | printf("Invalid USB PID!\n"); | ||
408 | exit(3); | ||
409 | } | ||
410 | di.product_id = strtoul(end + 1, &end, 16); | ||
411 | if(*end) | ||
412 | { | ||
413 | printf("Invalid USB VID!\n"); | ||
414 | exit(4); | ||
415 | } | ||
416 | break; | ||
417 | } | ||
418 | case 'b': | ||
419 | { | ||
420 | char *end; | ||
421 | usb_bus = strtol(optarg, &end, 0); | ||
422 | if(*end != ':') | ||
423 | { | ||
424 | printf("Invalid USB bus!\n"); | ||
425 | exit(5); | ||
426 | } | ||
427 | usb_dev = strtol(end, &end, 0); | ||
428 | if(*end) | ||
429 | { | ||
430 | printf("Invalid USB device!\n"); | ||
431 | exit(6); | ||
432 | } | ||
433 | break; | ||
434 | } | ||
435 | case 'p': | ||
436 | if(strcmp(optarg, "hid") == 0) | ||
437 | di.dev_type = HID_DEVICE; | ||
438 | else if(strcmp(optarg, "recovery") == 0) | ||
439 | di.dev_type = RECOVERY_DEVICE; | ||
440 | else | ||
441 | { | ||
442 | printf("Invalid protocol!\n"); | ||
443 | exit(7); | ||
444 | } | ||
445 | break; | ||
446 | default: | ||
447 | abort(); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | if(optind + 1 != argc) | ||
452 | usage(); | ||
453 | const char *filename = argv[optind]; | ||
454 | /* lookup device */ | ||
181 | libusb_init(NULL); | 455 | libusb_init(NULL); |
182 | |||
183 | libusb_set_debug(NULL, 3); | 456 | libusb_set_debug(NULL, 3); |
457 | libusb_device **list; | ||
458 | ssize_t list_size = libusb_get_device_list(NULL, &list); | ||
459 | libusb_device_handle *dev = NULL; | ||
460 | int db_idx = -1; | ||
184 | 461 | ||
185 | unsigned i; | ||
186 | for(i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) | ||
187 | { | 462 | { |
188 | dev = libusb_open_device_with_vid_pid(NULL, | 463 | libusb_device *mdev = NULL; |
189 | g_dev_info[i].vendor_id, g_dev_info[i].product_id); | 464 | int nr_matches = 0; |
190 | if(dev == NULL) | 465 | for(int i = 0; i < list_size; i++) |
191 | continue; | 466 | { |
192 | if(xfer_size == 0) | 467 | // match bus/dev if specified |
193 | xfer_size = g_dev_info[i].xfer_size; | 468 | if(dev_match(list[i], &di, usb_bus, usb_dev, &db_idx)) |
194 | printf("Found a match for %04x:%04x\n", | 469 | { |
195 | g_dev_info[i].vendor_id, g_dev_info[i].product_id); | 470 | mdev = list[i]; |
196 | break; | 471 | nr_matches++; |
472 | } | ||
473 | } | ||
474 | if(nr_matches == 0) | ||
475 | { | ||
476 | printf("No device found\n"); | ||
477 | exit(8); | ||
478 | } | ||
479 | if(nr_matches > 1) | ||
480 | { | ||
481 | printf("Several devices match the specified parameters:\n"); | ||
482 | for(int i = 0; i < list_size; i++) | ||
483 | { | ||
484 | // match bus/dev if specified | ||
485 | if(dev_match(list[i], &di, usb_bus, usb_dev, NULL)) | ||
486 | { | ||
487 | printf(" "); | ||
488 | print_match(list[i]); | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | printf("Device: "); | ||
493 | print_match(mdev); | ||
494 | libusb_open(mdev, &dev); | ||
197 | } | 495 | } |
198 | if(dev == NULL) | 496 | if(dev == NULL) |
199 | { | 497 | { |
200 | printf("Cannot open device\n"); | 498 | printf("Cannot open device\n"); |
201 | return 1; | 499 | return 1; |
202 | } | 500 | } |
203 | 501 | /* get protocol */ | |
204 | FILE *f = fopen(argv[2], "r"); | 502 | enum dev_type_t dev_type = PROBE_DEVICE; |
503 | int xfer_size = di.xfer_size; | ||
504 | if(db_idx >= 0) | ||
505 | { | ||
506 | dev_type = g_dev_info[db_idx].dev_type; | ||
507 | xfer_size = g_dev_info[db_idx].xfer_size; | ||
508 | } | ||
509 | if(dev_type == PROBE_DEVICE) | ||
510 | dev_type = probe_protocol(dev); | ||
511 | if(xfer_size == 0) | ||
512 | xfer_size = probe_xfer_size(dev_type, dev); | ||
513 | /* open file */ | ||
514 | FILE *f = fopen(filename, "r"); | ||
205 | if(f == NULL) | 515 | if(f == NULL) |
206 | { | 516 | { |
207 | perror("cannot open file"); | 517 | perror("Cannot open file"); |
208 | return 1; | 518 | return 1; |
209 | } | 519 | } |
210 | fseek(f, 0, SEEK_END); | 520 | fseek(f, 0, SEEK_END); |
@@ -222,8 +532,8 @@ int main(int argc, char **argv) | |||
222 | return 1; | 532 | return 1; |
223 | } | 533 | } |
224 | fclose(f); | 534 | fclose(f); |
225 | 535 | /* send file */ | |
226 | switch(g_dev_info[i].dev_type) | 536 | switch(dev_type) |
227 | { | 537 | { |
228 | case HID_DEVICE: | 538 | case HID_DEVICE: |
229 | send_hid(dev, xfer_size, file_buf, size, nr_xfers); | 539 | send_hid(dev, xfer_size, file_buf, size, nr_xfers); |