summaryrefslogtreecommitdiff
path: root/utils/imxtools/sbtools/sbloader.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/imxtools/sbtools/sbloader.c')
-rw-r--r--utils/imxtools/sbtools/sbloader.c382
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
29bool 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
31void put32le(uint8_t *buf, uint32_t i) 35#ifndef MAX
36#define MAX(a,b) ((a) > (b) ? (a) : (b))
37#endif
38
39static 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
39void put32be(uint8_t *buf, uint32_t i) 47static 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
47enum dev_type_t 55enum 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
69int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers) 78static 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
134int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers) 140static 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
162int main(int argc, char **argv) 168static 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
198static 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
222static 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
232static 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; 247static 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
252static 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
267struct hid_item_t
268{
269 int tag;
270 int type;
271 int total_size;
272 int data_offset;
273 int data_size;
274};
275
276static 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
288static 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
304static 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
350static 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
359int 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);