USB HID客户驱动程序并不直接和硬件打交道,而是通过调用HID类以及USB设备栈是提供的DDI服务控制USB硬件的操作。USB客户驱动程序将有关的控制信息包装在URB(USB请求块)结构中,作为IRP的一个参数传递给HID类驱动程序,类驱动程序再调用USB设备栈的下层驱动程序,获取有关的控制信息,与具体的USB设备进行交互,并返回结构给USB客户驱动程序。
USB接口HID内核模式的客户驱动程序是WDM驱动程序,它们支持即插即用和电源管理。其结构和普通WDM驱动程序一样,由很多例程构成。在这些例程中可以调用HID类提供的各种DDI函数(一般以HIDP开头)完成设备的操作。
3 USB接口的IC卡读写装置用户模式客户程序
IC卡读写装置采用Cypress公司生产的CY7C63001A微控制器。该芯片是8位一次性编程的微控制器,内置1.5Mbps的USB串行接口引擎。该芯片35条专门用于USB操作的指令,20个引脚,128字节的RAM以及4K字节的可编程空间,可以满足IC卡读写程序存储的需要。CY7C63001A芯片与主机USB端口的互连如图3所示。
3.1 IC卡设备部分的描述符结构
按HID类规范的规定,对于每一个USB接口的HID设备,应包含以下几个描述符:设备描述符、配置描述符、接口描述符、HID类描述符及端点描述符。这些描述符定义了本USB设备的基本信息,如零售商ID(应该向USB组织申请,本设备采用0X4242H)、产品ID、配置数目(1个)、接口数目(1个)、端点数目(1个,控制端点为缺省的,还包括一个中断输入端点)及设备类别等。对于HID设备,在接口描述符中需要规定其类ID为3,在HID类描述符中需要规定其描述符类型为0X21。HID类设备的基本通信机制是HID报告。每一个HID设备必须定义一个报告描述符,用以详细描述该设备报告的数据协议及数据类型。报告描述符由主项目、局部项目和全局项目组成。一个报告必须包含下列项目:Input(Output或Feature),Usage,Usage Page,Logical Minimum,Logical Maximum,Report Size,Report Count。每一个数据项的位长度等于Report Size × Report Count。不同的报告通过报告ID相互区分,用法(Usage)表明该用法页(Usage Page)下各个数据的具体含义或目的。报告的大小并不受端点的结束,主机会自动将报告分解成适合传输大小的数据包。
本系统中,IC卡数据以字节为单位读出、写入。一次最多读取或写入8个字节,再加上命令字(表明数据的含义)及结束标志,最长为13个字节。输入报告描述符如下所示:
usage 01-vendor defined
usage 02-vendor defined
Logical Minimum (-128)
Logical Maximum (127)
Physical Minimum (0)
Physical Maximum (255)
Report Size (13)(fields) //一个字节为8位
Report Conut (13) (fields) //最大为13个字节
Input(Data, Variable,Absolute)
为了更好地说明设备的信息,还可以为USB设备定义可选的字符串描述符。定义字节串描述符的好处是可以让主机显示有意义的设备信息,如厂商名称、产品名称等,这对于非通用设备很有好处。本IC卡设备为非通用设备,因此也定义了字符串描述符。另外,字符串语言可通过LanguageID设备,微软规定中文的LanguageID为0804H,美国英语的LanguageID为0409H。
3.2 用户模式客户程序的编制步骤
HID类驱动程序及HID.DLL提供了两类读写USB设备的函数。一类是以HidP为前缀,适用于内核模式的客户驱动程序。另外一类是以HidD为前缀,适用于用户模式的客户程序。利用这些函数编制读写USB设备的步骤如下:
第一步,检测已安装的HID设备,或称USB设备枚举。在和USB设备通信之前,必须得知设备的配置、接口及所用的端点信息,还要获取设备名。
·首先调用HidD_GetHidGuid函数获取HID设备的类标识(GUID);
·调用SetupDiGetClassDevs函数查询所有已安装的HID设备,得到一个指向该HID设备集合的句柄;
·调用SetupDiEnumDeviceInterface函数查询HID设备集中每一个设备的接口信息;
·对每一个接口,调用SetupDiGetDeviceInterface Detail函数获取其详细的信息,包括设备名称(头四个字节),CreateFile用此设备名打开设备。
·调用SetupDiDestroyDeviceInfoList函数释放设备信息集合。
第二步,打开设备,获取设备的属性值以及设备能力描述。
·调用CreaterFile函数打开本设备。
·调用HidD_GetAttributes函数,获取USB设备的有关属性。它包含了设备的零售商ID、产品ID及产品的版本号等。可以根据这些信息判断该设备是否为目标设备。
·调用HidD_Get PraparsedData函数,获取USB设备的预解析数据。这些数据存储在缓冲区内,该缓冲区的数据也可以为其它API函数使用;
·调用HidP_GetCaps函数从上述缓冲区中获取关于该设备能力(如用法、报告描述符的大小等)的描述。调用HidP_GetValueCaps函数得到每一个设备的输入、输出及特征报告的属性值。调用HidD_GetXXXString函数,获取设备的字符串描述信息。
第三步,与HID设备交互。
主机在接收报告的时候,需要从报告中提取数据。由于报告中包含了各种类型的数据,为了便辨别不同类型的数据,HID类提供了HidP_GetXxx例程,从设备中读取不同类型的数据。如果程序员知道各个数据的含义,则可直接使用ReadDFile函数读出数据。本程序直接使用ReadFile函数读出数据。同样,主机发送数据给设备的时候,也要先创建报告;为了方便发送不同类型的数据给设备,HID类提供了HidP_SetXxx函数。当然,如果程序员知道各个数据的含义,也可以直接使用WriteFile函数将报告传给设备。
完成设备操作后,应关闭设备句柄,释放预解析数据所占用的内存区域。
微软为了方便串行通信,提供了一个MSCom控件。同样,为了方便类似USB接口的HID设备通信,可以将以上的通信程序制作成控件,供不同的HID设备使用。