ftdi: don't wait forever if we fail
[openocd.git] / src / jtag / drivers / mpsse.c
index 59927a0fe76f9085e41057fb0db8d16b2425b8cf..3c1c97cdc23f1a3f14f1125042ccf1dc3b5e37ae 100644 (file)
@@ -13,9 +13,7 @@
  *   GNU General Public License for more details.                          *
  *                                                                         *
  *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
@@ -24,7 +22,7 @@
 
 #include "mpsse.h"
 #include "helper/log.h"
-#include <libusb-1.0/libusb.h>
+#include <libusb.h>
 
 /* Compatibility define for older libusb-1.0 */
 #ifndef LIBUSB_CALL
@@ -98,18 +96,73 @@ static bool string_descriptor_equal(libusb_device_handle *device, uint8_t str_in
        retval = libusb_get_string_descriptor_ascii(device, str_index, (unsigned char *)desc_string,
                        sizeof(desc_string));
        if (retval < 0) {
-               LOG_ERROR("libusb_get_string_descriptor_ascii() failed with %d", retval);
+               LOG_ERROR("libusb_get_string_descriptor_ascii() failed with %s", libusb_error_name(retval));
                return false;
        }
        return strncmp(string, desc_string, sizeof(desc_string)) == 0;
 }
 
+static bool device_location_equal(libusb_device *device, const char *location)
+{
+       bool result = false;
+#ifdef HAVE_LIBUSB_GET_PORT_NUMBERS
+       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;
+
+       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);
+#endif
+       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;
@@ -118,14 +171,14 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con
        bool found = false;
        ssize_t cnt = libusb_get_device_list(ctx->usb_ctx, &list);
        if (cnt < 0)
-               LOG_ERROR("libusb_get_device_list() failed with %zi", cnt);
+               LOG_ERROR("libusb_get_device_list() failed with %s", libusb_error_name(cnt));
 
        for (ssize_t i = 0; i < cnt; i++) {
                libusb_device *device = list[i];
 
                err = libusb_get_device_descriptor(device, &desc);
                if (err != LIBUSB_SUCCESS) {
-                       LOG_ERROR("libusb_get_device_descriptor() failed with %d", err);
+                       LOG_ERROR("libusb_get_device_descriptor() failed with %s", libusb_error_name(err));
                        continue;
                }
 
@@ -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;
@@ -164,7 +222,7 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con
 
        err = libusb_get_config_descriptor(libusb_get_device(ctx->usb_dev), 0, &config0);
        if (err != LIBUSB_SUCCESS) {
-               LOG_ERROR("libusb_get_config_descriptor() failed with %d", err);
+               LOG_ERROR("libusb_get_config_descriptor() failed with %s", libusb_error_name(err));
                libusb_close(ctx->usb_dev);
                return false;
        }
@@ -173,14 +231,14 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con
        int cfg;
        err = libusb_get_configuration(ctx->usb_dev, &cfg);
        if (err != LIBUSB_SUCCESS) {
-               LOG_ERROR("libusb_get_configuration() failed with %d", err);
+               LOG_ERROR("libusb_get_configuration() failed with %s", libusb_error_name(err));
                goto error;
        }
 
        if (desc.bNumConfigurations > 0 && cfg != config0->bConfigurationValue) {
                err = libusb_set_configuration(ctx->usb_dev, config0->bConfigurationValue);
                if (err != LIBUSB_SUCCESS) {
-                       LOG_ERROR("libusb_set_configuration() failed with %d", err);
+                       LOG_ERROR("libusb_set_configuration() failed with %s", libusb_error_name(err));
                        goto error;
                }
        }
@@ -189,13 +247,13 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con
        err = libusb_detach_kernel_driver(ctx->usb_dev, ctx->interface);
        if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_NOT_FOUND
                        && err != LIBUSB_ERROR_NOT_SUPPORTED) {
-               LOG_ERROR("libusb_detach_kernel_driver() failed with %d", err);
+               LOG_ERROR("libusb_detach_kernel_driver() failed with %s", libusb_error_name(err));
                goto error;
        }
 
        err = libusb_claim_interface(ctx->usb_dev, ctx->interface);
        if (err != LIBUSB_SUCCESS) {
-               LOG_ERROR("libusb_claim_interface() failed with %d", err);
+               LOG_ERROR("libusb_claim_interface() failed with %s", libusb_error_name(err));
                goto error;
        }
 
@@ -204,7 +262,7 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con
                        SIO_RESET_REQUEST, SIO_RESET_SIO,
                        ctx->index, NULL, 0, ctx->usb_write_timeout);
        if (err < 0) {
-               LOG_ERROR("failed to reset FTDI device: %d", err);
+               LOG_ERROR("failed to reset FTDI device: %s", libusb_error_name(err));
                goto error;
        }
 
@@ -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;
@@ -288,20 +346,21 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
 
        err = libusb_init(&ctx->usb_ctx);
        if (err != LIBUSB_SUCCESS) {
-               LOG_ERROR("libusb_init() failed with %d", err);
+               LOG_ERROR("libusb_init() failed with %s", libusb_error_name(err));
                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;
        }
@@ -310,7 +369,7 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
                        SIO_SET_LATENCY_TIMER_REQUEST, 255, ctx->index, NULL, 0,
                        ctx->usb_write_timeout);
        if (err < 0) {
-               LOG_ERROR("unable to set latency timer: %d", err);
+               LOG_ERROR("unable to set latency timer: %s", libusb_error_name(err));
                goto error;
        }
 
@@ -323,7 +382,7 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
                        0,
                        ctx->usb_write_timeout);
        if (err < 0) {
-               LOG_ERROR("unable to set MPSSE bitmode: %d", err);
+               LOG_ERROR("unable to set MPSSE bitmode: %s", libusb_error_name(err));
                goto error;
        }
 
@@ -368,14 +427,14 @@ void mpsse_purge(struct mpsse_ctx *ctx)
        err = libusb_control_transfer(ctx->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
                        SIO_RESET_PURGE_RX, ctx->index, NULL, 0, ctx->usb_write_timeout);
        if (err < 0) {
-               LOG_ERROR("unable to purge ftdi rx buffers: %d", err);
+               LOG_ERROR("unable to purge ftdi rx buffers: %s", libusb_error_name(err));
                return;
        }
 
        err = libusb_control_transfer(ctx->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
                        SIO_RESET_PURGE_TX, ctx->index, NULL, 0, ctx->usb_write_timeout);
        if (err < 0) {
-               LOG_ERROR("unable to purge ftdi tx buffers: %d", err);
+               LOG_ERROR("unable to purge ftdi tx buffers: %s", libusb_error_name(err));
                return;
        }
 }
@@ -602,7 +661,7 @@ void mpsse_read_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t *data)
                return;
        }
 
-       if (buffer_write_space(ctx) < 1)
+       if (buffer_write_space(ctx) < 1 || buffer_read_space(ctx) < 1)
                ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, 0x81);
@@ -618,7 +677,7 @@ void mpsse_read_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t *data)
                return;
        }
 
-       if (buffer_write_space(ctx) < 1)
+       if (buffer_write_space(ctx) < 1 || buffer_read_space(ctx) < 1)
                ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, 0x83);
@@ -724,7 +783,7 @@ struct transfer_result {
 
 static LIBUSB_CALL void read_cb(struct libusb_transfer *transfer)
 {
-       struct transfer_result *res = (struct transfer_result *)transfer->user_data;
+       struct transfer_result *res = transfer->user_data;
        struct mpsse_ctx *ctx = res->ctx;
 
        unsigned packet_size = ctx->max_packet_size;
@@ -762,7 +821,7 @@ static LIBUSB_CALL void read_cb(struct libusb_transfer *transfer)
 
 static LIBUSB_CALL void write_cb(struct libusb_transfer *transfer)
 {
-       struct transfer_result *res = (struct transfer_result *)transfer->user_data;
+       struct transfer_result *res = transfer->user_data;
        struct mpsse_ctx *ctx = res->ctx;
 
        res->transferred += transfer->actual_length;
@@ -813,6 +872,8 @@ int mpsse_flush(struct mpsse_ctx *ctx)
        libusb_fill_bulk_transfer(write_transfer, ctx->usb_dev, ctx->out_ep, ctx->write_buffer,
                ctx->write_count, write_cb, &write_result, ctx->usb_write_timeout);
        retval = libusb_submit_transfer(write_transfer);
+       if (retval != LIBUSB_SUCCESS)
+               goto error_check;
 
        if (ctx->read_count) {
                read_transfer = libusb_alloc_transfer(0);
@@ -820,24 +881,38 @@ int mpsse_flush(struct mpsse_ctx *ctx)
                        ctx->read_chunk_size, read_cb, &read_result,
                        ctx->usb_read_timeout);
                retval = libusb_submit_transfer(read_transfer);
+               if (retval != LIBUSB_SUCCESS)
+                       goto error_check;
        }
 
        /* Polling loop, more or less taken from libftdi */
        while (!write_result.done || !read_result.done) {
-               retval = libusb_handle_events(ctx->usb_ctx);
+               struct timeval timeout_usb;
+
+               timeout_usb.tv_sec = 1;
+               timeout_usb.tv_usec = 0;
+
+               retval = libusb_handle_events_timeout_completed(ctx->usb_ctx, &timeout_usb, NULL);
                keep_alive();
-               if (retval != LIBUSB_SUCCESS && retval != LIBUSB_ERROR_INTERRUPTED) {
+               if (retval == LIBUSB_ERROR_NO_DEVICE || retval == LIBUSB_ERROR_INTERRUPTED)
+                       break;
+
+               if (retval != LIBUSB_SUCCESS) {
                        libusb_cancel_transfer(write_transfer);
                        if (read_transfer)
                                libusb_cancel_transfer(read_transfer);
-                       while (!write_result.done || !read_result.done)
-                               if (libusb_handle_events(ctx->usb_ctx) != LIBUSB_SUCCESS)
+                       while (!write_result.done || !read_result.done) {
+                               retval = libusb_handle_events_timeout_completed(ctx->usb_ctx,
+                                                               &timeout_usb, NULL);
+                               if (retval != LIBUSB_SUCCESS)
                                        break;
+                       }
                }
        }
 
+error_check:
        if (retval != LIBUSB_SUCCESS) {
-               LOG_ERROR("libusb_handle_events() failed with %d", retval);
+               LOG_ERROR("libusb_handle_events() failed with %s", libusb_error_name(retval));
                retval = ERROR_FAIL;
        } else if (write_result.transferred < ctx->write_count) {
                LOG_ERROR("ftdi device did not accept all data: %d, tried %d",

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)