基于串流云桌面的远程外设驱动——HID(人机接口设备)的描述符介绍

关于HID的描述符

1. 前言

  HID(Human Interface Device,人机接口设备)是USB设备中常用的设备类型,是直接与人交互的USB设备(例如键盘、鼠标与游戏杆等),他是一种相对简单的USB设备(相对USB摄像头这些来说)。当我们插入USB键盘或者鼠标之后,Windows就能自动识别HID设备插入,加载相关驱动;例如我们可以在设备管理器中看到如下:
在这里插入图片描述

  那么Windows是怎么知道插入的设备是U盘还是键盘鼠标呢?这是因为USB插入的时候,主机会请求相关的描述符,一般来说设备描述符和接口描述中就会存在一个Type的字段,表示插入设备的类型。

对于一个HID设备,如果主机的驱动程序要与HID设备通信,设备的固件必须符合下列需求:

  • 设备的描述符必须识别该设备包含有HID接口。
  • 除了默认控制管道外,固件必须另外支持一个中断输入管道。
  • 固件必须包含一个报表描述符来定义要传送与接收的设备数据;所有的HID数据都必须使用定义过的报表格式来定义报表中数据的大小与内容。

因此,HID设备存在两个重要的描述符:

  1. HID设备描述符。
  2. HID报表描述符。

下面我们具体看一下这两个描述符的具体格式。

2. HID描述符

HID描述符在Windows中被定义为如下:

typedef struct _HID_DESCRIPTOR
{
    UCHAR   bLength;
    UCHAR   bDescriptorType;
    USHORT  bcdHID;
    UCHAR   bCountry;
    UCHAR   bNumDescriptors;

    //
    // An array of one OR MORE descriptors.
    //
    struct _HID_DESCRIPTOR_DESC_LIST {
       UCHAR   bReportType;
       USHORT  wReportLength;
    } DescriptorList [1];

} HID_DESCRIPTOR, * PHID_DESCRIPTOR;

该结构中,每一个项的含义如下:

偏移字段字节数说明
0bLength1描述符的长度(字节数目)
1bDescriptorType1描述符的类型 0x21 = HID
2bcdHID2HID设备所遵循的HID版本号,为4位16进制的BCD码。1.0即0x0100,1.1即0x0101,2.0即0x0200。
4bCountry1硬件设备所在国家的国家代码
5bNumDescriptors1类别描述符数目(至少有一个报表描述符)
6bReportType
bDescriptorType
1类别描述符的类型
7wReportLength
wDescriptorLength
2报表描述符的总长度

其中DescriptorList可以有多个,表示有多个类别描述符。

关于bNumDescriptor表示HID设备支持的下级描述符的数量,下一级描述符的类型有两种:

  1. 报告描述符。
  2. 物理描述符。

一般来说,我们都使用报表描述符,物理描述符极少用到。

3. HID报表描述符

  USB主机一般是以中断的方式向HID设备发送或者索取数据;也就是说USB主机发送一个请求,设备要根据硬件操作,向主机提交自己的状态变化,例如鼠标,当主机给鼠标设备发送请求后,鼠标需要把自己当前位置信息发送给主机。

  报告描述符就是描述我们说的传输事务中的原生数据,如果是鼠标的话,这份报告则为鼠标左移、鼠标右移、鼠标滑轮滚动、鼠标左键、鼠标右键的当前状态数据的集合。

  报告描述符是由一个一个通用项(Item)组成,每一个通用项可以描述一个或者多个相同功能的数据(比如同时描述8个按钮),包括数据的用途和各种属性。

Item的结构分为两部分:

  1. 前缀:分为bTag,bType,bSize三部分组成。
  2. 数据部分:前缀bSize描述的长度(短项目格式)。

Item有两种类型:

  1. Short Item。
  2. Long Item。

一般情况下我们使用Short Item的格式,这种Item格式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tCHgtRUU-1689645290674)(assets/images/c423d445717ea4f3f4b56a9e7e416fbcd3e3419d61d17689c7940e460dbf21e8.png)]

每一个Item使用bType来进行细分,称之为标签,主要有三种:

  1. 0(Bit为00)Main:分为Input、Output、Feature、Collection、End Collection。
  2. 1(Bit为01)Global:分为Usage Page、 Logical Minimum、Logical Maximum、Physical Minimum、Physical Maximum、Report Size、Report ID等等。
  3. 2(Bit为10)Local:分为Usage 、Usage Minimum、Usage Maximum、String 等等。

Main项目的功能如下:

项目标志(Tag)项目前缀(nn为数据长度)功能说明
Input0x8?
1000 00 nn
定义输入报表,主机利用该信息解析设备提供的数据。主机向控制端口发送Get_Report实现输入。
Output0x9?
1001 00 nn
创建输出报表,通过向设备发送Set_Report实现输出。
Feature0xb?
1011 00 nn
定义送往设备的设置信息。
Collection0xa1
1010 00 nn
Collection 开始一个集合。
End Collection0xc?
1100 00 nn
End Collection 结束集合。

定义2个以上数据(Input、 Output 和 Feature)的关系为集合,Collection 开始一个集合,之后的End Collection结束集合。Collection项目的数据部分说明Collection的类型。

Global项目的功能如下:

项目标志(Tag)项目前缀(nn为数据长度)功能说明
Usage Page0x0?
0000 01 nn
指定设备的功能
另外由于Usage项目有32位数据值,Usage Page项目用于为Usage项目在报表描述符中占居存储空间。用于存放后续Usage项目的高16位。
Logical Minimum0x1?
0001 01 nn
定义变量或数组项目的逻辑最小值
Logical Maximum0x2?
0010 01 nn
定义变量或数组项目的逻辑最大值
Physical Minimum0x3?
0011 01 nn
定义变量或数组项目的物理最小值和Logical Minimum对应
Physical Maximum0x4?
0100 01 nn
定义变量或数组项目的物理最大值和Logical Maximum对应
Unit Exponent0x5
0101 01 nn
定义数值是基于 10 的指数
Unit0x6?
0110 01 nn
单位
Report Size0x7?
0111 01 nn
指定报表数据区域所包含的位数
Report ID0x8?
1000 01 nn
报表 ID,该项目在报表中插入一个字节的报表ID
Report Count0x9?
1001 01 nn
报表中数据域的数目
Push0xa?
1010 01 nn
将 Global 项目状态表送入堆栈
Pop0xb?
1011 01 nn
从堆栈恢复 Global 项目状态表

Local项目的功能如下:

项目标志(Tag)项目前缀(nn为数据长度)功能说明
Usage0x0?
0000 10 nn
用法索引值,表示对项目或集合建议的用法,用于当一个项目描述多个控制,对每一个变量和数组元素都有建议的用法。
Usage Minimum0x1?
0001 10 nn
定义阵列或位图中控制操作的第一个用法
Usage Maximum0x2?
0010 10 nn
定义阵列或位图中控制操作的最后一个用法
Designator Index0x3?
0011 10 nn
确定用于控制的实体,指向物理描述符中的目标。
Designator Minimum0x4?
0100 10 nn
定义阵列或位图目标的起始索引值
Designator Maximum0x5
0101 10 nn
定义阵列或位图目标的终止索引值
String Index0x7?
0111 10 nn
确定字符串描述符中的索引值
String Minimum0x8?
1000 10 nn
定义用于阵列或位图控制中字符串序列索引值的最小值和最大值
String Maximum0x9?
1001 10 nn
定义用于阵列或位图控制中字符串序列索引值的最大值
Delimiter0xa?
1010 10 nn
定义一组 Local 项目的开始和结束, 1=开始, 0=结束。

在这些项目中,Usage Page用来指定设备的功能,而Usage项目用来指定个别报表的功能。Usage Page项目相当于是HID的子集合,Usage相当于是Usage Page的子集合。

关于报表描述符他是非常复杂的,主要是报表描述符需要描述不同HID设备的信息(各个厂商设备都可以定义自己的格式);因此我们无需从头自己写一个报表描述符,可以看懂报表描述符和修改定制报表描述符就行了,这里我们可以看一下一个键盘的报表描述符的定义:

;=========================================
;HID Reports Descriptor 报表描述符
;=========================================
DB 0x05, 1 ; Usage Page (1: Generic Desktop)
DB 0x09, 6 ; Usage (6: Keyboard) 表示报表定义的是HID键盘
DB 0xA1, 1 ; Collection (1: Application) ====================集合开始
;
; 以下定义了键盘的修饰键输入报表,共有8个键,组成一个字节
; 用法见HID Usage Table中的第10节中的键盘用法定义
DB 0x05, 7 ; Usage page (7: Key Codes)
DB 0x19, 224 ; Usage Minimum (224)
DB 0x29, 231 ; Usage Maximum (231)
DB 0x15, 0 ; Logical Minimum (0)
DB 0x25, 1 ; Logical Maximum (1)
DB 0x75, 1 ; Report Size (1)
DB 0x95, 8 ; Report Count (8)
DB 0x81, 2 ; Input (Data,Variable,Absolute)
;;
以下定义了一个保留字节的输入报表
DB 0x95, 1 ; Report Count (1)
DB 0x75, 8 ; Report Size (8),
DB 0x81, 1 ; Input (Constant) = Reserved Byte
;;
以下定义了键盘的LED指示灯输出报表项目,共有5个指示灯
; 用法见HID Usage Table中的第11节中的LED用法定义
DB 0x95, 5 ; Report Count (5)
DB 0x75, 1 ; Report Size (1)
DB 0x05, 8 ; Usage Page (Page# for LEDs)
DB 0x19, 1 ; Usage Minimum (1)
DB 0x29, 5 ; Usage Maximum (5)
DB 0x91, 2 ; Output (Data, Variable, Absolute)
;;
以下定义了3个填充位,与前面的5个LED指示灯数据组成一个完整的字节
DB 0x95, 1 ; Report Count (1)
DB 0x75, 3 ; Report Size (3)
DB 0x91, 1 ; Output (Constant)
;;
以下定义了键盘的按键值输入报表项目,共6个字节,存放键编号(0~101)
; 用法见HID Usage Table中的第10节中的键盘用法定义
; 这样的设计可以允许一次输入6个按键的键值
DB 0x95, 6 ; Report Count (6)
DB 0x75, 8 ; Report Size (8)
DB 0x15, 0 ; Logical Minimum (0)
DB 0x25, 101 ; Logical Maximum (101)
DB 0x05, 7 ; Usage Page (7: Key Codes)
DB 0x19, 0 ; Usage Minimum (0)
DB 0x29, 101 ; Usage Maximum (101)
DB 0x81, 0 ; Input (Data, Array)
DB 0xC0 ; End_Collection ================================ 集合结束

4. 总结

HID设备通过HID设备描述符和HID报表描述符来描述HID设备的具体信息;对于如果进一步了解HID设备,掌握这两个描述符是必不可少的技能,尤其是我们后面讨论的键盘,鼠标,手写板等硬件设备的虚拟。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值