HID鼠标的学习

HID鼠标的设备描述符

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
char ReportDescriptor[52] = {
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) // 使用的最小值为1。值为1表示左键按下
0x29, 0x03, // USAGE_MAXIMUM (Button 3) // 使用的最大值为3。值为2表示右键按下,3表示中键按下
0x15, 0x00, // LOGICAL_MINIMUM (0) // 逻辑最小值0
0x25, 0x01, // LOGICAL_MAXIMUM (1) // 逻辑最大值1
0x95, 0x03, // REPORT_COUNT (3) // 报告的个数为3,即总共有3个bits
0x75, 0x01, // REPORT_SIZE (1) // 报告的大小为1bit。
0x81, 0x02, // INPUT (Data,Var,Abs) // 输入按键的值。bit-0为左键,bit-1为右键,bit-2为中键,按下时对应的位值为1,释放时对应的值为0
0x95, 0x01, // REPORT_COUNT (1) -- 该数据段个数为1
0x75, 0x05, // REPORT_SIZE (5) -- 每个段长度为5bit,即一个5bit
0x81, 0x03, // INPUT (Cnst,Var,Abs) -- 用上面两行填充的一个5bit来补足第一个字节
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 用途页为通用桌面
0x09, 0x30, // USAGE (X) // 在通用桌面中对应为X
0x09, 0x31, // USAGE (Y) // 在通用桌面中对应为Y
0x09, 0x38, // USAGE (Wheel) // 在通用桌面中对应为滚轮(本行可去掉)
0x15, 0x81, // LOGICAL_MINIMUM (-127) // 逻辑最小值为-127
0x25, 0x7F, // LOGICAL_MAXIMUM (127) // 逻辑最大值为+127
0x75, 0x08, // REPORT_SIZE (8) // 每个段长度为8个bits
0x95, 0x03, // REPORT_COUNT (3) // 该数据段个数为3个。分别代表x,y,滚轮(本行可将数量变为2,即没有滚轮)
0x81, 0x06, // INPUT (Data,Var,Rel) // 输入X,Y,滚轮的值
0xC0, // END_COLLECTION // 物理层次描述结束
0xC0 // END_COLLECTION // 应用层次描述结束
};

// 通过对上面的报告分析,我们知道该鼠标报告返回4个字节,分别代表 [按键,X,Y,滚轮]
// 如果鼠标左键按下则返回01 00 00 00(十六进制值),如果右键按下则返回02 00 00 00
// 如果鼠标中键按下则返回04 00 00 00,如果三个键同时按下,则返回07 00 00 00

HID鼠标通过脚本创建

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
#!/bin/bash

#modprobe libcomposite # 加入驱动

# 定义USB产品的VID和PID
mkdir -p /sys/kernel/config/usb_gadget/g1 # 在usb_gadget 中创建我们设备的文件夹
cd /sys/kernel/config/usb_gadget/g1 # 当我们创建完这个文件夹之后,系统自动的在这个文件夹中创建usb相关的内容 ,这些内容需要由创建者自己填写。
echo "0x046d" > idVendor # Logitech, Inc.
echo "0xc53f" > idProduct # USB Receiver
echo "0x0100" > bcdDevice # v1.0.0
echo "0x0200" > bcdUSB # USB2

mkdir -p strings/0x409 # 实例化英语语言ID(0x409是USB language ID 美国英语)
echo "831409-0000" > strings/0x409/serialnumber
echo "Logitech" > strings/0x409/manufacturer # 将序列号和制造商等信息字符串写入内核
echo "Logitech USB Receiver" > strings/0x409/product

# 创建HID鼠标设备功能的描述 /dev/hidg0
mkdir -p functions/hid.mouse
echo 0 > functions/hid.mouse/subclass # 0 No subclass
echo 2 > functions/hid.mouse/protocol # 2 Mouse
echo 4 > functions/hid.mouse/report_length # 鼠标报告的长度
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x09\\x38\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x03\\x81\\x06\\xc0\\xc0 > functions/hid.mouse/report_desc

# 创建HID键盘设备功能的描述 /dev/hidg1
# mkdir -p functions/hid.keyboard
# echo 1 > functions/hid.keyboard/subclass # 1 Boot Interface Subclass
# echo 1 > functions/hid.keyboard/protocol # 1 Keyboard
# echo 8 > functions/hid.keyboard/report_length # 键盘报告的长度
# echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.keyboard/report_desc

# 创建一个USB Configuration配置实例并定义配置描述符使用的字符串
mkdir -p configs/c.1/strings/0x409
echo 250 > configs/c.1/MaxPower # 当设备采用总线供电时,设备可从主机提取的最大功率
#echo 0x80 > configs/c.1/bmAttributes # 配置是否支持远程唤醒功能,以及设备是总线供电还是自供电
#echo "Config 1" > configs/c.1/strings/0x409/configuration
# bmAttributes:一个字节大小,BIT7:保留,必须为1。BIT6:1表示设备是自己供电,0表示是总线供电。BIT5:1表示支持远程唤醒。BIT4~BIT0:保留,必须为0
# bMaxPower:总线供电时的最大电流,单位以2mA为基准,例如0x32为50*2=100mA。USB设备可以从USB总线上获得最大的电流为500mA(所以最大值为250)

# 关联配置和功能的文件夹和启用设备
ln -s functions/hid.mouse configs/c.1 # 捆绑功能 Function 实例到配置 Configuration
# ln -s functions/hid.keyboard configs/c.1 # 当我们执行完这段命令后,系统就自动的帮我们创建了hid设备
ls /sys/class/udc > UDC # 将gadget驱动注册到UDC上,插上USB线到电脑上,电脑就会枚举USB设备

HID鼠标上报数据格式

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
# 字节(BYTE)是计算机数据处理的最小单位,一个字节是八个比特(BIT)
# HID鼠标发送给PC的数据每次为四个字节,这四个字节的定义分别是:

BYTE1 –-
|– 7~3: 用0填充
|–bit2: 1 表示中键按下,0 表示中键抬起
|–bit1: 1 表示右键按下,0 表示右键抬起
|–bit0: 1 表示左键按下,0 表示左键抬起
BYTE2 –- X 坐标变化量, 1~127 (0x01~0x7F)为鼠标光标朝 右/下/滚轮上推 移动。
BYTE3 –- Y 坐标变化量,255~129 (0xFF~0x81)为鼠标光标朝 左/上/滚轮下拉 移动。
BYTE4 –- 滚轮 变化量,与 XY 的坐标变化量相同,0和128 (0x00和0x80)为不移动。

# 首字节值的解释,最右边的是第一个比特 右边是头 左边是尾
| BIT 7 | BIT 6 | BIT 5 | BIT 4 | BIT 3 | BIT 2 | BIT 1 | BIT 0 |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| 0 | 0 | 0 | 0 | 0 | 中 键 | 右 键 | 左 键 |

左键按下:0 0 0 0 0 0 0 1 该二进制:1 的十进制值为:1
右键按下:0 0 0 0 0 0 1 0 该二进制:10 的十进制值为:2
中键按下:0 0 0 0 0 1 0 0 该二进制:100 的十进制值为:4


# X,Y,滚轮的取值。7F和81为大范围移动,01和FF为微距离移动
# 以128为原点,XY的值越靠近原点鼠标的移动速度越快,越远离原点越慢
# 1~127 (0x01~0x7F) ---- 128 (0x80) ---- 129~255 (0x81~0xFF)

255 (0xFF) ↑ Y
|
|
|
|
129 (0x81) |
255 ---------------128--------------→ 1
| 127 (0x7F) X
|
|
|
|
1 (0x01) |

部分编程语言操作示例

  1. Shell下操作示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 松开为 0,左键为 1,右键为 2,中键为 4
echo -ne "\x01\x00\x00\x00" > /dev/hidg1 # 按住左键
echo -ne "\x00\x00\x00\x00" > /dev/hidg1 # 松开按键
echo -ne "\x02\x00\x00\x00\x00\x00\x00\x00" > /dev/hidg1 # 单击右键
echo -ne "\x04\x00\x00\x00\x00\x00\x00\x00" > /dev/hidg1 # 单击中键

# 1~127 (0x01~0x7F)为向 右/下/滚轮上 移动,255~129 (0xFF~0x81)为向 左/上/滚轮下 移动
echo -ne "\x00\x01\x00\x00" > /dev/hidg1 # 微量右移
echo -ne "\x00\x7f\x00\x00" > /dev/hidg1 # 大量右移
echo -ne "\x00\xFF\x00\x00" > /dev/hidg1 # 微量左移
echo -ne "\x00\x81\x00\x00" > /dev/hidg1 # 大量左移

# 以128为原点,XY的值越靠近原点鼠标的移动速度越快,越远离原点越慢
echo -ne "\x00\x00\x01\x00" > /dev/hidg1 # 微量下移
echo -ne "\x00\x00\x7f\x00" > /dev/hidg1 # 大量下移
echo -ne "\x00\x00\xff\x00" > /dev/hidg1 # 微量上移
echo -ne "\x00\x00\x81\x00" > /dev/hidg1 # 大量上移

# 滚轮变化
echo -ne "\x00\x00\x00\x01" > /dev/hidg1 # 滚轮上推(滚动5行)
echo -ne "\x00\x00\x00\x7F" > /dev/hidg1 # 滚轮上推(滚120行)
echo -ne "\x00\x00\x00\x81" > /dev/hidg1 # 滚轮下拉(滚120行)
echo -ne "\x00\x00\x00\xFF" > /dev/hidg1 # 滚轮下拉(滚动4行)