ARM板 usb gadget hid 模拟键鼠
说明
在rv1106平台上内核版本5.10上通过usb口接入电脑,被电脑识别成鼠标和键盘,模拟成键盘鼠标设备
一:修改内核配置
1:增加hid gadget,这里直接编进内核了,进入到内核源码树,make ARCH=arm menuconfig
Device Drivers--->USB support--->(*)USB Gadget Support--->USB Gadget precomposed configurations--->HID Gadget(*)
2:usb host修改为usb device
CONFIG_USB_DWC3=m
CONFIG_USB_DWC3_GADGET=y




二:修改设备树
usb修改为从机模式
/**********USB**********/
&usbdrd_dwc3 {
status = "okay";
dr_mode="peripheral";//"host";//dr_mode = "peripheral";
};
三:修改hid源码增加键鼠描述符及设备注册
/home/hfzuo/rock/luckfox-pico/sysdrv/source/kernel/drivers/usb/gadget/legacy/hid.c
/* hid descriptor for a keyboard */
static struct hidg_func_descriptor pcduino_keyboard_date = {
.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 */
}
};
static struct platform_device pcduino_hid_keyboard = {
.name = "hidg",
.id = 0,
.num_resources = 0,
.resource = 0,
.dev.platform_data = &pcduino_keyboard_date,
};
/* hid descriptor for a mouse */
static struct hidg_func_descriptor pcduino_mouse_data = {
.subclass = 0, //No SubClass
.protocol = 2, //Mouse
.report_length = 4,
.report_desc_length = 52,
.report_desc = {
0x05, 0x01, //Usage Page(Generic Desktop Controls)
0x09, 0x02, //Usage (Mouse)
0xa1, 0x01, //Collction (Application)
0x09, 0x01, //Usage (pointer)
0xa1, 0x00, //Collction (Physical)
0x05, 0x09, //Usage Page (Button)
0x19, 0x01, //Usage Minimum(1)
0x29, 0x05, //Usage Maximum(5)
0x15, 0x00, //Logical Minimum(1)
0x25, 0x01, //Logical Maximum(1)
0x95, 0x05, //Report Count(5)
0x75, 0x01, //Report Size(1)
0x81, 0x02, //Input(Data,Variable,Absolute,BitField)
0x95, 0x01, //Report Count(1)
0x75, 0x03, //Report Size(3)
0x81, 0x01, //Input(Constant,Array,Absolute,BitField)
0x05, 0x01, //Usage Page(Generic Desktop Controls)
0x09, 0x30, //Usage(x)
0x09, 0x31, //Usage(y)
0x09, 0x38, //Usage(Wheel)
0x15, 0x81, //Logical Minimum(-127)
0x25, 0x7F, //Logical Maximum(127)
0x75, 0x08, //Report Size(8)
0x95, 0x03, //Report Count(3)
0x81, 0x06, //Input(Data,Variable,Relative,BitField)
0xc0, //End Collection
0xc0 //End Collection
}
};
static struct platform_device pcduino_hid_mouse = {
.name = "hidg",
.id = 1,
.num_resources = 0,
.resource = 0,
.dev.platform_data = &pcduino_mouse_data,
};
static int __init hidg_init(void)
{
int status;
status = platform_device_register(&pcduino_hid_keyboard);
if (status < 0) {
printk("hid keyboard reg failed\n");
platform_device_unregister(&pcduino_hid_keyboard);
return status;
}
status = platform_device_register(&pcduino_hid_mouse);
if (status < 0) {
printk("hid mouse reg failed\n");
platform_device_unregister(&pcduino_hid_mouse);
return status;
}
status = platform_driver_probe(&hidg_plat_driver,
hidg_plat_driver_probe);
if (status < 0)
return status;
status = usb_composite_probe(&hidg_driver);
if (status < 0)
platform_driver_unregister(&hidg_plat_driver);
return status;
}
module_init(hidg_init);
static void __exit hidg_cleanup(void)
{
platform_device_unregister(&pcduino_hid_keyboard);
platform_device_unregister(&pcduino_hid_mouse);
usb_composite_unregister(&hidg_driver);
platform_driver_unregister(&hidg_plat_driver);
}
四:加载驱动
insmod dwc3.ko insmod dwc3-of-simple.ko,如果没问题就应该造 /sys/class/udc 下能找到udc
五:应用代码测试,模拟键鼠想电脑发送数据
/* hid_gadget_test */
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_LEN 512
struct options
{
const char *opt;
unsigned char val;
};
static struct options kmod[] = {
{.opt = "-left - ctrl", .val = 0x01},
{.opt = "-right - ctrl", .val = 0x10},
{.opt = "-left - shift", .val = 0x02},
{.opt = "-right - shift", .val = 0x20},
{.opt = "-left - alt", .val = 0x04},
{.opt = "-right - alt", .val = 0x40},
{.opt = "-left - meta", .val = 0x08},
{.opt = "-right - meta", .val = 0x80},
{.opt = NULL}
};
static struct options kval[] = {
{.opt = "-return", .val = 0x28},
{.opt = "-esc", .val = 0x29},
{.opt = "-bckspc", .val = 0x2a},
{.opt = "-tab", .val = 0x2b},
{.opt = "-spacebar", .val = 0x2c},
{.opt = "-caps - lock", .val = 0x39},
{.opt = "-f1", .val = 0x3a},
{.opt = "-f2", .val = 0x3b},
{.opt = "-f3", .val = 0x3c},
{.opt = "-f4", .val = 0x3d},
{.opt = "-f5", .val = 0x3e},
{.opt = "-f6", .val = 0x3f},
{.opt = "-f7", .val = 0x40},
{.opt = "-f8", .val = 0x41},
{.opt = "-f9", .val = 0x42},
{.opt = "-f10", .val = 0x43},
{.opt = "-f11", .val = 0x44},
{.opt = "-f12", .val = 0x45},
{.opt = "-insert", .val = 0x49},
{.opt = "-home", .val = 0x4a},
{.opt = "-pageup", .val = 0x4b},
{.opt = "-del", .val = 0x4c},
{.opt = "-end", .val = 0x4d},
{.opt = "-pagedown", .val = 0x4e},
{.opt = "-right", .val = 0x4f},
{.opt = "-left", .val = 0x50},
{.opt = "-down", .val = 0x51},
{.opt = "-kp - enter", .val = 0x58},
{.opt = "-up", .val = 0x52},
{.opt = "-num - lock", .val = 0x53},
{.opt = NULL}};
int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
char *tok = strtok(buf, " ");
int key = 0;
int i = 0;
for (; tok != NULL; tok = strtok(NULL, " "))
{
if (strcmp(tok, "-quit") == 0)
return -1;
if (strcmp(tok, "-hold") == 0)
{
*hold = 1;
continue;
}
if (key < 6)
{
for (i = 0; kval[i].opt != NULL; i++)
if (strcmp(tok, kval[i].opt) == 0)
{
report[2 + key++] = kval[i].val;
break;
}
if (kval[i].opt != NULL)
continue;
}
if (key < 6)
if (islower(tok[0]))
{
report[2 + key++] = (tok[0] - ('a' - 0x04));
continue;
}
for (i = 0; kmod[i].opt != NULL; i++)
if (strcmp(tok, kmod[i].opt) == 0)
{
report[0] = report[0] | kmod[i].val;
break;
}
if (kmod[i].opt != NULL)
continue;
if (key < 6)
fprintf(stderr, "unknown option : % s\n", tok);
}
return 8;
}
static struct options mmod[] = {
{.opt = "-b1", .val = 0x01},
{.opt = "-b2", .val = 0x02},
{.opt = "-b3", .val = 0x04},
{.opt = NULL}
};
int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
char *tok = strtok(buf, " ");
int mvt = 0;
int i = 0;
for (; tok != NULL; tok = strtok(NULL, " "))
{
if (strcmp(tok, "-quit") == 0)
return -1;
if (strcmp(tok, "-hold") == 0)
{
*hold = 1;
continue;
}
for (i = 0; mmod[i].opt != NULL; i++)
if (strcmp(tok, mmod[i].opt) == 0)
{
report[0] = report[0] | mmod[i].val;
break;
}
if (mmod[i].opt != NULL)
continue;
if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2)
{
errno = 0;
report[1 + mvt++] = (char)strtol(tok, NULL, 0);
if (errno != 0)
{
fprintf(stderr, "Bad value :'% s'\n", tok);
report[1 + mvt--] = 0;
}
continue;
}
fprintf(stderr, "unknown option : % s\n", tok);
}
return 3;
}
static struct options jmod[] = {
{.opt = "-b1", .val = 0x10},
{.opt = "-b2", .val = 0x20},
{.opt = "-b3", .val = 0x40},
{.opt = "-b4", .val = 0x80},
{.opt = "-hat1", .val = 0x00},
{.opt = "-hat2", .val = 0x01},
{.opt = "-hat3", .val = 0x02},
{.opt = "-hat4", .val = 0x03},
{.opt = "-hatneutral", .val = 0x04},
{.opt = NULL}
};
int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
char *tok = strtok(buf, " ");
int mvt = 0;
int i = 0;
*hold = 1;
/* set default hat position: neutral */
report[3] = 0x04;
for (; tok != NULL; tok = strtok(NULL, " "))
{
if (strcmp(tok, "-quit") == 0)
return -1;
for (i = 0; jmod[i].opt != NULL; i++)
if (strcmp(tok, jmod[i].opt) == 0)
{
report[3] = (report[3] & 0xF0) | jmod[i].val;
break;
}
if (jmod[i].opt != NULL)
continue;
if (!(tok[0] == '-' && tok[1] == '-') && mvt < 3)
{
errno = 0;
report[mvt++] = (char)strtol(tok, NULL, 0);
if (errno != 0)
{
fprintf(stderr, "Bad value :'% s'\n", tok);
report[mvt--] = 0;
}
continue;
}
fprintf(stderr, "unknown option : % s\n", tok);
}
return 4;
}
void print_options(char c)
{
int i = 0;
if (c == 'k')
{
printf(" keyboard options :\n"
" -hold\n");
for (i = 0; kmod[i].opt != NULL; i++)
printf("\t\t % s\n", kmod[i].opt);
printf("\n keyboard values :\n"
" [a - z] or\n");
for (i = 0; kval[i].opt != NULL; i++)
printf("\t\t % -8s % s", kval[i].opt, i % 2 ? "\n" : "");
printf("\n");
}
else if (c == 'm')
{
printf(" mouse options :\n"
" -hold\n");
for (i = 0; mmod[i].opt != NULL; i++)
printf("\t\t % s\n", mmod[i].opt);
printf("\n mouse values :\n"
" Two signed numbers\n"
"-quit to close\n");
}
else
{
printf(" joystick options :\n");
for (i = 0; jmod[i].opt != NULL; i++)
printf("\t\t % s\n", jmod[i].opt);
printf("\n joystick values :\n"
" three signed numbers\n"
"-quit to close\n");
}
}
int main(int argc, const char *argv[])
{
const char *filename = NULL;
int fd = 0;
char buf[BUF_LEN];
int cmd_len;
char report[8];
int to_send = 8;
int hold = 0;
fd_set rfds;
int retval, i;
if (argc < 3)
{
fprintf(stderr, "Usage : % s devname mouse | keyboard | joystick\n",
argv[0]);
return 1;
}
if (argv[2][0] != 'k' && argv[2][0] != 'm' && argv[2][0] != 'j')
return 2;
filename = argv[1];
if ((fd = open(filename, O_RDWR, 0666)) == -1)
{
perror(filename);
return 3;
}
print_options(argv[2][0]);
while (42)
{
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
FD_SET(fd, &rfds);
retval = select(fd + 1, &rfds, NULL, NULL, NULL);
if (retval == -1 && errno == EINTR)
continue;
if (retval < 0)
{
perror("select()");
return 4;
}
if (FD_ISSET(fd, &rfds))
{
cmd_len = read(fd, buf, BUF_LEN - 1);
printf("recv report :");
for (i = 0; i < cmd_len; i++)
printf(" % 02x", buf[i]);
printf("\n");
}
if (FD_ISSET(STDIN_FILENO, &rfds))
{
memset(report, 0x0, sizeof(report));
cmd_len = read(STDIN_FILENO, buf, BUF_LEN - 1);
if (cmd_len == 0)
break;
buf[cmd_len - 1] = '\0';
hold = 0;
memset(report, 0x0, sizeof(report));
if (argv[2][0] == 'k')
to_send = keyboard_fill_report(report, buf, &hold);
else if (argv[2][0] == 'm')
to_send = mouse_fill_report(report, buf, &hold);
else
to_send = joystick_fill_report(report, buf, &hold);
if (to_send == -1)
break;
for(i=0;i<to_send;i++)
{
printf("data[%d]=%x\r\n",i,report[i]);
}
if (write(fd, report, to_send) != to_send)
{
perror(filename);
return 5;
}
if (!hold)
{
memset(report, 0x0, sizeof(report));
if (write(fd, report, to_send) != to_send)
{
perror(filename);
return 6;
}
}
}
}
close(fd);
return 0;
}
./gadget_hid /dev/hidg1 k 终端按提示输入 a b 即可看到键盘有输入a b
./gadget_hid /dev/hidg2 j -b3 -10 100 即可看到鼠标移动
五:键鼠数据分析
鼠标发送给 PC 的数据每次 4 个字节
BYTE1 BYTE2 BYTE3 BYTE4
定义分别是:
BYTE1 –
|–bit7: 1 表示 Y 坐标的变化量超出-256 ~ 255 的范围,0 表示没有溢出
|–bit6: 1 表示 X 坐标的变化量超出-256 ~ 255 的范围,0 表示没有溢出
|–bit5: Y 坐标变化的符号位,1 表示负数,即鼠标向下移动
|–bit4: X 坐标变化的符号位,1 表示负数,即鼠标向左移动
|–bit3: 恒为 1
|–bit2: 1 表示中键按下
|–bit1: 1 表示右键按下
|–bit0: 1 表示左键按下
BYTE2 — X 坐标变化量,与 byte 的 bit4 组成 9 位符号数,负数表示向左移,正数表右移。用补码表示变化量
BYTE3 — Y 坐标变化量,与 byte 的 bit5 组成 9 位符号数,负数表示向下移,正数表上移。用补码表示变化量
BYTE4 — 滚轮变化。
由于手上没有 USB 鼠标,对 BYTE1 的 4-7 位没有测试,对于 BYTE2 BYTE3 做个测试,BYTE1 的 4-7 全为 0 的时候,BYTE2 BYTE3 的正负表示鼠标移动方向
键盘发送给 PC 的数据每次 8 个字节
BYTE1 BYTE2 BYTE3 BYTE4 BYTE5 BYTE6 BYTE7 BYTE8
定义分别是:
BYTE1 –
|–bit0: Left Control 是否按下,按下为 1
|–bit1: Left Shift 是否按下,按下为 1
|–bit2: Left Alt 是否按下,按下为 1
|–bit3: Left GUI 是否按下,按下为 1
|–bit4: Right Control 是否按下,按下为 1
|–bit5: Right Shift 是否按下,按下为 1
|–bit6: Right Alt 是否按下,按下为 1
|–bit7: Right GUI 是否按下,按下为 1
BYTE2 — 暂不清楚,有的地方说是保留位
BYTE3–BYTE8 — 这六个为普通按键
键盘经过测试。
例如:键盘发送一帧数据 02 00 0×04 0×05 00 00 00 00
表示同时按下了 Left Shift + ‘a’+‘b’三个键
附件里面是 usb 协议中文版,喜欢折腾的可以看看。USB1.1 协议中文版
