文档章节

[转]Tutorial about USB HID Report Descriptors

卜霞森
 卜霞森
发布于 2015/04/15 19:20
字数 2435
阅读 27
收藏 1

Tutorial about USB HID Report Descriptors

This page is from my old website, and it is sort of popular, so I’ve moved it here.

A USB HID report descriptor is one of the descriptors that a USB host can request from a USB device. HID devices send data to the host using reports, and the descriptor tells the host how to interpret the data. I will try to show you how to write one of these descriptors.

First, go to this page http://www.usb.org/developers/hidpage/ and find the document titled “Device Class Definition for HID”. What I will be talking about is essentially paraphrasing the important sections of that document.

Second, go get the HID descriptor tool from the same page. You’ll want to play with it as you go through this tutorial. It is an absolute headache to write the HID report descriptors manually (converting between binary and hex and looking up the meanings of the numbers) so this tool is essential.


What is a USB HID report descriptor?

The HID protocol makes implementation of devices very simple. Devices define their data packets and then present a “HID descriptor” to the host. The HID descriptor is a hard coded array of bytes that describe the device’s data packets. This includes: how many packets the device supports, how large are the packets, and the purpose of each byte and bit in the packet. For example, a keyboard with a calculator program button can tell the host that the button’s pressed/released state is stored as the 2nd bit in the 6th byte in data packet number 4 (note: these locations are only illustrative and are device specific). The device typically stores the HID descriptor in ROM and does not need to intrinsically understand or parse the HID descriptor. Some mouse and keyboard hardware in the market today are implemented using only an 8-bit CPU.

– Wikipedia on Human Interface Device

I’m going to try teaching you about USB HID report descriptors by walking you through writing a few.


For a simple starting point, let us make a standard mouse. Just three buttons, and movement on the X and Y axis. So we want to send data regarding the buttons and movement. It takes one bit to represent each button, and one byte to represent the movement on one axis as a signed integer. So we can say that we want the data structure to look something like this

  Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Byte 0 Useless Useless Useless Useless Useless Left Button Middle Button Right Button
Byte 1 X Axis Relative Movement as Signed Integer
Byte 2 Y Axis Relative Movement as Signed Integer

And then we can say our data structure in C looks like

?
1
2
3
4
5
6
structmouse_report_t
{
    uint8_t buttons;
    int8_t x;
    int8_t y;
}

So now in our descriptor, our first item must describe buttons, three of them

?
1
2
3
USAGE_PAGE (Button)
USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM (Button 3)

each button status is represented by a bit, 0 or 1

?
1
2
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)

there are three of these bits

?
1
2
REPORT_COUNT (3)
REPORT_SIZE (1)

send this variable data to the computer

?
1
INPUT (Data,Var,Abs)

and the final result looks like

?
1
2
3
4
5
6
7
8
USAGE_PAGE (Button)
USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM (Button 3)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)
REPORT_COUNT (3)
REPORT_SIZE (1)
INPUT (Data,Var,Abs)

that will represent the buttons

but what about the five useless padding bits?

?
1
2
3
REPORT_COUNT (1)
REPORT_SIZE (5)
INPUT (Cnst,Var,Abs)

now we make the X axis movement

?
1
2
USAGE_PAGE (Generic Desktop)
USAGE (X)

we want it to be a signed integer that takes one byte, so it has a value between -127 and +127 (actually -128 and +127, but I want to keep things even)

?
1
2
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)

we want it to take an entire byte which is 8 bits

?
1
2
REPORT_SIZE (8)
REPORT_COUNT (1)

and send it to the computer as a variable relative coordinate

?
1
INPUT (Data,Var,Rel)

you end up with something like this to represent the X axis movement

?
1
2
3
4
5
6
7
USAGE_PAGE (Generic Desktop)
USAGE (X)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8)
REPORT_COUNT (1)
INPUT (Data,Var,Rel)

How about Y axis? You can try

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
USAGE_PAGE (Generic Desktop)
USAGE (X)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8)
REPORT_COUNT (1)
INPUT (Data,Var,Rel)
USAGE_PAGE (Generic Desktop)
USAGE (Y)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8)
REPORT_COUNT (1)
INPUT (Data,Var,Rel)

Which will work, but to save memory, we can do this instead

?
1
2
3
4
5
6
7
8
USAGE_PAGE (Generic Desktop)
USAGE (X)
USAGE (Y)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8)
REPORT_COUNT (2)
INPUT (Data,Var,Rel)

So all your data will end up looking like

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
USAGE_PAGE (Button)
USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM (Button 3)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)
REPORT_COUNT (3)
REPORT_SIZE (1)
INPUT (Data,Var,Abs)
REPORT_COUNT (1)
REPORT_SIZE (5)
INPUT (Cnst,Var,Abs)
USAGE_PAGE (Generic Desktop)
USAGE (X)
USAGE (Y)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8)
REPORT_COUNT (2)
INPUT (Data,Var,Rel)

Ah but we are not done, in order to make the computer know that this is a mouse, we do

?
1
2
3
4
5
6
7
8
9
10
USAGE_PAGE (Generic Desktop)
USAGE (Mouse)
COLLECTION (Application)
    USAGE (Pointer)
    COLLECTION (Physical)
     
    ... What we wrote already goes here
     
    END COLLECTION
END COLLECTION

So in the end, this is the USB HID report descriptor for a standard mouse

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
0x05, 0x01,                   // USAGE_PAGE (Generic Desktop)
0x09, 0x02,                   // USAGE (Mouse)
0xa1, 0x01,                   // COLLECTION (Application)
0x09, 0x01,                   //   USAGE (Pointer)
0xa1, 0x00,                   //   COLLECTION (Physical)
0x05, 0x09,                   //     USAGE_PAGE (Button)
0x19, 0x01,                   //     USAGE_MINIMUM (Button 1)
0x29, 0x03,                   //     USAGE_MAXIMUM (Button 3)
0x15, 0x00,                   //     LOGICAL_MINIMUM (0)
0x25, 0x01,                   //     LOGICAL_MAXIMUM (1)
0x95, 0x03,                   //     REPORT_COUNT (3)
0x75, 0x01,                   //     REPORT_SIZE (1)
0x81, 0x02,                   //     INPUT (Data,Var,Abs)
0x95, 0x01,                   //     REPORT_COUNT (1)
0x75, 0x05,                   //     REPORT_SIZE (5)
0x81, 0x03,                   //     INPUT (Cnst,Var,Abs)
0x05, 0x01,                   //     USAGE_PAGE (Generic Desktop)
0x09, 0x30,                   //     USAGE (X)
0x09, 0x31,                   //     USAGE (Y)
0x15, 0x81,                   //     LOGICAL_MINIMUM (-127)
0x25, 0x7f,                   //     LOGICAL_MAXIMUM (127)
0x75, 0x08,                   //     REPORT_SIZE (8)
0x95, 0x02,                   //     REPORT_COUNT (2)
0x81, 0x06,                   //     INPUT (Data,Var,Rel)
0xc0,                         //   END_COLLECTION
0xc0                          // END_COLLECTION

This is actually the example descriptor provided with the USB HID documentation, and you can also find this as an example provided with the HID tool.


Cool, at this point, you will have encountered some concepts that you may have questions about, you should research the following:

Usage Pages

There’s one thing that I think isn’t explained well in the documentation, USAGE, USAGE_PAGE, USAGE_MINIMUM and USAGE_MAXIMUM. In a descriptor, you first set a USAGE_PAGE, and certain USAGEs are available. In the mouse example, USAGE_PAGE (Generic Desktop) allowed you to use USAGE (Mouse), and when the usage page was changed to USAGE_PAGE (Button), then the USAGE_MINIMUM and USAGE_MAXIMUM allowed you to specify the buttons, and before you can use USAGE (X) and USAGE (Y), the usage page was changed back to USAGE_PAGE (Generic Desktop). The usage page is like a namespace, changing the usage page affects what “usages” are available. Read the documentation called ” HID Usage Tables” for more info.

Collections

Read the documentation about the official proper use of collections. In my own words, collections can be used to organize your data, for example, a keyboard may have a built-in touchpad, then the data for the keyboard should be kept in one application collection while the touchpad data is kept in another. We can assign an “Report ID” to each collection, which I will show you later.


Hey here’s something you can do, by turning “USAGE (Mouse)” into “USAGE (Gamepad)”, you make the computer think that it’s a game pad with one joystick and 3 buttons. How about converting a Playstation 2 controller into a USB gamepad? The controller has 16 buttons and two thumb sticks, so we want the data to look like

  Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Byte 0 Button Button Button Button Button Button Button Button
Byte 1 Button Button Button Button Button Button Button Button
Byte 2 Left X Axis as Signed Integer
Byte 3 Left Y Axis as Signed Integer
Byte 4 Right X Axis as Signed Integer
Byte 5 Right Y Axis as Signed Integer

So our data structure looks like

?
1
2
3
4
5
6
7
8
structgamepad_report_t
{
    uint16_t buttons;
    int8_t left_x;
    int8_t left_y;
    int8_t right_x;
    int8_t right_y;
}

We make the computer understand that it’s a game pad

?
1
2
3
4
5
6
7
8
9
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
 
    ...
 
    END COLLECTION
END COLLECTION

for the buttons

?
1
2
3
4
5
6
7
8
USAGE_PAGE (Button)
USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM (Button 16)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)
REPORT_COUNT (16)
REPORT_SIZE (1)
INPUT (Data,Var,Abs)

for the four thumb stick axis

?
1
2
3
4
5
6
7
8
9
10
USAGE_PAGE (Generic Desktop)
USAGE (X)
USAGE (Y)
USAGE (Z)
USAGE (Rx)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8)
REPORT_COUNT (4)
INPUT (Data,Var,Abs)

NOTE: Z is used to represent the right stick’s X axis, Rx is used to represent the right stick’s Y axis. This doesn’t make sense but this is how most existing USB game pads work. I have tested this using Battlefield Bad Company 2, it works.

NOTE: Use “absolute” for something like joysticks, but “relative” for things like mouse.

So now you end up with

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
        USAGE_PAGE (Button)
        USAGE_MINIMUM (Button 1)
        USAGE_MAXIMUM (Button 16)
        LOGICAL_MINIMUM (0)
        LOGICAL_MAXIMUM (1)
        REPORT_COUNT (16)
        REPORT_SIZE (1)
        INPUT (Data,Var,Abs)
        USAGE_PAGE (Generic Desktop)
        USAGE (X)
        USAGE (Y)
        USAGE (Z)
        USAGE (Rx)
        LOGICAL_MINIMUM (-127)
        LOGICAL_MAXIMUM (127)
        REPORT_SIZE (8)
        REPORT_COUNT (4)
        INPUT (Data,Var,Abs)
    END COLLECTION
END COLLECTION

Hey how about two players? Here’s where collections get handy

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
        REPORT_ID (1)
        ...
    END COLLECTION
END COLLECTION
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
        REPORT_ID (2)
        ...
    END COLLECTION
END COLLECTION

fill in the data areas and you end up with

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
        REPORT_ID (1)
        USAGE_PAGE (Button)
        USAGE_MINIMUM (Button 1)
        USAGE_MAXIMUM (Button 16)
        LOGICAL_MINIMUM (0)
        LOGICAL_MAXIMUM (1)
        REPORT_COUNT (16)
        REPORT_SIZE (1)
        INPUT (Data,Var,Abs)
        USAGE_PAGE (Generic Desktop)
        USAGE (X)
        USAGE (Y)
        USAGE (Z)
        USAGE (Rx)
        LOGICAL_MINIMUM (-127)
        LOGICAL_MAXIMUM (127)
        REPORT_SIZE (8)
        REPORT_COUNT (4)
        INPUT (Data,Var,Abs)
    END COLLECTION
END COLLECTION
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
        REPORT_ID (2)
        USAGE_PAGE (Button)
        USAGE_MINIMUM (Button 1)
        USAGE_MAXIMUM (Button 16)
        LOGICAL_MINIMUM (0)
        LOGICAL_MAXIMUM (1)
        REPORT_COUNT (16)
        REPORT_SIZE (1)
        INPUT (Data,Var,Abs)
        USAGE_PAGE (Generic Desktop)
        USAGE (X)
        USAGE (Y)
        USAGE (Z)
        USAGE (Rx)
        LOGICAL_MINIMUM (-127)
        LOGICAL_MAXIMUM (127)
        REPORT_SIZE (8)
        REPORT_COUNT (4)
        INPUT (Data,Var,Abs)
    END COLLECTION
END COLLECTION

This is really important: You must change your data structure to include the report ID

  Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Byte 0 Report ID
Byte 1 Button Button Button Button Button Button Button Button
Byte 2 Button Button Button Button Button Button Button Button
Byte 3 Left X Axis as Signed Integer
Byte 4 Left Y Axis as Signed Integer
Byte 5 Right X Axis as Signed Integer
Byte 6 Right Y Axis as Signed Integer
?
1
2
3
4
5
6
7
8
9
structmultiplayer_gamepad_report_t
{
    uint8_t report_id;
    uint16_t buttons;
    int8_t left_x;
    int8_t left_y;
    int8_t right_x;
    int8_t right_y;
}

You must manually set the report ID before you send the data to the computer in order for the computer to understand which player the data belongs to.

?
1
2
3
4
multiplayer_gamepad_report_t player1_report;
multiplayer_gamepad_report_t player2_report;
player1_report.report_id = 1;
player2_report.report_id = 2;

You can also use collections and report IDs to make composite devices. So far I’ve shown you the keyboard, mouse, and gamepad. Here’s something that describes a composite device that is a keyboard, mouse, and two player game pad.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
USAGE_PAGE (Generic Desktop)
USAGE (Keyboard)
COLLECTION (Application)
    REPORT_ID (1)
    ...
END COLLECTION
USAGE_PAGE (Generic Desktop)
USAGE (Mouse)
COLLECTION (Application)
    USAGE (Pointer)
    COLLECTION (Physical)
        REPORT_ID (2)
        ...
    END COLLECTION
END COLLECTION
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
        REPORT_ID (3)
        ...
    END COLLECTION
END COLLECTION
USAGE_PAGE (Generic Desktop)
USAGE (Game Pad)
COLLECTION (Application)
    COLLECTION (Physical)
        REPORT_ID (4)
        ...
    END COLLECTION
END COLLECTION

and of course, your data structures with the added report ID.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
structkeyboard_report_t
{
    uint8_t report_id;
    uint8_t modifier;
    uint8_t reserved;
    uint8_t keycode[6];
}
 
structmouse_report_t
{
    uint8_t report_id;
    uint8_t buttons;
    int8_t x;
    int8_t y;
}
 
structgamepad_report_t
{
    uint8_t report_id;
    uint16_t buttons;
    int8_t left_x;
    int8_t left_y;
    int8_t right_x;
    int8_t right_y;
}

But since we changed the data structure, your device no-longer supports boot protocol, and hence you will not need to define a protocol. So make sure you change usbconfig.h accordingly.

Want to see this in action? Load up this example project into USnooBie and let Windows’s “Devices and Printers” show you!

Example Project Files

hid_tutorial_1_ss[1]

This entry was posted in  Tutorial and tagged  usb on  January 1, 2013.


© 著作权归作者所有

上一篇: Error Code Name
下一篇: python 简易爬虫
卜霞森
粉丝 2
博文 50
码字总数 32877
作品 0
深圳
程序员
私信 提问
USB与BT HID reports描述符实践与抓包分析

文章内容提示 这篇文章主要说明如何获取与查看USB和BLE HoGP HID设备报告描述符,如何根据获取的描述符读懂report。然后说明了可以使用hidrd-convert工具,来如何根据hex报告描述符转化得到C...

sy373466062
2017/10/13
0
0
我的usb midi描述符 源码 共享

这里发一个midi描述符,意在投石问路,与大家交流。 usb midi描述符 源码: /******************** (C) COPYRIGHT 2008 STMicroelectronics ******************** * File Name : usb_desc.c ......

powder
2013/05/26
762
1
【USB】USB设备类代码表(USB Class Code)

USB设备类代码表官网链接:http://www.usb.org/developers/definedclass USB defines class code information that is used to identify a device’s functionality and to nominally load a......

u014647208
2018/04/18
0
0
基于libhid/libusb进行usb传输数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/byxdaz/article/details/88587091 一、libhid/libusb介绍 libusb介绍: libusb 设计了一系列的外部API 为应用...

byxdaz
03/15
0
0
USB-HID鼠标、键盘通讯格式

原文地址::https://www.amobbs.com/forum.php?mod=viewthread&tid=4823160&page=1&authorid=249202

xqhrs232
2018/01/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

可能是国内第一篇全面解读 Java 现状及趋势的文章

作者 | 张晓楠 Dragonwell JDK 最新版本 8.1.1-GA 发布,包括全新特性和更新! 导读:InfoQ 发布《2019 中国 Java 发展趋势报告》,反映 Java 在中国发展的独特性,同时也希望大家对 Java 有...

阿里云官方博客
2分钟前
0
0
Spring Boot 2.x基础教程:Swagger静态文档的生成

前言 通过之前的两篇关于Swagger入门以及具体使用细节的介绍之后,我们已经能够轻松地为Spring MVC的Web项目自动构建出API文档了。如果您还不熟悉这块,可以先阅读: Spring Boot 2.x基础教程...

程序猿DD
6分钟前
0
0
《毅力》读书笔记

1.确信你全身心地投入 2.准备好为目标进行艰难的跋涉 3.通过减少需要使用毅力的情形,为将来的挑战做好准备 4.尽可能具体细致地确定你的目标和实现目标的过程 5.把挑战分解为小而易于管理的小...

lingch
7分钟前
1
0
zk中快速选举FastLeaderElection实现

选举涉及概念 服务器状态 投票 如何选择投票? 协议 选举 如何进行选举? epoch 发送者 接收者 发送队列 接收队列 服务器状态 public enum ServerState { LOOKING,寻找Leader状态,当服务处于...

writeademo
10分钟前
0
0
教你玩转Linux—磁盘管理

Linux磁盘管理好坏直接关系到整个系统的性能问题,Linux磁盘管理常用三个命令为df、du和fdisk。 df df命令参数功能:检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了多少...

Linux就该这么学
12分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部