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>
This commit is contained in:
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.)
@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
Selects the channel of the FTDI device to use for MPSSE operations. Most
adapters use the default, channel 0, but there are exceptions.

View File

@ -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 = "<bus>:port[,port]...",
},
{
.name = "ftdi_channel",
.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;
}
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;
}

View File

@ -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);