HID键盘的设备描述符12345678910111213141516171819202122232425262728293031323334353637383940414243char ReportDescriptor[63] = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 用途页为通用桌面设备 0x09, 0x06, // USAGE (Keyboard) // 用途为键盘 0xA1, 0x01, // COLLECTION (Application) // 表示应用集合 0x05, 0x07, // USAGE_PAGE (Keyboard) // 用途为键盘设备 0x19, 0xE0, // USAGE_MINIMUM (LeftControl) // 首字节的最小值为E0。首字节的组合按键 0x29, 0xE7, // USAGE_MAXIMUM (Right GUI) // 首字节的最大值为E7。组合按键取值范围 0x15, 0x00, // LOGICAL_MINIMUM (0) // 逻辑最小值0 0x25, 0x01, // LOGICAL_MAXIMUM (1) // 逻辑最大值1 0x75, 0x01, // REPORT_SIZE (1) // 表示每个修饰键大小为1bits 0x95, 0x08, // REPORT_COUNT (8) // 表示有8个这样的修饰键位 0x81, 0x02, // INPUT (Data,Var,Abs) // 输入第一字节的值(组合按键) 0x95, 0x01, // REPORT_COUNT (1) // 报告的个数为1,即总共有8个bits 0x75, 0x08, // REPORT_SIZE (8) // 报告的大小为8bit。 0x81, 0x03, // INPUT (Cnst,Var,Abs) // 输入第二字节空值(规范中保留所以0填充) 0x95, 0x05, // REPORT_COUNT (5) // 该报告的总个数为5个,即总共有5个bits 0x75, 0x01, // REPORT_SIZE (1) // 该报告每个大小为1bit 0x05, 0x08, // USAGE_PAGE (LEDs) // 用途为指示灯 0x19, 0x01, // USAGE_MINIMUM (Num Lock) // 该字节的最小值为01 0x29, 0x05, // USAGE_MAXIMUM (Kana) // 该字节的最大值为05 0x91, 0x02, // OUTPUT (Data,Var,Abs) // 该输出报告包含5bits的键盘指示灯状态信息 0x95, 0x01, // REPORT_COUNT (1) // 该报告的个数为1,即总共有3个bits用来补足 0x75, 0x03, // REPORT_SIZE (3) // 该报告的大小为3bit 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) // 将上面的输出报告填充补足为一字节 0x95, 0x06, // REPORT_COUNT (6) // 该报告的个数为6个 0x75, 0x08, // REPORT_SIZE (8) // 该报告每个大小为8bit 0x15, 0x00, // LOGICAL_MINIMUM (0) // 逻辑最小值0 0x25, 0x65, // LOGICAL_MAXIMUM (101) // 逻辑最大值101 0x05, 0x07, // USAGE_PAGE (Keyboard) // 用途为键盘设备 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) // 该字节的最小值为00 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) // 该字节的最大值为65 0x81, 0x00, // INPUT (Data,Ary,Abs) // 输入第三~八字节(即按键值) 0xC0 // END_COLLECTION // 应用集合结束};// 输入报告含8个字节,第1字节为特殊功能键,第2字节保留,第3~8字节为普通键值(取值范围为0~101)// 输出报告共1个字节,其中 BIT0 ~ BIT5 各自对应着下面某盏指示灯状态,该字节剩余用0填充// Num Lock、Caps Lock、Scroll Lock、Compose、Kana 这五盏指示灯的状态。灯亮为1,灯灭为0| BIT 7 | BIT 6 | BIT 5 | BIT 4 | BIT 3 | BIT 2 | BIT 1 | BIT 0 || :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | :-----: || 0 | 0 | 0 | Kana | Compose | Scroll | Caps | Num |
HID键盘通过脚本创建1234567891011121314151617181920212223242526272829303132333435363738394041424344#!/bin/bash#modprobe libcomposite # 加入驱动# 定义USB产品的VID和PIDmkdir -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 Receiverecho "0x0100" > bcdDevice # v1.0.0echo "0x0200" > bcdUSB # USB2mkdir -p strings/0x409 # 实例化英语语言ID(0x409是USB language ID 美国英语)echo "831409-0000" > strings/0x409/serialnumberecho "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/hidg1mkdir -p functions/hid.keyboardecho 1 > functions/hid.keyboard/subclass # 1 Boot Interface Subclassecho 1 > functions/hid.keyboard/protocol # 1 Keyboardecho 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/0x409echo 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 实例到配置 Configurationln -s functions/hid.keyboard configs/c.1 # 当我们执行完这段命令后,系统就自动的帮我们创建了hid设备ls /sys/class/udc > UDC # 将gadget驱动注册到UDC上,插上USB线到电脑上,电脑就会枚举USB设备
HID键盘数据上报格式12345678910111213141516171819202122232425262728293031# 字节(BYTE)是计算机数据处理的最小单位,一个字节是八个比特(bit)# HID键盘发送给PC的数据每次为八个字节,这八个字节的定义分别是:BYTE1 -- |--bit7: Right GUI # 是否按下,按下为1 |--bit6: Right Alt # 是否按下,按下为1 |--bit5: Right Shift # 是否按下,按下为1 |--bit4: Right Control # 是否按下,按下为1 |--bit3: Left GUI # 是否按下,按下为1 |--bit2: Left Alt # 是否按下,按下为1 |--bit1: Left Shift # 是否按下,按下为1 |--bit0: Left Control # 是否按下,按下为1BYTE2 -- # 暂不清楚,有的地方说是保留位BYTE3 ~ BYTE8 -- # 这六个为普通按键HID码的16进制# HID键盘数据上报示例01 00 00 00 00 00 00 00 # 按下 L_Ctrl 键01 00 06 00 00 00 00 00 # 按下 L_Ctrl + C 键02 00 1B 00 00 00 00 00 # 按下 L_Shift + X 键04 00 3B 00 00 00 00 00 # 按下 L_Alt + F2 键02 00 04 05 00 00 00 00 # 同时按下了 L_Shift + A + B 三个键# 键盘只能上报6个按键值,如果超过6个那么6个按键值将都是1,但特殊按键还是有效的20 00 01 01 01 01 01 01 # 按下 R_Shift 且另外6个按键也被按下# 键盘上报数据中,哪个按键先被按下,就先记录他的按键值00 00 04 05 00 00 00 00 # 按下键盘 A B 的USB上报数据值00 00 04 05 06 00 00 00 # 保持按下键盘 A B ,继续按下 C 的上报值00 00 05 06 00 00 00 00 # 松开 A,但保持 B C 不松开的上报值00 00 00 00 00 00 00 00 # 松开所有按键
组合键在第一字节的值
BIT 7
BIT 6
BIT 5
BIT 4
BIT 3
BIT 2
BIT 1
BIT 0
R_Win
R_Alt
R_Shift
R_Ctrl
L_Win
L_Alt
L_Shift
L_Ctrl
12345678910# 第一个字节(BYTE1)组合按键的值。最右边的是第一个比特(BIT0),左边是尾 右边是头L_Ctrl 按下:0 0 0 0 0 0 0 1 该二进制:1 的十进制值为:1L_Shift按下:0 0 0 0 0 0 1 0 该二进制:10 的十进制值为:2L_Alt 按下:0 0 0 0 0 1 0 0 该二进制:100 的十进制值为:4L_Win 按下:0 0 0 0 1 0 0 0 该二进制:1000 的十进制值为:8R_Ctrl 按下:0 0 0 1 0 0 0 0 该二进制: 10000 的十进制值为:16R_Shift按下:0 0 1 0 0 0 0 0 该二进制: 100000 的十进制值为:32R_Alt 按下:0 1 0 0 0 0 0 0 该二进制: 1000000 的十进制值为:64R_Win 按下:1 0 0 0 0 0 0 0 该二进制: 10000000 的十进制值为:128
组合按键
HID码值 (10 [16]进制)
组合按键
HID码值 (10 [16]进制)
L_Ctrl
1 [0X01]
R_Ctrl
16 [0X10]
L_Shift
2 [0X02]
R_Shift
32 [0X20]
L_Alt
4 [0X04]
R_Alt
64 [0X40]
L_Win
8 [0X08]
R_Win
128 [0X80]
部分编程语言操作示例
Shell下操作示例
1234567891011121314151617181920212223# -n:不自动换行 -e:解释转义字符# \0NN 插入NN( 八进制) 所代表的ASCII字符# \xAA 插入AA(十六进制)所代表的ASCII数字# 第一行为按下a键,第二行为松开所有按键。即输入aecho -ne "\x00\x00\x04\x00\x00\x00\x00\x00" > /dev/hidg0echo -ne "\x00\x00\x00\x00\x00\x00\x00\x00" > /dev/hidg0# 把ESC按键按下松开动作结合起来echo -ne "\0\0\x29\0\0\0\0\0\0\0\0\0\0\0\0\0" > /dev/hidg0# 分别单独输入 t e r m 字母echo -ne "\0\0\x17\0\0\0\0\0\0\0\0\0\0\0\0\0" > /dev/hidg0echo -ne "\0\0\x8\0\0\0\0\0\0\0\0\0\0\0\0\0" > /dev/hidg0echo -ne "\0\0\x15\0\0\0\0\0\0\0\0\0\0\0\0\0" > /dev/hidg0echo -ne "\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0" > /dev/hidg0# 按下并松开ENTER键echo -ne "\0\0\x28\0\0\0\0\0\0\0\0\0\0\0\0\0" > /dev/hidg0# 松开所有按键echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0
Python下操作示例
123456789101112131415161718192021222324252627#!/usr/bin/env python3NULL_CHAR = chr(0)def write_report(report): with open('/dev/hidg0', 'rb+') as fd: fd.write(report.encode())# Press a # HID键盘码的十进制即可write_report(NULL_CHAR*2+chr(4)+NULL_CHAR*5)# Release keyswrite_report(NULL_CHAR*8)# Press SHIFT + a = Awrite_report(chr(32)+NULL_CHAR+chr(4)+NULL_CHAR*5)# Press zwrite_report(NULL_CHAR*2+chr(29)+NULL_CHAR*5)# Release keyswrite_report(NULL_CHAR*8)# Press SHIFT + z = Zwrite_report(chr(32)+NULL_CHAR+chr(29)+NULL_CHAR*5)# RETURN/ENTER keywrite_report(NULL_CHAR*2+chr(40)+NULL_CHAR*5)# Release all keyswrite_report(NULL_CHAR*8)
JavaScript下操作示例
123456789101112131415161718192021222324#!/usr/bin/env nodevar fs = require("fs");// Buffer类用来创建一个专门存放二进制数据的缓存区// 创建一个长度为 8 用零填充的 Buffer,该值的作用是松开键盘按键const Rest_Key = Buffer.alloc(8);// 缓存区内可以使用十进制或十六进制的HID键盘码,该值的作用是按下Z键// var Hid_Key = Buffer.from([0, 0, 29, 0, 0, 0, 0, 0]);var Hid_Key = Buffer.from([0x0, 0x0, 0x1D, 0x0, 0x0, 0x0, 0x0, 0x0]);var Hid_Device = '/dev/hidg0'; // 指定Gadget Hid设备路径// fs中如果不指定encoding编码格式,默认以buffer格式读取function WriteReport(Hid_Device, Hid_Report) { fs.writeFile(Hid_Device, Hid_Report, function(err) { if (err) { return console.error(err); } });}WriteReport(Hid_Device, Hid_Key); // 按下 Z键WriteReport(Hid_Device, Rest_Key); // 松开按键
HID键盘输入数据查看12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758# 首先通过 cat /proc/bus/input/devices 查看各个设备的描述# 然后可根据关键字 keyboard或mouse 之类的定位到 event* 节点# 然后直接 cat /dev/input/event* 来查看输入设备是否有数据输入# 根据 N: Name= 为设备名,H: Handlers= 中 event* 节点可找到指定设备I: Bus=0003 Vendor=2207 Product=3588 Version=0101N: Name="rockchip Rockchip HID Gadget"P: Phys=usb-ff100000.usb-1/input0S: Sysfs=/devices/platform/ff100000.usb/usb1/1-1/1-1:1.0/0003:2207:3588.0001/input/input2U: Uniq=H: Handlers=kbd leds event2B: PROP=0B: EV=120013B: KEY=e080ffdf01cfffff fffffffffffffffeB: MSC=10B: LED=1fI: Bus=0003 Vendor=2207 Product=3588 Version=0101N: Name="rockchip Rockchip HID Gadget"P: Phys=usb-ff100000.usb-1/input1S: Sysfs=/devices/platform/ff100000.usb/usb1/1-1/1-1:1.1/0003:2207:3588.0002/input/input3U: Uniq=H: Handlers=event3B: PROP=0B: EV=17B: KEY=70000 0 0 0 0B: REL=903B: MSC=10I: Bus=0003 Vendor=046d Product=c53f Version=0111N: Name="Logitech USB Receiver Mouse"P: Phys=usb-xhci-hcd.4.auto-1/input1S: Sysfs=/devices/platform/usbdrd/fe500000.dwc3/xhci-hcd.4.auto/usb3/3-1/3-1:1.1/0003:046D:C53F.0004/input/input5U: Uniq=H: Handlers=event5B: PROP=0B: EV=17B: KEY=ffff0000 0 0 0 0B: REL=1943B: MSC=10-----------------------------------------------------------# 更好的办法是直接格式化读取HID输入设备root@debian:/# hexdump -e '8/1 " %02X" "\n"' /dev/hidraw0 00 00 04 00 00 00 00 00 # 按下 A 键 00 00 00 00 00 00 00 00 # 松开 A 键 01 00 00 00 00 00 00 00 # 按下 LCtrl 01 00 06 00 00 00 00 00 # 按下 C 键 01 00 00 00 00 00 00 00 # 松开 C 键 00 00 00 00 00 00 00 00 # 松开 LCtrl 02 00 00 00 00 00 00 00 # 按下 LShift 02 00 1D 00 00 00 00 00 # 按下 Z 键 02 00 00 00 00 00 00 00 # 松开 Z 键 00 00 00 00 00 00 00 00 # 松开 LShift
常用HID规范的键盘码
按键名称
HID码值 (10 [16]进制)
按键名称
HID码值 (10 [16]进制)
A
4 [0X04]
CapLck
57 [0X39]
B
5 [0X05]
F1
58 [0X3A]
C
6 [0X06]
F2
59 [0X3B]
D
7 [0X07]
F3
60 [0X3C]
E
8 [0X08]
F4
61 [0X3D]
F
9 [0X09]
F5
62 [0X3E]
G
10 [0X0A]
F6
63 [0X3F]
H
11 [0X0B]
F7
64 [0X40]
I
12 [0X0C]
F8
65 [0X41]
J
13 [0X0D]
F9
66 [0X42]
K
14 [0X0E]
F10
67 [0X43]
L
15 [0X0F]
F11
68 [0X44]
M
16 [0X10]
F12
69 [0X45]
N
17 [0X11]
PrintScr
70 [0X46]
O
18 [0X12]
Scroll
71 [0X47]
P
19 [0X13]
Pause
72 [0X48]
Q
20 [0X14]
Insert
73 [0X49]
R
21 [0X15]
Home
74 [0X4A]
S
22 [0X16]
PageUp
75 [0X4B]
T
23 [0X17]
Delete
76 [0X4C]
U
24 [0X18]
End
77 [0X4D]
V
25 [0X19]
PageDn
78 [0X4E]
W
26 [0X1A]
Right
79 [0X4F]
X
27 [0X1B]
Left
80 [0X50]
Y
28 [0X1C]
Down
81 [0X51]
Z
29 [0X1D]
Up
82 [0X52]
1
30 [0X1E]
NumLock
83 [0X53]
2
31 [0X1F]
KEYPAD /
84 [0X54]
3
32 [0X20]
KEYPAD *
85 [0X55]
4
33 [0X21]
KEYPAD -
86 [0X56]
5
34 [0X22]
KEYPAD +
87 [0X57]
6
35 [0X23]
KEYPAD ENTER
88 [0X58]
7
36 [0X24]
KEYPAD 1
89 [0X59]
8
37 [0X25]
KEYPAD 2
90 [0X5A]
9
38 [0X26]
KEYPAD 3
91 [0X5B]
0
39 [0X27]
KEYPAD 4
92 [0X5C]
Enter
40 [0X28]
KEYPAD 5
93 [0X5D]
ESC
41 [0X29]
KEYPAD 6
94 [0X5E]
BackSpace
42 [0X2A]
KEYPAD 7
95 [0X5F]
Tab
43 [0X2B]
KEYPAD 8
96 [0X60]
Space
44 [0X2C]
KEYPAD 9
97 [0X61]
-
45 [0X2D]
KEYPAD 0
98 [0X62]
=
46 [0X2E]
KEYPAD .
99 [0X63]
[
47 [0X2F]
——–
———
]
48 [0X30]
——–
———
\
49 [0X31]
LCtrl
224 [0XE0]
\
50 [0X32]
LShift
225 [0XE1]
;
51 [0X33]
LAlt
226 [0XE2]
‘
52 [0X34]
LWIN
227 [0XE3]
`
53 [0X35]
RCtrl
228 [0XE4]
,
54 [0X36]
RShift
229 [0XE5]
.
55 [0X37]
RAlt
230 [0XE6]
/
56 [0X38]
RWIN
231 [0XE7]