text: /* Modified for the LUFA HID Bootloader by Dean Camera
   *           http://www.lufa-lib.org
   *
   *   THIS MODIFIED VERSION IS UNSUPPORTED BY PJRC.
   */
  
  /* Teensy Loader, Command Line Interface
   * Program and Reboot Teensy Board with HalfKay Bootloader
   * http://www.pjrc.com/teensy/loader_cli.html
   * Copyright 2008-2010, PJRC.COM, LLC
   *
   *
   * You may redistribute this program and/or modify it under the terms
   * of the GNU General Public License as published by the Free Software
   * Foundation, version 3 of the License.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see http://www.gnu.org/licenses/
   */
  
  /* Want to incorporate this code into a proprietary application??
   * Just email paul@pjrc.com to ask.  Usually it's not a problem,
   * but you do need to ask to use this code in any way other than
   * those permitted by the GNU General Public License, version 3  */
  
  /* For non-root permissions on ubuntu or similar udev-based linux
   * http://www.pjrc.com/teensy/49-teensy.rules
   */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <stdint.h>
  #include <stdarg.h>
  #include <string.h>
  #include <unistd.h>
  
  void usage(void)
  {
  	fprintf(stderr, "Usage: hid_bootloader_cli -mmcu=<MCU> [-w] [-h] [-n] [-v] <file.hex>\n");
  	fprintf(stderr, "\t-w : Wait for device to appear\n");
  	fprintf(stderr, "\t-r : Use hard reboot if device not online\n");
  	fprintf(stderr, "\t-n : No reboot after programming\n");
  	fprintf(stderr, "\t-v : Verbose output\n");
  	fprintf(stderr, "\n<MCU> = atmegaXXuY or at90usbXXXY");
  
  	fprintf(stderr, "\nFor support and more information, please visit:\n");
  	fprintf(stderr, "http://www.lufa-lib.org\n");
  
  	fprintf(stderr, "\nBased on the TeensyHID command line programmer software:\n");
  	fprintf(stderr, "http://www.pjrc.com/teensy/loader_cli.html\n");
  	exit(1);
  }
  
  // USB Access Functions
  int teensy_open(void);
  int teensy_write(void *buf, int len, double timeout);
  void teensy_close(void);
  int hard_reboot(void);
  
  // Intel Hex File Functions
  int read_intel_hex(const char *filename);
  int ihex_bytes_within_range(int begin, int end);
  void ihex_get_data(int addr, int len, unsigned char *bytes);
  
  // Misc stuff
  int printf_verbose(const char *format, ...);
  void delay(double seconds);
  void die(const char *str, ...);
  void parse_options(int argc, char **argv);
  
  // options (from user via command line args)
  int wait_for_device_to_appear = 0;
  int hard_reboot_device = 0;
  int reboot_after_programming = 1;
  int verbose = 0;
  int code_size = 0, block_size = 0;
  const char *filename=NULL;
  
  
  /****************************************************************/
  /*                                                              */
  /*                       Main Program                           */
  /*                                                              */
  /****************************************************************/
  
  int main(int argc, char **argv)
  {
  	unsigned char buf[260];
  	int num, addr, r, first_block=1, waited=0;
  
  	// parse command line arguments
  	parse_options(argc, argv);
  	if (!filename) {
  		fprintf(stderr, "Filename must be specified\n\n");
  		usage();
  	}
  	if (!code_size) {
  		fprintf(stderr, "MCU type must be specified\n\n");
  		usage();
  	}
  	printf_verbose("Teensy Loader, Command Line, Version 2.0\n");
  
  	// read the intel hex file
  	// this is done first so any error is reported before using USB
  	num = read_intel_hex(filename);
  	if (num < 0) die("error reading intel hex file \"%s\"", filename);
  	printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
  		filename, num, (double)num / (double)code_size * 100.0);
  
  	// open the USB device
  	while (1) {
  		if (teensy_open()) break;
  		if (hard_reboot_device) {
  			if (!hard_reboot()) die("Unable to find rebootor\n");
  			printf_verbose("Hard Reboot performed\n");
  			hard_reboot_device = 0; // only hard reboot once
  			wait_for_device_to_appear = 1;
  		}
  		if (!wait_for_device_to_appear) die("Unable to open device\n");
  		if (!waited) {
  			printf_verbose("Waiting for Teensy device...\n");
  			printf_verbose(" (hint: press the reset button)\n");
  			waited = 1;
  		}
  		delay(0.25);
  	}
  	printf_verbose("Found HalfKay Bootloader\n");
  
  	// if we waited for the device, read the hex file again
  	// perhaps it changed while we were waiting?
  	if (waited) {
  		num = read_intel_hex(filename);
  		if (num < 0) die("error reading intel hex file \"%s\"", filename);
  		printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
  		 	filename, num, (double)num / (double)code_size * 100.0);
  	}
  
  	// program the data
  	printf_verbose("Programming");
  	fflush(stdout);
  	for (addr = 0; addr < code_size; addr += block_size) {
  		if (addr > 0 && !ihex_bytes_within_range(addr, addr + block_size - 1)) {
  			// don't waste time on blocks that are unused,
  			// but always do the first one to erase the chip
  			continue;
  		}
  		printf_verbose(".");
  		if (code_size < 0x10000) {
  			buf[0] = addr & 255;
  			buf[1] = (addr >> 8) & 255;
  		} else {
  			buf[0] = (addr >> 8) & 255;
  			buf[1] = (addr >> 16) & 255;
  		}
  		ihex_get_data(addr, block_size, buf + 2);
  		r = teensy_write(buf, block_size + 2, first_block ? 3.0 : 0.25);
  		if (!r) die("error writing to Teensy\n");
  		first_block = 0;
  	}
  	printf_verbose("\n");
  
  	// reboot to the user's new code
  	if (reboot_after_programming) {
  		printf_verbose("Booting\n");
  		buf[0] = 0xFF;
  		buf[1] = 0xFF;
  		memset(buf + 2, 0, sizeof(buf) - 2);
  		teensy_write(buf, block_size + 2, 0.25);
  	}
  	teensy_close();
  	return 0;
  }
  
  
  
  
  /****************************************************************/
  /*                                                              */
  /*             USB Access - libusb (Linux & FreeBSD)            */
  /*                                                              */
  /****************************************************************/
  
  #if defined(USE_LIBUSB)
  
  // http://libusb.sourceforge.net/doc/index.html
  #include <usb.h>
  
  usb_dev_handle * open_usb_device(int vid, int pid)
  {
  	struct usb_bus *bus;
  	struct usb_device *dev;
  	usb_dev_handle *h;
  	#ifdef LIBUSB_HAS_GET_DRIVER_NP
  	char buf[128];
  	#endif
  	int r;
  
  	usb_init();
  	usb_find_busses();
  	usb_find_devices();
  	//printf_verbose("\nSearching for USB device:\n");
  	for (bus = usb_get_busses(); bus; bus = bus->next) {
  		for (dev = bus->devices; dev; dev = dev->next) {
  			//printf_verbose("bus \"%s\", device \"%s\" vid=%04X, pid=%04X\n",
  			//	bus->dirname, dev->filename,
  			//	dev->descriptor.idVendor,
  			//	dev->descriptor.idProduct
  			//);
  			if (dev->descriptor.idVendor != vid) continue;
  			if (dev->descriptor.idProduct != pid) continue;
  			h = usb_open(dev);
  			if (!h) {
  				printf_verbose("Found device but unable to open");
  				continue;
  			}
  			#ifdef LIBUSB_HAS_GET_DRIVER_NP
  			r = usb_get_driver_np(h, 0, buf, sizeof(buf));
  			if (r >= 0) {
  				r = usb_detach_kernel_driver_np(h, 0);
  				if (r < 0) {
  					usb_close(h);
  					printf_verbose("Device is in use by \"%s\" driver", buf);
  					continue;
  				}
  			}
  			#endif
  			// Mac OS-X - removing this call to usb_claim_interface() might allow
  			// this to work, even though it is a clear misuse of the libusb API.
  			// normally Apple's IOKit should be used on Mac OS-X
  			r = usb_claim_interface(h, 0);
  			if (r < 0) {
  				usb_close(h);
  				printf_verbose("Unable to claim interface, check USB permissions");
  				continue;
  			}
  			return h;
  		}
  	}
  	return NULL;
  }
  
  static usb_dev_handle *libusb_teensy_handle = NULL;
  
  int teensy_open(void)
  {
  	teensy_close();
  	libusb_teensy_handle = open_usb_device(0x16C0, 0x0478);
  
  	if (!libusb_teensy_handle)
  		libusb_teensy_handle = open_usb_device(0x03eb, 0x2067);
  
  	if (!libusb_teensy_handle) return 0;
  	return 1;
  }
  
  int teensy_write(void *buf, int len, double timeout)
  {
  	int r;
  
  	if (!libusb_teensy_handle) return 0;
  	r = usb_control_msg(libusb_teensy_handle, 0x21, 9, 0x0200, 0, (char *)buf,
  		len, (int)(timeout * 1000.0));
  	if (r < 0) return 0;
  	return 1;
  }
  
  void teensy_close(void)
  {
  	if (!libusb_teensy_handle) return;
  	usb_release_interface(libusb_teensy_handle, 0);
  	usb_close(libusb_teensy_handle);
  	libusb_teensy_handle = NULL;
  }
  
  int hard_reboot(void)
  {
  	usb_dev_handle *rebootor;
  	int r;
  
  	rebootor = open_usb_device(0x16C0, 0x0477);
  
  	if (!rebootor)
  		rebootor = open_usb_device(0x03eb, 0x2067);
  
  	if (!rebootor) return 0;
  	r = usb_control_msg(rebootor, 0x21, 9, 0x0200, 0, "reboot", 6, 100);
  	usb_release_interface(rebootor, 0);
  	usb_close(rebootor);
  	if (r < 0) return 0;
  	return 1;
  }
  
  #endif
  
  
  /****************************************************************/
  /*                                                              */
  /*               USB Access - Microsoft WIN32                   */
  /*                                                              */
  /****************************************************************/
  
  #if defined(USE_WIN32)
  
  // http://msdn.microsoft.com/en-us/library/ms790932.aspx
  #include <windows.h>
  #include <setupapi.h>
  #include <ddk/hidsdi.h>
  #include <ddk/hidclass.h>
  
  HANDLE open_usb_device(int vid, int pid)
  {
  	GUID guid;
  	HDEVINFO info;
  	DWORD index, required_size;
  	SP_DEVICE_INTERFACE_DATA iface;
  	SP_DEVICE_INTERFACE_DETAIL_DATA *details;
  	HIDD_ATTRIBUTES attrib;
  	HANDLE h;
  	BOOL ret;
  
  	HidD_GetHidGuid(&guid);
  	info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  	if (info == INVALID_HANDLE_VALUE) return NULL;
  	for (index=0; 1 ;index++) {
  		iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  		ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface);
  		if (!ret) {
  			SetupDiDestroyDeviceInfoList(info);
  			break;
  		}
  		SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL);
  		details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(required_size);
  		if (details == NULL) continue;
  		memset(details, 0, required_size);
  		details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  		ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details,
  			required_size, NULL, NULL);
  		if (!ret) {
  			free(details);
  			continue;
  		}
  		h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE,
  			FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  			FILE_FLAG_OVERLAPPED, NULL);
  		free(details);
  		if (h == INVALID_HANDLE_VALUE) continue;
  		attrib.Size = sizeof(HIDD_ATTRIBUTES);
  		ret = HidD_GetAttributes(h, &attrib);
  		if (!ret) {
  			CloseHandle(h);
  			continue;
  		}
  		if (attrib.VendorID != vid || attrib.ProductID != pid) {
  			CloseHandle(h);
  			continue;
  		}
  		SetupDiDestroyDeviceInfoList(info);
  		return h;
  	}
  	return NULL;
  }
  
  int write_usb_device(HANDLE h, void *buf, int len, int timeout)
  {
  	static HANDLE event = NULL;
  	unsigned char tmpbuf[1040];
  	OVERLAPPED ov;
  	DWORD n, r;
  
  	if (len > sizeof(tmpbuf) - 1) return 0;
  	if (event == NULL) {
  		event = CreateEvent(NULL, TRUE, TRUE, NULL);
  		if (!event) return 0;
  	}
  	ResetEvent(&event);
  	memset(&ov, 0, sizeof(ov));
  	ov.hEvent = event;
  	tmpbuf[0] = 0;
  	memcpy(tmpbuf + 1, buf, len);
  	if (!WriteFile(h, tmpbuf, len + 1, NULL, &ov)) {
  		if (GetLastError() != ERROR_IO_PENDING) return 0;
  		r = WaitForSingleObject(event, timeout);
  		if (r == WAIT_TIMEOUT) {
  			CancelIo(h);
  			return 0;
  		}
  		if (r != WAIT_OBJECT_0) return 0;
  	}
  	if (!GetOverlappedResult(h, &ov, &n, FALSE)) return 0;
  	return 1;
  }
  
  static HANDLE win32_teensy_handle = NULL;
  
  int teensy_open(void)
  {
  	teensy_close();
  	win32_teensy_handle = open_usb_device(0x16C0, 0x0478);
  
  	if (!win32_teensy_handle)
  		win32_teensy_handle = open_usb_device(0x03eb, 0x2067);
  
  	if (!win32_teensy_handle) return 0;
  	return 1;
  }
  
  int teensy_write(void *buf, int len, double timeout)
  {
  	int r;
  	if (!win32_teensy_handle) return 0;
  	r = write_usb_device(win32_teensy_handle, buf, len, (int)(timeout * 1000.0));
  	return r;
  }
  
  void teensy_close(void)
  {
  	if (!win32_teensy_handle) return;
  	CloseHandle(win32_teensy_handle);
  	win32_teensy_handle = NULL;
  }
  
  int hard_reboot(void)
  {
  	HANDLE rebootor;
  	int r;
  
  	rebootor = open_usb_device(0x16C0, 0x0477);
  
  	if (!rebootor)
  		rebootor = open_usb_device(0x03eb, 0x2067);
  
  	if (!rebootor) return 0;
  	r = write_usb_device(rebootor, "reboot", 6, 100);
  	CloseHandle(rebootor);
  	return r;
  }
  
  #endif
  
  
  
  /****************************************************************/
  /*                                                              */
  /*             USB Access - Apple's IOKit, Mac OS-X             */
  /*                                                              */
  /****************************************************************/
  
  #if defined(USE_APPLE_IOKIT)
  
  // http://developer.apple.com/technotes/tn2007/tn2187.html
  #include <IOKit/IOKitLib.h>
  #include <IOKit/hid/IOHIDLib.h>
  #include <IOKit/hid/IOHIDDevice.h>
  
  struct usb_list_struct {
  	IOHIDDeviceRef ref;
  	int pid;
  	int vid;
  	struct usb_list_struct *next;
  };
  
  static struct usb_list_struct *usb_list=NULL;
  static IOHIDManagerRef hid_manager=NULL;
  
  void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
  {
  	CFTypeRef type;
  	struct usb_list_struct *n, *p;
  	int32_t pid, vid;
  
  	if (!dev) return;
  	type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey));
  	if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
  	if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &vid)) return;
  	type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey));
  	if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
  	if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &pid)) return;
  	n = (struct usb_list_struct *)malloc(sizeof(struct usb_list_struct));
  	if (!n) return;
  	//printf("attach callback: vid=%04X, pid=%04X\n", vid, pid);
  	n->ref = dev;
  	n->vid = vid;
  	n->pid = pid;
  	n->next = NULL;
  	if (usb_list == NULL) {
  		usb_list = n;
  	} else {
  		for (p = usb_list; p->next; p = p->next) ;
  		p->next = n;
  	}
  }
  
  void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
  {
  	struct usb_list_struct *p, *tmp, *prev=NULL;
  
  	p = usb_list;
  	while (p) {
  		if (p->ref == dev) {
  			if (prev) {
  				prev->next = p->next;
  			} else {
  				usb_list = p->next;
  			}
  			tmp = p;
  			p = p->next;
  			free(tmp);
  		} else {
  			prev = p;
  			p = p->next;
  		}
  	}
  }
  
  void init_hid_manager(void)
  {
  	CFMutableDictionaryRef dict;
  	IOReturn ret;
  
  	if (hid_manager) return;
  	hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
  	if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) {
  		if (hid_manager) CFRelease(hid_manager);
  		printf_verbose("no HID Manager - maybe this is a pre-Leopard (10.5) system?\n");
  		return;
  	}
  	dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
  		&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  	if (!dict) return;
  	IOHIDManagerSetDeviceMatching(hid_manager, dict);
  	CFRelease(dict);
  	IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  	IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL);
  	IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL);
  	ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
  	if (ret != kIOReturnSuccess) {
  		IOHIDManagerUnscheduleFromRunLoop(hid_manager,
  			CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  		CFRelease(hid_manager);
  		printf_verbose("Error opening HID Manager");
  	}
  }
  
  static void do_run_loop(void)
  {
  	while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ;
  }
  
  IOHIDDeviceRef open_usb_device(int vid, int pid)
  {
  	struct usb_list_struct *p;
  	IOReturn ret;
  
  	init_hid_manager();
  	do_run_loop();
  	for (p = usb_list; p; p = p->next) {
  		if (p->vid == vid && p->pid == pid) {
  			ret = IOHIDDeviceOpen(p->ref, kIOHIDOptionsTypeNone);
  			if (ret == kIOReturnSuccess) return p->ref;
  		}
  	}
  	return NULL;
  }
  
  void close_usb_device(IOHIDDeviceRef dev)
  {
  	struct usb_list_struct *p;
  
  	do_run_loop();
  	for (p = usb_list; p; p = p->next) {
  		if (p->ref == dev) {
  			IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone);
  			return;
  		}
  	}
  }
  
  static IOHIDDeviceRef iokit_teensy_reference = NULL;
  
  int teensy_open(void)
  {
  	teensy_close();
  	iokit_teensy_reference = open_usb_device(0x16C0, 0x0478);
  
  	if (!iokit_teensy_reference)
  		iokit_teensy_reference = open_usb_device(0x03eb, 0x2067);
  
  	if (!iokit_teensy_reference) return 0;
  	return 1;
  }
  
  int teensy_write(void *buf, int len, double timeout)
  {
  	IOReturn ret;
  
  	// timeouts do not work on OS-X
  	// IOHIDDeviceSetReportWithCallback is not implemented
  	// even though Apple documents it with a code example!
  	// submitted to Apple on 22-sep-2009, problem ID 7245050
  	if (!iokit_teensy_reference) return 0;
  	ret = IOHIDDeviceSetReport(iokit_teensy_reference,
  		kIOHIDReportTypeOutput, 0, buf, len);
  	if (ret == kIOReturnSuccess) return 1;
  	return 0;
  }
  
  void teensy_close(void)
  {
  	if (!iokit_teensy_reference) return;
  	close_usb_device(iokit_teensy_reference);
  	iokit_teensy_reference = NULL;
  }
  
  int hard_reboot(void)
  {
  	IOHIDDeviceRef rebootor;
  	IOReturn ret;
  
  	rebootor = open_usb_device(0x16C0, 0x0477);
  
  	if (!rebootor)
  		rebootor = open_usb_device(0x03eb, 0x2067);
  
  	if (!rebootor) return 0;
  	ret = IOHIDDeviceSetReport(rebootor,
  		kIOHIDReportTypeOutput, 0, (uint8_t *)("reboot"), 6);
  	close_usb_device(rebootor);
  	if (ret == kIOReturnSuccess) return 1;
  	return 0;
  }
  
  #endif
  
  
  
  /****************************************************************/
  /*                                                              */
  /*              USB Access - BSD's UHID driver                  */
  /*                                                              */
  /****************************************************************/
  
  #if defined(USE_UHID)
  
  // Thanks to Todd T Fries for help getting this working on OpenBSD
  // and to Chris Kuethe for the initial patch to use UHID.
  
  #include <sys/ioctl.h>
  #include <fcntl.h>
  #include <dirent.h>
  #include <dev/usb/usb.h>
  #ifndef USB_GET_DEVICEINFO
  #include <dev/usb/usb_ioctl.h>
  #endif
  
  #ifndef USB_GET_DEVICEINFO
  # define USB_GET_DEVICEINFO 0
  # error The USB_GET_DEVICEINFO ioctl() value is not defined for your system.
  #endif
  
  int open_usb_device(int vid, int pid)
  {
  	int r, fd;
  	DIR *dir;
  	struct dirent *d;
  	struct usb_device_info info;
  	char buf[256];
  
  	dir = opendir("/dev");
  	if (!dir) return -1;
  	while ((d = readdir(dir)) != NULL) {
  		if (strncmp(d->d_name, "uhid", 4) != 0) continue;
  		snprintf(buf, sizeof(buf), "/dev/%s", d->d_name);
  		fd = open(buf, O_RDWR);
  		if (fd < 0) continue;
  		r = ioctl(fd, USB_GET_DEVICEINFO, &info);
  		if (r < 0) {
  			// NetBSD: added in 2004
  			// OpenBSD: added November 23, 2009
  			// FreeBSD: missing (FreeBSD 8.0) - USE_LIBUSB works!
  			die("Error: your uhid driver does not support"
  			  " USB_GET_DEVICEINFO, please upgrade!\n");
  			close(fd);
  			closedir(dir);
  			exit(1);
  		}
  		//printf("%s: v=%d, p=%d\n", buf, info.udi_vendorNo, info.udi_productNo);
  		if (info.udi_vendorNo == vid && info.udi_productNo == pid) {
  			closedir(dir);
  			return fd;
  		}
  		close(fd);
  	}
  	closedir(dir);
  	return -1;
  }
  
  static int uhid_teensy_fd = -1;
  
  int teensy_open(void)
  {
  	teensy_close();
  	uhid_teensy_fd = open_usb_device(0x16C0, 0x0478);
  
  	if (uhid_teensy_fd < 0)
  		uhid_teensy_fd = open_usb_device(0x03eb, 0x2067);
  
  	if (uhid_teensy_fd < 0) return 0;
  	return 1;
  }
  
  int teensy_write(void *buf, int len, double timeout)
  {
  	int r;
  
  	// TODO: implement timeout... how??
  	r = write(uhid_teensy_fd, buf, len);
  	if (r == len) return 1;
  	return 0;
  }
  
  void teensy_close(void)
  {
  	if (uhid_teensy_fd >= 0) {
  		close(uhid_teensy_fd);
  		uhid_teensy_fd = -1;
  	}
  }
  
  int hard_reboot(void)
  {
  	int r, rebootor_fd;
  
  	rebootor_fd = open_usb_device(0x16C0, 0x0477);
  
  	if (rebootor_fd < 0)
  		rebootor_fd = open_usb_device(0x03eb, 0x2067);
  
  	if (rebootor_fd < 0) return 0;
  	r = write(rebootor_fd, "reboot", 6);
  	delay(0.1);
  	close(rebootor_fd);
  	if (r == 6) return 1;
  	return 0;
  }
  
  #endif
  
  
  
  /****************************************************************/
  /*                                                              */
  /*                     Read Intel Hex File                      */
  /*                                                              */
  /****************************************************************/
  
  // the maximum flash image size we can support
  // chips with larger memory may be used, but only this
  // much intel-hex data can be loaded into memory!
  #define MAX_MEMORY_SIZE 0x10000
  
  static unsigned char firmware_image[MAX_MEMORY_SIZE];
  static unsigned char firmware_mask[MAX_MEMORY_SIZE];
  static int end_record_seen=0;
  static int byte_count;
  static unsigned int extended_addr = 0;
  static int parse_hex_line(char *line);
  
  int read_intel_hex(const char *filename)
  {
  	FILE *fp;
  	int i, lineno=0;
  	char buf[1024];
  
  	byte_count = 0;
  	end_record_seen = 0;
  	for (i=0; i<MAX_MEMORY_SIZE; i++) {
  		firmware_image[i] = 0xFF;
  		firmware_mask[i] = 0;
  	}
  	extended_addr = 0;
  
  	fp = fopen(filename, "r");
  	if (fp == NULL) {
  		//printf("Unable to read file %s\n", filename);
  		return -1;
  	}
  	while (!feof(fp)) {
  		*buf = '\0';
  		if (!fgets(buf, sizeof(buf), fp)) break;
  		lineno++;
  		if (*buf) {
  			if (parse_hex_line(buf) == 0) {
  				//printf("Warning, parse error line %d\n", lineno);
  				fclose(fp);
  				return -2;
  			}
  		}
  		if (end_record_seen) break;
  		if (feof(stdin)) break;
  	}
  	fclose(fp);
  	return byte_count;
  }
  
  
  /* from ihex.c, at http://www.pjrc.com/tech/8051/pm2_docs/intel-hex.html */
  
  /* parses a line of intel hex code, stores the data in bytes[] */
  /* and the beginning address in addr, and returns a 1 if the */
  /* line was valid, or a 0 if an error occurred.  The variable */
  /* num gets the number of bytes that were stored into bytes[] */
  
  
  int
  parse_hex_line(char *line)
  {
  	int addr, code, num;
          int sum, len, cksum, i;
          char *ptr;
  
          num = 0;
          if (line[0] != ':') return 0;
          if (strlen(line) < 11) return 0;
          ptr = line+1;
          if (!sscanf(ptr, "%02x", &len)) return 0;
          ptr += 2;
          if ((int)strlen(line) < (11 + (len * 2)) ) return 0;
          if (!sscanf(ptr, "%04x", &addr)) return 0;
          ptr += 4;
            /* printf("Line: length=%d Addr=%d\n", len, addr); */
          if (!sscanf(ptr, "%02x", &code)) return 0;
  	if (addr + extended_addr + len >= MAX_MEMORY_SIZE) return 0;
          ptr += 2;
          sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255);
  	if (code != 0) {
  		if (code == 1) {
  			end_record_seen = 1;
  			return 1;
  		}
  		if (code == 2 && len == 2) {
  			if (!sscanf(ptr, "%04x", &i)) return 1;
  			ptr += 4;
  			sum += ((i >> 8) & 255) + (i & 255);
          		if (!sscanf(ptr, "%02x", &cksum)) return 1;
  			if (((sum & 255) + (cksum & 255)) & 255) return 1;
  			extended_addr = i << 4;
  			//printf("ext addr = %05X\n", extended_addr);
  		}
  		if (code == 4 && len == 2) {
  			if (!sscanf(ptr, "%04x", &i)) return 1;
  			ptr += 4;
  			sum += ((i >> 8) & 255) + (i & 255);
          		if (!sscanf(ptr, "%02x", &cksum)) return 1;
  			if (((sum & 255) + (cksum & 255)) & 255) return 1;
  			extended_addr = i << 16;
  			//printf("ext addr = %08X\n", extended_addr);
  		}
  		return 1;	// non-data line
  	}
  	byte_count += len;
          while (num != len) {
                  if (sscanf(ptr, "%02x", &i) != 1) return 0;
  		i &= 255;
  		firmware_image[addr + extended_addr + num] = i;
  		firmware_mask[addr + extended_addr + num] = 1;
                  ptr += 2;
                  sum += i;
                  (num)++;
                  if (num >= 256) return 0;
          }
          if (!sscanf(ptr, "%02x", &cksum)) return 0;
          if (((sum & 255) + (cksum & 255)) & 255) return 0; /* checksum error */
          return 1;
  }
  
  int ihex_bytes_within_range(int begin, int end)
  {
  	int i;
  
  	if (begin < 0 || begin >= MAX_MEMORY_SIZE ||
  	   end < 0 || end >= MAX_MEMORY_SIZE) {
  		return 0;
  	}
  	for (i=begin; i<=end; i++) {
  		if (firmware_mask[i]) return 1;
  	}
  	return 0;
  }
  
  void ihex_get_data(int addr, int len, unsigned char *bytes)
  {
  	int i;
  
  	if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) {
  		for (i=0; i<len; i++) {
  			bytes[i] = 255;
  		}
  		return;
  	}
  	for (i=0; i<len; i++) {
  		if (firmware_mask[addr]) {
  			bytes[i] = firmware_image[addr];
  		} else {
  			bytes[i] = 255;
  		}
  		addr++;
  	}
  }
  
  /****************************************************************/
  /*                                                              */
  /*                       Misc Functions                         */
  /*                                                              */
  /****************************************************************/
  
  int printf_verbose(const char *format, ...)
  {
  	va_list ap;
  	int r;
  
  	va_start(ap, format);
  	if (verbose) {
  		r = vprintf(format, ap);
  		fflush(stdout);
  		return r;
  	}
  	return 0;
  }
  
  void delay(double seconds)
  {
  	#ifdef USE_WIN32
  	sleep(seconds * 1000.0);
  	#else
  	usleep(seconds * 1000000.0);
  	#endif
  }
  
  void die(const char *str, ...)
  {
  	va_list  ap;
  
  	va_start(ap, str);
  	vfprintf(stderr, str, ap);
  	fprintf(stderr, "\n");
  	exit(1);
  }
  
  #if defined USE_WIN32
  #define strcasecmp stricmp
  #endif
  
  void parse_options(int argc, char **argv)
  {
  	int i;
  	const char *arg;
  
  	for (i=1; i<argc; i++) {
  		arg = argv[i];
  
  		if (*arg == '-') {
  			if (strcmp(arg, "-w") == 0) {
  				wait_for_device_to_appear = 1;
  			} else if (strcmp(arg, "-r") == 0) {
  				hard_reboot_device = 1;
  			} else if (strcmp(arg, "-n") == 0) {
  				reboot_after_programming = 0;
  			} else if (strcmp(arg, "-v") == 0) {
  				verbose = 1;
  			} else if (strncmp(arg, "-mmcu=", 6) == 0) {
  				arg += 6;
  
  				if (strncmp(arg, "at90usb", 7) == 0) {
  					arg += 7;
  				} else if (strncmp(arg, "atmega", 6) == 0) {
  					arg += 6;
  				} else {
  					die("Unknown MCU type\n");
  				}
  
  				if (strncmp(arg, "128", 3) == 0) {
  					code_size  = 128 * 1024;
  					block_size = 256;
  				} else if (strncmp(arg, "64", 2) == 0) {
  					code_size  = 64 * 1024;
  					block_size = 256;
  				} else if (strncmp(arg, "32", 2) == 0) {
  					code_size  = 32 * 1024;
  					block_size = 128;
  				} else if (strncmp(arg, "16", 2) == 0) {
  					code_size  = 16 * 1024;
  					block_size = 128;
  				} else if (strncmp(arg, "8", 1) == 0) {
  					code_size  = 8 * 1024;
  					block_size = 128;
  				} else {
  					die("Unknown MCU type\n");
  				}
  			}
  		} else {
  			filename = argv[i];
  		}
  	}
  }
  
  