参考资料:
doc/input/uinput.rst
(Linux Kernel 5.x/Documentation/input/uinput.rst)
drivers/input/misc/uinput.c
(项目使用的是 Kernel 4.x )
1 概念
uinput
是一个内核模块,可以从用户空间模拟输入设备。通过写入 /dev/uinput
(/dev/input/uinput
)设备,进程可以创建具有特定功能的虚拟输入设备。一旦创建了这个虚拟设备,进程就可以通过它发送事件,这些事件将被传递给用户空间和内核中的使用者。
2 驱动准备
-
配置内核,编译成 .o 会被打包进 uImage
Symbol: INPUT_UINPUT [=y] -> Device Drivers -> Input device support -> Generic input layer -> Miscellaneous devices <y> User level driver support Defined at drivers/input/misc/Kconfig:530 Depends on: !UML && INPUT [=y] && INPUT_MISC [=y]
-
编译
$ make
3 应用编程—上报事件
/*
* 是 doc/uinput.rst 里的例程
* uinput.c UINPUT_VERSION >= 5 才能使用下面的例程
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/uinput.h>
void emit(int fd, int type, int code, int val)
{
struct input_event ie;
ie.type = type;
ie.code = code;
ie.value = val;
/* timestamp values below are ignored */
ie.time.tv_sec = 0;
ie.time.tv_usec = 0;
write(fd, &ie, sizeof(ie));
}
int main(void)
{
struct uinput_setup usetup;
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
/*
* The ioctls below will enable the device that is about to be
* created, to pass key events, in this case the space key.
*/
ioctl(fd, UI_SET_EVBIT, EV_KEY); /* 事件类型是EV_KEY */
ioctl(fd, UI_SET_KEYBIT, KEY_SPACE); /* 按键KEY_SPACE可以上报 */
memset(&usetup, 0, sizeof(usetup));
usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x1234; /* sample vendor */
usetup.id.product = 0x5678; /* sample product */
strcpy(usetup.name, "Example device");
ioctl(fd, UI_DEV_SETUP, &usetup);
ioctl(fd, UI_DEV_CREATE); /* 这里会创建一个input dev, 在/dev/input/目录下 */
/*
* On UI_DEV_CREATE the kernel will create the device node for this
* device. We are inserting a pause here so that userspace has time
* to detect, initialize the new device, and can start listening to
* the event, otherwise it will not notice the event we are about
* to send. This pause is only needed in our example code!
*/
sleep(1);
/* Key press, report the event, send key release, and report again */
emit(fd, EV_KEY, KEY_SPACE, 1);
emit(fd, EV_SYN, SYN_REPORT, 0);
emit(fd, EV_KEY, KEY_SPACE, 0);
emit(fd, EV_SYN, SYN_REPORT, 0);
/*
* Give userspace some time to read the events before we destroy the
* device with UI_DEV_DESTOY.
*/
sleep(1);
ioctl(fd, UI_DEV_DESTROY);
close(fd);
return 0;
}
4 应用编程—监听事件
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <linux/input.h>
/* 定义一个input_event变量,存放输入事件信息 */
static struct input_event inputevent;
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd;
int err = 0;
char *filename;
filename = argv[1];
if(argc != 2) {
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
while (1) {
err = read(fd, &inputevent, sizeof(inputevent));
if (err > 0) { /* 读取数据成功 */
switch (inputevent.type) {
case EV_KEY:
if (inputevent.code < BTN_MISC) { /* 键盘键值 */
printf("key %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
} else {
printf("button %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
}
break;
/* 其他类型的事件,自行处理 */
case EV_REL:
break;
case EV_ABS:
break;
case EV_MSC:
break;
case EV_SW:
break;
}
} else {
printf("读取数据失败\r\n");
}
}
return 0;
}