Update HID bootloader loader application to support the VID/PID used by the bootloader.
[pub/lufa.git] / Bootloaders / HID / HostLoaderApp / hid_bootloader_cli.c
diff --git a/Bootloaders/HID/HostLoaderApp/hid_bootloader_cli.c b/Bootloaders/HID/HostLoaderApp/hid_bootloader_cli.c
new file mode 100644 (file)
index 0000000..9cb81b5
--- /dev/null
@@ -0,0 +1,1021 @@
+/* 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-lub.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;
+       char buf[128];
+       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;
+       if (n <= 0) return 0;
+       return 1;
+}
+
+void print_win32_err(void)
+{
+        char buf[256];
+        DWORD err;
+
+        err = GetLastError();
+        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
+                0, buf, sizeof(buf), NULL);
+        printf("err %ld: %s\n", err, buf);
+}
+
+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 1;
+       return 0;
+}
+
+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 1;
+       return 0;
+}
+
+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
+
+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) return 0;
+       return 1;
+}
+
+int teensy_write(void *buf, int len, double timeout)
+{
+       int r;
+
+       // TODO: imeplement 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);
+                               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 occured.  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 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(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;
+
+                               uint8_t valid_prefix = 0;
+
+                               if (strncmp(arg, "at90usb", 7) == 0) {
+                                       valid_prefix = 1;
+                                       arg += 7;
+                               } else if (strncmp(arg, "atmega", 6) == 0) {
+                                       valid_prefix = 1;
+                                       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];
+               }
+       }
+}
+