f29dc81fcdae933214cbe72cf9de120eb47b8018
[pub/lufa.git] / Bootloaders / HID / HostLoaderApp / hid_bootloader_cli.c
1 /* Modified for the LUFA HID Bootloader by Dean Camera
2 * http://www.lufa-lib.org
3 *
4 * THIS MODIFIED VERSION IS UNSUPPORTED BY PJRC.
5 */
6
7 /* Teensy Loader, Command Line Interface
8 * Program and Reboot Teensy Board with HalfKay Bootloader
9 * http://www.pjrc.com/teensy/loader_cli.html
10 * Copyright 2008-2016, PJRC.COM, LLC
11 *
12 * You may redistribute this program and/or modify it under the terms
13 * of the GNU General Public License as published by the Free Software
14 * Foundation, version 3 of the License.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/
23 */
24
25 /* Want to incorporate this code into a proprietary application??
26 * Just email paul@pjrc.com to ask. Usually it's not a problem,
27 * but you do need to ask to use this code in any way other than
28 * those permitted by the GNU General Public License, version 3 */
29
30 /* For non-root permissions on ubuntu or similar udev-based linux
31 * http://www.pjrc.com/teensy/49-teensy.rules
32 */
33
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 void usage(const char *err)
43 {
44 if(err != NULL) fprintf(stderr, "%s\n\n", err);
45 fprintf(stderr,
46 "Usage: hid_bootloader_cli --mcu=<MCU> [-w] [-h] [-n] [-b] [-v] <file.hex>\n"
47 "\t-w : Wait for device to appear\n"
48 "\t-r : Use hard reboot if device not online\n"
49 "\t-s : Use soft reboot if device not online (Teensy 3.x & 4.x)\n"
50 "\t-n : No reboot after programming\n"
51 "\t-b : Boot only, do not program\n"
52 "\t-v : Verbose output\n"
53 "\nUse `hid_bootloader_cli --list-mcus` to list supported MCUs.\n"
54 "\nFor support and more information, please visit:\n"
55 "\thttp://www.lufa-lib.org\n"
56
57 "\nBased on the TeensyHID command line programmer software:\n"
58 "\thttp://www.pjrc.com/teensy/loader_cli.html\n");
59 exit(1);
60 }
61
62 // USB Access Functions
63 int teensy_open(void);
64 int teensy_write(void *buf, int len, double timeout);
65 void teensy_close(void);
66 int hard_reboot(void);
67 int soft_reboot(void);
68
69 // Intel Hex File Functions
70 int read_intel_hex(const char *filename);
71 int ihex_bytes_within_range(int begin, int end);
72 void ihex_get_data(int addr, int len, unsigned char *bytes);
73 int memory_is_blank(int addr, int block_size);
74
75 // Misc stuff
76 int printf_verbose(const char *format, ...);
77 void delay(double seconds);
78 void die(const char *str, ...);
79 void parse_options(int argc, char **argv);
80 void boot(unsigned char *buf, int write_size);
81
82 // options (from user via command line args)
83 int wait_for_device_to_appear = 0;
84 int hard_reboot_device = 0;
85 int soft_reboot_device = 0;
86 int reboot_after_programming = 1;
87 int verbose = 0;
88 int boot_only = 0;
89 int code_size = 0, block_size = 0;
90 const char *filename=NULL;
91
92
93 /****************************************************************/
94 /* */
95 /* Main Program */
96 /* */
97 /****************************************************************/
98
99 int main(int argc, char **argv)
100 {
101 unsigned char buf[2048];
102 int num, addr, r, write_size;
103
104 int first_block=1, waited=0;
105
106 // parse command line arguments
107 parse_options(argc, argv);
108 if (!filename && !boot_only) {
109 usage("Filename must be specified");
110 }
111 if (!code_size) {
112 usage("MCU type must be specified");
113 }
114 printf_verbose("Teensy Loader, Command Line, Version 2.2\n");
115
116 if (block_size == 512 || block_size == 1024) {
117 write_size = block_size + 64;
118 } else {
119 write_size = block_size + 2;
120 };
121
122 if (!boot_only) {
123 // read the intel hex file
124 // this is done first so any error is reported before using USB
125 num = read_intel_hex(filename);
126 if (num < 0) die("error reading intel hex file \"%s\"", filename);
127 printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
128 filename, num, (double)num / (double)code_size * 100.0);
129 }
130
131 // open the USB device
132 while (1) {
133 if (teensy_open()) break;
134 if (hard_reboot_device) {
135 if (!hard_reboot()) die("Unable to find rebootor\n");
136 printf_verbose("Hard Reboot performed\n");
137 hard_reboot_device = 0; // only hard reboot once
138 wait_for_device_to_appear = 1;
139 }
140 if (soft_reboot_device) {
141 if (soft_reboot()) {
142 printf_verbose("Soft reboot performed\n");
143 }
144 soft_reboot_device = 0;
145 wait_for_device_to_appear = 1;
146 }
147 if (!wait_for_device_to_appear) die("Unable to open device (hint: try -w option)\n");
148 if (!waited) {
149 printf_verbose("Waiting for Teensy device...\n");
150 printf_verbose(" (hint: press the reset button)\n");
151 waited = 1;
152 }
153 delay(0.25);
154 }
155 printf_verbose("Found HalfKay Bootloader\n");
156
157 if (boot_only) {
158 boot(buf, write_size);
159 teensy_close();
160 return 0;
161 }
162
163 // if we waited for the device, read the hex file again
164 // perhaps it changed while we were waiting?
165 if (waited) {
166 num = read_intel_hex(filename);
167 if (num < 0) die("error reading intel hex file \"%s\"", filename);
168 printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
169 filename, num, (double)num / (double)code_size * 100.0);
170 }
171
172 // program the data
173 printf_verbose("Programming");
174 fflush(stdout);
175 for (addr = 0; addr < code_size; addr += block_size) {
176 if (!first_block && !ihex_bytes_within_range(addr, addr + block_size - 1)) {
177 // don't waste time on blocks that are unused,
178 // but always do the first one to erase the chip
179 continue;
180 }
181 if (!first_block && memory_is_blank(addr, block_size)) continue;
182 printf_verbose(".");
183 if (block_size <= 256 && code_size < 0x10000) {
184 buf[0] = addr & 255;
185 buf[1] = (addr >> 8) & 255;
186 ihex_get_data(addr, block_size, buf + 2);
187 write_size = block_size + 2;
188 } else if (block_size == 256) {
189 buf[0] = (addr >> 8) & 255;
190 buf[1] = (addr >> 16) & 255;
191 ihex_get_data(addr, block_size, buf + 2);
192 write_size = block_size + 2;
193 } else if (block_size == 512 || block_size == 1024) {
194 buf[0] = addr & 255;
195 buf[1] = (addr >> 8) & 255;
196 buf[2] = (addr >> 16) & 255;
197 memset(buf + 3, 0, 61);
198 ihex_get_data(addr, block_size, buf + 64);
199 write_size = block_size + 64;
200 } else {
201 die("Unknown code/block size\n");
202 }
203 r = teensy_write(buf, write_size, first_block ? 5.0 : 0.5);
204 if (!r) die("error writing to Teensy\n");
205 first_block = 0;
206 }
207 printf_verbose("\n");
208
209 // reboot to the user's new code
210 if (reboot_after_programming) {
211 boot(buf, write_size);
212 }
213 teensy_close();
214 return 0;
215 }
216
217
218
219
220 /****************************************************************/
221 /* */
222 /* USB Access - libusb (Linux & FreeBSD) */
223 /* */
224 /* Uses libusb v0.1. To install: */
225 /* - [debian, ubuntu, mint] apt install libusb-dev */
226 /* - [redhat, centos] yum install libusb-devel */
227 /* - [fedora] dnf install libusb-devel */
228 /* - [arch linux] pacman -S libusb-compat */
229 /* - [gentoo] emerge dev-libs/libusb-compat */
230 /* */
231 /* - [freebsd] seems to be preinstalled */
232 /****************************************************************/
233
234 #if defined(USE_LIBUSB)
235
236 // http://libusb.sourceforge.net/doc/index.html
237 #include <usb.h>
238
239 usb_dev_handle * open_usb_device(int vid, int pid)
240 {
241 struct usb_bus *bus;
242 struct usb_device *dev;
243 usb_dev_handle *h;
244 char buf[128];
245 int r;
246
247 usb_init();
248 usb_find_busses();
249 usb_find_devices();
250 //printf_verbose("\nSearching for USB device:\n");
251 for (bus = usb_get_busses(); bus; bus = bus->next) {
252 for (dev = bus->devices; dev; dev = dev->next) {
253 //printf_verbose("bus \"%s\", device \"%s\" vid=%04X, pid=%04X\n",
254 // bus->dirname, dev->filename,
255 // dev->descriptor.idVendor,
256 // dev->descriptor.idProduct
257 //);
258 if (dev->descriptor.idVendor != vid) continue;
259 if (dev->descriptor.idProduct != pid) continue;
260 h = usb_open(dev);
261 if (!h) {
262 printf_verbose("Found device but unable to open\n");
263 continue;
264 }
265 #ifdef LIBUSB_HAS_GET_DRIVER_NP
266 r = usb_get_driver_np(h, 0, buf, sizeof(buf));
267 if (r >= 0) {
268 r = usb_detach_kernel_driver_np(h, 0);
269 if (r < 0) {
270 usb_close(h);
271 printf_verbose("Device is in use by \"%s\" driver\n", buf);
272 continue;
273 }
274 }
275 #endif
276 // Mac OS-X - removing this call to usb_claim_interface() might allow
277 // this to work, even though it is a clear misuse of the libusb API.
278 // normally Apple's IOKit should be used on Mac OS-X
279 #if !defined(MACOSX)
280 r = usb_claim_interface(h, 0);
281 if (r < 0) {
282 usb_close(h);
283 printf_verbose("Unable to claim interface, check USB permissions\n");
284 continue;
285 }
286 #endif
287
288 return h;
289 }
290 }
291 return NULL;
292 }
293
294 static usb_dev_handle *libusb_teensy_handle = NULL;
295
296 int teensy_open(void)
297 {
298 teensy_close();
299 libusb_teensy_handle = open_usb_device(0x16C0, 0x0478);
300
301 if (!libusb_teensy_handle)
302 libusb_teensy_handle = open_usb_device(0x03eb, 0x2067);
303
304 if (libusb_teensy_handle) return 1;
305 return 0;
306 }
307
308 int teensy_write(void *buf, int len, double timeout)
309 {
310 int r;
311
312 if (!libusb_teensy_handle) return 0;
313 while (timeout > 0) {
314 r = usb_control_msg(libusb_teensy_handle, 0x21, 9, 0x0200, 0,
315 (char *)buf, len, (int)(timeout * 1000.0));
316 if (r >= 0) return 1;
317 //printf("teensy_write, r=%d\n", r);
318 usleep(10000);
319 timeout -= 0.01; // TODO: subtract actual elapsed time
320 }
321 return 0;
322 }
323
324 void teensy_close(void)
325 {
326 if (!libusb_teensy_handle) return;
327 usb_release_interface(libusb_teensy_handle, 0);
328 usb_close(libusb_teensy_handle);
329 libusb_teensy_handle = NULL;
330 }
331
332 int hard_reboot(void)
333 {
334 usb_dev_handle *rebootor;
335 int r;
336
337 rebootor = open_usb_device(0x16C0, 0x0477);
338
339 if (!rebootor)
340 rebootor = open_usb_device(0x03eb, 0x2067);
341
342 if (!rebootor) return 0;
343 r = usb_control_msg(rebootor, 0x21, 9, 0x0200, 0, "reboot", 6, 100);
344 usb_release_interface(rebootor, 0);
345 usb_close(rebootor);
346 if (r < 0) return 0;
347 return 1;
348 }
349
350 int soft_reboot(void)
351 {
352 usb_dev_handle *serial_handle = NULL;
353
354 serial_handle = open_usb_device(0x16C0, 0x0483);
355
356 if (!serial_handle)
357 serial_handle = open_usb_device(0x03eb, 0x2067);
358
359 if (!serial_handle) {
360 char *error = usb_strerror();
361 printf("Error opening USB device: %s\n", error);
362 return 0;
363 }
364
365 char reboot_command[] = {0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08};
366 int response = usb_control_msg(serial_handle, 0x21, 0x20, 0, 0, reboot_command, sizeof reboot_command, 10000);
367
368 usb_release_interface(serial_handle, 0);
369 usb_close(serial_handle);
370
371 if (response < 0) {
372 char *error = usb_strerror();
373 printf("Unable to soft reboot with USB error: %s\n", error);
374 return 0;
375 }
376
377 return 1;
378 }
379
380 #endif
381
382
383 /****************************************************************/
384 /* */
385 /* USB Access - Microsoft WIN32 */
386 /* */
387 /****************************************************************/
388
389 #if defined(USE_WIN32)
390
391 // http://msdn.microsoft.com/en-us/library/ms790932.aspx
392 #include <windows.h>
393 #include <setupapi.h>
394 #include <ddk/hidsdi.h>
395 #include <ddk/hidclass.h>
396
397 HANDLE open_usb_device(int vid, int pid)
398 {
399 GUID guid;
400 HDEVINFO info;
401 DWORD index, required_size;
402 SP_DEVICE_INTERFACE_DATA iface;
403 SP_DEVICE_INTERFACE_DETAIL_DATA *details;
404 HIDD_ATTRIBUTES attrib;
405 HANDLE h;
406 BOOL ret;
407
408 HidD_GetHidGuid(&guid);
409 info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
410 if (info == INVALID_HANDLE_VALUE) return NULL;
411 for (index=0; 1 ;index++) {
412 iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
413 ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface);
414 if (!ret) {
415 SetupDiDestroyDeviceInfoList(info);
416 break;
417 }
418 SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL);
419 details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(required_size);
420 if (details == NULL) continue;
421 memset(details, 0, required_size);
422 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
423 ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details,
424 required_size, NULL, NULL);
425 if (!ret) {
426 free(details);
427 continue;
428 }
429 h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE,
430 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
431 FILE_FLAG_OVERLAPPED, NULL);
432 free(details);
433 if (h == INVALID_HANDLE_VALUE) continue;
434 attrib.Size = sizeof(HIDD_ATTRIBUTES);
435 ret = HidD_GetAttributes(h, &attrib);
436 if (!ret) {
437 CloseHandle(h);
438 continue;
439 }
440 if (attrib.VendorID != vid || attrib.ProductID != pid) {
441 CloseHandle(h);
442 continue;
443 }
444 SetupDiDestroyDeviceInfoList(info);
445 return h;
446 }
447 return NULL;
448 }
449
450 int write_usb_device(HANDLE h, void *buf, int len, int timeout)
451 {
452 static HANDLE event = NULL;
453 unsigned char tmpbuf[1089];
454 OVERLAPPED ov;
455 DWORD n, r;
456
457 if (len > sizeof(tmpbuf) - 1) return 0;
458 if (event == NULL) {
459 event = CreateEvent(NULL, TRUE, TRUE, NULL);
460 if (!event) return 0;
461 }
462 ResetEvent(&event);
463 memset(&ov, 0, sizeof(ov));
464 ov.hEvent = event;
465 tmpbuf[0] = 0;
466 memcpy(tmpbuf + 1, buf, len);
467 if (!WriteFile(h, tmpbuf, len + 1, NULL, &ov)) {
468 if (GetLastError() != ERROR_IO_PENDING) return 0;
469 r = WaitForSingleObject(event, timeout);
470 if (r == WAIT_TIMEOUT) {
471 CancelIo(h);
472 return 0;
473 }
474 if (r != WAIT_OBJECT_0) return 0;
475 }
476 if (!GetOverlappedResult(h, &ov, &n, FALSE)) return 0;
477 if (n <= 0) return 0;
478 return 1;
479 }
480
481 void print_win32_err(void)
482 {
483 char buf[256];
484 DWORD err;
485
486 err = GetLastError();
487 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
488 0, buf, sizeof(buf), NULL);
489 printf("err %ld: %s\n", err, buf);
490 }
491
492 static HANDLE win32_teensy_handle = NULL;
493
494 int teensy_open(void)
495 {
496 teensy_close();
497 win32_teensy_handle = open_usb_device(0x16C0, 0x0478);
498
499 if (!win32_teensy_handle)
500 win32_teensy_handle = open_usb_device(0x03eb, 0x2067);
501
502
503 if (win32_teensy_handle) return 1;
504 return 0;
505 }
506
507 int teensy_write(void *buf, int len, double timeout)
508 {
509 int r;
510 uint32_t begin, now, total;
511
512 if (!win32_teensy_handle) return 0;
513 total = (uint32_t)(timeout * 1000.0);
514 begin = timeGetTime();
515 now = begin;
516 do {
517 r = write_usb_device(win32_teensy_handle, buf, len, total - (now - begin));
518 if (r > 0) return 1;
519 Sleep(10);
520 now = timeGetTime();
521 } while (now - begin < total);
522 return 0;
523 }
524
525 void teensy_close(void)
526 {
527 if (!win32_teensy_handle) return;
528 CloseHandle(win32_teensy_handle);
529 win32_teensy_handle = NULL;
530 }
531
532 int hard_reboot(void)
533 {
534 HANDLE rebootor;
535 int r;
536
537 rebootor = open_usb_device(0x16C0, 0x0477);
538
539 if (!rebootor)
540 rebootor = open_usb_device(0x03eb, 0x2067);
541
542 if (!rebootor) return 0;
543 r = write_usb_device(rebootor, "reboot", 6, 100);
544 CloseHandle(rebootor);
545 return r;
546 }
547
548 int soft_reboot(void)
549 {
550 printf("Soft reboot is not implemented for Win32\n");
551 return 0;
552 }
553
554 #endif
555
556
557
558 /****************************************************************/
559 /* */
560 /* USB Access - Apple's IOKit, Mac OS-X */
561 /* */
562 /****************************************************************/
563
564 #if defined(USE_APPLE_IOKIT)
565
566 // http://developer.apple.com/technotes/tn2007/tn2187.html
567 #include <IOKit/IOKitLib.h>
568 #include <IOKit/hid/IOHIDLib.h>
569 #include <IOKit/hid/IOHIDDevice.h>
570
571 struct usb_list_struct {
572 IOHIDDeviceRef ref;
573 int pid;
574 int vid;
575 struct usb_list_struct *next;
576 };
577
578 static struct usb_list_struct *usb_list=NULL;
579 static IOHIDManagerRef hid_manager=NULL;
580
581 void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
582 {
583 CFTypeRef type;
584 struct usb_list_struct *n, *p;
585 int32_t pid, vid;
586
587 if (!dev) return;
588 type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey));
589 if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
590 if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &vid)) return;
591 type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey));
592 if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
593 if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &pid)) return;
594 n = (struct usb_list_struct *)malloc(sizeof(struct usb_list_struct));
595 if (!n) return;
596 //printf("attach callback: vid=%04X, pid=%04X\n", vid, pid);
597 n->ref = dev;
598 n->vid = vid;
599 n->pid = pid;
600 n->next = NULL;
601 if (usb_list == NULL) {
602 usb_list = n;
603 } else {
604 for (p = usb_list; p->next; p = p->next) ;
605 p->next = n;
606 }
607 }
608
609 void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
610 {
611 struct usb_list_struct *p, *tmp, *prev=NULL;
612
613 p = usb_list;
614 while (p) {
615 if (p->ref == dev) {
616 if (prev) {
617 prev->next = p->next;
618 } else {
619 usb_list = p->next;
620 }
621 tmp = p;
622 p = p->next;
623 free(tmp);
624 } else {
625 prev = p;
626 p = p->next;
627 }
628 }
629 }
630
631 void init_hid_manager(void)
632 {
633 CFMutableDictionaryRef dict;
634 IOReturn ret;
635
636 if (hid_manager) return;
637 hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
638 if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) {
639 if (hid_manager) CFRelease(hid_manager);
640 printf_verbose("no HID Manager - maybe this is a pre-Leopard (10.5) system?\n");
641 return;
642 }
643 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
644 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
645 if (!dict) return;
646 IOHIDManagerSetDeviceMatching(hid_manager, dict);
647 CFRelease(dict);
648 IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
649 IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL);
650 IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL);
651 ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
652 if (ret != kIOReturnSuccess) {
653 IOHIDManagerUnscheduleFromRunLoop(hid_manager,
654 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
655 CFRelease(hid_manager);
656 printf_verbose("Error opening HID Manager\n");
657 }
658 }
659
660 static void do_run_loop(void)
661 {
662 while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ;
663 }
664
665 IOHIDDeviceRef open_usb_device(int vid, int pid)
666 {
667 struct usb_list_struct *p;
668 IOReturn ret;
669
670 init_hid_manager();
671 do_run_loop();
672 for (p = usb_list; p; p = p->next) {
673 if (p->vid == vid && p->pid == pid) {
674 ret = IOHIDDeviceOpen(p->ref, kIOHIDOptionsTypeNone);
675 if (ret == kIOReturnSuccess) return p->ref;
676 }
677 }
678 return NULL;
679 }
680
681 void close_usb_device(IOHIDDeviceRef dev)
682 {
683 struct usb_list_struct *p;
684
685 do_run_loop();
686 for (p = usb_list; p; p = p->next) {
687 if (p->ref == dev) {
688 IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone);
689 return;
690 }
691 }
692 }
693
694 static IOHIDDeviceRef iokit_teensy_reference = NULL;
695
696 int teensy_open(void)
697 {
698 teensy_close();
699 iokit_teensy_reference = open_usb_device(0x16C0, 0x0478);
700
701 if (!iokit_teensy_reference)
702 iokit_teensy_reference = open_usb_device(0x03eb, 0x2067);
703
704 if (iokit_teensy_reference) return 1;
705 return 0;
706 }
707
708 int teensy_write(void *buf, int len, double timeout)
709 {
710 IOReturn ret;
711
712 // timeouts do not work on OS-X
713 // IOHIDDeviceSetReportWithCallback is not implemented
714 // even though Apple documents it with a code example!
715 // submitted to Apple on 22-sep-2009, problem ID 7245050
716 if (!iokit_teensy_reference) return 0;
717
718 double start = CFAbsoluteTimeGetCurrent();
719 while (CFAbsoluteTimeGetCurrent() - timeout < start) {
720 ret = IOHIDDeviceSetReport(iokit_teensy_reference,
721 kIOHIDReportTypeOutput, 0, buf, len);
722 if (ret == kIOReturnSuccess) return 1;
723 usleep(10000);
724 }
725
726 return 0;
727 }
728
729 void teensy_close(void)
730 {
731 if (!iokit_teensy_reference) return;
732 close_usb_device(iokit_teensy_reference);
733 iokit_teensy_reference = NULL;
734 }
735
736 int hard_reboot(void)
737 {
738 IOHIDDeviceRef rebootor;
739 IOReturn ret;
740
741 rebootor = open_usb_device(0x16C0, 0x0477);
742
743 if (!rebootor)
744 rebootor = open_usb_device(0x03eb, 0x2067);
745
746 if (!rebootor) return 0;
747 ret = IOHIDDeviceSetReport(rebootor,
748 kIOHIDReportTypeOutput, 0, (uint8_t *)("reboot"), 6);
749 close_usb_device(rebootor);
750 if (ret == kIOReturnSuccess) return 1;
751 return 0;
752 }
753
754 int soft_reboot(void)
755 {
756 printf("Soft reboot is not implemented for OSX\n");
757 return 0;
758 }
759
760 #endif
761
762
763
764 /****************************************************************/
765 /* */
766 /* USB Access - BSD's UHID driver */
767 /* */
768 /****************************************************************/
769
770 #if defined(USE_UHID)
771
772 // Thanks to Todd T Fries for help getting this working on OpenBSD
773 // and to Chris Kuethe for the initial patch to use UHID.
774
775 #include <sys/ioctl.h>
776 #include <fcntl.h>
777 #include <dirent.h>
778 #include <dev/usb/usb.h>
779 #ifndef USB_GET_DEVICEINFO
780 #include <dev/usb/usb_ioctl.h>
781 #endif
782
783 int open_usb_device(int vid, int pid)
784 {
785 int r, fd;
786 DIR *dir;
787 struct dirent *d;
788 struct usb_device_info info;
789 char buf[256];
790
791 dir = opendir("/dev");
792 if (!dir) return -1;
793 while ((d = readdir(dir)) != NULL) {
794 if (strncmp(d->d_name, "uhid", 4) != 0) continue;
795 snprintf(buf, sizeof(buf), "/dev/%s", d->d_name);
796 fd = open(buf, O_RDWR);
797 if (fd < 0) continue;
798 r = ioctl(fd, USB_GET_DEVICEINFO, &info);
799 if (r < 0) {
800 // NetBSD: added in 2004
801 // OpenBSD: added November 23, 2009
802 // FreeBSD: missing (FreeBSD 8.0) - USE_LIBUSB works!
803 die("Error: your uhid driver does not support"
804 " USB_GET_DEVICEINFO, please upgrade!\n");
805 close(fd);
806 closedir(dir);
807 exit(1);
808 }
809 //printf("%s: v=%d, p=%d\n", buf, info.udi_vendorNo, info.udi_productNo);
810 if (info.udi_vendorNo == vid && info.udi_productNo == pid) {
811 closedir(dir);
812 return fd;
813 }
814 close(fd);
815 }
816 closedir(dir);
817 return -1;
818 }
819
820 static int uhid_teensy_fd = -1;
821
822 int teensy_open(void)
823 {
824 teensy_close();
825 uhid_teensy_fd = open_usb_device(0x16C0, 0x0478);
826
827 if (uhid_teensy_fd < 0)
828 uhid_teensy_fd = open_usb_device(0x03eb, 0x2067);
829
830 if (uhid_teensy_fd < 0) return 0;
831 return 1;
832 }
833
834 int teensy_write(void *buf, int len, double timeout)
835 {
836 int r;
837
838 // TODO: imeplement timeout... how??
839 r = write(uhid_teensy_fd, buf, len);
840 if (r == len) return 1;
841 return 0;
842 }
843
844 void teensy_close(void)
845 {
846 if (uhid_teensy_fd >= 0) {
847 close(uhid_teensy_fd);
848 uhid_teensy_fd = -1;
849 }
850 }
851
852 int hard_reboot(void)
853 {
854 int r, rebootor_fd;
855
856 rebootor_fd = open_usb_device(0x16C0, 0x0477);
857
858 if (rebootor_fd < 0)
859 rebootor_fd = open_usb_device(0x03eb, 0x2067);
860
861 if (rebootor_fd < 0) return 0;
862 r = write(rebootor_fd, "reboot", 6);
863 delay(0.1);
864 close(rebootor_fd);
865 if (r == 6) return 1;
866 return 0;
867 }
868
869 int soft_reboot(void)
870 {
871 printf("Soft reboot is not implemented for UHID\n");
872 return 0;
873 }
874
875 #endif
876
877
878
879 /****************************************************************/
880 /* */
881 /* Read Intel Hex File */
882 /* */
883 /****************************************************************/
884
885 // the maximum flash image size we can support
886 // chips with larger memory may be used, but only this
887 // much intel-hex data can be loaded into memory!
888 #define MAX_MEMORY_SIZE 0x100000
889
890 static unsigned char firmware_image[MAX_MEMORY_SIZE];
891 static unsigned char firmware_mask[MAX_MEMORY_SIZE];
892 static int end_record_seen=0;
893 static int byte_count;
894 static unsigned int extended_addr = 0;
895 static int parse_hex_line(char *line);
896
897 int read_intel_hex(const char *filename)
898 {
899 FILE *fp;
900 int i, lineno=0;
901 char buf[1024];
902
903 byte_count = 0;
904 end_record_seen = 0;
905 for (i=0; i<MAX_MEMORY_SIZE; i++) {
906 firmware_image[i] = 0xFF;
907 firmware_mask[i] = 0;
908 }
909 extended_addr = 0;
910
911 fp = fopen(filename, "r");
912 if (fp == NULL) {
913 //printf("Unable to read file %s\n", filename);
914 return -1;
915 }
916 while (!feof(fp)) {
917 *buf = '\0';
918 if (!fgets(buf, sizeof(buf), fp)) break;
919 lineno++;
920 if (*buf) {
921 if (parse_hex_line(buf) == 0) {
922 printf("Warning, HEX parse error line %d\n", lineno);
923 return -2;
924 }
925 }
926 if (end_record_seen) break;
927 if (feof(stdin)) break;
928 }
929 fclose(fp);
930 return byte_count;
931 }
932
933
934 /* from ihex.c, at http://www.pjrc.com/tech/8051/pm2_docs/intel-hex.html */
935
936 /* parses a line of intel hex code, stores the data in bytes[] */
937 /* and the beginning address in addr, and returns a 1 if the */
938 /* line was valid, or a 0 if an error occured. The variable */
939 /* num gets the number of bytes that were stored into bytes[] */
940
941
942 int
943 parse_hex_line(char *line)
944 {
945 int addr, code, num;
946 int sum, len, cksum, i;
947 char *ptr;
948
949 num = 0;
950 if (line[0] != ':') return 0;
951 if (strlen(line) < 11) return 0;
952 ptr = line+1;
953 if (!sscanf(ptr, "%02x", &len)) return 0;
954 ptr += 2;
955 if ((int)strlen(line) < (11 + (len * 2)) ) return 0;
956 if (!sscanf(ptr, "%04x", &addr)) return 0;
957 ptr += 4;
958 /* printf("Line: length=%d Addr=%d\n", len, addr); */
959 if (!sscanf(ptr, "%02x", &code)) return 0;
960 if (addr + extended_addr + len >= MAX_MEMORY_SIZE) return 0;
961 ptr += 2;
962 sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255);
963 if (code != 0) {
964 if (code == 1) {
965 end_record_seen = 1;
966 return 1;
967 }
968 if (code == 2 && len == 2) {
969 if (!sscanf(ptr, "%04x", &i)) return 1;
970 ptr += 4;
971 sum += ((i >> 8) & 255) + (i & 255);
972 if (!sscanf(ptr, "%02x", &cksum)) return 1;
973 if (((sum & 255) + (cksum & 255)) & 255) return 1;
974 extended_addr = i << 4;
975 //printf("ext addr = %05X\n", extended_addr);
976 }
977 if (code == 4 && len == 2) {
978 if (!sscanf(ptr, "%04x", &i)) return 1;
979 ptr += 4;
980 sum += ((i >> 8) & 255) + (i & 255);
981 if (!sscanf(ptr, "%02x", &cksum)) return 1;
982 if (((sum & 255) + (cksum & 255)) & 255) return 1;
983 extended_addr = i << 16;
984 if (code_size > 1048576 && block_size >= 1024 &&
985 extended_addr >= 0x60000000 && extended_addr < 0x60000000 + code_size) {
986 // Teensy 4.0 HEX files have 0x60000000 FlexSPI offset
987 extended_addr -= 0x60000000;
988 }
989 //printf("ext addr = %08X\n", extended_addr);
990 }
991 return 1; // non-data line
992 }
993 byte_count += len;
994 while (num != len) {
995 if (sscanf(ptr, "%02x", &i) != 1) return 0;
996 i &= 255;
997 firmware_image[addr + extended_addr + num] = i;
998 firmware_mask[addr + extended_addr + num] = 1;
999 ptr += 2;
1000 sum += i;
1001 (num)++;
1002 if (num >= 256) return 0;
1003 }
1004 if (!sscanf(ptr, "%02x", &cksum)) return 0;
1005 if (((sum & 255) + (cksum & 255)) & 255) return 0; /* checksum error */
1006 return 1;
1007 }
1008
1009 int ihex_bytes_within_range(int begin, int end)
1010 {
1011 int i;
1012
1013 if (begin < 0 || begin >= MAX_MEMORY_SIZE ||
1014 end < 0 || end >= MAX_MEMORY_SIZE) {
1015 return 0;
1016 }
1017 for (i=begin; i<=end; i++) {
1018 if (firmware_mask[i]) return 1;
1019 }
1020 return 0;
1021 }
1022
1023 void ihex_get_data(int addr, int len, unsigned char *bytes)
1024 {
1025 int i;
1026
1027 if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) {
1028 for (i=0; i<len; i++) {
1029 bytes[i] = 255;
1030 }
1031 return;
1032 }
1033 for (i=0; i<len; i++) {
1034 if (firmware_mask[addr]) {
1035 bytes[i] = firmware_image[addr];
1036 } else {
1037 bytes[i] = 255;
1038 }
1039 addr++;
1040 }
1041 }
1042
1043 int memory_is_blank(int addr, int block_size)
1044 {
1045 if (addr < 0 || addr > MAX_MEMORY_SIZE) return 1;
1046
1047 while (block_size && addr < MAX_MEMORY_SIZE) {
1048 if (firmware_mask[addr] && firmware_image[addr] != 255) return 0;
1049 addr++;
1050 block_size--;
1051 }
1052 return 1;
1053 }
1054
1055
1056
1057
1058 /****************************************************************/
1059 /* */
1060 /* Misc Functions */
1061 /* */
1062 /****************************************************************/
1063
1064 int printf_verbose(const char *format, ...)
1065 {
1066 va_list ap;
1067 int r;
1068
1069 va_start(ap, format);
1070 if (verbose) {
1071 r = vprintf(format, ap);
1072 fflush(stdout);
1073 return r;
1074 }
1075 return 0;
1076 }
1077
1078 void delay(double seconds)
1079 {
1080 #ifdef WIN32
1081 Sleep(seconds * 1000.0);
1082 #else
1083 usleep(seconds * 1000000.0);
1084 #endif
1085 }
1086
1087 void die(const char *str, ...)
1088 {
1089 va_list ap;
1090
1091 va_start(ap, str);
1092 vfprintf(stderr, str, ap);
1093 fprintf(stderr, "\n");
1094 exit(1);
1095 }
1096
1097 #if defined(WIN32)
1098 #define strcasecmp stricmp
1099 #endif
1100
1101
1102 static const struct {
1103 const char *name;
1104 int code_size;
1105 int block_size;
1106 } MCUs[] = {
1107 {"at90usb162", 15872, 128},
1108 {"atmega32u4", 32256, 128},
1109 {"at90usb646", 64512, 256},
1110 {"at90usb1286", 130048, 256},
1111 #if defined(USE_LIBUSB) || defined(USE_APPLE_IOKIT) || defined(USE_WIN32)
1112 {"mkl26z64", 63488, 512},
1113 {"mk20dx128", 131072, 1024},
1114 {"mk20dx256", 262144, 1024},
1115 {"mk66fx1m0", 1048576, 1024},
1116 {"mk64fx512", 524288, 1024},
1117 {"imxrt1062", 2031616, 1024},
1118
1119 // Add duplicates that match friendly Teensy Names
1120 // Match board names in boards.txt
1121 {"TEENSY2", 32256, 128},
1122 {"TEENSY2PP", 130048, 256},
1123 {"TEENSYLC", 63488, 512},
1124 {"TEENSY30", 131072, 1024},
1125 {"TEENSY31", 262144, 1024},
1126 {"TEENSY32", 262144, 1024},
1127 {"TEENSY35", 524288, 1024},
1128 {"TEENSY36", 1048576, 1024},
1129 {"TEENSY40", 2031616, 1024},
1130 {"TEENSY41", 8126464, 1024},
1131 #endif
1132 {NULL, 0, 0},
1133 };
1134
1135
1136 void list_mcus()
1137 {
1138 int i;
1139 printf("Supported MCUs are:\n");
1140 for(i=0; MCUs[i].name != NULL; i++)
1141 printf(" - %s\n", MCUs[i].name);
1142 exit(1);
1143 }
1144
1145
1146 void read_mcu(char *name)
1147 {
1148 int i;
1149
1150 if(name == NULL) {
1151 fprintf(stderr, "No MCU specified.\n");
1152 list_mcus();
1153 }
1154
1155 for(i=0; MCUs[i].name != NULL; i++) {
1156 if(strcasecmp(name, MCUs[i].name) == 0) {
1157 code_size = MCUs[i].code_size;
1158 block_size = MCUs[i].block_size;
1159 return;
1160 }
1161 }
1162
1163 fprintf(stderr, "Unknown MCU type \"%s\"\n", name);
1164 list_mcus();
1165 }
1166
1167
1168 void parse_flag(char *arg)
1169 {
1170 int i;
1171 for(i=1; arg[i]; i++) {
1172 switch(arg[i]) {
1173 case 'w': wait_for_device_to_appear = 1; break;
1174 case 'r': hard_reboot_device = 1; break;
1175 case 's': soft_reboot_device = 1; break;
1176 case 'n': reboot_after_programming = 0; break;
1177 case 'v': verbose = 1; break;
1178 case 'b': boot_only = 1; break;
1179 default:
1180 fprintf(stderr, "Unknown flag '%c'\n\n", arg[i]);
1181 usage(NULL);
1182 }
1183 }
1184 }
1185
1186
1187 void parse_options(int argc, char **argv)
1188 {
1189 int i;
1190 char *arg;
1191
1192 for (i=1; i<argc; i++) {
1193 arg = argv[i];
1194
1195 //backward compatibility with previous versions.
1196 if(strncmp(arg, "-mmcu=", 6) == 0) {
1197 read_mcu(strchr(arg, '=') + 1);
1198 }
1199
1200 else if(arg[0] == '-') {
1201 if(arg[1] == '-') {
1202 char *name = &arg[2];
1203 char *val = strchr(name, '=');
1204 if(val == NULL) {
1205 //value must be the next string.
1206 val = argv[++i];
1207 }
1208 else {
1209 //we found an =, so split the string at it.
1210 *val = '\0';
1211 val = &val[1];
1212 }
1213
1214 if(strcasecmp(name, "help") == 0) usage(NULL);
1215 else if(strcasecmp(name, "mcu") == 0) read_mcu(val);
1216 else if(strcasecmp(name, "list-mcus") == 0) list_mcus();
1217 else {
1218 fprintf(stderr, "Unknown option \"%s\"\n\n", arg);
1219 usage(NULL);
1220 }
1221 }
1222 else parse_flag(arg);
1223 }
1224 else filename = arg;
1225 }
1226 }
1227
1228 void boot(unsigned char *buf, int write_size)
1229 {
1230 printf_verbose("Booting\n");
1231 memset(buf, 0, write_size);
1232 buf[0] = 0xFF;
1233 buf[1] = 0xFF;
1234 buf[2] = 0xFF;
1235 teensy_write(buf, write_size, 0.5);
1236 }