linux 在应用层创建虚拟hid设备

linux 在应用层创建虚拟hid设备

主要功能
  应用层发送鼠标数据给驱动,驱动会解析完,传到input子系统,这样,其他的应用程序就能通用了。
具体步骤
  应用层通过调用uhid驱动,来让uhid驱动创建一个虚拟的hid设备,然后,应用层向uhid发送hid报告描述符,用于生成具体的hid设备。例如,报告描述符包含鼠标描述符的话,就会创建一个input设备,报告描述符包含鼠标和键盘描述符的话,就会创建两个input设备,分别代表鼠标和键盘。接着,应用层发送hid报告数据给uhid驱动,虚拟hid解析完,就会将数据转发到input子系统。

使用步骤

  1. 使能uhid驱动
    在内核配置文件中加入:CONFIG_UHID=y。该驱动源文件的具体路径为drivers/hid/uhid.c。
    成功并烧写到系统的话,在/dev/目录下,会多出一个uhid文件。
  2. 编写应用程序
    下面的代码,会创建一个鼠标,一个键盘设备,并且向hid发送鼠标数据,input子系统会报告事件的产生。

【uhid_control.h】

#pragma oncestruct MT_event {int state;int x; // 0-7808int y; // 0-4480
};struct Keyboard_Ext {bool LeftControl;bool LeftShift;bool LeftAlt;bool Left_GUI;bool RightControl;bool RightShift;bool RightAlt;bool Right_GUI;
};
void uhid_create(char *descriptor, int size);
void uhid_create_default(bool mouse_enable, bool keyboard_enable);
void uhid_send_report(const char *data, int size);
void uhid_send_default_mouse(bool left_btn, bool right_btn, bool middle_btn, int x, int y, int wheel);
void uhid_send_default_keyboard(struct Keyboard_Ext ext, int key[6]);
void uhid_close(void);
void uhid_test(void);void multi_touch_create(void);
void multi_touch_set(struct MT_event *event, int count);
void multi_touch_close(void);
void multi_touch_test(void);

【uhid_control.cpp】

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "uhid_control.h"static int uhid_fd;
void uhid_create(char *descriptor, int size)
{if (access("/dev/uhid", F_OK) != 0)return;uhid_fd = open("/dev/uhid", O_RDWR);if (uhid_fd > 0){int ret;struct uhid_event event;event.type = UHID_CREATE2;strcpy((char*)event.u.create2.name, "uibc");strcpy((char*)event.u.create2.phys, "uibc_phys");strcpy((char*)event.u.create2.uniq, "uibc_uniq");event.u.create2.vendor = 0x1233;event.u.create2.product = 0x3455;event.u.create2.version = 0x5677;event.u.create2.rd_size = size;memcpy(event.u.create2.rd_data, descriptor, size);ret = write(uhid_fd, &event, sizeof(event));usleep(10000);}
}
void uhid_create_default(bool mouse_enable, bool keyboard_enable)
{int size = 0;char descriptor[4096];// 描述符在线分析: https://www.usbzh.com/tool/usb.html// hid附录 E.10char standard_mouse_descriptor[] = "\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\x01\x05\x01\x09\x30\x09\x31\x15\x81\x25\x7f" \"\x75\x08\x95\x02\x81\x06\xc0\xc0";// hid附录 E.6char standard_keyboard_descriptor[] = "\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\x01\x95\x05\x75\x01\x05\x08\x19\x01\x29\x05\x91\x02\x95\x01" \"\x75\x03\x91\x01\x95\x06\x75\x08\x15\x00\x25\x65\x05\x07\x19\x00" \"\x29\x65\x81\x00\xc0";// 带report_id的鼠标描述符char mouse_descriptor[] = "\x05\x01\x09\x02\xa1\x01\x85" \"\x28\x09\x01\xa1\x00\x05\x09\x19\x01\x29\x08\x15\x00\x25\x01\x95" \"\x08\x75\x01\x81\x02\x05\x01\x09\x30\x09\x31\x09\x38\x0a\x38\x02" \"\x15\x81\x25\x7f\x75\x08\x95\x04\x81\x06\xc0\xc0";// 带report_id的键盘描述符char keyboard_descriptor[] = "\x05\x01\x09\x06\xa1\x01\x85" \"\x29\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";// standard_mouse_descriptor 和 standard_keyboard_descriptor 不可同时使用,// 因为他们没有Report ID,无法区别输入数据是属于鼠标还是键盘if (mouse_enable){memcpy(&descriptor[size], mouse_descriptor, sizeof(mouse_descriptor) - 1);size += sizeof(mouse_descriptor) - 1;}if (keyboard_enable){memcpy(&descriptor[size], keyboard_descriptor, sizeof(keyboard_descriptor) - 1);size += sizeof(keyboard_descriptor) - 1;}if (size > 0)uhid_create(descriptor, size);
}
void uhid_send_report(const char *data, int size)
{int ret;struct uhid_event event;if (uhid_fd > 0 && size <= 4096){memset(&event, 0, sizeof(event));event.type = UHID_INPUT2;event.u.input2.size = size;memcpy(event.u.input2.data, data, size);ret = write(uhid_fd, &event, sizeof(event));}
}
void uhid_send_default_mouse(bool left_btn, bool right_btn, bool middle_btn, int x, int y, int wheel)
{char report[5] = { 0x28 };report[1] += left_btn ? 0x01 : 0;report[1] += right_btn ? 0x02 : 0;report[1] += middle_btn ? 0x04 : 0;report[2] = x;report[3] = y;report[4] = wheel;uhid_send_report(report, 5);
}
void uhid_send_default_keyboard(struct Keyboard_Ext ext, int key[6])
{char report[10] = { 0x29 };report[1] += ext.LeftControl? 0x01 : 0;report[1] += ext.LeftShift	? 0x02 : 0;report[1] += ext.LeftAlt	? 0x04 : 0;report[1] += ext.Left_GUI	? 0x08 : 0;report[1] += ext.RightControl?0x10 : 0;report[1] += ext.RightShift ? 0x20 : 0;report[1] += ext.RightAlt	? 0x40 : 0;report[1] += ext.Right_GUI	? 0x80 : 0;report[4] = key[0];report[5] = key[1];report[6] = key[2];report[7] = key[3];report[8] = key[4];report[9] = key[5];uhid_send_report(report, 10);
}
void uhid_close(void)
{if (uhid_fd > 0)close(uhid_fd);uhid_fd = 0;
}
void uhid_test(void)
{struct Keyboard_Ext ext = {};int key[6] = {};// 创建虚拟鼠标键盘设备uhid_create_default(true, true);// 打开input子系统调试工具system("evtest /dev/input/event4&");	// 鼠标input设备,具体数字需根据实际进行修改system("evtest /dev/input/event5&");	// 键盘input设备,具体数字需根据实际进行修改usleep(500000);// 按下鼠标左键uhid_send_default_mouse(1, 0, 0, 10, 20, 30);usleep(100000);// 松开鼠标左键uhid_send_default_mouse(0, 0, 0, 0, 0, 0);usleep(100000);printf("\n\n\n");// 按下键盘上的aext.LeftControl = 1;key[0] = 4;	// 具体的键值,需查询hid usage规范pdfuhid_send_default_keyboard(ext, key);usleep(100000);// 松开键盘上的aext.LeftControl = 0;key[0] = 0;uhid_send_default_keyboard(ext, key);usleep(100000);uhid_close();system("killall evtest");
}static int mt_fd;
static struct timeval time_start;
static struct MT_event mt_event[10];
void multi_touch_create(void)
{if (access("/dev/uhid", F_OK) != 0)return;mt_fd = open("/dev/uhid", O_RDWR);if (mt_fd > 0){int ret;struct uhid_event event;event.type = UHID_CREATE2;strcpy((char*)event.u.create2.name, "uibc_mt");strcpy((char*)event.u.create2.phys, "uibc_mt_phys");strcpy((char*)event.u.create2.uniq, "uibc_mt_uniq");event.u.create2.vendor = 0x1230;event.u.create2.product = 0x3450;event.u.create2.version = 0x5670;// 十点触控描述符char multi_touch_descriptor[] ="\x05\x0D\x09\x04\xA1\x01\x85\x04\x09\x22\xA1\x02\x05\x0D\x95\x01" \"\x75\x06\x09\x51\x15\x00\x25\x3F\x81\x02\x09\x42\x25\x01\x75\x01" \"\x95\x01\x81\x02\x75\x01\x95\x01\x81\x03\x05\x01\x75\x10\x55\x0E" \"\x65\x11\x09\x30\x26\x80\x1E\x35\x00\x46\x75\x0D\x81\x42\x09\x31" \"\x26\x80\x11\x46\x8A\x07\x81\x42\xC0\x05\x0D\x09\x22\xA1\x02\x05" \"\x0D\x95\x01\x75\x06\x09\x51\x15\x00\x25\x3F\x81\x02\x09\x42\x25" \"\x01\x75\x01\x95\x01\x81\x02\x75\x01\x95\x01\x81\x03\x05\x01\x75" \"\x10\x55\x0E\x65\x11\x09\x30\x26\x80\x1E\x35\x00\x46\x75\x0D\x81" \"\x42\x09\x31\x26\x80\x11\x46\x8A\x07\x81\x42\xC0\x05\x0D\x09\x22" \"\xA1\x02\x05\x0D\x95\x01\x75\x06\x09\x51\x15\x00\x25\x3F\x81\x02" \"\x09\x42\x25\x01\x75\x01\x95\x01\x81\x02\x75\x01\x95\x01\x81\x03" \"\x05\x01\x75\x10\x55\x0E\x65\x11\x09\x30\x26\x80\x1E\x35\x00\x46" \"\x75\x0D\x81\x42\x09\x31\x26\x80\x11\x46\x8A\x07\x81\x42\xC0\x05" \"\x0D\x09\x22\xA1\x02\x05\x0D\x95\x01\x75\x06\x09\x51\x15\x00\x25" \"\x3F\x81\x02\x09\x42\x25\x01\x75\x01\x95\x01\x81\x02\x75\x01\x95" \"\x01\x81\x03\x05\x01\x75\x10\x55\x0E\x65\x11\x09\x30\x26\x80\x1E" \"\x35\x00\x46\x75\x0D\x81\x42\x09\x31\x26\x80\x11\x46\x8A\x07\x81" \"\x42\xC0\x05\x0D\x09\x22\xA1\x02\x05\x0D\x95\x01\x75\x06\x09\x51" \"\x15\x00\x25\x3F\x81\x02\x09\x42\x25\x01\x75\x01\x95\x01\x81\x02" \"\x75\x01\x95\x01\x81\x03\x05\x01\x75\x10\x55\x0E\x65\x11\x09\x30" \"\x26\x80\x1E\x35\x00\x46\x75\x0D\x81\x42\x09\x31\x26\x80\x11\x46" \"\x8A\x07\x81\x42\xC0\x05\x0D\x09\x22\xA1\x02\x05\x0D\x95\x01\x75" \"\x06\x09\x51\x15\x00\x25\x3F\x81\x02\x09\x42\x25\x01\x75\x01\x95" \"\x01\x81\x02\x75\x01\x95\x01\x81\x03\x05\x01\x75\x10\x55\x0E\x65" \"\x11\x09\x30\x26\x80\x1E\x35\x00\x46\x75\x0D\x81\x42\x09\x31\x26" \"\x80\x11\x46\x8A\x07\x81\x42\xC0\x05\x0D\x09\x22\xA1\x02\x05\x0D" \"\x95\x01\x75\x06\x09\x51\x15\x00\x25\x3F\x81\x02\x09\x42\x25\x01" \"\x75\x01\x95\x01\x81\x02\x75\x01\x95\x01\x81\x03\x05\x01\x75\x10" \"\x55\x0E\x65\x11\x09\x30\x26\x80\x1E\x35\x00\x46\x75\x0D\x81\x42" \"\x09\x31\x26\x80\x11\x46\x8A\x07\x81\x42\xC0\x05\x0D\x09\x22\xA1" \"\x02\x05\x0D\x95\x01\x75\x06\x09\x51\x15\x00\x25\x3F\x81\x02\x09" \"\x42\x25\x01\x75\x01\x95\x01\x81\x02\x75\x01\x95\x01\x81\x03\x05" \"\x01\x75\x10\x55\x0E\x65\x11\x09\x30\x26\x80\x1E\x35\x00\x46\x75" \"\x0D\x81\x42\x09\x31\x26\x80\x11\x46\x8A\x07\x81\x42\xC0\x05\x0D" \"\x09\x22\xA1\x02\x05\x0D\x95\x01\x75\x06\x09\x51\x15\x00\x25\x3F" \"\x81\x02\x09\x42\x25\x01\x75\x01\x95\x01\x81\x02\x75\x01\x95\x01" \"\x81\x03\x05\x01\x75\x10\x55\x0E\x65\x11\x09\x30\x26\x80\x1E\x35" \"\x00\x46\x75\x0D\x81\x42\x09\x31\x26\x80\x11\x46\x8A\x07\x81\x42" \"\xC0\x05\x0D\x09\x22\xA1\x02\x05\x0D\x95\x01\x75\x06\x09\x51\x15" \"\x00\x25\x3F\x81\x02\x09\x42\x25\x01\x75\x01\x95\x01\x81\x02\x75" \"\x01\x95\x01\x81\x03\x05\x01\x75\x10\x55\x0E\x65\x11\x09\x30\x26" \"\x80\x1E\x35\x00\x46\x75\x0D\x81\x42\x09\x31\x26\x80\x11\x46\x8A" \"\x07\x81\x42\xC0\x05\x0D\x09\x56\x55\x00\x65\x00\x27\xFF\xFF\xFF" \"\x7F\x95\x01\x75\x20\x81\x02\x09\x54\x25\x7F\x95\x01\x75\x08\x81" \"\x02\xC0";event.u.create2.rd_size = sizeof(multi_touch_descriptor) - 1;memcpy(event.u.create2.rd_data, multi_touch_descriptor, sizeof(multi_touch_descriptor) - 1);ret = write(mt_fd, &event, sizeof(event));usleep(10000);}
}
void multi_touch_set(struct MT_event *event, int count)
{int ret;long usec;char report[56] = { 0x04 };struct uhid_event uevent;for (int i = 0; i < 10 && i < count; i++){unsigned char s;s = event[i].state ? 0x40 : 00;report[i * 5 + 1] = i + s;report[i * 5 + 2] = event[i].x & 0xff;report[i * 5 + 3] = (event[i].x >> 8) & 0xff;report[i * 5 + 4] = event[i].y & 0xff;report[i * 5 + 5] = (event[i].y >> 8) & 0xff;}if (time_start.tv_sec == 0 && time_start.tv_usec == 0){gettimeofday(&time_start, NULL);usec = 1000000 * time_start.tv_sec + time_start.tv_usec;usec = 0;}else{struct timeval end;gettimeofday(&end, NULL);usec = 1000000 * (end.tv_sec - time_start.tv_sec) + (end.tv_usec - time_start.tv_usec);}usec = usec / 100;report[51] = usec & 0xff;report[52] = (usec >> 8) & 0xff;report[53] = (usec >> 16) & 0xff;report[54] = (usec >> 24) & 0xff;report[55] = count;memset(&uevent, 0, sizeof(event));uevent.type = UHID_INPUT2;uevent.u.input2.size = 56;memcpy(uevent.u.input2.data, report, 56);ret = write(mt_fd, &uevent, sizeof(uevent));
}
void multi_touch_close(void)
{if (mt_fd > 0)close(mt_fd);mt_fd = 0;
}
static void multi_touch_replay(void)
{char feature[] ="\x06\x0A\xFC\x28\xFE\x84\x40\xCB\x9A\x87\x0D\xBE\x57\x3C\xB6\x70" \"\x09\x88\x07\x97\x2D\x2B\xE3\x38\x34\xB6\x6C\xED\xB0\xF7\xE5\x9C" \"\xF6\xC2\x2E\x84\x1B\xE8\xB4\x51\x78\x43\x1F\x28\x4B\x7C\x2D\x53" \"\xAF\xFC\x47\x70\x1B\x59\x6F\x74\x43\xC4\xF3\x47\x18\x53\x1A\xA2" \"\xA1\x71\xC7\x95\x0E\x31\x55\x21\xD3\xB5\x1E\xE9\x0C\xBA\xEC\xB8" \"\x89\x19\x3E\xB3\xAF\x75\x81\x9D\x53\xB9\x41\x57\xF4\x6D\x39\x25" \"\x29\x7C\x87\xD9\xB4\x98\x45\x7D\xA7\x26\x9C\x65\x3B\x85\x68\x89" \"\xD7\x3B\xBD\xFF\x14\x67\xF2\x2B\xF0\x2A\x41\x54\xF0\xFD\x2C\x66" \"\x7C\xF8\xC0\x8F\x33\x13\x03\xF1\xD3\xC1\x0B\x89\xD9\x1B\x62\xCD" \"\x51\xB7\x80\xB8\xAF\x3A\x10\xC1\x8A\x5B\xE8\x8A\x56\xF0\x8C\xAA" \"\xFA\x35\xE9\x42\xC4\xD8\x55\xC3\x38\xCC\x2B\x53\x5C\x69\x52\xD5" \"\xC8\x73\x02\x38\x7C\x73\xB6\x41\xE7\xFF\x05\xD8\x2B\x79\x9A\xE2" \"\x34\x60\x8F\xA3\x32\x1F\x09\x78\x62\xBC\x80\xE3\x0F\xBD\x65\x20" \"\x08\x13\xC1\xE2\xEE\x53\x2D\x86\x7E\xA7\x5A\xC5\xD3\x7D\x98\xBE" \"\x31\x48\x1F\xFB\xDA\xAF\xA2\xA8\x6A\x89\xD6\xBF\xF2\xD3\x32\x2A" \"\x9A\xE4\xCF\x17\xB7\xB8\xF4\xE1\x33\x08\x24\x8B\xC4\x43\xA5\xE5" \"\x24\xC2";// 单个手指触摸按下char a1[] ="\x04\x40\xAC\x15\x0B\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\xD0\x5C\x00\x00\x01";// 单个手指触摸移动到另一个位置char a2[] ="\x04\x40\xAC\x15\x0B\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\x88\x5E\x00\x00\x01";// 单个手指松开char a3[] ="\x04\x00\xAC\x15\x0B\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \"\x00\x00\x00\xE0\x5E\x00\x00\x01";multi_touch_create();system("evtest /dev/input/event4&");usleep(500000);	{int ret;struct uhid_event event;sleep(1);printf("\n\n");memset(&event, 0, sizeof(event));event.type = UHID_INPUT2;event.u.input2.size = sizeof(a1) - 1;memcpy(event.u.input2.data, a1, sizeof(a1) - 1);ret = write(mt_fd, &event, sizeof(event));printf("\n[write ret = %d]\n", ret);}{int ret;struct uhid_event event;sleep(1);printf("\n\n");memset(&event, 0, sizeof(event));event.type = UHID_INPUT2;event.u.input2.size = sizeof(a2) - 1;memcpy(event.u.input2.data, a2, sizeof(a2) - 1);ret = write(mt_fd, &event, sizeof(event));printf("\n[write ret = %d]\n", ret);}{int ret;struct uhid_event event;sleep(1);printf("\n\n");memset(&event, 0, sizeof(event));event.type = UHID_INPUT2;event.u.input2.size = sizeof(a3) - 1;memcpy(event.u.input2.data, a3, sizeof(a3) - 1);ret = write(mt_fd, &event, sizeof(event));printf("\n[write ret = %d]\n", ret);}multi_touch_close();system("killall evtest");
}
void multi_touch_test(void)
{multi_touch_create();system("evtest /dev/input/event4&");usleep(500000);memset(mt_event, 0, sizeof(mt_event));mt_event[0].state = 1;mt_event[0].x = 160;mt_event[0].y = 160;multi_touch_set(mt_event, 1);sleep(1);printf("\n\n");memset(mt_event, 0, sizeof(mt_event));mt_event[0].state = 1;mt_event[0].x = 7900;multi_touch_set(mt_event, 1);sleep(1);printf("\n\n");memset(mt_event, 0, sizeof(mt_event));mt_event[0].state = 1;mt_event[0].x = 480;multi_touch_set(mt_event, 1);sleep(1);printf("\n\n");memset(mt_event, 0, sizeof(mt_event));mt_event[0].state = 0;mt_event[0].x = 640;multi_touch_set(mt_event, 1);sleep(1);multi_touch_close();system("killall evtest");
}int main(int argc, char **argv)
{if(1){// 创建鼠标键盘uhid_create_default(true, true);// 延迟999s,用户这时可以用cat /proc/bus/input/devices查看,是否多出两个input设备sleep(999);// 删除鼠标键盘uhid_close();}else{// 创建并测试鼠标键盘uhid_test();}return 0;
}

测试结果

  1.执行cat /proc/bus/input/devices会发现多了这两个input设备 。

在这里插入图片描述

  2.创建并测试鼠标键盘时,应用程序会发送鼠标和键盘数据给hid,我们会发现input有数据上报。 图片中上半部分为鼠标事件,下半部分为键盘事件。
在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部