]> git.donarmstrong.com Git - qmk_firmware.git/blob - lib/lufa/Bootloaders/HID/HostLoaderApp/hid_bootloader_cli.c
Merge commit '60b30c036397cb5627fa374bb930794b225daa29' as 'lib/lufa'
[qmk_firmware.git] / lib / lufa / 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-2010, PJRC.COM, LLC
11  *
12  *
13  * You may redistribute this program and/or modify it under the terms
14  * of the GNU General Public License as published by the Free Software
15  * Foundation, version 3 of the License.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program.  If not, see http://www.gnu.org/licenses/
24  */
25
26 /* Want to incorporate this code into a proprietary application??
27  * Just email paul@pjrc.com to ask.  Usually it's not a problem,
28  * but you do need to ask to use this code in any way other than
29  * those permitted by the GNU General Public License, version 3  */
30
31 /* For non-root permissions on ubuntu or similar udev-based linux
32  * http://www.pjrc.com/teensy/49-teensy.rules
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(void)
43 {
44         fprintf(stderr, "Usage: hid_bootloader_cli -mmcu=<MCU> [-w] [-h] [-n] [-v] <file.hex>\n");
45         fprintf(stderr, "\t-w : Wait for device to appear\n");
46         fprintf(stderr, "\t-r : Use hard reboot if device not online\n");
47         fprintf(stderr, "\t-n : No reboot after programming\n");
48         fprintf(stderr, "\t-v : Verbose output\n");
49         fprintf(stderr, "\n<MCU> = atmegaXXuY or at90usbXXXY");
50
51         fprintf(stderr, "\nFor support and more information, please visit:\n");
52         fprintf(stderr, "http://www.lufa-lib.org\n");
53
54         fprintf(stderr, "\nBased on the TeensyHID command line programmer software:\n");
55         fprintf(stderr, "http://www.pjrc.com/teensy/loader_cli.html\n");
56         exit(1);
57 }
58
59 // USB Access Functions
60 int teensy_open(void);
61 int teensy_write(void *buf, int len, double timeout);
62 void teensy_close(void);
63 int hard_reboot(void);
64
65 // Intel Hex File Functions
66 int read_intel_hex(const char *filename);
67 int ihex_bytes_within_range(int begin, int end);
68 void ihex_get_data(int addr, int len, unsigned char *bytes);
69
70 // Misc stuff
71 int printf_verbose(const char *format, ...);
72 void delay(double seconds);
73 void die(const char *str, ...);
74 void parse_options(int argc, char **argv);
75
76 // options (from user via command line args)
77 int wait_for_device_to_appear = 0;
78 int hard_reboot_device = 0;
79 int reboot_after_programming = 1;
80 int verbose = 0;
81 int code_size = 0, block_size = 0;
82 const char *filename=NULL;
83
84
85 /****************************************************************/
86 /*                                                              */
87 /*                       Main Program                           */
88 /*                                                              */
89 /****************************************************************/
90
91 int main(int argc, char **argv)
92 {
93         unsigned char buf[260];
94         int num, addr, r, first_block=1, waited=0;
95
96         // parse command line arguments
97         parse_options(argc, argv);
98         if (!filename) {
99                 fprintf(stderr, "Filename must be specified\n\n");
100                 usage();
101         }
102         if (!code_size) {
103                 fprintf(stderr, "MCU type must be specified\n\n");
104                 usage();
105         }
106         printf_verbose("Teensy Loader, Command Line, Version 2.0\n");
107
108         // read the intel hex file
109         // this is done first so any error is reported before using USB
110         num = read_intel_hex(filename);
111         if (num < 0) die("error reading intel hex file \"%s\"", filename);
112         printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
113                 filename, num, (double)num / (double)code_size * 100.0);
114
115         // open the USB device
116         while (1) {
117                 if (teensy_open()) break;
118                 if (hard_reboot_device) {
119                         if (!hard_reboot()) die("Unable to find rebootor\n");
120                         printf_verbose("Hard Reboot performed\n");
121                         hard_reboot_device = 0; // only hard reboot once
122                         wait_for_device_to_appear = 1;
123                 }
124                 if (!wait_for_device_to_appear) die("Unable to open device\n");
125                 if (!waited) {
126                         printf_verbose("Waiting for Teensy device...\n");
127                         printf_verbose(" (hint: press the reset button)\n");
128                         waited = 1;
129                 }
130                 delay(0.25);
131         }
132         printf_verbose("Found HalfKay Bootloader\n");
133
134         // if we waited for the device, read the hex file again
135         // perhaps it changed while we were waiting?
136         if (waited) {
137                 num = read_intel_hex(filename);
138                 if (num < 0) die("error reading intel hex file \"%s\"", filename);
139                 printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
140                         filename, num, (double)num / (double)code_size * 100.0);
141         }
142
143         // program the data
144         printf_verbose("Programming");
145         fflush(stdout);
146         for (addr = 0; addr < code_size; addr += block_size) {
147                 if (addr > 0 && !ihex_bytes_within_range(addr, addr + block_size - 1)) {
148                         // don't waste time on blocks that are unused,
149                         // but always do the first one to erase the chip
150                         continue;
151                 }
152                 printf_verbose(".");
153                 if (code_size < 0x10000) {
154                         buf[0] = addr & 255;
155                         buf[1] = (addr >> 8) & 255;
156                 } else {
157                         buf[0] = (addr >> 8) & 255;
158                         buf[1] = (addr >> 16) & 255;
159                 }
160                 ihex_get_data(addr, block_size, buf + 2);
161                 r = teensy_write(buf, block_size + 2, first_block ? 3.0 : 0.25);
162                 if (!r) die("error writing to Teensy\n");
163                 first_block = 0;
164         }
165         printf_verbose("\n");
166
167         // reboot to the user's new code
168         if (reboot_after_programming) {
169                 printf_verbose("Booting\n");
170                 buf[0] = 0xFF;
171                 buf[1] = 0xFF;
172                 memset(buf + 2, 0, sizeof(buf) - 2);
173                 teensy_write(buf, block_size + 2, 0.25);
174         }
175         teensy_close();
176         return 0;
177 }
178
179
180
181
182 /****************************************************************/
183 /*                                                              */
184 /*             USB Access - libusb (Linux & FreeBSD)            */
185 /*                                                              */
186 /****************************************************************/
187
188 #if defined(USE_LIBUSB)
189
190 // http://libusb.sourceforge.net/doc/index.html
191 #include <usb.h>
192
193 usb_dev_handle * open_usb_device(int vid, int pid)
194 {
195         struct usb_bus *bus;
196         struct usb_device *dev;
197         usb_dev_handle *h;
198         #ifdef LIBUSB_HAS_GET_DRIVER_NP
199         char buf[128];
200         #endif
201         int r;
202
203         usb_init();
204         usb_find_busses();
205         usb_find_devices();
206         //printf_verbose("\nSearching for USB device:\n");
207         for (bus = usb_get_busses(); bus; bus = bus->next) {
208                 for (dev = bus->devices; dev; dev = dev->next) {
209                         //printf_verbose("bus \"%s\", device \"%s\" vid=%04X, pid=%04X\n",
210                         //      bus->dirname, dev->filename,
211                         //      dev->descriptor.idVendor,
212                         //      dev->descriptor.idProduct
213                         //);
214                         if (dev->descriptor.idVendor != vid) continue;
215                         if (dev->descriptor.idProduct != pid) continue;
216                         h = usb_open(dev);
217                         if (!h) {
218                                 printf_verbose("Found device but unable to open");
219                                 continue;
220                         }
221                         #ifdef LIBUSB_HAS_GET_DRIVER_NP
222                         r = usb_get_driver_np(h, 0, buf, sizeof(buf));
223                         if (r >= 0) {
224                                 r = usb_detach_kernel_driver_np(h, 0);
225                                 if (r < 0) {
226                                         usb_close(h);
227                                         printf_verbose("Device is in use by \"%s\" driver", buf);
228                                         continue;
229                                 }
230                         }
231                         #endif
232                         // Mac OS-X - removing this call to usb_claim_interface() might allow
233                         // this to work, even though it is a clear misuse of the libusb API.
234                         // normally Apple's IOKit should be used on Mac OS-X
235                         r = usb_claim_interface(h, 0);
236                         if (r < 0) {
237                                 usb_close(h);
238                                 printf_verbose("Unable to claim interface, check USB permissions");
239                                 continue;
240                         }
241                         return h;
242                 }
243         }
244         return NULL;
245 }
246
247 static usb_dev_handle *libusb_teensy_handle = NULL;
248
249 int teensy_open(void)
250 {
251         teensy_close();
252         libusb_teensy_handle = open_usb_device(0x16C0, 0x0478);
253
254         if (!libusb_teensy_handle)
255                 libusb_teensy_handle = open_usb_device(0x03eb, 0x2067);
256
257         if (!libusb_teensy_handle) return 0;
258         return 1;
259 }
260
261 int teensy_write(void *buf, int len, double timeout)
262 {
263         int r;
264
265         if (!libusb_teensy_handle) return 0;
266         r = usb_control_msg(libusb_teensy_handle, 0x21, 9, 0x0200, 0, (char *)buf,
267                 len, (int)(timeout * 1000.0));
268         if (r < 0) return 0;
269         return 1;
270 }
271
272 void teensy_close(void)
273 {
274         if (!libusb_teensy_handle) return;
275         usb_release_interface(libusb_teensy_handle, 0);
276         usb_close(libusb_teensy_handle);
277         libusb_teensy_handle = NULL;
278 }
279
280 int hard_reboot(void)
281 {
282         usb_dev_handle *rebootor;
283         int r;
284
285         rebootor = open_usb_device(0x16C0, 0x0477);
286
287         if (!rebootor)
288                 rebootor = open_usb_device(0x03eb, 0x2067);
289
290         if (!rebootor) return 0;
291         r = usb_control_msg(rebootor, 0x21, 9, 0x0200, 0, "reboot", 6, 100);
292         usb_release_interface(rebootor, 0);
293         usb_close(rebootor);
294         if (r < 0) return 0;
295         return 1;
296 }
297
298 #endif
299
300
301 /****************************************************************/
302 /*                                                              */
303 /*               USB Access - Microsoft WIN32                   */
304 /*                                                              */
305 /****************************************************************/
306
307 #if defined(USE_WIN32)
308
309 // http://msdn.microsoft.com/en-us/library/ms790932.aspx
310 #include <windows.h>
311 #include <setupapi.h>
312 #include <ddk/hidsdi.h>
313 #include <ddk/hidclass.h>
314
315 HANDLE open_usb_device(int vid, int pid)
316 {
317         GUID guid;
318         HDEVINFO info;
319         DWORD index, required_size;
320         SP_DEVICE_INTERFACE_DATA iface;
321         SP_DEVICE_INTERFACE_DETAIL_DATA *details;
322         HIDD_ATTRIBUTES attrib;
323         HANDLE h;
324         BOOL ret;
325
326         HidD_GetHidGuid(&guid);
327         info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
328         if (info == INVALID_HANDLE_VALUE) return NULL;
329         for (index=0; 1 ;index++) {
330                 iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
331                 ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface);
332                 if (!ret) {
333                         SetupDiDestroyDeviceInfoList(info);
334                         break;
335                 }
336                 SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL);
337                 details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(required_size);
338                 if (details == NULL) continue;
339                 memset(details, 0, required_size);
340                 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
341                 ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details,
342                         required_size, NULL, NULL);
343                 if (!ret) {
344                         free(details);
345                         continue;
346                 }
347                 h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE,
348                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
349                         FILE_FLAG_OVERLAPPED, NULL);
350                 free(details);
351                 if (h == INVALID_HANDLE_VALUE) continue;
352                 attrib.Size = sizeof(HIDD_ATTRIBUTES);
353                 ret = HidD_GetAttributes(h, &attrib);
354                 if (!ret) {
355                         CloseHandle(h);
356                         continue;
357                 }
358                 if (attrib.VendorID != vid || attrib.ProductID != pid) {
359                         CloseHandle(h);
360                         continue;
361                 }
362                 SetupDiDestroyDeviceInfoList(info);
363                 return h;
364         }
365         return NULL;
366 }
367
368 int write_usb_device(HANDLE h, void *buf, int len, int timeout)
369 {
370         static HANDLE event = NULL;
371         unsigned char tmpbuf[1040];
372         OVERLAPPED ov;
373         DWORD n, r;
374
375         if (len > sizeof(tmpbuf) - 1) return 0;
376         if (event == NULL) {
377                 event = CreateEvent(NULL, TRUE, TRUE, NULL);
378                 if (!event) return 0;
379         }
380         ResetEvent(&event);
381         memset(&ov, 0, sizeof(ov));
382         ov.hEvent = event;
383         tmpbuf[0] = 0;
384         memcpy(tmpbuf + 1, buf, len);
385         if (!WriteFile(h, tmpbuf, len + 1, NULL, &ov)) {
386                 if (GetLastError() != ERROR_IO_PENDING) return 0;
387                 r = WaitForSingleObject(event, timeout);
388                 if (r == WAIT_TIMEOUT) {
389                         CancelIo(h);
390                         return 0;
391                 }
392                 if (r != WAIT_OBJECT_0) return 0;
393         }
394         if (!GetOverlappedResult(h, &ov, &n, FALSE)) return 0;
395         return 1;
396 }
397
398 static HANDLE win32_teensy_handle = NULL;
399
400 int teensy_open(void)
401 {
402         teensy_close();
403         win32_teensy_handle = open_usb_device(0x16C0, 0x0478);
404
405         if (!win32_teensy_handle)
406                 win32_teensy_handle = open_usb_device(0x03eb, 0x2067);
407
408         if (!win32_teensy_handle) return 0;
409         return 1;
410 }
411
412 int teensy_write(void *buf, int len, double timeout)
413 {
414         int r;
415         if (!win32_teensy_handle) return 0;
416         r = write_usb_device(win32_teensy_handle, buf, len, (int)(timeout * 1000.0));
417         return r;
418 }
419
420 void teensy_close(void)
421 {
422         if (!win32_teensy_handle) return;
423         CloseHandle(win32_teensy_handle);
424         win32_teensy_handle = NULL;
425 }
426
427 int hard_reboot(void)
428 {
429         HANDLE rebootor;
430         int r;
431
432         rebootor = open_usb_device(0x16C0, 0x0477);
433
434         if (!rebootor)
435                 rebootor = open_usb_device(0x03eb, 0x2067);
436
437         if (!rebootor) return 0;
438         r = write_usb_device(rebootor, "reboot", 6, 100);
439         CloseHandle(rebootor);
440         return r;
441 }
442
443 #endif
444
445
446
447 /****************************************************************/
448 /*                                                              */
449 /*             USB Access - Apple's IOKit, Mac OS-X             */
450 /*                                                              */
451 /****************************************************************/
452
453 #if defined(USE_APPLE_IOKIT)
454
455 // http://developer.apple.com/technotes/tn2007/tn2187.html
456 #include <IOKit/IOKitLib.h>
457 #include <IOKit/hid/IOHIDLib.h>
458 #include <IOKit/hid/IOHIDDevice.h>
459
460 struct usb_list_struct {
461         IOHIDDeviceRef ref;
462         int pid;
463         int vid;
464         struct usb_list_struct *next;
465 };
466
467 static struct usb_list_struct *usb_list=NULL;
468 static IOHIDManagerRef hid_manager=NULL;
469
470 void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
471 {
472         CFTypeRef type;
473         struct usb_list_struct *n, *p;
474         int32_t pid, vid;
475
476         if (!dev) return;
477         type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey));
478         if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
479         if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &vid)) return;
480         type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey));
481         if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
482         if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &pid)) return;
483         n = (struct usb_list_struct *)malloc(sizeof(struct usb_list_struct));
484         if (!n) return;
485         //printf("attach callback: vid=%04X, pid=%04X\n", vid, pid);
486         n->ref = dev;
487         n->vid = vid;
488         n->pid = pid;
489         n->next = NULL;
490         if (usb_list == NULL) {
491                 usb_list = n;
492         } else {
493                 for (p = usb_list; p->next; p = p->next) ;
494                 p->next = n;
495         }
496 }
497
498 void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
499 {
500         struct usb_list_struct *p, *tmp, *prev=NULL;
501
502         p = usb_list;
503         while (p) {
504                 if (p->ref == dev) {
505                         if (prev) {
506                                 prev->next = p->next;
507                         } else {
508                                 usb_list = p->next;
509                         }
510                         tmp = p;
511                         p = p->next;
512                         free(tmp);
513                 } else {
514                         prev = p;
515                         p = p->next;
516                 }
517         }
518 }
519
520 void init_hid_manager(void)
521 {
522         CFMutableDictionaryRef dict;
523         IOReturn ret;
524
525         if (hid_manager) return;
526         hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
527         if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) {
528                 if (hid_manager) CFRelease(hid_manager);
529                 printf_verbose("no HID Manager - maybe this is a pre-Leopard (10.5) system?\n");
530                 return;
531         }
532         dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
533                 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
534         if (!dict) return;
535         IOHIDManagerSetDeviceMatching(hid_manager, dict);
536         CFRelease(dict);
537         IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
538         IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL);
539         IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL);
540         ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
541         if (ret != kIOReturnSuccess) {
542                 IOHIDManagerUnscheduleFromRunLoop(hid_manager,
543                         CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
544                 CFRelease(hid_manager);
545                 printf_verbose("Error opening HID Manager");
546         }
547 }
548
549 static void do_run_loop(void)
550 {
551         while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ;
552 }
553
554 IOHIDDeviceRef open_usb_device(int vid, int pid)
555 {
556         struct usb_list_struct *p;
557         IOReturn ret;
558
559         init_hid_manager();
560         do_run_loop();
561         for (p = usb_list; p; p = p->next) {
562                 if (p->vid == vid && p->pid == pid) {
563                         ret = IOHIDDeviceOpen(p->ref, kIOHIDOptionsTypeNone);
564                         if (ret == kIOReturnSuccess) return p->ref;
565                 }
566         }
567         return NULL;
568 }
569
570 void close_usb_device(IOHIDDeviceRef dev)
571 {
572         struct usb_list_struct *p;
573
574         do_run_loop();
575         for (p = usb_list; p; p = p->next) {
576                 if (p->ref == dev) {
577                         IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone);
578                         return;
579                 }
580         }
581 }
582
583 static IOHIDDeviceRef iokit_teensy_reference = NULL;
584
585 int teensy_open(void)
586 {
587         teensy_close();
588         iokit_teensy_reference = open_usb_device(0x16C0, 0x0478);
589
590         if (!iokit_teensy_reference)
591                 iokit_teensy_reference = open_usb_device(0x03eb, 0x2067);
592
593         if (!iokit_teensy_reference) return 0;
594         return 1;
595 }
596
597 int teensy_write(void *buf, int len, double timeout)
598 {
599         IOReturn ret;
600
601         // timeouts do not work on OS-X
602         // IOHIDDeviceSetReportWithCallback is not implemented
603         // even though Apple documents it with a code example!
604         // submitted to Apple on 22-sep-2009, problem ID 7245050
605         if (!iokit_teensy_reference) return 0;
606         ret = IOHIDDeviceSetReport(iokit_teensy_reference,
607                 kIOHIDReportTypeOutput, 0, buf, len);
608         if (ret == kIOReturnSuccess) return 1;
609         return 0;
610 }
611
612 void teensy_close(void)
613 {
614         if (!iokit_teensy_reference) return;
615         close_usb_device(iokit_teensy_reference);
616         iokit_teensy_reference = NULL;
617 }
618
619 int hard_reboot(void)
620 {
621         IOHIDDeviceRef rebootor;
622         IOReturn ret;
623
624         rebootor = open_usb_device(0x16C0, 0x0477);
625
626         if (!rebootor)
627                 rebootor = open_usb_device(0x03eb, 0x2067);
628
629         if (!rebootor) return 0;
630         ret = IOHIDDeviceSetReport(rebootor,
631                 kIOHIDReportTypeOutput, 0, (uint8_t *)("reboot"), 6);
632         close_usb_device(rebootor);
633         if (ret == kIOReturnSuccess) return 1;
634         return 0;
635 }
636
637 #endif
638
639
640
641 /****************************************************************/
642 /*                                                              */
643 /*              USB Access - BSD's UHID driver                  */
644 /*                                                              */
645 /****************************************************************/
646
647 #if defined(USE_UHID)
648
649 // Thanks to Todd T Fries for help getting this working on OpenBSD
650 // and to Chris Kuethe for the initial patch to use UHID.
651
652 #include <sys/ioctl.h>
653 #include <fcntl.h>
654 #include <dirent.h>
655 #include <dev/usb/usb.h>
656 #ifndef USB_GET_DEVICEINFO
657 #include <dev/usb/usb_ioctl.h>
658 #endif
659
660 #ifndef USB_GET_DEVICEINFO
661 # define USB_GET_DEVICEINFO 0
662 # error The USB_GET_DEVICEINFO ioctl() value is not defined for your system.
663 #endif
664
665 int open_usb_device(int vid, int pid)
666 {
667         int r, fd;
668         DIR *dir;
669         struct dirent *d;
670         struct usb_device_info info;
671         char buf[256];
672
673         dir = opendir("/dev");
674         if (!dir) return -1;
675         while ((d = readdir(dir)) != NULL) {
676                 if (strncmp(d->d_name, "uhid", 4) != 0) continue;
677                 snprintf(buf, sizeof(buf), "/dev/%s", d->d_name);
678                 fd = open(buf, O_RDWR);
679                 if (fd < 0) continue;
680                 r = ioctl(fd, USB_GET_DEVICEINFO, &info);
681                 if (r < 0) {
682                         // NetBSD: added in 2004
683                         // OpenBSD: added November 23, 2009
684                         // FreeBSD: missing (FreeBSD 8.0) - USE_LIBUSB works!
685                         die("Error: your uhid driver does not support"
686                           " USB_GET_DEVICEINFO, please upgrade!\n");
687                         close(fd);
688                         closedir(dir);
689                         exit(1);
690                 }
691                 //printf("%s: v=%d, p=%d\n", buf, info.udi_vendorNo, info.udi_productNo);
692                 if (info.udi_vendorNo == vid && info.udi_productNo == pid) {
693                         closedir(dir);
694                         return fd;
695                 }
696                 close(fd);
697         }
698         closedir(dir);
699         return -1;
700 }
701
702 static int uhid_teensy_fd = -1;
703
704 int teensy_open(void)
705 {
706         teensy_close();
707         uhid_teensy_fd = open_usb_device(0x16C0, 0x0478);
708
709         if (uhid_teensy_fd < 0)
710                 uhid_teensy_fd = open_usb_device(0x03eb, 0x2067);
711
712         if (uhid_teensy_fd < 0) return 0;
713         return 1;
714 }
715
716 int teensy_write(void *buf, int len, double timeout)
717 {
718         int r;
719
720         // TODO: implement timeout... how??
721         r = write(uhid_teensy_fd, buf, len);
722         if (r == len) return 1;
723         return 0;
724 }
725
726 void teensy_close(void)
727 {
728         if (uhid_teensy_fd >= 0) {
729                 close(uhid_teensy_fd);
730                 uhid_teensy_fd = -1;
731         }
732 }
733
734 int hard_reboot(void)
735 {
736         int r, rebootor_fd;
737
738         rebootor_fd = open_usb_device(0x16C0, 0x0477);
739
740         if (rebootor_fd < 0)
741                 rebootor_fd = open_usb_device(0x03eb, 0x2067);
742
743         if (rebootor_fd < 0) return 0;
744         r = write(rebootor_fd, "reboot", 6);
745         delay(0.1);
746         close(rebootor_fd);
747         if (r == 6) return 1;
748         return 0;
749 }
750
751 #endif
752
753
754
755 /****************************************************************/
756 /*                                                              */
757 /*                     Read Intel Hex File                      */
758 /*                                                              */
759 /****************************************************************/
760
761 // the maximum flash image size we can support
762 // chips with larger memory may be used, but only this
763 // much intel-hex data can be loaded into memory!
764 #define MAX_MEMORY_SIZE 0x10000
765
766 static unsigned char firmware_image[MAX_MEMORY_SIZE];
767 static unsigned char firmware_mask[MAX_MEMORY_SIZE];
768 static int end_record_seen=0;
769 static int byte_count;
770 static unsigned int extended_addr = 0;
771 static int parse_hex_line(char *line);
772
773 int read_intel_hex(const char *filename)
774 {
775         FILE *fp;
776         int i, lineno=0;
777         char buf[1024];
778
779         byte_count = 0;
780         end_record_seen = 0;
781         for (i=0; i<MAX_MEMORY_SIZE; i++) {
782                 firmware_image[i] = 0xFF;
783                 firmware_mask[i] = 0;
784         }
785         extended_addr = 0;
786
787         fp = fopen(filename, "r");
788         if (fp == NULL) {
789                 //printf("Unable to read file %s\n", filename);
790                 return -1;
791         }
792         while (!feof(fp)) {
793                 *buf = '\0';
794                 if (!fgets(buf, sizeof(buf), fp)) break;
795                 lineno++;
796                 if (*buf) {
797                         if (parse_hex_line(buf) == 0) {
798                                 //printf("Warning, parse error line %d\n", lineno);
799                                 fclose(fp);
800                                 return -2;
801                         }
802                 }
803                 if (end_record_seen) break;
804                 if (feof(stdin)) break;
805         }
806         fclose(fp);
807         return byte_count;
808 }
809
810
811 /* from ihex.c, at http://www.pjrc.com/tech/8051/pm2_docs/intel-hex.html */
812
813 /* parses a line of intel hex code, stores the data in bytes[] */
814 /* and the beginning address in addr, and returns a 1 if the */
815 /* line was valid, or a 0 if an error occurred.  The variable */
816 /* num gets the number of bytes that were stored into bytes[] */
817
818
819 int
820 parse_hex_line(char *line)
821 {
822         int addr, code, num;
823         int sum, len, cksum, i;
824         char *ptr;
825
826         num = 0;
827         if (line[0] != ':') return 0;
828         if (strlen(line) < 11) return 0;
829         ptr = line+1;
830         if (!sscanf(ptr, "%02x", &len)) return 0;
831         ptr += 2;
832         if ((int)strlen(line) < (11 + (len * 2)) ) return 0;
833         if (!sscanf(ptr, "%04x", &addr)) return 0;
834         ptr += 4;
835           /* printf("Line: length=%d Addr=%d\n", len, addr); */
836         if (!sscanf(ptr, "%02x", &code)) return 0;
837         if (addr + extended_addr + len >= MAX_MEMORY_SIZE) return 0;
838         ptr += 2;
839         sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255);
840         if (code != 0) {
841                 if (code == 1) {
842                         end_record_seen = 1;
843                         return 1;
844                 }
845                 if (code == 2 && len == 2) {
846                         if (!sscanf(ptr, "%04x", &i)) return 1;
847                         ptr += 4;
848                         sum += ((i >> 8) & 255) + (i & 255);
849                         if (!sscanf(ptr, "%02x", &cksum)) return 1;
850                         if (((sum & 255) + (cksum & 255)) & 255) return 1;
851                         extended_addr = i << 4;
852                         //printf("ext addr = %05X\n", extended_addr);
853                 }
854                 if (code == 4 && len == 2) {
855                         if (!sscanf(ptr, "%04x", &i)) return 1;
856                         ptr += 4;
857                         sum += ((i >> 8) & 255) + (i & 255);
858                         if (!sscanf(ptr, "%02x", &cksum)) return 1;
859                         if (((sum & 255) + (cksum & 255)) & 255) return 1;
860                         extended_addr = i << 16;
861                         //printf("ext addr = %08X\n", extended_addr);
862                 }
863                 return 1;       // non-data line
864         }
865         byte_count += len;
866         while (num != len) {
867                 if (sscanf(ptr, "%02x", &i) != 1) return 0;
868                 i &= 255;
869                 firmware_image[addr + extended_addr + num] = i;
870                 firmware_mask[addr + extended_addr + num] = 1;
871                 ptr += 2;
872                 sum += i;
873                 (num)++;
874                 if (num >= 256) return 0;
875         }
876         if (!sscanf(ptr, "%02x", &cksum)) return 0;
877         if (((sum & 255) + (cksum & 255)) & 255) return 0; /* checksum error */
878         return 1;
879 }
880
881 int ihex_bytes_within_range(int begin, int end)
882 {
883         int i;
884
885         if (begin < 0 || begin >= MAX_MEMORY_SIZE ||
886            end < 0 || end >= MAX_MEMORY_SIZE) {
887                 return 0;
888         }
889         for (i=begin; i<=end; i++) {
890                 if (firmware_mask[i]) return 1;
891         }
892         return 0;
893 }
894
895 void ihex_get_data(int addr, int len, unsigned char *bytes)
896 {
897         int i;
898
899         if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) {
900                 for (i=0; i<len; i++) {
901                         bytes[i] = 255;
902                 }
903                 return;
904         }
905         for (i=0; i<len; i++) {
906                 if (firmware_mask[addr]) {
907                         bytes[i] = firmware_image[addr];
908                 } else {
909                         bytes[i] = 255;
910                 }
911                 addr++;
912         }
913 }
914
915 /****************************************************************/
916 /*                                                              */
917 /*                       Misc Functions                         */
918 /*                                                              */
919 /****************************************************************/
920
921 int printf_verbose(const char *format, ...)
922 {
923         va_list ap;
924         int r = 0;
925
926         va_start(ap, format);
927         if (verbose) {
928                 r = vprintf(format, ap);
929                 fflush(stdout);
930         }
931         va_end(ap);
932
933         return r;
934 }
935
936 void delay(double seconds)
937 {
938         #ifdef USE_WIN32
939         sleep(seconds * 1000.0);
940         #else
941         usleep(seconds * 1000000.0);
942         #endif
943 }
944
945 void die(const char *str, ...)
946 {
947         va_list  ap;
948
949         va_start(ap, str);
950         vfprintf(stderr, str, ap);
951         fprintf(stderr, "\n");
952         va_end(ap);
953
954         exit(1);
955 }
956
957 #if defined USE_WIN32
958 #define strcasecmp stricmp
959 #endif
960
961 void parse_options(int argc, char **argv)
962 {
963         int i;
964         const char *arg;
965
966         for (i=1; i<argc; i++) {
967                 arg = argv[i];
968
969                 if (*arg == '-') {
970                         if (strcmp(arg, "-w") == 0) {
971                                 wait_for_device_to_appear = 1;
972                         } else if (strcmp(arg, "-r") == 0) {
973                                 hard_reboot_device = 1;
974                         } else if (strcmp(arg, "-n") == 0) {
975                                 reboot_after_programming = 0;
976                         } else if (strcmp(arg, "-v") == 0) {
977                                 verbose = 1;
978                         } else if (strncmp(arg, "-mmcu=", 6) == 0) {
979                                 arg += 6;
980
981                                 if (strncmp(arg, "at90usb", 7) == 0) {
982                                         arg += 7;
983                                 } else if (strncmp(arg, "atmega", 6) == 0) {
984                                         arg += 6;
985                                 } else {
986                                         die("Unknown MCU type\n");
987                                 }
988
989                                 if (strncmp(arg, "128", 3) == 0) {
990                                         code_size  = 128 * 1024;
991                                         block_size = 256;
992                                 } else if (strncmp(arg, "64", 2) == 0) {
993                                         code_size  = 64 * 1024;
994                                         block_size = 256;
995                                 } else if (strncmp(arg, "32", 2) == 0) {
996                                         code_size  = 32 * 1024;
997                                         block_size = 128;
998                                 } else if (strncmp(arg, "16", 2) == 0) {
999                                         code_size  = 16 * 1024;
1000                                         block_size = 128;
1001                                 } else if (strncmp(arg, "8", 1) == 0) {
1002                                         code_size  = 8 * 1024;
1003                                         block_size = 128;
1004                                 } else {
1005                                         die("Unknown MCU type\n");
1006                                 }
1007                         }
1008                 } else {
1009                         filename = argv[i];
1010                 }
1011         }
1012 }
1013