Add wall clock timeout warning to mpsse_flush()
[openocd.git] / src / jtag / drivers / mpsse.c
index d6cbc8404c6e93330f3f38d6ed87317776b0e617..7488d9dd877c65a97a8b88057a3299d25001e3de 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.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 
 #include "mpsse.h"
 #include "helper/log.h"
-#include <libusb-1.0/libusb.h>
+#include "helper/time_support.h"
+#include <libusb.h>
 
 /* Compatibility define for older libusb-1.0 */
 #ifndef LIBUSB_CALL
 #define LIBUSB_CALL
 #endif
 
-#ifdef _DEBUG_JTAG_IO_
-#define DEBUG_IO(expr...) LOG_DEBUG(expr)
 #define DEBUG_PRINT_BUF(buf, len) \
        do { \
-               char buf_string[32 * 3 + 1]; \
-               int buf_string_pos = 0; \
-               for (int i = 0; i < len; i++) { \
-                       buf_string_pos += sprintf(buf_string + buf_string_pos, " %02x", buf[i]); \
-                       if (i % 32 == 32 - 1) { \
-                               LOG_DEBUG("%s", buf_string); \
-                               buf_string_pos = 0; \
+               if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) { \
+                       char buf_string[32 * 3 + 1]; \
+                       int buf_string_pos = 0; \
+                       for (int i = 0; i < len; i++) { \
+                               buf_string_pos += sprintf(buf_string + buf_string_pos, " %02x", buf[i]); \
+                               if (i % 32 == 32 - 1) { \
+                                       LOG_DEBUG_IO("%s", buf_string); \
+                                       buf_string_pos = 0; \
+                               } \
                        } \
+                       if (buf_string_pos > 0) \
+                               LOG_DEBUG_IO("%s", buf_string);\
                } \
-               if (buf_string_pos > 0) \
-                       LOG_DEBUG("%s", buf_string);\
        } while (0)
-#else
-#define DEBUG_IO(expr...) do {} while (0)
-#define DEBUG_PRINT_BUF(buf, len) do {} while (0)
-#endif
 
 #define FTDI_DEVICE_OUT_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE)
 #define FTDI_DEVICE_IN_REQTYPE (0x80 | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE)
@@ -86,6 +81,7 @@ struct mpsse_ctx {
        uint8_t *read_chunk;
        unsigned read_chunk_size;
        struct bit_copy_queue read_queue;
+       int retval;
 };
 
 /* Returns true if the string descriptor indexed by str_index in device matches string */
@@ -97,18 +93,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;
@@ -117,14 +168,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;
                }
 
@@ -135,7 +186,13 @@ static bool open_matching_device(struct mpsse_ctx *ctx, const uint16_t *vid, con
 
                err = libusb_open(device, &ctx->usb_dev);
                if (err != LIBUSB_SUCCESS) {
-                       LOG_ERROR("libusb_open() failed with %d", err);
+                       LOG_ERROR("libusb_open() failed with %s",
+                                 libusb_error_name(err));
+                       continue;
+               }
+
+               if (location && !device_location_equal(device, location)) {
+                       libusb_close(ctx->usb_dev);
                        continue;
                }
 
@@ -162,7 +219,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;
        }
@@ -171,14 +228,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;
                }
        }
@@ -187,13 +244,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);
-               goto error;
+               LOG_WARNING("libusb_detach_kernel_driver() failed with %s, trying to continue anyway",
+                       libusb_error_name(err));
        }
 
        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;
        }
 
@@ -202,7 +259,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;
        }
 
@@ -261,7 +318,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;
@@ -275,7 +332,13 @@ struct mpsse_ctx *mpsse_open(const uint16_t *vid, const uint16_t *pid, const cha
        ctx->write_size = 16384;
        ctx->read_chunk = malloc(ctx->read_chunk_size);
        ctx->read_buffer = malloc(ctx->read_size);
-       ctx->write_buffer = malloc(ctx->write_size);
+
+       /* Use calloc to make valgrind happy: buffer_write() sets payload
+        * on bit basis, so some bits can be left uninitialized in write_buffer.
+        * Although this is perfectly ok with MPSSE, valgrind reports
+        * Syscall param ioctl(USBDEVFS_SUBMITURB).buffer points to uninitialised byte(s) */
+       ctx->write_buffer = calloc(1, ctx->write_size);
+
        if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer)
                goto error;
 
@@ -286,20 +349,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;
        }
@@ -308,7 +372,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;
        }
 
@@ -321,7 +385,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;
        }
 
@@ -361,18 +425,19 @@ void mpsse_purge(struct mpsse_ctx *ctx)
        LOG_DEBUG("-");
        ctx->write_count = 0;
        ctx->read_count = 0;
+       ctx->retval = ERROR_OK;
        bit_copy_discard(&ctx->read_queue);
        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;
        }
 }
@@ -390,7 +455,7 @@ static unsigned buffer_read_space(struct mpsse_ctx *ctx)
 
 static void buffer_write_byte(struct mpsse_ctx *ctx, uint8_t data)
 {
-       DEBUG_IO("%02x", data);
+       LOG_DEBUG_IO("%02x", data);
        assert(ctx->write_count < ctx->write_size);
        ctx->write_buffer[ctx->write_count++] = data;
 }
@@ -398,7 +463,7 @@ static void buffer_write_byte(struct mpsse_ctx *ctx, uint8_t data)
 static unsigned buffer_write(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
        unsigned bit_count)
 {
-       DEBUG_IO("%d bits", bit_count);
+       LOG_DEBUG_IO("%d bits", bit_count);
        assert(ctx->write_count + DIV_ROUND_UP(bit_count, 8) <= ctx->write_size);
        bit_copy(ctx->write_buffer + ctx->write_count, 0, out, out_offset, bit_count);
        ctx->write_count += DIV_ROUND_UP(bit_count, 8);
@@ -408,7 +473,7 @@ static unsigned buffer_write(struct mpsse_ctx *ctx, const uint8_t *out, unsigned
 static unsigned buffer_add_read(struct mpsse_ctx *ctx, uint8_t *in, unsigned in_offset,
        unsigned bit_count, unsigned offset)
 {
-       DEBUG_IO("%d bits, offset %d", bit_count, offset);
+       LOG_DEBUG_IO("%d bits, offset %d", bit_count, offset);
        assert(ctx->read_count + DIV_ROUND_UP(bit_count, 8) <= ctx->read_size);
        bit_copy_queued(&ctx->read_queue, in, in_offset, ctx->read_buffer + ctx->read_count, offset,
                bit_count);
@@ -416,24 +481,28 @@ static unsigned buffer_add_read(struct mpsse_ctx *ctx, uint8_t *in, unsigned in_
        return bit_count;
 }
 
-int mpsse_clock_data_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+void mpsse_clock_data_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
        unsigned length, uint8_t mode)
 {
-       return mpsse_clock_data(ctx, out, out_offset, 0, 0, length, mode);
+       mpsse_clock_data(ctx, out, out_offset, 0, 0, length, mode);
 }
 
-int mpsse_clock_data_in(struct mpsse_ctx *ctx, uint8_t *in, unsigned in_offset, unsigned length,
+void mpsse_clock_data_in(struct mpsse_ctx *ctx, uint8_t *in, unsigned in_offset, unsigned length,
        uint8_t mode)
 {
-       return mpsse_clock_data(ctx, 0, 0, in, in_offset, length, mode);
+       mpsse_clock_data(ctx, 0, 0, in, in_offset, length, mode);
 }
 
-int mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+void mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
        unsigned in_offset, unsigned length, uint8_t mode)
 {
        /* TODO: Fix MSB first modes */
-       DEBUG_IO("%s%s %d bits", in ? "in" : "", out ? "out" : "", length);
-       int retval = ERROR_OK;
+       LOG_DEBUG_IO("%s%s %d bits", in ? "in" : "", out ? "out" : "", length);
+
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
 
        /* TODO: On H chips, use command 0x8E/0x8F if in and out are both 0 */
        if (out || (!out && !in))
@@ -445,7 +514,7 @@ int mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_off
                /* Guarantee buffer space enough for a minimum size transfer */
                if (buffer_write_space(ctx) + (length < 8) < (out || (!out && !in) ? 4 : 3)
                                || (in && buffer_read_space(ctx) < 1))
-                       retval = mpsse_flush(ctx);
+                       ctx->retval = mpsse_flush(ctx);
 
                if (length < 8) {
                        /* Transfer remaining bits in bit mode */
@@ -493,21 +562,24 @@ int mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_off
                        }
                }
        }
-       return retval;
 }
 
-int mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+void mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
        unsigned length, bool tdi, uint8_t mode)
 {
-       return mpsse_clock_tms_cs(ctx, out, out_offset, 0, 0, length, tdi, mode);
+       mpsse_clock_tms_cs(ctx, out, out_offset, 0, 0, length, tdi, mode);
 }
 
-int mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+void mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
        unsigned in_offset, unsigned length, bool tdi, uint8_t mode)
 {
-       DEBUG_IO("%sout %d bits, tdi=%d", in ? "in" : "", length, tdi);
+       LOG_DEBUG_IO("%sout %d bits, tdi=%d", in ? "in" : "", length, tdi);
        assert(out);
-       int retval = ERROR_OK;
+
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
 
        mode |= 0x42;
        if (in)
@@ -516,7 +588,7 @@ int mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_o
        while (length > 0) {
                /* Guarantee buffer space enough for a minimum size transfer */
                if (buffer_write_space(ctx) < 3 || (in && buffer_read_space(ctx) < 1))
-                       retval = mpsse_flush(ctx);
+                       ctx->retval = mpsse_flush(ctx);
 
                /* Byte transfer */
                unsigned this_bits = length;
@@ -547,99 +619,109 @@ int mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_o
                        length -= this_bits;
                }
        }
-       return retval;
 }
 
-int mpsse_set_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir)
+void mpsse_set_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir)
 {
-       DEBUG_IO("-");
-       int retval = ERROR_OK;
+       LOG_DEBUG_IO("-");
+
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
 
        if (buffer_write_space(ctx) < 3)
-               retval = mpsse_flush(ctx);
+               ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, 0x80);
        buffer_write_byte(ctx, data);
        buffer_write_byte(ctx, dir);
-
-       return retval;
 }
 
-int mpsse_set_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir)
+void mpsse_set_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir)
 {
-       DEBUG_IO("-");
-       int retval = ERROR_OK;
+       LOG_DEBUG_IO("-");
+
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
 
        if (buffer_write_space(ctx) < 3)
-               retval = mpsse_flush(ctx);
+               ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, 0x82);
        buffer_write_byte(ctx, data);
        buffer_write_byte(ctx, dir);
-
-       return retval;
 }
 
-int mpsse_read_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t *data)
+void mpsse_read_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t *data)
 {
-       DEBUG_IO("-");
-       int retval = ERROR_OK;
+       LOG_DEBUG_IO("-");
 
-       if (buffer_write_space(ctx) < 1)
-               retval = mpsse_flush(ctx);
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
+
+       if (buffer_write_space(ctx) < 1 || buffer_read_space(ctx) < 1)
+               ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, 0x81);
        buffer_add_read(ctx, data, 0, 8, 0);
-
-       return retval;
 }
 
-int mpsse_read_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t *data)
+void mpsse_read_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t *data)
 {
-       DEBUG_IO("-");
-       int retval = ERROR_OK;
+       LOG_DEBUG_IO("-");
 
-       if (buffer_write_space(ctx) < 1)
-               retval = mpsse_flush(ctx);
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
+
+       if (buffer_write_space(ctx) < 1 || buffer_read_space(ctx) < 1)
+               ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, 0x83);
        buffer_add_read(ctx, data, 0, 8, 0);
-
-       return retval;
 }
 
-static int single_byte_boolean_helper(struct mpsse_ctx *ctx, bool var, uint8_t val_if_true,
+static void single_byte_boolean_helper(struct mpsse_ctx *ctx, bool var, uint8_t val_if_true,
        uint8_t val_if_false)
 {
-       int retval = ERROR_OK;
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
 
        if (buffer_write_space(ctx) < 1)
-               retval = mpsse_flush(ctx);
+               ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, var ? val_if_true : val_if_false);
-
-       return retval;
 }
 
-int mpsse_loopback_config(struct mpsse_ctx *ctx, bool enable)
+void mpsse_loopback_config(struct mpsse_ctx *ctx, bool enable)
 {
        LOG_DEBUG("%s", enable ? "on" : "off");
-       return single_byte_boolean_helper(ctx, enable, 0x84, 0x85);
+       single_byte_boolean_helper(ctx, enable, 0x84, 0x85);
 }
 
-int mpsse_set_divisor(struct mpsse_ctx *ctx, uint16_t divisor)
+void mpsse_set_divisor(struct mpsse_ctx *ctx, uint16_t divisor)
 {
        LOG_DEBUG("%d", divisor);
-       int retval = ERROR_OK;
+
+       if (ctx->retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring command due to previous error");
+               return;
+       }
 
        if (buffer_write_space(ctx) < 3)
-               retval = mpsse_flush(ctx);
+               ctx->retval = mpsse_flush(ctx);
 
        buffer_write_byte(ctx, 0x86);
        buffer_write_byte(ctx, divisor & 0xff);
        buffer_write_byte(ctx, divisor >> 8);
-
-       return retval;
 }
 
 int mpsse_divide_by_5_config(struct mpsse_ctx *ctx, bool enable)
@@ -648,8 +730,9 @@ int mpsse_divide_by_5_config(struct mpsse_ctx *ctx, bool enable)
                return ERROR_FAIL;
 
        LOG_DEBUG("%s", enable ? "on" : "off");
+       single_byte_boolean_helper(ctx, enable, 0x8b, 0x8a);
 
-       return single_byte_boolean_helper(ctx, enable, 0x8b, 0x8a);
+       return ERROR_OK;
 }
 
 int mpsse_rtck_config(struct mpsse_ctx *ctx, bool enable)
@@ -658,8 +741,9 @@ int mpsse_rtck_config(struct mpsse_ctx *ctx, bool enable)
                return ERROR_FAIL;
 
        LOG_DEBUG("%s", enable ? "on" : "off");
+       single_byte_boolean_helper(ctx, enable, 0x96, 0x97);
 
-       return single_byte_boolean_helper(ctx, enable, 0x96, 0x97);
+       return ERROR_OK;
 }
 
 int mpsse_set_frequency(struct mpsse_ctx *ctx, int frequency)
@@ -673,10 +757,7 @@ int mpsse_set_frequency(struct mpsse_ctx *ctx, int frequency)
 
        mpsse_rtck_config(ctx, false); /* just try */
 
-       if (frequency > 60000000 / 2 / 65536 && mpsse_is_high_speed(ctx)) {
-               int retval = mpsse_divide_by_5_config(ctx, false);
-               if (retval != ERROR_OK)
-                       return retval;
+       if (frequency > 60000000 / 2 / 65536 && mpsse_divide_by_5_config(ctx, false) == ERROR_OK) {
                base_clock = 60000000;
        } else {
                mpsse_divide_by_5_config(ctx, true); /* just try */
@@ -688,9 +769,7 @@ int mpsse_set_frequency(struct mpsse_ctx *ctx, int frequency)
                divisor = 65535;
        assert(divisor >= 0);
 
-       int retval = mpsse_set_divisor(ctx, divisor);
-       if (retval != ERROR_OK)
-               return retval;
+       mpsse_set_divisor(ctx, divisor);
 
        frequency = base_clock / 2 / (1 + divisor);
        LOG_DEBUG("actually %d Hz", frequency);
@@ -707,7 +786,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;
@@ -735,7 +814,7 @@ static LIBUSB_CALL void read_cb(struct libusb_transfer *transfer)
                }
        }
 
-       DEBUG_IO("raw chunk %d, transferred %d of %d", transfer->actual_length, res->transferred,
+       LOG_DEBUG_IO("raw chunk %d, transferred %d of %d", transfer->actual_length, res->transferred,
                ctx->read_count);
 
        if (!res->done)
@@ -745,12 +824,12 @@ 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;
 
-       DEBUG_IO("transferred %d of %d", res->transferred, ctx->write_count);
+       LOG_DEBUG_IO("transferred %d of %d", res->transferred, ctx->write_count);
 
        DEBUG_PRINT_BUF(transfer->buffer, transfer->actual_length);
 
@@ -766,10 +845,18 @@ static LIBUSB_CALL void write_cb(struct libusb_transfer *transfer)
 
 int mpsse_flush(struct mpsse_ctx *ctx)
 {
-       DEBUG_IO("write %d%s, read %d", ctx->write_count, ctx->read_count ? "+1" : "",
+       int retval = ctx->retval;
+
+       if (retval != ERROR_OK) {
+               LOG_DEBUG_IO("Ignoring flush due to previous error");
+               assert(ctx->write_count == 0 && ctx->read_count == 0);
+               ctx->retval = ERROR_OK;
+               return retval;
+       }
+
+       LOG_DEBUG_IO("write %d%s, read %d", ctx->write_count, ctx->read_count ? "+1" : "",
                        ctx->read_count);
        assert(ctx->write_count > 0 || ctx->read_count == 0); /* No read data without write data */
-       int retval = ERROR_OK;
 
        if (ctx->write_count == 0)
                return retval;
@@ -779,11 +866,8 @@ int mpsse_flush(struct mpsse_ctx *ctx)
        if (ctx->read_count) {
                buffer_write_byte(ctx, 0x87); /* SEND_IMMEDIATE */
                read_result.done = false;
-               read_transfer = libusb_alloc_transfer(0);
-               libusb_fill_bulk_transfer(read_transfer, ctx->usb_dev, ctx->in_ep, ctx->read_chunk,
-                       ctx->read_chunk_size, read_cb, &read_result,
-                       ctx->usb_read_timeout);
-               retval = libusb_submit_transfer(read_transfer);
+               /* delay read transaction to ensure the FTDI chip can support us with data
+                  immediately after processing the MPSSE commands in the write transaction */
        }
 
        struct transfer_result write_result = { .ctx = ctx, .done = false };
@@ -791,23 +875,56 @@ 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);
+               libusb_fill_bulk_transfer(read_transfer, ctx->usb_dev, ctx->in_ep, ctx->read_chunk,
+                       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 */
+       int64_t start = timeval_ms();
+       int64_t warn_after = 2000;
        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;
+                       }
+               }
+
+               int64_t now = timeval_ms();
+               if (now - start > warn_after) {
+                       LOG_WARNING("Haven't made progress in mpsse_flush() for %" PRId64
+                                       "ms.", now - start);
+                       warn_after *= 2;
                }
        }
 
+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)