HID 设备,
|– 通过设备描述符来标识自己的设备信息,如设备ID、厂商名称、版本、配置数量等;
|– 通过配置描述符集合来标识自己的配置信息,包括
| |– 配置描述符(总述)、
| `– 接口描述符(接口类型,鼠标,键盘,自定义设备等)、
| |– 端点描述符(端点读写类型,支持的数据包属性等)和
| `– HID描述符(HID版本、下级描述符信息等)。
|– 通过字符串描述符来定义自己的一些字符串,如厂商名称等;
`– 通过报告描述符来标识数据流的格式,如数据用途(鼠标、键盘、自定义等)、长度、大小等。
网上的很多示例和介绍都使用c语言,但是,我遇到的源码,却是汇编编写的,所以我以我的项目为例,用汇编来介绍各个描述符结构,也算是个不一样的体验吧。
具体的描述介绍如下:
deviceDesc: ; Device descriptor DB deviceDescEnd - deviceDesc ; bLength 该描述符长度 DB DESC_TYPE_DEVICE ; bDescriptorType 类型 DW 0200H ; bcdUSB (USB 2.0) USB 版本 DB 00H ; bDeviceClass (given by interface) 类代码 DB 00H ; bDeviceSubClass 子类代码 DB 00H ; bDeviceProtocol 设备协议 DB EP0_PACKET_SIZE ; 端点0最大包长 DW 0451H ; idVendor (Texas Instruments) 厂商ID (VID) #if (chip==2531) DW 16A9H ; idProduct (CC2531 HID) 产品ID (PID) #elif (chip==2511) DW 16A5H ; idProduct (CC2511 HID) #else DW 16A7H ; idProduct (CC1111 HID) #endif DW 0100H ; bcdDevice (v1.0) 设备版本号 DB 01H ; iManufacturer 描述厂商的字符串索引 DB 02H ; iProduct 描述产品的字符串索引 DB 00H ; iSerialNumber 产品序列号字符串的索引 DB 01H ; bNumConfigurations 可能的配置数 deviceDescEnd:
在里面,最重要的是VID和PID,它一般用来标识你的USB设备。VID需要向USB组织申请,PID则是公司内部自己定义。其实,我们还可以通过产品序列号来标识,但目前,我还没有添加这个功能,直接使用VID和PID识别的。
另外我们可以通过这份代码看到一些技巧,就是在填写描述符长度时,让编译器自己计算:deviceDescEnd – deviceDesc
configDesc: ; Configuration descriptor DB configDescEnd - configDesc ; bLength 该描述符长度 DB DESC_TYPE_CONFIG ; bDescriptorType 类型 DW config1LengthEnd - config1LengthStart ; wTotalLength 所有配置描述符的总长度 ; adding a custom interface, so this should be 3! - by tankery. DB 03H ; bNumInterfaces 端点数量 DB 01H ; bConfigurationValue 该配置的值?不懂。。。。。。 DB 00H ; iConfiguration 该配置的字符串索引值 DB 80H ; bmAttributes 该配置属性 DB 25 ; bMaxPower (max 2 * 25 = 50 mA) 设备所需电流大小。 configDescEnd:
我目前的设备是鼠标、键盘、自定义3种接口。可以支持三种端点同时传输数据,非常high~~
interface2Desc: ; custom device interface descriptor DB interface2DescEnd - interface2Desc ; bLength 该描述符长度 DB DESC_TYPE_INTERFACE ; bDescriptorType 类型 ; this should be increaced - by tankery. DB 02H ; bInterfaceNumber 该接口的编号 DB 00H ; bAlternateSetting (none) 备用编号 DB 01H ; bNumEndpoints 端点数 DB 03H ; bInterfaceClass (HID) 接口使用的类(HID) DB 00H ; bInterfaceSubClass (custom) 接口使用的子类(00为自定义) DB 00H ; bInterfaceProcotol (custom) 接口使用的协议(00为自定义) DB 00H ; iInterface 该接口的字符串索引值 interface2DescEnd:
我的这个项目中,有3 种接口,原始代码已经实现了鼠标和键盘的接口描述符。感兴趣的读者也可以在网上查阅。有现成的示例代码。
从本文开始的那个介绍看出,接口之下是包含端点的,因此,接口描述符中有一项的端点数,这个端点数标明的是除默认控制端点0之外的数据输入输出端点数量。目前我只实现了一个数据输入(USB中所有的输入、输出的对象都是主设备,在我这里,指的是PC)。
后期我会添加一个数据输出端点。
endpoint2Desc: ; Custom endpoint descriptor (EP3 IN) ; need endpoint 3 - by tankery. DB endpoint2DescEnd - endpoint2Desc ; bLength 该描述符长度 DB DESC_TYPE_ENDPOINT ; bDescriptorType 类型 DB 83H ; bEndpointAddress 端点地址(3) DB EP_ATTR_INT ; bmAttributes (INT) 端点类型(中断) DW 0008H ; wMaxPacketSize (8 bit) 最大数据包长度 DB 0AH ; bInterval (10 full-speed frames = 10 ms) endpoint2DescEnd:
端点描述符中的bmAttributes 是个比较重要的字段,标明了该端点的类型,端点有控制(0)、等时(1)、批量(2)、中断(3)四种类型。HID只有控制端点和中断端点两种类型。
hid2Desc: ; Custom HID descriptor DB hid2DescEnd - hid2Desc ; bLength DB DESC_TYPE_HID ; bDescriptorType DW 0111H ; bcdHID (HID v1.11) DB 00H ; bCountryCode (not localized) DB 01H ; bNumDescriptors DB DESC_TYPE_HIDREPORT ; bDescriptorType DW entity2DescEnd - entity2Desc ; wDescriptorLength hid2DescEnd:
这个没啥要紧的,我也不清楚,不详述了。
报告描述符相当复杂,但也是HID设备的精髓。它标识了HID设备与主机通讯的报告格式以及数据用途,数据是存放在报告中的。
报告描述符的每个条目都有个条目前缀,是如下格式:
,-----------------------. |7 6 5 4 3 2 1 0 | |-----------------------| | bTag | bType | bSize | `-----------------------'
高 4 位bTag标识该条目的功能,具体的可以参看HID协议,第 2, 3 位为条目类型(0――主条目、1――全局条目、2――局部条目、3――保留),第 0, 1 位为条目大小,表示该前缀之后有多少个字节的内容。
比如 09H (00001001B) 表示这是一个局部条目,后跟一个字节。功能为用途页(0)。各种功能的定义可以参看HID协议或者其它示例,下面,我就以我写的自定义设备为例大致介绍一下报告描述符的设计:
entity2Desc: ; Custom HID device report descriptor DB 0005H, 0001H ; Usage Pg (Generic Desktop) 全局,功能为“用途页”。后跟的0001H表示这是通用桌面用途 DB 0009H, 0000H ; Usage (0) ; custom usage 局部,功能为“用途页”。后跟0000H表示这是自定义用途。 DB 00A1H, 0001H ; Collection: (Application) 主条目,功能为“开集合”,表示后面的条目都在集合 Application 下。 ; DB 0015H, 0000H ; Log Min (0) 全局,功能为“最小值”,表示数据的可以达到的逻辑最小值为0 DB 0025H, 00FFH ; Log Max (255) 全局,“最大值”,表示最大为255 (一个字节)。 DB 0019H, 0000H ; Usage Min (0) 局部,功能为“最小值”,表示局部用途,即上文定义的自定义用途最小值为0 DB 0029H, 00FFH ; Usage Max (255) 局部。。。。 ; ; we will send the remote ID and selection ; need 2 byte (16 bit) data space. DB 0095H, 0002H ; Report Count (2) 全局,功能为“数据数量”,表示每个报告中数据的数据有2个 DB 0075H, 0008H ; Report Size (8) 全局,功能为“数据大小”,表示每个数据大小为 8 个bit。 DB 0081H, 0002H ; Input: (Data, Variable, Absolute) 主,功能为“输入”, ; 02H (00000010B) 表示以上的数据定义是变量,独立(非数组),绝对值(无符号)的。 DB 00C0H ; End Collection 主条目,不跟内容,“关集合”。表示关闭上面打开的Application集合。 entity2DescEnd:
通过这份报告描述符,我们就能知道,这个接口传输的数据是用于自定义用途的,而且是两个字节的数据。
当然,我这还是测试之用,到具体使用时,我会使用两个数据段,一个是 4 个字节的手持端ID数组,一个是1字节的手持端数据内容。
到时就会更复杂一些。
OK,剩下的工作就是编写这些描述符的发送函数了。我使用的是TI公司的代码,所以没有自己编写这些函数,就不误人子弟了。。
并且不同的芯片编写的代码也不一样。要看具体情况。有兴趣的读者就自己去体会吧~~