X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fmpsse.c;h=0e3d2be0e0d778b1d06af6320a9fe6039bda5113;hb=a555434c50544410ab6f8915f1e2a829cc6e6de2;hp=c5fd6d7fac1ab295e0686bfbbb83497c3a158e12;hpb=209e0beed384a5401158cd0c06add3590e0109dc;p=openocd.git diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c index c5fd6d7fac..0e3d2be0e0 100644 --- a/src/jtag/drivers/mpsse.c +++ b/src/jtag/drivers/mpsse.c @@ -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 . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -24,33 +22,31 @@ #include "mpsse.h" #include "helper/log.h" -#include +#include "helper/replacements.h" +#include "helper/time_support.h" +#include /* 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) @@ -67,8 +63,8 @@ #define SIO_RESET_PURGE_TX 2 struct mpsse_ctx { - libusb_context *usb_ctx; - libusb_device_handle *usb_dev; + struct libusb_context *usb_ctx; + struct libusb_device_handle *usb_dev; unsigned int usb_write_timeout; unsigned int usb_read_timeout; uint8_t in_ep; @@ -90,7 +86,7 @@ struct mpsse_ctx { }; /* Returns true if the string descriptor indexed by str_index in device matches string */ -static bool string_descriptor_equal(libusb_device_handle *device, uint8_t str_index, +static bool string_descriptor_equal(struct libusb_device_handle *device, uint8_t str_index, const char *string) { int retval; @@ -98,34 +94,89 @@ 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(struct 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) { + 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) { + 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 **list; struct libusb_device_descriptor desc; struct libusb_config_descriptor *config0; int err; 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]; + struct 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 +192,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 +220,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 +229,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 +245,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; } @@ -204,7 +260,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 +319,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; @@ -277,7 +333,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; @@ -288,20 +350,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 +373,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 +386,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; } @@ -342,13 +405,10 @@ void mpsse_close(struct mpsse_ctx *ctx) if (ctx->usb_ctx) libusb_exit(ctx->usb_ctx); bit_copy_discard(&ctx->read_queue); - if (ctx->write_buffer) - free(ctx->write_buffer); - if (ctx->read_buffer) - free(ctx->read_buffer); - if (ctx->read_chunk) - free(ctx->read_chunk); + free(ctx->write_buffer); + free(ctx->read_buffer); + free(ctx->read_chunk); free(ctx); } @@ -368,14 +428,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; } } @@ -393,7 +453,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; } @@ -401,7 +461,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); @@ -411,7 +471,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); @@ -435,10 +495,10 @@ void mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_of unsigned in_offset, unsigned length, uint8_t mode) { /* TODO: Fix MSB first modes */ - DEBUG_IO("%s%s %d bits", in ? "in" : "", out ? "out" : "", length); + LOG_DEBUG_IO("%s%s %d bits", in ? "in" : "", out ? "out" : "", length); if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -511,11 +571,11 @@ void mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned 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); if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -561,10 +621,10 @@ void mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_ void mpsse_set_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir) { - DEBUG_IO("-"); + LOG_DEBUG_IO("-"); if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -578,10 +638,10 @@ void mpsse_set_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t d void mpsse_set_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir) { - DEBUG_IO("-"); + LOG_DEBUG_IO("-"); if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -595,10 +655,10 @@ void mpsse_set_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t void mpsse_read_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t *data) { - DEBUG_IO("-"); + LOG_DEBUG_IO("-"); if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -611,10 +671,10 @@ void mpsse_read_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t *data) void mpsse_read_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t *data) { - DEBUG_IO("-"); + LOG_DEBUG_IO("-"); if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -629,7 +689,7 @@ static void single_byte_boolean_helper(struct mpsse_ctx *ctx, bool var, uint8_t uint8_t val_if_false) { if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -650,7 +710,7 @@ void mpsse_set_divisor(struct mpsse_ctx *ctx, uint16_t divisor) LOG_DEBUG("%d", divisor); if (ctx->retval != ERROR_OK) { - DEBUG_IO("Ignoring command due to previous error"); + LOG_DEBUG_IO("Ignoring command due to previous error"); return; } @@ -724,7 +784,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; @@ -752,7 +812,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) @@ -762,12 +822,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); @@ -786,13 +846,13 @@ int mpsse_flush(struct mpsse_ctx *ctx) int retval = ctx->retval; if (retval != ERROR_OK) { - DEBUG_IO("Ignoring flush due to previous error"); + LOG_DEBUG_IO("Ignoring flush due to previous error"); assert(ctx->write_count == 0 && ctx->read_count == 0); ctx->retval = ERROR_OK; return retval; } - DEBUG_IO("write %d%s, read %d", ctx->write_count, ctx->read_count ? "+1" : "", + 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 */ @@ -813,6 +873,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 +882,47 @@ 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 */ + 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", @@ -860,12 +945,12 @@ int mpsse_flush(struct mpsse_ctx *ctx) retval = ERROR_OK; } + if (retval != ERROR_OK) + mpsse_purge(ctx); + libusb_free_transfer(write_transfer); if (read_transfer) libusb_free_transfer(read_transfer); - if (retval != ERROR_OK) - mpsse_purge(ctx); - return retval; }