1、Linux USB HID gadget 驱动程序
(1)介绍
Linux HID gadget 驱动程序提供一种模拟 USB 人机接口设备(HID)的方式。基本的 HID 处理是在内核中完成的,并且 HID 报告可以通过字符设备 /dev/hidgX 来进行发送和接收。
有关 HID 的更多详细信息,请参阅 http://www.usb.org/developers/hidpage/
(2)配置
g_hid 是一个平台驱动程序,因此要使用它,需要添加结构 platform_device 到的内核平台代码,同时定义想要使用的 HID 函数描述符——比如类似这样:
#include <linux/platform_device.h>
#include <linux/usb/g_hid.h>
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 */
}
};
static struct platform_device my_hid = {
.name = "hidg",
.id = 0,
.num_resources = 0,
.resource = 0,
.dev.platform_data = &my_hid_data,
};
2、如何实现 mtk 平板电脑模拟 HID 鼠标
(1)在 device/mediatek/mt6771/init.mt6771.usb.rc 中添加脚本,实现 USB gadget 切换到 HID:
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=hid && \
property:vendor.usb.acm_enable=0 && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "HID"
write /config/usb_gadget/g1/idProduct 0x20FF
write /config/usb_gadget/g1/functions/hid.gs0/subclass 0
write /config/usb_gadget/g1/functions/hid.gs0/protocol 2
symlink /config/usb_gadget/g1/functions/hid.gs0 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${vendor.usb.controller}
setprop sys.usb.state ${sys.usb.config}
(2)在 kernel-4.4/drivers/usb/gadget/function/f_hid.c 定义鼠标 HID 描述符:
static struct hidg_func_descriptor hid_data = {
.subclass = 0, // No subclass
.protocol = 2, // Mouse
.report_length = 5,
.report_desc_length = 74,
.report_desc = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
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 (0x01)
0x29, 0x05, // Usage Maximum (0x05)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x31, // Usage (Y)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
}
};
(3)在平板电脑的 touchscreen 驱动里通过字符设备节点 /dev/hidg0 发送触摸消息(包括状态和坐标值)到 USB 主机:
int dev_exist(char *dev)
{
struct file *fp = NULL;
fp = filp_open(dev, O_RDWR, 0664);
if (IS_ERR(fp))
{
return -1;
}
return 0;
}
static int write_msg(const char* dev, char *buff, int size)
{
mm_segment_t fs;
loff_t pos;
int ret;
if(fp == NULL) {
fp = filp_open(dev, O_RDWR, 0664);
if (IS_ERR(fp))
{
filp_close(fp, NULL);
fp = NULL;
return -1;
}
}
fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
ret = vfs_write(fp, buff, size, &pos);
set_fs(fs);
return 0;
}
void move_to(int state, int x, int y)
{
char buf[5]={0,0,0,0,0};
buf[0] = state & 0xFF;
buf[1] = x & 0xFF;
buf[2] = (x >> 8) & 0xFF;
buf[3] = y & 0xFF;
buf[4] = (y >> 8) & 0xFF;
// 发送触摸消息(包括状态和坐标值)
write_msg("/dev/hidg0", buf , 5);
}
// touchscreen 上报触摸消息的工作队列处理函数
static void ts_work_func(struct work_struct *work) {
... // 省略部分代码
// 检查是否存在 HID 字符设备节点
if(dev_exist("/dev/hidg0") == 0) {
move_to(state, input_x, input_y);
}
}