使用uinput创建虚拟设备节点,生成虚拟设备。

开始之前首先要了解查看Linux下USB设备的命令:

root@ubuntu:~# cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=10000000000000 0I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input1
U: Uniq=
H: Handlers=sysrq kbd event1 leds
B: PROP=0
B: EV=120013
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=7I: Bus=0011 Vendor=0002 Product=0013 Version=0006
N: Name="VirtualPS/2 VMware VMMouse"
P: Phys=isa0060/serio1/input1
S: Sysfs=/devices/platform/i8042/serio1/input/input4
U: Uniq=
H: Handlers=mouse0 event2
B: PROP=0
B: EV=b
B: KEY=70000 0 0 0 0
B: ABS=3I: Bus=0011 Vendor=0002 Product=0013 Version=0006
N: Name="VirtualPS/2 VMware VMMouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input3
U: Uniq=
H: Handlers=mouse1 event3
B: PROP=1
B: EV=7
B: KEY=30000 0 0 0 0
B: REL=103I: Bus=0003 Vendor=0e0f Product=0003 Version=0110
N: Name="VMware VMware Virtual USB Mouse"
P: Phys=usb-0000:02:00.0-1/input0
S: Sysfs=/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0/0003:0E0F:0003.0001/input/input5
U: Uniq=
H: Handlers=mouse2 event4
B: PROP=0
B: EV=17
B: KEY=ff0000 0 0 0 0
B: REL=903
B: MSC=10I: Bus=0003 Vendor=093a Product=2510 Version=0001
N: Name="Virtual Mouse"
P: Phys=mouse41547/41547
S: Sysfs=/devices/virtual/input/input47
U: Uniq=
H: Handlers=mouse3 event5
B: PROP=0
B: EV=17
B: KEY=ff0000 0 0 0 0
B: REL=903
B: MSC=10

在我的虚拟机下运行结果是这样的;event0-event4这5个设备节点是这样的(虽然我也不太清楚为什么会默认生成这5个节点),event5是我的程序生成的一个虚拟鼠标的节点。

使用libinput record命令输入对应的节点可以查看到当前设备存在哪些键值:

root@ubuntu:~# libinput record
Available devices:
/dev/input/event0:  Power Button
/dev/input/event1:  AT Translated Set 2 keyboard
/dev/input/event2:  VirtualPS/2 VMware VMMouse
/dev/input/event3:  VirtualPS/2 VMware VMMouse
/dev/input/event4:  VMware VMware Virtual USB Mouse
/dev/input/event5:  Virtual Mouse
Select the device event number: 5
Recording to 'stdout'.
version: 1
ndevices: 1
libinput:version: "1.15.5"git: "unknown"
system:os: "ubuntu:20.04"kernel: "5.11.0-40-generic"dmi: "dmi:bvnPhoenixTechnologiesLTD:bvr6.00:bd11/12/2020:br4.6:efr0.0:svnVMware,Inc.:pnVMwareVirtualPlatform:pvrNone:sku:rvnIntelCorporation:rn440BXDesktopReferencePlatform:rvrNone:cvnNoEnclosure:ct1:cvrN/A:"
devices:
- node: /dev/input/event5evdev:# Name: Virtual Mouse# ID: bus 0x3 vendor 0x93a product 0x2510 version 0x1# Supported Events:# Event type 0 (EV_SYN)# Event type 1 (EV_KEY)#   Event code 272 (BTN_LEFT)#   Event code 273 (BTN_RIGHT)#   Event code 274 (BTN_MIDDLE)#   Event code 275 (BTN_SIDE)#   Event code 276 (BTN_EXTRA)#   Event code 277 (BTN_FORWARD)#   Event code 278 (BTN_BACK)#   Event code 279 (BTN_TASK)# Event type 2 (EV_REL)#   Event code 0 (REL_X)#   Event code 1 (REL_Y)#   Event code 8 (REL_WHEEL)#   Event code 11 (REL_WHEEL_HI_RES)# Event type 4 (EV_MSC)#   Event code 4 (MSC_SCAN)# Properties:name: "Virtual Mouse"id: [3, 2362, 9488, 1]codes:0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] # EV_SYN1: [272, 273, 274, 275, 276, 277, 278, 279] # EV_KEY2: [0, 1, 8, 11] # EV_REL4: [4] # EV_MSCproperties: []udev:properties:- ID_INPUT=1- ID_INPUT_MOUSE=1- LIBINPUT_DEVICE_GROUP=3/93a/2510:mouse41547/41547quirks:events:

libinput reocrd可以记录来自设备的内核事件(比如说鼠标按下的事件),当然,在这里我们只是查看一下当前设备的一些属性。我们可以看到在这个虚拟鼠标中有四种事件类型,分别是EV_SYN,EV_KEY,EV_REL,EV_MSC。

这里有事件类型及对应的意思

inux 驱动程序开发中输入子系统总共能产生哪些事件类型(EV_KEY,EV_ABS,EV_REL) - maxiongying - 博客园

了解了这些之后就进入代码:

//代码中用到的一些头文件
#include <cstdio>
#include <iostream>
#include <fcntl.h>
#include <string>
#include <vector>
#include <cstring>
#include <unistd.h>
#include "linux/input.h"
#include "linux/uinput.h"

1、创建文件句柄:

要创建虚拟设备,首先要需要创建一个文件句柄fd,用来对设备进行读写操作,使用open()函数以写入模式和不可阻断的方式打开文件这里的O_NONBLOCK方式是无论有没有数据读取或等待,都会立即返回进程之中。详细的文件打开方式可参考此链接:

C语言open()函数:打开文件函数_C语言中文网 (biancheng.net)

(这里的打开方式很重要,我试过不加后面的方式,会导致写入失败。)

    int32_t fd = -1;fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);if (fd < 0) {printf("Failed to open uinput %s", __func__);return false;}

在这里我们需要使用open ()函数打开"/dev/uinput"路径,当open()函数打开失败时会返回-1,程序结束。

2、配置虚拟设备基本参数

在uinput中,uinput_user_dev结构体可以完成对设备基本信息的设置,例如设备名称、连接方式、产品ID等信息,具体可查看uinput.h文件:

    /* uinput.h中定义了如下内容:#define UINPUT_MAX_NAME_SIZE    80struct uinput_user_dev {char name[UINPUT_MAX_NAME_SIZE];     ------>用户定义生成虚拟设备的名称struct input_id id;                  ------>生成描述虚拟设备的一些参数信息(连接方式、厂商ID、产品ID、版本)__u32 ff_effects_max;__s32 absmax[ABS_CNT];               ------>最大绝对坐标值__s32 absmin[ABS_CNT];               ------>最小绝对坐标值__s32 absfuzz[ABS_CNT];              ------>坐标的分辨率__s32 absflat[ABS_CNT];              ------>坐标的基准值}; */uinput_user_dev dev{};std::string deviceName = "Test Mouse";strncpy(dev.name, deviceName.c_str(), deviceName.size());dev.id.bustype = BUS_USB;dev.id.vendor = 0x93a; // 这里是仿写真实鼠标的值dev.id.product = 0x2510; // 这里是仿写真实鼠标的值dev.id.version = 1;

定义一个结构体变量用来存放我们需要设置的信息,这里以虚拟鼠标为例,设置设备名称为"Test Mouse",连接方式选择USB模式,更多模式input.h文件中有定义,这里的厂商ID和产品ID我们设置为跟真实鼠标设备保持一致,版本号为1,因为鼠标中不存在ABS事件,所以对ABS的数据可以不进行设置。

3、设置其他相关信息

linux - Explain EV in /proc/bus/input/devices data - Unix & Linux Stack Exchange

这里有对设备信息在/proc/bus/input/devices中的解释,我们要设置一些PHYS:

    /*根据cat /proc/bus/input/devices设置一些相关信息*/std::string phys = "This is a mouse";if (ioctl(fd, UI_SET_PHYS, phys.c_str()) < 0) { // 设置PHYSprintf("Fail to set PHYS! %s", __func__);return -1; // 设置PHYS失败.}

这是系统层次结构中设备的物理路径,经过查看,每个设备对应的都是不同的,即使是两个相同的设备这里也是有区分的,所以如果在生成多个同样类型的虚拟设备的时候需要对这个进行区分,在这里我们随便对他进行设置一下(因为我感觉这个不是很重要)。

4、设置事件及对应的键值

    /*设置虚拟鼠标包含的事件类型*/const std::vector<uint32_t> eventType{ EV_KEY, EV_REL, EV_MSC };for (auto it : eventType) {if (ioctl(fd, UI_SET_EVBIT, it) < 0) { // 循环设置事件类型printf("%s ioctl failed", __func__); // 设置事件类型失败return -1;}}/*设置虚拟鼠标包含的EV_KEY事件*/const std::vector<uint32_t> keys{BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, BTN_SIDE, BTN_EXTRA, BTN_FORWARD, BTN_BACK, BTN_TASK};for (auto it : keys) {if (ioctl(fd, UI_SET_KEYBIT, it) < 0) { // 循环设置KEY事件printf("%s ioctl failed", __func__); // 设置KEY事件失败return -1;}}/*设置虚拟鼠标包含的REL事件*/const std::vector<uint32_t> rels{ REL_X, REL_Y, REL_WHEEL, REL_WHEEL_HI_RES };for (auto it : rels) {if (ioctl(fd, UI_SET_RELBIT, it) < 0) { // 循环设置REL事件printf("%s ioctl failed", __func__); // 设置REL事件失败return -1;}}/*设置虚拟鼠标包含的MSC事件*/if (ioctl(fd, UI_SET_MSCBIT, MSC_SCAN) < 0) {printf("%s ioctl failed", __func__); // 设置MSC事件失败return -1;}

一般情况下一个设备会有多种事件类型以及一种事件会有多个键值,首先用ioctl()函数对事件类型进行设置,在我们的虚拟鼠标中存在EV_KEY, EV_REL, EV_MSC三种事件,然后再分别对三种事件设置对应的键值,这里应该很好理解,最终的成品就是我们在libinput record中查到的一些键值属性。

5、数据写入文件句柄

在第二步的时候我们将设备的一些基本信息存储到了结构体当中,这里我们同样需要将信息写入文件句柄当中,这里我们用到write()函数:

    /*将设备信息写入到文件标识符fd中*/if (write(fd, &dev, sizeof(dev)) < 0) {printf("Unable to set input device info: %s", __func__); // 设置设备信息失败return -1;}

6、创建设备

完成了对设备基本信息以及事件键值的写入后,最后就要创建设备:

    /*创建设备*/if (ioctl(fd, UI_DEV_CREATE) < 0) {printf("Unable to create input device : %s", __func__);return -1;}while (true) {usleep(1500000); // 加入睡眠、使进程保持运行}return 0;

通过ioctl()创建出设备,当程序运行时,如果创建完成后程序结束,那创建的设备也会随着程序的结束而销毁,所以这里需要让程序保持运行,才能保证设备一直存在。

7、销毁设备

在这个程序中可以不用到,但是这里写一下代码:

    if (fd_ >= 0) {ioctl(fd_, UI_DEV_DESTROY);close(fd_);fd_ = -1;}

同样是通过ioctl()函数来进行控制。

8、编译执行

root@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug# ll
总用量 124
drwxr-xr-x 2 root root   4096 Nov 16 06:26 ./
drwxr-xr-x 3 root root   4096 Nov 16 06:26 ../
-rwxr-xr-x 1 root root 114976 Nov 16 06:26 CreateVirtualMouse.out*
root@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug# ./CreateVirtualMouse.out  &
[1] 57873
root@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug# 

这里我们将编译好的可执行文件后台运行;再次查看设备信息:

root@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug# cat /proc/bus/input/devices
#
#这里就不展示event0-4了
#I: Bus=0003 Vendor=093a Product=2510 Version=0001
N: Name="Test Mouse"
P: Phys=This is a mouse
S: Sysfs=/devices/virtual/input/input50
U: Uniq=
H: Handlers=mouse3 event5
B: PROP=0
B: EV=17
B: KEY=ff0000 0 0 0 0
B: REL=903
B: MSC=10root@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug#
root@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug# libinput record
Available devices:
/dev/input/event0:  Power Button
/dev/input/event1:  AT Translated Set 2 keyboard
/dev/input/event2:  VirtualPS/2 VMware VMMouse
/dev/input/event3:  VirtualPS/2 VMware VMMouse
/dev/input/event4:  VMware VMware Virtual USB Mouse
/dev/input/event5:  Test Mouse
Select the device event number: 5
Recording to 'stdout'.
version: 1
ndevices: 1
libinput:version: "1.15.5"git: "unknown"
system:os: "ubuntu:20.04"kernel: "5.11.0-40-generic"dmi: "dmi:bvnPhoenixTechnologiesLTD:bvr6.00:bd11/12/2020:br4.6:efr0.0:svnVMware,Inc.:pnVMwareVirtualPlatform:pvrNone:sku:rvnIntelCorporation:rn440BXDesktopReferencePlatform:rvrNone:cvnNoEnclosure:ct1:cvrN/A:"
devices:
- node: /dev/input/event5evdev:# Name: Test Mouse# ID: bus 0x3 vendor 0x93a product 0x2510 version 0x1# Supported Events:# Event type 0 (EV_SYN)# Event type 1 (EV_KEY)#   Event code 272 (BTN_LEFT)#   Event code 273 (BTN_RIGHT)#   Event code 274 (BTN_MIDDLE)#   Event code 275 (BTN_SIDE)#   Event code 276 (BTN_EXTRA)#   Event code 277 (BTN_FORWARD)#   Event code 278 (BTN_BACK)#   Event code 279 (BTN_TASK)# Event type 2 (EV_REL)#   Event code 0 (REL_X)#   Event code 1 (REL_Y)#   Event code 8 (REL_WHEEL)#   Event code 11 (REL_WHEEL_HI_RES)# Event type 4 (EV_MSC)#   Event code 4 (MSC_SCAN)# Properties:name: "Test Mouse"id: [3, 2362, 9488, 1]codes:0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] # EV_SYN1: [272, 273, 274, 275, 276, 277, 278, 279] # EV_KEY2: [0, 1, 8, 11] # EV_REL4: [4] # EV_MSCproperties: []udev:properties:- ID_INPUT=1- ID_INPUT_MOUSE=1- LIBINPUT_DEVICE_GROUP=3/93a/2510:This is a mousequirks:events:

可以看到在上面设置的东西在这里都体现出来了,键值也都设置成功,已经可以进行使用了。

简单的虚拟设备已经创建成功,本文内容基本都是自己总结出来了,希望大佬们指出不足之处。

PS:

uinput创建其他设备可查看这里:

multimodalinput_input: Providing traditional input methods, such as key, touch, keyboard, and mouse inputs | 提供传统的输入交互方式,例如按键、触控、键盘、鼠标等 - Gitee.com

Linux下使用uinput创建虚拟设备(Ubuntu20.04.2)相关推荐

  1. linux安装和配置 MariaDB (ubuntu20.04)

    文章目录 linux安装和配置 MariaDB (ubuntu20.04) 为什么选择 MariaDB,而不是 MySQL 呢? ubuntu20.04 安装和配置 MariaDB 安装特定版本 方法 ...

  2. 《LINUX下动态链接库的创建与应用》

    大家都知道,在windows系统中有很多的动态链接库(以.dll为后缀的文档,dll即dynamic link library).这种动态链接库,和静态函数库不同,他里面的函数并不是执行程式本身的一部 ...

  3. linux lvm添加磁盘,Linux下添加磁盘创建lvm分区

    shell> fdisk /dev/xvdb #### 选择磁盘 Command (m for help): m #### 帮助 Command action a toggle a bootab ...

  4. linux下多线程的创建与等待详解 【转载】

    linux下多线程的创建与等待详解 http://blog.chinaunix.net/uid-23842323-id-2656572.html 所有线程都有一个线程号,也就是Thread ID.其类 ...

  5. linux cvs账户,在linux下为cvs创建用户

    在linux下为cvs创建用户 1.创建可以登陆cvs服务器的用户名和密码: #> su cvsroot #> vi /home/cvsroot/CVSROOT/passwd test1: ...

  6. linux下使用mdadm组软raid,Linux下使用mdadm创建和管理软raid

    Linux下使用mdadm创建和管理软raid 注:本次操作以RHEL4为例,但应该可以应用到其它大部分的distro上(guess). mdadm的几个常用参数 -C 创建Raid,后面跟参数,代表 ...

  7. linux软RAId配置与管理总结,Linux下使用mdadm创建和管理软raid(转)

    Linux下使用mdadm创建和管理软raid 注:本次操作以RHEL4为例,但应该可以应用到其它大部分的distro上(guess). mdadm的几个常用参数 -C 创建Raid,后面跟参数,代表 ...

  8. linux mdadm 脚本,Linux下使用mdadm创建和管理软raid

    Linux下使用mdadm创建和管理软raid 注:本次操作以RHEL4为例,但应该可以应用到其它大部分的distro上(guess). mdadm的几个常用参数 -C 创建Raid,后面跟参数,代表 ...

  9. linux下的进程创建,Linux下进程的创建

    这篇文章主要是讲解到Linux进程的控制,包括程序和进程.守护进程.守护进程的出错处理. 1.程序和进程 程序(program)是存放在磁盘文件中的可执行文件,程序的执行实例被称为进程(process ...

最新文章

  1. spring之DelegatingFilterProxy
  2. VMTK学习——02.基本的PYPES教程
  3. 取某个日期所在周的任意一天日期
  4. epoll 边沿触发(ET 模式)和水平触发(LT 模式)
  5. docker的daemon.json基本配置
  6. LightSwitch 2011 数据字段唯一性验证方案
  7. 使用RichTextBox控件实现系统剪切板功能
  8. Coding Interview Guide -- 向有序的环形单链表中插入新节点
  9. pnpm 是更快的包管理工具
  10. mysql服务remove失败_《MySQL数据库》MySql简介、下载与安装
  11. 用心去体验幸福的感觉
  12. Matlab非线性拟合函数——nlinfit
  13. Android 电子书及阅读器Demo
  14. 常见的监控项目组网方案技术系统图,一文了解清楚!
  15. 常用的前端框架有哪些?
  16. 计算机显卡型号中数字含义详解,显卡型号中字母和数字所代表的含义
  17. 【OpenGrok代码搜索引擎】四、OpenGrok使用指南
  18. Proteus8.9 VSM Studio WINAVR编译器仿真ATmega16系列a09_扩展内存
  19. php购物车页面如何做,HTML代码实现简易购物车
  20. 中国真空断续器市场现状研究分析与发展前景预测报告(2022)

热门文章

  1. 历史性时刻:“苹果税”的时代结束了
  2. Uber无人车愈挫愈勇 估值百亿 已运送数万乘客
  3. GN_1_在Ubuntu22.04安装GN
  4. 如何让程序运行后不谈程序兼容性助手
  5. 在线制作数据库ER模型
  6. 钉钉内网穿透工具使用
  7. 我买了一辆奥迪,然后……
  8. 有什么好用的测量仪器尺子?手机也能其妙满足
  9. mysql世界国家数据库_世界国家 的数据库sql
  10. easyExcel的一些操作