diff --git a/doc/openocd.texi b/doc/openocd.texi index f272f2198..fb987e727 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2558,6 +2558,14 @@ If not specified, serial numbers are not considered. and are not restricted to containing only decimal digits.) @end deffn +@deffn {Config Command} {ftdi_location} :[,]... +Specifies the physical USB port of the adapter to use. The path +roots at @var{bus} and walks down the physical ports, with each +@var{port} option specifying a deeper level in the bus topology, the last +@var{port} denoting where the target adapter is actually plugged. +The USB bus topology can be queried with the command @emph{lsusb -t}. +@end deffn + @deffn {Config Command} {ftdi_channel} channel Selects the channel of the FTDI device to use for MPSSE operations. Most adapters use the default, channel 0, but there are exceptions. diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 7f113918a..b32fa6cb2 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -91,6 +91,7 @@ static char *ftdi_device_desc; static char *ftdi_serial; +static char *ftdi_location; static uint8_t ftdi_channel; static uint8_t ftdi_jtag_mode = JTAG_MODE; @@ -631,7 +632,7 @@ static int ftdi_initialize(void) for (int i = 0; ftdi_vid[i] || ftdi_pid[i]; i++) { mpsse_ctx = mpsse_open(&ftdi_vid[i], &ftdi_pid[i], ftdi_device_desc, - ftdi_serial, ftdi_channel); + ftdi_serial, ftdi_location, ftdi_channel); if (mpsse_ctx) break; } @@ -698,6 +699,19 @@ COMMAND_HANDLER(ftdi_handle_serial_command) return ERROR_OK; } +COMMAND_HANDLER(ftdi_handle_location_command) +{ + if (CMD_ARGC == 1) { + if (ftdi_location) + free(ftdi_location); + ftdi_location = strdup(CMD_ARGV[0]); + } else { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; +} + COMMAND_HANDLER(ftdi_handle_channel_command) { if (CMD_ARGC == 1) @@ -875,6 +889,13 @@ static const struct command_registration ftdi_command_handlers[] = { .help = "set the serial number of the FTDI device", .usage = "serial_string", }, + { + .name = "ftdi_location", + .handler = &ftdi_handle_location_command, + .mode = COMMAND_CONFIG, + .help = "set the USB bus location of the FTDI device", + .usage = ":port[,port]...", + }, { .name = "ftdi_channel", .handler = &ftdi_handle_channel_command, diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c index d9f73a2a7..a3820a226 100644 --- a/src/jtag/drivers/mpsse.c +++ b/src/jtag/drivers/mpsse.c @@ -104,12 +104,65 @@ static bool string_descriptor_equal(libusb_device_handle *device, uint8_t str_in return strncmp(string, desc_string, sizeof(desc_string)) == 0; } +static bool device_location_equal(libusb_device *device, const char *location) +{ + char *loc = strdup(location); + uint8_t port_path[7]; + int path_step, path_len; + uint8_t dev_bus = libusb_get_bus_number(device); + char *ptr; + bool result = false; + + path_len = libusb_get_port_numbers(device, port_path, 7); + if (path_len == LIBUSB_ERROR_OVERFLOW) { + LOG_ERROR("cannot determine path to usb device! (more than 7 ports in path)"); + goto done; + } + + LOG_DEBUG("device path has %i steps", path_len); + + ptr = strtok(loc, ":"); + if (ptr == NULL) { + LOG_DEBUG("no ':' in path"); + goto done; + } + if (atoi(ptr) != dev_bus) { + LOG_DEBUG("bus mismatch"); + goto done; + } + + path_step = 0; + while (path_step < 7) { + ptr = strtok(NULL, ","); + if (ptr == NULL) { + LOG_DEBUG("no more tokens in path at step %i", path_step); + break; + } + + if (path_step < path_len + && atoi(ptr) != port_path[path_step]) { + LOG_DEBUG("path mismatch at step %i", path_step); + break; + } + + path_step++; + }; + + /* walked the full path, all elements match */ + if (path_step == path_len) + result = true; + + done: + free(loc); + return result; +} + /* Helper to open a libusb device that matches vid, pid, product string and/or serial string. * Set any field to 0 as a wildcard. If the device is found true is returned, with ctx containing * the already opened handle. ctx->interface must be set to the desired interface (channel) number * prior to calling this function. */ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, const uint16_t *pid, - const char *product, const char *serial) + const char *product, const char *serial, const char *location) { libusb_device **list; struct libusb_device_descriptor desc; @@ -141,6 +194,11 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con continue; } + if (location && !device_location_equal(device, location)) { + libusb_close(ctx->usb_dev); + continue; + } + if (product && !string_descriptor_equal(ctx->usb_dev, desc.iProduct, product)) { libusb_close(ctx->usb_dev); continue; @@ -263,7 +321,7 @@ error: } struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const char *description, - const char *serial, int channel) + const char *serial, const char *location, int channel) { struct mpsse_ctx *ctx = calloc(1, sizeof(*ctx)); int err; @@ -292,16 +350,17 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha goto error; } - if (!open_matching_device(ctx, vid, pid, description, serial)) { + if (!open_matching_device(ctx, vid, pid, description, serial, location)) { /* Four hex digits plus terminating zero each */ char vidstr[5]; char pidstr[5]; - LOG_ERROR("unable to open ftdi device with vid %s, pid %s, description '%s' and " - "serial '%s'", + LOG_ERROR("unable to open ftdi device with vid %s, pid %s, description '%s', " + "serial '%s' at bus location '%s'", vid ? sprintf(vidstr, "%04x", *vid), vidstr : "*", pid ? sprintf(pidstr, "%04x", *pid), pidstr : "*", description ? description : "*", - serial ? serial : "*"); + serial ? serial : "*", + location ? location : "*"); ctx->usb_dev = 0; goto error; } diff --git a/src/jtag/drivers/mpsse.h b/src/jtag/drivers/mpsse.h index 3e287f75a..4c06bbdea 100644 --- a/src/jtag/drivers/mpsse.h +++ b/src/jtag/drivers/mpsse.h @@ -43,7 +43,7 @@ struct mpsse_ctx; /* Device handling */ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const char *description, - const char *serial, int channel); + const char *serial, const char *location, int channel); void mpsse_close(struct mpsse_ctx *ctx); bool mpsse_is_high_speed(struct mpsse_ctx *ctx);