简述
USB协议在计算机中使用非常广泛,在外部有USB设备插入时,就会检测到并进行初始化和启动,而后遵循USB协议传输数据,供应用获取来使用。
USB设备种类过于繁多,因此被划分为了不同的设备类(Device Class),而HID(Human Interface Device Class,人机交互设备类)就是其中尤为重要的一类。当然也还有许多其他的常见的设备类:
什么是HID
人机交互?还是比较笼统,HID具体的定义是“HID设备是指那些可被用来控制和操作计算机系统的设备”。其常见的一些举例如下:
- 键盘与指针设备—-如标准mouse,trackballs, joystick
- 面板控制设备—-如knobs, switches, buttons, and sliders
- 在类似手机,VCR遥控器上的控制设备—-如data gloves, throttles, steering wheels, and rudder pedals
- 一些可能并不需要人输入但输出数据的控制设备—-如bar-code readers, thermometers, voltmeters
HID设备管理
HID设备的信息被存于其ROM中,被称为descriptor(描述符,还是用英文更贴切)的结构,因此在设计实现自己的HID设备时,弄清楚或者定义好descriptor应该是最为关键的一步。如下所示,descriptors组成了information和data:
而在设备的不同阶段和状态,会涉及到许多不同类别的descriptor:
这其中HID开发时接触最多的还是Report Descriptor了,因为Report Descriptor都被用来传输各类应用数据。
HID之所以要使用Report Descriptor,主要是扩展性考虑,用Report来表示其传输的数据类型,这样对于不同的HID设备,就不需要针对每个定义一个SubClass了。HID设备一被检测到,Report Descriptor就会被加载和解析。
Report Descriptor Format
Report Descriptor的数据组织形式是一个个的信息段,每个信息段称为Item:
Item Format
上面提到的Item的格式表示如下:
Item的第一个byte可表示Item的类型,是long item或short item,如下是一个long item示例:
Item Parser
Item parser会从接收到的Report Descriptor数据中,逐个进行Item的抽取,每抽取一个就放在Item state table的对应位置。这就是Item parser的过程了:
当一个Main item被找到时,就需要分配并初始化一个report结构,并将其中的Local item放到这个report结构下。而Global item是对于所有report结构都适用的,只是Global item会对其后面的所有的report结构都会起作用。
对于Push和Pop item的理解:
item state table相当于一个全局变量,只能暂存一个item state table(表示一个Report Descriptor的整体数据),而另外有一个stack,可以将某一时刻的item state table的快照放到该stack当中,当后面的item state table全局变量改变了,要使用之前的item state table内容时,这时候stack中暂存的item state table就要起作用咯。
Usages
用来表明Report Descriptor中数据的用途的,一个Report Descriptor中是会有多个usage的,因为其中数据本来就是分片的(items)。
Usage划分为Page和ID组合的方式,这样可以表示更多用途,即通过两级来区分用途,首先是用途page,然后对应用途page下的用途ID:
Reports
Report是HID最终实际用来进行有用数据传输的,前面的Usage在HID设备刚接入时,就会被读取,而Report Descriptor被Host读取后,HID Device可以向Host发送Reports,也可以从Host接收Reports。有三种Reports类别:
有些时候一个Report Descriptor只描述了一个Report,这时候就不需要区分是哪一个Report的问题:
而有的时候,一个Report Descriptor中描述了好几个Report,这时候就要引入一个Report ID来标识是哪个Report的数据:
Strings
一般,在用户自定义了某数据段用途,此时Strings的使用就有必要的,因为要进行比较详细的描述。
Strings和Usage一样,对一个item而言并不是必须的。
多字节值格式
有些数值是可变的,即在一定的范围之内,这个数值可能用多个byte区域来表示,这些bit的排列都是按照little endian来排列的,如下是一个实例:
Orientation
一些有方向性的数据表示时,HID设备一般有统一的方向上的规定:
如上面就是坐标的方向正负规定,另外还有on(1)/off(0)表示和true(1)/false(0)的统一规定。
Null Values
HID设备有忽略一个report中某些数据段的能力。这里的意思是,通过声明report中的某bit区域可以放置比实际control产生的数据要大的数值的(一个control就是一个控制,如键按下,用data来描述一个control的内涵的)。
当host或device收到这个超出实际control含义的数据时,也不会去改变这个数据。
如果每次发给device的report的某个数据段,Application都不会去改变它,这个数据段应当提供一个Null Value。
Null Value举例:
比如一个8 bit的数据段,数据可以表示从0到0xFF的,但是该数据段已声明有效范围是0到0x7F,那接收到的在0x80到0xFF之间的数值都会被忽略掉。
HID设备的Descriptor
三种Descriptor,HID Descriptor, Report Descriptor和Physical Descriptor。
HID Descriptor
Report Descriptor
Report Descriptor的意义:
只要通过查看Report Descriptor,App就可以知道详细的HID设备的数据的含义,进而进行对应的处理。
Global和Local item是对Main item内容的附加限定:
Control的再次说明:
一个HID device可能有多个control,一个control就是一个方面的控制功能,如一个按键控制功能,一个LED控制功能等。
Report必须具备的item
关于其中的Input(Output or Feature)类型的说明:
Feature item,是Host可以通过application来设置Device的一些特性,比如复位坐标原点,扫描频率等,这些设置可能Device并不能很明显的看到,是在Device内部的一些属性特征的改变。
而Output,则是对Device执行的某些命令控制操作,例如点灯,播放音频等。
Physical Descriptor
用来描述人在控制某个动作时,有怎样的物理上的要求。比如去按下其中第二个按钮时,需要使用左手等这类描述。