Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright © 2001 Stephen Williams (steve@icarus.com) |
| 3 | * Copyright © 2001-2002 David Brownell (dbrownell@users.sourceforge.net) |
| 4 | * Copyright © 2008 Roger Williams (rawqux@users.sourceforge.net) |
| 5 | * Copyright © 2012 Pete Batard (pete@akeo.ie) |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 6 | * Copyright © 2013 Federico Manzan (f.manzan@gmail.com) |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 7 | * |
| 8 | * This source code is free software; you can redistribute it |
| 9 | * and/or modify it in source code form under the terms of the GNU |
| 10 | * General Public License as published by the Free Software |
| 11 | * Foundation; either version 2 of the License, or (at your option) |
| 12 | * any later version. |
| 13 | * |
| 14 | * This program is distributed in the hope that it will be useful, |
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | * GNU General Public License for more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU General Public License |
| 20 | * along with this program; if not, write to the Free Software |
| 21 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
| 22 | */ |
| 23 | |
Chris Dickens | f2e551a | 2020-11-27 15:22:29 -0800 | [diff] [blame] | 24 | #include <config.h> |
| 25 | |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 26 | #include <stdlib.h> |
| 27 | #include <stdio.h> |
| 28 | #include <string.h> |
| 29 | #include <stdint.h> |
| 30 | #include <stdarg.h> |
| 31 | #include <sys/types.h> |
| 32 | #include <getopt.h> |
| 33 | |
Ludovic Rousseau | 38e6eb8 | 2012-10-12 23:28:51 +0200 | [diff] [blame] | 34 | #include "libusb.h" |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 35 | #include "ezusb.h" |
| 36 | |
Chris Dickens | 6f0330c | 2020-04-09 11:46:03 -0700 | [diff] [blame] | 37 | #if !defined(_WIN32) || defined(__CYGWIN__) |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 38 | #include <syslog.h> |
| 39 | static bool dosyslog = false; |
| 40 | #include <strings.h> |
| 41 | #define _stricmp strcasecmp |
| 42 | #endif |
| 43 | |
| 44 | #ifndef FXLOAD_VERSION |
hjelmn@cs.unm.edu | 1eff220 | 2014-01-08 23:50:34 +0000 | [diff] [blame] | 45 | #define FXLOAD_VERSION (__DATE__ " (libusb)") |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 46 | #endif |
| 47 | |
| 48 | #ifndef ARRAYSIZE |
| 49 | #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) |
| 50 | #endif |
| 51 | |
| 52 | void logerror(const char *format, ...) |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 53 | { |
| 54 | va_list ap; |
| 55 | va_start(ap, format); |
| 56 | |
Chris Dickens | 6f0330c | 2020-04-09 11:46:03 -0700 | [diff] [blame] | 57 | #if !defined(_WIN32) || defined(__CYGWIN__) |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 58 | if (dosyslog) |
| 59 | vsyslog(LOG_ERR, format, ap); |
| 60 | else |
| 61 | #endif |
| 62 | vfprintf(stderr, format, ap); |
| 63 | va_end(ap); |
| 64 | } |
| 65 | |
Pete Batard | 77a37cb | 2013-04-14 22:38:52 +0100 | [diff] [blame] | 66 | static int print_usage(int error_code) { |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 67 | fprintf(stderr, "\nUsage: fxload [-v] [-V] [-t type] [-d vid:pid] [-p bus,addr] [-s loader] -i firmware\n"); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 68 | fprintf(stderr, " -i <path> -- Firmware to upload\n"); |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 69 | fprintf(stderr, " -s <path> -- Second stage loader\n"); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 70 | fprintf(stderr, " -t <type> -- Target type: an21, fx, fx2, fx2lp, fx3\n"); |
| 71 | fprintf(stderr, " -d <vid:pid> -- Target device, as an USB VID:PID\n"); |
hjelmn@cs.unm.edu | 1eff220 | 2014-01-08 23:50:34 +0000 | [diff] [blame] | 72 | fprintf(stderr, " -p <bus,addr> -- Target device, as a libusb bus number and device address path\n"); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 73 | fprintf(stderr, " -v -- Increase verbosity\n"); |
Pete Batard | 77a37cb | 2013-04-14 22:38:52 +0100 | [diff] [blame] | 74 | fprintf(stderr, " -q -- Decrease verbosity (silent mode)\n"); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 75 | fprintf(stderr, " -V -- Print program version\n"); |
Pete Batard | 77a37cb | 2013-04-14 22:38:52 +0100 | [diff] [blame] | 76 | return error_code; |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 77 | } |
| 78 | |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 79 | #define FIRMWARE 0 |
| 80 | #define LOADER 1 |
| 81 | int main(int argc, char*argv[]) |
| 82 | { |
| 83 | fx_known_device known_device[] = FX_KNOWN_DEVICES; |
| 84 | const char *path[] = { NULL, NULL }; |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 85 | const char *device_id = NULL; |
| 86 | const char *device_path = getenv("DEVICE"); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 87 | const char *type = NULL; |
| 88 | const char *fx_name[FX_TYPE_MAX] = FX_TYPE_NAMES; |
| 89 | const char *ext, *img_name[] = IMG_TYPE_NAMES; |
| 90 | int fx_type = FX_TYPE_UNDEFINED, img_type[ARRAYSIZE(path)]; |
Ludovic Rousseau | 686ccc2 | 2016-02-27 17:52:05 +0100 | [diff] [blame] | 91 | int opt, status; |
| 92 | unsigned int i, j; |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 93 | unsigned vid = 0, pid = 0; |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 94 | unsigned busnum = 0, devaddr = 0, _busnum, _devaddr; |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 95 | libusb_device *dev, **devs; |
| 96 | libusb_device_handle *device = NULL; |
| 97 | struct libusb_device_descriptor desc; |
| 98 | |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 99 | while ((opt = getopt(argc, argv, "qvV?hd:p:i:I:s:S:t:")) != EOF) |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 100 | switch (opt) { |
| 101 | |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 102 | case 'd': |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 103 | device_id = optarg; |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 104 | if (sscanf(device_id, "%x:%x" , &vid, &pid) != 2 ) { |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 105 | fputs ("please specify VID & PID as \"vid:pid\" in hexadecimal format\n", stderr); |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 106 | return -1; |
| 107 | } |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 108 | break; |
| 109 | |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 110 | case 'p': |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 111 | device_path = optarg; |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 112 | if (sscanf(device_path, "%u,%u", &busnum, &devaddr) != 2 ) { |
| 113 | fputs ("please specify bus number & device number as \"bus,dev\" in decimal format\n", stderr); |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 114 | return -1; |
| 115 | } |
| 116 | break; |
| 117 | |
| 118 | case 'i': |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 119 | case 'I': |
| 120 | path[FIRMWARE] = optarg; |
| 121 | break; |
| 122 | |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 123 | case 's': |
| 124 | case 'S': |
| 125 | path[LOADER] = optarg; |
| 126 | break; |
| 127 | |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 128 | case 'V': |
| 129 | puts(FXLOAD_VERSION); |
| 130 | return 0; |
| 131 | |
| 132 | case 't': |
| 133 | type = optarg; |
| 134 | break; |
| 135 | |
| 136 | case 'v': |
| 137 | verbose++; |
| 138 | break; |
| 139 | |
Pete Batard | 77a37cb | 2013-04-14 22:38:52 +0100 | [diff] [blame] | 140 | case 'q': |
| 141 | verbose--; |
| 142 | break; |
| 143 | |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 144 | case '?': |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 145 | case 'h': |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 146 | default: |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 147 | return print_usage(-1); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 148 | |
| 149 | } |
| 150 | |
| 151 | if (path[FIRMWARE] == NULL) { |
| 152 | logerror("no firmware specified!\n"); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 153 | return print_usage(-1); |
| 154 | } |
| 155 | if ((device_id != NULL) && (device_path != NULL)) { |
Carl Karsten | b38ac3f | 2014-04-06 10:06:35 -0500 | [diff] [blame] | 156 | logerror("only one of -d or -p can be specified\n"); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 157 | return print_usage(-1); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | /* determine the target type */ |
| 161 | if (type != NULL) { |
| 162 | for (i=0; i<FX_TYPE_MAX; i++) { |
| 163 | if (strcmp(type, fx_name[i]) == 0) { |
| 164 | fx_type = i; |
| 165 | break; |
| 166 | } |
| 167 | } |
| 168 | if (i >= FX_TYPE_MAX) { |
| 169 | logerror("illegal microcontroller type: %s\n", type); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 170 | return print_usage(-1); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 171 | } |
| 172 | } |
| 173 | |
hjelmn@cs.unm.edu | 1eff220 | 2014-01-08 23:50:34 +0000 | [diff] [blame] | 174 | /* open the device using libusb */ |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 175 | status = libusb_init(NULL); |
| 176 | if (status < 0) { |
| 177 | logerror("libusb_init() failed: %s\n", libusb_error_name(status)); |
| 178 | return -1; |
| 179 | } |
Chris Dickens | 539f22e | 2017-07-10 22:37:13 -0700 | [diff] [blame] | 180 | libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, verbose); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 181 | |
| 182 | /* try to pick up missing parameters from known devices */ |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 183 | if ((type == NULL) || (device_id == NULL) || (device_path != NULL)) { |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 184 | if (libusb_get_device_list(NULL, &devs) < 0) { |
| 185 | logerror("libusb_get_device_list() failed: %s\n", libusb_error_name(status)); |
| 186 | goto err; |
| 187 | } |
| 188 | for (i=0; (dev=devs[i]) != NULL; i++) { |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 189 | _busnum = libusb_get_bus_number(dev); |
| 190 | _devaddr = libusb_get_device_address(dev); |
| 191 | if ((type != NULL) && (device_path != NULL)) { |
| 192 | // if both a type and bus,addr were specified, we just need to find our match |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 193 | if ((libusb_get_bus_number(dev) == busnum) && (libusb_get_device_address(dev) == devaddr)) |
| 194 | break; |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 195 | } else { |
| 196 | status = libusb_get_device_descriptor(dev, &desc); |
| 197 | if (status >= 0) { |
Pete Batard | 77a37cb | 2013-04-14 22:38:52 +0100 | [diff] [blame] | 198 | if (verbose >= 3) { |
| 199 | logerror("examining %04x:%04x (%d,%d)\n", |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 200 | desc.idVendor, desc.idProduct, _busnum, _devaddr); |
| 201 | } |
| 202 | for (j=0; j<ARRAYSIZE(known_device); j++) { |
| 203 | if ((desc.idVendor == known_device[j].vid) |
| 204 | && (desc.idProduct == known_device[j].pid)) { |
| 205 | if (// nothing was specified |
| 206 | ((type == NULL) && (device_id == NULL) && (device_path == NULL)) || |
| 207 | // vid:pid was specified and we have a match |
| 208 | ((type == NULL) && (device_id != NULL) && (vid == desc.idVendor) && (pid == desc.idProduct)) || |
| 209 | // bus,addr was specified and we have a match |
| 210 | ((type == NULL) && (device_path != NULL) && (busnum == _busnum) && (devaddr == _devaddr)) || |
| 211 | // type was specified and we have a match |
| 212 | ((type != NULL) && (device_id == NULL) && (device_path == NULL) && (fx_type == known_device[j].type)) ) { |
| 213 | fx_type = known_device[j].type; |
| 214 | vid = desc.idVendor; |
| 215 | pid = desc.idProduct; |
| 216 | busnum = _busnum; |
| 217 | devaddr = _devaddr; |
| 218 | break; |
| 219 | } |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 220 | } |
| 221 | } |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 222 | if (j < ARRAYSIZE(known_device)) { |
| 223 | if (verbose) |
| 224 | logerror("found device '%s' [%04x:%04x] (%d,%d)\n", |
| 225 | known_device[j].designation, vid, pid, busnum, devaddr); |
| 226 | break; |
| 227 | } |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 228 | } |
| 229 | } |
| 230 | } |
| 231 | if (dev == NULL) { |
| 232 | libusb_free_device_list(devs, 1); |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 233 | libusb_exit(NULL); |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 234 | logerror("could not find a known device - please specify type and/or vid:pid and/or bus,dev\n"); |
| 235 | return print_usage(-1); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 236 | } |
| 237 | status = libusb_open(dev, &device); |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 238 | libusb_free_device_list(devs, 1); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 239 | if (status < 0) { |
| 240 | logerror("libusb_open() failed: %s\n", libusb_error_name(status)); |
| 241 | goto err; |
| 242 | } |
Pete Batard | 8aa5063 | 2013-03-16 23:36:47 +0000 | [diff] [blame] | 243 | } else if (device_id != NULL) { |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 244 | device = libusb_open_device_with_vid_pid(NULL, (uint16_t)vid, (uint16_t)pid); |
| 245 | if (device == NULL) { |
| 246 | logerror("libusb_open() failed\n"); |
| 247 | goto err; |
| 248 | } |
| 249 | } |
Federico Manzan | d345a1e | 2013-03-10 07:56:56 +0000 | [diff] [blame] | 250 | |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 251 | /* We need to claim the first interface */ |
Hans de Goede | 02281fd | 2013-06-14 11:06:47 +0200 | [diff] [blame] | 252 | libusb_set_auto_detach_kernel_driver(device, 1); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 253 | status = libusb_claim_interface(device, 0); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 254 | if (status != LIBUSB_SUCCESS) { |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 255 | libusb_close(device); |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 256 | logerror("libusb_claim_interface failed: %s\n", libusb_error_name(status)); |
| 257 | goto err; |
| 258 | } |
| 259 | |
| 260 | if (verbose) |
| 261 | logerror("microcontroller type: %s\n", fx_name[fx_type]); |
| 262 | |
| 263 | for (i=0; i<ARRAYSIZE(path); i++) { |
| 264 | if (path[i] != NULL) { |
| 265 | ext = path[i] + strlen(path[i]) - 4; |
| 266 | if ((_stricmp(ext, ".hex") == 0) || (strcmp(ext, ".ihx") == 0)) |
| 267 | img_type[i] = IMG_TYPE_HEX; |
| 268 | else if (_stricmp(ext, ".iic") == 0) |
| 269 | img_type[i] = IMG_TYPE_IIC; |
| 270 | else if (_stricmp(ext, ".bix") == 0) |
| 271 | img_type[i] = IMG_TYPE_BIX; |
Federico Manzan | b74b7f7 | 2013-03-10 21:00:00 +0000 | [diff] [blame] | 272 | else if (_stricmp(ext, ".img") == 0) |
| 273 | img_type[i] = IMG_TYPE_IMG; |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 274 | else { |
| 275 | logerror("%s is not a recognized image type\n", path[i]); |
| 276 | goto err; |
| 277 | } |
| 278 | } |
| 279 | if (verbose && path[i] != NULL) |
| 280 | logerror("%s: type %s\n", path[i], img_name[img_type[i]]); |
| 281 | } |
| 282 | |
Chris Dickens | 8c24a1b | 2015-08-04 23:51:12 -0700 | [diff] [blame] | 283 | if (path[LOADER] == NULL) { |
| 284 | /* single stage, put into internal memory */ |
| 285 | if (verbose > 1) |
| 286 | logerror("single stage: load on-chip memory\n"); |
| 287 | status = ezusb_load_ram(device, path[FIRMWARE], fx_type, img_type[FIRMWARE], 0); |
| 288 | } else { |
| 289 | /* two-stage, put loader into internal memory */ |
| 290 | if (verbose > 1) |
| 291 | logerror("1st stage: load 2nd stage loader\n"); |
| 292 | status = ezusb_load_ram(device, path[LOADER], fx_type, img_type[LOADER], 0); |
| 293 | if (status == 0) { |
| 294 | /* two-stage, put firmware into internal memory */ |
| 295 | if (verbose > 1) |
| 296 | logerror("2nd state: load on-chip memory\n"); |
| 297 | status = ezusb_load_ram(device, path[FIRMWARE], fx_type, img_type[FIRMWARE], 1); |
| 298 | } |
| 299 | } |
Pete Batard | 0597533 | 2012-09-11 01:01:07 +0100 | [diff] [blame] | 300 | |
| 301 | libusb_release_interface(device, 0); |
| 302 | libusb_close(device); |
| 303 | libusb_exit(NULL); |
| 304 | return status; |
| 305 | err: |
| 306 | libusb_exit(NULL); |
| 307 | return -1; |
| 308 | } |