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:
parent
73b676c2fd
commit
0a97b232b1
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue