uinput 简介
uinput 是一个内核驱动,应用程序通过它可以在内核中模拟一个输入设备,其设备文件名是 /dev/uinput 或 /dev/input/uinput。
uinput 使用
使用 uinput 时遵循以下步骤:
- 通过 open 打开 uinput 设备
- 通过 ioctl 设置属性位图
- 通过 ioctl 设置事件类型位图和对应的事件码位图
- 通过 ioctl 设置ID和名称
- 通过 ioctl 创建输入设备
- 通过 write 上报输入事件
- 通过 close 关闭 uinput 设备
使能 uinput
在内核源码目录通过 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- menuconfig 打开配置菜单,然后选择 User level driver support ,路径如下:
-> Device Drivers
-> Input device support
-> Miscellaneous devices
<M> User level driver support
如果选择编译成模块,其生成 ko 文件位于内核目录的 drivers/input/misc/中,文件名是uinput.ko
编程实践
在应用层中使用 uinput 在内核中模拟一个按键输入设备,程序流程如下:
- 打开 uinput 设备
- 设置属性位图、事件类型位图、事件码位图
- 设置ID和名称
- 创建输入设备
- 周期上报按键事件
完整的代码如下所示:
#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;
//以下参数忽略
ie.time.tv_sec = 0;
ie.time.tv_usec = 0;
//上报输入事件
write(fd, &ie, sizeof(ie));
}
int main(int argc, const char *argv[])
{
int fd;
struct uinput_setup usetup;
const char *name = "/dev/uinput";
if(argc >= 2)
name = argv[1];
//打开 uinput 设备
fd = open(name, O_WRONLY);
if(fd < 0)
{
perror("open");
return -1;
}
//设置属性位图
if(ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_BUTTONPAD) < 0)
{
close(fd);
perror("UI_SET_PROPBIT");
return -1;
}
//设置事件类型位图和对应的事件码位图
if(ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0)
{
close(fd);
perror("UI_SET_EVBIT");
return -1;
}
if(ioctl(fd, UI_SET_KEYBIT, KEY_0) < 0)
{
close(fd);
perror("UI_SET_KEYBIT");
return -1;
}
//设置ID和名称
memset(&usetup, 0, sizeof(usetup));
usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x1234;
usetup.id.product = 0x5678;
strcpy(usetup.name, "Example device");
if(ioctl(fd, UI_DEV_SETUP, &usetup) < 0)
{
close(fd);
perror("UI_DEV_SETUP");
return -1;
}
//创建输入设备
if(ioctl(fd, UI_DEV_CREATE) < 0)
{
close(fd);
perror("UI_DEV_CREATE");
return -1;
}
while(1)
{
emit(fd, EV_KEY, KEY_0, 1);
emit(fd, EV_SYN, SYN_REPORT, 0);
usleep(500*1000);
emit(fd, EV_KEY, KEY_0, 0);
emit(fd, EV_SYN, SYN_REPORT, 0);
usleep(500*1000);
}
//close(fd);
}
测试程序参考10.1Linux输入子系统介绍中的按键测试程序
上机测试
- 修改内核,使能 uinput
- 在这里下载代码,并进行编译,得到 uinput_app.out 和 test_app.out 两个可执行程序。
- 执行命令 ./uinput_app.out ,此时会通过 uinput 在内核空间创建一个按键输入设备。
- 打开另一个终端,执行命令 ./test_app.out /dev/input/event0 ,运行测试程序,其中 /dev/input/event0 为按键输入设备的文件名,由系统自动生成,需要根据实际情况确定,此时测试程序会输出按键状态。