This is an automated email from Gerrit. "ZhiYuanNJ <871238...@qq.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8025
-- gerrit commit 7fd37cdbec078e60bde5a5ad11484f5b483f6304 Author: Marc Schink <d...@zapb.de> Date: Sun Nov 12 11:43:48 2023 +0100 jtag/drivers: Add support for high-speed USB adapter chip CH347 A WCH CH347T/F chip : 480Mbps high-speed USB to UART, I2C, SPI and JTAG, supports drivers on Windows/ Linux/ Android /macOS, used to high-speed USB to UART, USB to SPI, USB to IIC, USB to JTAG. The ch347 driver source code is adopted from https://github.com/WCHSoftGroup/ch347 The WCH official website : https://www.wch-ic.com/ Change-Id: Ibbe3e09fc863d775bac04aee69b0d91ad29894cd Signed-off-by: ZhiYuanNJ <871238...@qq.com> diff --git a/configure.ac b/configure.ac index a2442d40b5..9743dfdc22 100644 --- a/configure.ac +++ b/configure.ac @@ -129,7 +129,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]], + [[ch347], [Mode 3 of the CH347 devices], [CH347]]]) m4_define([HIDAPI_ADAPTERS], [[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP_HID]], diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index e404afe9f0..9b00d547db 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -204,6 +204,9 @@ endif if AM335XGPIO DRIVERFILES += %D%/am335xgpio.c endif +if CH347 +DRIVERFILES += %D%/ch347.c +endif DRIVERHEADERS = \ %D%/bitbang.h \ diff --git a/src/jtag/drivers/ch347.c b/src/jtag/drivers/ch347.c new file mode 100644 index 0000000000..a2df45f318 --- /dev/null +++ b/src/jtag/drivers/ch347.c @@ -0,0 +1,1864 @@ +/*************************************************************************** + * Driver for CH347-JTAG interface V1.1 * + * * + * Copyright (C) 2022 by oidcat. * + * Author: oidcat...@163.com * + * * + * CH347 is a high-speed USB bus converter chip that provides UART, I2C * + * and SPI synchronous serial ports and JTAG interface through USB bus. * + * * + * The Jtag interface by CH347 can supports transmission frequency * + * configuration up to 60MHz. * + * * + * The USB2.0 to JTAG scheme based on CH347 can be used to build * + * customized USB high-speed JTAG debugger and other products. * + * * + * 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. * + * * + * _____________ * + * | |____JTAG/SWD (TDO,TDI,TMS,TCK,TRST) * + * USB__| CH347T/F | * + * |_____________|____UART(TXD1,RXD1,RTS1,CTS1,DTR1) * + * ______|______ * + * | | * + * | 8 MHz XTAL | * + * |_____________| * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if IS_CYGWIN == 1 +#include "windows.h" +#undef LOG_ERROR +#endif + +/* project specific includes */ +#include <jtag/interface.h> +#include <jtag/commands.h> +#include <jtag/swd.h> +#include <helper/time_support.h> +#include <helper/replacements.h> +#include <helper/list.h> +#include <helper/binarybuffer.h> +#include "libusb_helper.h" + +/* system includes */ +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> + +#define CH347_VID (0x1a86) +#define CH347_PID (0x55dd) +#define JTAGIO_STA_OUT_TDI (0x10) +#define JTAGIO_STA_OUT_TMS (0x02) +#define JTAGIO_STA_OUT_TCK (0x01) +#define JTAGIO_STA_OUT_TRST (0x20) +#define TDI_H JTAGIO_STA_OUT_TDI +#define TDI_L 0 +#define TMS_H JTAGIO_STA_OUT_TMS +#define TMS_L 0 +#define TCK_H JTAGIO_STA_OUT_TCK +#define TCK_L 0 +#define TRST_H JTAGIO_STA_OUT_TRST +#define TRST_L 0 + +#define KHZ(n) ((n)*UINT64_C(1000)) +#define MHZ(n) ((n)*UINT64_C(1000000)) +#define GHZ(n) ((n)*UINT64_C(1000000000)) + +#define HW_TDO_BUF_SIZE 4096 +#define SF_PACKET_BUF_SIZE 51200 /* Command packet length */ +#define UCMDPKT_DATA_MAX_BYTES_USBHS 507 /* The data length contained in each + command packet during USB + high-speed operation */ +#define USBC_PACKET_USBHS 512 /* Maximum data length per packet at + USB high speed */ +#define USBC_PACKET_USBHS_SINGLE 510 /* usb high speed max + package length */ +#define CH347_CMD_HEADER 3 /* Protocol header length */ + +#define CH347_CMD_HEADER 3 /* Э���ͷ���� */ +/* Protocol transmission format: CMD (1 byte)+Length (2 bytes)+Data */ +#define CH347_CMD_INFO_RD 0xCA /* Parameter acquisition, used to + obtain firmware version, + JTAG interface related parameters, + etc */ +#define CH347_CMD_JTAG_INIT 0xD0 /* JTAG Interface Initialization + Command */ +#define CH347_CMD_JTAG_BIT_OP 0xD1 /* JTAG interface pin bit control + command */ +#define CH347_CMD_JTAG_BIT_OP_RD 0xD2 /* JTAG interface pin bit control and + read commands */ +#define CH347_CMD_JTAG_DATA_SHIFT 0xD3 /* JTAG interface data shift + command */ +#define CH347_CMD_JTAG_DATA_SHIFT_RD 0xD4 /* JTAG interface data shift and read + command */ +/* SWD */ +#define CH347_CMD_SWD_INIT 0xE5 /* SWD Interface Initialization Command */ +#define CH347_CMD_SWD 0xE8 +#define CH347_CMD_SWD_REG_W 0xA0 /* SWD Interface write reg */ +#define CH347_CMD_SWD_SEQ_W 0xA1 /* SWD Interface write spec seq */ +#define CH347_CMD_SWD_REG_R 0xA2 /* SWD Interface read reg */ +#define CH347_MAX_SEND_CMD 0X20 /* max send cmd number */ +#define CH347_MAX_SEND_BUF 0X200 +#define CH347_MAX_RECV_BUF 0X200 +#define BUILD_UINT16(loByte, hiByte) \ + ((uint16_t)(((loByte)&0x00FF) + (((hiByte)&0x00FF) << 8))) +#pragma pack(1) + +typedef enum pack { + STANDARD_PACK = 0, + LARGER_PACK = 1, +} PACK_SIZE; + +typedef struct _CH347_info /* Record the CH347 pin status */ +{ + int TMS; + int TDI; + int TCK; + int TRST; + + int buffer_idx; + uint8_t *buffer; + + int len_idx; + int len_value; + uint8_t lastCmd; + + uint8_t *read_buffer; + uint32_t read_idx; + uint32_t read_count; + struct bit_copy_queue read_queue; + PACK_SIZE pack_size; +} _CH347_Info; + +int DevIsOpened; /* Whether the device is turned on */ +bool UsbHighDev = true; +unsigned long USBC_PACKET; + +typedef struct _CH347_SWD_IO { + uint8_t usbcmd; /* 0xA0��0xA1��0xA2 */ + uint8_t cmd; + uint32_t *dst; + uint32_t value; + struct list_head list_entry; +} CH347_SWD_IO, *PCH347_SWD_IO; + +typedef struct _CH347_SWD_CONTEXT { + uint8_t send_buf[CH347_MAX_SEND_BUF]; + uint8_t recv_buf[CH347_MAX_RECV_BUF]; + uint32_t send_len; + uint32_t recv_len; + uint32_t need_recv_len; + int queued_retval; + uint8_t sent_cmd_count; + struct list_head send_cmd_head; + struct list_head free_cmd_head; + uint8_t *ch347_cmd_buf; +} CH347_SWD_CONTEXT; +static CH347_SWD_CONTEXT ch347_swd_context; +static bool swd_mode; +#pragma pack() + +#ifdef _WIN32 +#include <windows.h> + +typedef int(__stdcall * pCH347OpenDevice)(unsigned long iIndex); + +typedef int(__stdcall * pCH347CloseDevice)(unsigned long iIndex); + +typedef unsigned long(__stdcall * pCH347SetTimeout)( + unsigned long iIndex, /* Specify equipment serial number */ + unsigned long iWriteTimeout, /* Specifies the timeout period for USB + write out data blocks, in milliseconds + mS, and 0xFFFFFFFF specifies no timeout + (default) */ + unsigned long iReadTimeout); /* Specifies the timeout period for USB + reading data blocks, in milliseconds mS, + and 0xFFFFFFFF specifies no timeout + (default) */ + +typedef unsigned long(__stdcall * pCH347WriteData)( + unsigned long iIndex, /* Specify equipment serial number */ + void *oBuffer, /* Point to a buffer large enough to hold + the descriptor */ + unsigned long *ioLength); /* Pointing to the length unit, the input + is the length to be read, and the + return is the actual read length */ + +typedef unsigned long(__stdcall * pCH347ReadData)( + unsigned long iIndex, /* Specify equipment serial number */ + void *oBuffer, /* Point to a buffer large enough to + hold the descriptor */ + unsigned long *ioLength); /* Pointing to the length unit, the input + is the length to be read, and the + return is the actual read length */ + +typedef unsigned long(__stdcall * pCH347Jtag_INIT)( + unsigned long iIndex, /* Specify equipment serial number */ + unsigned char iClockRate); /* Pointing to the length unit, the input + is the length to be read, and the + return is the actual read length */ +HMODULE uhModule; +BOOL ugOpen; +unsigned long ugIndex; +pCH347OpenDevice CH347OpenDevice; +pCH347CloseDevice CH347CloseDevice; +pCH347SetTimeout CH347SetTimeout; +pCH347ReadData CH347ReadData; +pCH347WriteData CH347WriteData; +/* pCH347Jtag_INIT CH347Jtag_INIT; */ +#elif defined(__linux__) + +#include <jtag/drivers/libusb_helper.h> + +#define CH347_EPOUT 0x06u +#define CH347_EPIN 0x86u + +bool ugOpen; +unsigned long ugIndex; +struct libusb_device_handle *ch347_handle; + +static const uint16_t ch347_vids[] = {CH347_VID, 0}; +static const uint16_t ch347_pids[] = {CH347_PID, 0}; + +static uint32_t CH347OpenDevice(uint64_t iIndex) +{ + if (jtag_libusb_open(ch347_vids, ch347_pids, + &ch347_handle, NULL) != ERROR_OK) { + return false; + } else { + return true; + } +} + +static bool CH347WriteData(uint64_t iIndex, uint8_t *data, uint64_t *length) +{ + int ret, tmp = 0; + ret = jtag_libusb_bulk_write(ch347_handle, + CH347_EPOUT, + (char *)data, + *length, + 100, &tmp); + *length = tmp; + + if (!ret) + return true; + else + return false; +} + +static bool CH347ReadData(uint64_t iIndex, uint8_t *data, uint64_t *length) +{ + int ret, tmp = 0; + + int size = *length; + ret = jtag_libusb_bulk_read(ch347_handle, + CH347_EPIN, + (char *)data, + size, + 100, &tmp); + + *length = tmp; + if (!ret) + return true; + else + return false; +} + +static bool CH347CloseDevice(uint64_t iIndex) +{ + jtag_libusb_close(ch347_handle); + return true; +} + +#endif + +_CH347_Info ch347; + +static int ch347_swd_run_queue(void); +/* swd init func */ +static bool CH347SWD_INIT(uint64_t iIndex, uint8_t iClockRate) +{ + uint8_t cmdBuf[128] = ""; + unsigned long i = 0; + cmdBuf[i++] = CH347_CMD_SWD_INIT; + cmdBuf[i++] = 8; /* Data length is 6 */ + cmdBuf[i++] = 0; + cmdBuf[i++] = 0x40; + cmdBuf[i++] = 0x42; + + cmdBuf[i++] = 0x0f; /* Reserved Bytes */ + cmdBuf[i++] = 0x00; /* Reserved Bytes */ + cmdBuf[i++] = iClockRate; /* JTAG clock speed */ + i += 3; /* Reserved Bytes */ + + unsigned long mLength = i; + if (!CH347WriteData(iIndex, cmdBuf, &mLength) || (mLength != i)) + return false; + + mLength = 4; + memset(cmdBuf, 0, sizeof(cmdBuf)); + + if (!CH347ReadData(iIndex, cmdBuf, &mLength) || (mLength != 4)) + return false; + + return true; +} + +/** + * HexToString - Hex Conversion String Function + * @param buf Point to a buffer to place the Hex data to be converted + * @param size Pointing to the length unit where data needs to be converted + * + * @return Returns a converted string + */ +static char *HexToString(uint8_t *buf, uint32_t size) +{ + uint32_t i; + if (buf == NULL) + return "NULL"; + + char *str = (char *)calloc(size * 2 + 1, 1); + + for (i = 0; i < size; i++) + sprintf(str + 2 * i, "%02x ", buf[i]); + return str; +} + +/** + * CH347_Write - CH347 Write + * @param oBuffer Point to a buffer to place the data to be written out + * @param ioLength Pointing to the length unit, the input is the length to be + * written out, and the return is the actual written length + * + * @return Write success returns 1, failure returns 0 + */ +static int CH347_Write(void *oBuffer, unsigned long *ioLength) +{ + char *log_buf = NULL; + int ret = -1; + unsigned long wlength = *ioLength, WI; + if (*ioLength >= HW_TDO_BUF_SIZE) + wlength = HW_TDO_BUF_SIZE; + WI = 0; + while (1) { + ret = CH347WriteData(ugIndex, (uint8_t *)oBuffer + WI, + &wlength); + if (!ret) { + LOG_ERROR("CH347 write fail"); + *ioLength = 0; + return false; + } + log_buf = HexToString((uint8_t *)oBuffer, wlength); + LOG_DEBUG_IO("(size=%lu, buf=[%s]) -> %" PRIu32, wlength, + log_buf, + (uint32_t)wlength); + WI += wlength; + if (WI >= *ioLength) + break; + if ((*ioLength - WI) > HW_TDO_BUF_SIZE) + wlength = HW_TDO_BUF_SIZE; + else + wlength = *ioLength - WI; + free(log_buf); + } + + *ioLength = WI; + return true; +} + +/** + * CH347_Read - CH347 Read + * @param oBuffer Point to a buffer to place the data to be read in + * @param ioLength Pointing to the length unit, the input is the length to + * be read, and the return is the actual read length + * + * @return Write success returns 1, failure returns 0 + */ +static int CH347_Read(void *oBuffer, unsigned long *ioLength) +{ + char *log_buf = NULL; + unsigned long rlength = *ioLength, WI = 0; + /* The maximum allowable reading for a single read is 4096B of data. + If it exceeds the allowable reading limit, it will be calculated as + 4096B */ + if (rlength > HW_TDO_BUF_SIZE) + rlength = HW_TDO_BUF_SIZE; + while (1) { + if (!CH347ReadData(ugIndex, (uint8_t *)oBuffer + WI, + &rlength)) { + LOG_ERROR("CH347 read fail"); + return false; + } + + WI += rlength; + if (WI >= *ioLength) + break; + if ((*ioLength - WI) > HW_TDO_BUF_SIZE) + rlength = HW_TDO_BUF_SIZE; + else + rlength = *ioLength - WI; + } + log_buf = HexToString((uint8_t *)oBuffer, WI); + LOG_DEBUG_IO("(size=%lu, buf=[%s]) -> %" PRIu32, WI, + log_buf, (uint32_t)WI); + free(log_buf); + *ioLength = WI; + return true; +} + +static void CH347_Read_Scan(unsigned char *pBuffer, unsigned int length) +{ + unsigned long read_size = 0; + unsigned long rx_len = 0; + unsigned long index = 0; + unsigned long read_buf_index = 0; + unsigned char *read_buf = NULL; + int dataLen = 0, i = 0; /*, this_bits = 0; */ + + read_size = length; + rx_len = read_size; + read_buf = (unsigned char *)calloc(read_size, sizeof(unsigned char)); + if (!read_buf){ + LOG_ERROR("read_buf calloc failed."); + return; + } + if (!CH347_Read(read_buf, &rx_len)) { + LOG_ERROR("CH347_Read read data failure."); + return; + } + while (index < read_size) { /* deal with the CH347_CMD_JTAG_BIT_OP_RD or CH347_CMD_JTAG_DATA_SHIFT_RD */ + if (read_buf[index] == CH347_CMD_JTAG_DATA_SHIFT_RD) { + dataLen = read_buf[++index] & 0xFF; + dataLen += (read_buf[++index] & 0xFF) << 8; + memcpy(pBuffer + read_buf_index, &read_buf[index + 1], + dataLen); + read_buf_index += dataLen; + index += dataLen + 1; + } else if (read_buf[index] == CH347_CMD_JTAG_BIT_OP_RD) { + dataLen = read_buf[++index] & 0xFF; + dataLen += (read_buf[++index] & 0xFF) << 8; + + for (i = 0; i < dataLen; i++) { + if (read_buf[index + 1 + i] & 1) + *(pBuffer + read_buf_index) |= (1 << i); + else + *(pBuffer + read_buf_index) &= ~(1 << i); + } + read_buf_index++; + index += dataLen + 1; + } else { + LOG_ERROR("readbuf read_commend error"); + *(pBuffer + read_buf_index) = read_buf[index]; + read_buf_index++; + index++; + } + } + if (read_buf) { + free(read_buf); + read_buf = NULL; + } +} + +static void CH347_Flush_Buffer(void) +{ + unsigned long retlen = ch347.buffer_idx; + int nb = ch347.buffer_idx, ret = ERROR_OK; + + while (ret == ERROR_OK && nb > 0) { + ret = CH347_Write(ch347.buffer, &retlen); + nb -= retlen; + } + memset(ch347.buffer, 0, SF_PACKET_BUF_SIZE); + ch347.buffer_idx = 0; + ch347.lastCmd = 0; + ch347.len_idx = 0; + ch347.len_value = 0; + + if (ch347.read_count == 0) + return; + if (ch347.pack_size == LARGER_PACK) { + CH347_Read_Scan(&ch347.read_buffer[0], ch347.read_count); + bit_copy_execute(&ch347.read_queue); + memset(ch347.read_buffer, 0, SF_PACKET_BUF_SIZE); + ch347.read_count = 0; + ch347.read_idx = 0; + } +} + +static void CH347_In_Buffer(uint8_t byte) +{ + if ((SF_PACKET_BUF_SIZE - ch347.buffer_idx) < 1) + CH347_Flush_Buffer(); + ch347.buffer[ch347.buffer_idx] = byte; + ch347.buffer_idx++; + if ((SF_PACKET_BUF_SIZE - ch347.buffer_idx) == 0) + CH347_Flush_Buffer(); +} + +static void CH347_In_Buffer_bytes(uint8_t *bytes, unsigned long bytes_length) +{ + if ((ch347.buffer_idx + bytes_length) > SF_PACKET_BUF_SIZE) + CH347_Flush_Buffer(); + memcpy(&ch347.buffer[ch347.buffer_idx], bytes, bytes_length); + ch347.buffer_idx += bytes_length; + if ((SF_PACKET_BUF_SIZE - ch347.buffer_idx) < 1) + CH347_Flush_Buffer(); +} + +static void combinePackets(uint8_t cmd, int cur_idx, unsigned long int len) +{ + if (cmd != ch347.lastCmd) { + ch347.buffer[cur_idx] = cmd; + ch347.buffer[cur_idx + 1] = + (uint8_t)(((len - CH347_CMD_HEADER) >> 0) & 0xFF); + ch347.buffer[cur_idx + 2] = + (uint8_t)(((len - CH347_CMD_HEADER) >> 8) & 0xFF); + + /* update the ch347 struct */ + ch347.lastCmd = cmd; + ch347.len_idx = cur_idx + 1; + ch347.len_value = (len - CH347_CMD_HEADER); + } else { + /* update the ch347 struct cmd data leng */ + ch347.len_value += (len - CH347_CMD_HEADER); + + /* update the cmd packet valid leng */ + ch347.buffer[ch347.len_idx] = (uint8_t)((ch347.len_value >> 0) \ + & 0xFF); + ch347.buffer[ch347.len_idx + 1] = (uint8_t)( + (ch347.len_value >> 8) & 0xFF); + + /* update the buffer data leng */ + memcpy(&ch347.buffer[cur_idx], + &ch347.buffer[cur_idx + CH347_CMD_HEADER], + (len - CH347_CMD_HEADER)); + + /* update the ch347 buffer index */ + ch347.buffer_idx -= CH347_CMD_HEADER; + } +} +/** + * CH347_ClockTms - Function function used to change the TMS value at the + * rising edge of TCK to switch its Tap state + * @param BitBangPkt Protocol package + * @param tms TMS value to be changed + * @param BI Protocol packet length + * + * @return Return protocol packet length + */ +static unsigned long CH347_ClockTms(int tms, unsigned long BI) +{ + uint8_t data = 0; + unsigned char cmd = 0; + + if (tms == 1) + cmd = TMS_H; + else + cmd = TMS_L; + + BI += 2; + + data = cmd | TDI_L | TCK_L | TRST_H; + CH347_In_Buffer(data); + data = cmd | TDI_L | TCK_H | TRST_H; + CH347_In_Buffer(data); + ch347.TMS = cmd; + ch347.TDI = TDI_L; + ch347.TCK = TCK_H; + ch347.TRST = TRST_H; + + return BI; +} + +/** + * CH347_IdleClock - Function function to ensure that the clock is in a low state + * @param BitBangPkt Protocol package + * @param BI Protocol packet length + * + * @return Return protocol packet length + */ +static unsigned long CH347_IdleClock(unsigned long BI) +{ + unsigned char byte = 0; + byte |= ch347.TMS ? TMS_H : TMS_L; + byte |= ch347.TDI ? TDI_H : TDI_L; + byte |= ch347.TRST ? TRST_H : TRST_L; + BI++; + CH347_In_Buffer(byte); + + return BI; +} + +/** + * CH347_TmsChange - Function function that performs state switching by changing the value of TMS + * @param tmsValue The TMS values that need to be switched form one byte of data in the switching order + * @param step The number of bit values that need to be read from the tmsValue value + * @param skip Count from the skip bit of tmsValue to step + * + */ +static void CH347_TmsChange(const unsigned char *tmsValue, int step, int skip) +{ + int i; + int index = ch347.buffer_idx; + unsigned long BI, retlen, cmdLen; + + BI = CH347_CMD_HEADER; + retlen = CH347_CMD_HEADER; + LOG_DEBUG_IO("(TMS Value: %02x..., step = %d, skip = %d)", tmsValue[0], + step, skip); + + for (i = 0; i < 3; i++) + CH347_In_Buffer(0); + + for (i = skip; i < step; i++) { + retlen = CH347_ClockTms((tmsValue[i / 8] >> (i % 8)) & 0x01, BI); + BI = retlen; + } + cmdLen = CH347_IdleClock(BI); + + combinePackets(CH347_CMD_JTAG_BIT_OP, index, cmdLen); +} + +/** + * CH347_TMS - By ch347_ execute_ Queue call + * @param cmd Upper layer transfer command parameters + * + */ +static void CH347_TMS(struct tms_command *cmd) +{ + LOG_DEBUG_IO("(step: %d)", cmd->num_bits); + CH347_TmsChange(cmd->bits, cmd->num_bits, 0); +} + +/** + * CH347_Reset - CH347 Reset Tap Status Function + * @brief If there are more than six consecutive TCKs and TMS is high, the state + * machine can be set to a Test-Logic-Reset state + * + */ +static int ch347_reset(int trst, int srst) +{ + LOG_DEBUG_IO("reset trst: %i srst %i", trst, srst); +#if 1 + unsigned char BitBang[512] = "", BII, i; + unsigned long TxLen; + + BII = CH347_CMD_HEADER; + for (i = 0; i < 7; i++) { + BitBang[BII++] = TMS_H | TDI_L | TCK_L; + BitBang[BII++] = TMS_H | TDI_L | TCK_H; + } + BitBang[BII++] = TMS_H | TDI_L | TCK_L; + + ch347.TCK = TCK_L; + ch347.TDI = TDI_L; + ch347.TMS = 0; + + BitBang[0] = CH347_CMD_JTAG_BIT_OP; + BitBang[1] = BII - CH347_CMD_HEADER; + BitBang[2] = 0; + + TxLen = BII; + + if (!CH347_Write(BitBang, &TxLen) && (TxLen != BII)) { + LOG_ERROR("JTAG_Init send usb data failure."); + return false; + } +#else + if (!swd_mode && trst == 0) { + + unsigned long int BI = 0; + + CH347_In_Buffer(CH347_CMD_JTAG_BIT_OP); + CH347_In_Buffer(0x01); + CH347_In_Buffer(0); + + ch347.TRST = 0; + CH347_IdleClock(BI); + + CH347_Flush_Buffer(); + + Sleep(50); + + CH347_In_Buffer(CH347_CMD_JTAG_BIT_OP); + CH347_In_Buffer(0x01); + CH347_In_Buffer(0); + + ch347.TRST = 1; + CH347_IdleClock(BI); + + CH347_Flush_Buffer(); + return ERROR_OK; + } +#endif + return ERROR_OK; +} + +/** + * CH347_MovePath - Obtain the current Tap status and switch to the status TMS + * value passed down by cmd + * @param cmd Upper layer transfer command parameters + * + */ +static void CH347_MovePath(struct pathmove_command *cmd) +{ + int i; + int index = ch347.buffer_idx; + unsigned long BI, retlen = 0, cmdLen; + + BI = CH347_CMD_HEADER; + + for (i = 0; i < 3; i++) + CH347_In_Buffer(0); + LOG_DEBUG_IO("(num_states=%d, last_state=%d)", + cmd->num_states, cmd->path[cmd->num_states - 1]); + + for (i = 0; i < cmd->num_states; i++) { + if (tap_state_transition(tap_get_state(), false) == + cmd->path[i]) + retlen = CH347_ClockTms(0, BI); + BI = retlen; + if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) + retlen = CH347_ClockTms(1, BI); + BI = retlen; + tap_set_state(cmd->path[i]); + } + + cmdLen = CH347_IdleClock(BI); + + combinePackets(CH347_CMD_JTAG_BIT_OP, index, cmdLen); +} + +/** + * CH347_MoveState - Toggle the Tap state to the Target state stat + * @param stat Pre switch target path + * @param skip Number of digits to skip + * + */ +static void CH347_MoveState(tap_state_t state, int skip) +{ + uint8_t tms_scan; + int tms_len; + + LOG_DEBUG_IO("(from %s to %s)", tap_state_name(tap_get_state()), + tap_state_name(state)); + if (tap_get_state() == state) + return; + tms_scan = tap_get_tms_path(tap_get_state(), state); + tms_len = tap_get_tms_path_len(tap_get_state(), state); + CH347_TmsChange(&tms_scan, tms_len, skip); + tap_set_state(state); +} + +/** + * CH347_WriteRead - CH347 Batch read/write function + * @param bits Read and write data this time + * @param nb_bits Incoming data length + * @param scan The transmission method of incoming data to determine whether + * to perform data reading + */ +static void CH347_WriteRead(struct scan_command *cmd, uint8_t *bits, + int nb_bits, enum scan_type scan) +{ + int nb8 = nb_bits / 8; + int nb1 = nb_bits % 8; + int i, num_bits = 0; + bool IsRead = false; + uint8_t TMS_Bit = 0, TDI_Bit = 0, CMD_Bit = 0; + uint8_t *byte0 = (uint8_t *)calloc(SF_PACKET_BUF_SIZE, 1); + uint8_t *readData = (uint8_t *)calloc(SF_PACKET_BUF_SIZE, 1); + unsigned long readLen = 0; + unsigned long BI = 0, DI, DII, PktDataLen, DLen = 0, tempIndex, + totalReadLength = 0, tempLength = 0; + if (!byte0 || !readData){ + LOG_DEBUG("CH347_WriteRead calloc failed"); + goto END; + } + if (ch347.pack_size == LARGER_PACK) { + if ((ch347.read_count >= (USBC_PACKET_USBHS_SINGLE * 1))) + CH347_Flush_Buffer(); + } else { + CH347_Flush_Buffer(); + } + + if (nb8 > 0 && nb1 == 0) { + nb8--; + nb1 = 8; + } + + IsRead = (scan == SCAN_IN || scan == SCAN_IO); + DI = BI = 0; + while (DI < (unsigned long)nb8) { + if ((nb8 - DI) > UCMDPKT_DATA_MAX_BYTES_USBHS) + PktDataLen = UCMDPKT_DATA_MAX_BYTES_USBHS; + else + PktDataLen = nb8 - DI; + + DII = PktDataLen; + + if (IsRead) + CH347_In_Buffer(CH347_CMD_JTAG_DATA_SHIFT_RD); + else + CH347_In_Buffer(CH347_CMD_JTAG_DATA_SHIFT); + + /* packet data don't deal D3 & D4 */ + if ((CH347_CMD_JTAG_DATA_SHIFT_RD != ch347.lastCmd) | + (CH347_CMD_JTAG_DATA_SHIFT != ch347.lastCmd)) { + /* update the ch347 struct */ + ch347.lastCmd = 0; + ch347.len_idx = 0; + ch347.len_value = 0; + } + + CH347_In_Buffer((uint8_t)(PktDataLen >> 0) & 0xFF); + CH347_In_Buffer((uint8_t)(PktDataLen >> 8) & 0xFF); + + if (bits) + CH347_In_Buffer_bytes(&bits[DI], PktDataLen); + else + CH347_In_Buffer_bytes(byte0, PktDataLen); + DI += DII; + + tempLength += (DII + CH347_CMD_HEADER); + } + + totalReadLength += tempLength; + + if (IsRead) { + ch347.read_count += tempLength; + readLen += tempLength; + } + + if (bits) { + CMD_Bit = IsRead ? CH347_CMD_JTAG_BIT_OP_RD : \ + CH347_CMD_JTAG_BIT_OP; + DLen = (nb1 * 2) + 1; + + if (CMD_Bit != ch347.lastCmd) { + CH347_In_Buffer(CMD_Bit); + CH347_In_Buffer((uint8_t)(DLen >> 0) & 0xFF); + CH347_In_Buffer((uint8_t)(DLen >> 8) & 0xFF); + ch347.lastCmd = CMD_Bit; + ch347.len_idx = ch347.buffer_idx - 2; + ch347.len_value = DLen; + } else { + /* update the ch347 struct cmd data leng */ + ch347.len_value += DLen; + /* update the cmd packet valid leng */ + ch347.buffer[ch347.len_idx] = + (uint8_t)(ch347.len_value >> 0) & 0xFF; + ch347.buffer[ch347.len_idx + 1] = + (uint8_t)(ch347.len_value >> 8) & 0xFF; + } + + TMS_Bit = TMS_L; + for (i = 0; i < nb1; i++) { + if ((bits[nb8] >> i) & 0x01) + TDI_Bit = TDI_H; + else + TDI_Bit = TDI_L; + + if ((i + 1) == nb1) + TMS_Bit = TMS_H; + + CH347_In_Buffer(TMS_Bit | TDI_Bit | TCK_L | TRST_H); + CH347_In_Buffer(TMS_Bit | TDI_Bit | TCK_H | TRST_H); + } + CH347_In_Buffer(TMS_Bit | TDI_Bit | TCK_L | TRST_H); + } + + ch347.TMS = TMS_Bit; + ch347.TDI = TDI_Bit; + ch347.TCK = TCK_L; + + if (IsRead) { + tempLength = ((DLen / 2) + CH347_CMD_HEADER); + totalReadLength += tempLength; + ch347.read_count += tempLength; + readLen += tempLength; + DI = BI = 0; + } + int offset = 0, bit_count = 0; + if (IsRead && totalReadLength > 0) { + if (ch347.pack_size == STANDARD_PACK && bits && cmd) { + CH347_Flush_Buffer(); + if (readLen > SF_PACKET_BUF_SIZE){ + readData = realloc(readData, readLen); + if (!readData){ + LOG_DEBUG("CH347_WriteRead realloc failed"); + goto END; + } + } + else{ + CH347_Read_Scan(&readData[0], readLen); + } + } + + for (i = 0; i < cmd->num_fields; i++) { + /* if neither in_value nor in_handler + * are specified we don't have to examine this field + */ + LOG_DEBUG("fields[%i].in_value[%i], offset: %d", + i, cmd->fields[i].num_bits, offset); + num_bits = cmd->fields[i].num_bits; + if (cmd->fields[i].in_value) { + if (ch347.pack_size == LARGER_PACK) { + bit_copy_queued( + &ch347.read_queue, + cmd->fields[i].in_value, + 0, + &ch347.read_buffer[ch347.read_idx], + offset, num_bits); + } else { + uint8_t *captured = buf_set_buf( + readData, bit_count, + malloc(DIV_ROUND_UP(num_bits, + 8)), 0, + num_bits); + if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) { + char *char_buf = + buf_to_hex_str( + captured, + (num_bits > + DEBUG_JTAG_IOZ) + ? DEBUG_JTAG_IOZ + : num_bits); + free(char_buf); + } + buf_cpy(captured, + cmd->fields[i].in_value, + num_bits); + free(captured); + } + }else { + LOG_DEBUG_IO("cmd->fields with no data"); + } + bit_count += cmd->fields[i].num_bits; + if (ch347.pack_size == LARGER_PACK) { + if (num_bits > 7) + ch347.read_idx += DIV_ROUND_UP(bit_count, 8); + offset += num_bits; + } + } + } + + tempIndex = ch347.buffer_idx; + for (i = 0; i < CH347_CMD_HEADER; i++) + CH347_In_Buffer(0); + BI = CH347_CMD_HEADER; + BI = CH347_IdleClock(BI); + + combinePackets(CH347_CMD_JTAG_BIT_OP, tempIndex, BI); +END: + if (byte0) + free(byte0); + if (readData) + free(readData); +} + +static void CH347_RunTest(int cycles, tap_state_t state) +{ + LOG_DEBUG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state); + if (tap_get_state() != TAP_IDLE) + CH347_MoveState(TAP_IDLE, 0); + + uint8_t tmsValue = 0; + CH347_TmsChange(&tmsValue, 7, 1); + + CH347_WriteRead(NULL, NULL, cycles, SCAN_OUT); + CH347_MoveState(state, 0); +} + +static void CH347_TableClocks(int cycles) +{ + LOG_DEBUG_IO("%s(cycles=%i)", __func__, cycles); + CH347_WriteRead(NULL, NULL, cycles, SCAN_OUT); +} + +/** + * CH347_Scan - Switch to SHIFT-DR or SHIFT-IR status for scanning + * @param cmd Upper layer transfer command parameters + * + * @return Success returns ERROR_OK + */ +static int CH347_Scan(struct scan_command *cmd) +{ + int scan_bits; + uint8_t *buf = NULL; + enum scan_type type; + int ret = ERROR_OK; + static const char *const type2str[] = { + "", "SCAN_IN", "SCAN_OUT", "SCAN_IO" + }; + char *log_buf = NULL; + + type = jtag_scan_type(cmd); + scan_bits = jtag_build_buffer(cmd, &buf); + + if (cmd->ir_scan) + CH347_MoveState(TAP_IRSHIFT, 0); + else + CH347_MoveState(TAP_DRSHIFT, 0); + + log_buf = HexToString(buf, DIV_ROUND_UP(scan_bits, 8)); + LOG_DEBUG_IO("Scan"); + LOG_DEBUG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", + __func__, + cmd->ir_scan ? "IRSCAN" : "DRSCAN", + type2str[type], + scan_bits, log_buf, cmd->end_state); + + free(log_buf); + + CH347_WriteRead(cmd, buf, scan_bits, type); + + free(buf); + + CH347_MoveState(cmd->end_state, 1); + + return ret; +} + +static void CH347_Sleep(int us) +{ + LOG_DEBUG_IO("%s(us=%d)", __func__, us); + jtag_sleep(us); +} + +static int ch347_execute_queue(void) +{ + struct jtag_command *cmd; + int ret = ERROR_OK; + + for (cmd = jtag_command_queue; ret == ERROR_OK && cmd; + cmd = cmd->next) { + switch (cmd->type) { + case JTAG_RESET: + LOG_DEBUG_IO("JTAG_RESET : %d %d.\n", + cmd->cmd.reset->trst, + cmd->cmd.reset->srst); + ch347_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); + break; + case JTAG_RUNTEST: + CH347_RunTest(cmd->cmd.runtest->num_cycles, + cmd->cmd.runtest->end_state); + break; + case JTAG_STABLECLOCKS: + CH347_TableClocks(cmd->cmd.stableclocks->num_cycles); + break; + case JTAG_TLR_RESET: + CH347_MoveState(cmd->cmd.statemove->end_state, 0); + break; + case JTAG_PATHMOVE: + CH347_MovePath(cmd->cmd.pathmove); + break; + case JTAG_TMS: + CH347_TMS(cmd->cmd.tms); + break; + case JTAG_SLEEP: + CH347_Sleep(cmd->cmd.sleep->us); + break; + case JTAG_SCAN: + ret = CH347_Scan(cmd->cmd.scan); + break; + default: + LOG_ERROR("BUG: unknown JTAG command type 0x%X", + cmd->type); + ret = ERROR_FAIL; + break; + } + } + + CH347_Flush_Buffer(); + return ret; +} + +/** + * ch347_init - CH347 Initialization function + * + * Todo: + * Initialize dynamic library functions + * Open Device + * @return Success returns 0, failure returns ERROR_FAIL + */ +static int ch347_init(void) +{ +#ifdef _WIN32 + if (uhModule == 0) { + uhModule = LoadLibrary("CH347DLL.DLL"); + if (uhModule) { + CH347OpenDevice = (pCH347OpenDevice)GetProcAddress( + uhModule, "CH347OpenDevice"); + CH347CloseDevice = (pCH347CloseDevice)GetProcAddress( + uhModule, "CH347CloseDevice"); + CH347ReadData = (pCH347ReadData)GetProcAddress( + uhModule, "CH347ReadData"); + CH347WriteData = (pCH347WriteData)GetProcAddress( + uhModule, "CH347WriteData"); + CH347SetTimeout = (pCH347SetTimeout)GetProcAddress( + uhModule, "CH347SetTimeout"); + if (CH347OpenDevice == NULL || CH347CloseDevice == NULL + || CH347SetTimeout == NULL || CH347ReadData == NULL + || CH347WriteData == NULL) { + LOG_ERROR("Jtag_init error "); + return ERROR_FAIL; + } + } + } + DevIsOpened = CH347OpenDevice(ugIndex); +#elif defined(__linux__) + DevIsOpened = CH347OpenDevice(ugIndex); + ugIndex = DevIsOpened; +#endif + if (DevIsOpened == -1) { + LOG_ERROR("CH347 Open Error."); + return ERROR_FAIL; + } else { + LOG_INFO("CH347 Open Succ."); + } + + if (!swd_mode) { + USBC_PACKET = USBC_PACKET_USBHS; + /* ch347 init */ + ch347.TCK = TCK_L; + ch347.TMS = TMS_H; + ch347.TDI = TDI_L; + ch347.TRST = TRST_H; + ch347.buffer_idx = 0; + ch347.buffer = (uint8_t *)calloc(1, SF_PACKET_BUF_SIZE); + if (!ch347.buffer) + LOG_ERROR("ch347.buffer calloc failed."); + ch347.len_idx = 0; + ch347.len_value = 0; + ch347.lastCmd = 0; + ch347.read_buffer = (uint8_t *)calloc(1, SF_PACKET_BUF_SIZE); + if (!ch347.read_buffer) + LOG_ERROR("ch347.read_buffer calloc failed."); + ch347.read_count = 0; + ch347.read_idx = 0; + + bit_copy_queue_init(&ch347.read_queue); + + /* CH347SetTimeout(ugIndex, 500, 500); */ + + tap_set_state(TAP_RESET); + } else { /* swd init */ + CH347SWD_INIT(ugIndex, 1); + } + return ERROR_OK; +} + +/** + * ch347_quit - CH347 Device Release Function + * + * Todo: + * Reset JTAG pin signal + * Close + * @return always returns 0 + */ +static int ch347_quit(void) +{ + unsigned long retlen = 4; + uint8_t byte[4] = {CH347_CMD_JTAG_BIT_OP, 0x01, 0x00, ch347.TRST}; + if (!swd_mode) { + CH347_Write(byte, &retlen); + bit_copy_discard(&ch347.read_queue); + } + if (DevIsOpened) { + CH347CloseDevice(ugIndex); + LOG_INFO("Close the CH347."); + DevIsOpened = false; + } + if (ch347.buffer){ + free(ch347.buffer); + ch347.buffer = NULL; + } + if (ch347.read_buffer){ + free(ch347.read_buffer); + ch347.read_buffer = NULL; + } + return 0; +} + +static bool Check_Speed(uint64_t iIndex, uint8_t iClockRate) +{ + unsigned long int i = 0, j; + bool retVal; + uint8_t cmdBuf[32] = ""; + cmdBuf[i++] = CH347_CMD_JTAG_INIT; + cmdBuf[i++] = 6; + cmdBuf[i++] = 0; + + cmdBuf[i++] = 0; + cmdBuf[i++] = iClockRate; + + for (j = 0; j < 4; j++) + cmdBuf[i++] = ch347.TCK | ch347.TDI | ch347.TMS | ch347.TRST; + + unsigned long int mLength = i; + if (!CH347WriteData(iIndex, cmdBuf, &mLength) || (mLength != i)) + return false; + + mLength = 4; + memset(cmdBuf, 0, sizeof(cmdBuf)); + + if (!CH347ReadData(iIndex, cmdBuf, &mLength) || (mLength != 4)) + return false; + + retVal = ((cmdBuf[0] == CH347_CMD_JTAG_INIT) && \ + (cmdBuf[CH347_CMD_HEADER] == 0)); + return retVal; +} + +static bool CH347Jtag_INIT(uint64_t iIndex, uint8_t iClockRate) +{ + ch347.pack_size = (Check_Speed(iIndex, 0x09) == true) ? \ + STANDARD_PACK : LARGER_PACK; + if (ch347.pack_size == STANDARD_PACK) { + if (iClockRate - 2 < 0) + return Check_Speed(iIndex, 0); + else + return Check_Speed(iIndex, iClockRate - 2); + } + + return Check_Speed(iIndex, iClockRate); +} + +/** + * ch347_speed - CH347 TCK frequency setting + * @param speed Frequency size set + * @return Success returns ERROR_OK��failed returns FALSE + */ +static int ch347_speed(int speed) +{ + unsigned long i = 0; + uint8_t clockRate; + int retval = -1; + int speed_clock[8] = { + KHZ(468.75), KHZ(937.5), MHZ(1.875), + MHZ(3.75), MHZ(7.5), MHZ(15), MHZ(30), MHZ(60) + }; + + if (!swd_mode) { + for (i = 0; i < ARRAY_SIZE(speed_clock); i++) { + if ((speed >= speed_clock[i]) && + (speed <= speed_clock[i + 1])) { + clockRate = i + 1; + retval = CH347Jtag_INIT(ugIndex, clockRate); + if (!retval) { + LOG_ERROR("Couldn't set CH347 TCK speed"); + return retval; + } else { + break; + } + } else if (speed < speed_clock[0]) { + retval = CH347Jtag_INIT(ugIndex, 0); + if (!retval) { + LOG_ERROR("Couldn't set CH347 TCK speed"); + return retval; + } else { + break; + } + } + } + } + return ERROR_OK; +} + +static int ch347_speed_div(int speed, int *khz) +{ + *khz = speed / 1000; + return ERROR_OK; +} + +static int ch347_khz(int khz, int *jtag_speed) +{ + if (khz == 0) { + LOG_ERROR("Couldn't support the adapter speed"); + return ERROR_FAIL; + } + *jtag_speed = khz * 1000; + return ERROR_OK; +} + +static int ch347_trst_out(unsigned char status) +{ + unsigned long int BI = 0; + unsigned char byte = 0; + unsigned char cmdPacket[4] = ""; + cmdPacket[BI++] = CH347_CMD_JTAG_BIT_OP; + cmdPacket[BI++] = 0x01; + cmdPacket[BI++] = 0; + byte = ch347.TCK | ch347.TDI | ch347.TMS | (ch347.TRST = + (status ? TRST_H : TRST_L)); + cmdPacket[BI++] = byte; + + if (!CH347_Write(cmdPacket, &BI)) { + LOG_ERROR("TRST set failure."); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(ch347_handle_vid_pid_command) +{ + /* TODO */ + return ERROR_OK; +} + +COMMAND_HANDLER(ch347_trst) +{ + ch347_trst_out(TRST_L); + jtag_sleep(atoi(CMD_ARGV[0]) * 1000); + ch347_trst_out(TRST_H); + return ERROR_OK; +} + +static const struct command_registration ch347_subcommand_handlers[] = { + { + .name = "vid_pid", + .handler = &ch347_handle_vid_pid_command, + .mode = COMMAND_CONFIG, + .help = "", + .usage = "", + }, + { + .name = "jtag_ntrst_delay", + .handler = &ch347_trst, + .mode = COMMAND_ANY, + .help = "set the trst of the CH347 device that is used as JTAG", + .usage = "[milliseconds]", + }, + + COMMAND_REGISTRATION_DONE}; + +static const struct command_registration ch347_command_handlers[] = { + { + .name = "ch347", + .mode = COMMAND_ANY, + .help = "perform ch347 management", + .chain = ch347_subcommand_handlers, + .usage = "", + }, + COMMAND_REGISTRATION_DONE}; + +static int ch347_swd_init(void) +{ + PCH347_SWD_IO pswd_io; + LOG_INFO("CH347 SWD mode enabled"); + swd_mode = true; + memset(&ch347_swd_context, 0, sizeof(ch347_swd_context)); + + INIT_LIST_HEAD(&ch347_swd_context.send_cmd_head); + INIT_LIST_HEAD(&ch347_swd_context.free_cmd_head); + + ch347_swd_context.queued_retval = ERROR_OK; + /* 0XE8 + 2byte len + N byte cmds */ + ch347_swd_context.send_len = CH347_CMD_HEADER; + /* 0XE8 + 2byte len + N byte ack + data */ + ch347_swd_context.need_recv_len = CH347_CMD_HEADER; + ch347_swd_context.ch347_cmd_buf = malloc(128 * sizeof(CH347_SWD_IO)); + if (ch347_swd_context.ch347_cmd_buf) { + pswd_io = (PCH347_SWD_IO)ch347_swd_context.ch347_cmd_buf; + for (int i = 0; i < 128; i++, pswd_io++) { + INIT_LIST_HEAD(&pswd_io->list_entry); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.free_cmd_head); + } + } + return ch347_swd_context.ch347_cmd_buf ? ERROR_OK : ERROR_FAIL; +} + +static PCH347_SWD_IO ch347_get_one_swd_io(void) +{ + PCH347_SWD_IO pswd_io; + if (list_empty(&ch347_swd_context.free_cmd_head)) { + return NULL; + } else { + pswd_io = list_first_entry(&ch347_swd_context.free_cmd_head, + CH347_SWD_IO, list_entry); + list_del_init(&pswd_io->list_entry); + pswd_io->cmd = 0; + pswd_io->usbcmd = CH347_CMD_SWD_SEQ_W; + pswd_io->dst = NULL; + return pswd_io; + } +} + +static void ch347_swd_queue_flush(void) +{ + unsigned long mLength = ch347_swd_context.send_len; + ch347_swd_context.send_buf[0] = (uint8_t)CH347_CMD_SWD; + ch347_swd_context.send_buf[1] = (uint8_t)(ch347_swd_context.send_len - + CH347_CMD_HEADER); + ch347_swd_context.send_buf[2] = (uint8_t)((ch347_swd_context.send_len - + CH347_CMD_HEADER) >> 8); + if (!CH347WriteData(ugIndex, ch347_swd_context.send_buf, &mLength) || + (mLength != ch347_swd_context.send_len)) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_DEBUG("CH347WriteData error "); + return; + } + ch347_swd_context.recv_len = 0; + do { + mLength = CH347_MAX_RECV_BUF - ch347_swd_context.recv_len; + if (!CH347ReadData(ugIndex, + &ch347_swd_context.recv_buf[ch347_swd_context.recv_len], + &mLength)) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_DEBUG("CH347ReadData error "); + return; + } + ch347_swd_context.recv_len += mLength; + } while (ch347_swd_context.recv_len < ch347_swd_context.need_recv_len); + + if (ch347_swd_context.need_recv_len > ch347_swd_context.recv_len) { + LOG_ERROR("ch347_swd_queue_flush write/read failed %d %d %d", + __LINE__, + ch347_swd_context.recv_len, + ch347_swd_context.need_recv_len); + } +} +static void ch347_wrtie_swd_reg(uint8_t cmd, const uint8_t *out, uint8_t parity) +{ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = + CH347_CMD_SWD_REG_W; + /* 8bit + 32bit +1bit */ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x29; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x00; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = cmd; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[0]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[1]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[2]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[3]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = parity; + /* 0xA0 + 1 byte(3bit ACK) */ + ch347_swd_context.need_recv_len += (1 + 1); +} + +static void ch347_wrtie_spec_seq(const uint8_t *out, uint8_t out_len) +{ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = + CH347_CMD_SWD_SEQ_W; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out_len; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x00; + for (uint8_t i = 0; i < DIV_ROUND_UP(out_len, 8); i++) { + if (out) { + ch347_swd_context.send_buf[ch347_swd_context.send_len++] + = out[i]; + } else { + ch347_swd_context.send_buf[ch347_swd_context.send_len++] + = 0x00; + } + } + ch347_swd_context.need_recv_len += 1; /* 0xA1 */ +} + +static void ch347_read_swd_reg(uint8_t cmd) +{ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = + CH347_CMD_SWD_REG_R; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x22; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x00; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = cmd; + /* 0xA2 + 1 byte(3bit ACK) + 4 byte(data) + + 1 byte(1bit parity+1bit trn) */ + ch347_swd_context.need_recv_len += 1 + 1 + 4 + 1; +} + +static int ch347_swd_switch_out(enum swd_special_seq seq, const uint8_t *out, + unsigned out_len) +{ + PCH347_SWD_IO pswd_io; + if ((ch347_swd_context.send_len + + (1 + 2 + DIV_ROUND_UP(out_len, 8))) > CH347_MAX_SEND_BUF) { + return ERROR_FAIL; + } + if ((ch347_swd_context.need_recv_len + 2) > CH347_MAX_RECV_BUF) + return ERROR_FAIL; + + pswd_io = ch347_get_one_swd_io(); + if (pswd_io) { + ch347_wrtie_spec_seq(out, out_len); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.send_cmd_head); + return ERROR_OK; + } else { + return ERROR_FAIL; + } +} + +/* check read/write REG can fill in remaining buff */ +static bool ch347_chk_buf_size(uint8_t cmd, uint32_t ap_delay_clk) +{ + bool bflush; + uint32_t send_len, recv_len, len; + bflush = false; + send_len = ch347_swd_context.send_len; + recv_len = ch347_swd_context.need_recv_len; + do { + + if (cmd & SWD_CMD_RNW) { + len = 1 + 1 + 1 + 1; /* 0xA2 + len + rev + cmd */ + if (send_len + len > CH347_MAX_SEND_BUF) + break; + send_len += len; + len = 1 + 1 + 4 + 1; + /* 0xA2 + 1byte(3bit ack) + 4byte(data) + + 1byte(1bit parity+1bit trn) */ + if (recv_len + len > CH347_MAX_RECV_BUF) + break; + recv_len += len; + } else { /* write reg */ + len = 1 + 1 + 1 + 1 + 4 + 1; + /* 0xA0 + len + rev + cmd +data + parity */ + if (send_len + len > CH347_MAX_SEND_BUF) + break; + send_len += len; + len = 1 + 1; /* 0xA0 + 1byte(3bit ack) */ + if (recv_len + len > CH347_MAX_RECV_BUF) + break; + recv_len += len; + } + if (cmd & SWD_CMD_APNDP) { + len = 1 + 1 + 1 + DIV_ROUND_UP(ap_delay_clk, 8); + /* 0xA1 + Len + rev + n byte(delay) */ + if (send_len + len > CH347_MAX_SEND_BUF) + break; + len = 1; /* 0xA1 */ + if ((recv_len + len) > CH347_MAX_RECV_BUF) + break; + } + /* swd packet requests */ + bflush = true; + } while (false); + + return bflush; +} + +static void ch347_swd_send_idle(uint32_t ap_delay_clk) +{ + PCH347_SWD_IO pswd_io; + + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + ch347_swd_run_queue(); + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + LOG_DEBUG("ch347_swd_queue_cmd error "); + ch347_swd_context.queued_retval = ERROR_FAIL; + return; + } + } + ch347_wrtie_spec_seq(NULL, ap_delay_clk); + + list_add_tail(&pswd_io->list_entry, &ch347_swd_context.send_cmd_head); +} + +static void ch347_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, + uint32_t ap_delay_clk) +{ + PCH347_SWD_IO pswd_io; + if (ap_delay_clk > 255) + printf("ch347_swd_queue_cmd ap_delay_clk = %d\r\n", + ap_delay_clk); + + if (ch347_swd_context.sent_cmd_count >= CH347_MAX_SEND_CMD) + ch347_swd_run_queue(); + + if (!ch347_chk_buf_size(cmd, ap_delay_clk)) + ch347_swd_run_queue(); + + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + ch347_swd_run_queue(); + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + LOG_DEBUG("ch347_swd_queue_cmd error "); + ch347_swd_context.queued_retval = ERROR_FAIL; + return; + } + } + + pswd_io->cmd = cmd | SWD_CMD_START | SWD_CMD_PARK; + + if (pswd_io->cmd & SWD_CMD_RNW) { + pswd_io->usbcmd = CH347_CMD_SWD_REG_R; + pswd_io->dst = dst; + ch347_read_swd_reg(pswd_io->cmd); + } else { + pswd_io->usbcmd = CH347_CMD_SWD_REG_W; + pswd_io->value = data; + ch347_wrtie_swd_reg(pswd_io->cmd, (uint8_t *)&data, + parity_u32(data)); + } + + ch347_swd_context.sent_cmd_count++; + list_add_tail(&pswd_io->list_entry, &ch347_swd_context.send_cmd_head); + /* Insert idle cycles after AP accesses to avoid WAIT */ + if (cmd & SWD_CMD_APNDP) { + if (ap_delay_clk == 0) + printf("ap_delay_clk == 0"); + ch347_swd_send_idle(ap_delay_clk); + } +} + +static int ch347_swd_switch_seq(enum swd_special_seq seq) +{ + printf("ch347_swd_switch_seq %d \r\n", seq); + switch (seq) { + case LINE_RESET: + LOG_DEBUG("SWD line reset"); + return ch347_swd_switch_out(seq, swd_seq_line_reset, + swd_seq_line_reset_len); + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + return ch347_swd_switch_out(seq, swd_seq_jtag_to_swd, + swd_seq_jtag_to_swd_len); + case JTAG_TO_DORMANT: + LOG_DEBUG("JTAG-to-DORMANT"); + return ch347_swd_switch_out(seq, swd_seq_jtag_to_dormant, + swd_seq_jtag_to_dormant_len); + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + return ch347_swd_switch_out(seq, swd_seq_swd_to_jtag, + swd_seq_swd_to_jtag_len); + case SWD_TO_DORMANT: + LOG_DEBUG("SWD-to-DORMANT"); + return ch347_swd_switch_out(seq, swd_seq_swd_to_dormant, + swd_seq_swd_to_dormant_len); + case DORMANT_TO_SWD: + LOG_DEBUG("DORMANT-to-SWD"); + return ch347_swd_switch_out(seq, swd_seq_dormant_to_swd, + swd_seq_dormant_to_swd_len); + case DORMANT_TO_JTAG: + LOG_DEBUG("DORMANT-to-JTAG"); + return ch347_swd_switch_out(seq, swd_seq_dormant_to_jtag, + swd_seq_dormant_to_jtag_len); + default: + LOG_ERROR("Sequence %d not supported", seq); + return ERROR_FAIL; + } +} + +static void ch347_swd_read_reg(uint8_t cmd, uint32_t *value, + uint32_t ap_delay_clk) +{ + assert(cmd & SWD_CMD_RNW); + ch347_swd_queue_cmd(cmd, value, 0, ap_delay_clk); +} + +static void ch347_swd_write_reg(uint8_t cmd, uint32_t value, + uint32_t ap_delay_clk) +{ + assert(!(cmd & SWD_CMD_RNW)); + ch347_swd_queue_cmd(cmd, NULL, value, ap_delay_clk); +} + +static int ch347_swd_run_queue(void) +{ + LOG_DEBUG_IO("Executing %u queued transactions", + ch347_swd_context.sent_cmd_count); + int retval, parity; + struct list_head *tmp, *pos; + uint8_t *recv_buf; + uint32_t recv_len, cmds_len, data; + PCH347_SWD_IO pswd_io; + if (ch347_swd_context.queued_retval != ERROR_OK) { + LOG_DEBUG_IO("Skipping due to previous errors: %d", + ch347_swd_context.queued_retval); + goto skip; + } + + /* A transaction must be followed by another transaction or at least 8 + idle cycles to ensure that data is clocked through the AP. */ + if ((ch347_swd_context.send_len + (1 + 2 + 1)) > CH347_MAX_SEND_BUF) + goto skip_idle; + + if ((ch347_swd_context.need_recv_len + 1) > CH347_MAX_RECV_BUF) + goto skip_idle; + + ch347_swd_send_idle(8); + +skip_idle: + + ch347_swd_queue_flush(); + + if (ch347_swd_context.queued_retval != ERROR_OK) { + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + recv_buf = ch347_swd_context.recv_buf; + recv_len = 0; + if (recv_buf[recv_len++] != CH347_CMD_SWD) { /* 0XE8 */ + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + + cmds_len = BUILD_UINT16(recv_buf[recv_len], recv_buf[recv_len + 1]); + recv_len += 2; /* cmds_len */ + if ((cmds_len + CH347_CMD_HEADER) > ch347_swd_context.recv_len) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + + list_for_each_safe(pos, tmp, &ch347_swd_context.send_cmd_head) { + pswd_io = list_entry(pos, CH347_SWD_IO, list_entry); + if (pswd_io->usbcmd == CH347_CMD_SWD_SEQ_W) { + if (recv_buf[recv_len++] != CH347_CMD_SWD_SEQ_W) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", + __LINE__); + goto skip; + } + } else { /* read/write Reg */ + int ack; + bool check_ack; + /* read Reg */ + if (recv_buf[recv_len] == CH347_CMD_SWD_REG_R) { + recv_len++; + ack = buf_get_u32(&recv_buf[recv_len++], 0, 3); + /* Devices do not reply to DP_TARGETSEL write + cmd, ignore received ack */ + check_ack = swd_cmd_returns_ack(pswd_io->cmd); + if (ack != SWD_ACK_OK && check_ack) { + ch347_swd_context.queued_retval = + swd_ack_to_error_code(ack); + LOG_ERROR("ack != SWD_ACK_OK %d", + __LINE__); + goto skip; + } + if (pswd_io->cmd & SWD_CMD_RNW) { + data = buf_get_u32(&recv_buf[recv_len], + 0, 32); + parity = buf_get_u32( + &recv_buf[recv_len], 32, 1); + if (parity != parity_u32(data)) { + LOG_ERROR("SWD Read data parity mismatch %d", + __LINE__); + ch347_swd_context.queued_retval = ERROR_FAIL; + goto skip; + } + + LOG_DEBUG_IO("%s%s %s %s reg %X = %08X\n" PRIx32, + check_ack ? "" : "ack ignored ", + ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : \ + ack == SWD_ACK_FAULT ? "FAULT" : "JUNK", + pswd_io->cmd & SWD_CMD_APNDP ? "AP" : "DP", + pswd_io->cmd & SWD_CMD_RNW ? "read" : "write", + (pswd_io->cmd & SWD_CMD_A32) >> 1, + data); + + if (pswd_io->dst) + *pswd_io->dst = data; + } else { + ch347_swd_context.queued_retval = + ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + recv_len += 5; + } else if (recv_buf[recv_len] == CH347_CMD_SWD_REG_W) { + recv_len++; + ack = buf_get_u32(&recv_buf[recv_len++], 0, 3); + /* Devices do not reply to DP_TARGETSEL write + cmd, ignore received ack */ + check_ack = swd_cmd_returns_ack(pswd_io->cmd); + if (ack != SWD_ACK_OK && check_ack) { + ch347_swd_context.queued_retval = + swd_ack_to_error_code(ack); + LOG_ERROR("SWD Read data parity mismatch%d", __LINE__); + goto skip; + } + LOG_DEBUG_IO("%s%s %s %s reg %X = %08X\n" PRIx32, + check_ack ? "" : "ack ignored ", + ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : \ + ack == SWD_ACK_FAULT ? "FAULT" : "JUNK", + pswd_io->cmd & SWD_CMD_APNDP ? "AP" : "DP", + pswd_io->cmd & SWD_CMD_RNW ? "read" : "write", + (pswd_io->cmd & SWD_CMD_A32) >> 1, + pswd_io->value); + } else { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d recv_len = %d", + __LINE__, recv_len); + goto skip; + } + } + list_del_init(&pswd_io->list_entry); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.free_cmd_head); + } + +skip: + if (!list_empty(&ch347_swd_context.send_cmd_head)) { + list_for_each_safe(pos, tmp, &ch347_swd_context.send_cmd_head) + { + pswd_io = list_entry(pos, CH347_SWD_IO, list_entry); + list_del_init(&pswd_io->list_entry); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.free_cmd_head); + } + } + /* 0xE8 + 2byte len */ + ch347_swd_context.send_len = CH347_CMD_HEADER; + /* 0xE8 + 2byte len */ + ch347_swd_context.need_recv_len = CH347_CMD_HEADER; + ch347_swd_context.recv_len = 0; + ch347_swd_context.sent_cmd_count = 0; + retval = ch347_swd_context.queued_retval; + ch347_swd_context.queued_retval = ERROR_OK; + + return retval; +} + +static const struct swd_driver ch347_swd = { + .init = ch347_swd_init, + .switch_seq = ch347_swd_switch_seq, + .read_reg = ch347_swd_read_reg, + .write_reg = ch347_swd_write_reg, + .run = ch347_swd_run_queue, +}; + +static const char *const ch347_transports[] = {"jtag", "swd", NULL}; + +static struct jtag_interface ch347_interface = { + .supported = DEBUG_CAP_TMS_SEQ, + .execute_queue = ch347_execute_queue, +}; + +struct adapter_driver ch347_adapter_driver = { + .name = "ch347", + .transports = ch347_transports, + .commands = ch347_command_handlers, + + .init = ch347_init, + .quit = ch347_quit, + .reset = ch347_reset, + .speed = ch347_speed, + .khz = ch347_khz, + .speed_div = ch347_speed_div, + + .jtag_ops = &ch347_interface, + .swd_ops = &ch347_swd, +}; \ No newline at end of file diff --git a/src/jtag/interface.h b/src/jtag/interface.h index 3df424086d..2958896b73 100644 --- a/src/jtag/interface.h +++ b/src/jtag/interface.h @@ -401,5 +401,6 @@ extern struct adapter_driver vdebug_adapter_driver; extern struct adapter_driver vsllink_adapter_driver; extern struct adapter_driver xds110_adapter_driver; extern struct adapter_driver xlnx_pcie_xvc_adapter_driver; +extern struct adapter_driver ch347_adapter_driver; #endif /* OPENOCD_JTAG_INTERFACE_H */ diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index c24ead8cd9..5ac64ccae0 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -152,6 +152,9 @@ struct adapter_driver *adapter_drivers[] = { #endif #if BUILD_AM335XGPIO == 1 &am335xgpio_adapter_driver, +#endif +#if BUILD_CH347 == 1 + &ch347_adapter_driver, #endif NULL, }; diff --git a/src/target/target.c b/src/target/target.c index d8e65863c6..4a4c62613f 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -5845,7 +5845,17 @@ COMMAND_HANDLER(handle_target_debug_reason) struct target *target = get_current_target(CMD_CTX); - command_print(CMD, "%s", debug_reason_name(target)); + + const char *debug_reason = nvp_value2name(nvp_target_debug_reason, + target->debug_reason)->name; + + if (!debug_reason) { + command_print(CMD, "bug: invalid debug reason (%d)", + target->debug_reason); + return ERROR_FAIL; + } + + command_print(CMD, "%s", debug_reason); return ERROR_OK; } --