|
From: <ge...@op...> - 2025-09-23 15:42:01
|
This is an automated email from Gerrit. "Tomasz Motylewski <tom...@gm...>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9140 -- gerrit commit 2366263ba5e851f12bf7164bf4a18182eb8c4260 Author: Tomasz Motylewski <tom...@gm...> Date: Tue Sep 23 17:04:16 2025 +0200 drivers/ft232r: support multichannel FTx232H chips using bitbang mode with arbitrary IO pins Add "channel" parameter to use all ports of multiport FTDI bridges. Code borrowed from ftdi.c and mpsse.c. Synchronous bitbang mode in ft232r driver works in all modern FTDI bridges and despite being less efficient, allows full flexibility in assigning functions to IOs. In addition, 4-port FTDI ICs like FT4232H support MPSSE only in the first 2 channels, which does not allow channels 2 and 3 to be used with ftdi.c interface driver. These channels may be used now with ft232r driver. Change-Id: Ifb61e83ba9454abbac819147e41da2147cd3f932 Signed-off-by: Tomasz Motylewski <tom...@gm...> diff --git a/doc/openocd.texi b/doc/openocd.texi index 7034c049c5..2ce5ec693e 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2854,6 +2854,8 @@ For example adapter definitions, see the configuration files shipped in the This driver implements JTAG and SWD via synchronous bitbang mode of an FTDI FT232R, FT230X, FT231X and similar USB UART bridge ICs, reusing RS232 signals as GPIO. It currently doesn't support using CBUS pins as GPIO. +It should work with multiport ICs like FT2232 or FT4232 as they support the same +synchronous bitbang mode. List of connections (default physical pin numbers for FT232R in 28-pin SSOP package): @itemize @minus @@ -2878,7 +2880,7 @@ that are sometimes not used like TRST or SRST. SWD lines may share pins with JTAG lines, as the two protocols are not active simultaneously. -FT232R +FT232R, FT2232H and FT4232H bit numbers: @itemize @minus @item bit 7 - RI @item bit 6 - DCD @@ -2898,6 +2900,11 @@ The vendor ID and product ID of the adapter. If not specified, default 0x0403:0x6001 is used. @end deffn +@deffn {Config Command} {ft232r channel} channel +Selects the channel of the FTDI device to use for synchronous bitbang operations. Most +adapters use the default - channel 0, but multiport chips like FTx232 may use other channels. +@end deffn + @deffn {Config Command} {ft232r jtag_nums} @var{tck} @var{tms} @var{tdi} @var{tdo} Set four JTAG GPIO numbers at once. If not specified, default 0 3 1 2 or TXD CTS RXD RTS is used. diff --git a/src/jtag/drivers/ft232r.c b/src/jtag/drivers/ft232r.c index 94568ca32b..2e07b65c50 100644 --- a/src/jtag/drivers/ft232r.c +++ b/src/jtag/drivers/ft232r.c @@ -6,7 +6,9 @@ ***************************************************************************/ /* 2022-05: SWD support added by Michael Hough; - * strongly inspired by bcm2835gpio.c, implements bitbang.c driver + * strongly inspired by bcm2835gpio.c, implements bitbang.c driver. + * 2025-09: channel parameter added by Tomasz Motylewski + * to support FT2232 and FT4232 and possibly other multiport FTDI chips * TODO: SWD bitbang should be optimized to use block transfers, with either: * implementing that feature in bitbang.c SWD like JTAG sample() or * removing dependency on bitbang.c and implementing SWD directly here @@ -44,10 +46,10 @@ */ /* - * USB endpoints. + * USB endpoints. We write to device IN_EP and call it here usb_write_ep */ -#define IN_EP 0x02 -#define OUT_EP 0x81 +#define IN_EP usb_write_ep +#define OUT_EP usb_read_ep /* Control request types: */ #define SIO_RESET 0 /* Reset the port */ @@ -76,7 +78,11 @@ static uint16_t ft232r_vid = 0x0403; /* FTDI */ static uint16_t ft232r_pid = 0x6001; /* FT232R */ +static uint8_t ft232r_channel; /* for other FTx23x chips which have more interfaces */ static struct libusb_device_handle *adapter; +static int usb_read_ep = 0x81; +static int usb_write_ep = 0x02; +static int usb_max_packet_size = 64; // read from USB descriptor, not used yet static uint8_t *ft232r_output; static size_t ft232r_output_len; @@ -107,7 +113,6 @@ static int swclk_gpio = 7; /* Some device connectors share TMS/SDIO and TCK/SWCLK; * this is allowed in configuration. */ - static size_t ft232r_buf_size = FT232R_BUF_SIZE_EXTRA; /* 0xFFFF disables restore by default, after exit serial port will not work. @@ -126,7 +131,7 @@ static int ft232r_set_dirs(unsigned char dir_mask) return jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, SIO_SET_BITMODE, dir_mask | 0x0400, - 0, NULL, 0, 1000, NULL); + ft232r_channel + 1, NULL, 0, 1000, NULL); } /** @@ -154,7 +159,7 @@ static int ft232r_swd_write(int swclk, int swdio) Telling it to nuke buffer is much, much faster than actually reading. */ jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_RESET, SIO_RESET_PURGE_TX, 0, NULL, 0, 1000, NULL); + SIO_RESET, SIO_RESET_PURGE_TX, ft232r_channel + 1, NULL, 0, 1000, NULL); runs = 0; } @@ -182,7 +187,7 @@ static int ft232r_swdio_read(void) jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, SIO_READ_PINS, 0, - 0, &data, 1, 1000, NULL); + ft232r_channel + 1, &data, 1, 1000, NULL); return (data & swdio_mask) != 0; } @@ -221,7 +226,7 @@ static int ft232r_send_recv(void) if (jtag_libusb_bulk_write(adapter, IN_EP, (char *) ft232r_output + total_written, bytes_to_write, 1000, &n) != ERROR_OK) { - LOG_ERROR("usb bulk write failed"); + LOG_ERROR("usb bulk write failed in send_recv"); return ERROR_JTAG_DEVICE_ERROR; } @@ -335,7 +340,7 @@ static int ft232r_speed(int divisor) if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_SET_BAUD_RATE, divisor, 0, NULL, 0, 1000, NULL) != ERROR_OK) { + SIO_SET_BAUD_RATE, divisor, ft232r_channel + 1, NULL, 0, 1000, NULL) != ERROR_OK) { LOG_ERROR("cannot set baud rate"); return ERROR_JTAG_DEVICE_ERROR; } @@ -355,6 +360,10 @@ static struct bitbang_interface ft232r_bitbang = { static int ft232r_init(void) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *config0; + int err; + bitbang_interface = &ft232r_bitbang; uint16_t avids[] = {ft232r_vid, 0}; @@ -367,13 +376,46 @@ static int ft232r_init(void) } LOG_DEBUG("Device found"); + err = libusb_get_device_descriptor(libusb_get_device(adapter), &desc); + if (err != LIBUSB_SUCCESS) { + LOG_ERROR("libusb_get_device_descriptor() failed with %s", libusb_error_name(err)); + libusb_close(adapter); + return ERROR_JTAG_INIT_FAILED; + } + + err = libusb_get_config_descriptor(libusb_get_device(adapter), 0, &config0); + if (err != LIBUSB_SUCCESS) { + LOG_ERROR("libusb_get_config_descriptor() failed with %s", libusb_error_name(err)); + libusb_close(adapter); + return ERROR_JTAG_INIT_FAILED; + } + + /* Make sure the first configuration is selected */ + int cfg; + err = libusb_get_configuration(adapter, &cfg); + if (err != LIBUSB_SUCCESS) { + LOG_ERROR("libusb_get_configuration() failed with %s", libusb_error_name(err)); + libusb_close(adapter); + return ERROR_JTAG_INIT_FAILED; + } + + if (desc.bNumConfigurations > 0 && cfg != config0->bConfigurationValue) { + err = libusb_set_configuration(adapter, config0->bConfigurationValue); + if (err != LIBUSB_SUCCESS) { + LOG_ERROR("libusb_set_configuration() failed with %s", libusb_error_name(err)); + libusb_close(adapter); + return ERROR_JTAG_INIT_FAILED; + } + } + if (ft232r_restore_bitmode == 0xFFFF) /* serial port will not be restored after jtag: */ - libusb_detach_kernel_driver(adapter, 0); + libusb_detach_kernel_driver(adapter, ft232r_channel); else /* serial port will be restored after jtag: */ libusb_set_auto_detach_kernel_driver(adapter, 1); /* 1: DONT_DETACH_SIO_MODULE */ - if (libusb_claim_interface(adapter, 0)) { + if (libusb_claim_interface(adapter, ft232r_channel)) { LOG_ERROR("unable to claim interface"); + libusb_close(adapter); return ERROR_JTAG_INIT_FAILED; } LOG_DEBUG("Interface claimed"); @@ -381,19 +423,49 @@ static int ft232r_init(void) /* Reset the device. */ if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_RESET, 0, 0, NULL, 0, 1000, NULL) != ERROR_OK) { + SIO_RESET, 0, ft232r_channel + 1, NULL, 0, 1000, NULL) != ERROR_OK) { LOG_ERROR("unable to reset device"); + libusb_close(adapter); return ERROR_JTAG_INIT_FAILED; } LOG_DEBUG("Device reset"); + /* Determine maximum packet size and endpoint addresses */ + if (!(desc.bNumConfigurations > 0 && ft232r_channel < config0->bNumInterfaces + && config0->interface[ft232r_channel].num_altsetting > 0)) { + goto desc_error; +} + + const struct libusb_interface_descriptor *descriptor; + descriptor = &config0->interface[ft232r_channel].altsetting[0]; + if (descriptor->bNumEndpoints != 2) + goto desc_error; + + usb_read_ep = 0; + usb_write_ep = 0; + for (int i = 0; i < descriptor->bNumEndpoints; i++) { + if (descriptor->endpoint[i].bEndpointAddress & 0x80) { + usb_read_ep = descriptor->endpoint[i].bEndpointAddress; + usb_max_packet_size = + descriptor->endpoint[i].wMaxPacketSize; + } else { + usb_write_ep = descriptor->endpoint[i].bEndpointAddress; + } + } + + if (usb_read_ep == 0 || usb_write_ep == 0) + goto desc_error; + + libusb_free_config_descriptor(config0); + /* Sync bit bang mode. TDO is the only input to start. */ unsigned char dir_mask = ~(1 << tdo_gpio); if (ft232r_set_dirs(dir_mask) != 0) { LOG_ERROR("cannot set sync bitbang mode"); return ERROR_JTAG_INIT_FAILED; } - LOG_DEBUG("Bitbang mode enabled"); + LOG_INFO("Bitbang mode enabled channel %d read_ep 0x%02x write_ep 0x%02x maxsize %d", ft232r_channel, + usb_read_ep, usb_write_ep, usb_max_packet_size); /* Exactly 500 nsec between updates. */ unsigned int divisor = 1; @@ -407,7 +479,7 @@ static int ft232r_init(void) if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_SET_LATENCY_TIMER, latency_timer, 0, NULL, 0, 1000, NULL) != ERROR_OK) { + SIO_SET_LATENCY_TIMER, latency_timer, ft232r_channel + 1, NULL, 0, 1000, NULL) != ERROR_OK) { LOG_ERROR("unable to set latency timer"); return ERROR_JTAG_INIT_FAILED; } @@ -421,6 +493,11 @@ static int ft232r_init(void) LOG_DEBUG("Buffer allocated"); return ERROR_OK; +desc_error: + LOG_ERROR("unrecognized USB device descriptor"); + libusb_free_config_descriptor(config0); + libusb_close(adapter); + return ERROR_JTAG_INIT_FAILED; } static int ft232r_quit(void) @@ -430,12 +507,12 @@ static int ft232r_quit(void) if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, SIO_SET_BITMODE, ft232r_restore_bitmode, - 0, NULL, 0, 1000, NULL) != ERROR_OK) { + ft232r_channel + 1, NULL, 0, 1000, NULL) != ERROR_OK) { LOG_ERROR("cannot set bitmode to restore serial port"); } } - if (libusb_release_interface(adapter, 0) != 0) + if (libusb_release_interface(adapter, ft232r_channel) != 0) LOG_ERROR("usb release interface failed"); jtag_libusb_close(adapter); @@ -516,6 +593,16 @@ COMMAND_HANDLER(ft232r_handle_vid_pid_command) return ERROR_OK; } +COMMAND_HANDLER(ft232r_handle_channel_command) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], ft232r_channel); + else + return ERROR_COMMAND_SYNTAX_ERROR; + + return ERROR_OK; +} + COMMAND_HANDLER(ft232r_handle_jtag_nums_command) { if (CMD_ARGC == 4) { @@ -695,6 +782,13 @@ static const struct command_registration ft232r_subcommand_handlers[] = { .help = "USB VID and PID of the adapter", .usage = "vid pid", }, + { + .name = "channel", + .handler = &ft232r_handle_channel_command, + .mode = COMMAND_CONFIG, + .help = "set the channel of the FTDI device that is used as JTAG/SWD", + .usage = "(0-3)", + }, { .name = "jtag_nums", .handler = ft232r_handle_jtag_nums_command, @@ -1063,5 +1157,3 @@ struct adapter_driver ft232r_adapter_driver = { .jtag_ops = &ft232r_interface, .swd_ops = &bitbang_swd, }; - - -- |