This is an automated email from Gerrit.

"Fredrik Anderson (John Sanpe) <sanp...@gmail.com>" just uploaded a new patch 
set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7430

-- gerrit

commit ecdd911cf6c70982beede65c306ed8d7b42dbdef
Author: John Sanpe <sanp...@gmail.com>
Date:   Wed Dec 14 06:56:18 2022 +0800

    jtag/drivers/ch341a: Added ch341a jtag driver support.
    
    Added new jtag device support.
    
    Change-Id: I97a46e6e23d33ce31bfb181f48d9f5754a82f121
    Signed-off-by: John Sanpe <sanp...@gmail.com>

diff --git a/configure.ac b/configure.ac
index bcacfb2404..c25045677f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -128,7 +128,8 @@ m4_define([USB1_ADAPTERS],
        [[armjtagew], [Olimex ARM-JTAG-EW Programmer], [ARMJTAGEW]],
        [[rlink], [Raisonance RLink JTAG Programmer], [RLINK]],
        [[usbprog], [USBProg JTAG Programmer], [USBPROG]],
-       [[esp_usb_jtag], [Espressif JTAG Programmer], [ESP_USB_JTAG]]])
+       [[esp_usb_jtag], [Espressif JTAG Programmer], [ESP_USB_JTAG]],
+       [[ch341a], [CH341A JTAG Programmer], [CH341A]]])
 
 m4_define([DEPRECATED_USB1_ADAPTERS],
        [[[aice], [Andes JTAG Programmer (deprecated)], [AICE]]])
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 6410f37545..d9859c5c9b 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -193,6 +193,9 @@ endif
 if AM335XGPIO
 DRIVERFILES += %D%/am335xgpio.c
 endif
+if CH341A
+DRIVERFILES += %D%/ch341a.c
+endif
 
 DRIVERHEADERS = \
        %D%/bitbang.h \
diff --git a/src/jtag/drivers/ch341a.c b/src/jtag/drivers/ch341a.c
new file mode 100644
index 0000000000..759ef03111
--- /dev/null
+++ b/src/jtag/drivers/ch341a.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright(c) 2021 John Sanpe <sanp...@gmail.com>
+ */
+
+/* project specific includes */
+#include <jtag/adapter.h>
+#include <jtag/interface.h>
+#include <jtag/commands.h>
+#include <helper/time_support.h>
+#include "libusb_helper.h"
+
+/* system includes */
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define CH341A_USB_VENDOR               0x1a86
+#define CH341A_USB_PRODUCT              0x5512
+
+#define CH341A_PACKET_LENGTH            32
+#define CH341A_MAX_PACKETS              256
+#define CH341A_BULK_TIMEOUT             1000
+
+#define CH341A_INTER_READ_ENDPOINT      0x81
+#define CH341A_INTER_WRITE_ENDPOINT     0x01
+#define CH341A_BULK_READ_ENDPOINT       0x82
+#define CH341A_BULK_WRITE_ENDPOINT      0x02
+
+#define CH341A_VENDOR_READ              0xc0
+#define CH341A_VENDOR_WRITE             0x40
+#define CH341A_VENDOR_VERSION           0x5f
+
+#define CH341A_PARA_CMD_R0              0xac
+#define CH341A_PARA_CMD_R1              0xad
+#define CH341A_PARA_CMD_W0              0xa6
+#define CH341A_PARA_CMD_W1              0xa7
+#define CH341A_PARA_CMD_STS             0xa0
+
+#define CH341A_PARA_MODE_EPP            0x00
+#define CH341A_PARA_MODE_EPP17          0x00
+#define CH341A_PARA_MODE_EPP19          0x01
+#define CH341A_PARA_MODE_MEM            0x02
+#define CH341A_PARA_MODE_ECP            0x03
+
+#define CH341A_CMD_SET_OUTPUT           0xa1
+#define CH341A_CMD_IO_ADDR              0xa2
+#define CH341A_CMD_PRINT_OUT            0xa3
+#define CH341A_CMD_PWM_OUT              0xa4
+#define CH341A_CMD_SHORT_PKT            0xa5
+#define CH341A_CMD_SPI_STREAM           0xa8
+#define CH341A_CMD_SIO_STREAM           0xa9
+#define CH341A_CMD_I2C_STREAM           0xaa
+#define CH341A_CMD_UIO_STREAM           0xab
+
+#define CH341A_IO_CMD_ADDR_W            0x00
+#define CH341A_IO_CMD_ADDR_R            0x80
+
+#define CH341A_I2C_CMD_STM_STA          0x74
+#define CH341A_I2C_CMD_STM_STO          0x75
+#define CH341A_I2C_CMD_STM_OUT          0x80
+#define CH341A_I2C_CMD_STM_IN           0xc0
+#define CH341A_I2C_CMD_STM_MAX          0x20
+#define CH341A_I2C_CMD_STM_SET          0x60
+#define CH341A_I2C_CMD_STM_US           0x40
+#define CH341A_I2C_CMD_STM_MS           0x50
+#define CH341A_I2C_CMD_STM_DLY          0x0f
+#define CH341A_I2C_CMD_STM_END          0x00
+
+#define CH341A_I2C_STM_20K              0x00
+#define CH341A_I2C_STM_100K             0x01
+#define CH341A_I2C_STM_400K             0x02
+#define CH341A_I2C_STM_750K             0x03
+#define CH341A_SPI_STM_DBL              0x04
+
+#define CH341A_UIO_CMD_STM_IN           0x00
+#define CH341A_UIO_CMD_STM_DIR          0x40
+#define CH341A_UIO_CMD_STM_OUT          0x80
+#define CH341A_UIO_CMD_STM_US           0xc0
+#define CH341A_UIO_CMD_STM_END          0x20
+
+#define CH341A_SET_OUTPUT_ERR           0x000100
+#define CH341A_SET_OUTPUT_PEMP          0x000200
+#define CH341A_SET_OUTPUT_INT           0x000400
+#define CH341A_SET_OUTPUT_SLCT          0x000800
+#define CH341A_SET_OUTPUT_WAIT          0x002000
+#define CH341A_SET_OUTPUT_DATAS         0x004000
+#define CH341A_SET_OUTPUT_ADDRS         0x008000
+#define CH341A_SET_OUTPUT_RESET         0x010000
+#define CH341A_SET_OUTPUT_WRITE         0x020000
+#define CH341A_SET_OUTPUT_SCL           0x400000
+#define CH341A_SET_OUTPUT_SDA           0x800000
+
+enum ch341a_pin_num {
+       CH341A_PIN_D0       = 0,
+       CH341A_PIN_D1       = 1,
+       CH341A_PIN_D2       = 2,
+       CH341A_PIN_D3       = 3,
+       CH341A_PIN_D4       = 4,
+       CH341A_PIN_D5       = 5,
+       CH341A_PIN_D6       = 6,
+       CH341A_PIN_D7       = 7,
+       CH341A_PIN_NULL,
+};
+
+static const char * const ch341a_pin_name[] = {
+       [CH341A_PIN_D0]     = "D0",
+       [CH341A_PIN_D1]     = "D1",
+       [CH341A_PIN_D2]     = "D2",
+       [CH341A_PIN_D3]     = "D3",
+       [CH341A_PIN_D4]     = "D4",
+       [CH341A_PIN_D5]     = "D5",
+       [CH341A_PIN_D6]     = "D6",
+       [CH341A_PIN_D7]     = "D7",
+};
+
+static uint16_t ch341a_vid = 0x1a86;
+static uint16_t ch341a_pid = 0x5512;
+static struct libusb_device_handle *ch341a_adapter;
+
+static unsigned int ch341a_tck_gpio     = CH341A_PIN_D3;
+static unsigned int ch341a_tms_gpio     = CH341A_PIN_D0;
+static unsigned int ch341a_tdo_gpio     = CH341A_PIN_D7;
+static unsigned int ch341a_tdi_gpio     = CH341A_PIN_D5;
+static unsigned int ch341a_trst_gpio    = CH341A_PIN_D1;
+static unsigned int ch341a_srst_gpio    = CH341A_PIN_D2;
+
+static uint8_t *ch341a_port_buffer;
+static unsigned long ch341a_port_buffer_curr;
+static unsigned long ch341a_port_buffer_size = 4096;
+
+static enum ch341a_pin_num ch341a_name_to_pin(const char *name)
+{
+       unsigned int count;
+
+       for (count = 0; count < ARRAY_SIZE(ch341a_pin_name); ++count)
+               if (!strcmp(ch341a_pin_name[count], name))
+                       return count;
+
+       return CH341A_PIN_NULL;
+}
+
+static bool ch341a_pin_is_read(enum ch341a_pin_num num)
+{
+       return num <= CH341A_PIN_D7;
+}
+
+static bool ch341a_pin_is_write(enum ch341a_pin_num num)
+{
+       return num <= CH341A_PIN_D5;
+}
+
+COMMAND_HANDLER(ch341a_handle_vid_pid_command)
+{
+       if (CMD_ARGC > 2) {
+               LOG_WARNING("ignoring extra IDs in ch341a_vid_pid "
+                                       "(maximum is 1 pair)");
+               CMD_ARGC = 2;
+       }
+       if (CMD_ARGC == 2) {
+               COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], ch341a_vid);
+               COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], ch341a_pid);
+       } else {
+               LOG_WARNING("incomplete ch341a_vid_pid configuration");
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ch341a_handle_jtag_nums_command)
+{
+       if (CMD_ARGC != 8)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ch341a_tck_gpio     = ch341a_name_to_pin(CMD_ARGV[0]);
+       ch341a_tms_gpio     = ch341a_name_to_pin(CMD_ARGV[1]);
+       ch341a_tdo_gpio     = ch341a_name_to_pin(CMD_ARGV[2]);
+       ch341a_tdi_gpio     = ch341a_name_to_pin(CMD_ARGV[3]);
+       ch341a_trst_gpio    = ch341a_name_to_pin(CMD_ARGV[4]);
+       ch341a_srst_gpio    = ch341a_name_to_pin(CMD_ARGV[5]);
+
+       if (!ch341a_pin_is_write(ch341a_tck_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       if (!ch341a_pin_is_write(ch341a_tms_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       if (!ch341a_pin_is_read(ch341a_tdo_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       if (!ch341a_pin_is_write(ch341a_tdi_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       if (!ch341a_pin_is_write(ch341a_trst_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       if (!ch341a_pin_is_write(ch341a_srst_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       command_print(CMD, "ch341a nums: "
+               "TCK = %d %s, TMS = %d %s, TDI = %d %s,"
+               "TDO = %d %s, TRST = %d %s, SRST = %d %s",
+               ch341a_tck_gpio,  ch341a_pin_name[ch341a_tck_gpio],
+               ch341a_tms_gpio,  ch341a_pin_name[ch341a_tms_gpio],
+               ch341a_tdo_gpio,  ch341a_pin_name[ch341a_tdo_gpio],
+               ch341a_tdi_gpio,  ch341a_pin_name[ch341a_tdi_gpio],
+               ch341a_trst_gpio, ch341a_pin_name[ch341a_trst_gpio],
+               ch341a_srst_gpio, ch341a_pin_name[ch341a_srst_gpio]
+       );
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ch341a_handle_tck_num_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ch341a_tck_gpio = ch341a_name_to_pin(CMD_ARGV[0]);
+
+       if (!ch341a_pin_is_write(ch341a_tck_gpio))
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       command_print(CMD, "ch341a num: TCK = %d %s",
+                                 ch341a_tck_gpio, 
ch341a_pin_name[ch341a_tck_gpio]);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ch341a_handle_tms_num_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ch341a_tms_gpio = ch341a_name_to_pin(CMD_ARGV[0]);
+
+       if (!ch341a_pin_is_write(ch341a_tms_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       command_print(CMD, "ch341a num: TMS = %d %s",
+                                 ch341a_tms_gpio, 
ch341a_pin_name[ch341a_tms_gpio]);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ch341a_handle_tdo_num_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ch341a_tdo_gpio = ch341a_name_to_pin(CMD_ARGV[0]);
+
+       if (!ch341a_pin_is_read(ch341a_tdo_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       command_print(CMD, "ch341a num: TDO = %d %s",
+                                 ch341a_tdo_gpio, 
ch341a_pin_name[ch341a_tdo_gpio]);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ch341a_handle_tdi_num_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ch341a_tdi_gpio = ch341a_name_to_pin(CMD_ARGV[0]);
+
+       if (!ch341a_pin_is_write(ch341a_tdi_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       command_print(CMD, "ch341a num: TDI = %d %s",
+                                 ch341a_tdi_gpio, 
ch341a_pin_name[ch341a_tdi_gpio]);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ch341a_handle_trst_num_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ch341a_trst_gpio = ch341a_name_to_pin(CMD_ARGV[0]);
+
+       if (!ch341a_pin_is_write(ch341a_trst_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       command_print(CMD, "ch341a num: TRST = %d %s",
+                                 ch341a_trst_gpio, 
ch341a_pin_name[ch341a_trst_gpio]);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(ch341a_handle_srst_num_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       ch341a_srst_gpio = ch341a_name_to_pin(CMD_ARGV[0]);
+
+       if (!ch341a_pin_is_write(ch341a_srst_gpio))
+               return ERROR_COMMAND_CLOSE_CONNECTION;
+
+       command_print(CMD, "ch341a num: SRST = %d %s",
+                                 ch341a_srst_gpio, 
ch341a_pin_name[ch341a_srst_gpio]);
+
+       return ERROR_OK;
+}
+
+static int ch341a_port_direction(uint8_t dire)
+{
+       uint8_t transfer[3];
+       int ret, received;
+
+       transfer[0] = CH341A_CMD_UIO_STREAM;
+       transfer[1] = CH341A_UIO_CMD_STM_DIR | (0x3f & dire);
+       transfer[2] = CH341A_UIO_CMD_STM_END;
+
+       ret = jtag_libusb_bulk_write(ch341a_adapter, CH341A_BULK_WRITE_ENDPOINT,
+                                                                (void 
*)transfer, 3, CH341A_BULK_TIMEOUT, &received);
+
+       if (ret < 0 || received < 0) {
+               LOG_ERROR("%s: usb bulk write failed", __func__);
+               exit(1);
+       }
+
+       return received;
+}
+
+static int ch341a_port_transfer(uint8_t *buff, unsigned int len, bool read)
+{
+       uint8_t transfer[CH341A_PACKET_LENGTH + 2];
+       unsigned int xfer;
+       int ret, received;
+
+       for (received = 0; (xfer = MIN(len, CH341A_PACKET_LENGTH / (read + 
1))); len -= xfer, buff += xfer) {
+               unsigned int xfer_send = xfer * (read + 1) + 2;
+               unsigned int count;
+               int xfer_recv;
+
+               transfer[0] = CH341A_CMD_UIO_STREAM;
+               transfer[xfer_send - 1] = CH341A_UIO_CMD_STM_END;
+
+               if (read) {
+                       for (count = 0; count < xfer; ++count) {
+                               transfer[(count + 1) * 2 - 1] = 
CH341A_UIO_CMD_STM_OUT | (0x3f & *(buff + count));
+                               transfer[(count + 1) * 2 + 0] = 
CH341A_UIO_CMD_STM_IN;
+                       }
+               } else {
+                       for (count = 0; count < xfer; ++count)
+                               transfer[count + 1] = CH341A_UIO_CMD_STM_OUT | 
(0x3f & *(buff + count));
+               }
+
+               ret = jtag_libusb_bulk_write(ch341a_adapter, 
CH341A_BULK_WRITE_ENDPOINT,
+                                                                        (void 
*)transfer, xfer_send, CH341A_BULK_TIMEOUT, &xfer_recv);
+
+               if (ret < 0 || xfer_recv < 0) {
+                       LOG_ERROR("%s: usb bulk write failed", __func__);
+                       exit(1);
+               }
+
+               if (read) {
+                       ret = jtag_libusb_bulk_read(ch341a_adapter, 
CH341A_BULK_READ_ENDPOINT,
+                                                                               
(void *)buff, xfer, CH341A_BULK_TIMEOUT, &xfer_recv);
+
+                       if (ret < 0 || xfer_recv < 0) {
+                               LOG_ERROR("%s: usb bulk read failed", __func__);
+                               exit(1);
+                       }
+                       received += xfer_recv;
+               }
+       }
+
+       return received;
+}
+
+static void ch341a_port_buffer_flush(bool read)
+{
+       ch341a_port_transfer(ch341a_port_buffer, ch341a_port_buffer_curr, read);
+       ch341a_port_buffer_curr = 0;
+}
+
+static void ch341a_port_buffer_increase(unsigned long new_size)
+{
+       uint8_t *new_ptr;
+
+       if (new_size < ch341a_port_buffer_size)
+               return;
+
+       new_size = ch341a_port_buffer_size * 2;
+       new_ptr = realloc(ch341a_port_buffer, new_size);
+       if (!new_ptr)
+               return;
+
+       ch341a_port_buffer = new_ptr;
+       ch341a_port_buffer_size = new_size;
+}
+
+static inline void ch341a_jtag_write(bool tck, bool tms, bool tdi)
+{
+       uint8_t value = 0xff;
+
+       value = tck ? value | (1 << ch341a_tck_gpio) : value & ~(1 << 
ch341a_tck_gpio);
+       value = tms ? value | (1 << ch341a_tms_gpio) : value & ~(1 << 
ch341a_tms_gpio);
+       value = tdi ? value | (1 << ch341a_tdi_gpio) : value & ~(1 << 
ch341a_tdi_gpio);
+
+       ch341a_port_buffer_increase(ch341a_port_buffer_curr);
+       if (ch341a_port_buffer_curr >= ch341a_port_buffer_size) {
+               LOG_ERROR("%s: buffer overflow", __func__);
+               exit(1);
+       }
+
+       ch341a_port_buffer[ch341a_port_buffer_curr++] = value;
+}
+
+static inline void ch341a_jtag_reset(bool trst, bool srst)
+{
+       uint8_t value = 0xff;
+
+       value = trst ? value & ~(1 << ch341a_trst_gpio) : value | (1 << 
ch341a_trst_gpio);
+       value = srst ? value & ~(1 << ch341a_srst_gpio) : value | (1 << 
ch341a_srst_gpio);
+
+       ch341a_port_buffer_increase(ch341a_port_buffer_curr);
+       if (ch341a_port_buffer_curr >= ch341a_port_buffer_size) {
+               LOG_ERROR("%s: buffer overflow", __func__);
+               exit(1);
+       }
+
+       ch341a_port_buffer[ch341a_port_buffer_curr++] = value;
+       ch341a_port_buffer_flush(false);
+}
+
+static void syncbb_end_state(tap_state_t state)
+{
+       if (tap_is_state_stable(state)) {
+               tap_set_end_state(state);
+       } else {
+               LOG_ERROR("BUG: %i is not a valid end state", state);
+               exit(-1);
+       }
+}
+
+static void syncbb_state_move(int skip)
+{
+       uint8_t tms_scan = tap_get_tms_path(tap_get_state(), 
tap_get_end_state());
+       int tms_count = tap_get_tms_path_len(tap_get_state(), 
tap_get_end_state());
+       bool tms = 0;
+       int count;
+
+       for (count = skip; count < tms_count; ++count) {
+               tms = (tms_scan >> count) & 0x01;
+               ch341a_jtag_write(0, tms, 0);
+               ch341a_jtag_write(1, tms, 0);
+       }
+
+       ch341a_jtag_write(0, tms, 0);
+       tap_set_state(tap_get_end_state());
+}
+
+static void syncbb_execute_tms(struct jtag_command *cmd)
+{
+       unsigned int num_bits = cmd->cmd.tms->num_bits;
+       const uint8_t *bits = cmd->cmd.tms->bits;
+       bool tms = 0;
+
+       LOG_DEBUG_IO("TMS: %d bits", num_bits);
+
+       for (unsigned int i = 0; i < num_bits; i++) {
+               tms = ((bits[i / 8] >> (i % 8)) & 1);
+               ch341a_jtag_write(0, tms, 0);
+               ch341a_jtag_write(1, tms, 0);
+       }
+
+       ch341a_jtag_write(0, tms, 0);
+}
+
+static void syncbb_path_move(struct pathmove_command *cmd)
+{
+       int num_states = cmd->num_states;
+       int state_count = 0;
+       bool tms = 0;
+
+       while (num_states--) {
+               if (tap_state_transition(tap_get_state(), false) == 
cmd->path[state_count]) {
+                       tms = 0;
+               } else if (tap_state_transition(tap_get_state(), true) == 
cmd->path[state_count]) {
+                       tms = 1;
+               } else {
+                       LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition",
+                               tap_state_name(tap_get_state()),
+                               tap_state_name(cmd->path[state_count]));
+                       exit(-1);
+               }
+
+               ch341a_jtag_write(0, tms, 0);
+               ch341a_jtag_write(1, tms, 0);
+               tap_set_state(cmd->path[state_count]);
+               state_count++;
+       }
+
+       ch341a_jtag_write(0, tms, 0);
+       tap_set_end_state(tap_get_state());
+}
+
+static void syncbb_runtest(unsigned int cycles)
+{
+       tap_state_t saved_end_state = tap_get_end_state();
+       unsigned int count;
+
+       if (tap_get_state() != TAP_IDLE) {
+               syncbb_end_state(TAP_IDLE);
+               syncbb_state_move(0);
+       }
+
+       for (count = 0; count < cycles; ++count) {
+               ch341a_jtag_write(0, 0, 0);
+               ch341a_jtag_write(1, 0, 0);
+       }
+
+       ch341a_jtag_write(0, 0, 0);
+       syncbb_end_state(saved_end_state);
+
+       if (tap_get_state() != tap_get_end_state())
+               syncbb_state_move(0);
+}
+
+static void syncbb_stableclocks(unsigned int cycles)
+{
+       bool tms = tap_get_state() == TAP_RESET;
+       unsigned int count;
+
+       for (count = 0; count < cycles; ++count) {
+               ch341a_jtag_write(1, tms, 0);
+               ch341a_jtag_write(0, tms, 0);
+       }
+}
+
+static void syncbb_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, 
int scan_size)
+{
+       tap_state_t saved_end_state = tap_get_end_state();
+       unsigned long bit_base;
+       int bit_cnt;
+
+       if (!((!ir_scan && (tap_get_state() == TAP_DRSHIFT)) || (ir_scan && 
(tap_get_state() == TAP_IRSHIFT)))) {
+               if (ir_scan)
+                       syncbb_end_state(TAP_IRSHIFT);
+               else
+                       syncbb_end_state(TAP_DRSHIFT);
+
+               syncbb_state_move(0);
+               syncbb_end_state(saved_end_state);
+       }
+
+       bit_base = ch341a_port_buffer_curr;
+
+       for (bit_cnt = 0; bit_cnt < scan_size; ++bit_cnt) {
+               int bcval, bytec;
+               bool tdi, tms;
+
+               bcval = 1 << (bit_cnt % 8);
+               bytec = bit_cnt / 8;
+               tms = bit_cnt == scan_size - 1;
+               tdi = (type != SCAN_IN) && (buffer[bytec] & bcval);
+
+               ch341a_jtag_write(0, tms, tdi);
+               ch341a_jtag_write(1, tms, tdi);
+       }
+
+       if (tap_get_state() != tap_get_end_state())
+               syncbb_state_move(1);
+
+       ch341a_port_buffer_flush(type != SCAN_OUT);
+
+       if (type != SCAN_OUT) {
+               for (bit_cnt = 0; bit_cnt < scan_size; ++bit_cnt) {
+                       int bcval, bytec;
+                       uint8_t value;
+
+                       bcval = 1 << (bit_cnt % 8);
+                       bytec = bit_cnt / 8;
+                       value = ch341a_port_buffer[bit_base + bit_cnt * 2 + 1];
+
+                       if (value & (1 << ch341a_tdo_gpio))
+                               buffer[bytec] |= bcval;
+                       else
+                               buffer[bytec] &= ~bcval;
+               }
+       }
+}
+
+static int ch341a_jtag_execute_queue(void)
+{
+       struct jtag_command *cmd = jtag_command_queue;
+       enum scan_type type;
+       int scan_size;
+       uint8_t *buffer;
+       int retval = ERROR_OK;
+
+       while (cmd) {
+               switch (cmd->type) {
+                       case JTAG_RESET:
+                               LOG_DEBUG_IO("reset trst: %i srst %i", 
cmd->cmd.reset->trst, cmd->cmd.reset->srst);
+                               if (cmd->cmd.reset->trst == 1 ||
+                                       (cmd->cmd.reset->srst &&
+                                       (jtag_get_reset_config() & 
RESET_SRST_PULLS_TRST))) {
+                                       tap_set_state(TAP_RESET);
+                               }
+                               ch341a_jtag_reset(cmd->cmd.reset->trst, 
cmd->cmd.reset->srst);
+                               break;
+
+                       case JTAG_RUNTEST:
+                               LOG_DEBUG_IO("runtest %i cycles, end in %s", 
cmd->cmd.runtest->num_cycles,
+                                       
tap_state_name(cmd->cmd.runtest->end_state));
+                               syncbb_end_state(cmd->cmd.runtest->end_state);
+                               syncbb_runtest(cmd->cmd.runtest->num_cycles);
+                               break;
+
+                       case JTAG_STABLECLOCKS:
+                               
syncbb_stableclocks(cmd->cmd.stableclocks->num_cycles);
+                               break;
+
+                       case JTAG_TLR_RESET:
+                               LOG_DEBUG_IO("statemove end in %s", 
tap_state_name(cmd->cmd.statemove->end_state));
+                               syncbb_end_state(cmd->cmd.statemove->end_state);
+                               syncbb_state_move(0);
+                               break;
+
+                       case JTAG_PATHMOVE:
+                               LOG_DEBUG_IO("pathmove: %i states, end in %s", 
cmd->cmd.pathmove->num_states,
+                                       
tap_state_name(cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]));
+                               syncbb_path_move(cmd->cmd.pathmove);
+                               break;
+
+                       case JTAG_SCAN:
+                               LOG_DEBUG_IO("%s scan end in %s",  
(cmd->cmd.scan->ir_scan) ? "IR" : "DR",
+                                       
tap_state_name(cmd->cmd.scan->end_state));
+                               syncbb_end_state(cmd->cmd.scan->end_state);
+                               scan_size = jtag_build_buffer(cmd->cmd.scan, 
&buffer);
+                               type = jtag_scan_type(cmd->cmd.scan);
+                               syncbb_scan(cmd->cmd.scan->ir_scan, type, 
buffer, scan_size);
+                               if (jtag_read_buffer(buffer, cmd->cmd.scan) != 
ERROR_OK)
+                                       retval = ERROR_JTAG_QUEUE_FAILED;
+                               free(buffer);
+                               break;
+
+                       case JTAG_SLEEP:
+                               LOG_DEBUG_IO("sleep %" PRIu32, 
cmd->cmd.sleep->us);
+                               jtag_sleep(cmd->cmd.sleep->us);
+                               break;
+
+                       case JTAG_TMS:
+                               syncbb_execute_tms(cmd);
+                               break;
+
+                       default:
+                               LOG_ERROR("BUG: unknown JTAG command type 
encountered");
+                               exit(-1);
+               }
+
+               if (ch341a_port_buffer_curr)
+                       ch341a_port_buffer_flush(false);
+
+               cmd = cmd->next;
+       }
+
+       return retval;
+}
+
+static int ch341a_init(void)
+{
+       uint16_t avids[] = {ch341a_vid, 0};
+       uint16_t apids[] = {ch341a_pid, 0};
+       uint8_t desc[0x12];
+
+       if (jtag_libusb_open(avids, apids, &ch341a_adapter, NULL)) {
+               const char *ch341a_serial_desc = adapter_get_required_serial();
+               LOG_ERROR("ch341a not found: vid=%04x, pid=%04x, serial=%s\n",
+                       ch341a_vid, ch341a_pid, (!ch341a_serial_desc) ? "[any]" 
: ch341a_serial_desc);
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       if (libusb_kernel_driver_active(ch341a_adapter, 0)) {
+               if (libusb_detach_kernel_driver(ch341a_adapter, 0)) {
+                       LOG_ERROR("Failed to detach kernel driver");
+                       return ERROR_JTAG_INIT_FAILED;
+               }
+       }
+
+       if (libusb_claim_interface(ch341a_adapter, 0)) {
+               LOG_ERROR("Failed to claim interface 0");
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       if (libusb_get_descriptor(ch341a_adapter, LIBUSB_DT_DEVICE, 0x00, desc, 
0x12) < 0) {
+               LOG_ERROR("Failed to get device descriptor");
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       if (libusb_control_transfer(ch341a_adapter, CH341A_VENDOR_READ, 
CH341A_VENDOR_VERSION,
+                                                               0, 0, (void 
*)desc, 2, CH341A_BULK_TIMEOUT) < 0) {
+               LOG_ERROR("Failed to get device version");
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       LOG_INFO("ch341a chip version: 0x%02x%02x", desc[1], desc[0]);
+
+       ch341a_port_buffer = malloc(ch341a_port_buffer_size);
+       if (!ch341a_port_buffer) {
+               LOG_ERROR("Unable to allocate memory for the buffer");
+               return ERROR_JTAG_INIT_FAILED;
+       }
+
+       ch341a_port_direction((1 << ch341a_tdi_gpio) | (1 << ch341a_tck_gpio) |
+                                                 (1 << ch341a_tms_gpio) | (1 
<< ch341a_trst_gpio) |
+                                                 (1 << ch341a_srst_gpio));
+
+       return ERROR_OK;
+}
+
+static int ch341a_quit(void)
+{
+       ch341a_port_direction(0);
+       jtag_libusb_close(ch341a_adapter);
+
+       free(ch341a_port_buffer);
+       ch341a_port_buffer = NULL;
+       ch341a_port_buffer_curr = 0;
+       ch341a_port_buffer_size = 4096;
+
+       return ERROR_OK;
+}
+
+static const struct command_registration ch341a_subcommand_handlers[] = {
+       {
+               .name = "vid_pid",
+               .handler = ch341a_handle_vid_pid_command,
+               .mode = COMMAND_CONFIG,
+               .help = "USB VID and PID of the adapter",
+               .usage = "vid pid",
+       }, {
+               .name = "jtag_nums",
+               .handler = ch341a_handle_jtag_nums_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio numbers for tck, tms, tdo, tdi, trst, srst.",
+               .usage = "<D0|D1|D2|D3|D4|D5|D6|D7>",
+       }, {
+               .name = "tck_num",
+               .handler = ch341a_handle_tck_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for tck.",
+               .usage = "<D0|D1|D2|D3|D4|D5|D6|D7>",
+       }, {
+               .name = "tms_num",
+               .handler = ch341a_handle_tms_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for tms.",
+               .usage = "<D0|D1|D2|D3|D4|D5|D6|D7>",
+       }, {
+               .name = "tdo_num",
+               .handler = ch341a_handle_tdo_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for tdo.",
+               .usage = "<D0|D1|D2|D3|D4|D5|D6|D7>",
+       }, {
+               .name = "tdi_num",
+               .handler = ch341a_handle_tdi_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for tdi.",
+               .usage = "<D0|D1|D2|D3|D4|D5|D6|D7>",
+       }, {
+               .name = "trst_num",
+               .handler = ch341a_handle_trst_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for trst.",
+               .usage = "<D0|D1|D2|D3|D4|D5|D6|D7>",
+       }, {
+               .name = "srst_num",
+               .handler = ch341a_handle_srst_num_command,
+               .mode = COMMAND_CONFIG,
+               .help = "gpio number for srst.",
+               .usage = "<D0|D1|D2|D3|D4|D5|D6|D7>",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration ch341a_command_handlers[] = {
+       {
+               .name = "ch341a",
+               .mode = COMMAND_ANY,
+               .help = "perform ch341a management",
+               .chain = ch341a_subcommand_handlers,
+               .usage = "",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static struct jtag_interface ch341a_jtag_ops = {
+       .supported = DEBUG_CAP_TMS_SEQ,
+       .execute_queue = ch341a_jtag_execute_queue,
+};
+
+struct adapter_driver ch341a_adapter_driver = {
+       .name = "ch341a",
+       .transports = jtag_only,
+       .commands = ch341a_command_handlers,
+
+       .init = ch341a_init,
+       .quit = ch341a_quit,
+
+       .jtag_ops = &ch341a_jtag_ops,
+};
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index 67bbb3b366..a3c3920863 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -145,6 +145,9 @@ extern struct adapter_driver rshim_dap_adapter_driver;
 #if BUILD_AM335XGPIO == 1
 extern struct adapter_driver am335xgpio_adapter_driver;
 #endif
+#if BUILD_CH341A == 1
+extern struct adapter_driver ch341a_adapter_driver;
+#endif
 
 /**
  * The list of built-in JTAG interfaces, containing entries for those
@@ -264,6 +267,9 @@ struct adapter_driver *adapter_drivers[] = {
 #endif
 #if BUILD_AM335XGPIO == 1
                &am335xgpio_adapter_driver,
+#endif
+#if BUILD_CH341A == 1
+               &ch341a_adapter_driver,
 #endif
                NULL,
        };
diff --git a/tcl/interface/ch341a.cfg b/tcl/interface/ch341a.cfg
new file mode 100644
index 0000000000..8a804374e9
--- /dev/null
+++ b/tcl/interface/ch341a.cfg
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright(c) 2021 John Sanpe <sanp...@gmail.com>
+#
+
+adapter driver ch341a
+ch341a vid_pid 0x1a86 0x5512
+ch341a tck_num D3
+ch341a tms_num D0
+ch341a tdo_num D7
+ch341a tdi_num D5
+ch341a trst_num D1
+ch341a srst_num D2

-- 

Reply via email to