1. 环境
linux内核版本
1Linux (none) 3.10.14 #4 PREEMPT Wed May 2 16:42:10 CST 2018 mips GNU/Linux
2. 使用linux下的gadget来实现模拟hid键盘输入
修改linux内核配置选项开启”HID Gadget”功能
1
2
3
4
5
6
7root@dong-VirtualBox:~/opt/ingenic-linux-kernel3.10.14# make menuconfig
Device Drivers --->
[*] USB support --->
USB Gadget Support --->
USB Gadget Drivers (HID Gadget) --->
(X) HID Gadget
3. 修改linux内核代码 “kernel/drivers/usb/gadget/hid.c”
在文件中增加以下代码后,重新编译内核,将内核镜像下载到设备中,在/dev目录下我们会看到设备节点”/dev/hidg0”,这个新的设备节点名就是我们刚刚加入的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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76//add
/* hid descriptor for a keyboard */
static struct hidg_func_descriptor my_hid_data = {
.subclass = 0, /* No subclass */
.protocol = 1, /* Keyboard */
.report_length = 8,
.report_desc_length = 63,
.report_desc = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x06, /* USAGE (Keyboard) */
0xa1, 0x01, /* COLLECTION (Application) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */
0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x08, /* REPORT_COUNT (8) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
0x95, 0x05, /* REPORT_COUNT (5) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */
0x29, 0x05, /* USAGE_MAXIMUM (Kana) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x03, /* REPORT_SIZE (3) */
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
0x95, 0x06, /* REPORT_COUNT (6) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x65, /* LOGICAL_MAXIMUM (101) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0x00, /* USAGE_MINIMUM (Reserved) */
0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
0xc0 /* END_COLLECTION */
}
};
...
/****************************** Some noise ******************************/
//add
static struct platform_device my_hid = {
.name = "hidg",
.id = 0,
.num_resources = 0,
.resource = 0,
.dev.platform_data = &my_hid_data,
};
......
static int __init hidg_init(void)
{
//add
status = platform_device_register(&my_hid);
if (status < 0)
{
platform_driver_unregister(&my_hid);
return status;
}
...
return status;
}
static void __exit hidg_cleanup(void)
{
//add
platform_device_unregister(&my_hid);
...
}
4. Demo1
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#include "virtual_keyboard.h"
#include
int main(int argc, const char *argv[])
{
int ret = 0;
unsigned char keyboardChar[]= "1234567890abcdefghijklmnopqrstuvwsyzABCDEFGHIJKLMNOPQRSTUVWSYZ"
"`-=[]\\;',./~!@#$%^&*()_+{}|:\"<>?\n";
ret = virtual_keyboard_open("/dev/hidg0");
if(ret < 0)
{
return -1;
}
while(1)
{
ret = virtual_keyboard_write(keyboardChar, 95);
if(ret < 0)
{
break;
}
sleep(5);
}
virtual_keyboard_close();
return 0;
}
5. 完整源码: 点击下载
下载后hid.c文件可直接替换内核源码中”kernel/drivers/usb/gadget/hid.c”文件。
未处理的hid setup指令:
在上面步骤修改完成后,把设备插入到usb接口,在PC的设备管理器就可以识别出来人体输入学设备,基本上我们可以使用了。 但是我们为使设备在不同系统下使用均无问题。我们使用Bus Hound 6.01软件观察设备上电枚举数据,发现有”USTS c0000004”问题,使用USB分析仪跟踪USB上电枚举数据发现usb setupt包中Class request Out(0x0A)指令未处理。
分析
Bus Hound数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Device Length Phase Data Description
------ -------- ----- -------------------------------------------------- ----------------
18.0 CTL 80 06 00 01 00 00 12 00 GET DESCRIPTOR
18.0 18 IN 12 01 00 02 00 00 00 40 c1 22 02 0e 10 03 01 02 .......@."......
00 01 ..
18.0 CTL 80 06 00 02 00 00 09 00 GET DESCRIPTOR
18.0 9 IN 09 02 2c 00 01 01 00 e0 01 ..,......
18.0 CTL 80 06 00 02 00 00 2c 00 GET DESCRIPTOR
18.0 44 IN 09 02 2c 00 01 01 00 e0 01 03 09 03 09 04 00 00 ..,.............
02 03 00 01 04 09 21 01 01 00 01 22 3f 00 07 05 ......!...."?...
81 03 08 00 04 07 05 01 03 08 00 04 ............
18.0 CTL 00 09 01 00 00 00 00 00 SET CONFIG
18.0 CTL 21 0a 00 00 00 00 00 00 SET IDLE
18.0 USTS c0000004 stall pid
18.0 CTL 81 06 00 22 00 00 7f 00 GET DESCRIPTOR
18.0 63 IN 05 01 09 06 a1 01 05 07 19 e0 29 e7 15 00 25 01 ..........)...%.
75 01 95 08 81 02 95 01 75 08 81 03 95 05 75 01 u.......u.....u.
05 08 19 01 29 05 91 02 95 01 75 03 91 03 95 06 ....).....u.....
75 08 15 00 25 65 05 07 19 00 29 65 81 00 c0 u...%e....)e...
18.1 1 OUT 00 .
USB分析仪数据:
Class request Out (0x0A)
主控制器发出包:21 0A 00 00 00 00 00 00
从设备应答包:No data
Setup request:
Name
Value
Dec
Hex
bmRequestType.Recipient
Interface
1
0x01
bmRequestType.Type
Class
1
0x1
bmRequestType.Direction
Host-to-device
0
0x0
bRequest
Class: 0x0A (Findout moren online)
10
0x0A
wValue
0x0000
0
0x0000
wIndex
0x0000
0
0x0000
wLength
0
0
0x0000
解决
经过分析发现设备在上电枚举过程中的setup包处理有问题,没有处理”Class request Out (0x0A)”指令。既然问题找到了,那么接下来就好办了。修改linux内核文件”kernel/drivers/usb/gadget/f_hid.c”
在最后的default分支前加入以下代码:
1
2
3
4
5
6
7
8//add
case((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| USB_REQ_GET_INTERFACE):
VDBG(cdev, "get_interface\n");
length = min_t(unsigned short, length, hidg->report_length);
memset(req->buf, 0x0, length);
goto respond;
break;
代码结构如下:
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
42static int hidg_setup(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
...
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
...
case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
| USB_REQ_GET_DESCRIPTOR):
switch (value >> 8) {
...
default:
VDBG(cdev, "Unknown descriptor request 0x%x\n",
value >> 8);
goto stall;
break;
}
break;
//add
case((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| USB_REQ_GET_INTERFACE):
VDBG(cdev, "get_interface\n");
length = min_t(unsigned short, length, hidg->report_length);
memset(req->buf, 0x0, length);
goto respond;
break;
default:
VDBG(cdev, "Unknown request 0x%x\n",
ctrl->bRequest);
goto stall;
break;
}
...
}