This is an automated email from Gerrit. "Weijie Gao <hackpas...@gmail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7589
-- gerrit commit d5e2eb80571ae23b0de68fd40a5f79c1c9632011 Author: Weijie Gao <hackpas...@gmail.com> Date: Mon Apr 10 15:13:01 2023 +0800 jtag/drivers/ftdi: add support for using FTDI D2XX driver on Windows This patch adds FTDI D2XX support on Windows platform. Many FTDI boards are shipped with blank EEPROM which will be likely installed with the FTDI official D2XX driver on Windows. As many users don't know how to replace the D2XX driver with libusb one, it's necessary to support the FTDI D2XX driver. This patch adds new subcommands for ftdi adapter driver: - ftdi backend (libusb|d2xx) Selects the backend to be used for accessing device, libusb or d2xx. libusb is default if not specified. This makes sure the support for D2XX does not break the compatibility. - ftdi device_location <location-id> Specify the D2XX USB device location ID of the FTDI device. Since FT_OpenEx supports three methods of opening a device, this patch has also added support for these methods: 1. By serial number: ftdi backend d2xx adapter serial <serial_string> 2. By description: ftdi backend d2xx ftdi device_desc <d2xx-related-description> 3. By location ftdi backend d2xx ftdi device_location <d2xx-location-id> For portability, the original ftd2xx.lib is not used. Instead, dynamically loading of ftd2xx.dll is used. This will make the executable not depend on ftd2xx.dll, and this feature can be always enabled for Windows platform. Signed-off-by: Weijie Gao <hackpas...@gmail.com> Change-Id: Iae42e28357da54061122fe56e4cd8e041df5255c diff --git a/configure.ac b/configure.ac index ac2808e1f5..faef5985d5 100644 --- a/configure.ac +++ b/configure.ac @@ -156,6 +156,9 @@ m4_define([PCIE_ADAPTERS], m4_define([SERIAL_PORT_ADAPTERS], [[[buspirate], [Bus Pirate], [BUS_PIRATE]]]) +m4_define([FTDI_D2XX_ADAPTERS], + [[[ftdi_d2xx], [MPSSE mode of FTDI based devices (D2XX)], [FTDI_D2XX]]]) + m4_define([OPTIONAL_LIBRARIES], [[[capstone], [Use Capstone disassembly framework], []]]) @@ -258,7 +261,8 @@ AC_ARG_ADAPTERS([ LIBFTDI_USB1_ADAPTERS LIBGPIOD_ADAPTERS, SERIAL_PORT_ADAPTERS, - LIBJAYLINK_ADAPTERS + LIBJAYLINK_ADAPTERS, + FTDI_D2XX_ADAPTERS ],[auto]) AC_ARG_ENABLE([parport], @@ -685,6 +689,7 @@ PROCESS_ADAPTERS([LIBFTDI_ADAPTERS], ["x$use_libftdi" = "xyes"], [libftdi]) PROCESS_ADAPTERS([LIBFTDI_USB1_ADAPTERS], ["x$use_libftdi" = "xyes" -a "x$use_libusb1" = "xyes"], [libftdi and libusb-1.x]) PROCESS_ADAPTERS([LIBGPIOD_ADAPTERS], ["x$use_libgpiod" = "xyes"], [libgpiod]) PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_internal_libjaylink" = "xyes" -o "x$use_libjaylink" = "xyes"], [libjaylink-0.2]) +PROCESS_ADAPTERS([FTDI_D2XX_ADAPTERS], ["x$is_win32" = "xyes"], [Windows platform]) AS_IF([test "x$enable_linuxgpiod" != "xno"], [ build_bitbang=yes @@ -832,6 +837,7 @@ m4_foreach([adapter], [USB1_ADAPTERS, LIBFTDI_USB1_ADAPTERS, LIBGPIOD_ADAPTERS, LIBJAYLINK_ADAPTERS, PCIE_ADAPTERS, SERIAL_PORT_ADAPTERS, + FTDI_D2XX_ADAPTERS, OPTIONAL_LIBRARIES], [s=m4_format(["%-40s"], ADAPTER_DESC([adapter])) AS_CASE([$ADAPTER_VAR([adapter])], diff --git a/doc/openocd.texi b/doc/openocd.texi index 67661845d0..1a52e8d7d9 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2581,7 +2581,8 @@ This driver is for adapters using the MPSSE (Multi-Protocol Synchronous Serial Engine) mode built into many FTDI chips, such as the FT2232, FT4232 and FT232H. The driver is using libusb-1.0 in asynchronous mode to talk to the FTDI device, -bypassing intermediate libraries like libftdi. +bypassing intermediate libraries like libftdi. On Windows platform, FTDI's +official D2XX driver is also supported. Support for new FTDI based adapters can be added completely through configuration files, without the need to patch and rebuild OpenOCD. @@ -2616,23 +2617,47 @@ signal. The following output buffer configurations are supported: These interfaces have several commands, used to configure the driver before initializing the JTAG scan chain: +@deffn {Config Command} {ftdi backend} libusb|d2xx +Selects which driver backend to be used. This command is only available if +OpenOCD is compiled with support for D2XX on Windows platform. +By default libusb is selected. To use the D2XX driver interface, d2xx must be +explicitly selected, e.g. +@example +ftdi backend d2xx +@end example +@end deffn + @deffn {Config Command} {ftdi vid_pid} [vid pid]+ The vendor ID and product ID of the adapter. Up to eight [@var{vid}, @var{pid}] pairs may be given, e.g. @example ftdi vid_pid 0x0403 0xcff8 0x15ba 0x0003 @end example +This is valid only if libusb backend is selected. @end deffn @deffn {Config Command} {ftdi device_desc} description Provides the USB device description (the @emph{iProduct string}) of the adapter. If not specified, the device description is ignored during device selection. +If d2xx backend is selected, this specifies the D2XX-related device description +string containing the channel information, not the USB device description. +Only one of serial/description/location can be specified for d2xx backend. +@end deffn + +@deffn {Config Command} {ftdi device_location} location_id +If d2xx backend is selected, the location id is a hex-based path value that +points to a specific D2XX device. Only one of serial/description/location can +be specified for d2xx backend. e.g. +@example +ftdi device_location 0x101 +@end example @end deffn @deffn {Config Command} {ftdi channel} channel Selects the channel of the FTDI device to use for MPSSE operations. Most adapters use the default, channel 0, but there are exceptions. +This is valid only if libusb backend is selected. @end deffn @deffn {Config Command} {ftdi layout_init} data direction diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 069f69f549..39b4dc841b 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -71,6 +71,9 @@ endif if FTDI DRIVERFILES += %D%/ftdi.c %D%/mpsse.c %D%/mpsse-libusb.c endif +if FTDI_D2XX +DRIVERFILES += %D%/ftdi.c %D%/mpsse.c %D%/mpsse-d2xx.c +endif if LINUXGPIOD DRIVERFILES += %D%/linuxgpiod.c endif diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 618fc82645..0c957a1883 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -92,6 +92,13 @@ static uint16_t ftdi_vid[MAX_USB_IDS + 1] = { 0 }; static uint16_t ftdi_pid[MAX_USB_IDS + 1] = { 0 }; #endif +#if BUILD_FTDI_D2XX == 1 +static uint32_t ftdi_d2xx_open_type; +static uint32_t ftdi_device_location; +#endif + +static bool ftdi_d2xx; + static struct mpsse_ctx *mpsse_ctx; struct signal { @@ -657,14 +664,42 @@ static int ftdi_initialize(void) LOG_DEBUG("ftdi interface using shortest path jtag state transitions"); #if BUILD_FTDI == 1 - if (!ftdi_vid[0] && !ftdi_pid[0]) { - LOG_ERROR("Please specify ftdi vid_pid"); - return ERROR_JTAG_INIT_FAILED; + if (!ftdi_d2xx) { + if (!ftdi_vid[0] && !ftdi_pid[0]) { + LOG_ERROR("Please specify ftdi vid_pid"); + return ERROR_JTAG_INIT_FAILED; + } + + mpsse_ctx = mpsse_libusb_open(ftdi_vid, ftdi_pid, ftdi_device_desc, + adapter_get_required_serial(), adapter_usb_get_location(), + ftdi_channel); } +#endif + +#if BUILD_FTDI_D2XX == 1 + if (ftdi_d2xx) { + switch (ftdi_d2xx_open_type) { + case FT_OPEN_BY_DESCRIPTION: + mpsse_ctx = mpsse_d2xx_open(ftdi_d2xx_open_type, (uintptr_t)ftdi_device_desc); + break; + + case FT_OPEN_BY_LOCATION: + mpsse_ctx = mpsse_d2xx_open(ftdi_d2xx_open_type, ftdi_device_location); + break; - mpsse_ctx = mpsse_libusb_open(ftdi_vid, ftdi_pid, ftdi_device_desc, - adapter_get_required_serial(), adapter_usb_get_location(), ftdi_channel); + default: + if (adapter_get_required_serial()) { + const char *serial = adapter_get_required_serial(); + mpsse_ctx = mpsse_d2xx_open(FT_OPEN_BY_SERIAL_NUMBER, (uintptr_t)serial); + break; + } + + LOG_ERROR("Please specify open type\n"); + return ERROR_JTAG_INIT_FAILED; + } + } #endif + if (!mpsse_ctx) return ERROR_JTAG_INIT_FAILED; @@ -695,7 +730,13 @@ static int ftdi_initialize(void) static int ftdi_quit(void) { #if BUILD_FTDI == 1 - mpsse_libusb_close(mpsse_ctx); + if (!ftdi_d2xx) + mpsse_libusb_close(mpsse_ctx); +#endif + +#if BUILD_FTDI_D2XX == 1 + if (ftdi_d2xx) + mpsse_d2xx_close(mpsse_ctx); #endif struct signal *sig = signals; @@ -718,8 +759,14 @@ COMMAND_HANDLER(ftdi_handle_device_desc_command) if (CMD_ARGC == 1) { free(ftdi_device_desc); ftdi_device_desc = strdup(CMD_ARGV[0]); +#if BUILD_FTDI_D2XX == 1 + ftdi_d2xx_open_type = FT_OPEN_BY_DESCRIPTION; +#endif } else { LOG_ERROR("expected exactly one argument to ftdi device_desc <description>"); +#if BUILD_FTDI_D2XX == 1 + return ERROR_COMMAND_SYNTAX_ERROR; +#endif } return ERROR_OK; @@ -737,6 +784,39 @@ COMMAND_HANDLER(ftdi_handle_channel_command) } #endif +#if BUILD_FTDI_D2XX == 1 +COMMAND_HANDLER(ftdi_handle_device_location_command) +{ + if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], ftdi_device_location); + ftdi_d2xx_open_type = FT_OPEN_BY_SERIAL_NUMBER; + return FT_OPEN_BY_LOCATION; + } + + LOG_ERROR("expected exactly one argument to ftdi device_location <location_id>"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HANDLER(ftdi_handle_device_backend_command) +{ + if (CMD_ARGC == 1) { + if (!strcmp(CMD_ARGV[0], "libusb")) { + ftdi_d2xx = false; + } else if (!strcmp(CMD_ARGV[0], "d2xx")) { + ftdi_d2xx = true; + } else { + LOG_ERROR("unrecognised ftdi backend '%s'\n", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; + } + + LOG_ERROR("expected exactly one argument to ftdi device_backend <libusb|d2xx>"); + return ERROR_COMMAND_SYNTAX_ERROR; +} +#endif + COMMAND_HANDLER(ftdi_handle_layout_init_command) { if (CMD_ARGC != 2) @@ -944,6 +1024,22 @@ static const struct command_registration ftdi_subcommand_handlers[] = { .help = "set the channel of the FTDI device that is used as JTAG", .usage = "(0-3)", }, +#endif +#if BUILD_FTDI_D2XX == 1 + { + .name = "device_location", + .handler = &ftdi_handle_device_location_command, + .mode = COMMAND_CONFIG, + .help = "set the D2XX USB device location ID of the FTDI device", + .usage = "hex value", + }, + { + .name = "backend", + .handler = &ftdi_handle_device_backend_command, + .mode = COMMAND_CONFIG, + .help = "set the backend to be used for opening FTDI device (libusb by default)", + .usage = "(libusb|d2xx)", + }, #endif { .name = "layout_init", diff --git a/src/jtag/drivers/mpsse-d2xx.c b/src/jtag/drivers/mpsse-d2xx.c new file mode 100644 index 0000000000..16b4752a51 --- /dev/null +++ b/src/jtag/drivers/mpsse-d2xx.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/************************************************************************** + * Copyright (C) 2023 by Weijie Gao * + * hackpas...@gmail.com * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mpsse.h" +#include "mpsse-private.h" +#include "helper/log.h" +#include "helper/replacements.h" + +#if IS_CYGWIN == 1 +#include <windows.h> +#endif + +typedef PVOID FT_HANDLE; +typedef ULONG FT_STATUS; +typedef ULONG FT_DEVICE; + +#define FT_DEVICE_2232C 4 +#define FT_DEVICE_232R 5 +#define FT_DEVICE_2232H 6 +#define FT_DEVICE_4232H 7 +#define FT_DEVICE_232H 8 + +#define FT_PURGE_RX 1 +#define FT_PURGE_TX 2 + +typedef FT_STATUS (WINAPI *fn_FT_OpenEx)(PVOID pArg1, DWORD Flags, FT_HANDLE *pHandle); +typedef FT_STATUS (WINAPI *fn_FT_Close)(FT_HANDLE ftHandle); +typedef FT_STATUS (WINAPI *fn_FT_GetDeviceInfo)(FT_HANDLE ftHandle, FT_DEVICE *lpftDevice, LPDWORD lpdwID, + PCHAR SerialNumber, PCHAR Description, LPVOID Dummy); +typedef FT_STATUS (WINAPI *fn_FT_GetStatus)(FT_HANDLE ftHandle, DWORD *dwRxBytes, + DWORD *dwTxBytes,DWORD *dwEventDWord); +typedef FT_STATUS (WINAPI *fn_FT_Read)(FT_HANDLE ftHandle, LPVOID lpBuffer, DWORD dwBytesToRead, + LPDWORD lpBytesReturned); +typedef FT_STATUS (WINAPI *fn_FT_Write)(FT_HANDLE ftHandle, LPVOID lpBuffer, + DWORD dwBytesToWrite,LPDWORD lpBytesWritten); +typedef FT_STATUS (WINAPI *fn_FT_Purge)(FT_HANDLE ftHandle, ULONG Mask); +typedef FT_STATUS (WINAPI *fn_FT_ResetDevice)(FT_HANDLE ftHandle); +typedef FT_STATUS (WINAPI *fn_FT_SetLatencyTimer)(FT_HANDLE ftHandle, UCHAR ucLatency); +typedef FT_STATUS (WINAPI *fn_FT_SetBitMode)(FT_HANDLE ftHandle, UCHAR ucMask, UCHAR ucEnable); +typedef FT_STATUS (WINAPI *fn_FT_SetTimeouts)(FT_HANDLE ftHandle, DWORD dwReadTimeout, DWORD dwWriteTimeout); + +static fn_FT_OpenEx FT_OpenEx; +static fn_FT_Close FT_Close; +static fn_FT_GetDeviceInfo FT_GetDeviceInfo; +static fn_FT_GetStatus FT_GetStatus; +static fn_FT_Read FT_Read; +static fn_FT_Write FT_Write; +static fn_FT_Purge FT_Purge; +static fn_FT_ResetDevice FT_ResetDevice; +static fn_FT_SetLatencyTimer FT_SetLatencyTimer; +static fn_FT_SetBitMode FT_SetBitMode; +static fn_FT_SetTimeouts FT_SetTimeouts; + +struct d2xx_load_symbol { + const char *name; + FARPROC *fnsym; +}; + +struct mpsse_ctx_d2xx { + struct mpsse_ctx ctx; + FT_HANDLE ftHandle; +}; + +static struct d2xx_load_symbol d2xx_symbols[] = { + { "FT_OpenEx", (FARPROC *)&FT_OpenEx }, + { "FT_Close", (FARPROC *)&FT_Close }, + { "FT_GetDeviceInfo", (FARPROC *)&FT_GetDeviceInfo }, + { "FT_GetStatus", (FARPROC *)&FT_GetStatus }, + { "FT_Read", (FARPROC *)&FT_Read }, + { "FT_Write", (FARPROC *)&FT_Write }, + { "FT_Purge", (FARPROC *)&FT_Purge }, + { "FT_ResetDevice", (FARPROC *)&FT_ResetDevice }, + { "FT_SetLatencyTimer", (FARPROC *)&FT_SetLatencyTimer }, + { "FT_SetBitMode", (FARPROC *)&FT_SetBitMode }, + { "FT_SetTimeouts", (FARPROC *)&FT_SetTimeouts }, +}; + +static void mpsse_d2xx_purge(struct mpsse_ctx *ctx); +static int mpsse_d2xx_flush(struct mpsse_ctx *ctx); + +static HMODULE hFtd2xx; + +/* Dynamically load all required symbols to ensure the executable does not depend on ftd2xx.dll */ +static bool load_d2xx(void) +{ + uint32_t i; + + if (hFtd2xx) + return true; + + hFtd2xx = LoadLibrary("ftd2xx.dll"); + if (!hFtd2xx) { + LOG_ERROR("Unable to load ftd2xx.dll"); + return false; + } + + for (i = 0; i < ARRAY_SIZE(d2xx_symbols); i++) { + *d2xx_symbols[i].fnsym = GetProcAddress(hFtd2xx, d2xx_symbols[i].name); + if (!*d2xx_symbols[i].fnsym) { + LOG_ERROR("Unable to locate %s in ftd2xx.dll", d2xx_symbols[i].name); + goto cleanup; + } + } + + LOG_DEBUG("Loaded ftd2xx.dll"); + return true; + +cleanup: + FreeLibrary(hFtd2xx); + hFtd2xx = NULL; + + return false; +} + +struct mpsse_ctx *mpsse_d2xx_open(uint32_t open_type, uintptr_t arg) +{ + char SerialNumber[16], Description[64]; + struct mpsse_ctx_d2xx *dctx; + FT_DEVICE ftDevice; + FT_STATUS ftStatus; + DWORD dwID; + + if (!load_d2xx()) + return NULL; + + dctx = calloc(1, sizeof(*dctx)); + if (!dctx) + return NULL; + + if (!mpsse_common_init(&dctx->ctx)) + goto error; + + dctx->ctx.flush = mpsse_d2xx_flush; + dctx->ctx.purge = mpsse_d2xx_purge; + + switch (open_type) { + case FT_OPEN_BY_SERIAL_NUMBER: + case FT_OPEN_BY_DESCRIPTION: + case FT_OPEN_BY_LOCATION: + ftStatus = FT_OpenEx((PVOID)arg, open_type, &dctx->ftHandle); + break; + + default: + LOG_ERROR("Invalid FT_OpenEx type %u\n", open_type); + goto error; + } + + if (ftStatus) { + switch (open_type) { + case FT_OPEN_BY_SERIAL_NUMBER: + LOG_ERROR("Failed to open D2XX device with serial number '%s', error %lu\n", + (const char *)arg, ftStatus); + break; + + case FT_OPEN_BY_DESCRIPTION: + LOG_ERROR("Failed to open D2XX device with description '%s', error %lu\n", + (const char *)arg, ftStatus); + break; + + case FT_OPEN_BY_LOCATION: + LOG_ERROR("Failed to open D2XX device with location 0x%zx, error %lu\n", + arg, ftStatus); + break; + } + + goto error; + } + + /* Reset FTDI device */ + ftStatus = FT_ResetDevice(dctx->ftHandle); + if (ftStatus) { + LOG_ERROR("Failed to reset FTDI device, error %lu\n", ftStatus); + goto error; + } + + ftStatus = FT_GetDeviceInfo(dctx->ftHandle, &ftDevice, &dwID, SerialNumber, Description, NULL); + if (ftStatus) { + LOG_ERROR("Failed to get FTDI device information, error %lu\n", ftStatus); + goto error; + } + + switch (ftDevice) { + case FT_DEVICE_2232C: + dctx->ctx.type = TYPE_FT2232C; + break; + + case FT_DEVICE_2232H: + dctx->ctx.type = TYPE_FT2232H; + break; + + case FT_DEVICE_4232H: + dctx->ctx.type = TYPE_FT4232H; + break; + + case FT_DEVICE_232H: + dctx->ctx.type = TYPE_FT232H; + break; + + default: + LOG_ERROR("Unsupported FTDI chip type: %lu", ftDevice); + goto error; + } + + ftStatus = FT_SetLatencyTimer(dctx->ftHandle, 255); + if (ftStatus) { + LOG_ERROR("Failed to set latency timer, error %lu\n", ftStatus); + goto error; + } + + ftStatus = FT_SetTimeouts(dctx->ftHandle, 5000, 5000); + if (ftStatus) { + LOG_ERROR("Failed to set FTDI device read/write timeout, error %lu\n", ftStatus); + goto error; + } + + ftStatus = FT_SetBitMode(dctx->ftHandle, 0x0b, BITMODE_MPSSE); + if (ftStatus) { + LOG_ERROR("Failed to set MPSSE bitmode, error %lu\n", ftStatus); + goto error; + } + + mpsse_d2xx_purge(&dctx->ctx); + + return &dctx->ctx; + +error: + mpsse_d2xx_close(&dctx->ctx); + return NULL; +} + +void mpsse_d2xx_close(struct mpsse_ctx *ctx) +{ + struct mpsse_ctx_d2xx *dctx = container_of(ctx, struct mpsse_ctx_d2xx, ctx); + + if (dctx->ftHandle) + FT_Close(dctx->ftHandle); + + mpsse_common_cleanup(ctx); + + free(dctx); +} + +static void mpsse_d2xx_purge(struct mpsse_ctx *ctx) +{ + struct mpsse_ctx_d2xx *dctx = container_of(ctx, struct mpsse_ctx_d2xx, ctx); + FT_STATUS ftStatus; + + LOG_DEBUG("-"); + + ctx->write_count = 0; + ctx->read_count = 0; + ctx->retval = ERROR_OK; + bit_copy_discard(&ctx->read_queue); + + ftStatus = FT_Purge(dctx->ftHandle, FT_PURGE_RX | FT_PURGE_TX); + if (ftStatus) + LOG_ERROR("Failed to purge FTDI rx/tx buffers, error %lu\n", ftStatus); +} + +static int mpsse_d2xx_flush(struct mpsse_ctx *ctx) +{ + struct mpsse_ctx_d2xx *dctx = container_of(ctx, struct mpsse_ctx_d2xx, ctx); + DWORD dwBytesWritten = 0, dwBytesRead = 0; + int retval = ctx->retval; + FT_STATUS ftStatus; + + 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 %u%s, read %u", 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 */ + + if (ctx->write_count == 0) + return retval; + + if (ctx->read_count) + buffer_write_byte(ctx, 0x87); /* SEND_IMMEDIATE */ + + ftStatus = FT_Write(dctx->ftHandle, ctx->write_buffer, ctx->write_count, &dwBytesWritten); + if (ftStatus) { + LOG_ERROR("Failed to write data to FTDI device, error %lu\n", ftStatus); + retval = ERROR_FAIL; + goto error_out; + } + + LOG_DEBUG_IO("written %lu of %u", dwBytesWritten, ctx->write_count); + + if (dwBytesWritten < ctx->write_count) { + LOG_ERROR("FTDI device did not accept all data: %lu, tried %u", + dwBytesWritten, ctx->write_count); + retval = ERROR_TIMEOUT_REACHED; + goto error_out; + } + + if (ctx->read_count) { + ftStatus = FT_Read(dctx->ftHandle, ctx->read_buffer, ctx->read_count, &dwBytesRead); + if (ftStatus) { + LOG_ERROR("Failed to read data from FTDI device, error %lu\n", ftStatus); + retval = ERROR_FAIL; + goto error_out; + } + + LOG_DEBUG_IO("read %lu of %u", dwBytesRead, ctx->read_count); + + if (dwBytesRead < ctx->read_count) { + LOG_ERROR("FTDI device did not return all data: %lu, tried %u", + dwBytesRead, ctx->read_count); + retval = ERROR_TIMEOUT_REACHED; + goto error_out; + } + } + + 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; + } + +error_out: + if (retval != ERROR_OK) + mpsse_d2xx_purge(ctx); + + return retval; +} diff --git a/src/jtag/drivers/mpsse.h b/src/jtag/drivers/mpsse.h index c147906b60..7dc039e139 100644 --- a/src/jtag/drivers/mpsse.h +++ b/src/jtag/drivers/mpsse.h @@ -34,6 +34,16 @@ struct mpsse_ctx *mpsse_libusb_open(const uint16_t *vid, const uint16_t *pid, co const char *serial, const char *location, int channel); void mpsse_libusb_close(struct mpsse_ctx *ctx); #endif + +#ifdef BUILD_FTDI_D2XX +#define FT_OPEN_BY_SERIAL_NUMBER 1 +#define FT_OPEN_BY_DESCRIPTION 2 +#define FT_OPEN_BY_LOCATION 4 + +struct mpsse_ctx *mpsse_d2xx_open(uint32_t open_type, uintptr_t arg); +void mpsse_d2xx_close(struct mpsse_ctx *ctx); +#endif + bool mpsse_is_high_speed(struct mpsse_ctx *ctx); /* Command queuing. These correspond to the MPSSE commands with the same names, but no need to care diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 12848be03a..f140d09ae6 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -37,7 +37,7 @@ extern struct adapter_driver parport_adapter_driver; #if BUILD_DUMMY == 1 extern struct adapter_driver dummy_adapter_driver; #endif -#if BUILD_FTDI == 1 +#if BUILD_FTDI == 1 || BUILD_FTDI_D2XX == 1 extern struct adapter_driver ftdi_adapter_driver; #endif #if BUILD_USB_BLASTER == 1 || BUILD_USB_BLASTER_2 == 1 @@ -154,7 +154,7 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_DUMMY == 1 &dummy_adapter_driver, #endif -#if BUILD_FTDI == 1 +#if BUILD_FTDI == 1 || BUILD_FTDI_D2XX == 1 &ftdi_adapter_driver, #endif #if BUILD_USB_BLASTER || BUILD_USB_BLASTER_2 == 1 --