]> git.donarmstrong.com Git - kiibohd-controller.git/blob - LoadFile/teensy_loader_cli.c
Porting teensy-loader-cli to use libusb-1.0 (from 0.1).
[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 #include <libusb-1.0/libusb.h>
202
203 struct libusb_device_handle *open_usb_device( int vid, int pid )
204 {
205         libusb_device **devs;
206         struct libusb_device_handle *handle = NULL;
207         struct libusb_device_handle *ret = NULL;
208
209         if ( libusb_init( NULL ) != 0 )
210                 fprintf( stderr, "libusb_init failed.\n" );
211
212         size_t count = libusb_get_device_list( NULL, &devs );
213         if ( count < 0 )
214                 fprintf( stderr, "libusb_get_device_list failed.\n" );
215
216         for ( size_t c = 0; c < count; c++ )
217         {
218                 struct libusb_device_descriptor desc;
219                 libusb_device *dev = devs[c];
220
221                 if ( libusb_get_device_descriptor( dev, &desc ) < 0 )
222                         fprintf( stderr, "libusb_get_device_descriptor failed.\n" );
223
224                 //printf("ID: %04x  Product: %04x\n", desc.idVendor, desc.idProduct );
225                 // Not correct device
226                 if ( desc.idVendor != vid || desc.idProduct != pid )
227                         continue;
228
229                 // Attempt to open the device
230                 if ( libusb_open( dev, &handle ) != 0 )
231                 {
232                         fprintf( stderr, "Found device but unable to open\n" );
233                         continue;
234                 }
235
236                 // Only required on Linux, other platforms will just ignore this call
237                 libusb_detach_kernel_driver( handle, 0 );
238
239                 // Mac OS-X - removing this call to usb_claim_interface() might allow
240                 // this to work, even though it is a clear misuse of the libusb API.
241                 // normally Apple's IOKit should be used on Mac OS-X
242                 // XXX Is this still valid with libusb-1.0?
243
244                 // Attempt to claim interface
245                 int err = libusb_claim_interface( handle, 0 );
246                 if ( err < 0 )
247                 {
248                         libusb_close( handle );
249                         fprintf( stderr, "Unable to claim interface, check USB permissions: %d\n", err );
250                         continue;
251                 }
252
253                 ret = handle;
254                 break;
255         }
256
257         libusb_free_device_list( devs, 1 );
258
259         return ret;
260 }
261
262 static struct libusb_device_handle *libusb_teensy_handle = NULL;
263
264 int teensy_open()
265 {
266         teensy_close();
267
268         libusb_teensy_handle = open_usb_device( 0x16C0, 0x0478 );
269
270         if ( libusb_teensy_handle )
271                 return 1;
272
273         return 0;
274 }
275
276 int teensy_write( void *buf, int len, double timeout )
277 {
278         int r;
279
280         if ( !libusb_teensy_handle )
281                 return 0;
282
283         while ( timeout > 0 ) {
284                 r = libusb_control_transfer( libusb_teensy_handle,
285                         0x21, 9, 0x0200, 0,
286                         (unsigned char *)buf, len,
287                         (int)(timeout * 1000.0) );
288
289                 if ( r >= 0 )
290                         return 1;
291
292                 //printf("teensy_write, r=%d\n", r);
293                 usleep( 10000 );
294                 timeout -= 0.01;  // TODO: subtract actual elapsed time
295         }
296
297         return 0;
298 }
299
300 void teensy_close()
301 {
302         if ( !libusb_teensy_handle)
303                 return;
304
305         libusb_release_interface( libusb_teensy_handle, 0 );
306         libusb_close( libusb_teensy_handle );
307
308         libusb_teensy_handle = NULL;
309 }
310
311 int hard_reboot()
312 {
313         struct libusb_device_handle *rebootor;
314         int r;
315
316         rebootor = open_usb_device( 0x16C0, 0x0477 );
317
318         if (!rebootor)
319                 return 0;
320
321         r = libusb_control_transfer( rebootor,
322                 0x21, 9, 0x0200, 0,
323                 (unsigned char*)"reboot", 6,
324                 100 );
325
326         libusb_release_interface( rebootor, 0 );
327         libusb_close( rebootor );
328
329         if (r < 0)
330                 return 0;
331
332         return 1;
333 }
334
335 #endif
336
337
338 /****************************************************************/
339 /*                                                              */
340 /*               USB Access - Microsoft WIN32                   */
341 /*                                                              */
342 /****************************************************************/
343
344 #if defined(USE_WIN32)
345
346 // http://msdn.microsoft.com/en-us/library/ms790932.aspx
347 #include <windows.h>
348 #include <setupapi.h>
349 #include <ddk/hidsdi.h>
350 #include <ddk/hidclass.h>
351
352 HANDLE open_usb_device(int vid, int pid)
353 {
354         GUID guid;
355         HDEVINFO info;
356         DWORD index, required_size;
357         SP_DEVICE_INTERFACE_DATA iface;
358         SP_DEVICE_INTERFACE_DETAIL_DATA *details;
359         HIDD_ATTRIBUTES attrib;
360         HANDLE h;
361         BOOL ret;
362
363         HidD_GetHidGuid(&guid);
364         info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
365         if (info == INVALID_HANDLE_VALUE) return NULL;
366         for (index=0; 1 ;index++) {
367                 iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
368                 ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface);
369                 if (!ret) {
370                         SetupDiDestroyDeviceInfoList(info);
371                         break;
372                 }
373                 SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL);
374                 details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(required_size);
375                 if (details == NULL) continue;
376                 memset(details, 0, required_size);
377                 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
378                 ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details,
379                         required_size, NULL, NULL);
380                 if (!ret) {
381                         free(details);
382                         continue;
383                 }
384                 h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE,
385                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
386                         FILE_FLAG_OVERLAPPED, NULL);
387                 free(details);
388                 if (h == INVALID_HANDLE_VALUE) continue;
389                 attrib.Size = sizeof(HIDD_ATTRIBUTES);
390                 ret = HidD_GetAttributes(h, &attrib);
391                 if (!ret) {
392                         CloseHandle(h);
393                         continue;
394                 }
395                 if (attrib.VendorID != vid || attrib.ProductID != pid) {
396                         CloseHandle(h);
397                         continue;
398                 }
399                 SetupDiDestroyDeviceInfoList(info);
400                 return h;
401         }
402         return NULL;
403 }
404
405 int write_usb_device(HANDLE h, void *buf, int len, int timeout)
406 {
407         static HANDLE event = NULL;
408         unsigned char tmpbuf[1040];
409         OVERLAPPED ov;
410         DWORD n, r;
411
412         if (len > sizeof(tmpbuf) - 1) return 0;
413         if (event == NULL) {
414                 event = CreateEvent(NULL, TRUE, TRUE, NULL);
415                 if (!event) return 0;
416         }
417         ResetEvent(&event);
418         memset(&ov, 0, sizeof(ov));
419         ov.hEvent = event;
420         tmpbuf[0] = 0;
421         memcpy(tmpbuf + 1, buf, len);
422         if (!WriteFile(h, tmpbuf, len + 1, NULL, &ov)) {
423                 if (GetLastError() != ERROR_IO_PENDING) return 0;
424                 r = WaitForSingleObject(event, timeout);
425                 if (r == WAIT_TIMEOUT) {
426                         CancelIo(h);
427                         return 0;
428                 }
429                 if (r != WAIT_OBJECT_0) return 0;
430         }
431         if (!GetOverlappedResult(h, &ov, &n, FALSE)) return 0;
432         if (n <= 0) return 0;
433         return 1;
434 }
435
436 void print_win32_err(void)
437 {
438         char buf[256];
439         DWORD err;
440
441         err = GetLastError();
442         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
443                 0, buf, sizeof(buf), NULL);
444         printf("err %ld: %s\n", err, buf);
445 }
446
447 static HANDLE win32_teensy_handle = NULL;
448
449 int teensy_open(void)
450 {
451         teensy_close();
452         win32_teensy_handle = open_usb_device(0x16C0, 0x0478);
453         if (win32_teensy_handle) return 1;
454         return 0;
455 }
456
457 int teensy_write(void *buf, int len, double timeout)
458 {
459         int r;
460         if (!win32_teensy_handle) return 0;
461         r = write_usb_device(win32_teensy_handle, buf, len, (int)(timeout * 1000.0));
462         //if (!r) print_win32_err();
463         return r;
464 }
465
466 void teensy_close(void)
467 {
468         if (!win32_teensy_handle) return;
469         CloseHandle(win32_teensy_handle);
470         win32_teensy_handle = NULL;
471 }
472
473 int hard_reboot(void)
474 {
475         HANDLE rebootor;
476         int r;
477
478         rebootor = open_usb_device(0x16C0, 0x0477);
479         if (!rebootor) return 0;
480         r = write_usb_device(rebootor, "reboot", 6, 100);
481         CloseHandle(rebootor);
482         return r;
483 }
484
485 #endif
486
487
488
489 /****************************************************************/
490 /*                                                              */
491 /*             USB Access - Apple's IOKit, Mac OS-X             */
492 /*                                                              */
493 /****************************************************************/
494
495 #if defined(USE_APPLE_IOKIT)
496
497 // http://developer.apple.com/technotes/tn2007/tn2187.html
498 #include <IOKit/IOKitLib.h>
499 #include <IOKit/hid/IOHIDLib.h>
500 #include <IOKit/hid/IOHIDDevice.h>
501
502 struct usb_list_struct {
503         IOHIDDeviceRef ref;
504         int pid;
505         int vid;
506         struct usb_list_struct *next;
507 };
508
509 static struct usb_list_struct *usb_list=NULL;
510 static IOHIDManagerRef hid_manager=NULL;
511
512 void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
513 {
514         CFTypeRef type;
515         struct usb_list_struct *n, *p;
516         int32_t pid, vid;
517
518         if (!dev) return;
519         type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey));
520         if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
521         if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &vid)) return;
522         type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey));
523         if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
524         if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &pid)) return;
525         n = (struct usb_list_struct *)malloc(sizeof(struct usb_list_struct));
526         if (!n) return;
527         //printf("attach callback: vid=%04X, pid=%04X\n", vid, pid);
528         n->ref = dev;
529         n->vid = vid;
530         n->pid = pid;
531         n->next = NULL;
532         if (usb_list == NULL) {
533                 usb_list = n;
534         } else {
535                 for (p = usb_list; p->next; p = p->next) ;
536                 p->next = n;
537         }
538 }
539
540 void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
541 {
542         struct usb_list_struct *p, *tmp, *prev=NULL;
543
544         p = usb_list;
545         while (p) {
546                 if (p->ref == dev) {
547                         if (prev) {
548                                 prev->next = p->next;
549                         } else {
550                                 usb_list = p->next;
551                         }
552                         tmp = p;
553                         p = p->next;
554                         free(tmp);
555                 } else {
556                         prev = p;
557                         p = p->next;
558                 }
559         }
560 }
561
562 void init_hid_manager(void)
563 {
564         CFMutableDictionaryRef dict;
565         IOReturn ret;
566
567         if (hid_manager) return;
568         hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
569         if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) {
570                 if (hid_manager) CFRelease(hid_manager);
571                 printf_verbose("no HID Manager - maybe this is a pre-Leopard (10.5) system?\n");
572                 return;
573         }
574         dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
575                 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
576         if (!dict) return;
577         IOHIDManagerSetDeviceMatching(hid_manager, dict);
578         CFRelease(dict);
579         IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
580         IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL);
581         IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL);
582         ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
583         if (ret != kIOReturnSuccess) {
584                 IOHIDManagerUnscheduleFromRunLoop(hid_manager,
585                         CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
586                 CFRelease(hid_manager);
587                 printf_verbose("Error opening HID Manager");
588         }
589 }
590
591 static void do_run_loop(void)
592 {
593         while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ;
594 }
595
596 IOHIDDeviceRef open_usb_device(int vid, int pid)
597 {
598         struct usb_list_struct *p;
599         IOReturn ret;
600
601         init_hid_manager();
602         do_run_loop();
603         for (p = usb_list; p; p = p->next) {
604                 if (p->vid == vid && p->pid == pid) {
605                         ret = IOHIDDeviceOpen(p->ref, kIOHIDOptionsTypeNone);
606                         if (ret == kIOReturnSuccess) return p->ref;
607                 }
608         }
609         return NULL;
610 }
611
612 void close_usb_device(IOHIDDeviceRef dev)
613 {
614         struct usb_list_struct *p;
615
616         do_run_loop();
617         for (p = usb_list; p; p = p->next) {
618                 if (p->ref == dev) {
619                         IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone);
620                         return;
621                 }
622         }
623 }
624
625 static IOHIDDeviceRef iokit_teensy_reference = NULL;
626
627 int teensy_open(void)
628 {
629         teensy_close();
630         iokit_teensy_reference = open_usb_device(0x16C0, 0x0478);
631         if (iokit_teensy_reference) return 1;
632         return 0;
633 }
634
635 int teensy_write(void *buf, int len, double timeout)
636 {
637         IOReturn ret;
638
639         // timeouts do not work on OS-X
640         // IOHIDDeviceSetReportWithCallback is not implemented
641         // even though Apple documents it with a code example!
642         // submitted to Apple on 22-sep-2009, problem ID 7245050
643         if (!iokit_teensy_reference) return 0;
644         ret = IOHIDDeviceSetReport(iokit_teensy_reference,
645                 kIOHIDReportTypeOutput, 0, buf, len);
646         if (ret == kIOReturnSuccess) return 1;
647         return 0;
648 }
649
650 void teensy_close(void)
651 {
652         if (!iokit_teensy_reference) return;
653         close_usb_device(iokit_teensy_reference);
654         iokit_teensy_reference = NULL;
655 }
656
657 int hard_reboot(void)
658 {
659         IOHIDDeviceRef rebootor;
660         IOReturn ret;
661
662         rebootor = open_usb_device(0x16C0, 0x0477);
663         if (!rebootor) return 0;
664         ret = IOHIDDeviceSetReport(rebootor,
665                 kIOHIDReportTypeOutput, 0, (uint8_t *)("reboot"), 6);
666         close_usb_device(rebootor);
667         if (ret == kIOReturnSuccess) return 1;
668         return 0;
669 }
670
671 #endif
672
673
674
675 /****************************************************************/
676 /*                                                              */
677 /*              USB Access - BSD's UHID driver                  */
678 /*                                                              */
679 /****************************************************************/
680
681 #if defined(USE_UHID)
682
683 // Thanks to Todd T Fries for help getting this working on OpenBSD
684 // and to Chris Kuethe for the initial patch to use UHID.
685
686 #include <sys/ioctl.h>
687 #include <fcntl.h>
688 #include <dirent.h>
689 #include <dev/usb/usb.h>
690 #ifndef USB_GET_DEVICEINFO
691 #include <dev/usb/usb_ioctl.h>
692 #endif
693
694 int open_usb_device(int vid, int pid)
695 {
696         int r, fd;
697         DIR *dir;
698         struct dirent *d;
699         struct usb_device_info info;
700         char buf[256];
701
702         dir = opendir("/dev");
703         if (!dir) return -1;
704         while ((d = readdir(dir)) != NULL) {
705                 if (strncmp(d->d_name, "uhid", 4) != 0) continue;
706                 snprintf(buf, sizeof(buf), "/dev/%s", d->d_name);
707                 fd = open(buf, O_RDWR);
708                 if (fd < 0) continue;
709                 r = ioctl(fd, USB_GET_DEVICEINFO, &info);
710                 if (r < 0) {
711                         // NetBSD: added in 2004
712                         // OpenBSD: added November 23, 2009
713                         // FreeBSD: missing (FreeBSD 8.0) - USE_LIBUSB works!
714                         die("Error: your uhid driver does not support"
715                           " USB_GET_DEVICEINFO, please upgrade!\n");
716                         close(fd);
717                         closedir(dir);
718                         exit(1);
719                 }
720                 //printf("%s: v=%d, p=%d\n", buf, info.udi_vendorNo, info.udi_productNo);
721                 if (info.udi_vendorNo == vid && info.udi_productNo == pid) {
722                         closedir(dir);
723                         return fd;
724                 }
725                 close(fd);
726         }
727         closedir(dir);
728         return -1;
729 }
730
731 static int uhid_teensy_fd = -1;
732
733 int teensy_open(void)
734 {
735         teensy_close();
736         uhid_teensy_fd = open_usb_device(0x16C0, 0x0478);
737         if (uhid_teensy_fd < 0) return 0;
738         return 1;
739 }
740
741 int teensy_write(void *buf, int len, double timeout)
742 {
743         int r;
744
745         // TODO: imeplement timeout... how??
746         r = write(uhid_teensy_fd, buf, len);
747         if (r == len) return 1;
748         return 0;
749 }
750
751 void teensy_close(void)
752 {
753         if (uhid_teensy_fd >= 0) {
754                 close(uhid_teensy_fd);
755                 uhid_teensy_fd = -1;
756         }
757 }
758
759 int hard_reboot(void)
760 {
761         int r, rebootor_fd;
762
763         rebootor_fd = open_usb_device(0x16C0, 0x0477);
764         if (rebootor_fd < 0) return 0;
765         r = write(rebootor_fd, "reboot", 6);
766         delay(0.1);
767         close(rebootor_fd);
768         if (r == 6) return 1;
769         return 0;
770 }
771
772 #endif
773
774
775
776 /****************************************************************/
777 /*                                                              */
778 /*                     Read Intel Hex File                      */
779 /*                                                              */
780 /****************************************************************/
781
782 // the maximum flash image size we can support
783 // chips with larger memory may be used, but only this
784 // much intel-hex data can be loaded into memory!
785 #define MAX_MEMORY_SIZE 0x40000
786
787 static unsigned char firmware_image[MAX_MEMORY_SIZE];
788 static unsigned char firmware_mask[MAX_MEMORY_SIZE];
789 static int end_record_seen=0;
790 static int byte_count;
791 static unsigned int extended_addr = 0;
792 static int parse_hex_line(char *line);
793
794 int read_intel_hex(const char *filename)
795 {
796         FILE *fp;
797         int i, lineno=0;
798         char buf[1024];
799
800         byte_count = 0;
801         end_record_seen = 0;
802         for (i=0; i<MAX_MEMORY_SIZE; i++) {
803                 firmware_image[i] = 0xFF;
804                 firmware_mask[i] = 0;
805         }
806         extended_addr = 0;
807
808         fp = fopen(filename, "r");
809         if (fp == NULL) {
810                 //printf("Unable to read file %s\n", filename);
811                 return -1;
812         }
813         while (!feof(fp)) {
814                 *buf = '\0';
815                 if (!fgets(buf, sizeof(buf), fp)) break;
816                 lineno++;
817                 if (*buf) {
818                         if (parse_hex_line(buf) == 0) {
819                                 printf("Warning, HEX parse error line %d\n", lineno);
820                                 return -2;
821                         }
822                 }
823                 if (end_record_seen) break;
824                 if (feof(stdin)) break;
825         }
826         fclose(fp);
827         return byte_count;
828 }
829
830
831 /* from ihex.c, at http://www.pjrc.com/tech/8051/pm2_docs/intel-hex.html */
832
833 /* parses a line of intel hex code, stores the data in bytes[] */
834 /* and the beginning address in addr, and returns a 1 if the */
835 /* line was valid, or a 0 if an error occured.  The variable */
836 /* num gets the number of bytes that were stored into bytes[] */
837
838
839 int
840 parse_hex_line(char *line)
841 {
842         int addr, code, num;
843         int sum, len, cksum, i;
844         char *ptr;
845
846         num = 0;
847         if (line[0] != ':') return 0;
848         if (strlen(line) < 11) return 0;
849         ptr = line+1;
850         if (!sscanf(ptr, "%02x", &len)) return 0;
851         ptr += 2;
852         if ((int)strlen(line) < (11 + (len * 2)) ) return 0;
853         if (!sscanf(ptr, "%04x", &addr)) return 0;
854         ptr += 4;
855           /* printf("Line: length=%d Addr=%d\n", len, addr); */
856         if (!sscanf(ptr, "%02x", &code)) return 0;
857         if (addr + extended_addr + len >= MAX_MEMORY_SIZE) return 0;
858         ptr += 2;
859         sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255);
860         if (code != 0) {
861                 if (code == 1) {
862                         end_record_seen = 1;
863                         return 1;
864                 }
865                 if (code == 2 && len == 2) {
866                         if (!sscanf(ptr, "%04x", &i)) return 1;
867                         ptr += 4;
868                         sum += ((i >> 8) & 255) + (i & 255);
869                         if (!sscanf(ptr, "%02x", &cksum)) return 1;
870                         if (((sum & 255) + (cksum & 255)) & 255) return 1;
871                         extended_addr = i << 4;
872                         //printf("ext addr = %05X\n", extended_addr);
873                 }
874                 if (code == 4 && len == 2) {
875                         if (!sscanf(ptr, "%04x", &i)) return 1;
876                         ptr += 4;
877                         sum += ((i >> 8) & 255) + (i & 255);
878                         if (!sscanf(ptr, "%02x", &cksum)) return 1;
879                         if (((sum & 255) + (cksum & 255)) & 255) return 1;
880                         extended_addr = i << 16;
881                         //printf("ext addr = %08X\n", extended_addr);
882                 }
883                 return 1;       // non-data line
884         }
885         byte_count += len;
886         while (num != len) {
887                 if (sscanf(ptr, "%02x", &i) != 1) return 0;
888                 i &= 255;
889                 firmware_image[addr + extended_addr + num] = i;
890                 firmware_mask[addr + extended_addr + num] = 1;
891                 ptr += 2;
892                 sum += i;
893                 (num)++;
894                 if (num >= 256) return 0;
895         }
896         if (!sscanf(ptr, "%02x", &cksum)) return 0;
897         if (((sum & 255) + (cksum & 255)) & 255) return 0; /* checksum error */
898         return 1;
899 }
900
901 int ihex_bytes_within_range(int begin, int end)
902 {
903         int i;
904
905         if (begin < 0 || begin >= MAX_MEMORY_SIZE ||
906            end < 0 || end >= MAX_MEMORY_SIZE) {
907                 return 0;
908         }
909         for (i=begin; i<=end; i++) {
910                 if (firmware_mask[i]) return 1;
911         }
912         return 0;
913 }
914
915 void ihex_get_data(int addr, int len, unsigned char *bytes)
916 {
917         int i;
918
919         if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) {
920                 for (i=0; i<len; i++) {
921                         bytes[i] = 255;
922                 }
923                 return;
924         }
925         for (i=0; i<len; i++) {
926                 if (firmware_mask[addr]) {
927                         bytes[i] = firmware_image[addr];
928                 } else {
929                         bytes[i] = 255;
930                 }
931                 addr++;
932         }
933 }
934
935 int memory_is_blank(int addr, int block_size)
936 {
937         if (addr < 0 || addr > MAX_MEMORY_SIZE) return 1;
938
939         while (block_size && addr < MAX_MEMORY_SIZE) {
940                 if (firmware_mask[addr] && firmware_image[addr] != 255) return 0;
941                 addr++;
942                 block_size--;
943         }
944         return 1;
945 }
946
947
948
949
950 /****************************************************************/
951 /*                                                              */
952 /*                       Misc Functions                         */
953 /*                                                              */
954 /****************************************************************/
955
956 int printf_verbose(const char *format, ...)
957 {
958         va_list ap;
959         int r;
960
961         va_start(ap, format);
962         if (verbose) {
963                 r = vprintf(format, ap);
964                 fflush(stdout);
965                 return r;
966         }
967         return 0;
968 }
969
970 void delay(double seconds)
971 {
972         #ifdef WIN32
973         Sleep(seconds * 1000.0);
974         #else
975         usleep(seconds * 1000000.0);
976         #endif
977 }
978
979 void die(const char *str, ...)
980 {
981         va_list  ap;
982
983         va_start(ap, str);
984         vfprintf(stderr, str, ap);
985         fprintf(stderr, "\n");
986         exit(1);
987 }
988
989 #if defined(WIN32)
990 #define strcasecmp stricmp
991 #endif
992
993 void parse_options(int argc, char **argv)
994 {
995         int i;
996         const char *arg;
997
998         for (i=1; i<argc; i++) {
999                 arg = argv[i];
1000                 //printf("arg: %s\n", arg);
1001                 if (*arg == '-') {
1002                         if (strcmp(arg, "-w") == 0) {
1003                                 wait_for_device_to_appear = 1;
1004                         } else if (strcmp(arg, "-r") == 0) {
1005                                 hard_reboot_device = 1;
1006                         } else if (strcmp(arg, "-n") == 0) {
1007                                 reboot_after_programming = 0;
1008                         } else if (strcmp(arg, "-v") == 0) {
1009                                 verbose = 1;
1010                         } else if (strncmp(arg, "-mmcu=", 6) == 0) {
1011                                 if (strcasecmp(arg+6, "at90usb162") == 0) {
1012                                         code_size = 15872;
1013                                         block_size = 128;
1014                                 } else if (strcasecmp(arg+6, "atmega32u4") == 0) {
1015                                         code_size = 32256;
1016                                         block_size = 128;
1017                                 } else if (strcasecmp(arg+6, "at90usb646") == 0) {
1018                                         code_size = 64512;
1019                                         block_size = 256;
1020                                 } else if (strcasecmp(arg+6, "at90usb1286") == 0) {
1021                                         code_size = 130048;
1022                                         block_size = 256;
1023 #if defined(USE_LIBUSB)
1024                                 } else if (strcasecmp(arg+6, "mk20dx128") == 0) {
1025                                         code_size = 131072;
1026                                         block_size = 1024;
1027                                 } else if (strcasecmp(arg+6, "mk20dx256") == 0) {
1028                                         code_size = 262144;
1029                                         block_size = 1024;
1030 #endif
1031                                 } else {
1032                                         die("Unknown MCU type\n");
1033                                 }
1034                         }
1035                 } else {
1036                         filename = argv[i];
1037                 }
1038         }
1039 }
1040