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