2014-04-13 03:52:32 +00:00
|
|
|
/* 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 <haata@kiibohd.com>
|
|
|
|
*
|
|
|
|
* 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: teensy_loader_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");
|
|
|
|
#if defined(USE_LIBUSB)
|
|
|
|
fprintf(stderr, "\n<MCU> = atmega32u4 | at90usb162 | at90usb646 | at90usb1286 | mk20dx128 | mk20dx256\n");
|
|
|
|
#else
|
|
|
|
fprintf(stderr, "\n<MCU> = 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",
|
2015-03-09 01:40:01 +00:00
|
|
|
filename, num, (double)num / (double)code_size * 100.0);
|
2014-04-13 03:52:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
/* */
|
2014-04-18 20:16:47 +00:00
|
|
|
/* USB Access - libusb (Linux, Windows & FreeBSD) */
|
2014-04-13 03:52:32 +00:00
|
|
|
/* */
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
|
|
#if defined(USE_LIBUSB)
|
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
#include <libusb-1.0/libusb.h>
|
2014-04-13 03:52:32 +00:00
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
struct libusb_device_handle *open_usb_device( int vid, int pid )
|
2014-04-13 03:52:32 +00:00
|
|
|
{
|
2014-04-18 07:18:02 +00:00
|
|
|
libusb_device **devs;
|
|
|
|
struct libusb_device_handle *handle = NULL;
|
|
|
|
struct libusb_device_handle *ret = NULL;
|
|
|
|
|
|
|
|
if ( libusb_init( NULL ) != 0 )
|
|
|
|
fprintf( stderr, "libusb_init failed.\n" );
|
|
|
|
|
|
|
|
size_t count = libusb_get_device_list( NULL, &devs );
|
|
|
|
if ( count < 0 )
|
|
|
|
fprintf( stderr, "libusb_get_device_list failed.\n" );
|
|
|
|
|
|
|
|
for ( size_t c = 0; c < count; c++ )
|
|
|
|
{
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
libusb_device *dev = devs[c];
|
2014-04-13 03:52:32 +00:00
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
if ( libusb_get_device_descriptor( dev, &desc ) < 0 )
|
|
|
|
fprintf( stderr, "libusb_get_device_descriptor failed.\n" );
|
|
|
|
|
|
|
|
//printf("ID: %04x Product: %04x\n", desc.idVendor, desc.idProduct );
|
|
|
|
// Not correct device
|
|
|
|
if ( desc.idVendor != vid || desc.idProduct != pid )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Attempt to open the device
|
|
|
|
if ( libusb_open( dev, &handle ) != 0 )
|
|
|
|
{
|
|
|
|
fprintf( stderr, "Found device but unable to open\n" );
|
|
|
|
continue;
|
2014-04-13 03:52:32 +00:00
|
|
|
}
|
2014-04-18 07:18:02 +00:00
|
|
|
|
|
|
|
// Only required on Linux, other platforms will just ignore this call
|
|
|
|
libusb_detach_kernel_driver( handle, 0 );
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// XXX Is this still valid with libusb-1.0?
|
|
|
|
|
|
|
|
// Attempt to claim interface
|
|
|
|
int err = libusb_claim_interface( handle, 0 );
|
|
|
|
if ( err < 0 )
|
|
|
|
{
|
|
|
|
libusb_close( handle );
|
|
|
|
fprintf( stderr, "Unable to claim interface, check USB permissions: %d\n", err );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = handle;
|
|
|
|
break;
|
2014-04-13 03:52:32 +00:00
|
|
|
}
|
2014-04-18 07:18:02 +00:00
|
|
|
|
|
|
|
libusb_free_device_list( devs, 1 );
|
|
|
|
|
|
|
|
return ret;
|
2014-04-13 03:52:32 +00:00
|
|
|
}
|
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
static struct libusb_device_handle *libusb_teensy_handle = NULL;
|
2014-04-13 03:52:32 +00:00
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
int teensy_open()
|
2014-04-13 03:52:32 +00:00
|
|
|
{
|
|
|
|
teensy_close();
|
2014-04-18 07:18:02 +00:00
|
|
|
|
|
|
|
libusb_teensy_handle = open_usb_device( 0x16C0, 0x0478 );
|
|
|
|
|
|
|
|
if ( libusb_teensy_handle )
|
|
|
|
return 1;
|
|
|
|
|
2014-04-13 03:52:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
int teensy_write( void *buf, int len, double timeout )
|
2014-04-13 03:52:32 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
if ( !libusb_teensy_handle )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while ( timeout > 0 ) {
|
|
|
|
r = libusb_control_transfer( libusb_teensy_handle,
|
|
|
|
0x21, 9, 0x0200, 0,
|
|
|
|
(unsigned char *)buf, len,
|
|
|
|
(int)(timeout * 1000.0) );
|
|
|
|
|
|
|
|
if ( r >= 0 )
|
|
|
|
return 1;
|
|
|
|
|
2014-04-13 03:52:32 +00:00
|
|
|
//printf("teensy_write, r=%d\n", r);
|
2014-04-18 07:18:02 +00:00
|
|
|
usleep( 10000 );
|
2014-04-13 03:52:32 +00:00
|
|
|
timeout -= 0.01; // TODO: subtract actual elapsed time
|
|
|
|
}
|
2014-04-18 07:18:02 +00:00
|
|
|
|
2014-04-13 03:52:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
void teensy_close()
|
2014-04-13 03:52:32 +00:00
|
|
|
{
|
2014-04-18 07:18:02 +00:00
|
|
|
if ( !libusb_teensy_handle)
|
|
|
|
return;
|
|
|
|
|
|
|
|
libusb_release_interface( libusb_teensy_handle, 0 );
|
|
|
|
libusb_close( libusb_teensy_handle );
|
|
|
|
|
2014-04-13 03:52:32 +00:00
|
|
|
libusb_teensy_handle = NULL;
|
|
|
|
}
|
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
int hard_reboot()
|
2014-04-13 03:52:32 +00:00
|
|
|
{
|
2014-04-18 07:18:02 +00:00
|
|
|
struct libusb_device_handle *rebootor;
|
2014-04-13 03:52:32 +00:00
|
|
|
int r;
|
|
|
|
|
2014-04-18 07:18:02 +00:00
|
|
|
rebootor = open_usb_device( 0x16C0, 0x0477 );
|
|
|
|
|
|
|
|
if (!rebootor)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
r = libusb_control_transfer( rebootor,
|
|
|
|
0x21, 9, 0x0200, 0,
|
|
|
|
(unsigned char*)"reboot", 6,
|
|
|
|
100 );
|
|
|
|
|
|
|
|
libusb_release_interface( rebootor, 0 );
|
|
|
|
libusb_close( rebootor );
|
|
|
|
|
|
|
|
if (r < 0)
|
|
|
|
return 0;
|
|
|
|
|
2014-04-13 03:52:32 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#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) 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 <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) 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; 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, HEX 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;
|
2015-03-09 01:40:01 +00:00
|
|
|
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;
|
2014-04-13 03:52:32 +00:00
|
|
|
if (addr + extended_addr + len >= MAX_MEMORY_SIZE) return 0;
|
2015-03-09 01:40:01 +00:00
|
|
|
ptr += 2;
|
|
|
|
sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255);
|
2014-04-13 03:52:32 +00:00
|
|
|
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);
|
2015-03-09 01:40:01 +00:00
|
|
|
if (!sscanf(ptr, "%02x", &cksum)) return 1;
|
2014-04-13 03:52:32 +00:00
|
|
|
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);
|
2015-03-09 01:40:01 +00:00
|
|
|
if (!sscanf(ptr, "%02x", &cksum)) return 1;
|
2014-04-13 03:52:32 +00:00
|
|
|
if (((sum & 255) + (cksum & 255)) & 255) return 1;
|
|
|
|
extended_addr = i << 16;
|
|
|
|
//printf("ext addr = %08X\n", extended_addr);
|
|
|
|
}
|
2015-03-09 01:40:01 +00:00
|
|
|
return 1; // non-data line
|
2014-04-13 03:52:32 +00:00
|
|
|
}
|
|
|
|
byte_count += len;
|
2015-03-09 01:40:01 +00:00
|
|
|
while (num != len) {
|
|
|
|
if (sscanf(ptr, "%02x", &i) != 1) return 0;
|
2014-04-13 03:52:32 +00:00
|
|
|
i &= 255;
|
|
|
|
firmware_image[addr + extended_addr + num] = i;
|
|
|
|
firmware_mask[addr + extended_addr + num] = 1;
|
2015-03-09 01:40:01 +00:00
|
|
|
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;
|
2014-04-13 03:52:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int memory_is_blank(int addr, int block_size)
|
|
|
|
{
|
|
|
|
if (addr < 0 || addr > 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<argc; i++) {
|
|
|
|
arg = argv[i];
|
|
|
|
//printf("arg: %s\n", arg);
|
|
|
|
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) {
|
|
|
|
if (strcasecmp(arg+6, "at90usb162") == 0) {
|
|
|
|
code_size = 15872;
|
|
|
|
block_size = 128;
|
|
|
|
} else if (strcasecmp(arg+6, "atmega32u4") == 0) {
|
|
|
|
code_size = 32256;
|
|
|
|
block_size = 128;
|
|
|
|
} else if (strcasecmp(arg+6, "at90usb646") == 0) {
|
|
|
|
code_size = 64512;
|
|
|
|
block_size = 256;
|
|
|
|
} else if (strcasecmp(arg+6, "at90usb1286") == 0) {
|
|
|
|
code_size = 130048;
|
|
|
|
block_size = 256;
|
|
|
|
#if defined(USE_LIBUSB)
|
|
|
|
} else if (strcasecmp(arg+6, "mk20dx128") == 0) {
|
|
|
|
code_size = 131072;
|
|
|
|
block_size = 1024;
|
|
|
|
} else if (strcasecmp(arg+6, "mk20dx256") == 0) {
|
|
|
|
code_size = 262144;
|
|
|
|
block_size = 1024;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
die("Unknown MCU type\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
filename = argv[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|