ftdi: allow selecting device by usb bus location

This patch adds a 'ftdi_location' command to select an adapter by usb
bus number and port path.

This is helpful if you have a rack full of adapters in a testing or
manufacturing setup where the only constant is the physical usb bus
location of the adapter you want to address. Vid:Pid are not unique,
serial number _may_ be unique (and maybe not with embedded adapters) but
will change when a new target is plugged.

Specifying a location allows to understand instantly which board failed
bringup or testing.

Change-Id: I403c7c6c8e34fe42041b3f967db80f3160a4f1a3
Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
Reviewed-on: http://openocd.zylin.com/3351
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
__archive__
Matthias Welwarsky 2016-02-02 17:03:08 +01:00 committed by Paul Fertser
parent 73b676c2fd
commit 0a97b232b1
4 changed files with 96 additions and 8 deletions

View File

@ -2558,6 +2558,14 @@ If not specified, serial numbers are not considered.
and are not restricted to containing only decimal digits.) and are not restricted to containing only decimal digits.)
@end deffn @end deffn
@deffn {Config Command} {ftdi_location} <bus>:<port>[,<port>]...
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 @deffn {Config Command} {ftdi_channel} channel
Selects the channel of the FTDI device to use for MPSSE operations. Most Selects the channel of the FTDI device to use for MPSSE operations. Most
adapters use the default, channel 0, but there are exceptions. adapters use the default, channel 0, but there are exceptions.

View File

@ -91,6 +91,7 @@
static char *ftdi_device_desc; static char *ftdi_device_desc;
static char *ftdi_serial; static char *ftdi_serial;
static char *ftdi_location;
static uint8_t ftdi_channel; static uint8_t ftdi_channel;
static uint8_t ftdi_jtag_mode = JTAG_MODE; 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++) { for (int i = 0; ftdi_vid[i] || ftdi_pid[i]; i++) {
mpsse_ctx = mpsse_open(&ftdi_vid[i], &ftdi_pid[i], ftdi_device_desc, 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) if (mpsse_ctx)
break; break;
} }
@ -698,6 +699,19 @@ COMMAND_HANDLER(ftdi_handle_serial_command)
return ERROR_OK; 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) COMMAND_HANDLER(ftdi_handle_channel_command)
{ {
if (CMD_ARGC == 1) 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", .help = "set the serial number of the FTDI device",
.usage = "serial_string", .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 = "<bus>:port[,port]...",
},
{ {
.name = "ftdi_channel", .name = "ftdi_channel",
.handler = &ftdi_handle_channel_command, .handler = &ftdi_handle_channel_command,

View File

@ -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; 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. /* 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 * 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 * the already opened handle. ctx->interface must be set to the desired interface (channel) number
* prior to calling this function. */ * prior to calling this function. */
static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, const uint16_t *pid, 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; libusb_device **list;
struct libusb_device_descriptor desc; struct libusb_device_descriptor desc;
@ -141,6 +194,11 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con
continue; 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)) { if (product && !string_descriptor_equal(ctx->usb_dev, desc.iProduct, product)) {
libusb_close(ctx->usb_dev); libusb_close(ctx->usb_dev);
continue; continue;
@ -263,7 +321,7 @@ error:
} }
struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const char *description, 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)); struct mpsse_ctx *ctx = calloc(1, sizeof(*ctx));
int err; int err;
@ -292,16 +350,17 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
goto error; 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 */ /* Four hex digits plus terminating zero each */
char vidstr[5]; char vidstr[5];
char pidstr[5]; char pidstr[5];
LOG_ERROR("unable to open ftdi device with vid %s, pid %s, description '%s' and " LOG_ERROR("unable to open ftdi device with vid %s, pid %s, description '%s', "
"serial '%s'", "serial '%s' at bus location '%s'",
vid ? sprintf(vidstr, "%04x", *vid), vidstr : "*", vid ? sprintf(vidstr, "%04x", *vid), vidstr : "*",
pid ? sprintf(pidstr, "%04x", *pid), pidstr : "*", pid ? sprintf(pidstr, "%04x", *pid), pidstr : "*",
description ? description : "*", description ? description : "*",
serial ? serial : "*"); serial ? serial : "*",
location ? location : "*");
ctx->usb_dev = 0; ctx->usb_dev = 0;
goto error; goto error;
} }

View File

@ -43,7 +43,7 @@ struct mpsse_ctx;
/* Device handling */ /* Device handling */
struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const char *description, 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); void mpsse_close(struct mpsse_ctx *ctx);
bool mpsse_is_high_speed(struct mpsse_ctx *ctx); bool mpsse_is_high_speed(struct mpsse_ctx *ctx);