This is an automated email from Gerrit. Andreas Fritiofson ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/451
-- gerrit commit ee3548f65745f9b01af0a5dba818f131c625fffa Author: Andreas Fritiofson <[email protected]> Date: Mon Jan 30 00:16:41 2012 +0100 add MPSSE communications layer for FTDI chips This is a higher-level libftdi replacement for use when implementing protocol drivers for FT2232, FT2232H or FT4232H. It takes care of device open/close and, unlike libftdi, also MPSSE command abstraction, command queueing, buffer handling and return data parsing. The FTDI device is accessed through libusb-1.0 in asynchronous mode. Change-Id: I051adb574dcc39f8ca9cd7f6dbe6ae4aeea5f4c8 Signed-off-by: Andreas Fritiofson <[email protected]> diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c new file mode 100644 index 0000000..fdbf9b2 --- /dev/null +++ b/src/jtag/drivers/mpsse.c @@ -0,0 +1,767 @@ +/************************************************************************** + * Copyright (C) 2012 by Andreas Fritiofson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * 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. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mpsse.h" +#include "helper/log.h" +#include <libusb-1.0/libusb.h> + +#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 (buf_string_pos > 0) \ + LOG_DEBUG("%s", buf_string);\ + } while (0) +#else +#define DEBUG_IO(expr...) (void)0 +#define DEBUG_PRINT_BUF(buf, len) (void)0 +#endif + +/* Constants taken from libftdi */ +#define FTDI_DEVICE_OUT_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE \ + | LIBUSB_ENDPOINT_OUT) +#define FTDI_DEVICE_IN_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE \ + | LIBUSB_ENDPOINT_IN) + +#define BITMODE_MPSSE 0x02 + +#define SIO_RESET_REQUEST 0x00 +#define SIO_SET_LATENCY_TIMER_REQUEST 0x09 +#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A +#define SIO_SET_BITMODE_REQUEST 0x0B + +#define SIO_RESET_SIO 0 +#define SIO_RESET_PURGE_RX 1 +#define SIO_RESET_PURGE_TX 2 + +struct mpsse_ctx { + libusb_context *usb_ctx; + libusb_device_handle *usb_dev; + unsigned int usb_write_timeout; + unsigned int usb_read_timeout; + uint8_t in_ep; + uint8_t out_ep; + uint16_t max_packet_size; + uint16_t index; + int interface; + enum ftdi_chip_type type; + uint8_t *write_buffer; + unsigned write_size; + unsigned write_count; + uint8_t *read_buffer; + unsigned read_size; + unsigned read_count; + uint8_t *read_chunk; + unsigned read_chunk_size; + struct bit_copy_queue read_queue; +}; + +/* 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, + const char *string) +{ + int retval; + int length = strlen(string) + 1; + char *desc_string = malloc(length); + if (!desc_string) + return false; + retval = libusb_get_string_descriptor_ascii(device, str_index, (unsigned char *)desc_string, + length); + if (retval >= 0) + retval = strcmp(string, desc_string); + free(desc_string); + return retval == 0; +} + +/* 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, uint16_t vid, uint16_t pid, + const char *product, const char *serial) +{ + 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); + + for (ssize_t i = 0; i < cnt; i++) { + libusb_device *device = list[i]; + + err = libusb_get_device_descriptor(device, &desc); + if (err) { + LOG_ERROR("libusb_get_device_descriptor() failed with %d", err); + continue; + } + + if (vid && vid != desc.idVendor) + continue; + if (pid && pid != desc.idProduct) + continue; + + err = libusb_open(device, &ctx->usb_dev); + if (err) { + LOG_ERROR("libusb_open() failed with %d", err); + continue; + } + + if (product && !string_descriptor_equal(ctx->usb_dev, desc.iProduct, product)) { + libusb_close(ctx->usb_dev); + continue; + } + + if (serial && !string_descriptor_equal(ctx->usb_dev, desc.iSerialNumber, serial)) { + libusb_close(ctx->usb_dev); + continue; + } + + found = true; + break; + } + + if (!found) { + LOG_ERROR("no device found"); + return false; + } + + err = libusb_get_config_descriptor(libusb_get_device(ctx->usb_dev), 0, &config0); + if (err < 0) { + LOG_ERROR("libusb_get_config_descriptor() failed with %d", err); + libusb_close(ctx->usb_dev); + return false; + } + + /* Try to detach ftdi_sio kernel module */ + libusb_detach_kernel_driver(ctx->usb_dev, ctx->interface); + + /* Make sure the first configuration is selected */ + int cfg; + err = libusb_get_configuration(ctx->usb_dev, &cfg); + if (err < 0) { + LOG_ERROR("libusb_get_configuration() failed with %d", err); + libusb_free_config_descriptor(config0); + libusb_close(ctx->usb_dev); + return false; + } + + if (desc.bNumConfigurations > 0 && cfg != config0->bConfigurationValue) { + err = libusb_set_configuration(ctx->usb_dev, config0->bConfigurationValue); + if (err < 0) { + LOG_ERROR("libusb_set_configuration() failed with %d", err); + libusb_free_config_descriptor(config0); + libusb_close(ctx->usb_dev); + return false; + } + } + + err = libusb_claim_interface(ctx->usb_dev, ctx->interface); + if (err < 0) { + LOG_ERROR("libusb_claim_interface() failed with %d", err); + libusb_free_config_descriptor(config0); + libusb_close(ctx->usb_dev); + return false; + } + + /* Reset FTDI device */ + err = libusb_control_transfer(ctx->usb_dev, FTDI_DEVICE_OUT_REQTYPE, + 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); + libusb_free_config_descriptor(config0); + libusb_close(ctx->usb_dev); + return false; + } + + if (desc.bcdDevice == 0x500) + ctx->type = TYPE_FT2232C; + else if (desc.bcdDevice == 0x700) + ctx->type = TYPE_FT2232H; + else if (desc.bcdDevice == 0x800) + ctx->type = TYPE_FT4232H; + else { + LOG_ERROR("unsupported FTDI chip type: %04x", desc.bcdDevice); + libusb_free_config_descriptor(config0); + libusb_close(ctx->usb_dev); + return false; + } + + /* Determine maximum packet size and endpoint addresses */ + if (desc.bNumConfigurations > 0 && ctx->interface < config0->bNumInterfaces + && config0->interface[ctx->interface].num_altsetting > 0) { + struct libusb_interface_descriptor descriptor; + descriptor = config0->interface[ctx->interface].altsetting[0]; + if (descriptor.bNumEndpoints == 2) { + for (int i = 0; i < descriptor.bNumEndpoints; i++) { + if (descriptor.endpoint[i].bEndpointAddress & 0x80) { + ctx->in_ep = descriptor.endpoint[i].bEndpointAddress; + ctx->max_packet_size = + descriptor.endpoint[i].wMaxPacketSize; + } else { + ctx->out_ep = descriptor.endpoint[i].bEndpointAddress; + } + } + } + } + + libusb_free_config_descriptor(config0); + + if (ctx->max_packet_size == 0) { + LOG_ERROR("could not determine maximum packet size"); + libusb_close(ctx->usb_dev); + return false; + } + + return true; +} + +struct mpsse_ctx *mpsse_open(uint16_t vid, uint16_t pid, const char *description, + const char *serial, int channel, unsigned char latency) +{ + struct mpsse_ctx *ctx = calloc(1, sizeof(*ctx)); + int err; + + if (!ctx) + return 0; + + bit_copy_queue_init(&ctx->read_queue); + ctx->read_chunk_size = 16384; + ctx->read_size = 16384; + 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); + if (!ctx->read_chunk || !ctx->read_buffer || !ctx->write_buffer) + goto error; + + ctx->interface = channel; + ctx->index = channel + 1; + ctx->usb_read_timeout = 5000; + ctx->usb_write_timeout = 5000; + + err = libusb_init(&ctx->usb_ctx); + if (err) { + LOG_ERROR("libusb_init() failed with %d", err); + goto error; + } + + if (!open_matching_device(ctx, vid, pid, description, serial)) { + LOG_ERROR("unable to open ftdi device with vid %04x pid %04x", vid, pid); + ctx->usb_dev = 0; + goto error; + } + + assert(latency > 0); + err = libusb_control_transfer(ctx->usb_dev, FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_LATENCY_TIMER_REQUEST, latency, ctx->index, NULL, 0, + ctx->usb_write_timeout); + if (err < 0) { + LOG_ERROR("unable to set latency timer: %d", err); + goto error; + } + + err = libusb_control_transfer(ctx->usb_dev, FTDI_DEVICE_IN_REQTYPE, + SIO_GET_LATENCY_TIMER_REQUEST, 0, ctx->index, + (unsigned char *)&latency, 1, ctx->usb_read_timeout); + if (err != 1) { + LOG_ERROR("unable to get latency timer: %d", err); + goto error; + } else + LOG_DEBUG("current latency timer: %i", latency); + + err = libusb_control_transfer(ctx->usb_dev, + FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_BITMODE_REQUEST, + 0x0b | (BITMODE_MPSSE << 8), + ctx->index, + NULL, + 0, + ctx->usb_write_timeout); + if (err < 0) { + LOG_ERROR("unable to set MPSSE bitmode: %d", err); + goto error; + } + + mpsse_purge(ctx); + + return ctx; +error: + mpsse_close(ctx); + return 0; +} + +void mpsse_close(struct mpsse_ctx *ctx) +{ + if (ctx->usb_dev) + libusb_close(ctx->usb_dev); + 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); +} + +void mpsse_purge(struct mpsse_ctx *ctx) +{ + int err; + LOG_DEBUG("-"); + ctx->write_count = 0; + ctx->read_count = 0; + 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); + 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); + return; + } +} + +static unsigned buffer_write_space(struct mpsse_ctx *ctx) +{ + /* Reserve one byte for SEND_IMMEDIATE */ + return ctx->write_size - ctx->write_count - 1; +} + +static unsigned buffer_read_space(struct mpsse_ctx *ctx) +{ + return ctx->read_size - ctx->read_count; +} + +static void buffer_write_byte(struct mpsse_ctx *ctx, uint8_t data) +{ + DEBUG_IO("%02x", data); + assert(ctx->write_count < ctx->write_size); + ctx->write_buffer[ctx->write_count++] = 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); + 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); + return bit_count; +} + +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); + 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); + ctx->read_count += DIV_ROUND_UP(bit_count, 8); + return bit_count; +} + +int 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); +} + +int 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); +} + +int 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; + + /* TODO: On H chips, use command 0x8E/0x8F if in and out are both 0 */ + if (out || (!out && !in)) + mode |= 0x10; + if (in) + mode |= 0x20; + + while (length > 0) { + /* Guarantee buffer space enough for a minimum size transfer */ + if (buffer_write_space(ctx) + (length < 8) < (out ? 4 : 3) + || (in && buffer_read_space(ctx) < 1)) + retval = mpsse_flush(ctx); + + if (length < 8) { + /* Transfer remaining bits in bit mode */ + buffer_write_byte(ctx, 0x02 | mode); + buffer_write_byte(ctx, length - 1); + if (out) + out_offset += buffer_write(ctx, out, out_offset, length); + if (in) + in_offset += buffer_add_read(ctx, in, in_offset, length, 8 - length); + if (!out && !in) + buffer_write_byte(ctx, 0x00); + length = 0; + } else { + /* Byte transfer */ + unsigned this_bytes = length / 8; + /* MPSSE command limit */ + if (this_bytes > 65536) + this_bytes = 65536; + /* Buffer space limit. We already made sure there's space for the minimum + *transfer. */ + if (out && this_bytes + 3 > buffer_write_space(ctx)) + this_bytes = buffer_write_space(ctx) - 3; + if (in && this_bytes > buffer_read_space(ctx)) + this_bytes = buffer_read_space(ctx); + + if (this_bytes > 0) { + buffer_write_byte(ctx, mode); + buffer_write_byte(ctx, (this_bytes - 1) & 0xff); + buffer_write_byte(ctx, (this_bytes - 1) >> 8); + if (out) + out_offset += buffer_write(ctx, + out, + out_offset, + this_bytes * 8); + if (in) + in_offset += buffer_add_read(ctx, + in, + in_offset, + this_bytes * 8, + 0); + if (!out && !in) + for (unsigned n = 0; n < this_bytes; n++) + buffer_write_byte(ctx, 0x00); + length -= this_bytes * 8; + } + } + } + return retval; +} + +int 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); +} + +int 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); + assert(out); + int retval = ERROR_OK; + + mode |= 0x42; + if (in) + mode |= 0x20; + + 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); + + /* Byte transfer */ + unsigned this_bits = length; + /* MPSSE command limit */ + /* NOTE: there's a report of an FT2232 bug in this area, where shifting + * exactly 7 bits can make problems with TMS signaling for the last + * clock cycle: + * + * http://developer.intra2net.com/mailarchive/html/libftdi/2009/msg00292.html + */ + if (this_bits > 7) + this_bits = 7; + + if (this_bits > 0) { + buffer_write_byte(ctx, mode); + buffer_write_byte(ctx, this_bits - 1); + uint8_t data = 0; + /* TODO: Fix MSB first, if allowed in MPSSE */ + bit_copy(&data, 0, out, out_offset, this_bits); + out_offset += this_bits; + buffer_write_byte(ctx, data | (tdi ? 0x80 : 0x00)); + if (in) + in_offset += buffer_add_read(ctx, + in, + in_offset, + this_bits, + 8 - this_bits); + length -= this_bits; + } + } + return retval; +} + +int mpsse_set_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir) +{ + DEBUG_IO("-"); + int retval = ERROR_OK; + + if (buffer_write_space(ctx) < 3) + 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) +{ + DEBUG_IO("-"); + int retval = ERROR_OK; + + if (buffer_write_space(ctx) < 3) + 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) +{ + DEBUG_IO("-"); + int retval = ERROR_OK; + + if (buffer_write_space(ctx) < 1) + 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) +{ + DEBUG_IO("-"); + int retval = ERROR_OK; + + if (buffer_write_space(ctx) < 1) + retval = mpsse_flush(ctx); + + buffer_write_byte(ctx, 0x83); + buffer_add_read(ctx, data, 0, 8, 0); + + return retval; +} + +int mpsse_loopback_config(struct mpsse_ctx *ctx, bool enable) +{ + LOG_DEBUG("%s", enable ? "on" : "off"); + int retval = ERROR_OK; + + if (buffer_write_space(ctx) < 1) + retval = mpsse_flush(ctx); + + buffer_write_byte(ctx, enable ? 0x84 : 0x85); + + return retval; +} + +int mpsse_set_divisor(struct mpsse_ctx *ctx, uint16_t divisor) +{ + LOG_DEBUG("%d", divisor); + int retval = ERROR_OK; + + if (buffer_write_space(ctx) < 3) + retval = mpsse_flush(ctx); + + buffer_write_byte(ctx, 0x86); + buffer_write_byte(ctx, divisor & 0xff); + buffer_write_byte(ctx, divisor >> 8); + + return retval; +} + +/* Context needed by the callbacks */ +struct transfer_result { + struct mpsse_ctx *ctx; + bool done; + unsigned transferred; +}; + +static void read_cb(struct libusb_transfer *transfer) +{ + struct transfer_result *res = (struct transfer_result *)transfer->user_data; + struct mpsse_ctx *ctx = res->ctx; + + unsigned packet_size = ctx->max_packet_size; + + DEBUG_PRINT_BUF(transfer->buffer, transfer->actual_length); + + /* Strip the two status bytes sent at the beginning of each USB packet + * while copying the chunk buffer to the read buffer */ + unsigned num_packets = DIV_ROUND_UP(transfer->actual_length, packet_size); + unsigned chunk_remains = transfer->actual_length; + for (unsigned i = 0; i < num_packets && chunk_remains > 2; i++) { + unsigned this_size = packet_size - 2; + if (this_size > chunk_remains - 2) + this_size = chunk_remains - 2; + if (this_size > ctx->read_count - res->transferred) + this_size = ctx->read_count - res->transferred; + memcpy(ctx->read_buffer + res->transferred, + ctx->read_chunk + packet_size * i + 2, + this_size); + res->transferred += this_size; + chunk_remains -= this_size + 2; + if (res->transferred == ctx->read_count) { + res->done = true; + break; + } + } + + LOG_DEBUG("raw chunk %d, transferred %d of %d", transfer->actual_length, res->transferred, + ctx->read_count); + + if (!res->done) + if (libusb_submit_transfer(transfer) < 0) + res->done = true; +} + +static void write_cb(struct libusb_transfer *transfer) +{ + struct transfer_result *res = (struct transfer_result *)transfer->user_data; + struct mpsse_ctx *ctx = res->ctx; + + res->transferred += transfer->actual_length; + + LOG_DEBUG("transferred %d of %d", res->transferred, ctx->write_count); + + DEBUG_PRINT_BUF(transfer->buffer, transfer->actual_length); + + if (res->transferred == ctx->write_count) + res->done = 1; + else { + transfer->length = ctx->write_count - res->transferred; + transfer->buffer = ctx->write_buffer + res->transferred; + if (libusb_submit_transfer(transfer) < 0) + res->done = true; + } +} + +int mpsse_flush(struct mpsse_ctx *ctx) +{ + LOG_DEBUG("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; + + struct libusb_transfer *read_transfer = 0; + struct transfer_result read_result = { .ctx = ctx, .done = true }; + 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); + } + + struct transfer_result write_result = { .ctx = ctx, .done = false }; + struct libusb_transfer *write_transfer = libusb_alloc_transfer(0); + 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); + + /* Polling loop, more or less taken from libftdi */ + while (!write_result.done || !read_result.done) { + retval = libusb_handle_events(ctx->usb_ctx); + keep_alive(); + if (retval < 0) { + if (retval == LIBUSB_ERROR_INTERRUPTED) + continue; + 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) < 0) + break; + } + } + + if (retval < 0) { + LOG_ERROR("libusb_handle_events() failed with %d", retval); + retval = ERROR_FAIL; + } else if (write_result.transferred < ctx->write_count) { + LOG_ERROR("ftdi device did not accept all data: %d, tried %d", + write_result.transferred, + ctx->write_count); + retval = ERROR_FAIL; + } else if (read_result.transferred < ctx->read_count) { + LOG_ERROR("ftdi device did not return all data: %d, expected %d", + read_result.transferred, + ctx->read_count); + retval = ERROR_FAIL; + } else if (ctx->read_count) { + ctx->write_count = 0; + ctx->read_count = 0; + bit_copy_execute(&ctx->read_queue); + retval = ERROR_OK; + } else { + ctx->write_count = 0; + bit_copy_discard(&ctx->read_queue); + retval = ERROR_OK; + } + + libusb_free_transfer(write_transfer); + if (read_transfer) + libusb_free_transfer(read_transfer); + + if (retval != ERROR_OK) + mpsse_purge(ctx); + + return retval; +} diff --git a/src/jtag/drivers/mpsse.h b/src/jtag/drivers/mpsse.h new file mode 100644 index 0000000..77efdef --- /dev/null +++ b/src/jtag/drivers/mpsse.h @@ -0,0 +1,72 @@ +/************************************************************************** + * Copyright (C) 2012 by Andreas Fritiofson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * 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. * + ***************************************************************************/ + +#ifndef MPSSE_H_ +#define MPSSE_H_ + +#include <stdbool.h> +#include "helper/binarybuffer.h" + +/* Mode flags */ +#define POS_EDGE_OUT 0x00 +#define NEG_EDGE_OUT 0x01 +#define POS_EDGE_IN 0x00 +#define NEG_EDGE_IN 0x04 +#define MSB_FIRST 0x00 +#define LSB_FIRST 0x08 + +enum ftdi_chip_type { + TYPE_FT2232C, + TYPE_FT2232H, + TYPE_FT4232H, +}; + +struct mpsse_ctx; + +/* Device open/close */ +struct mpsse_ctx *mpsse_open(uint16_t vid, uint16_t pid, const char *description, + const char *serial, int channel, unsigned char latency); +void mpsse_close(struct mpsse_ctx *ctx); + +/* Command queuing. These correspond to the MPSSE commands with the same names, but no need to care + * about bit/byte transfer or data length limitation. Read data is guaranteed to be available only + * after the following mpsse_flush(). */ +int mpsse_clock_data_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, + unsigned length, uint8_t mode); +int mpsse_clock_data_in(struct mpsse_ctx *ctx, uint8_t *in, unsigned in_offset, unsigned length, + uint8_t mode); +int 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); +int mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, + unsigned length, bool tdi, uint8_t mode); +int 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); +int mpsse_set_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir); +int mpsse_set_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t data, uint8_t dir); +int mpsse_read_data_bits_low_byte(struct mpsse_ctx *ctx, uint8_t *data); +int mpsse_read_data_bits_high_byte(struct mpsse_ctx *ctx, uint8_t *data); +int mpsse_loopback_config(struct mpsse_ctx *ctx, bool enable); +int mpsse_set_divisor(struct mpsse_ctx *ctx, uint16_t divisor); + +/* Queue handling */ +int mpsse_flush(struct mpsse_ctx *ctx); +void mpsse_purge(struct mpsse_ctx *ctx); + +#endif /* MPSSE_H_ */ -- ------------------------------------------------------------------------------ Keep Your Developer Skills Current with LearnDevNow! The most comprehensive online learning library for Microsoft developers is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3, Metro Style Apps, more. Free future releases when you subscribe now! http://p.sf.net/sfu/learndevnow-d2d _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
