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__
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.)
|
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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue