/*
 * application library for ch347.
 *
 * Copyright (C) 2023 Nanjing Qinheng Microelectronics Co., Ltd.
 * Web: http://wch.cn
 * Author: WCH <tech@wch.cn>
 *
 * 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.
 *
 * Cross-compile with cross-gcc -I /path/to/cross-kernel/include
 *
 * V1.0 - initial version
 * V1.1 - add operation for HID mode
 * V1.2 - add HID serial port operation, fixed byteorder error
 * V1.3 - fix memory out of bound issue in CH347StreamI2C API
 * V1.4 - add support of ch347 gpio interrupt function
 * 		- add support of I2C clock stretching, more I2C and SPI clock settings, etc.
 * V1.5 - add support for ch346c
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <signal.h>
#include <linux/serial.h>
#include <linux/hidraw.h>
#include <endian.h>
#include <error.h>
#define termios asmtermios
#define winsize asmwinsize
#define termio asmtermio
#include <asm/termios.h>
#undef termios
#undef winsize
#undef termio
#include <termios.h>
#include <pthread.h>

#include "ch347_lib.h"
#include "ch341_lib.h"

#define GCC_LOW_COMPAT
// #undef GCC_LOW_COMPAT

#define LIB_INFO "V1.5 On 2024.08"

#ifndef min
#define min(x, y) (((x) < (y)) ? (x) : (y))
#endif

#ifndef max
#define max(x, y) (((x) < (y)) ? (y) : (x))
#endif

#ifndef BIT
#define BIT(i) (1 << i)
#endif

#define CH341_PACKET_LENGTH 32
#define CH347_PACKET_LENGTH 512
#define MAX_BUFFER_LENGTH 0x1000
#define MAX_BUFFER_LENGTH_CH339W 0x400
#define MAX_BUFFER_LENGTH_CH346C 0x100000
#define DEFAULT_BUFFER_LEN 0x0400
#define CH341_MAX_NUMBER 128

#define CH341A_CMD_I2C_STREAM 0xAA // I2C Interface Command
#define CH341A_CMD_I2C_STM_STA 0x74 // I2C Stream Start Command
#define CH341A_CMD_I2C_STM_STO 0x75 // I2C Stream Stop byte Command
#define CH341A_CMD_I2C_STM_OUT 0x80 // I2C Stream Out Command
#define CH341A_CMD_I2C_STM_IN 0xC0 // I2C Stream In Command
#define CH341A_CMD_I2C_STM_MAX \
	(min(0x3F, CH341_PACKET_LENGTH)) // I2C Stream Max Length
#define CH347_CMD_I2C_STM_MAX 0x3F // I2C Stream Max Length
#define CH341A_CMD_I2C_STM_SET 0x60 // I2C Stream Set Mode
#define CH341A_CMD_I2C_STM_US 0x40 // I2C Stream Delay(us)
#define CH341A_CMD_I2C_STM_MS 0x50 // I2C Stream Delay(ms)
#define CH341A_CMD_I2C_STM_DLY 0x0F // I2C Stream Set Max Delay
#define CH341A_CMD_I2C_STM_END 0x00 // I2C Stream End CommandF
#define CH347_CMD_I2C_STM_OUT_CLK_US 0x20 // I2C Stream Out Clock Delay(us)
#define CH347_CMD_I2C_STM_IN_CLK_US 0x30 // I2C Stream In Clock Delay(us)
#define CH347_CMD_I2C_STM_CLK_DLY 0x3FF // I2C Stream Set Max Clock Delay

#define CH347_CMD_I2C_STRETCH_Y 0x15 // I2C Clock Stretch enable
#define CH347_CMD_I2C_STRETCH_N 0x16 // I2C Clock Stretch disable

#define CH347_CMD_I2C_OD_Y 0x11 // I2C Open-Drain enable
#define CH347_CMD_I2C_OD_N 0x13 // I2C Open-Drain disable

#define USB20_CMD_HEADER 3
#define USB20_CMD_SPI_INIT 0xC0 // SPI Init
#define USB20_CMD_SPI_CONTROL \
	0xC1 // SPI接口控制命令,用于控制SPI接口片选引脚输出高低电平以及电平延时时间。
#define USB20_CMD_SPI_RD_WR \
	0xC2 // SPI接口常规读取写入数据命令,用于SPI接口通用读取写入操作，用于短包通信。
#define USB20_CMD_SPI_BLCK_RD \
	0xC3 // SPI接口批量读取数据命令,用于SPI接口批量读取数据，一般用于批量数据的读取操作。
#define USB20_CMD_SPI_BLCK_WR \
	0xC4 // SPI接口批量写入数据命令,用于SPI接口批量写入数据，一般用于批量数据的写入操作。
#define USB20_CMD_INFO_RD 0xCA // 参数获取,用于获取SPI接口相关参数等

/*SPI CMD*/
#define SET_CS 0
#define CLR_CS 1

/* SPI_mode */
#define SPI_Mode_Master ((uint16_t)0x0104)
#define SPI_Mode_Slave ((uint16_t)0x0000)

/* SPI_Clock_Polarity */
#define SPI_CPOL_Low ((uint16_t)0x0000)
#define SPI_CPOL_High ((uint16_t)0x0002)

/* SPI_Clock_Phase */
#define SPI_CPHA_1Edge ((uint16_t)0x0000)
#define SPI_CPHA_2Edge ((uint16_t)0x0001)

/*JTAG Define*/
#define JTAGIO_STA_OUT_TDI 0x10
#define JTAGIO_STA_OUT_TMS 0x02
#define JTAGIO_STA_OUT_TCK 0x01
#define JTAGIO_STA_OUT_RESET 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_RESET
#define TRST_L 0

#define HW_TDO_BUF_SIZE 4096

#define USB20_CMD_INFO_RD \
	0xCA // 参数获取,用于获取固件版本、SPI、I2C、JTAG接口相关参数等
#define USB20_CMD_JTAG_INIT 0xD0 // JTAG接口初始化命令
#define USB20_CMD_JTAG_BIT_OP 0xD1 // JTAG接口引脚位控制命令
#define USB20_CMD_JTAG_BIT_OP_RD 0xD2 // JTAG接口引脚位控制并读取命令
#define USB20_CMD_JTAG_DATA_SHIFT 0xD3 // JTAG接口数据移位命令
#define USB20_CMD_JTAG_DATA_SHIFT_RD 0xD4 // JTAG接口数据移位并读取命令

/*GPIO Define*/
#define CH347_GPIO_CNT 8
#define USB20_CMD_INFO_RD \
	0xCA // 用于获取固件版本、SPI、IIC、JTAG接口相关参数等
#define USB20_CMD_UART0_INIT 0xC9 // 串口0初始化
#define USB20_CMD_UART1_INIT 0xCB // 串口1初始化
#define USB20_CMD_GPIO_OP 0xCC // GPIO口操作,用于GPIO口控制操作
#define USB20_CMD_JUMP_IAP 0xCD // 跳转进入IAP,用于控制跳转进入IAP模式
#define USB20_CMD_BIT_STREAM 0xDA // GPIO数据流输入输出控制命令
#define USB20_CMD_SPI_CLK_INIT 0xE1 // SYSCLK系统运行主频控制命令
#define USB20_CMD_FUNC_SWITCH 0xE2 // CH347F的引脚功能切换命令

#define INFO_CHIP 0x00
#define INFO_SPI_IIC 0x01
#define INFO_JTAG 0x02
#define INFO_UART1 0x03
#define INFO_UART0 0x04

/* ioctl commands for interaction between driver and application */
#define IOCTL_MAGIC 'W'

#define CH34x_CHIP_VERSION _IOR(IOCTL_MAGIC, 0x81, uint16_t)
#define CH34x_SET_MODE _IOW(IOCTL_MAGIC, 0x94, uint16_t)
#define CH34x_START_BUFFERED_UPLOAD _IOW(IOCTL_MAGIC, 0xb4, uint16_t)
#define CH34x_STOP_BUFFERED_UPLOAD _IOW(IOCTL_MAGIC, 0xb5, uint16_t)
#define CH34x_QWERY_SLAVE_FIFO _IOR(IOCTL_MAGIC, 0xb6, uint16_t)
#define CH34x_RESET_SLAVE_FIFO _IOW(IOCTL_MAGIC, 0xb7, uint16_t)
#define CH34x_READ_SLAVE_FIFO _IOR(IOCTL_MAGIC, 0xb8, uint16_t)
#define CH34x_START_IRQ_TASK _IOW(IOCTL_MAGIC, 0xc0, uint16_t)
#define CH34x_STOP_IRQ_TASK _IOW(IOCTL_MAGIC, 0xc1, uint16_t)

#define DIV_ROUND_UP(m, n) (((m) + (n)-1) / (n))

typedef struct _CH347_JTAG_STA { // 记录CH347引脚状态
	int TMS;
	int TDI;
	int TCK;
	int TRST;
} CH347JtatStaS, pCH347JtatStaS;

typedef void (*Isr_Func)(int);

struct mDeviceInfo {
	mDeviceInforS ch347device;
	uint32_t iSpiSpeedHz; /* 60MHz Max */
	uint8_t iDataBits; /* 0: 8bit, 1: 16bit */
	CHIP_TYPE ChipType; /* chip model */
	uint16_t FirmwareVer;
	uint8_t iClockIndex;
	uint8_t UartIndex;
	uint16_t I2C_ReadWriteDelay;
	bool deAutoCS;
	pthread_mutex_t mutex;
	Isr_Func isr_routine;
	uint8_t workmode;
};

struct mDeviceInfo gusbch347[CH341_MAX_NUMBER] = { 0 };

CH347JtatStaS JtatPinSta = { 0, 0, 0,
			     TRST_H }; // 初始化JTAG引脚设备结构状态

bool CH347Jtag_SwitchTapState(int fd, uint8_t TapState);
bool CH347F_SPI_CS2_Enable(int fd);

int speed_arr[] = { B2000000, B1000000, B921600, B460800, B230400,
		    B115200,  B57600,	B38400,	 B19200,  B9600,
		    B4800,    B2400,	B1200,	 B300 };

int name_arr[] = { 2000000, 1000000, 921600, 460800, 230400, 115200, 57600,
		   38400,   19200,   9600,   4800,   2400,   1200,   300 };

/**
 * CH347GetLibInfo - get ch347 library information
 */
const char *CH347GetLibInfo(void)
{
	return LIB_INFO;
}

/**
 * CH347AddFd - store new device messages
 * @fd: file descriptor of device
 */
static int CH347AddFd(int fd)
{
	int i;

	for (i = 0; i < CH341_MAX_NUMBER; i++) {
		if (gusbch347[i].ch347device.fd <= 0) {
			gusbch347[i].ch347device.fd = fd;
			pthread_mutex_init(&gusbch347[i].mutex, 0);
			break;
		}
		continue;
	}
	if (i == CH341_MAX_NUMBER)
		return -ERR_RANGE;
	else
		return i;
}

/**
 * CH347DelFd - remove device
 * @fd: file descriptor of device
 */
static void CH347DelFd(int fd)
{
	int i;

	for (i = 0; i < CH341_MAX_NUMBER; i++) {
		if (gusbch347[i].ch347device.fd == fd) {
			gusbch347[i].ch347device.fd = 0;
			pthread_mutex_destroy(&gusbch347[i].mutex);
			break;
		}
		continue;
	}
}

/**
 * GetDevIndex - get device index via file descriptor
 * @fd: file descriptor of device
 *
 * The function return device index if successful, negative if fail.
 */
static int GetDevIndex(int fd)
{
	int i;

	for (i = 0; i < CH341_MAX_NUMBER; i++) {
		if (gusbch347[i].ch347device.fd == fd) {
			return i;
		}
		continue;
	}
	return -ERR_RANGE;
}

/**
 * GetDevObj - get global device object
 *
 * The function return pointer to device.
 */
mPDeviceInforS GetDevObj(int fd)
{
	int i = GetDevIndex(fd);

	if (i < 0)
		return NULL;

	return &gusbch347[i].ch347device;
}

/**
 * libtty_setcustombaudrate - set baud rate of tty device
 * @fd: device handle
 * @speed: baud rate to set
 *
 * The function return 0 if success, or -1 if fail.
 */
static int libtty_setcustombaudrate(int fd, int baudrate)
{
#ifndef GCC_LOW_COMPAT
	struct termios2 tio = { 0 };

	if (ioctl(fd, TCGETS2, &tio)) {
		perror("TCGETS2");
		return -ERR_IOCTL;
	}

	tio.c_cflag &= ~CBAUD;
	tio.c_cflag |= BOTHER;
	tio.c_ispeed = baudrate;
	tio.c_ospeed = baudrate;

	if (ioctl(fd, TCSETS2, &tio)) {
		perror("TCSETS2");
		return -ERR_IOCTL;
	}

	if (ioctl(fd, TCGETS2, &tio)) {
		perror("TCGETS2");
		return -ERR_IOCTL;
	}

	return 0;
#else
	int i;
	int ret;
	struct termios tio;

	tcgetattr(fd, &tio);
	for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
		if (baudrate == name_arr[i]) {
			tcflush(fd, TCIOFLUSH);
			cfsetispeed(&tio, speed_arr[i]);
			cfsetospeed(&tio, speed_arr[i]);
			ret = tcsetattr(fd, TCSANOW, &tio);
			if (ret != 0)
				perror("tcsetattr: ");
			tcflush(fd, TCIOFLUSH);
		}
	}
	return ret;
#endif
}

/**
 * CH347GetInfo - get ch347 hardware information
 * @fd: file descriptor of device
 * @index: type, 0 : chip info, 1 : SPI and I2C info, 2 : JTAG info, 3 : UART1 info, 4: UART0 info
 * @infodata: pointer to information array
 *
 * The function return true if success, others if fail.
 */
static bool CH347GetInfo(int fd, uint8_t index, void *infodata)
{
	uint8_t mWBuf[128] = { 0 }, mRBuf[128] = { 0 };
	uint32_t i, mLength, tmpLength;
	bool retval = false;

	i = 0;
	mWBuf[i++] = USB20_CMD_INFO_RD;
	mWBuf[i++] = 0x01;
	mWBuf[i++] = 0x00;
	mWBuf[i++] = index;
	mLength = tmpLength = i;

	if (!CH347WriteData(fd, mWBuf, &mLength) || (mLength != tmpLength))
		goto exit;

	switch (index) {
	case INFO_CHIP:
		tmpLength = USB20_CMD_HEADER + 4;
		break;
	case INFO_SPI_IIC:
		tmpLength = USB20_CMD_HEADER + 26;
		break;
	case INFO_JTAG:
		tmpLength = USB20_CMD_HEADER + 6;
		break;
	case INFO_UART1:
	case INFO_UART0:
		tmpLength = USB20_CMD_HEADER + 8;
		break;
	default:
		goto exit;
	}

	mLength = tmpLength;
	if (!CH347ReadData(fd, mRBuf, &mLength) || (mLength != tmpLength))
		goto exit;

	memcpy(infodata, &mRBuf[3], mLength - USB20_CMD_HEADER);
	retval = true;

exit:
	return retval;
}

static bool CH347GetInfo_Chip(int fd)
{
	bool retval = false;
	uint8_t data[4] = { 0 };
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	pthread_mutex_lock(&gusbch347[index].mutex);
	retval = CH347GetInfo(fd, INFO_CHIP, data);
	if (retval)
		gusbch347[index].FirmwareVer = (data[1] << 8) | data[0];
	else
		goto exit;

exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

static bool CH347GetInfo_ChipFreq(int fd)
{
	bool retval = false;
	uint8_t data[6] = { 0 };
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	retval = CH347GetInfo(fd, INFO_JTAG, data);
	if (retval)
		gusbch347[index].iClockIndex = data[2];

	return retval;
}

/**
 * libtty_setopt - config tty device
 * @fd: device handle
 * @speed: baud rate to set
 * @databits: data bits to set
 * @stopbits: stop bits to set
 * @parity: parity to set
 *
 * The function return 0 if success, or -1 if fail.
 */
static int libtty_setopt(int fd, int speed, int databits, int stopbits,
			 char parity, int timeout)
{
	struct termios newtio;
	struct termios oldtio;

	memset(&newtio, 0x00, sizeof(newtio));
	memset(&oldtio, 0x00, sizeof(oldtio));

	if (tcgetattr(fd, &oldtio) != 0) {
		perror("tcgetattr");
		return -1;
	}
	newtio.c_cflag |= CLOCAL | CREAD;
	newtio.c_cflag &= ~CSIZE;

	/* set data bits */
	switch (databits) {
	case 0:
		newtio.c_cflag |= CS5;
		break;
	case 1:
		newtio.c_cflag |= CS6;
		break;
	case 2:
		newtio.c_cflag |= CS7;
		break;
	case 3:
		newtio.c_cflag |= CS8;
		break;
	default:
		fprintf(stderr, "unsupported data size\n");
		return -1;
	}

	/* set parity */
	switch (parity) {
	case 0:
		newtio.c_cflag &= ~PARENB; /* Clear parity enable */
		newtio.c_iflag &= ~INPCK; /* Disable input parity check */
		break;
	case 1:
		newtio.c_cflag |=
			(PARODD | PARENB); /* Odd parity instead of even */
		newtio.c_iflag |= INPCK; /* Enable input parity check */
		break;
	case 2:
		newtio.c_cflag |= PARENB; /* Enable parity */
		newtio.c_cflag &= ~PARODD; /* Even parity instead of odd */
		newtio.c_iflag |= INPCK; /* Enable input parity check */
		break;
	case 3:
		newtio.c_cflag |= PARENB; /* Enable parity */
		newtio.c_cflag |= CMSPAR; /* Stick parity instead */
		newtio.c_cflag |= PARODD; /* Even parity instead of odd */
		newtio.c_iflag |= INPCK; /* Enable input parity check */
		break;
	case 4:
		newtio.c_cflag |= PARENB; /* Enable parity */
		newtio.c_cflag |= CMSPAR; /* Stick parity instead */
		newtio.c_cflag &= ~PARODD; /* Even parity instead of odd */
		newtio.c_iflag |= INPCK; /* Enable input parity check */
		break;
	default:
		fprintf(stderr, "unsupported parity\n");
		return -1;
	}

	/* set stop bits */
	switch (stopbits) {
	case 0:
		newtio.c_cflag &= ~CSTOPB;
		break;
	case 2:
		newtio.c_cflag |= CSTOPB;
		break;
	default:
		perror("unsupported stop bits\n");
		return -1;
	}

	newtio.c_cflag &= ~CRTSCTS;

	newtio.c_cc[VTIME] =
		timeout; /* Time-out value (tenths of a second) [!ICANON]. */
	newtio.c_cc[VMIN] =
		0; /* Minimum number of bytes read at once [!ICANON]. */

	tcflush(fd, TCIOFLUSH);

	if (tcsetattr(fd, TCSANOW, &newtio) != 0) {
		perror("tcsetattr");
		return -1;
	}

	/* set tty speed */
	if (libtty_setcustombaudrate(fd, speed) != 0) {
		perror("setbaudrate");
		return -1;
	}

	return 0;
}

/**
 * libtty_open - open tty device
 * @devname: the device name to open
 *
 * In this demo device is opened blocked, you could modify it at will.
 */
static int libtty_open(const char *devname)
{
	int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY);
	int flags = 0;

	if (fd < 0) {
		return -1;
	}

	flags = fcntl(fd, F_GETFL, 0);
	flags &= ~O_NONBLOCK;
	if (fcntl(fd, F_SETFL, flags) < 0) {
		return -1;
	}

	if (isatty(fd) == 0) {
		return -1;
	}

	return fd;
}

/**
 * CH347OpenDevice - open device
 * @pathname: device path in /dev directory
 *
 * The function return positive file descriptor if successful, others if fail.
 */
int CH347OpenDevice(const char *pathname)
{
	int fd;
	int index;
	int ret;
	char buf[256] = { 0 };
	struct hidraw_devinfo info = { 0 };

	if (strstr(pathname, "tty"))
		fd = libtty_open(pathname);
	else
		fd = open(pathname, O_RDWR);

	if (fd > 0) {
		index = CH347AddFd(fd);
		if (index < 0) {
			printf("No space to store new file descriptor.\n");
			return -ERR_RANGE;
		}
		if (strstr(pathname, "tty")) {
			gusbch347[index].ch347device.FuncType = TYPE_TTY;
		} else if (strstr(pathname, "hidraw")) {
			gusbch347[index].ch347device.FuncType = TYPE_HID;

			/* Get Raw Info */
			ret = ioctl(fd, HIDIOCGRAWINFO, &info);
			if (ret < 0) {
				perror("HIDIOCGRAWINFO");
				ret = -ERR_IOCTL;
				goto exit;
			} else {
				if (info.vendor == 0x1a86) {
					if (info.product == 0x55dc)
						gusbch347[index].ChipType =
							CHIP_CH347T;
					else if (info.product == 0x55e5)
						gusbch347[index].ChipType =
							CHIP_CH347F;
					else {
						printf("Current HID device PID is not CH347.\n");
						ret = -ERR_INVAL;
						goto exit;
					}
				} else {
					printf("Current HID device VID is not CH347.\n");
					ret = -ERR_INVAL;
					goto exit;
				}
			}

			/* Get Physical Location */
			ret = ioctl(fd, HIDIOCGRAWPHYS(256), buf);
			if (ret < 0) {
				perror("HIDIOCGRAWPHYS");
				ret = -ERR_IOCTL;
				goto exit;
			}
			if (gusbch347[index].ChipType == CHIP_CH347T) {
				if (strstr(buf, "input0")) {
					gusbch347[index].UartIndex = 1;
				} else if (strstr(buf, "input1")) {
					CH347GetInfo_Chip(fd);
				}
			} else {
				if (strstr(buf, "input0"))
					gusbch347[index].UartIndex = 0;
				else if (strstr(buf, "input2"))
					gusbch347[index].UartIndex = 1;
				else
					CH347GetInfo_Chip(fd);
			}
		} else if (strstr(pathname, "ch34x_pis")) {
			gusbch347[index].ch347device.FuncType = TYPE_VCP;
			ret = CH34x_GetChipType(
				fd, &gusbch347[index].ChipType);
			if (ret < 0) {
				printf("CH34x_GetChipType error.\n");
				ret = -ERR_INVAL;
				goto exit;
			}
			if (gusbch347[index].ChipType != CHIP_CH346C)
				CH347GetInfo_Chip(fd);
			fcntl(fd, F_SETOWN, getpid());
			fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
		}
	}

	return fd;

exit:
	if (ret < 0) {
		CH347DelFd(fd);
		return ret;
	}
	return fd;
}

/**
 * CH347CloseDevice - close device
 * @fd: file descriptor of device
 *
 * The function return true if successful, false if fail.
 */
bool CH347CloseDevice(int fd)
{
	if (close(fd) == 0) {
		CH347DelFd(fd);
		return true;
	}
	return false;
}

bool CH347GetDeviceInfor(int fd, mDeviceInforS *DevInformation)
{
	// TODO:
	return true;
}

/**
 * CH347ReadData - read data
 * @fd: file descriptor of device
 * @oBuffer: pointer to read buffer
 * @ioLength: pointer to read length
 *
 * The function return true if successful, false if fail.
 */
bool CH347ReadData(int fd, void *oBuffer, uint32_t *ioLength)
{
	int retval = -1;
	uint8_t obuf[4096] = { 0 };
	uint32_t total = 0;
	int packlen = 0;
	int epsize = 512;
	int fillsize = 0;
	int max_len;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	max_len = (gusbch347[index].ChipType == CHIP_CH339W) ?
			  MAX_BUFFER_LENGTH_CH339W :
			  MAX_BUFFER_LENGTH;

	if (*ioLength > max_len)
		return false;

	if (gusbch347[index].ch347device.FuncType == TYPE_TTY) {
		retval = read(fd, oBuffer, *ioLength);
		if (retval < 0)
			goto exit;
	} else if (gusbch347[index].ch347device.FuncType == TYPE_HID) {
		// if (*ioLength % (epsize - 2) == 0)
		// 	fillsize = *ioLength / (epsize - 2) * 2;
		// else
		// 	fillsize = (*ioLength / (epsize - 2) + 1) * 2;
		while (1) {
			retval = read(fd, obuf, epsize);
			if (retval < 0)
				goto exit;
			if (retval < 3)
				break;
			packlen = obuf[0] + (obuf[1] << 8);
			memcpy((uint8_t *)oBuffer + total, &obuf[2],
			       packlen);
			total += packlen;
			if (total % (epsize - 2) == 0) {
				fillsize = total / (epsize - 2) * 2;
				// printf("packlen: %d, total: %d\n", packlen, total);
				// printf("total: %d, *ioLength: %d, fillsize: %d\n", total, *ioLength, fillsize);
				if (total >= (*ioLength - fillsize))
					break;
			}
			if (packlen < (epsize - 2))
				break;
		}
		*ioLength = total;
	} else if (gusbch347[index].ch347device.FuncType == TYPE_VCP) {
		return CH34xReadData(fd, oBuffer, ioLength);
	}
	return true;

exit:
	return false;
}

/**
 * CH347WriteData - write data
 * @fd: file descriptor of device
 * @iBuffer: pointer to write buffer
 * @ioLength: pointer to write length
 *
 * The function return true if successful, false if fail.
 */
bool CH347WriteData(int fd, void *iBuffer, uint32_t *ioLength)
{
	int retval = -1;
	uint8_t ibuf[4096] = { 0 };
	uint32_t total = 0;
	int epsize = 513;
	int ilen = 0;
	int max_len;
	int index = GetDevIndex(fd);

#ifdef VERBOSE_DEBUG
	int i;
	printf("CH347WriteData-->Len: %d\n", *ioLength);
	for (i = 0; i < *ioLength; i++) {
		printf("0x%2x ", *((uint8_t *)iBuffer + i));
	}
	printf("CH347WriteData Over.\n\n");
#endif

	if (index < 0)
		return false;

	max_len = (gusbch347[index].ChipType == CHIP_CH339W) ?
			  MAX_BUFFER_LENGTH_CH339W :
			  MAX_BUFFER_LENGTH;

	if (*ioLength > max_len)
		return false;

	if (gusbch347[index].ch347device.FuncType == TYPE_TTY) {
		retval = write(fd, iBuffer, *ioLength);
		if (retval != *ioLength)
			goto exit;
	} else if (gusbch347[index].ch347device.FuncType == TYPE_HID) {
		while (1) {
			if (total >= *ioLength)
				break;
			/* need more than 1 packet */
			if ((total + epsize - 3) < *ioLength) {
				ilen = epsize - 3;
				ibuf[0] = 0x00;
				ibuf[1] = (uint8_t)ilen;
				ibuf[2] = (uint8_t)(ilen >> 8);
				memcpy(&ibuf[3],
				       (uint8_t *)iBuffer + total, ilen);
			} else {
				ilen = *ioLength - total;
				ibuf[0] = 0x00;
				ibuf[1] = (uint8_t)ilen;
				ibuf[2] = (uint8_t)(ilen >> 8);

				// printf("*ioLength: %d, ilen: %d, ibuf0: 0x%2x, ibuf1: 0x%2x\n", *ioLength, ilen,
				//        ibuf[0], ibuf[1]);

				memcpy(&ibuf[3],
				       (uint8_t *)iBuffer + total, ilen);
				memset(ibuf + ilen + 3, 0, epsize - ilen);
			}
			retval = write(fd, ibuf, epsize);
			if (retval != epsize)
				goto exit;
			total += ilen;
		}
		*ioLength = total;
	} else if (gusbch347[index].ch347device.FuncType == TYPE_VCP) {
		return CH34xWriteData(fd, iBuffer, ioLength);
	}
	return true;

exit:
	return false;
}

/**
 * CH347WriteRead - write data then read
 * @fd: file descriptor of device
 * @iWriteLength: write length
 * @iWriteBuffer: pointer to write buffer
 * @iReadStep: per read length
 * @iReadTimes: read times
 * @oReadLength: pointer to read length
 * @oReadBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347WriteRead(int fd, int iWriteLength, void *iWriteBuffer,
		    int iReadStep, int iReadTimes, uint32_t *oReadLength,
		    void *oReadBuffer)
{
	bool retval = false;
	uint32_t RI, rlen, i;
	uint32_t ilen = (uint32_t)iWriteLength;

	retval = CH347WriteData(fd, iWriteBuffer, &ilen);
	if (retval) {
		RI = 0;
		for (i = 0; i < iReadTimes; i++) {
			rlen = iReadStep;
			retval = CH347ReadData(
				fd, (uint8_t *)oReadBuffer + RI, &rlen);
			if (retval == false)
				goto exit;
			RI += rlen;
		}
		*oReadLength = RI;
	}

exit:
	return retval;
}

/**
 * CH347SetTimeout - set USB data read and write timeout
 * @fd: file descriptor of device
 * @iWriteTimeout: data download timeout in milliseconds
 * @iReadTimeout: data upload timeout in milliseconds
 *
 * The function return true if successful, false if fail.
 */
bool CH347SetTimeout(int fd, uint32_t iWriteTimeout, uint32_t iReadTimeout)
{
	return CH34xSetTimeout(fd, iWriteTimeout, iReadTimeout);
}

void HwCfg_Host_To_LittleEndian(StreamHwCfgS *HwCfg)
{
	HwCfg->SpiWriteReadInterval = htole16(HwCfg->SpiWriteReadInterval);
	HwCfg->SPIInitCfg.SPI_Direction =
		htole16(HwCfg->SPIInitCfg.SPI_Direction);
	HwCfg->SPIInitCfg.SPI_Mode = htole16(HwCfg->SPIInitCfg.SPI_Mode);
	HwCfg->SPIInitCfg.SPI_DataSize =
		htole16(HwCfg->SPIInitCfg.SPI_DataSize);
	HwCfg->SPIInitCfg.SPI_CPOL = htole16(HwCfg->SPIInitCfg.SPI_CPOL);
	HwCfg->SPIInitCfg.SPI_CPHA = htole16(HwCfg->SPIInitCfg.SPI_CPHA);
	HwCfg->SPIInitCfg.SPI_NSS = htole16(HwCfg->SPIInitCfg.SPI_NSS);
	HwCfg->SPIInitCfg.SPI_BaudRatePrescaler =
		htole16(HwCfg->SPIInitCfg.SPI_BaudRatePrescaler);
	HwCfg->SPIInitCfg.SPI_FirstBit =
		htole16(HwCfg->SPIInitCfg.SPI_FirstBit);
	HwCfg->SPIInitCfg.SPI_CRCPolynomial =
		htole16(HwCfg->SPIInitCfg.SPI_CRCPolynomial);
}

void HwCfg_LittleEndian_To_Host(StreamHwCfgS *HwCfg)
{
	HwCfg->SpiWriteReadInterval = le16toh(HwCfg->SpiWriteReadInterval);
	HwCfg->SPIInitCfg.SPI_Direction =
		le16toh(HwCfg->SPIInitCfg.SPI_Direction);
	HwCfg->SPIInitCfg.SPI_Mode = le16toh(HwCfg->SPIInitCfg.SPI_Mode);
	HwCfg->SPIInitCfg.SPI_DataSize =
		le16toh(HwCfg->SPIInitCfg.SPI_DataSize);
	HwCfg->SPIInitCfg.SPI_CPOL = le16toh(HwCfg->SPIInitCfg.SPI_CPOL);
	HwCfg->SPIInitCfg.SPI_CPHA = le16toh(HwCfg->SPIInitCfg.SPI_CPHA);
	HwCfg->SPIInitCfg.SPI_NSS = le16toh(HwCfg->SPIInitCfg.SPI_NSS);
	HwCfg->SPIInitCfg.SPI_BaudRatePrescaler =
		le16toh(HwCfg->SPIInitCfg.SPI_BaudRatePrescaler);
	HwCfg->SPIInitCfg.SPI_FirstBit =
		le16toh(HwCfg->SPIInitCfg.SPI_FirstBit);
	HwCfg->SPIInitCfg.SPI_CRCPolynomial =
		le16toh(HwCfg->SPIInitCfg.SPI_CRCPolynomial);
}

/**
 * CH347SPI_GetHwStreamCfg - get spi setting from hardware
 * @fd: file descriptor of device
 * @StreamCfg: pointer to SPI stream configuration
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_GetHwStreamCfg(int fd, StreamHwCfgS *StreamCfg)
{
	uint8_t mBuffer[CH347_PACKET_LENGTH] = { 0 };
	uint32_t mLength, i, RetLen;
	bool retval = false;

	i = 0;
	mBuffer[i++] = USB20_CMD_INFO_RD;
	mBuffer[i++] = 1; // 后续长度,小端模式
	mBuffer[i++] = 0; // 参数类型
	mBuffer[i++] = 1;
	mLength = i;

	if (!CH347WriteData(fd, mBuffer, &mLength))
		goto exit;

	mLength = RetLen = 26 + 3;

	if (!CH347ReadData(fd, mBuffer, &mLength) || (mLength != RetLen))
		goto exit;

	if (StreamCfg)
		memcpy(StreamCfg, &mBuffer[3], RetLen - 3);

	HwCfg_LittleEndian_To_Host(StreamCfg);

	retval = true;

exit:
	return retval;
}

/**
 * CH347SPI_ClockInit - System Clock Initialization
 */
bool CH347SPI_ClockInit(int fd, int index)
{
	uint8_t mBuffer[CH347_PACKET_LENGTH] = { 0 };
	uint32_t mLength, i, RetLen;
	bool retval = false;

	i = 0;
	mBuffer[i++] = USB20_CMD_SPI_CLK_INIT;
	mBuffer[i++] = 1; // 后续长度,小端模式
	mBuffer[i++] = 0; // 参数类型
	mBuffer[i++] = index;
	mLength = i;

	if (!CH347WriteData(fd, mBuffer, &mLength))
		goto exit;

	mLength = RetLen = i;

	if (!CH347ReadData(fd, mBuffer, &mLength) || (mLength != RetLen) ||
	    (mBuffer[USB20_CMD_HEADER] != 0))
		goto exit;

	retval = true;
exit:
	return retval;
}

/**
 * CH347_FUNC_SWITCH - Switch functions of CH347F
 * @index: 0->SPI CS1 enable, 1->JTAG SRST enable 3->I2C enable, 4->OE enable
 */
bool CH347_FUNC_SWITCH(int fd, int index)
{
	uint8_t mBuffer[CH347_PACKET_LENGTH] = { 0 };
	uint32_t mLength, i, RetLen;
	bool retval = false;

	i = 0;
	mBuffer[i++] = USB20_CMD_FUNC_SWITCH;
	mBuffer[i++] = 8; // 后续长度,小端模式
	mBuffer[i++] = 0; // 参数类型
	switch (index) {
	case 0:
		mBuffer[USB20_CMD_HEADER] = 0x81;
		break;
	case 1:
		mBuffer[USB20_CMD_HEADER + 1] = 0x81;
		break;
	case 3:
		mBuffer[USB20_CMD_HEADER + 2] = 0x81;
		mBuffer[USB20_CMD_HEADER + 3] = 0x81;
		break;
	case 4:
		mBuffer[USB20_CMD_HEADER + 4] = 0x81;
		break;
	default:
		break;
	}

	mLength = USB20_CMD_HEADER + 8;

	if (!CH347WriteData(fd, mBuffer, &mLength))
		goto exit;

	mLength = RetLen = 4;

	if (CH347ReadData(fd, mBuffer, &mLength)) {
		if (mLength == RetLen) {
			if ((mBuffer[0] = USB20_CMD_FUNC_SWITCH) &&
			    (mBuffer[USB20_CMD_HEADER] == 0))
				retval = true;
		}
	}
exit:
	return retval;
}

/**
 * CH347_OE_Enable - CH347F chip OE switch
 * @fd: file descriptor of device
 *
 * The function return true if successful, false if fail.
 */
bool CH347_OE_Enable(int fd)
{
	mDeviceInforS *DevObj = NULL;
	int index;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	if ((gusbch347[index].FirmwareVer >= 0x0100) &&
	    (gusbch347[index].ChipType == CHIP_CH347F))
		return CH347_FUNC_SWITCH(fd, 4);

	return false;
}

/**
 * UserSpiCfgToHwConfig - update spi setting from user
 * @UserCfg: pointer to user configuration
 * @HwCfg: pointer to hardware configuration
 *
 * The function return true if successful, false if fail.
 */
bool UserSpiCfgToHwConfig(int index, mSpiCfgS *UserCfg,
			  StreamHwCfgS *HwCfg, bool NewVersion,
			  uint8_t *iClockIndex)
{
	int i, j;
	uint8_t scale;
	bool speedset = true;
	int clk_table0[] = { 72e6,   60e6,   48e6,   36e6,    30e6,
			     28e6,   24e6,   18e6,   15e6,    14e6,
			     12e6,   9e6,    75e5,   7e6,     6e6,
			     45e5,   375e4,  35e5,   3e6,     225e4,
			     1875e3, 175e4,  15e5,   1125e3,  9375e2,
			     875e3,  750e3,  5625e2, 46875e1, 4375e2,
			     375e3,  21875e1 };
	int clk_table1[] = {
		28e6,  14e6,   7e6,    35e5,
		175e4, 875e3,  4375e2, 21875e1, // clock index = 1
		72e6,  36e6,   18e6,   9e6,
		45e5,  225e4,  1125e3, 5625e2, // clock index = 5
		48e6,  24e6,   12e6,   6e6,
		3e6,   15e5,   750e3,  375e3, // clock index = 3
		60e6,  30e6,   15e6,   75e5,
		375e4, 1875e3, 9375e2, 46875e1, // clock index = 4
	};
	int clk_table2[] = { 60e6,  30e6,   15e6,   75e5,
			     375e4, 1875e3, 9375e2, 46875e1 };
	uint8_t clockindex;

	HwCfg->SPIInitCfg.SPI_Mode = SPI_Mode_Master;

	if ((UserCfg->iMode & 0x0F) > 3)
		return false;

	// if (UserCfg->iClock > 7)
	// 	return false;

	if (gusbch347[index].iSpiSpeedHz == 0) {
		speedset = false;
	} else if ((gusbch347[index].iSpiSpeedHz > CH347_SPI_MAX_FREQ) ||
		   (gusbch347[index].iSpiSpeedHz < CH347_SPI_MIN_FREQ))
		return false;

	UserCfg->iByteOrder &= 0x01;
	// 0=低位在前(LSB), 1=高位在前(MSB)
	HwCfg->SPIInitCfg.SPI_FirstBit = UserCfg->iByteOrder ? 0x00 : 0x80;
	// 时钟极性CPOL: 即SPI空闲时，时钟信号SCLK的电平（1:空闲时高电平; 0:空闲时低电平）
	// 时钟相位CPHA: 即SPI在SCLK第几个边沿开始采样（0:第一个边沿开始; 1:第二个边沿开始）
	switch (UserCfg->iMode & 0x0F) { // iMode0-3:SPI Mode0/1/2/3
	case 0: // mode0:CPOL=0, CPHA=0
		HwCfg->SPIInitCfg.SPI_CPHA = SPI_CPHA_1Edge;
		HwCfg->SPIInitCfg.SPI_CPOL = SPI_CPOL_Low;
		break;
	case 1: // mode1:CPOL=0, CPHA=1
		HwCfg->SPIInitCfg.SPI_CPHA = SPI_CPHA_2Edge;
		HwCfg->SPIInitCfg.SPI_CPOL = SPI_CPOL_Low;
		break;
	case 2: // mode2:CPOL=1, CPHA=0
		HwCfg->SPIInitCfg.SPI_CPHA = SPI_CPHA_1Edge;
		HwCfg->SPIInitCfg.SPI_CPOL = SPI_CPOL_High;
		break;
	case 3: // mode3:CPOL=1, CPHA=1
		HwCfg->SPIInitCfg.SPI_CPHA = SPI_CPHA_2Edge;
		HwCfg->SPIInitCfg.SPI_CPOL = SPI_CPOL_High;
		break;
	default: // default=mode3
		HwCfg->SPIInitCfg.SPI_CPHA = SPI_CPHA_2Edge;
		HwCfg->SPIInitCfg.SPI_CPOL = SPI_CPOL_High;
		break;
	}
	HwCfg->SPIInitCfg.SPI_DataSize =
		gusbch347[index].iDataBits ? 0x0800 : 0x0000;

	if (speedset) {
		if (NewVersion) {
			for (i = 0; i < sizeof(clk_table0) / sizeof(int);
			     i++) {
				if (clk_table0[i] <=
				    gusbch347[index].iSpiSpeedHz) {
					for (j = 0;
					     j < sizeof(clk_table1) /
							 sizeof(int);
					     j++) {
						if (clk_table0[i] ==
						    clk_table1[j])
							break;
					}
					clockindex = j / 8 + 1;
					scale = clk_table1[j] /
						clk_table1[clockindex * 8 -
							   1];
					/* use 144MHz instead of 72MHz clock */
					if (clockindex == 2)
						clockindex = 5;
					UserCfg->iClock = 7;
					while (scale / 2) {
						UserCfg->iClock--;
						scale /= 2;
					}
					// printf("clock freq: %d(index = %d), gusbch347[index].iSpiSpeedHz: %d, UserCfg->iClock: %d\n",
					//        clk_table1[j], clockindex, gusbch347[index].iSpiSpeedHz,
					//        UserCfg->iClock);
					break;
				}
			}
		} else {
			for (i = 0; i < sizeof(clk_table2) / sizeof(int);
			     i++) {
				if (clk_table2[i] <=
				    gusbch347[index].iSpiSpeedHz) {
					scale = clk_table2[i] /
						clk_table2[7];
					UserCfg->iClock = 7;
					while (scale / 2) {
						UserCfg->iClock--;
						scale /= 2;
					}
					// printf("clock freq: %d, gusbch347[index].iSpiSpeedHz: %d, UserCfg->iClock: %d\n",
					//        clk_table2[i], gusbch347[index].iSpiSpeedHz, UserCfg->iClock);
					break;
				}
			}
		}
	} else {
		clockindex = 4;
	}

	*iClockIndex = clockindex;

	HwCfg->SPIInitCfg.SPI_BaudRatePrescaler = UserCfg->iClock * 8;
	HwCfg->SpiOutDefaultData = UserCfg->iSpiOutDefaultData;
	HwCfg->SpiWriteReadInterval = UserCfg->iSpiWriteReadInterval;

	if (UserCfg->CS1Polarity)
		HwCfg->OtherCfg |= 0x80;
	else
		HwCfg->OtherCfg &= ~0x80;

	if (UserCfg->CS2Polarity)
		HwCfg->OtherCfg |= 0x40;
	else
		HwCfg->OtherCfg &= ~0x40;

	HwCfg_Host_To_LittleEndian(HwCfg);

	return true;
}

/**
 * HwConfigToUserSpiCfg - read spi setting from hardware
 * @UserCfg: pointer to user configuration
 * @HwCfg: pointer to hardware configuration
 *
 * The function return true if successful, false if fail.
 */
bool HwConfigToUserSpiCfg(mSpiCfgS *UserCfg, StreamHwCfgS *HwCfg)
{
	// UserCfg->iByteOrder &= 0x01;
	UserCfg->iByteOrder =
		(HwCfg->SPIInitCfg.SPI_FirstBit & 0x80) ?
			0x00 :
			0x01; // 0=低位在前(LSB), 1=高位在前(MSB)
	if ((HwCfg->SPIInitCfg.SPI_CPHA == SPI_CPHA_1Edge) &&
	    (HwCfg->SPIInitCfg.SPI_CPOL == SPI_CPOL_Low))
		UserCfg->iMode = 0;
	else if ((HwCfg->SPIInitCfg.SPI_CPHA == SPI_CPHA_2Edge) &&
		 (HwCfg->SPIInitCfg.SPI_CPOL == SPI_CPOL_Low))
		UserCfg->iMode = 1;
	else if ((HwCfg->SPIInitCfg.SPI_CPHA == SPI_CPHA_1Edge) &&
		 (HwCfg->SPIInitCfg.SPI_CPOL == SPI_CPOL_High))
		UserCfg->iMode = 2;
	else // if((HwCfg->SPIInitCfg.SPI_CPHA == SPI_CPHA_2Edge) && (HwCfg->SPIInitCfg.SPI_CPOL =SPI_CPOL_High))
		UserCfg->iMode = 3;

	// 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz，7=468.75KHz
	UserCfg->iClock = HwCfg->SPIInitCfg.SPI_BaudRatePrescaler / 8;
	UserCfg->iSpiOutDefaultData = HwCfg->SpiOutDefaultData;
	UserCfg->iSpiWriteReadInterval = HwCfg->SpiWriteReadInterval;

	UserCfg->CS1Polarity = (HwCfg->OtherCfg & 0x80) > 0;
	UserCfg->CS2Polarity = (HwCfg->OtherCfg & 0x40) > 0;

	return true;
}

/**
 * Note: SPI frequency value
 *     72e6, 60e6,   48e6,   36e6,  30e6,  28e6,   24e6,    18e6,   15e6,  14e6,    12e6,   9e6,
 *     75e5,   7e6,    6e6,   45e5,  375e4,  35e5,    3e6,    225e4, 1875e3,  175e4,  15e5,
 *     1125e3, 9375e2, 875e3, 750e3, 5625e2, 46875e1, 4375e2, 375e3, 21875e1
 */

/**
 * CH347SPI_SetFrequency - SPI frequency setting
 * @fd: file descriptor of device
 * @iSpiSpeedHz: SPI frequency value, 60e6/30e6/15e6/75e5/375e4/1875e3/9375e2/46875e1
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_SetFrequency(int fd, uint32_t iSpiSpeedHz)
{
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	gusbch347[index].iSpiSpeedHz = iSpiSpeedHz;

	return true;
}

/**
 * CH347SPI_SetAutoCS - SPI auto chipselect setting for CH347SPI_WriteRead
 * @fd: file descriptor of device
 * @disable: SPI auto chipselect setting switch, true on disable CS automatic control
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_SetAutoCS(int fd, bool disable)
{
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	gusbch347[index].deAutoCS = disable;

	return true;
}

/**
 * CH347SPI_SetDataBits - SPI data bits setting
 * @fd: file descriptor of device
 * @iDataBits: 0: 8bit, 1: 16bit 
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_SetDataBits(int fd, uint8_t iDataBits)
{
	int index = GetDevIndex(fd);

	if (index < 0 || gusbch347[index].ChipType == CHIP_CH339W)
		return false;

	gusbch347[index].iDataBits = iDataBits;

	return true;
}

/**
 * CH347SPI_Init - SPI interface initialization
 * @fd: file descriptor of device
 * @SpiCfg: pointer to SPI configuration, SPI frequency could be set by SpiCfg->iClock or CH347SPI_SetFrequency API,
 *          the latter is preferred
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_Init(int fd, mSpiCfgS *SpiCfg)
{
	uint8_t mBuffer[CH347_PACKET_LENGTH] = { 0 }, mRetBuf[512] = { 0 };
	uint32_t mLength, RetLen, i;
	StreamHwCfgS HwCfg = { 0 };
	mDeviceInforS *DevObj = NULL;
	bool NewVersion = false;
	uint8_t iClockIndex; /* 1: 56MHz, 3: 96MHz, 4: 120MHz, 5: 144MHz */
	int index;
	bool retval = false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	if (gusbch347[index].ChipType == CHIP_CH339W) {
		if ((SpiCfg->iChipSelect & 0xFF00)) {
			printf("This chip has only 1-channel spi cs.\n");
			return false;
		}
	}

	DevObj->CmdDataMaxSize = 507;
	DevObj->BulkInEndpMaxSize = 512;
	DevObj->BulkOutEndpMaxSize = 512;

	pthread_mutex_lock(&gusbch347[index].mutex);

	if (!CH347SPI_GetHwStreamCfg(fd, &HwCfg)) {
		retval = false;
		goto exit;
	}

	if ((gusbch347[index].FirmwareVer >= 0x0341) ||
	    (gusbch347[index].ChipType == CHIP_CH347F))
		NewVersion = true;

	if (gusbch347[index].ChipType == CHIP_CH347F) {
		if ((SpiCfg->iChipSelect & 0xFF00)) {
			if (CH347F_SPI_CS2_Enable(fd) == false) {
				retval = false;
				goto exit;
			}
		}
	}

	// HwCfg 还未进行大小端转换
	// UserCfg 的配置更新到 HwCfg

	if (!UserSpiCfgToHwConfig(index, SpiCfg, &HwCfg, NewVersion,
				  &iClockIndex)) {
		retval = false;
		goto exit;
	}

	if (NewVersion) {
		if (gusbch347[index].ChipType == CHIP_CH347F) {
			if (CH347GetInfo_ChipFreq(fd)) {
				if (gusbch347[index].iClockIndex ==
				    iClockIndex)
					goto ignore_freq;
			} else {
				retval = false;
				goto exit;
			}
		}
		if (!CH347SPI_ClockInit(fd, iClockIndex)) {
			retval = false;
			goto exit;
		}
	}

ignore_freq:
	i = 0;
	mBuffer[i++] = USB20_CMD_SPI_INIT;
	mLength = sizeof(StreamHwCfgS);
	mBuffer[i++] = (uint8_t)(mLength & 0xFF);
	mBuffer[i++] = (uint8_t)((mLength >> 8) & 0xFF);
	memcpy(&mBuffer[i], &HwCfg, mLength);
	mLength += i;

	if (CH347WriteData(fd, mBuffer, &mLength)) { // 写出数据块
		mLength = RetLen = USB20_CMD_HEADER + 1;
		if (CH347ReadData(fd, mRetBuf, &mLength)) {
			if (RetLen != mLength) {
				retval = false;
				goto exit;
			}
			if (mRetBuf[0] != USB20_CMD_SPI_INIT) {
				retval = false;
				goto exit;
			}
			if (mRetBuf[USB20_CMD_HEADER] == 0) {
				memcpy(&DevObj->dllUserSpiCfg, SpiCfg,
				       sizeof(mSpiCfgS));
				// memcpy(&DevObj->dllHwCfg, &HwCfg, sizeof(StreamHwCfgS));
				retval = true;
				goto exit;
			}
		}
	}
exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347SPI_GetCfg - get SPI configuration
 * @fd: file descriptor of device
 * @SpiCfg: pointer to SPI configuration
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_GetCfg(int fd, mSpiCfgS *SpiCfg)
{
	StreamHwCfgS HwCfg = { 0 };
	mSpiCfgS UserCfg = { 0 };
	int index = GetDevIndex(fd);
	bool retval = false;

	if (index < 0)
		return false;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (!CH347SPI_GetHwStreamCfg(fd, &HwCfg)) {
		retval = false;
		goto exit;
	}

	if (!HwConfigToUserSpiCfg(&UserCfg, &HwCfg)) {
		retval = false;
		goto exit;
	}
	// 保存用户和硬件配置信息
	memcpy(&gusbch347[index].ch347device.dllUserSpiCfg, &UserCfg,
	       sizeof(mSpiCfgS));
	// memcpy(&gusbch347[index].ch347device.dllHwCfg, &HwCfg, sizeof(StreamHwCfgS));
	memcpy(SpiCfg, &UserCfg, sizeof(mSpiCfgS)); // 返回配置信息

	retval = true;

exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347SPI_SetChipSelect - SPI chip selection initialization
 * @fd: file descriptor of device
 * @iEnableSelect: low 8 bits: CS1, high 8 bits: CS2, byte value -> 1: set CS, 0: ignore CS setting
 * @iChipSelect: low 8 bits: CS1, high 8 bits: CS2, CS output, byte value -> 1: set CS, 0: cancel CS
 * @iIsAutoDeativeCS: low 16 bits: CS1, high 16 bits: CS2, automatically undo the CS after operation completed
 * @iActiveDelay: low 16 bits: CS1, high 16 bits: CS2, delay time of read and write operation after setting CS, unit: us
 * @iDelayDeactive: low 16 bits: CS1, high 16 bits: CS2,, delay time of read and write operation after canceling CS, unit: us
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_SetChipSelect(int fd, uint16_t iEnableSelect,
			    uint16_t iChipSelect, int iIsAutoDeativeCS,
			    int iActiveDelay, int iDelayDeactive)
{
	uint8_t mBuffer[CH347_PACKET_LENGTH] = { 0 };
	uint32_t mLength, i, RetLen;
	int index = GetDevIndex(fd);
	bool retval = false;

	if (index < 0)
		return false;

	if ((iEnableSelect & 0x0101) == 0) // 未选择CS，不设置
		return true;

	i = 0;
	mBuffer[i++] = USB20_CMD_SPI_CONTROL;
	i += 2; // 两字节长度最后赋值
	/* CS1 Setting */
	if ((iEnableSelect & 0xFF))
		mBuffer[i] |= 0x80; // 启用CS1
	else
		mBuffer[i] &= 0x7F; // 忽略CS1设置
	if ((iChipSelect & 0x01) == SET_CS) {
		mBuffer[i] &= 0xBF; // 置CS1有效
	} else
		mBuffer[i] |= 0x40; // 置CS1无效
	if ((iIsAutoDeativeCS & 0xFF))
		mBuffer[i] |= 0x20; // 操作完成后自动撤消片选
	else
		mBuffer[i] &= 0xDF; // 手动撤消片选
	i++;
	mBuffer[i++] = (uint8_t)(iActiveDelay &
				 0xFF); // 设置片选后执行读写操作的延时时间
	mBuffer[i++] = (uint8_t)((iActiveDelay >> 8) & 0xFF);
	mBuffer[i++] = (uint8_t)(iDelayDeactive &
				 0xFF); // 撤消片选后执行读写操作的延时时间
	mBuffer[i++] = (uint8_t)((iDelayDeactive >> 8) & 0xFF);

	/* CS2 Setting */
	if ((iEnableSelect & 0xFF00))
		mBuffer[i] |= 0x80; // 启用CS2
	else
		mBuffer[i] &= 0x7F; // 忽略CS2设置
	if ((iChipSelect & 0x0100) == SET_CS) {
		mBuffer[i] &= 0xBF; // 置CS2有效
	} else
		mBuffer[i] |= 0x40; // 置CS2无效
	if ((iIsAutoDeativeCS & 0xFF00))
		mBuffer[i] |= 0x20; // 操作完成后自动撤消片选
	else
		mBuffer[i] &= 0xDF; // 手动撤消片选
	i++;
	mBuffer[i++] = (uint8_t)((iActiveDelay >> 16) &
				 0xFF); // 设置片选后执行读写操作的延时时间
	mBuffer[i++] = (uint8_t)((iActiveDelay >> 24) & 0xFF);
	mBuffer[i++] = (uint8_t)((iDelayDeactive >> 16) &
				 0xFF); // 撤消片选后执行读写操作的延时时间
	mBuffer[i++] = (uint8_t)((iDelayDeactive >> 24) & 0xFF);

	mBuffer[1] = (uint8_t)((i - 3) & 0xFF);
	mBuffer[2] = (uint8_t)(((i - 3) >> 8) & 0xFF);

	RetLen = mLength = i;

	if (!CH347WriteData(fd, mBuffer, &mLength) || (RetLen != mLength))
		retval = false;
	else
		retval = true;

	return retval;
}

/**
 * CH347SPI_ChangeCS - SPI CS setting, must call CH347SPI_Init first
 * @fd: file descriptor of device
 * @iStatus: 0: set CS, 1: cancle CS
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_ChangeCS(int fd, uint8_t iStatus)
{
	uint16_t iEnableCS; // 低八位为CS1，高八位为CS2; 字节值为1=设置CS,为0=忽略此CS设置
	uint16_t iChipSelect; // 低八位为CS1，高八位为CS2;片选输出,0=撤消片选,1=设置片选
	int iIsAutoDeativeCS; // 低16位为CS1，高16位为CS2;操作完成后是否自动撤消片选
	int iActiveDelay; // 低16位为CS1，高16位为CS2;设置片选后执行读写操作的延时时间,单位us
	int iDelayDeactive;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	iIsAutoDeativeCS =
		gusbch347[index].ch347device.dllUserSpiCfg.iIsAutoDeativeCS;
	iActiveDelay =
		gusbch347[index].ch347device.dllUserSpiCfg.iActiveDelay;
	iDelayDeactive =
		gusbch347[index].ch347device.dllUserSpiCfg.iDelayDeactive;
	iChipSelect = iStatus;

	if ((gusbch347[index].ch347device.dllUserSpiCfg.iChipSelect &
	     0x8000) &&
	    (gusbch347[index].ChipType == CHIP_CH339W))
		return false;

	if (gusbch347[index].ch347device.dllUserSpiCfg.iChipSelect &
	    0x8000) {
		iEnableCS = 0x0100;
		iChipSelect = (iChipSelect << 8) & 0xFF00;
		iIsAutoDeativeCS = (iIsAutoDeativeCS << 8) & 0xFF00;
		iActiveDelay = (iActiveDelay << 16) & 0xFFFF0000;
		iDelayDeactive = (iDelayDeactive << 16) & 0xFFFF0000;
	} else // if (gusbch347[index].ch347device.dllUserSpiCfg.iChipSelect & 0x80)
		iEnableCS = 0x01;

	return CH347SPI_SetChipSelect(fd, iEnableCS, iChipSelect,
				      iIsAutoDeativeCS, iActiveDelay,
				      iDelayDeactive);
}

/**
 * CH347F_SPI_CS2_Enable - CH347F SPI CS2 switch
 * @fd: file descriptor of device
 *
 * The function return true if successful, false if fail.
 */
bool CH347F_SPI_CS2_Enable(int fd)
{
	if (!CH347_FUNC_SWITCH(fd, 0))
		return false;

	return true;
}

/**
 * CH347SPI_Write - write SPI data
 * @fd: file descriptor of device
 * @ignoreCS: ignore SPI chip select while true, else auto set CS
 * @iChipSelect: SPI chip select, ignore while BIT7 is 0, valid while BIT7 is 1
 * @iLength: length to write
 * @iWriteStep: per write length
 * @ioBuffer: pointer to write buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_Write(int fd, bool ignoreCS, int iChipSelect, int iLength,
		    int iWriteStep, void *ioBuffer)
{
	uint8_t mBuffer[8192] = { 0 };
	uint32_t i, mLength, ilen, WI;
	uint8_t *mWrBuf;
	uint8_t *pBuf;
	bool retval = false;
	int index = GetDevIndex(fd);
	int max_len;
	int fill_len = 0;

	if (index < 0)
		return false;

	max_len = (gusbch347[index].ChipType == CHIP_CH339W) ?
			  MAX_BUFFER_LENGTH_CH339W :
			  MAX_BUFFER_LENGTH;

	if (iWriteStep > max_len)
		goto exit;
	else if (iWriteStep > 507)
		iWriteStep = 507;

	mWrBuf = (uint8_t *)mBuffer;
	i = 0;

	pthread_mutex_lock(&gusbch347[index].mutex);

	if (!ignoreCS) {
		if (!CH347SPI_ChangeCS(fd, iChipSelect))
			goto exit;
	}

	if (((gusbch347[index].ChipType == CHIP_CH347F) ||
	     (gusbch347[index].FirmwareVer >= 0x0341)) &&
	    (gusbch347[index].iDataBits))
		fill_len = 0x01;

	WI = 0;
	while (WI < iLength) {
		if ((WI + iWriteStep) > iLength)
			ilen = iLength - WI;
		else
			ilen = iWriteStep;

		pBuf = (uint8_t *)ioBuffer + WI;
		i = 0;

		mWrBuf[i++] = USB20_CMD_SPI_BLCK_WR;
		mWrBuf[i++] = (uint8_t)((ilen >> 0) & 0xFF);
		mWrBuf[i++] = (uint8_t)((ilen >> 8) & 0xFF);
		if (fill_len)
			mWrBuf[i++] = 0x00;
		memcpy(&mWrBuf[i], pBuf, ilen);
		i += ilen;
		mLength = i;
		if (!CH347WriteData(fd, mWrBuf, &mLength))
			goto exit;
		if (mLength != i)
			goto exit;
		mLength = 64;
		if (!CH347ReadData(fd, mWrBuf, &mLength))
			goto exit;
		WI += ilen;
	}
	retval = true;

exit:
	if (!ignoreCS)
		CH347SPI_ChangeCS(fd, (iChipSelect >> 7));

	pthread_mutex_unlock(&gusbch347[index].mutex);

	return retval;
}

/**
 * CH347SPI_Read - read SPI data
 * @fd: file descriptor of device
 * @ignoreCS: ignore SPI chip select while true, else auto set CS
 * @iChipSelect: SPI chip select, ignore while BIT7 is 0, valid while BIT7 is 1
 * @iLength: length to write
 * @oLength: pointer to read length
 * @ioBuffer: pointer to buffer, store data to be written from MOSI, and return data to be read from MISO
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_Read(int fd, bool ignoreCS, int iChipSelect, int iLength,
		   uint32_t *oLength, void *ioBuffer)
{
	uint8_t mBuffer[8192] = { 0 };
	int i;
	uint32_t mLength, RI;
	uint8_t *mWrBuf;
	uint8_t *pBuf;
	// int iReadStep;	// 准备读取的单个块的长度, 准备读取的总长度为(iReadStep*iReadTimes)
	int iReadTimes; // 准备读取的次数
	bool retval = false;
	int index = GetDevIndex(fd);
	int fill_len = 0;

	if (index < 0)
		return false;

	mWrBuf = (uint8_t *)mBuffer;
	pthread_mutex_lock(&gusbch347[index].mutex);
	if (!ignoreCS) {
		if (!CH347SPI_ChangeCS(fd, iChipSelect))
			goto exit;
	}

	if (((gusbch347[index].ChipType == CHIP_CH347F) ||
	     (gusbch347[index].FirmwareVer >= 0x0341)) &&
	    (gusbch347[index].iDataBits))
		fill_len = 0x01;

	if (iLength) {
		i = 0;
		mWrBuf[i++] = USB20_CMD_SPI_RD_WR;
		mWrBuf[i++] = (uint8_t)(iLength & 0xFF);
		mWrBuf[i++] = (uint8_t)((iLength >> 8) & 0xFF);
		if (fill_len)
			mWrBuf[i++] = 0x00;
		mLength = iLength;

		memcpy(&mWrBuf[i], ioBuffer, iLength);
		mLength = i + iLength;
		if (!CH347WriteData(fd, mWrBuf, &mLength))
			goto exit;
		if (mLength != (i + iLength))
			goto exit;

		mLength = i + iLength;
		if (!CH347ReadData(fd, mWrBuf, &mLength))
			goto exit;
		if (mWrBuf[0] != USB20_CMD_SPI_RD_WR)
			goto exit;
		if (mLength != (i + iLength))
			goto exit;
	}
	/* no data to read */
	if (*oLength < 1) {
		retval = true;
		goto exit;
	}
	i = 0;
	mWrBuf[i++] = USB20_CMD_SPI_BLCK_RD;
	mWrBuf[i++] = 0x04;
	mWrBuf[i++] = 0x00;
	mWrBuf[i++] = (uint8_t)((*oLength >> 0) & 0xFF);
	mWrBuf[i++] = (uint8_t)((*oLength >> 8) & 0xFF);
	mWrBuf[i++] = (uint8_t)((*oLength >> 16) & 0xFF);
	mWrBuf[i++] = (uint8_t)((*oLength >> 24) & 0xFF);
	mLength = i;

	if (!CH347WriteData(fd, mWrBuf, &mLength))
		goto exit;
	if (mLength != i)
		goto exit;

	// iReadStep = gusbch347[index].ch347device.CmdDataMaxSize; // 分全速和高速
	RI = 0;
	iReadTimes = (*oLength +
		      gusbch347[index].ch347device.CmdDataMaxSize - 1) /
		     gusbch347[index].ch347device.CmdDataMaxSize;

	for (i = 0; i < iReadTimes; i++) {
		mLength = gusbch347[index].ch347device.CmdDataMaxSize + 3;
		memset(mWrBuf, 0xFF, mLength + 5);
		pBuf = (uint8_t *)ioBuffer + RI;

		if (!CH347ReadData(fd, mWrBuf, &mLength))
			goto exit;
		if (mLength < USB20_CMD_HEADER + fill_len)
			goto exit;
		if (mWrBuf[0] != USB20_CMD_SPI_BLCK_RD)
			goto exit;
		mLength = (mWrBuf[1] & 0xFF) + ((mWrBuf[2] << 8) & 0xFF00);
		memcpy(pBuf, mWrBuf + USB20_CMD_HEADER + fill_len,
		       mLength);
		RI += mLength;
	}
	retval = true;

exit:
	if (!ignoreCS)
		CH347SPI_ChangeCS(fd, (iChipSelect >> 7)); // 撤消CS片选
	*oLength = RI;

	pthread_mutex_unlock(&gusbch347[index].mutex);

	return retval;
}

/**
 * CH347SPI_WriteRead - write then read SPI data
 * @fd: file descriptor of device
 * @ignoreCS: ignore SPI chip select while true, else auto set CS
 * @iChipSelect: SPI chip select, ignore while BIT7 is 0, valid while BIT7 is 1
 * @iLength: data length to xfer
 * @ioBuffer: pointer to buffer, store data to be written from MOSI, and return data to be read from MISO
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_WriteRead(int fd, bool ignoreCS, int iChipSelect,
			int iLength, void *ioBuffer)
{
	uint8_t mBuffer[MAX_BUFFER_LENGTH * 2] = { 0 };
	uint32_t i, mLength;
	uint8_t *mWrBuf = mBuffer;
	uint8_t *pBuf;
	int ilen, PI;
	bool retval = false;
	int index = GetDevIndex(fd);
	int fill_len = 0;
	int max_len;
	bool NewVersion = false;

	if (index < 0)
		return fd;

	max_len = (gusbch347[index].ChipType == CHIP_CH339W) ?
			  MAX_BUFFER_LENGTH_CH339W :
			  MAX_BUFFER_LENGTH;

	if (iLength < 1 || iLength > MAX_BUFFER_LENGTH)
		return false;

	if ((gusbch347[index].FirmwareVer >= 0x0341) ||
	    (gusbch347[index].ChipType == CHIP_CH347F))
		NewVersion = true;

	if (NewVersion && gusbch347[index].iDataBits) {
		if (iLength % 2)
			return false;
		fill_len = 0x01;
	}

	pthread_mutex_lock(&gusbch347[index].mutex);

	if (NewVersion & !gusbch347[index].deAutoCS) {
		pBuf = (uint8_t *)ioBuffer;
		i = 0;
		mWrBuf[i++] = USB20_CMD_SPI_RD_WR;
		if (!ignoreCS) {
			if (gusbch347[index]
				    .ch347device.dllUserSpiCfg.iChipSelect &
			    0x8000) {
				mWrBuf[i++] = (uint8_t)(iLength & 0xFF);
				mWrBuf[i++] =
					(uint8_t)((iLength >> 8) & 0xFF) |
					BIT(5) | BIT(4);
			} else {
				mWrBuf[i++] = (uint8_t)(iLength & 0xFF);
				mWrBuf[i++] =
					(uint8_t)((iLength >> 8) & 0xFF) |
					BIT(7) | BIT(6);
			}
		} else {
			mWrBuf[i++] = (uint8_t)(iLength & 0xFF);
			mWrBuf[i++] = (uint8_t)((iLength >> 8) & 0xFF);
		}
		if (fill_len)
			mWrBuf[i++] = 0x00;
		memcpy(&mWrBuf[i], (uint8_t *)pBuf, iLength);
		i += iLength;
		mLength = i;

		if (!CH347WriteData(fd, mWrBuf, &mLength) ||
		    (mLength != i))
			goto exit;
		memset(mWrBuf, 0, mLength);

		if (!CH347ReadData(fd, mWrBuf, &mLength))
			goto exit;
		if (mWrBuf[0] != USB20_CMD_SPI_RD_WR)
			goto exit;
		if (mLength != i)
			goto exit;
		ilen = ((uint8_t *)mWrBuf)[1] +
		       (((uint8_t *)mWrBuf)[2] << 8); // 后续数据长度
		memcpy(pBuf,
		       (uint8_t *)mWrBuf + USB20_CMD_HEADER + fill_len,
		       ilen);

		retval = true;
		goto exit;
	} else {
		if (!ignoreCS) {
			if (!CH347SPI_ChangeCS(fd, iChipSelect))
				goto exit;
		}
		PI = 0;
		while (PI < iLength) {
			if ((PI +
			     gusbch347[index].ch347device.CmdDataMaxSize -
			     fill_len) > iLength)
				ilen = iLength - PI;
			else
				ilen = gusbch347[index]
					       .ch347device.CmdDataMaxSize -
				       fill_len;
			pBuf = (uint8_t *)ioBuffer + PI;

			i = 0;
			mWrBuf[i++] = USB20_CMD_SPI_RD_WR;
			mWrBuf[i++] = (uint8_t)(ilen & 0xFF);
			mWrBuf[i++] = (uint8_t)((ilen >> 8) & 0xFF);
			if (fill_len)
				mWrBuf[i++] = 0x00;
			memcpy(&mWrBuf[i], (uint8_t *)pBuf, ilen);
			i += ilen;
			mLength = i;

			if (!CH347WriteData(fd, mWrBuf, &mLength) ||
			    (mLength != i))
				goto exit1;
			memset(mWrBuf, 0, mLength);

			if (!CH347ReadData(fd, mWrBuf, &mLength))
				goto exit1;
			if (mWrBuf[0] != USB20_CMD_SPI_RD_WR)
				goto exit1;
			if (mLength != i)
				goto exit1;

			ilen = ((uint8_t *)mWrBuf)[1] +
			       (((uint8_t *)mWrBuf)[2]
				<< 8); // 后续数据长度
			memcpy(pBuf,
			       (uint8_t *)mWrBuf + USB20_CMD_HEADER +
				       fill_len,
			       ilen);
			PI += ilen;
		}
	}
	retval = true;

exit1:
	if (!ignoreCS)
		CH347SPI_ChangeCS(fd, (iChipSelect >> 7)); // 撤消CS片选
exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347StreamSPI4 - write then read SPI data in stream mode
 * @fd: file descriptor of device
 * @ignoreCS: ignore SPI chip select while true, else auto set CS
 * @iChipSelect: SPI chip select, ignore while BIT7 is 0, valid while BIT7 is 1
 * @iLength: data length to xfer
 * @ioBuffer: pointer to buffer, store data to be written from MOSI, and return data to be read from MISO
 *
 * The function return true if successful, false if fail.
 */
bool CH347StreamSPI4(int fd, bool ignoreCS, uint8_t iChipSelect,
		     int iLength, void *ioBuffer)
{
	return CH347SPI_WriteRead(fd, ignoreCS, iChipSelect, iLength,
				  ioBuffer);
}

/**
 * CH347SPI_Slave_Control - switch of reading SPI data from master 
 * @fd: file descriptor of device
 * @enable: true: start reading continuously, false: stop reading
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_Slave_Control(int fd, bool enable)
{
	int retval;
	int index = GetDevIndex(fd);

	if (gusbch347[index].ChipType != CHIP_CH347F)
		return false;

	if (enable)
		retval = ioctl(fd, CH34x_START_BUFFERED_UPLOAD, NULL);
	else
		retval = ioctl(fd, CH34x_STOP_BUFFERED_UPLOAD, NULL);

	return retval == 0 ? true : false;
}

/**
 * CH347SPI_Slave_QweryData - get spi data length
 * @fd: file descriptor of device
 * @oLength: pointer to read length
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_Slave_QweryData(int fd, uint32_t *oLength)
{
	int retval;

	retval = ioctl(fd, CH34x_QWERY_SLAVE_FIFO, oLength);
	if (retval != 0) {
		return false;
	}

	return true;
}

/**
 * CH347SPI_Slave_FIFOReset - reset spi data fifo
 * @fd: file descriptor of device
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_Slave_FIFOReset(int fd)
{
	int retval;

	retval = ioctl(fd, CH34x_RESET_SLAVE_FIFO, NULL);
	if (retval != 0) {
		return false;
	}

	return true;
}

/**
 * CH347SPI_Slave_ReadData - read spi data in slave mode
 * @fd: file descriptor of device
 * @oReadBuffer: pointer to read buffer
 * @oReadLength: pointer to read length
 *
 * The function return true if successful, false if fail.
 */
bool CH347SPI_Slave_ReadData(int fd, void *oReadBuffer,
			     uint32_t *oReadLength)
{
	struct _bulkUp {
		uint32_t len;
		uint8_t odata[0];
	} __attribute__((packed));

	struct _bulkUp *bulkUp;
	int retval;

	bulkUp = (struct _bulkUp *)malloc(sizeof(struct _bulkUp) +
					  SLAVE_MAX_LENGTH);
	memset(bulkUp, 0x00, sizeof(struct _bulkUp) + SLAVE_MAX_LENGTH);
	bulkUp->len = *oReadLength;

	retval = ioctl(fd, CH34x_READ_SLAVE_FIFO, (unsigned long)bulkUp);
	if (retval < 0)
		goto exit;

	*oReadLength = bulkUp->len;

	memcpy((uint8_t *)oReadBuffer, bulkUp->odata, bulkUp->len);

exit:
	free(bulkUp);
	return retval == 0 ? true : false;
}

/**
 * BuildPkt_EnterShiftDR - build bitbang data command packet which used to switch state machine:: Test-Idle to Shift-DR
 * @BitBangPkt: bitbang packet array
 *
 * The function return packet size.
 */
int BuildPkt_EnterShiftDR(uint8_t *BitBangPkt)
{
	uint8_t BI = 0;

	BI = USB20_CMD_HEADER; // 预留3字节包头:1字节命令码+2字节后续长度(小端)
	// Go to Run-Test/Idle
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
	// Go to Select-DR-Scan
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
	// Go to Capture-DR
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;
	// Go to Shift-DR
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;

	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
	// usb to jtag usb packet head
	BitBangPkt[0] = USB20_CMD_JTAG_BIT_OP;
	BitBangPkt[1] = BI - USB20_CMD_HEADER;
	BitBangPkt[2] = 0;

	return BI;
}

/**
 * BuildPkt_EnterShiftIR - build state machine switch bit band data: Test-Idle to Shift-IR
 * @BitBangPkt: bitbang packet array
 *
 * The function return packet size.
 */
int BuildPkt_EnterShiftIR(uint8_t *BitBangPkt)
{
	uint8_t BI = 0;

	BI = USB20_CMD_HEADER; // 预留3字节包头:1字节命令码+2字节后续长度(小端)
	// Go to Run-Test-Idle
	// BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L;
	// BitBangPkt[BI++] = TMS_L | TDI_H | TCK_H;
	// Go to Select-DR-Scan
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
	// Go to Select-IR-Scan
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
	// Go to Capture-IR
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;
	// Go to Shift-IR
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;

	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;

	// usb to jtag usb packet header
	BitBangPkt[0] = USB20_CMD_JTAG_BIT_OP;
	BitBangPkt[1] = BI - USB20_CMD_HEADER;
	BitBangPkt[2] = 0;

	return BI;
}

/**
 * BuildPkt_DataShift - build a TDO/TDI bitband data command packet to send data through USB jtag-tdi or read data through USB jtag-tdo.
 * It is recommended to use BuildPkt_DataShift_FAST for batch data.
 * State machine change: Shift-DR/IR -> data shift -> exit DR/IR
 * @index: device index
 * @DataBuf: data output from TDI
 * @BitCount: total number of digits to move out, return number to read
 * @CmdPktBuf: bitbang buffer
 * @isRead: whether read TDO data after TDI output
 * @CmdPktReturnSize: after the USB jtag reads the tdo data, returned size of packet containing TDO data and packet header
 * @IsLastPkt: whether the last batch of shift data
 * 
 * The function return packet size.
 */
int BuildPkt_DataShift(int index, uint8_t *DataBuf, int BitCount,
		       uint8_t *CmdPktBuf, bool IsRead,
		       uint32_t *CmdPktReturnSize, bool IsLastPkt)
{
	int BI, i, DI, ilen, RetPktBytes;
	uint8_t TMS_Bit, TDI_Bit;

	// 下传JTAG TDI位带数据
	TMS_Bit = TMS_L;
	BI = 0;
	RetPktBytes = 0;

	for (i = 0; i < BitCount; i++) {
		if ((i %
		     gusbch347[index].ch347device.CMDPKT_DATA_MAX_BITS) ==
		    0) // 包头位置,填充命令包头。
		{
			// 计算命令包内放入的字节数
			if ((BitCount - i) >
			    gusbch347[index]
				    .ch347device
				    .CMDPKT_DATA_MAX_BITS) // 满包
				ilen = gusbch347[index]
					       .ch347device
					       .CMDPKT_DATA_MAX_BITS *
				       2; // 每个位的位带数据需要两个字节表示(移位需在边沿,两个时钟)
			else // 最后一包
				ilen = (BitCount - i) *
				       2; // 每个位的位带数据需要两个字节表示(移位需在边沿,两个时钟)

			// 填充命令码
			if (IsRead) // 位带方式读操作，使用USB20_CMD_JTAG_BIT_OP_RD
				CmdPktBuf[BI++] = USB20_CMD_JTAG_BIT_OP_RD;
			else // 位带方式写操作
				CmdPktBuf[BI++] = USB20_CMD_JTAG_BIT_OP;

			// 填充命令包内数据长度,低八位在前
			CmdPktBuf[BI++] = (uint8_t)(ilen & 0xFF);
			CmdPktBuf[BI++] = (uint8_t)((ilen >> 8) & 0xFF);

			// USB20_CMD_JTAG_BIT_OP_RD命令返回时，返回的命令包大小。与发送不同，返回每个位由一个字节表示
			RetPktBytes += (ilen / 2 + USB20_CMD_HEADER);
		}

		// TDI赋值:位数据存于字节内,bit0对应于byte0 bit0,bit1对应用于byte bit1,由低位至高。LSB
		DI = i / 8; // 8位为一字节
		if ((DataBuf[DI] >> (i % 8)) & 0x01)
			TDI_Bit = TDI_H;
		else
			TDI_Bit = TDI_L;

		// TMS赋值:只有在最后一位时需切换值,最后一位需在exit1-DR状态输出,即TMS=H
		if (((i + 1) == BitCount) && IsLastPkt)
			TMS_Bit = TMS_H;

		// 每数据位需要两个位带字节,时钟高和低
		CmdPktBuf[BI++] = TMS_Bit | TDI_Bit | TCK_L |
				  JtatPinSta.TRST;
		CmdPktBuf[BI++] = TMS_Bit | TDI_Bit | TCK_H |
				  JtatPinSta.TRST;
	}
	if (CmdPktReturnSize)
		*CmdPktReturnSize = RetPktBytes;

	return BI; // 命令包大小
}

/**
 * BuildPkt_DataShift_Fast - build a command packet for TDO/TDI exchange data in fast mode, which is used to send data through USB jtag-tdi or read data through USB jtag-tdo.
 * USB transmission at the software and hardware, only transmits TDI/TDO data bits in bytes. At the same time, the hardware enables DMA mode to transmit TDI bits, which is more efficient than bitband mode.
 * State machine change: Shift-DR/IR -> exit DR/IR
 * @index: device index
 * @DataBuf: data output from TDI
 * @DataBytes: data length
 * @CmdPktBuf: packet buffer
 * @isRead: whether read TDO data after TDI output
 * @IsLastPkt: whether the last batch of shift data
 * 
 * The function return packet size.
 */
int BuildPkt_DataShift_Fast(int index, uint8_t *DataBuf, int DataBytes,
			    uint8_t *CmdPktBuf, bool IsRead,
			    bool IsLastPkt)
{
	int BI, i, DI, RemainBits, TdiBytes, PktDataLen;

	// 通过自定义快速协议传输JTAG TDX数据，TCK信号由硬件生成，以字节为单位传至USB20 JTAG
	TdiBytes = DataBytes;
	if ((TdiBytes) &&
	    (IsLastPkt)) // 最后一包数据的最后一位需要在exit dr/ir下发送，使用D1/D2位带命令。
		TdiBytes--; // 预留最后一字节，使用D1/D2位带命令发送

	DI = BI = 0;
	while (DI < TdiBytes) {
		if ((TdiBytes - DI) >
		    gusbch347[index].ch347device.CmdDataMaxSize) // 满包传递
			PktDataLen =
				gusbch347[index].ch347device.CmdDataMaxSize;
		else
			PktDataLen = TdiBytes - DI;
		if (IsRead)
			CmdPktBuf[BI++] = USB20_CMD_JTAG_DATA_SHIFT_RD;
		else
			CmdPktBuf[BI++] = USB20_CMD_JTAG_DATA_SHIFT;
		CmdPktBuf[BI++] = (uint8_t)(PktDataLen >> 0) & 0xFF;
		CmdPktBuf[BI++] = (uint8_t)(PktDataLen >> 8) &
				  0xFF; // 长度高8位
		memcpy(&CmdPktBuf[BI], &DataBuf[DI], PktDataLen);
		BI += PktDataLen;
		DI += PktDataLen; // TDX数据偏移
	}

	// D1/D2命令包，以位带方式组包
	RemainBits = (DataBytes - DI) * 8; // 最后一字节
	if (RemainBits) {
		uint8_t TMS_Bit, TDI_Bit;

		CmdPktBuf[BI++] = IsRead ? USB20_CMD_JTAG_BIT_OP_RD :
					   USB20_CMD_JTAG_BIT_OP;
		PktDataLen = RemainBits * 2; // 每个数据位由两字节位带组成

		CmdPktBuf[BI++] = (uint8_t)(PktDataLen >> 0) & 0xFF;
		CmdPktBuf[BI++] = (uint8_t)(PktDataLen >> 8) &
				  0xFF; // 长度高8位

		TMS_Bit = TMS_L;
		for (i = 0; i < RemainBits; i++) {
			if (((DataBuf[DI] >> i) & 0x01))
				TDI_Bit = TDI_H;
			else
				TDI_Bit = TDI_L;

			if (((i + 1) == RemainBits) &&
			    (IsLastPkt)) // 最后一位在exit1-DR状态输出
				TMS_Bit = TMS_H;
			CmdPktBuf[BI++] = TMS_Bit | TDI_Bit | TCK_L |
					  JtatPinSta.TRST;
			CmdPktBuf[BI++] = TMS_Bit | TDI_Bit | TCK_H |
					  JtatPinSta.TRST;
		}
	}
	return BI;
}

/**
 * ParseCmdRetPkt - parse the command packet returned by D2/D4 and extract TDI/TDO data. When the D4 command returns, if it is the last bit, it will use the D2 command to return
 * @CmdPktBuf: packet buffer
 * @CmdPktBufSize: packet length
 * @DataBuf: data output from TDI
 * @DataLen: data length
 * @DataBitCnt: total data bits
 *
 * The function return packet size.
 */
int ParseCmdRetPkt(uint8_t *CmdPktBuf, int CmdPktBufSize, uint8_t *DataBuf,
		   uint32_t *DataLen, uint32_t *DataBitCnt)
{
	int BI, RI, P, j, PktLen, BitCount;

	BI = RI = 0;
	BitCount = 0;

	if ((CmdPktBuf[0] != USB20_CMD_JTAG_BIT_OP_RD) &&
	    (CmdPktBuf[0] != USB20_CMD_JTAG_DATA_SHIFT_RD))
		printf("!!!Invalid First data.%02X %02X.\n", CmdPktBuf[0],
		       CmdPktBuf[1]);

	while (BI < CmdPktBufSize) {
		if ((BI + USB20_CMD_HEADER) >= CmdPktBufSize) // 不完整数据
		{
			printf("ParseCmdRetPkt.imperfect return data.\n");
			break;
		}
		if (CmdPktBuf[BI] ==
		    USB20_CMD_JTAG_BIT_OP_RD) // 返回数据为位带格式
		{
			PktLen = CmdPktBuf[BI + 1] +
				 ((CmdPktBuf[BI + 2] << 8) & 0xFF00);
			BI += USB20_CMD_HEADER;

			// 将位按字节存放
			for (P = 0; P < PktLen;) {
				((uint8_t *)DataBuf)[RI] = 0;
				for (j = 0; j < 8; j++) {
					if (CmdPktBuf[BI] & 0x01)
						((uint8_t *)DataBuf)[RI] |=
							(1 << j);
					BI++;
					P++;
					if (BI >= CmdPktBufSize)
						break;
				}
				RI++;
			}
			BitCount += P; // 实际上传位数
		} else if (CmdPktBuf[BI] ==
			   USB20_CMD_JTAG_DATA_SHIFT_RD) // 返回数据为字节，无需转换
		{
			PktLen = CmdPktBuf[BI + 1] +
				 ((CmdPktBuf[BI + 2] << 8) & 0xFF00);
			BitCount += PktLen * 8;
			BI += USB20_CMD_HEADER;
			if (BI + PktLen > CmdPktBufSize) // 传递数据不完整
			{
				printf("!!SPIRead incomplete(%X-%X),%d %0d\n",
				       PktLen, CmdPktBufSize - BI,
				       CmdPktBuf[BI], CmdPktBuf[BI + 1]);
				PktLen = CmdPktBufSize - BI;
			}
			memcpy(DataBuf + RI, CmdPktBuf + BI, PktLen);
			RI += PktLen;
			BI += PktLen;
		} else
			BI++;
	}
	*DataLen = RI;
	if (DataBitCnt)
		*DataBitCnt = BitCount;

	return RI;
}

/**
 * BuildPkt_exitShiftToRunIdle - build bitbang data packet which used to switch state machine: Shift-IR/DR to Test-Idle
 * @BitBangPkt: bitbang packet array
 *
 * The function return packet size.
 */
int BuildPkt_exitShiftToRunIdle(uint8_t *BitBangPkt)
{
	uint8_t BI = 0;

	// usb to jtag communication protocol packet header
	BitBangPkt[BI++] = USB20_CMD_JTAG_BIT_OP; // Cmd code
	BitBangPkt[BI++] = 4; // data length lower 8 bit
	BitBangPkt[BI++] = 0; // data length higher 8 bit

	// exit1-DR -> Update-DR
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;

	// Update-DR -> Run-Test-Idle
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;

	return BI;
}

/**
 * BuildPkt_Reset - build command packet which used to JTAG reset
 * @BitBangPkt: bitbang packet array
 *
 * The function return packet size.
 */
int BuildPkt_Reset(uint8_t *BitBangPkt)
{
	uint8_t BI = 0, i;

	BI = USB20_CMD_HEADER; // 预留3字节包头:1字节命令码+2字节后续长度(小端)
	for (i = 0; i < 7; i++) {
		BitBangPkt[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBangPkt[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
	}
	BitBangPkt[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;

	BitBangPkt[0] = USB20_CMD_JTAG_BIT_OP;
	BitBangPkt[1] = BI - USB20_CMD_HEADER;
	BitBangPkt[2] = 0; // 长度高8位

	return BI;
}

/**
 * CH347Jtag_Reset - Reset Tap Status, more than six consecutive TCK and TMS is high 
 * 					will set the state machine to the Test-Logic Reset state.
 * @fd: file descriptor of device
 *
 * The function return true if successful, false if fail.
 */
int CH347Jtag_Reset(int fd)
{
	uint8_t BitBang[512] = { 0 }, BI, i;
	uint32_t TxLen;
	int index = GetDevIndex(fd);
	bool retval = false;

	if (index < 0)
		return false;

	BI = USB20_CMD_HEADER;
	for (i = 0; i < 7; i++) {
		BitBang[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
	}
	BitBang[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;

	JtatPinSta.TCK = TCK_L;
	JtatPinSta.TDI = TDI_H;
	JtatPinSta.TMS = TMS_H;

	BitBang[0] = USB20_CMD_JTAG_BIT_OP;
	BitBang[1] = BI - USB20_CMD_HEADER;
	BitBang[2] = 0;

	TxLen = BI;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (!CH347WriteData(fd, BitBang, &TxLen) && (TxLen != BI)) {
		printf("Jtag_Reset send usb data failure.");
		retval = false;
	} else
		retval = true;

	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347Jtag_ResetTrst - Hard-reset JTAG device
 * @fd: file descriptor of device
 * @TRSTLevel: reset level, true on high, false on low
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_ResetTrst(int fd, bool TRSTLevel)
{
	uint32_t i = 0, mLength;
	uint8_t mBuffer[32] = { 0 };
	int index = GetDevIndex(fd);
	bool retval = false;

	if (index < 0)
		return false;

	mBuffer[i++] = USB20_CMD_JTAG_BIT_OP;
	mBuffer[i++] = 1;
	mBuffer[i++] = 0;

	if (TRSTLevel) {
		JtatPinSta.TRST = TRST_H;
	} else {
		JtatPinSta.TRST = TRST_L;
	}

	mBuffer[i++] = JtatPinSta.TCK | JtatPinSta.TDI | JtatPinSta.TMS |
		       JtatPinSta.TRST;

	mLength = i;
	pthread_mutex_lock(&gusbch347[index].mutex);
	if (!CH347WriteData(fd, mBuffer, &mLength) || (mLength != i))
		retval = false;
	else
		retval = true;

	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347Jtag_INIT - JTAG interface initialization, mode and speed setting
 * @fd: file descriptor of device
 * @iClockRate: communication speed, valid value is 0-5, the higher the value, the faster the speed
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_INIT(int fd, uint8_t iClockRate)
{
	uint8_t mBuffer[256] = { 0 };
	uint32_t mLength, i;
	bool retval = false;
	mDeviceInforS *DevObj = NULL;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	if (iClockRate > 5)
		goto exit;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (gusbch347[index].ChipType == CHIP_CH347F) {
		if (!CH347_FUNC_SWITCH(fd, 1)) {
			retval = false;
			goto exit;
		}
	}

	DevObj->BulkInEndpMaxSize = 512;
	DevObj->BulkOutEndpMaxSize = 512;
	DevObj->CmdDataMaxSize = 510;
	gusbch347[index].ch347device.CMDPKT_DATA_MAX_BITS =
		DevObj->CmdDataMaxSize / 16 * 16 / 2;

	// 根据USB速度,设置每个命令包最大数据长度
	// 根据硬件缓冲区大小计算每批量传输传输的位数,多命令拼包
	gusbch347[index].ch347device.MaxBitsPerBulk =
		HW_TDO_BUF_SIZE /
		gusbch347[index].ch347device.CmdDataMaxSize *
		gusbch347[index].ch347device.CMDPKT_DATA_MAX_BITS;
	// 根据硬件缓冲区大小计算每批量传输传输的字数,多命令拼包
	gusbch347[index].ch347device.MaxBytesPerBulk =
		HW_TDO_BUF_SIZE -
		(HW_TDO_BUF_SIZE +
		 gusbch347[index].ch347device.CmdDataMaxSize - 1) /
			gusbch347[index].ch347device.CmdDataMaxSize * 3;

	// 构建USB JTAG初始化命令包，并执行
	i = 0;
	mBuffer[i++] = USB20_CMD_JTAG_INIT;
	mBuffer[i++] = 6; // 数据长度为6
	mBuffer[i++] = 0;
	mBuffer[i++] = 0; // 保留字节
	mBuffer[i++] = iClockRate; // JTAG时钟速度
	i += 4; // 保留字节
	mLength = i;

	if (!CH347WriteData(fd, mBuffer, &mLength) || (mLength != i))
		goto exit;

	mLength = 4;

	if (!CH347ReadData(fd, mBuffer, &mLength) || (mLength != 4))
		goto exit;
	retval = ((mBuffer[0] == USB20_CMD_JTAG_INIT) &&
		  (mBuffer[USB20_CMD_HEADER] == 0)) ?
			 true :
			 false;

	CH347Jtag_SwitchTapState(fd, 0); // 默认先复位Target
	CH347Jtag_SwitchTapState(fd, 1); // Run-Test/Idle
	printf("Tap state: Rest -> Run-Test/Idle.\n");

exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347Jtag_GetCfg - get JTAG speed setting
 * @fd: file descriptor of device
 * @iClockRate: pointer to communication speed, valid value is 0-5, the higher the value, the faster the speed
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_GetCfg(int fd, uint8_t *ClockRate)
{
	uint8_t mBuffer[CH347_PACKET_LENGTH] = { 0 };
	uint32_t mLength, i, RetLen;
	bool retval = false;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	i = 0;
	mBuffer[i++] = USB20_CMD_INFO_RD;
	mBuffer[i++] = 1; // 后续长度,小端模式
	mBuffer[i++] = 0; // 参数类型
	mBuffer[i++] = 2; // 获取JTAG配置：模式和速度
	mLength = i;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (!CH347WriteData(fd, mBuffer, &mLength))
		goto exit;

	mLength = RetLen = 6 + 3; // 协议规定长度

	if (!CH347ReadData(fd, mBuffer, &mLength) || (mLength != RetLen))
		goto exit;

	*ClockRate = mBuffer[4];
	retval = true;

exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347Jtag_ClockTms - change TMS value on the rising edge of TCK to switch its Tap state
 * @BitBangPkt: protocol package
 * @Tms: TMS value to change
 * @BI: length of protocol package
 *
 * The function return length of protocol package.
 */
uint32_t CH347Jtag_ClockTms(uint8_t *BitBangPkt, uint32_t Tms, uint32_t BI)
{
	uint8_t CMD = 0;
	if (Tms == 1)
		CMD = TMS_H;
	else
		CMD = TMS_L;

	BitBangPkt[BI++] = CMD | TDI_H | TCK_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = CMD | TDI_H | TCK_H | JtatPinSta.TRST;

	JtatPinSta.TMS = CMD;
	JtatPinSta.TDI = TDI_H;
	JtatPinSta.TCK = TCK_H;

	return BI;
}

/**
 * CH347Jtag_IdleClock - ensure the clock is in low status
 * @BitBangPkt: protocol package
 * @BI: length of protocol package
 *
 * The function return length of protocol package.
 */
uint32_t CH347Jtag_IdleClock(uint8_t *BitBangPkt, uint32_t BI)
{
	uint8_t Byte = 0;
	Byte |= JtatPinSta.TMS ? TMS_H : TMS_L | JtatPinSta.TRST;
	Byte |= JtatPinSta.TDI ? TDI_H : TDI_L | JtatPinSta.TRST;
	BitBangPkt[BI++] = Byte;

	return BI;
}

/**
 * CH347Jtag_TmsChange - change TMS value to switch state
 * @fd: file descriptor of device
 * @tmsValue: pointer to TMS value, unit: byte
 * @Step: The valid bits which stored in tmsValue
 * @Skip: valid start bit
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_TmsChange(int fd, uint8_t *tmsValue, uint32_t Step,
			 uint32_t Skip)
{
	uint32_t i;
	uint32_t BI, retlen, TxLen;
	uint8_t BitBangPkt[4096] = { 0 };
	bool retval = false;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	BI = USB20_CMD_HEADER;
	retlen = USB20_CMD_HEADER;

	for (i = Skip; i < Step; i++) {
		retlen = CH347Jtag_ClockTms(
			BitBangPkt,
			(uint32_t)(tmsValue[i / 8] >> (i % 8)) & 0x01, BI);
		BI = retlen;
	}
	BI = CH347Jtag_IdleClock(BitBangPkt, BI);
	BitBangPkt[0] = USB20_CMD_JTAG_BIT_OP;
	BitBangPkt[1] = (uint8_t)BI - USB20_CMD_HEADER;
	BitBangPkt[2] = 0;

	TxLen = BI;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (!CH347WriteData(fd, BitBangPkt, &TxLen) && (TxLen != BI)) {
		printf("Jtag_TmsChange failure.");
		retval = false;
	} else
		retval = true;

	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347Jtag_IoScan - Read and write in the Shift-DR/IR state, and switch to Exit DR/IR after execution
 * State machine change: Shift-DR/IR.RW.->Exit DR/IR
 * @fd: file descriptor of device
 * @DataBits: data bits to be transmitted
 * @DataBitsNb: number of bits to be transmitted
 * @IsRead: whether to read data
 * 
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_IoScan(int fd, uint8_t *DataBits, uint32_t DataBitsNb,
		      bool IsRead)
{
	return CH347Jtag_IoScanT(fd, DataBits, DataBitsNb, IsRead, true);
}

/**
 * CH347Jtag_IoScanT - Read and write in the Shift-DR/IR state, if it is the last package, switch to Exit DR/IR; if not, stop at Shift-DR/IR
 * State machine change: Shift-DR/IR.RW..->[Exit DR/IR]
 * @fd: file descriptor of device
 * @DataBits: data bits to be transmitted
 * @DataBitsNb: number of bits to be transmitted
 * @IsRead: whether to read data
 * @IsLastPkt: whether the last package
 * 
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_IoScanT(int fd, uint8_t *DataBits, uint32_t DataBitsNb,
		       bool IsRead, bool IsLastPkt)
{
	uint32_t NB8 = DataBitsNb / 8; // 传输的字节数
	uint32_t NB1 = DataBitsNb % 8; // 传输最后xbit数
	uint32_t BI, DI, DII, PktLen, TxLen, RxLen, i;
	uint8_t *Tdos;
	uint8_t TMSBit, TDIBit;
	uint8_t JtagCmdPkt[HW_TDO_BUF_SIZE * 2] = { 0 };
	uint8_t JtagTdoPkt[HW_TDO_BUF_SIZE * 2] = { 0 };
	uint8_t temp[HW_TDO_BUF_SIZE * 2] = { 0 };
	uint8_t temp_a[HW_TDO_BUF_SIZE * 2] = { 0 };
	mDeviceInforS *DevObj = NULL;
	uint32_t CmdDataMaxSize = 0;
	bool RetVal;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	CmdDataMaxSize = DevObj->CmdDataMaxSize;

	Tdos = (uint8_t *)malloc(sizeof(uint8_t) * DataBitsNb +
				 CH347_PACKET_LENGTH);
	if (Tdos == NULL)
		return false;

	memset(Tdos, 0x00,
	       sizeof(uint8_t) * DataBitsNb + CH347_PACKET_LENGTH);

	if (IsLastPkt) {
		// 如果整除字节，则留出最后一字节在 EXIT-DR/IR 输出
		if ((NB8 > 0) && (NB1 == 0)) {
			NB8--;
			NB1 = 8;
		}
	}

	DI = BI = 0;

	pthread_mutex_lock(&gusbch347[index].mutex);

	while (DI < NB8) {
		// 单包长度组建
		if ((NB8 - DI) > CmdDataMaxSize)
			PktLen = CmdDataMaxSize;
		else
			PktLen = NB8 - DI;

		// 判断是否读取数据
		if (IsRead)
			JtagCmdPkt[BI++] = USB20_CMD_JTAG_DATA_SHIFT_RD;
		else
			JtagCmdPkt[BI++] = USB20_CMD_JTAG_DATA_SHIFT;

		JtagCmdPkt[BI++] = (uint8_t)((PktLen >> 0) & 0xFF);
		JtagCmdPkt[BI++] = (uint8_t)((PktLen >> 8) & 0xFF);

		DII = PktLen;

		if (DataBits)
			memcpy(&JtagCmdPkt[BI], &DataBits[DI], PktLen);
		else
			memset(&JtagCmdPkt[BI], 0, PktLen);
		BI += PktLen;

		if (IsRead) {
			TxLen = BI;
			RetVal = CH347WriteData(fd, JtagCmdPkt, &TxLen);
			if (!RetVal)
				goto Exit;
			BI = 0;
			while (PktLen > 0) {
				RxLen = PktLen + USB20_CMD_HEADER;
				RetVal = CH347ReadData(fd, temp, &RxLen);
				if ((!RetVal) || (RxLen == 0)) {
					RetVal = false;
					printf("CH347_IoScan read usb data failure.\n");
					goto Exit;
				}
				if (RxLen != TxLen) {
					RetVal = CH347ReadData(fd, temp_a,
							       &TxLen);
					if ((!RetVal) || (TxLen == 0)) {
						RetVal = false;
						printf("CH347_WriteRead read usb data failure.\n");
						goto Exit;
					}
					memcpy(&temp[RxLen], temp_a,
					       TxLen);
					RxLen += TxLen;
				}

				if (RxLen != 0)
					memcpy(&Tdos[DI],
					       &temp[USB20_CMD_HEADER],
					       RxLen - USB20_CMD_HEADER);

				memset(temp, 0, sizeof(temp));
				memset(temp_a, 0, sizeof(temp_a));
				PktLen -= (RxLen - USB20_CMD_HEADER);
			}
		}

		if (BI > (HW_TDO_BUF_SIZE * 0.8)) {
			TxLen = BI;
			RetVal = CH347WriteData(fd, JtagCmdPkt, &TxLen);
			if (!RetVal)
				goto Exit;
			BI = 0;
		}

		DI += DII;
	}

	if (BI > 0) {
		TxLen = BI;
		RetVal = CH347WriteData(fd, JtagCmdPkt, &TxLen);
		if (!RetVal)
			goto Exit;
		BI = 0;
	}

	if (IsLastPkt) {
		// 最后1 Byte字节数据处理
		if (DataBits) {
			JtagCmdPkt[BI++] =
				IsRead ? USB20_CMD_JTAG_BIT_OP_RD :
					 USB20_CMD_JTAG_BIT_OP;
			PktLen = (NB1 * 2) + 1; // 一个TDI对应两个TCK
			JtagCmdPkt[BI++] = (uint8_t)((PktLen >> 0) & 0xFF);
			JtagCmdPkt[BI++] = (uint8_t)((PktLen >> 8) & 0xFF);

			// 组建最后一字节数据，其TMS不变保持在当前SHIFT-DR/IR状态
			TMSBit = TMS_L;
			for (i = 0; i < NB1; i++) {
				if ((DataBits[NB8] >> i) & 0x01)
					TDIBit = TDI_H;
				else
					TDIBit = TDI_L;

				// 最后1bit数据切换状态在EXIT-DR/IR输出
				if ((i + 1) == NB1)
					TMSBit = TMS_H;
				JtagCmdPkt[BI++] = TDIBit | TMSBit |
						   TCK_L | JtatPinSta.TRST;
				JtagCmdPkt[BI++] = TDIBit | TMSBit |
						   TCK_H | JtatPinSta.TRST;
			}
			JtagCmdPkt[BI++] = TDIBit | TMSBit | TCK_L |
					   JtatPinSta.TRST;
		}

		if (NB1 && IsRead) {
			TxLen = BI;
			RetVal = CH347WriteData(fd, JtagCmdPkt, &TxLen);
			if (!RetVal)
				goto Exit;
			RxLen = (PktLen / 2) + USB20_CMD_HEADER;
			RetVal = CH347ReadData(fd, JtagTdoPkt, &RxLen);
			if (!RetVal)
				goto Exit;
			if ((RxLen != ((PktLen / 2) + USB20_CMD_HEADER))) {
				while (RxLen !=
				       ((PktLen / 2) + USB20_CMD_HEADER)) {
					TxLen = (PktLen / 2) +
						USB20_CMD_HEADER;
					RetVal = CH347ReadData(fd, temp_a,
							       &TxLen);
					if (!RetVal)
						goto Exit;
					memcpy(&JtagTdoPkt[RxLen], temp_a,
					       TxLen);
					RxLen += TxLen;
				}
			}

			for (i = 0; i < NB1; i++) {
				if (JtagTdoPkt[USB20_CMD_HEADER + i] &
				    0x01)
					Tdos[NB8] |= (1 << i);
				else
					Tdos[NB8] &= ~(1 << i);
			}
			BI = 0;
		}
	}

	if (BI > 0) {
		TxLen = BI;
		RetVal = CH347WriteData(fd, JtagCmdPkt, &TxLen);
		if (!RetVal)
			goto Exit;
		BI = 0;
	}

	if (DataBits)
		memcpy(DataBits, Tdos, DIV_ROUND_UP(DataBitsNb, 8));

	if (IsLastPkt) {
		BI = USB20_CMD_HEADER;
		BI = CH347Jtag_IdleClock(JtagCmdPkt, BI);

		JtagCmdPkt[0] = USB20_CMD_JTAG_BIT_OP;
		JtagCmdPkt[1] = (uint8_t)(BI - USB20_CMD_HEADER);
		JtagCmdPkt[2] = 0;

		TxLen = BI;
		RetVal = CH347WriteData(fd, JtagCmdPkt, &TxLen);
		if (!RetVal)
			goto Exit;
	}

Exit:
	if (Tdos != NULL)
		free(Tdos); // 如果是分配的内存则释放
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return RetVal;
}

/**
 * CH347Jtag_WriteRead - bitband mode JTAG IR/DR data read and write which is applicable for a small amount of data.
 * Exp: command operation, state machine switching and other control transmission.
 * For batch data transmission, it is recommended to use CH347Jtag_ WriteRead_Fast.
 * Command packets are read and written in batches in 4096 bytes
 * State machine: Run-Test -> Shift-IR/DR.. -> exit IR/DR -> Run-Test
 * @fd: file descriptor of device
 * @IsDR: true: DR data read and write, false: IR data read and write
 * @iWriteBitLength: write length
 * @iWriteBitBuffer: pointer to write buffer
 * @iReadTimes: read times
 * @oReadBitLength: pointer to read length, returns the actual number of bytes read on success
 * @oReadBitBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_WriteRead(int fd, bool IsDR, int iWriteBitLength,
			 void *iWriteBitBuffer, uint32_t *oReadBitLength,
			 void *oReadBitBuffer)
{
	int BI, BitCount, WBI, RI, BufSize, BulkOutBits, TotalRx = 0;
	uint32_t TxLen, rlen, RetPktBytes, BitLen;
	uint8_t CmdPktBuf[4096 + 16] = { 0 };
	bool IsLastData = false, IsRead, retval = false;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	IsRead = ((oReadBitLength != NULL) && (oReadBitBuffer != NULL)) ?
			 true :
			 false;
	BufSize = sizeof(CmdPktBuf);
	if (IsRead)
		BitCount = *oReadBitLength;
	else
		BitCount = iWriteBitLength;

	// 先构建命令包，再将多个命令包组成4K为单位进行USB传输。4K批数据内需都是完整命令包
	memset(CmdPktBuf, 0, BufSize);
	BI = 0;
	WBI = 0;
	RI = 0;
	if (IsRead)
		*oReadBitLength = 0;
	IsLastData = false;

	// 构建JTAG状态机命令包,Run-Test-Idle -> Shift-DR
	if (IsDR)
		BI += BuildPkt_EnterShiftDR(&CmdPktBuf[0]);
	else
		BI += BuildPkt_EnterShiftIR(&CmdPktBuf[0]);

	pthread_mutex_lock(&gusbch347[index].mutex);

	while (WBI < BitCount) {
		// 单批次写入位数,4KB USB传输块为单位计算。
		if ((WBI + gusbch347[index].ch347device.MaxBitsPerBulk) >
		    BitCount) {
			BulkOutBits = BitCount - WBI;
			IsLastData = true;
		} else
			BulkOutBits =
				gusbch347[index].ch347device.MaxBitsPerBulk;

		// 构建DR/IR命令包
		BI += BuildPkt_DataShift(
			index, (uint8_t *)iWriteBitBuffer + WBI / 8,
			BulkOutBits, &CmdPktBuf[BI], IsRead, &RetPktBytes,
			IsLastData); // 返回是位带组包后字节数
		WBI += BulkOutBits;

		// 发送命令包
		TxLen = BI;
		if (!CH347WriteData(0, CmdPktBuf, &TxLen) &&
		    (TxLen != BI)) {
			printf("JTAG_WriteRead send usb data failure.");
			goto exit;
		}
		// 接收命令包
		if (IsRead) {
			rlen = RetPktBytes;
			if (!CH347ReadData(fd, CmdPktBuf, &rlen)) {
				printf("JTAG_WriteRead read usb data failure.");
				goto exit;
			}
			TotalRx += rlen;
			// 从返回的多个命令包内提取数据
			ParseCmdRetPkt(CmdPktBuf, rlen,
				       (uint8_t *)oReadBitBuffer + RI,
				       &rlen, &BitLen);
			if (BitLen < 1) {
				// goto exit;
			} else {
				RI += rlen;
				*oReadBitLength += BitLen;
			}
		}
		BI = 0;
	}
	retval = true;

exit:
	CH347Jtag_SwitchTapState(fd, 6);

	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347Jtag_WriteRead_Fast - JTAG IR/DR data read and write in batches for multi-byte continuous operation. Exp: JTAG firmware download operation. Hardware has a 4K buffer, such as write then read, the length should not exceed 4096 bytes. The buffer size can be adjusted.
 * State machine: Run-Test -> Shift-IR/DR.. -> exit IR/DR -> Run-Test
 * @fd: file descriptor of device
 * @IsDR: true: DR data read and write, false: IR data read and write
 * @iWriteBitLength: write length
 * @iWriteBitBuffer: pointer to write buffer
 * @iReadTimes: read times
 * @oReadBitLength: pointer to read length, returns the actual number of bytes read on success
 * @oReadBitBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_WriteRead_Fast(int fd, bool IsDR, int iWriteLength,
			      void *iWriteBuffer, uint32_t *oReadLength,
			      void *oReadBuffer)
{
	int BI, WI, RI, BufSize, TotalWriteLen, BulkOutBytes,
		CmdPktBufSize;
	uint32_t TxLen, RxLen;
	uint8_t CmdPktBuf[8192] = { 0 };
	bool retval = false, IsLastData = false, IsRead;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	// 清空驱动内上传数据缓冲
	IsRead = ((oReadLength != NULL) && (oReadBuffer != NULL)) ? true :
								    false;
	BufSize = sizeof(CmdPktBuf);
	if (IsRead)
		TotalWriteLen = *oReadLength;
	else
		TotalWriteLen = iWriteLength;

	memset(CmdPktBuf, 0, BufSize);
	BI = WI = RI = 0;
	IsLastData = false;

	// 构建JTAG状态机命令包,Run-Test-Idle -> Shift-DR
	if (IsDR)
		BI += BuildPkt_EnterShiftDR(&CmdPktBuf[0]);
	else
		BI += BuildPkt_EnterShiftIR(&CmdPktBuf[0]);

	pthread_mutex_lock(&gusbch347[index].mutex);
	while (WI < TotalWriteLen) {
		if ((WI + gusbch347[index].ch347device.MaxBytesPerBulk) >
		    TotalWriteLen) // BI只有在传递第一包数据时会与DR/IR拼包
		{
			BulkOutBytes = TotalWriteLen - WI;
			IsLastData = true;
		} else
			BulkOutBytes =
				gusbch347[index]
					.ch347device.MaxBytesPerBulk;
		if ((BulkOutBytes + BI) >
		    gusbch347[index].ch347device.MaxBytesPerBulk)
			BulkOutBytes = BulkOutBytes - BI;

		// 构建快速模式下TDO/TDI多字节输入输出的数据包，转换以位为单位
		CmdPktBufSize = BuildPkt_DataShift_Fast(
			index, (uint8_t *)iWriteBuffer + WI, BulkOutBytes,
			&CmdPktBuf[BI], IsRead, IsLastData);
		;
		BI += CmdPktBufSize;
		WI += BulkOutBytes;

		TxLen = BI;
		// DumpBitBangData(BitBang,TxLen); //打印位带波形,仅调试
		if (!CH347WriteData(fd, CmdPktBuf, &TxLen) &&
		    (TxLen != BI)) {
			printf("JTAG_WriteRead_B send usb data failure.");
			goto exit;
		}
		if (IsRead) {
			if (IsLastData) // 最后一字节与发送相差8个字节。发送时是一位用两个字节位带表示。接收是一个位用一个字节带表示
				RxLen = CmdPktBufSize - 8;
			else
				RxLen = CmdPktBufSize;

			if (!CH347ReadData(fd, CmdPktBuf, &RxLen)) {
				printf("JTAG_WriteRead_B read usb data failure.");
				goto exit;
			}
			// 从返回的多个命令包内提取数据
			ParseCmdRetPkt(CmdPktBuf, RxLen,
				       (uint8_t *)oReadBuffer + RI, &RxLen,
				       NULL);
			if (RxLen < 1)
				goto exit;
			RI += RxLen;
		}
		BI = 0;
	}
	retval = true;

exit:
	CH347Jtag_SwitchTapState(fd, 6);
	if (IsRead)
		*oReadLength = RI;

	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347Jtag_SwitchTapState - switch JTAG state machine
 * @fd: file descriptor of device
 * @TapState: machine state
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_SwitchTapState(int fd, uint8_t TapState)
{
	// TODO:
	bool retval = false;
	uint32_t TxLen, BI;
	uint8_t BitBang[4096] = { 0 };

	// 构建位带命令包,使用D1命令
	switch (TapState) {
	case 0:
		// Test-Logic Reset
		BI = (uint8_t)BuildPkt_Reset(BitBang);
		break;
	case 1:
		// Run-Test/Idle
		BI = USB20_CMD_HEADER; // 有3字节包头:1字节命令码+2字节后续长度(小端)
		BitBang[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;

		BitBang[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;

		BitBang[0] = USB20_CMD_JTAG_BIT_OP;
		BitBang[1] = (uint8_t)BI - USB20_CMD_HEADER;
		BitBang[2] = 0;
		break;

	case 2:
		// Run-Test/Idle -> Shift-DR
		BI = (uint8_t)BuildPkt_EnterShiftDR(BitBang);
		break;
	case 3:
		// Shift-DR -> Run-Test/Idle
		BI = USB20_CMD_HEADER; // 有3字节包头:1字节命令码+2字节后续长度(小端)
		// Shift-DR -> exit1-DR
		BitBang[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
		// exit1-DR -> Update-DR
		BitBang[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
		// Update-DR -> Run-Test-Idle
		BitBang[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;

		BitBang[0] = USB20_CMD_JTAG_BIT_OP;
		BitBang[1] = (uint8_t)BI - USB20_CMD_HEADER;
		BitBang[2] = 0; // 长度高8位
		break;
	case 4:
		// Run-Test/Idle -> Shift-IR
		BI = BuildPkt_EnterShiftIR(BitBang);
		break;
	case 5:

		// Shift-IR -> Run-Test/Idle
		BI = USB20_CMD_HEADER; // 有3字节包头:1字节命令码+2字节后续长度(小端)
		// Shift-IR -> exit1-IR
		BitBang[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
		// exit1-IR -> Update-IR
		BitBang[BI++] = TMS_H | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_H | TDI_H | TCK_H | JtatPinSta.TRST;
		// Update-IR -> Run-Test-Idle
		BitBang[BI++] = TMS_L | TDI_H | TCK_L | JtatPinSta.TRST;
		BitBang[BI++] = TMS_L | TDI_H | TCK_H | JtatPinSta.TRST;

		BitBang[0] = USB20_CMD_JTAG_BIT_OP;
		BitBang[1] = (uint8_t)BI - USB20_CMD_HEADER;
		BitBang[2] = 0; // 长度高8位
		break;
	case 6:
		BI = BuildPkt_exitShiftToRunIdle(BitBang);
		break;
	default:
		break;
	}
	TxLen = BI;
	retval = (CH347WriteData(fd, BitBang, &TxLen) && (TxLen == BI)) ?
			 true :
			 false;

	return retval;
}

/**
 * CH347Jtag_ByteWriteDR - JTAG DR write in bytes which used for multi-byte continuous operation. Exp: JTAG firmware download operation.
 * State machine: Run-Test -> Shift-DR.. -> exit DR -> Run-Test
 * @fd: file descriptor of device
 * @iWriteLength: write length
 * @iWriteBuffer: pointer to write buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_ByteWriteDR(int fd, int iWriteLength, void *iWriteBuffer)
{
	return CH347Jtag_WriteRead_Fast(fd, true, iWriteLength,
					iWriteBuffer, NULL, NULL);
}

/**
 * CH347Jtag_ByteReadDR - JTAG DR read in bytes which used for multi-byte continuous operation.
 * State machine: Run-Test -> Shift-DR.. -> exit DR -> Run-Test
 * @fd: file descriptor of device
 * @oReadLength: pointer to read length, returns the actual number of bytes read on success
 * @oReadBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_ByteReadDR(int fd, uint32_t *oReadLength, void *oReadBuffer)
{
	uint8_t *iWriteBuffer;
	int iWriteLength;
	bool retval = false;

	iWriteLength = *oReadLength;
	iWriteBuffer = (uint8_t *)malloc(*oReadLength);
	if (iWriteBuffer == NULL)
		return false;
	memset(iWriteBuffer, 0xFF, iWriteLength);

	retval = CH347Jtag_WriteRead_Fast(fd, true, iWriteLength,
					  iWriteBuffer, oReadLength,
					  oReadBuffer);

	free(iWriteBuffer);
	return retval;
}

/**
 * CH347Jtag_ByteWriteIR - JTAG IR write in bytes which used for multi-byte continuous operation.
 * State machine: Run-Test -> Shift-IR.. -> exit IR -> Run-Test
 * @fd: file descriptor of device
 * @iWriteLength: pointer to write length
 * @iWriteBuffer: pointer to write buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_ByteWriteIR(int fd, int iWriteLength, void *iWriteBuffer)
{
	return CH347Jtag_WriteRead_Fast(fd, false, iWriteLength,
					iWriteBuffer, NULL, NULL);
}

/**
 * CH347Jtag_ByteReadIR - JTAG IR read in bytes which used for multi-byte continuous operation.
 * State machine: Run-Test -> Shift-IR.. -> exit IR -> Run-Test
 * @fd: file descriptor of device
 * @oReadLength: pointer to read length, returns the actual number of bytes read on success
 * @oReadBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_ByteReadIR(int fd, uint32_t *oReadLength, void *oReadBuffer)
{
	uint8_t *iWriteBuffer;
	int iWriteLength;
	bool retval = false;

	iWriteLength = *oReadLength;
	iWriteBuffer = (uint8_t *)malloc(iWriteLength);
	if (iWriteBuffer == NULL)
		return false;
	memset(iWriteBuffer, 0xFF, iWriteLength);

	retval = CH347Jtag_WriteRead_Fast(fd, false, iWriteLength,
					  iWriteBuffer, oReadLength,
					  oReadBuffer);

	free(iWriteBuffer);
	return retval;
}

/**
 * CH347Jtag_WriteRead - bitband mode JTAG DR data write which is applicable for a small amount of data. Exp: command operation, state machine switching and other control transmission. For batch data transmission, it is recommended to use USB20Jtag_ByeWriteDR
 * State machine: Run-Test -> Shift-DR.. -> exit DR -> Run-Test
 * @fd: file descriptor of device
 * @iWriteBitLength: pointer to write length
 * @iWriteBitBuffer: pointer to write buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_BitWriteDR(int fd, int iWriteBitLength,
			  void *iWriteBitBuffer)
{
	return CH347Jtag_WriteRead(fd, true, iWriteBitLength,
				   iWriteBitBuffer, NULL, NULL);
}

/**
 * CH347Jtag_BitWriteIR - bitband mode JTAG IR data write which is applicable for a small amount of data. Exp: command operation, state machine switching and other control transmission. For batch data transmission, it is recommended to use USB20Jtag_ByeWriteIR
 * State machine: Run-Test -> Shift-IR.. -> exit IR -> Run-Test
 * @fd: file descriptor of device
 * @iWriteBitLength: pointer to write length
 * @iWriteBitBuffer: pointer to write buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_BitWriteIR(int fd, int iWriteBitLength,
			  void *iWriteBitBuffer)
{
	return CH347Jtag_WriteRead(fd, false, iWriteBitLength,
				   iWriteBitBuffer, NULL, NULL);
}

/**
 * CH347Jtag_BitWriteIR - bitband mode JTAG IR data read which is applicable for a small amount of data. Exp: command operation, state machine switching and other control transmission. For batch data transmission, it is recommended to use USB20Jtag_ByteReadIR
 * State machine: Run-Test -> Shift-IR.. -> exit IR -> Run-Test
 * @fd: file descriptor of device
 * @oReadBitLength: pointer to read length, returns the actual number of bytes read on success
 * @oReadBitBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_BitReadIR(int fd, uint32_t *oReadBitLength,
			 void *oReadBitBuffer)
{
	uint8_t *iWriteBitBuffer;
	int iWriteBitLength;
	bool retval = false;

	iWriteBitLength = *oReadBitLength;
	iWriteBitBuffer = (uint8_t *)malloc(iWriteBitLength);
	if (iWriteBitBuffer == NULL)
		return false;
	memset(iWriteBitBuffer, 0xFF, iWriteBitLength);
	retval = CH347Jtag_WriteRead(fd, false, iWriteBitLength,
				     iWriteBitBuffer, oReadBitLength,
				     oReadBitBuffer);

	free(iWriteBitBuffer);
	return retval;
}

/**
 * CH347Jtag_BitReadDR - bitband mode JTAG DR data read which is applicable for a small amount of data. Exp: command operation, state machine switching and other control transmission. For batch data transmission, it is recommended to use USB20Jtag_ByteReadDR
 * State machine: Run-Test -> Shift-DR.. -> exit DR -> Run-Test
 * @fd: file descriptor of device
 * @oReadBitLength: pointer to read length, returns the actual number of bytes read on success
 * @oReadBitBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347Jtag_BitReadDR(int fd, uint32_t *oReadBitLength,
			 void *oReadBitBuffer)
{
	uint8_t *iWriteBitBuffer;
	int iWriteBitLength;
	bool retval = false;

	iWriteBitLength = *oReadBitLength;
	iWriteBitBuffer = (uint8_t *)malloc(iWriteBitLength);
	if (iWriteBitBuffer == NULL)
		return false;
	memset(iWriteBitBuffer, 0xFF, iWriteBitLength);
	retval = CH347Jtag_WriteRead(fd, true, iWriteBitLength,
				     iWriteBitBuffer, oReadBitLength,
				     oReadBitBuffer);

	free(iWriteBitBuffer);
	return retval;
}

/**
 * CH347GPIO - gpio setting
 * @fd: file descriptor of device
 * @CfgData: pointer to gpio configuraion array
 * @StatusData: pointer to gpio status array
 *
 * The function return true if success, others if fail.
 */
static bool CH347GPIO(int fd, uint8_t *CfgData, uint8_t *StatusData)
{
	uint8_t mWBuf[128] = { 0 }, mRBuf[128] = { 0 };
	uint32_t i, mLength;
	bool retval = false;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	i = 0;
	mWBuf[i++] = USB20_CMD_GPIO_OP;
	mWBuf[i++] = CH347_GPIO_CNT;
	mWBuf[i++] = 0;
	memcpy(&mWBuf[i], CfgData, CH347_GPIO_CNT);
	mLength = i + CH347_GPIO_CNT;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (CH347WriteData(fd, mWBuf, &mLength) &&
	    (mLength != (i + CH347_GPIO_CNT)))
		goto exit;

	mLength = i + CH347_GPIO_CNT;
	if (CH347ReadData(fd, mRBuf, &mLength) && (mLength < 9))
		goto exit;
	memcpy(StatusData, &mRBuf[3], CH347_GPIO_CNT);
	retval = true;

exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347GPIO_Get - get gpio status
 * @fd: file descriptor of device
 * @iDir: gpio direction bits, bits0-7 on gpio0-7, 1 on ouput, 0 on input
 * @iData: gpio level bits, bits0-7 on gpio0-7, 1 on high, 0 on low
 *
 * The function return true if success, others if fail.
 */
bool CH347GPIO_Get(int fd, uint8_t *iDir, uint8_t *iData)
{
	uint8_t mBuf[128] = { 0 };
	int j;
	bool retval = false;

	/* only get status */
	memset(mBuf, 0x00, CH347_GPIO_CNT);
	if (!CH347GPIO(fd, mBuf, mBuf))
		goto exit;

	*iDir = *iData = 0;
	for (j = 0; j < CH347_GPIO_CNT; j++) {
		/* get direction bit */
		if (mBuf[j] & 0x80)
			*iDir |= (1 << j);
		/* get value bit */
		if (mBuf[j] & 0x40)
			*iData |= (1 << j);
	}
	retval = true;

exit:
	return retval;
}

/**
 * CH347GPIO_Set - gpio setting
 * @fd: file descriptor of device
 * @iEnable: gpio function enable bits, bits0-7 on gpio0-7, 1 on enable
 * @iSetDirOut: gpio direction bits, bits0-7 on gpio0-7, 1 on ouput, 0 on input
 * @iSetDataOut: gpio output bits, bits0-7 on gpio0-7, if gpio direction is output, 1 on high, 0 on low
 *
 * The function return true if success, others if fail.
 */
bool CH347GPIO_Set(int fd, uint8_t iEnable, uint8_t iSetDirOut,
		   uint8_t iSetDataOut)
{
	uint8_t mBuf[128] = { 0 }, OldDir = 0, OldVal = 0;
	int i;
	bool retval = false;

	/* only get status */
	memset(mBuf, 0x00, CH347_GPIO_CNT);
	if (!CH347GPIO_Get(fd, &OldDir, &OldVal))
		goto exit;

	for (i = 0; i < CH347_GPIO_CNT; i++) {
		/* bit5->direction bit */
		if (OldDir & (1 << i))
			mBuf[i] |= 0x20;
		else
			mBuf[i] &= ~0x20;

		/* bit3->level bit */
		if (OldVal & (1 << i))
			mBuf[i] |= 0x08;
		else
			mBuf[i] &= ~0x08;
	}
	for (i = 0; i < CH347_GPIO_CNT; i++) {
		if (iEnable & (1 << i)) {
			mBuf[i] |= 0x80;
			{
				mBuf[i] |= 0x40; // 使能方向位设置,bit6
				if (iSetDirOut & (1 << i))
					mBuf[i] |= 0x20; // bit5
				else
					mBuf[i] &= ~0x20;
			}
			if (iSetDirOut & (1 << i)) // output,bit4
			{
				mBuf[i] |= 0x10; // 使能电平位设置
				if (iSetDataOut & (1 << i))
					mBuf[i] |= 0x08; // bit3
				else
					mBuf[i] &= ~0x08;
			}
		} else {
			mBuf[i] &= ~0x80; // 不操作该GPIOx
		}
	}
	if (!CH347GPIO(fd, mBuf, mBuf))
		goto exit;

	retval = true;

exit:
	return retval;
}

/**
 * CH347GPIO_IRQ_Control - switch of reading interrupt status
 * @fd: file descriptor of device
 * @enable: true: start reading interrupt status continuously, false: stop reading
 *
 * The function return true if successful, false if fail.
 */
static bool CH347GPIO_IRQ_Control(int fd, bool enable)
{
	int retval;

	if (enable)
		retval = ioctl(fd, CH34x_START_IRQ_TASK, NULL);
	else
		retval = ioctl(fd, CH34x_STOP_IRQ_TASK, NULL);

	return retval == 0 ? true : false;
}

/**
 * CH347GPIO_IRQ_Set - gpio irq function setting
 * @fd: file descriptor of device
 * @gpioindex: gpio index number, 0 and 2-7 valid
 * @enable:  0 : disable, 1 : enable
 * @irqtype: IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_BOTH
 * @isr_handler: handler to call when interrupt occurs, if isr disable, the routine will be ignored.
 * 
 * The function return true if success, others if fail.
 */
bool CH347GPIO_IRQ_Set(int fd, uint8_t gpioindex, bool enable,
		       uint8_t irqtype, void *isr_handler)
{
	uint8_t cfg_data[CH347_GPIO_CNT] = { 0 };
	mDeviceInforS *DevObj = NULL;
	bool NewVersion = false;
	int index;

	if ((gpioindex == 1) || (gpioindex > 7))
		return false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	if (gusbch347[index].ChipType == CHIP_CH339W)
		return false;

	if ((gusbch347[index].FirmwareVer >= 0x0341) ||
	    (gusbch347[index].ChipType == CHIP_CH347F))
		NewVersion = true;

	if (!NewVersion)
		return false;

	cfg_data[gpioindex] |= BIT(7);
	cfg_data[gpioindex] |= BIT(6);
	cfg_data[gpioindex] &= ~BIT(5);
	if (enable) {
		cfg_data[gpioindex] |= BIT(2);
		switch (irqtype) {
		case IRQ_TYPE_EDGE_FALLING:
			cfg_data[gpioindex] &= ~BIT(1);
			cfg_data[gpioindex] &= ~BIT(0);
			break;
		case IRQ_TYPE_EDGE_RISING:
			cfg_data[gpioindex] &= ~BIT(1);
			cfg_data[gpioindex] |= BIT(0);
			break;
		case IRQ_TYPE_EDGE_BOTH:
			cfg_data[gpioindex] |= BIT(1);
			cfg_data[gpioindex] &= ~BIT(0);
			break;
		default:
			goto exit;
		}
	} else
		cfg_data[gpioindex] &= ~BIT(2);

	if (!CH347GPIO(fd, cfg_data, cfg_data))
		goto exit;

	if (enable) {
		if ((isr_handler != NULL) &&
		    (gusbch347[index].isr_routine == NULL)) {
			signal(SIGIO, isr_handler);
			gusbch347[index].isr_routine = isr_handler;
			if (CH347GPIO_IRQ_Control(fd, true) == false)
				goto exit;
		} else
			goto exit;
	} else {
		signal(SIGIO, NULL);
		gusbch347[index].isr_routine = NULL;
		if (CH347GPIO_IRQ_Control(fd, false) == false)
			goto exit;
	}

	return true;

exit:
	return false;
}

/**
 * CH347Uart_Open - open device
 * @pathname: device path in /dev directory
 *
 * The function return positive file descriptor if successful, others if fail.
 */
int CH347Uart_Open(const char *pathname)
{
	return CH347OpenDevice(pathname);
}

/**
 * CH347Uart_Close - close device
 * @fd: file descriptor of device
 *
 * The function return true if successful, false if fail.
 */
bool CH347Uart_Close(int fd)
{
	return CH347CloseDevice(fd);
}

/**
 * CH347Uart_GetCfg - read uart setting
 * @fd: file descriptor of device
 * @BaudRate: pointer to uart baudrate
 * @ByteSize: pointer to data bits
 *                  --> 0 : 5 bits
 *                  --> 1 : 6 bits
 *                  --> 2 : 7 bits
 *                  --> 3 : 8 bits
 *                  --> 4 : 16 bits
 * @Parity: pointer parity
 *                  --> 0 : none
 *                  --> 1 : odd
 *                  --> 2 : even
 *                  --> 3 : mark
 *                  --> 4 : space
 * @StopBits: pointer to stop bits
 *                  --> 0 : 1 bit
 *                  --> 1 : 1.5 bits
 *                  --> 2 : 2 bits
 * @ByteTimeout: pointer to receive timeout value, unit: 100us
 *
 * The function return true if successful, false if fail.
 */
bool CH347Uart_GetCfg(int fd, uint32_t *BaudRate, uint8_t *ByteSize,
		      uint8_t *Parity, uint8_t *StopBits,
		      uint8_t *ByteTimeout)
{
	uint8_t mBuffer[512] = { 0 };
	uint32_t mLength, i, RetLen;
	bool retval = false;
	int index = GetDevIndex(fd);

	i = 0;
	mBuffer[i++] = USB20_CMD_INFO_RD;
	mBuffer[i++] = 1; // 后续长度,小端模式
	mBuffer[i++] = 0; // 参数类型
	if (gusbch347[index].UartIndex == 1)
		mBuffer[i++] = INFO_UART1;
	else
		mBuffer[i++] = INFO_UART0;
	mLength = i;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (!CH347WriteData(fd, mBuffer, &mLength))
		goto exit;

	mLength = RetLen = 8 + 3; // 协议规定长度
	if (!CH347ReadData(fd, mBuffer, &mLength) || (mLength != RetLen))
		goto exit;

	*BaudRate = ((mBuffer[3] << 0) & 0x000000FF) +
		    ((mBuffer[4] << 8) & 0x0000FF00) +
		    ((mBuffer[5] << 16) & 0x00FF0000) +
		    ((mBuffer[6] << 24) & 0xFF000000);
	*StopBits = mBuffer[7];
	*Parity = mBuffer[8];
	*ByteSize = mBuffer[9];
	*ByteTimeout = mBuffer[10];

	retval = true;

exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);

	return retval;
}

/**
 * CH347Uart_Init - uart setting
 * @fd: file descriptor of device
 * @BaudRate: uart baudrate
 * @ByteSize: data bits setting
 *                  --> 0 : 5 bits
 *                  --> 1 : 6 bits
 *                  --> 2 : 7 bits
 *                  --> 3 : 8 bits
 *                  --> 4 : 16 bits
 * @Parity: parity setting
 *                  --> 0 : none
 *                  --> 1 : odd
 *                  --> 2 : even
 *                  --> 3 : mark
 *                  --> 4 : space
 * @StopBits: stop bits setting
 *                  --> 0 : 1 bit
 *                  --> 1 : 1.5 bits
 *                  --> 2 : 2 bits
 * @ByteTimeout: receive timeout value, unit: 100us
 *
 * The function return true if successful, false if fail.
 */
bool CH347Uart_Init(int fd, int BaudRate, uint8_t ByteSize, uint8_t Parity,
		    uint8_t StopBits, uint8_t ByteTimeout)
{
	int retval;
	uint8_t mWBuf[4096] = { 0 };
	int i = 0;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	if (gusbch347[index].ch347device.FuncType == TYPE_HID) {
		mWBuf[i++] = 0;
		if (gusbch347[index].UartIndex == 1)
			mWBuf[i++] = USB20_CMD_UART1_INIT;
		else
			mWBuf[i++] = USB20_CMD_UART0_INIT;
		mWBuf[i++] = 0x08;
		mWBuf[i++] = 0;
		/* baudrate setting */
		mWBuf[i++] = (uint8_t)((BaudRate >> 0) & 0xFF);
		mWBuf[i++] = (uint8_t)((BaudRate >> 8) & 0xFF);
		mWBuf[i++] = (uint8_t)((BaudRate >> 16) & 0xFF);
		mWBuf[i++] = (uint8_t)((BaudRate >> 24) & 0xFF);
		/* stop bits setting */
		mWBuf[i++] = StopBits;
		/* parity bits setting */
		mWBuf[i++] = Parity;
		/* data bits setting */
		mWBuf[i++] = ByteSize;
		/* byte timeout setting */
		mWBuf[i++] = ByteTimeout;

		retval = ioctl(fd, HIDIOCSFEATURE(i), mWBuf);
		if (retval < 0)
			goto exit;
	} else if (gusbch347[index].ch347device.FuncType == TYPE_TTY) {
		/* standard tty operation */
		retval = libtty_setopt(fd, BaudRate, ByteSize, StopBits,
				       Parity, ByteTimeout);
		if (retval < 0)
			goto exit;
	}
	return true;

exit:
	return false;
}

/**
 * CH347Uart_Read - read for uart operation
 * @fd: file descriptor of device
 * @oBuffer: pointer to read buffer
 * @ioLength: pointer to read length
 *
 * The function return true if successful, false if fail.
 */
bool CH347Uart_Read(int fd, void *oBuffer, uint32_t *ioLength)
{
	return CH347ReadData(fd, oBuffer, ioLength);
}

/**
 * CH347Uart_Write - write data for uart operation
 * @fd: file descriptor of device
 * @iBuffer: pointer to write buffer
 * @ioLength: pointer to write length
 *
 * The function return true if successful, false if fail.
 */
bool CH347Uart_Write(int fd, void *iBuffer, uint32_t *ioLength)
{
	return CH347WriteData(fd, iBuffer, ioLength);
}

/**
 * CH347I2C_Set - configure i2c interface in stream mode
 * @fd: file descriptor of device
 * @iMode: stream mode
 * ->bit0~2: set I2C SCL rate
 * 			   --> 000 : low rate 20KHz
 * 			   --> 001 : standard rate 100KHz
 * 			   --> 010 : fast rate 400KHz
 * 			   --> 011 : high rate 750KHz
 * 			   --> 100 : rate 50KHz
 * 			   --> 101 : standard rate 200KHz
 * 			   --> 110 : fast rate 1MHz
 * other bits must keep 0
 *
 * The function return true if successful, false if fail.
 */
bool CH347I2C_Set(int fd, int iMode)
{
	uint8_t mBuffer[CH341_PACKET_LENGTH] = { 0 };
	uint32_t mLength;
	mDeviceInforS *DevObj = NULL;
	bool NewVersion = false;
	int index;
	bool retval = false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (gusbch347[index].ChipType == CHIP_CH347F) {
		if (!CH347_FUNC_SWITCH(fd, 3))
			return false;
	}

	if ((gusbch347[index].FirmwareVer >= 0x0341) ||
	    (gusbch347[index].ChipType == CHIP_CH347F))
		NewVersion = true;

	mBuffer[0] = CH341A_CMD_I2C_STREAM;
	if (NewVersion) {
		if (gusbch347[index].ChipType == CHIP_CH347F)
			mBuffer[1] = CH341A_CMD_I2C_STM_SET |
				     (iMode & 0x0f);
		else
			mBuffer[1] = CH341A_CMD_I2C_STM_SET |
				     (iMode & 0x07);
	} else
		mBuffer[1] = CH341A_CMD_I2C_STM_SET | (iMode & 0x03);
	mBuffer[2] = CH341A_CMD_I2C_STM_END;
	mLength = 3;

	if (CH347WriteData(fd, mBuffer, &mLength)) {
		if (mLength >= 2)
			retval = true;
	}
	pthread_mutex_unlock(&gusbch347[index].mutex);

	return retval;
}

/**
 * CH347I2C_SetStretch - I2C Clock Stretch function control
 * @fd: file descriptor of device
 * @enable: I2C Clock Stretch enable, 1 : enable, 0 : disable
 *
 * The function return true if successful, false if fail.
 */
bool CH347I2C_SetStretch(int fd, bool enable)
{
	uint8_t mBuffer[CH341_PACKET_LENGTH] = { 0 };
	uint32_t mLength;
	mDeviceInforS *DevObj = NULL;
	int index;
	bool retval = false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	if ((gusbch347[index].FirmwareVer < 0x0341) &&
	    (gusbch347[index].ChipType == CHIP_CH347T))
		return false;

	mBuffer[0] = CH341A_CMD_I2C_STREAM;
	if (enable)
		mBuffer[1] = CH347_CMD_I2C_STRETCH_Y;
	else
		mBuffer[1] = CH347_CMD_I2C_STRETCH_N;
	mBuffer[2] = CH341A_CMD_I2C_STM_END;
	mLength = 3;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (CH347WriteData(fd, mBuffer, &mLength)) {
		if (mLength >= 2)
			retval = true;
	}
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347I2C_SetDriveMode - I2C signal drive mode control
 * @fd: file descriptor of device
 * @enable: true: open-drain, disable: push-pull
 *
 * The function return true if successful, false if fail.
 */
bool CH347I2C_SetMode(int fd, bool enable)
{
	uint8_t mBuffer[CH341_PACKET_LENGTH] = { 0 };
	uint32_t mLength;
	mDeviceInforS *DevObj = NULL;
	int index;
	bool retval = false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	if ((gusbch347[index].FirmwareVer < 0x0544) ||
	    (gusbch347[index].ChipType != CHIP_CH347T))
		return false;

	mBuffer[0] = CH341A_CMD_I2C_STREAM;
	if (enable)
		mBuffer[1] = CH347_CMD_I2C_OD_Y;
	else
		mBuffer[1] = CH347_CMD_I2C_OD_N;
	mBuffer[2] = CH341A_CMD_I2C_STM_END;
	mLength = 3;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (CH347WriteData(fd, mBuffer, &mLength)) {
		if (mLength >= 2)
			retval = true;
	}
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347I2C_SetDelaymS - delay operation
 * @fd: file descriptor of device
 * @iDelay: delay time in millseconds, 0~500 valid
 *
 * The function return true if successful, false if fail.
 */
bool CH347I2C_SetDelaymS(int fd, int iDelay)
{
	mDeviceInforS *DevObj = NULL;
	int index;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	if (iDelay < 0 || iDelay > 500)
		return false;

	gusbch347[index].I2C_ReadWriteDelay = iDelay;

	return true;
}

/**
 * CH347I2C_SetAckClk_DelayuS - setting delay time between the 8th and 9th I2C clock
 * @fd: file descriptor of device
 * @iDelay: delay time in microseconds, max: 0x3ff
 *
 * The function return true if successful, false if fail.
 */
bool CH347I2C_SetAckClk_DelayuS(int fd, int iDelay)
{
	uint8_t mBuffer[CH341_PACKET_LENGTH] = { 0 };
	uint32_t mLength;
	mDeviceInforS *DevObj = NULL;
	int index;
	bool retval = false;

	DevObj = GetDevObj(fd);
	if (DevObj == NULL)
		return false;

	index = GetDevIndex(fd);
	if (index < 0)
		return false;

	if (((gusbch347[index].FirmwareVer <= 0x0441) &&
	     (gusbch347[index].ChipType == CHIP_CH347T)) ||
	    (gusbch347[index].ChipType == CHIP_CH347F))
		return false;

	mLength = iDelay >= CH347_CMD_I2C_STM_CLK_DLY ?
			  CH347_CMD_I2C_STM_CLK_DLY :
			  iDelay;

	mBuffer[0] = CH341A_CMD_I2C_STREAM;
	mBuffer[1] =
		(uint8_t)(CH347_CMD_I2C_STM_OUT_CLK_US | (mLength >> 8));
	mBuffer[2] = (uint8_t)mLength;
	mBuffer[3] = CH341A_CMD_I2C_STM_END;
	mLength = 4;

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (CH347WriteData(fd, mBuffer, &mLength) == false) {
		retval = false;
		goto exit;
	}
	mLength = iDelay >= CH347_CMD_I2C_STM_CLK_DLY ?
			  CH347_CMD_I2C_STM_CLK_DLY :
			  iDelay;
	mBuffer[0] = CH341A_CMD_I2C_STREAM;
	mBuffer[1] =
		(uint8_t)(CH347_CMD_I2C_STM_IN_CLK_US | (mLength >> 8));
	mBuffer[2] = (uint8_t)mLength;
	mBuffer[3] = CH341A_CMD_I2C_STM_END;
	mLength = 4;

	if (CH347WriteData(fd, mBuffer, &mLength) == false) {
		retval = false;
		goto exit;
	}

	retval = true;

exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return retval;
}

/**
 * CH347StreamI2C - write/read i2c in stream mode
 * @fd: file descriptor of device
 * @iWriteLength: write length
 * @iWriteBuffer: pointer to write buffer
 * @iReadLength: read length
 * @oReadBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347StreamI2C(int fd, int iWriteLength, void *iWriteBuffer,
		    int iReadLength, void *oReadBuffer)
{
	uint8_t mBuffer[MAX_BUFFER_LENGTH * 2] = { 0 };
	uint8_t oBuffer[MAX_BUFFER_LENGTH * 2] = { 0 };
	uint32_t i, j, mLength = 0;
	uint8_t *mWrBuf = mBuffer;
	int mCH347_PACKET_LENGTH_I2C;
	int AckBitCnt = 0, k;
	int packNum = 1;
	int readNum = 0;
	int tmpAckBit = 0;
	int max_len;
	int index = GetDevIndex(fd);
	int iDelay, iStep;

	if (index < 0)
		return false;

	if (gusbch347[index].ch347device.FuncType == TYPE_HID) {
		mCH347_PACKET_LENGTH_I2C = 510;
	} else {
		mCH347_PACKET_LENGTH_I2C = 512;
	}

	mLength = max(iWriteLength, iReadLength);

	max_len = (gusbch347[index].ChipType == CHIP_CH339W) ?
			  MAX_BUFFER_LENGTH_CH339W :
			  MAX_BUFFER_LENGTH;

	if (mLength > max_len)
		return false;

	i = 0;
	AckBitCnt = 0;
	mWrBuf[i++] = CH341A_CMD_I2C_STREAM; // 命令码
	mWrBuf[i++] = CH341A_CMD_I2C_STM_STA; // 产生起始位

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (iWriteLength) {
		for (j = 0; j < iWriteLength;) {
			mLength =
				mCH347_PACKET_LENGTH_I2C -
				(i %
				 mCH347_PACKET_LENGTH_I2C); // 当前包剩余长度,<mCH347_PACKET_LENGTH_I2C
			if (mLength <= 2) {
				while (mLength--)
					mWrBuf[i++] =
						CH341A_CMD_I2C_STM_END; // 当前包提前结束
				mLength = mCH347_PACKET_LENGTH_I2C;
			}
			if (mLength >= mCH347_PACKET_LENGTH_I2C) {
				mWrBuf[i++] =
					CH341A_CMD_I2C_STREAM; // 新包的命令码
				mLength = mCH347_PACKET_LENGTH_I2C - 1;
				packNum++;
			}
			mLength--; // 去掉尾部的提前结束码
			mLength--; // 去掉输出数据的命令码
			if (mLength >
			    CH347_CMD_I2C_STM_MAX) //一次组包最长为63字节
				mLength = CH347_CMD_I2C_STM_MAX;
			if (mLength > iWriteLength - j)
				mLength = iWriteLength -
					  j; // 本次输出有效数据长度
			mWrBuf[i++] =
				(uint8_t)(CH341A_CMD_I2C_STM_OUT |
					  mLength); // 输出数据,位5-位0为长度
			AckBitCnt += mLength;
			while (mLength--)
				mWrBuf[i++] = *((uint8_t *)iWriteBuffer +
						j++); // 复制数据
		}
	}
	if (iReadLength) {
		mLength =
			mCH347_PACKET_LENGTH_I2C -
			i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
		if (mLength <= 3) {
			while (mLength--)
				mWrBuf[i++] =
					CH341A_CMD_I2C_STM_END; // 当前包提前结束
			mLength = mCH347_PACKET_LENGTH_I2C;
		}
		if (mLength >= mCH347_PACKET_LENGTH_I2C)
			mWrBuf[i++] =
				CH341A_CMD_I2C_STREAM; // 新包的命令码
		iDelay = gusbch347[index].I2C_ReadWriteDelay;
		if (iWriteLength > 1) { // 先输出
			while (iDelay) {
				iStep = iDelay >= CH341A_CMD_I2C_STM_DLY ?
						CH341A_CMD_I2C_STM_DLY :
						iDelay;
				iDelay -= iStep;
				mWrBuf[i++] =
					(uint8_t)(CH341A_CMD_I2C_STM_MS |
						  iStep);
			}
			mWrBuf[i++] = CH341A_CMD_I2C_STM_STA; // 产生起始位
			mWrBuf[i++] =
				(uint8_t)(CH341A_CMD_I2C_STM_OUT |
					  1); // 输出数据,位5-位0为长度
			AckBitCnt += 1;
			mWrBuf[i++] =
				*(uint8_t *)iWriteBuffer |
				0x01; // I2C目标设备地址,最低位为1则进行读操作
		} else if (iWriteLength) { // 输出一字节后直接输入
			i--;
			mWrBuf[i++] =
				*(uint8_t *)iWriteBuffer |
				0x01; // I2C目标设备地址,最低位为1则进行读操作
			while (iDelay) {
				iStep = iDelay >= CH341A_CMD_I2C_STM_DLY ?
						CH341A_CMD_I2C_STM_DLY :
						iDelay;
				iDelay -= iStep;
				mWrBuf[i++] =
					(uint8_t)(CH341A_CMD_I2C_STM_MS |
						  iStep);
			}
		}
		tmpAckBit = AckBitCnt;
		for (j = 1; j < iReadLength;) {
			mLength =
				mCH347_PACKET_LENGTH_I2C -
				i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
			if (mLength <= 1) {
				if (mLength)
					mWrBuf[i++] =
						CH341A_CMD_I2C_STM_END; // 当前包提前结束
				mLength = mCH347_PACKET_LENGTH_I2C;
			}
			if (mLength >= mCH347_PACKET_LENGTH_I2C) {
				mWrBuf[i++] =
					CH341A_CMD_I2C_STREAM; // 新包的命令码
				packNum++;
			}
			mLength = (iReadLength - j) >=
						  CH347_CMD_I2C_STM_MAX ?
					  CH347_CMD_I2C_STM_MAX :
					  (iReadLength -
					   j); // 本次输入有效数据长度
			if ((readNum + mLength) >=
			    (mCH347_PACKET_LENGTH_I2C -
			     tmpAckBit)) { // 当前包将满
				mLength = mCH347_PACKET_LENGTH_I2C -
					  i % mCH347_PACKET_LENGTH_I2C;
				while (mLength--)
					mWrBuf[i++] =
						CH341A_CMD_I2C_STM_END; // 当前包提前结束
				readNum = 0;
				tmpAckBit = 0;
				continue;
			}
			mWrBuf[i++] =
				(uint8_t)(CH341A_CMD_I2C_STM_IN |
					  mLength); // 输入数据,位5-位0为长度
			j += mLength;
			readNum += mLength;
		}
		mLength =
			mCH347_PACKET_LENGTH_I2C -
			i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
		if (mLength <= 1) {
			if (mLength)
				mWrBuf[i++] =
					CH341A_CMD_I2C_STM_END; // 当前包提前结束
			mLength = mCH347_PACKET_LENGTH_I2C;
		}
		if (mLength >= mCH347_PACKET_LENGTH_I2C)
			mWrBuf[i++] =
				CH341A_CMD_I2C_STREAM; // 新包的命令码
		mWrBuf[i++] =
			CH341A_CMD_I2C_STM_IN; // 输入数据,只接收一个字节并发送无应答
	}
	mLength =
		mCH347_PACKET_LENGTH_I2C -
		i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
	if (mLength <= 1) {
		if (mLength)
			mWrBuf[i++] =
				CH341A_CMD_I2C_STM_END; // 当前包提前结束
		mLength = mCH347_PACKET_LENGTH_I2C;
	}
	if (mLength >= mCH347_PACKET_LENGTH_I2C)
		mWrBuf[i++] = CH341A_CMD_I2C_STREAM; // 新包的命令码
	mWrBuf[i++] = CH341A_CMD_I2C_STM_STO; // 产生停止位
	mWrBuf[i++] = CH341A_CMD_I2C_STM_END; // 当前包提前结束
	mLength = 0;

	// 增加了I2C的写应答返回
	if (iReadLength) {
		// printf("iReadLength + AckBitCnt: %d\n", iReadLength + AckBitCnt);
		// 执行数据流命令,先输出再输入
		j = CH347WriteRead(fd, i, mWrBuf, CH347_PACKET_LENGTH,
				   packNum, &mLength, oBuffer);
		// printf("mLength: %d, AckBitCnt: %d\n", mLength, AckBitCnt);
		j = (mLength == (iReadLength + AckBitCnt));
		if (j) {
			for (k = 0; k < AckBitCnt; k++) {
				if (((uint8_t *)oBuffer)[k] != 1) {
					// printf("nack here, buf[%d]: 0x%2x\n", k, ((uint8_t *)oBuffer)[k]);
					j = false;
					break;
				}
			}
			if (j) {
				mLength -= AckBitCnt; // 去掉I2C写应答字节
				// printf("AckBitCnd: %d, mLength: %d, oBuffer0: 0x%x, oBuffer1: 0x%x\n", AckBitCnt, mLength,
				// 	*((uint8_t *)oBuffer + AckBitCnt), *((uint8_t *)oBuffer + AckBitCnt + 1));
				memmove((uint8_t *)oBuffer,
					(uint8_t *)oBuffer + AckBitCnt,
					mLength);
				// printf("mLength: %d, oBuffer0: 0x%x, oBuffer1: 0x%x\n", mLength,
				// 	*((uint8_t *)oBuffer), *((uint8_t *)oBuffer + 1));
				memcpy(oReadBuffer, oBuffer, mLength);
			}
		}
	} else {
		// 执行数据流命令,先输出再输入
		j = CH347WriteRead(fd, i, mWrBuf, CH347_PACKET_LENGTH,
				   packNum, &mLength, oBuffer);
		if (j == false)
			goto exit;

		j = (mLength == AckBitCnt);
		if (j) // 判断写应答位
		{
			// 判断ACK
			for (k = 0; k < AckBitCnt; k++) {
				if (oBuffer[k] != 0x01) {
					j = false;
					break;
				}
			}
		}
	}
exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return j ? true : false;
}

/**
 * CH347StreamI2C_RetAck - write/read i2c in stream mode
 * @fd: file descriptor of device
 * @iWriteLength: write length
 * @iWriteBuffer: pointer to write buffer
 * @iReadLength: read length
 * @oReadBuffer: pointer to read buffer
 * @retAck: pointer to available ack count
 *
 * The function return true if successful, false if fail.
 */
bool CH347StreamI2C_RetAck(int fd, int iWriteLength, void *iWriteBuffer,
			   int iReadLength, void *oReadBuffer, int *retAck)
{
	uint8_t mBuffer[MAX_BUFFER_LENGTH * 2] = { 0 };
	uint8_t oBuffer[MAX_BUFFER_LENGTH * 2] = { 0 };
	uint32_t i, j, mLength = 0;
	uint8_t *mWrBuf = mBuffer;
	int mCH347_PACKET_LENGTH_I2C;
	int AckBitCnt = 0, k;
	int packNum = 1;
	int readNum = 0;
	int tmpAckBit = 0;
	int max_len;
	int index = GetDevIndex(fd);
	int iDelay, iStep;

	if (index < 0)
		return false;

	if (gusbch347[index].ch347device.FuncType == TYPE_HID) {
		mCH347_PACKET_LENGTH_I2C = 510;
	} else {
		mCH347_PACKET_LENGTH_I2C = 512;
	}

	mLength = max(iWriteLength, iReadLength);

	max_len = (gusbch347[index].ChipType == CHIP_CH339W) ?
			  MAX_BUFFER_LENGTH_CH339W :
			  MAX_BUFFER_LENGTH;

	if (mLength > max_len)
		return false;

	i = 0;
	AckBitCnt = 0;
	mWrBuf[i++] = CH341A_CMD_I2C_STREAM; // 命令码
	mWrBuf[i++] = CH341A_CMD_I2C_STM_STA; // 产生起始位

	pthread_mutex_lock(&gusbch347[index].mutex);
	if (iWriteLength) {
		for (j = 0; j < iWriteLength;) {
			mLength =
				mCH347_PACKET_LENGTH_I2C -
				(i %
				 mCH347_PACKET_LENGTH_I2C); // 当前包剩余长度,<mCH347_PACKET_LENGTH_I2C
			if (mLength <= 2) {
				while (mLength--)
					mWrBuf[i++] =
						CH341A_CMD_I2C_STM_END; // 当前包提前结束
				mLength = mCH347_PACKET_LENGTH_I2C;
			}
			if (mLength >= mCH347_PACKET_LENGTH_I2C) {
				mWrBuf[i++] =
					CH341A_CMD_I2C_STREAM; // 新包的命令码
				mLength = mCH347_PACKET_LENGTH_I2C - 1;
				packNum++;
			}
			mLength--; // 去掉尾部的提前结束码
			mLength--; // 去掉输出数据的命令码
			if (mLength >
			    CH347_CMD_I2C_STM_MAX) //一次组包最长为63字节
				mLength = CH347_CMD_I2C_STM_MAX;
			if (mLength > iWriteLength - j)
				mLength = iWriteLength -
					  j; // 本次输出有效数据长度
			mWrBuf[i++] =
				(uint8_t)(CH341A_CMD_I2C_STM_OUT |
					  mLength); // 输出数据,位5-位0为长度
			AckBitCnt += mLength;
			while (mLength--)
				mWrBuf[i++] = *((uint8_t *)iWriteBuffer +
						j++); // 复制数据
		}
	}
	if (iReadLength) {
		mLength =
			mCH347_PACKET_LENGTH_I2C -
			i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
		if (mLength <= 3) {
			while (mLength--)
				mWrBuf[i++] =
					CH341A_CMD_I2C_STM_END; // 当前包提前结束
			mLength = mCH347_PACKET_LENGTH_I2C;
		}
		if (mLength >= mCH347_PACKET_LENGTH_I2C)
			mWrBuf[i++] =
				CH341A_CMD_I2C_STREAM; // 新包的命令码
		iDelay = gusbch347[index].I2C_ReadWriteDelay;
		if (iWriteLength > 1) { // 先输出
			while (iDelay) {
				iStep = iDelay >= CH341A_CMD_I2C_STM_DLY ?
						CH341A_CMD_I2C_STM_DLY :
						iDelay;
				iDelay -= iStep;
				mWrBuf[i++] =
					(uint8_t)(CH341A_CMD_I2C_STM_MS |
						  iStep);
			}
			mWrBuf[i++] = CH341A_CMD_I2C_STM_STA; // 产生起始位
			mWrBuf[i++] =
				(uint8_t)(CH341A_CMD_I2C_STM_OUT |
					  1); // 输出数据,位5-位0为长度
			AckBitCnt += 1;
			mWrBuf[i++] =
				*(uint8_t *)iWriteBuffer |
				0x01; // I2C目标设备地址,最低位为1则进行读操作
		} else if (iWriteLength) { // 输出一字节后直接输入
			i--;
			mWrBuf[i++] =
				*(uint8_t *)iWriteBuffer |
				0x01; // I2C目标设备地址,最低位为1则进行读操作
			while (iDelay) {
				iStep = iDelay >= CH341A_CMD_I2C_STM_DLY ?
						CH341A_CMD_I2C_STM_DLY :
						iDelay;
				iDelay -= iStep;
				mWrBuf[i++] =
					(uint8_t)(CH341A_CMD_I2C_STM_MS |
						  iStep);
			}
		}
		tmpAckBit = AckBitCnt;
		for (j = 1; j < iReadLength;) {
			mLength =
				mCH347_PACKET_LENGTH_I2C -
				i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
			if (mLength <= 1) {
				if (mLength)
					mWrBuf[i++] =
						CH341A_CMD_I2C_STM_END; // 当前包提前结束
				mLength = mCH347_PACKET_LENGTH_I2C;
			}
			if (mLength >= mCH347_PACKET_LENGTH_I2C) {
				mWrBuf[i++] =
					CH341A_CMD_I2C_STREAM; // 新包的命令码
				packNum++;
			}
			mLength = (iReadLength - j) >=
						  CH347_CMD_I2C_STM_MAX ?
					  CH347_CMD_I2C_STM_MAX :
					  (iReadLength -
					   j); // 本次输入有效数据长度
			if ((readNum + mLength) >=
			    (mCH347_PACKET_LENGTH_I2C -
			     tmpAckBit)) { // 当前包将满
				mLength = mCH347_PACKET_LENGTH_I2C -
					  i % mCH347_PACKET_LENGTH_I2C;
				while (mLength--)
					mWrBuf[i++] =
						CH341A_CMD_I2C_STM_END; // 当前包提前结束
				readNum = 0;
				tmpAckBit = 0;
				continue;
			}
			mWrBuf[i++] =
				(uint8_t)(CH341A_CMD_I2C_STM_IN |
					  mLength); // 输入数据,位5-位0为长度
			j += mLength;
			readNum += mLength;
		}
		mLength =
			mCH347_PACKET_LENGTH_I2C -
			i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
		if (mLength <= 1) {
			if (mLength)
				mWrBuf[i++] =
					CH341A_CMD_I2C_STM_END; // 当前包提前结束
			mLength = mCH347_PACKET_LENGTH_I2C;
		}
		if (mLength >= mCH347_PACKET_LENGTH_I2C)
			mWrBuf[i++] =
				CH341A_CMD_I2C_STREAM; // 新包的命令码
		mWrBuf[i++] =
			CH341A_CMD_I2C_STM_IN; // 输入数据,只接收一个字节并发送无应答
	}
	mLength =
		mCH347_PACKET_LENGTH_I2C -
		i % mCH347_PACKET_LENGTH_I2C; // 当前包剩余长度,<CH347_CMD_I2C_STM_MAX
	if (mLength <= 1) {
		if (mLength)
			mWrBuf[i++] =
				CH341A_CMD_I2C_STM_END; // 当前包提前结束
		mLength = mCH347_PACKET_LENGTH_I2C;
	}
	if (mLength >= mCH347_PACKET_LENGTH_I2C)
		mWrBuf[i++] = CH341A_CMD_I2C_STREAM; // 新包的命令码
	mWrBuf[i++] = CH341A_CMD_I2C_STM_STO; // 产生停止位
	mWrBuf[i++] = CH341A_CMD_I2C_STM_END; // 当前包提前结束
	mLength = 0;

	// 增加了I2C的写应答返回
	if (iReadLength) {
		// 执行数据流命令,先输出再输入
		j = CH347WriteRead(fd, i, mWrBuf, CH347_PACKET_LENGTH,
				   packNum, &mLength, oBuffer);
		j = (mLength == (iReadLength + AckBitCnt));
		if (j) {
			for (k = 0; k < AckBitCnt; k++) {
				if (((uint8_t *)oBuffer)[k] != 1) {
					j = false;
					break;
				}
			}
			if (j) {
				mLength -= AckBitCnt; // 去掉I2C写应答字节
				memmove((uint8_t *)oBuffer,
					(uint8_t *)oBuffer + AckBitCnt,
					mLength);
				memcpy(oReadBuffer, oBuffer, mLength);
			}
		}
	} else {
		// 执行数据流命令,先输出再输入
		j = CH347WriteRead(fd, i, mWrBuf, CH347_PACKET_LENGTH,
				   packNum, &mLength, oBuffer);
		if (j == false)
			goto exit;

		j = (mLength == AckBitCnt);
		if (j) // 判断写应答位
		{
			// 判断ACK
			for (k = 0; k < AckBitCnt; k++) {
				if (oBuffer[k] != 0x01) {
					j = false;
					break;
				}
			}
		}
	}
	*retAck = k;
	if ((*retAck == AckBitCnt) || (*retAck == (AckBitCnt - 1)))
		j = true;
exit:
	pthread_mutex_unlock(&gusbch347[index].mutex);
	return j ? true : false;
}
/**
 * CH347ReadEEPROM - read data from eeprom
 * @fd: file descriptor of device
 * @iEepromID: eeprom type
 * @iAddr: address of eeprom
 * @iLength: read length
 * @oBuffer: pointer to read buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347ReadEEPROM(int fd, EEPROM_TYPE iEepromID, int iAddr, int iLength,
		     uint8_t *oBuffer)
{
	int mLen;
	uint8_t mWrBuf[4] = { 0 };

	if (iEepromID >= ID_24C01 &&
	    iEepromID <= ID_24C16) { // 7位-11位地址
		while (iLength) {
			mWrBuf[0] =
				(uint8_t)(0xA0 |
					  ((iAddr >> 7) &
					   0x0E)); // I2C目标设备地址,最低位为1则进行读操作
			mWrBuf[1] = (uint8_t)iAddr; // 低8位地址
			mLen = min(iLength, DEFAULT_BUFFER_LEN);
			if (CH347StreamI2C(fd, 2, mWrBuf, mLen, oBuffer) ==
			    false)
				return false; // 处理I2C数据流
			iAddr += mLen;
			iLength -= mLen;
			oBuffer += mLen;
		}
	} else if (iEepromID >= ID_24C32 &&
		   iEepromID <= ID_24C4096) { // 12位-19位地址
		while (iLength) {
			mWrBuf[0] =
				(uint8_t)(0xA0 |
					  ((iAddr >> 15) &
					   0x0E)); // I2C目标设备地址,最低位为1则进行读操作
			mWrBuf[1] = (uint8_t)(iAddr >> 8); // 高8位地址
			mWrBuf[2] = (uint8_t)iAddr; // 低8位地址
			mLen = min(iLength, DEFAULT_BUFFER_LEN);
			if (CH347StreamI2C(fd, 3, mWrBuf, mLen, oBuffer) ==
			    false)
				return false; // 处理I2C数据流
			iAddr += mLen;
			iLength -= mLen;
			oBuffer += mLen;
		}
	} else
		return false;

	return true;
}

/**
 * CH347WriteEEPROM - write data to eeprom
 * @fd: file descriptor of device
 * @iEepromID: eeprom type
 * @iAddr: address of eeprom
 * @iLength: write length
 * @iBuffer: pointer to write buffer
 *
 * The function return true if successful, false if fail.
 */
bool CH347WriteEEPROM(int fd, EEPROM_TYPE iEepromID, int iAddr,
		      int iLength, uint8_t *iBuffer)
{
	int mLen;
	uint8_t mWrBuf[256] = { 0 };

	if (iEepromID >= ID_24C01 &&
	    iEepromID <= ID_24C16) { // 7位-11位地址
		while (iLength) {
			mWrBuf[0] =
				(uint8_t)(0xA0 |
					  ((iAddr >> 7) &
					   0x0E)); // I2C目标设备地址,最低位为0则进行写操作
			mWrBuf[1] = (uint8_t)iAddr; // 低8位地址
			mLen = iEepromID >= ID_24C04 ?
				       16 - (iAddr & 15) :
				       8 - (iAddr & 7); // 不超过页长度
			if (mLen > iLength)
				mLen = iLength;
			memcpy(&mWrBuf[2], iBuffer, mLen);
			if (CH347StreamI2C(fd, 2 + mLen, mWrBuf, 0,
					   NULL) == false)
				return false; // 处理I2C数据流
			// CH347I2C_SetDelaymS(fd, 10); // 设置10毫秒硬件异步延时
			usleep(1000 * 10);
			iAddr += mLen;
			iLength -= mLen;
			iBuffer += mLen;
		}
	} else if (iEepromID >= ID_24C32 &&
		   iEepromID <= ID_24C4096) { // 12位-19位地址
		while (iLength) {
			mWrBuf[0] =
				(uint8_t)(0xA0 |
					  ((iAddr >> 15) &
					   0x0E)); // I2C目标设备地址,最低位为0则进行写操作
			mWrBuf[1] = (uint8_t)(iAddr >> 8); // 高8位地址
			mWrBuf[2] = (uint8_t)iAddr; // 低8位地址
			mLen = iEepromID >= ID_24C512 ?
				       128 - (iAddr & 127) :
				       (iEepromID >= ID_24C128 ?
						64 - (iAddr & 63) :
						32 - (iAddr &
						      31)); // 不超过页长度
			if (mLen > iLength)
				mLen = iLength;
			memcpy(&mWrBuf[3], iBuffer, mLen);
			if (CH347StreamI2C(fd, 3 + mLen, mWrBuf, 0,
					   NULL) == false)
				return false; // 处理I2C数据流
			// CH347I2C_SetDelaymS(fd, 10); // 设置10毫秒硬件异步延时
			usleep(1000 * 10);
			iAddr += mLen;
			iLength -= mLen;
			iBuffer += mLen;
		}
	} else
		return false;
	return true;
}

/*===========================================================================================*/
// CH346 APIs
/*===========================================================================================*/

/**
 * CH346_SetMode - set chip work mode
 * @fd: file descriptor of device
 * @Mode: work mode, 0->mode0, 1->mode1
 *
 * The function return true if successful, false if fail.
 */
bool CH346_SetMode(int fd, uint8_t Mode)
{
	int retval;
	int index = GetDevIndex(fd);

	if (index < 0 || gusbch347[index].ChipType != CHIP_CH346C ||
	    Mode > 1)
		return false;

	retval = ioctl(fd, CH34x_SET_MODE, &Mode);

	return retval == 0 ? true : false;
}

/**
 * CH346_GetMode - get chip work mode
 * @fd: file descriptor of device
 * @Mode: pointer to work mode, 0->mode0, 1->mode1
 *
 * The function return true if successful, false if fail.
 */
bool CH346_GetMode(int fd, uint8_t *Mode)
{
	int retval;
	int index = GetDevIndex(fd);

	if (index < 0)
		return false;

	retval = ioctl(fd, CH34x_CHIP_VERSION, Mode);
	if (retval != 0) {
		return false;
	}
	gusbch347[index].workmode = *Mode;

	return true;
}

/**
 * CH346_Slave_Control - switch of reading FIFO data from master 
 * @fd: file descriptor of device
 * @enable: true: start reading continuously, false: stop reading
 *
 * The function return true if successful, false if fail.
 */
bool CH346_Slave_Control(int fd, bool enable)
{
	int retval;
	int index = GetDevIndex(fd);

	if (gusbch347[index].ChipType != CHIP_CH346C)
		return false;

	if (enable)
		retval = ioctl(fd, CH34x_START_BUFFERED_UPLOAD, NULL);
	else
		retval = ioctl(fd, CH34x_STOP_BUFFERED_UPLOAD, NULL);

	return retval == 0 ? true : false;
}

/**
 * CH346_Slave_WriteData - write fifo/spi data
 * @fd: file descriptor of device
 * @iBuffer: pointer to write buffer
 * @ioLength: pointer to write length
 *
 * The function return true if successful, false if fail.
 */
bool CH346_Slave_WriteData(int fd, void *iBuffer, uint32_t *ioLength)
{
	int index = GetDevIndex(fd);

#ifdef VERBOSE_DEBUG
	int i;
	printf("CH346WriteData-->Len: %d\n", *ioLength);
	for (i = 0; i < *ioLength; i++) {
		printf("0x%2x ", *((uint8_t *)iBuffer + i));
	}
	printf("CH346WriteData Over.\n\n");
#endif

	if (index < 0)
		return false;

	if (*ioLength > MAX_BUFFER_LENGTH_CH346C)
		return false;

	return CH34xWriteData(fd, iBuffer, ioLength);
}

/**
 * CH346_Slave_QweryData - get spi/fifo data length
 * @fd: file descriptor of device
 * @oLength: pointer to read length
 *
 * The function return true if successful, false if fail.
 */
bool CH346_Slave_QweryData(int fd, uint32_t *oLength)
{
	return CH347SPI_Slave_QweryData(fd, oLength);
}

/**
 * CH346_Slave_FIFOReset - reset spi/fifo data fifo
 * @fd: file descriptor of device
 *
 * The function return true if successful, false if fail.
 */
bool CH346_Slave_FIFOReset(int fd)
{
	return CH347SPI_Slave_FIFOReset(fd);
}

/**
 * CH346_Slave_ReadData - read spi/fifo data in slave mode
 * @fd: file descriptor of device
 * @oReadBuffer: pointer to read buffer
 * @oReadLength: pointer to read length
 *
 * The function return true if successful, false if fail.
 */
bool CH346_Slave_ReadData(int fd, void *oReadBuffer, uint32_t *oReadLength)
{
	return CH347SPI_Slave_ReadData(fd, oReadBuffer, oReadLength);
}
