// 2004.05.28, 2004.10.20, 2005.01.08, 2005.03.25, 2005.04.28, 2005.07.18, 2005.07.28, 2005.09.19, 2007.12.19, 2008.10.15,2022.5
//****************************************
//**  Copyright  (C)  WCH   1999-2022   **
//**  Web:  http://www.winchiphead.com  **
//****************************************
//**  DLL for USB interface chip CH341  **
//**  C, VC5.0                          **
//****************************************
//
// USB总线接口芯片CH341并口应用层接口库 V2.2
// 南京沁恒电子有限公司  作者: W.ch 2008.10
// CH341-DLL  V2.2
// 运行环境: Windows 98/ME, Windows 2000/XP/7/10/11
// support USB chip: CH341, CH341A， CH347 SPI/I2C
// USB => Parallel, I2C, SPI, ...
//

#ifndef		_CH341_DLL_H
#define		_CH341_DLL_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef _WIN64
#define		mOFFSET( s, m )			( (ULONG_PTR) & ( ( ( s * ) 0 ) -> m ) )	// 定义获取结构成员相对偏移地址的宏
#else
#define		mOFFSET( s, m )			( (ULONG) & ( ( ( s * ) 0 ) -> m ) )	// 定义获取结构成员相对偏移地址的宏
#endif

#ifndef		max
#define		max( a, b )				( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )	// 较大值
#endif

#ifndef		min
#define		min( a, b )				( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )	// 较小值
#endif

#ifdef		ExAllocatePool
#undef		ExAllocatePool						// 删除带TAG的内存分配
#endif

#ifndef		NTSTATUS
typedef		LONG	NTSTATUS;					// 返回状态
#endif


typedef	struct	_USB_SETUP_PKT {				// USB控制传输的建立阶段的数据请求包结构
	UCHAR			mUspReqType;				// 00H 请求类型
	UCHAR			mUspRequest;				// 01H 请求代码
	union	{
		struct	{
			UCHAR	mUspValueLow;				// 02H 值参数低字节
			UCHAR	mUspValueHigh;				// 03H 值参数高字节
		};
		USHORT		mUspValue;					// 02H-03H 值参数
	};
	union	{
		struct	{
			UCHAR	mUspIndexLow;				// 04H 索引参数低字节
			UCHAR	mUspIndexHigh;				// 05H 索引参数高字节
		};
		USHORT		mUspIndex;					// 04H-05H 索引参数
	};
	USHORT			mLength;					// 06H-07H 数据阶段的数据长度
} mUSB_SETUP_PKT, *mPUSB_SETUP_PKT;


#define		mCH341_PACKET_LENGTH	32			// CH341支持的数据包的长度
#define		mCH341_PKT_LEN_SHORT	8			// CH341支持的短数据包的长度


typedef	struct	_WIN32_COMMAND {				// 定义WIN32命令接口结构
	union	{
		ULONG		mFunction;					// 输入时指定功能代码或者管道号
		NTSTATUS	mStatus;					// 输出时返回操作状态
	};
	ULONG			mLength;					// 存取长度,返回后续数据的长度
	union	{
		mUSB_SETUP_PKT	mSetupPkt;				// USB控制传输的建立阶段的数据请求
		UCHAR			mBuffer[ 512];      	// 数据缓冲区,长度为0至255B
	};
} mWIN32_COMMAND, *mPWIN32_COMMAND;


// WIN32应用层接口命令
#define		IOCTL_CH341_COMMAND		( FILE_DEVICE_UNKNOWN << 16 | FILE_ANY_ACCESS << 14 | 0x0f34 << 2 | METHOD_BUFFERED )	// 专用接口

#define		mWIN32_COMMAND_HEAD		mOFFSET( mWIN32_COMMAND, mBuffer )	// WIN32命令接口的头长度

#define		mCH341_MAX_NUMBER		16			// 最多同时连接的CH341数

#define		mMAX_BUFFER_LENGTH		0x1000		// 数据缓冲区最大长度4096

#define		mMAX_COMMAND_LENGTH		( mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH )	// 最大数据长度加上命令结构头的长度

#define		mDEFAULT_BUFFER_LEN		0x0400		// 数据缓冲区默认长度1024

#define		mDEFAULT_COMMAND_LEN	( mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN )	// 默认数据长度加上命令结构头的长度


// CH341端点地址
#define		mCH341_ENDP_INTER_UP	0x81		// CH341的中断数据上传端点的地址
#define		mCH341_ENDP_INTER_DOWN	0x01		// CH341的中断数据下传端点的地址
#define		mCH341_ENDP_DATA_UP		0x82		// CH341的数据块上传端点的地址
#define		mCH341_ENDP_DATA_DOWN	0x02		// CH341的数据块下传端点的地址


// 设备层接口提供的管道操作命令
#define		mPipeDeviceCtrl			0x00000004	// CH341的综合控制管道
#define		mPipeInterUp			0x00000005	// CH341的中断数据上传管道
#define		mPipeDataUp				0x00000006	// CH341的数据块上传管道
#define		mPipeDataDown			0x00000007	// CH341的数据块下传管道

// 应用层接口的功能代码
#define		mFuncNoOperation		0x00000000	// 无操作
#define		mFuncGetVersion			0x00000001	// 获取驱动程序版本号
#define		mFuncGetConfig			0x00000002	// 获取USB设备配置描述符
#define		mFuncSetTimeout			0x00000009	// 设置USB通讯超时
#define		mFuncSetExclusive		0x0000000b	// 设置独占使用
#define		mFuncResetDevice		0x0000000c	// 复位USB设备
#define		mFuncResetPipe			0x0000000d	// 复位USB管道
#define		mFuncAbortPipe			0x0000000e	// 取消USB管道的数据请求

// CH341并口专用的功能代码
#define		mFuncSetParaMode		0x0000000f	// 设置并口模式
#define		mFuncReadData0			0x00000010	// 从并口读取数据块0
#define		mFuncReadData1			0x00000011	// 从并口读取数据块1
#define		mFuncWriteData0			0x00000012	// 向并口写入数据块0
#define		mFuncWriteData1			0x00000013	// 向并口写入数据块1
#define		mFuncWriteRead			0x00000014	// 先输出再输入
#define		mFuncBufferMode			0x00000020	// 设定缓冲上传模式及查询缓冲区中的数据长度
#define		mFuncBufferModeDn		0x00000021	// 设定缓冲下传模式及查询缓冲区中的数据长度
#define		mFuncGetVersionEx		0x00000022	// 获取驱动程序版本号及芯片型号


// USB设备标准请求代码
#define		mUSB_CLR_FEATURE		0x01
#define		mUSB_SET_FEATURE		0x03
#define		mUSB_GET_STATUS			0x00
#define		mUSB_SET_ADDRESS		0x05
#define		mUSB_GET_DESCR			0x06
#define		mUSB_SET_DESCR			0x07
#define		mUSB_GET_CONFIG			0x08
#define		mUSB_SET_CONFIG			0x09
#define		mUSB_GET_INTERF			0x0a
#define		mUSB_SET_INTERF			0x0b
#define		mUSB_SYNC_FRAME			0x0c

// CH341控制传输的厂商专用请求类型
#define		mCH341_VENDOR_READ		0xC0		// 通过控制传输实现的CH341厂商专用读操作
#define		mCH341_VENDOR_WRITE		0x40		// 通过控制传输实现的CH341厂商专用写操作

// CH341控制传输的厂商专用请求代码
#define		mCH341_PARA_INIT		0xB1		// 初始化并口
#define		mCH341_I2C_STATUS		0x52		// 获取I2C接口的状态
#define		mCH341_I2C_COMMAND		0x53		// 发出I2C接口的命令

// CH341并口操作命令代码
#define		mCH341_PARA_CMD_R0		0xAC		// 从并口读数据0,次字节为长度
#define		mCH341_PARA_CMD_R1		0xAD		// 从并口读数据1,次字节为长度
#define		mCH341_PARA_CMD_W0		0xA6		// 向并口写数据0,从次字节开始为数据流
#define		mCH341_PARA_CMD_W1		0xA7		// 向并口写数据1,从次字节开始为数据流
#define		mCH341_PARA_CMD_STS		0xA0		// 获取并口状态

// CH341A并口操作命令代码
#define		mCH341A_CMD_SET_OUTPUT	0xA1		// 设置并口输出
#define		mCH341A_CMD_IO_ADDR		0xA2		// MEM带地址读写/输入输出,从次字节开始为命令流
#define		mCH341A_CMD_PRINT_OUT	0xA3		// PRINT兼容打印方式输出,从次字节开始为数据流
#define		mCH341A_CMD_PWM_OUT		0xA4		// PWM数据输出的命令包,从次字节开始为数据流
#define		mCH341A_CMD_SHORT_PKT	0xA5		// 短包,次字节是该命令包的真正长度,再次字节及之后的字节是原命令包
#define		mCH341A_CMD_SPI_STREAM	0xA8		// SPI接口的命令包,从次字节开始为数据流
//#define		mCH341A_CMD_SIO_STREAM	0xA9		// SIO接口的命令包,从次字节开始为数据流
#define		mCH341A_CMD_I2C_STREAM	0xAA		// I2C接口的命令包,从次字节开始为I2C命令流
#define		mCH341A_CMD_UIO_STREAM	0xAB		// UIO接口的命令包,从次字节开始为命令流
#define		mCH341A_CMD_PIO_STREAM	0xAE		// PIO接口的命令包,从次字节开始为数据流

// CH341A控制传输的厂商专用请求代码
#define		mCH341A_BUF_CLEAR		0xB2		// 清除未完成的数据
#define		mCH341A_I2C_CMD_X		0x54		// 发出I2C接口的命令,立即执行
#define		mCH341A_DELAY_MS		0x5E		// 以亳秒为单位延时指定时间
#define		mCH341A_GET_VER			0x5F		// 获取芯片版本

#define		mCH341_EPP_IO_MAX		( mCH341_PACKET_LENGTH - 1 )	// CH341在EPP/MEM方式下单次读写数据块的最大长度
#define		mCH341A_EPP_IO_MAX		0xFF		// CH341A在EPP/MEM方式下单次读写数据块的最大长度

#define		mCH341A_CMD_IO_ADDR_W	0x00		// MEM带地址读写/输入输出的命令流:写数据,位6-位0为地址,下一个字节为待写数据
#define		mCH341A_CMD_IO_ADDR_R	0x80		// MEM带地址读写/输入输出的命令流:读数据,位6-位0为地址,读出数据一起返回

#define		mCH341A_CMD_I2C_STM_STA	0x74		// I2C接口的命令流:产生起始位
#define		mCH341A_CMD_I2C_STM_STO	0x75		// I2C接口的命令流:产生停止位
#define		mCH341A_CMD_I2C_STM_OUT	0x80		// I2C接口的命令流:输出数据,位5-位0为长度,后续字节为数据,0长度则只发送一个字节并返回应答
#define		mCH341A_CMD_I2C_STM_IN	0xC0		// I2C接口的命令流:输入数据,位5-位0为长度,0长度则只接收一个字节并发送无应答
#define		mCH341A_CMD_I2C_STM_MAX	( min( 0x3F, mCH341_PACKET_LENGTH ) )	// I2C接口的命令流单个命令输入输出数据的最大长度
#define		mCH341A_CMD_I2C_STM_SET	0x60		// I2C接口的命令流:设置参数,位2=SPI的I/O数(0=单入单出,1=双入双出),位1位0=I2C速度(00=低速,01=标准,10=快速,11=高速)
#define		mCH341A_CMD_I2C_STM_US	0x40		// I2C接口的命令流:以微秒为单位延时,位3-位0为延时值
#define		mCH341A_CMD_I2C_STM_MS	0x50		// I2C接口的命令流:以亳秒为单位延时,位3-位0为延时值
#define		mCH341A_CMD_I2C_STM_DLY	0x0F		// I2C接口的命令流单个命令延时的最大值
#define		mCH341A_CMD_I2C_STM_END	0x00		// I2C接口的命令流:命令包提前结束

#define		mCH341A_CMD_UIO_STM_IN	0x00		// UIO接口的命令流:输入数据D7-D0
#define		mCH341A_CMD_UIO_STM_DIR	0x40		// UIO接口的命令流:设定I/O方向D5-D0,位5-位0为方向数据
#define		mCH341A_CMD_UIO_STM_OUT	0x80		// UIO接口的命令流:输出数据D5-D0,位5-位0为数据
#define		mCH341A_CMD_UIO_STM_US	0xC0		// UIO接口的命令流:以微秒为单位延时,位5-位0为延时值
#define		mCH341A_CMD_UIO_STM_END	0x20		// UIO接口的命令流:命令包提前结束


// CH341并口工作模式
#define		mCH341_PARA_MODE_EPP	0x00		// CH341并口工作模式为EPP方式
#define		mCH341_PARA_MODE_EPP17	0x00		// CH341A并口工作模式为EPP方式V1.7
#define		mCH341_PARA_MODE_EPP19	0x01		// CH341A并口工作模式为EPP方式V1.9
#define		mCH341_PARA_MODE_MEM	0x02		// CH341并口工作模式为MEM方式
#define		mCH341_PARA_MODE_ECP	0x03		// CH341A并口工作模式为ECP方式


// I/O方向设置位定义,直接输入的状态信号的位定义,直接输出的位数据定义
#define		mStateBitERR			0x00000100	// 只读可写,ERR#引脚输入状态,1:高电平,0:低电平
#define		mStateBitPEMP			0x00000200	// 只读可写,PEMP引脚输入状态,1:高电平,0:低电平
#define		mStateBitINT			0x00000400	// 只读可写,INT#引脚输入状态,1:高电平,0:低电平
#define		mStateBitSLCT			0x00000800	// 只读可写,SLCT引脚输入状态,1:高电平,0:低电平
#define		mStateBitWAIT			0x00002000	// 只读可写,WAIT#引脚输入状态,1:高电平,0:低电平
#define		mStateBitDATAS			0x00004000	// 只写可读,DATAS#/READ#引脚输入状态,1:高电平,0:低电平
#define		mStateBitADDRS			0x00008000	// 只写可读,ADDRS#/ADDR/ALE引脚输入状态,1:高电平,0:低电平
#define		mStateBitRESET			0x00010000	// 只写,RESET#引脚输入状态,1:高电平,0:低电平
#define		mStateBitWRITE			0x00020000	// 只写,WRITE#引脚输入状态,1:高电平,0:低电平
#define		mStateBitSCL			0x00400000	// 只读,SCL引脚输入状态,1:高电平,0:低电平
#define		mStateBitSDA			0x00800000	// 只读,SDA引脚输入状态,1:高电平,0:低电平


#define		MAX_DEVICE_PATH_SIZE	128			// 设备名称的最大字符数
#define		MAX_DEVICE_ID_SIZE		64			// 设备ID的最大字符数


typedef		VOID	( CALLBACK	* mPCH341_INT_ROUTINE ) (  // 中断服务程序
	ULONG			iStatus );  // 中断状态数据,参考下面的位说明
// 位7-位0对应CH341的D7-D0引脚
// 位8对应CH341的ERR#引脚, 位9对应CH341的PEMP引脚, 位10对应CH341的INT#引脚, 位11对应CH341的SLCT引脚


HANDLE	WINAPI	CH341OpenDevice(  // 打开CH341设备,返回句柄,出错则无效
	ULONG			iIndex );  // 指定CH341设备序号,0对应第一个设备


VOID	WINAPI	CH341CloseDevice(  // 关闭CH341设备
	ULONG			iIndex );  // 指定CH341设备序号


ULONG	WINAPI	CH341GetVersion( );  // 获得DLL版本号,返回版本号


ULONG	WINAPI	CH341DriverCommand(  // 直接传递命令给驱动程序,出错则返回0,否则返回数据长度
	ULONG			iIndex,  // 指定CH341设备序号,V1.6以上DLL也可以是设备打开后的句柄
	mPWIN32_COMMAND	ioCommand );  // 命令结构的指针
// 该程序在调用后返回数据长度,并且仍然返回命令结构,如果是读操作,则数据返回在命令结构中,
// 返回的数据长度在操作失败时为0,操作成功时为整个命令结构的长度,例如读一个字节,则返回mWIN32_COMMAND_HEAD+1,
// 命令结构在调用前,分别提供:管道号或者命令功能代码,存取数据的长度(可选),数据(可选)
// 命令结构在调用后,分别返回:操作状态代码,后续数据的长度(可选),
//   操作状态代码是由WINDOWS定义的代码,可以参考NTSTATUS.H,
//   后续数据的长度是指读操作返回的数据长度,数据存放在随后的缓冲区中,对于写操作一般为0


ULONG	WINAPI	CH341GetDrvVersion( );  // 获得驱动程序版本号,返回版本号,出错则返回0


BOOL	WINAPI	CH341ResetDevice(  // 复位USB设备
	ULONG			iIndex );  // 指定CH341设备序号


BOOL	WINAPI	CH341GetDeviceDescr(  // 读取设备描述符
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存描述符
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341GetConfigDescr(  // 读取配置描述符
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存描述符
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341SetIntRoutine(  // 设定中断服务程序
	ULONG			iIndex,  // 指定CH341设备序号
	mPCH341_INT_ROUTINE	iIntRoutine );  // 指定中断服务程序,为NULL则取消中断服务,否则在中断时调用该程序


BOOL	WINAPI	CH341ReadInter(  // 读取中断数据
	ULONG			iIndex,  // 指定CH341设备序号
	PULONG			iStatus );  // 指向一个双字单元,用于保存读取的中断状态数据,见下行
// 位7-位0对应CH341的D7-D0引脚
// 位8对应CH341的ERR#引脚, 位9对应CH341的PEMP引脚, 位10对应CH341的INT#引脚, 位11对应CH341的SLCT引脚


BOOL	WINAPI	CH341AbortInter(  // 放弃中断数据读操作
	ULONG			iIndex );  // 指定CH341设备序号


BOOL	WINAPI	CH341SetParaMode(  // 设置并口模式
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iMode );  // 指定并口模式: 0为EPP模式/EPP模式V1.7, 1为EPP模式V1.9, 2为MEM模式


BOOL	WINAPI	CH341InitParallel(  // 复位并初始化并口,RST#输出低电平脉冲
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iMode );  // 指定并口模式: 0为EPP模式/EPP模式V1.7, 1为EPP模式V1.9, 2为MEM模式, >= 0x00000100 保持当前模式


BOOL	WINAPI	CH341ReadData0(  // 从0#端口读取数据块
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存读取的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341ReadData1(  // 从1#端口读取数据块
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存读取的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341AbortRead(  // 放弃数据块读操作
	ULONG			iIndex );  // 指定CH341设备序号


BOOL	WINAPI	CH341WriteData0(  // 向0#端口写出数据块
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度


BOOL	WINAPI	CH341WriteData1(  // 向1#端口写出数据块
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度


BOOL	WINAPI	CH341AbortWrite(  // 放弃数据块写操作
	ULONG			iIndex );  // 指定CH341设备序号


BOOL	WINAPI	CH341GetStatus(  // 通过CH341直接输入数据和状态
	ULONG			iIndex,  // 指定CH341设备序号
	PULONG			iStatus );  // 指向一个双字单元,用于保存状态数据,参考下面的位说明
// 位7-位0对应CH341的D7-D0引脚
// 位8对应CH341的ERR#引脚, 位9对应CH341的PEMP引脚, 位10对应CH341的INT#引脚, 位11对应CH341的SLCT引脚, 位23对应CH341的SDA引脚
// 位13对应CH341的BUSY/WAIT#引脚, 位14对应CH341的AUTOFD#/DATAS#引脚,位15对应CH341的SLCTIN#/ADDRS#引脚


BOOL	WINAPI	CH341ReadI2C(  // 从I2C接口读取一个字节数据
	ULONG			iIndex,  // 指定CH341设备序号
	UCHAR			iDevice,  // 低7位指定I2C设备地址
	UCHAR			iAddr,  // 指定数据单元的地址
	PUCHAR			oByte );  // 指向一个字节单元,用于保存读取的字节数据


BOOL	WINAPI	CH341WriteI2C(  // 向I2C接口写入一个字节数据
	ULONG			iIndex,  // 指定CH341设备序号
	UCHAR			iDevice,  // 低7位指定I2C设备地址
	UCHAR			iAddr,  // 指定数据单元的地址
	UCHAR			iByte );  // 待写入的字节数据


BOOL	WINAPI	CH341EppReadData(  // EPP方式读数据: WR#=1, DS#=0, AS#=1, D0-D7=input
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存读取的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341EppReadAddr(  // EPP方式读地址: WR#=1, DS#=1, AS#=0, D0-D7=input
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存读取的地址数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341EppWriteData(  // EPP方式写数据: WR#=0, DS#=0, AS#=1, D0-D7=output
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度


BOOL	WINAPI	CH341EppWriteAddr(  // EPP方式写地址: WR#=0, DS#=1, AS#=0, D0-D7=output
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的地址数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度


BOOL	WINAPI	CH341EppSetAddr(  // EPP方式设置地址: WR#=0, DS#=1, AS#=0, D0-D7=output
	ULONG			iIndex,  // 指定CH341设备序号
	UCHAR			iAddr );  // 指定EPP地址


BOOL	WINAPI	CH341MemReadAddr0(  // MEM方式读地址0: WR#=1, DS#/RD#=0, AS#/ADDR=0, D0-D7=input
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存从地址0读取的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341MemReadAddr1(  // MEM方式读地址1: WR#=1, DS#/RD#=0, AS#/ADDR=1, D0-D7=input
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存从地址1读取的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341MemWriteAddr0(  // MEM方式写地址0: WR#=0, DS#/RD#=1, AS#/ADDR=0, D0-D7=output
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备向地址0写出的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度


BOOL	WINAPI	CH341MemWriteAddr1(  // MEM方式写地址1: WR#=0, DS#/RD#=1, AS#/ADDR=1, D0-D7=output
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备向地址1写出的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度


BOOL	WINAPI	CH341SetExclusive(  // 设置独占使用当前CH341设备
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iExclusive );  // 为0则设备可以共享使用,非0则独占使用


BOOL	WINAPI	CH341SetTimeout(  // 设置USB数据读写的超时
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iWriteTimeout,  // 指定USB写出数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)
	ULONG			iReadTimeout );  // 指定USB读取数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)


BOOL	WINAPI	CH341ReadData(  // 读取数据块
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存读取的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度


BOOL	WINAPI	CH341WriteData(  // 写出数据块
	ULONG			iIndex,  // 指定CH341设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的数据
	PULONG			ioLength );  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度


PVOID	WINAPI	CH341GetDeviceName(  // 返回指向CH341设备名称的缓冲区,出错则返回NULL
	ULONG			iIndex );  // 指定CH341设备序号,0对应第一个设备


ULONG	WINAPI	CH341GetVerIC(  // 获取CH341芯片的版本,返回:0=设备无效,0x10=CH341,0x20=CH341A
	ULONG			iIndex );  // 指定CH341设备序号
#define		IC_VER_CH341A		0x20
#define		IC_VER_CH341A3		0x30


BOOL	WINAPI	CH341FlushBuffer(  // 清空CH341的缓冲区
	ULONG			iIndex );  // 指定CH341设备序号


BOOL	WINAPI	CH341WriteRead(  // 执行数据流命令,先输出再输入
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iWriteLength,  // 写长度,准备写出的长度
	PVOID			iWriteBuffer,  // 指向一个缓冲区,放置准备写出的数据
	ULONG			iReadStep,  // 准备读取的单个块的长度, 准备读取的总长度为(iReadStep*iReadTimes)
	ULONG			iReadTimes,  // 准备读取的次数
	PULONG			oReadLength,  // 指向长度单元,返回后为实际读取的长度
	PVOID			oReadBuffer );  // 指向一个足够大的缓冲区,用于保存读取的数据


BOOL	WINAPI	CH341SetStream(  // 设置串口流模式
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iMode );  // 指定模式,见下行
// 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz
// 位2:     SPI的I/O数/IO引脚, 0=单入单出(D3时钟/D5出/D7入)(默认值),1=双入双出(D3时钟/D5出D4出/D7入D6入)
// 位7:     SPI字节中的位顺序, 0=低位在前, 1=高位在前
// 其它保留,必须为0


BOOL	WINAPI	CH341SetDelaymS(  // 设置硬件异步延时,调用后很快返回,而在下一个流操作之前延时指定毫秒数
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iDelay );  // 指定延时的毫秒数


BOOL	WINAPI	CH341StreamI2C(  // 处理I2C数据流,2线接口,时钟线为SCL引脚,数据线为SDA引脚(准双向I/O),速度约56K字节
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iWriteLength,  // 准备写出的数据字节数
	PVOID			iWriteBuffer,  // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位
	ULONG			iReadLength,  // 准备读取的数据字节数
	PVOID			oReadBuffer );  // 指向一个缓冲区,返回后是读入的数据


typedef	enum	_EEPROM_TYPE {					// EEPROM型号
	ID_24C01,
	ID_24C02,
	ID_24C04,
	ID_24C08,
	ID_24C16,
	ID_24C32,
	ID_24C64,
	ID_24C128,
	ID_24C256,
	ID_24C512,
	ID_24C1024,
	ID_24C2048,
	ID_24C4096
} EEPROM_TYPE;


BOOL	WINAPI	CH341ReadEEPROM(  // 从EEPROM中读取数据块,速度约56K字节
	ULONG			iIndex,  // 指定CH341设备序号
	EEPROM_TYPE		iEepromID,  // 指定EEPROM型号
	ULONG			iAddr,  // 指定数据单元的地址
	ULONG			iLength,  // 准备读取的数据字节数
	PUCHAR			oBuffer );  // 指向一个缓冲区,返回后是读入的数据


BOOL	WINAPI	CH341WriteEEPROM(  // 向EEPROM中写入数据块
	ULONG			iIndex,  // 指定CH341设备序号
	EEPROM_TYPE		iEepromID,  // 指定EEPROM型号
	ULONG			iAddr,  // 指定数据单元的地址
	ULONG			iLength,  // 准备写出的数据字节数
	PUCHAR			iBuffer );  // 指向一个缓冲区,放置准备写出的数据


BOOL	WINAPI	CH341GetInput(  // 通过CH341直接输入数据和状态,效率比CH341GetStatus更高
	ULONG			iIndex,  // 指定CH341设备序号
	PULONG			iStatus );  // 指向一个双字单元,用于保存状态数据,参考下面的位说明
// 位7-位0对应CH341的D7-D0引脚
// 位8对应CH341的ERR#引脚, 位9对应CH341的PEMP引脚, 位10对应CH341的INT#引脚, 位11对应CH341的SLCT引脚, 位23对应CH341的SDA引脚
// 位13对应CH341的BUSY/WAIT#引脚, 位14对应CH341的AUTOFD#/DATAS#引脚,位15对应CH341的SLCTIN#/ADDRS#引脚


BOOL	WINAPI	CH341SetOutput(  // 设置CH341的I/O方向,并通过CH341直接输出数据
/* ***** 谨慎使用该API, 防止修改I/O方向使输入引脚变为输出引脚导致与其它输出引脚之间短路而损坏芯片 ***** */
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iEnable,  // 数据有效标志,参考下面的位说明
// 位0为1说明iSetDataOut的位15-位8有效,否则忽略
// 位1为1说明iSetDirOut的位15-位8有效,否则忽略
// 位2为1说明iSetDataOut的7-位0有效,否则忽略
// 位3为1说明iSetDirOut的位7-位0有效,否则忽略
// 位4为1说明iSetDataOut的位23-位16有效,否则忽略
	ULONG			iSetDirOut,  // 设置I/O方向,某位清0则对应引脚为输入,某位置1则对应引脚为输出,并口方式下默认值为0x000FC000,参考下面的位说明
	ULONG			iSetDataOut );  // 输出数据,如果I/O方向为输出,那么某位清0时对应引脚输出低电平,某位置1时对应引脚输出高电平,参考下面的位说明
// 位7-位0对应CH341的D7-D0引脚
// 位8对应CH341的ERR#引脚, 位9对应CH341的PEMP引脚, 位10对应CH341的INT#引脚, 位11对应CH341的SLCT引脚
// 位13对应CH341的WAIT#引脚, 位14对应CH341的DATAS#/READ#引脚,位15对应CH341的ADDRS#/ADDR/ALE引脚
// 以下引脚只能输出,不考虑I/O方向: 位16对应CH341的RESET#引脚, 位17对应CH341的WRITE#引脚, 位18对应CH341的SCL引脚, 位29对应CH341的SDA引脚


BOOL	WINAPI	CH341Set_D5_D0(  // 设置CH341的D5-D0引脚的I/O方向,并通过CH341的D5-D0引脚直接输出数据,效率比CH341SetOutput更高
/* ***** 谨慎使用该API, 防止修改I/O方向使输入引脚变为输出引脚导致与其它输出引脚之间短路而损坏芯片 ***** */
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iSetDirOut,  // 设置D5-D0各引脚的I/O方向,某位清0则对应引脚为输入,某位置1则对应引脚为输出,并口方式下默认值为0x00全部输入
	ULONG			iSetDataOut );  // 设置D5-D0各引脚的输出数据,如果I/O方向为输出,那么某位清0时对应引脚输出低电平,某位置1时对应引脚输出高电平
// 以上数据的位5-位0分别对应CH341的D5-D0引脚


BOOL	WINAPI	CH341StreamSPI3(  // 该API已失效,请勿使用
	ULONG			iIndex,
	ULONG			iChipSelect,
	ULONG			iLength,
	PVOID			ioBuffer );


BOOL	WINAPI	CH341StreamSPI4(  // 处理SPI数据流,4线接口,时钟线为DCK/D3引脚,输出数据线为DOUT/D5引脚,输入数据线为DIN/D7引脚,片选线为D0/D1/D2,速度约68K字节
/* SPI时序: DCK/D3引脚为时钟输出, 默认为低电平, DOUT/D5引脚在时钟上升沿之前的低电平期间输出, DIN/D7引脚在时钟下降沿之前的高电平期间输入 */
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iChipSelect,  // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01/10分别选择D0/D1/D2引脚作为低电平有效片选
	ULONG			iLength,  // 准备传输的数据字节数
	PVOID			ioBuffer );  // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据


BOOL	WINAPI	CH341StreamSPI5(  // 处理SPI数据流,5线接口,时钟线为DCK/D3引脚,输出数据线为DOUT/D5和DOUT2/D4引脚,输入数据线为DIN/D7和DIN2/D6引脚,片选线为D0/D1/D2,速度约30K字节*2
/* SPI时序: DCK/D3引脚为时钟输出, 默认为低电平, DOUT/D5和DOUT2/D4引脚在时钟上升沿之前的低电平期间输出, DIN/D7和DIN2/D6引脚在时钟下降沿之前的高电平期间输入 */
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iChipSelect,  // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01/10分别选择D0/D1/D2引脚作为低电平有效片选
	ULONG			iLength,  // 准备传输的数据字节数
	PVOID			ioBuffer,  // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据
	PVOID			ioBuffer2 );  // 指向第二个缓冲区,放置准备从DOUT2写出的数据,返回后是从DIN2读入的数据


BOOL	WINAPI	CH341BitStreamSPI(  // 处理SPI位数据流,4线/5线接口,时钟线为DCK/D3引脚,输出数据线为DOUT/DOUT2引脚,输入数据线为DIN/DIN2引脚,片选线为D0/D1/D2,速度约8K位*2
	ULONG			iIndex,  // 指定CH341设备序号
	ULONG			iLength,  // 准备传输的数据位数,一次最多896,建议不超过256
	PVOID			ioBuffer );  // 指向一个缓冲区,放置准备从DOUT/DOUT2/D2-D0写出的数据,返回后是从DIN/DIN2读入的数据
/* SPI时序: DCK/D3引脚为时钟输出, 默认为低电平, DOUT/D5和DOUT2/D4引脚在时钟上升沿之前的低电平期间输出, DIN/D7和DIN2/D6引脚在时钟下降沿之前的高电平期间输入 */
/* ioBuffer中的一个字节共8位分别对应D7-D0引脚, 位5输出到DOUT, 位4输出到DOUT2, 位2-位0输出到D2-D0, 位7从DIN输入, 位6从DIN2输入, 位3数据忽略 */
/* 在调用该API之前,应该先调用CH341Set_D5_D0设置CH341的D5-D0引脚的I/O方向,并设置引脚的默认电平 */


BOOL	WINAPI	CH341SetBufUpload(  // 设定内部缓冲上传模式
	ULONG			iIndex,  // 指定CH341设备序号,0对应第一个设备
	ULONG			iEnableOrClear );  // 为0则禁止内部缓冲上传模式,使用直接上传,非0则启用内部缓冲上传模式并清除缓冲区中的已有数据
// 如果启用内部缓冲上传模式,那么CH341驱动程序创建线程自动接收USB上传数据到内部缓冲区,同时清除缓冲区中的已有数据,当应用程序调用CH341ReadData后将立即返回缓冲区中的已有数据


LONG	WINAPI	CH341QueryBufUpload(  // 查询内部上传缓冲区中的已有数据包个数,成功返回数据包个数,出错返回-1
	ULONG			iIndex );  // 指定CH341设备序号,0对应第一个设备


BOOL	WINAPI	CH341SetBufDownload(  // 设定内部缓冲下传模式
	ULONG			iIndex,  // 指定CH341设备序号,0对应第一个设备
	ULONG			iEnableOrClear );  // 为0则禁止内部缓冲下传模式,使用直接下传,非0则启用内部缓冲下传模式并清除缓冲区中的已有数据
// 如果启用内部缓冲下传模式,那么当应用程序调用CH341WriteData后将仅仅是将USB下传数据放到内部缓冲区并立即返回,而由CH341驱动程序创建的线程自动发送直到完毕


LONG	WINAPI	CH341QueryBufDownload(  // 查询内部下传缓冲区中的剩余数据包个数(尚未发送),成功返回数据包个数,出错返回-1
	ULONG			iIndex );  // 指定CH341设备序号,0对应第一个设备


BOOL	WINAPI	CH341ResetInter(  // 复位中断数据读操作
	ULONG			iIndex );  // 指定CH341设备序号


BOOL	WINAPI	CH341ResetRead(  // 复位数据块读操作
	ULONG			iIndex );  // 指定CH341设备序号


BOOL	WINAPI	CH341ResetWrite(  // 复位数据块写操作
	ULONG			iIndex );  // 指定CH341设备序号


typedef		VOID	( CALLBACK	* mPCH341_NOTIFY_ROUTINE ) (  // 设备事件通知回调程序
	ULONG			iEventStatus );  // 设备事件和当前状态(在下行定义): 0=设备拔出事件, 3=设备插入事件

#define		CH341_DEVICE_ARRIVAL		3		// 设备插入事件,已经插入
#define		CH341_DEVICE_REMOVE_PEND	1		// 设备将要拔出
#define		CH341_DEVICE_REMOVE			0		// 设备拔出事件,已经拔出


BOOL	WINAPI	CH341SetDeviceNotify(  // 设定设备事件通知程序
	ULONG					iIndex,  // 指定CH341设备序号,0对应第一个设备
	PCHAR					iDeviceID,  // 可选参数,指向字符串,指定被监控的设备的ID,字符串以\0终止
	mPCH341_NOTIFY_ROUTINE	iNotifyRoutine );  // 指定设备事件回调程序,为NULL则取消事件通知,否则在检测到事件时调用该程序


BOOL	WINAPI	CH341SetupSerial(  // 设定CH341的串口特性,该API只能用于工作于串口方式的CH341芯片
	ULONG					iIndex,  // 指定CH341设备序号,0对应第一个设备
	ULONG					iParityMode,  // 指定CH341串口的数据校验模式: NOPARITY/ODDPARITY/EVENPARITY/MARKPARITY/SPACEPARITY
	ULONG					iBaudRate );  // 指定CH341串口的通讯波特率值,可以是50至3000000之间的任意值

/*  以下API可以用于工作于串口方式的CH341芯片,除此之外的API一般只能用于并口方式的CH341芯片
	CH341OpenDevice
	CH341CloseDevice
	CH341SetupSerial
	CH341ReadData
	CH341WriteData
	CH341SetBufUpload
	CH341QueryBufUpload
	CH341SetBufDownload
	CH341QueryBufDownload
	CH341SetDeviceNotify
	CH341GetStatus
//  以上是主要API,以下是次要API
	CH341GetVersion
	CH341DriverCommand
	CH341GetDrvVersion
	CH341ResetDevice
	CH341GetDeviceDescr
	CH341GetConfigDescr
	CH341SetIntRoutine
	CH341ReadInter
	CH341AbortInter
	CH341AbortRead
	CH341AbortWrite
	CH341ReadI2C
	CH341WriteI2C
	CH341SetExclusive
	CH341SetTimeout
	CH341GetDeviceName
	CH341GetVerIC
	CH341FlushBuffer
	CH341WriteRead
	CH341ResetInter
	CH341ResetRead
	CH341ResetWrite
*/
HANDLE	WINAPI	CH341OpenDeviceEx(   // 打开CH341设备,返回句柄,出错则无效
    ULONG			iIndex );        // 指定CH341设备序号,0对应插入的第一个设备,1对应插入的第二个设备,为节约设备设备序号资源,用完后要关闭设备

VOID	WINAPI	CH341CloseDeviceEx(  // 关闭CH341设备
	ULONG			iIndex );        // 指定CH341设备序号

PCHAR	WINAPI	CH341GetDeviceNameEx(   // 返回指向CH341设备名称的缓冲区,出错则返回NULL
	ULONG			iIndex );           // 指定CH341设备序号,0对应第一个设备

BOOL	WINAPI	CH341SetDeviceNotifyEx(       // 设定设备事件通知程序
	ULONG					iIndex,           // 指定CH341设备序号,0对应第一个设备
	PCHAR					iDeviceID,        // 可选参数,指向字符串,指定被监控的设备的ID,字符串以\0终止
	mPCH341_NOTIFY_ROUTINE	iNotifyRoutine ); // 指定设备事件回调程序,为NULL则取消事件通知,否则在检测到事件时调用该程序


#ifdef __cplusplus
}
#endif

#endif		// _CH341_DLL_H
