diff --git a/CMakeLists.txt b/CMakeLists.txt index 8526109..a227858 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,8 +25,8 @@ set( CMAKE_USE_RELATIVE_PATHS 1 ) #| "avr" # Teensy++ 2.0 #| "arm" # Teensy 3.0 #| "arm" # Teensy 3.1 -#set( COMPILER_FAMILY "arm" ) -set( COMPILER_FAMILY "avr" ) +set( COMPILER_FAMILY "arm" ) +#set( COMPILER_FAMILY "avr" ) message( STATUS "Compiler Family:" ) message( "${COMPILER_FAMILY}" ) @@ -142,7 +142,8 @@ add_custom_command( TARGET ${TARGET_ELF} POST_BUILD #| After Changes Size Information #| TODO Do lookup on Flash and RAM sizes and do % used -add_custom_target( SizeAfter ALL ${SIZE} --target=${FORMAT} ${TARGET_HEX} ${TARGET_ELF} +add_custom_target( SizeAfter ALL + COMMAND ${SIZE} --target=${FORMAT} ${TARGET_HEX} ${TARGET_ELF} DEPENDS ${TARGET_ELF} COMMENT "Size after generation\n\tFlash Usage: data (hex)\n\t RAM Usage: data (elf)" ) @@ -150,17 +151,9 @@ add_custom_target( SizeAfter ALL ${SIZE} --target=${FORMAT} ${TARGET_HEX} ${TARG ### -# Setup Loader Script +# Setup Loader Script and Program # #| Provides the user with the correct teensy-loader-cli command for the built .HEX file -#| teensy-loader-cli must be in the user's path -if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" ) - configure_file( LoadFile/bash load ) -endif() - -#| TODO Windows -if( ${CMAKE_SYSTEM_NAME} MATCHES "Windows" ) - configure_file( LoadFile/bash load ) -endif() +configure_file( LoadFile/load load ) diff --git a/LoadFile/CMakeLists.txt b/LoadFile/CMakeLists.txt new file mode 100644 index 0000000..a2f252b --- /dev/null +++ b/LoadFile/CMakeLists.txt @@ -0,0 +1,118 @@ +###| CMAKE teensy-loader-cli |### +# +# Jacob Alexander 2014 +# Written to replace the pjrc's kludey Makefiles +# (that require hand edits for different platforms) +# +# Released into the Public Domain +# +### + +#| Windows / Cygwin Compatibility options +set( CMAKE_LEGACY_CYGWIN_WIN32 0 ) +set( CMAKE_USE_RELATIVE_PATHS 1 ) + + + +### +# Project Description +# + +#| Project +project( teensy-loader-cli ) + +#| Target Name (output name) +set( TARGET teensy-loader-cli ) + +#| General Settings +cmake_minimum_required( VERSION 2.8 ) + + + +### +# Source Defines +# + +#| Sources +set( SRCS + teensy_loader_cli.c +) + + + +### +# Platform Setup +# +list( APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR} ) # Use local find scripts + +#| Linux - libusb +if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" ) + # Find libusb (not 1.0) + find_package( LibUSB REQUIRED ) + + # Defines + set( DEFINES -s -DUSE_LIBUSB ) + + # Include directories + set( INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ) + + # Libraries + set( LIBS ${LIBUSB_LIBRARIES} ) + +#| Windows +elseif( ${CMAKE_SYSTEM_NAME} MATCHES "Windows" ) + message( AUTHOR_WARNING "Not Tested...") + + # Defines + set( DEFINES -s -DUSE_WIN32 ) + + # Libraries + set( LIBS hid setupapi ) + +#| Mac OS X +elseif( ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" ) + message( AUTHOR_WARNING "Not Tested...") + + # Defines - XXX What is SDK? + set( DEFINES -DUSE_APPLE_IOKIT -isysroot ${SDK} -Wl,-syslibroot,${SDK} -framework IOKit -framework CoreFoundation ) + +#| BSD - NetBSD and OpenBSD +elseif( ${CMAKE_SYSTEM_NAME} MATCHES "BSD" ) + message( AUTHOR_WARNING "Not Tested...") + + # Defines + set( DEFINES -s -DUSE_UHID ) +endif() + + + +### +# Defines +# + +#| Default CFLAGS +set( CFLAGS -O2 -Wall ) + +add_definitions( ${CFLAGS} ${DEFINES} ) + + + +### +# Includes +# + +#| Linux +include_directories( ${INCLUDE_DIRS} ) + + + +### +# Build Targets +# + +#| Create the executable +add_executable( ${TARGET} ${SRCS} ) + +#| Link executable +target_link_libraries( ${TARGET} ${LIBS} ) + diff --git a/LoadFile/FindLibUSB.cmake b/LoadFile/FindLibUSB.cmake new file mode 100644 index 0000000..9f7b264 --- /dev/null +++ b/LoadFile/FindLibUSB.cmake @@ -0,0 +1,94 @@ +# Attempts to find libusb (not libusb-1.0) +# +# LIBUSB_FOUND - system has libusb +# LIBUSB_INCLUDE_DIRS - the libusb include directory +# LIBUSB_LIBRARIES - Link these to use libusb +# LIBUSB_DEFINITIONS - Compiler switches required for using libusb +# +# Adapted from cmake-modules Google Code project +# +# Copyright (c) 2006 Andreas Schneider +# +# (Changes for libusb) Copyright (c) 2014 Jacob Alexander +# +# Redistribution and use is allowed according to the terms of the New BSD license. +# +# CMake-Modules Project New BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of the CMake-Modules Project nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +if ( LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS ) + # in cache already + set( LIBUSB_FOUND TRUE ) +else () + find_path( LIBUSB_INCLUDE_DIR + NAMES + usb.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + /include + ) + + find_library( LIBUSB_LIBRARY + NAMES + usb + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + /lib + ) + + set( LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIR} ) + set( LIBUSB_LIBRARIES ${LIBUSB_LIBRARY} ) + + if ( LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES ) + set( LIBUSB_FOUND TRUE ) + endif () + + if ( LIBUSB_FOUND ) + if ( NOT LIBUSB_FIND_QUIETLY ) + message( STATUS "Found libusb:" ) + message( STATUS " - Includes: ${LIBUSB_INCLUDE_DIRS}" ) + message( STATUS " - Libraries: ${LIBUSB_LIBRARIES}" ) + endif () + else () + if ( LIBUSB_FIND_REQUIRED ) + message( FATAL_ERROR "Could not find libusb" ) + endif () + endif () + + # show the LIBUSB_INCLUDE_DIRS and LIBUSB_LIBRARIES variables only in the advanced view + mark_as_advanced( LIBUSB_INCLUDE_DIRS LIBUSB_LIBRARIES ) + +endif () + diff --git a/LoadFile/bash b/LoadFile/bash deleted file mode 100755 index dc36d42..0000000 --- a/LoadFile/bash +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -#| Loads the hex file onto the teensy - -sudo teensy-loader-cli -mmcu=@MCU@ -w @TARGET_HEX@ - -exit 0 - diff --git a/LoadFile/load b/LoadFile/load new file mode 100755 index 0000000..2b38091 --- /dev/null +++ b/LoadFile/load @@ -0,0 +1,16 @@ +#!/bin/bash + +#| First check to see teensy-loader-cli has been compiled +if [ ! -e teensy-loader-cli/teensy-loader-cli ]; then + # Compile teensy-loader-cli + mkdir -p teensy-loader-cli + cd teensy-loader-cli + cmake @CMAKE_SOURCE_DIR@/LoadFile + make +fi + +#| Loads the hex file onto the teensy +sudo teensy-loader-cli/teensy-loader-cli -mmcu=@MCU@ -w @TARGET_HEX@ + +exit 0 + diff --git a/LoadFile/teensy_loader_cli.c b/LoadFile/teensy_loader_cli.c new file mode 100644 index 0000000..dfe31c1 --- /dev/null +++ b/LoadFile/teensy_loader_cli.c @@ -0,0 +1,1009 @@ +/* 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 + * Modifications 2014, Jacob Alexander + * + * 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 +#include +#include +#include +#include +#include + +void usage(void) +{ + fprintf(stderr, "Usage: teensy_loader_cli -mmcu= [-w] [-h] [-n] [-v] \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"); +#if defined(USE_LIBUSB) + fprintf(stderr, "\n = atmega32u4 | at90usb162 | at90usb646 | at90usb1286 | mk20dx128 | mk20dx256\n"); +#else + fprintf(stderr, "\n = atmega32u4 | at90usb162 | at90usb646 | at90usb1286\n"); +#endif + fprintf(stderr, "\nFor more information, please visit:\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); +int memory_is_blank(int addr, int block_size); + +// 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[2048]; + int num, addr, r, write_size=block_size+2; + int 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, Kiibohd-Mods\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 (!first_block && !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; + } + if (!first_block && memory_is_blank(addr, block_size)) continue; + printf_verbose("."); + if (code_size < 0x10000) { + buf[0] = addr & 255; + buf[1] = (addr >> 8) & 255; + ihex_get_data(addr, block_size, buf + 2); + write_size = block_size + 2; + } else if (block_size == 256) { + buf[0] = (addr >> 8) & 255; + buf[1] = (addr >> 16) & 255; + ihex_get_data(addr, block_size, buf + 2); + write_size = block_size + 2; + } else if (block_size >= 1024) { + buf[0] = addr & 255; + buf[1] = (addr >> 8) & 255; + buf[2] = (addr >> 16) & 255; + memset(buf + 3, 0, 61); + ihex_get_data(addr, block_size, buf + 64); + write_size = block_size + 64; + } else { + die("Unknown code/block size\n"); + } + r = teensy_write(buf, write_size, 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; + buf[2] = 0xFF; + memset(buf + 3, 0, sizeof(buf) - 3); + teensy_write(buf, write_size, 0.25); + } + teensy_close(); + return 0; +} + + + + +/****************************************************************/ +/* */ +/* USB Access - libusb (Linux & FreeBSD) */ +/* */ +/****************************************************************/ + +#if defined(USE_LIBUSB) + +// http://libusb.sourceforge.net/doc/index.html +#include + +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) return 1; + return 0; +} + +int teensy_write(void *buf, int len, double timeout) +{ + int r; + + if (!libusb_teensy_handle) return 0; + while (timeout > 0) { + r = usb_control_msg(libusb_teensy_handle, 0x21, 9, 0x0200, 0, + (char *)buf, len, (int)(timeout * 1000.0)); + if (r >= 0) return 1; + //printf("teensy_write, r=%d\n", r); + usleep(10000); + timeout -= 0.01; // TODO: subtract actual elapsed time + } + return 0; +} + +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) 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 +#include +#include +#include + +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) 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)); + //if (!r) print_win32_err(); + 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) 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 +#include +#include + +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) 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) 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 +#include +#include +#include +#ifndef USB_GET_DEVICEINFO +#include +#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) 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 0x40000 + +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) 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 MAX_MEMORY_SIZE) return 1; + + while (block_size && addr < MAX_MEMORY_SIZE) { + if (firmware_mask[addr] && firmware_image[addr] != 255) return 0; + addr++; + block_size--; + } + return 1; +} + + + + +/****************************************************************/ +/* */ +/* 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