应用代码如下:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.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);
ioctl(fd, UI_SET_KEYBIT, 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);
/*
* 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(10);
/* 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(10);
ioctl(fd, UI_DEV_DESTROY);
close(fd);
return 0;
}
先从open开始分析
open
open("/dev/uinput", O_WRONLY | O_NONBLOCK);
对应驱动代码
static int uinput_open(struct inode *inode, struct file *file)
{
struct uinput_device *newdev;
newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
if (!newdev)
return -ENOMEM;
mutex_init(&newdev->mutex);
spin_lock_init(&newdev->requests_lock);
init_waitqueue_head(&newdev->requests_waitq);
init_waitqueue_head(&newdev->waitq);
newdev->state = UIST_NEW_DEVICE;
file->private_data = newdev;
nonseekable_open(inode, file);
return 0;
}
可以看到只是简单的为uinput_device申请了空间,初始化了锁和工作队列,同时置状态为UIST_NEW_DEVICE
ioctl
ioctl(fd, UI_SET_EVBIT, EV_KEY);
对应的关系如下
uinput_ioctl() -> uinput_ioctl_handler()
static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
unsigned long arg, void __user *p)
{
int retval;
struct uinput_device *udev = file->private_data;
struct uinput_ff_upload ff_up;
struct uinput_ff_erase ff_erase;
struct uinput_request *req;
char *phys;
const char *name;
unsigned int size;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
if (!udev->dev) {
udev->dev = input_allocate_device();
if (!udev->dev) {
retval = -ENOMEM;
goto out;
}
}
...
switch (cmd) {
case UI_SET_EVBIT:
retval = uinput_set_bit(arg, evbit, EV_MAX);
goto out;
...
}
ioctl的时候,会判断udev->dev是否为空,这个dev就是input_dev,如果为空的话,则会申请分配input_dev,这里和之前分析的input设备注册一样。
uinput_set_bit的原型如下:
#define uinput_set_bit(_arg, _bit, _max) \
({ \
int __ret = 0; \
if (udev->state == UIST_CREATED) \
__ret = -EINVAL; \
else if ((_arg) > (_max)) \
__ret = -EINVAL; \
else set_bit((_arg), udev->dev->_bit); \
__ret; \
})
可以看到就是操作input设备的evbit,设置支持的事件
对于 ioctl(fd, UI_SET_KEYBIT, KEY_SPACE);
case UI_SET_KEYBIT:
retval = uinput_set_bit(arg, keybit, KEY_MAX);
goto out;
其实就是设置keybit,表示支持的按键键值,和之前的分析没有什么不同
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中的 uinput_dev_setup()
static int uinput_dev_setup(struct uinput_device *udev,
struct uinput_setup __user *arg)
{
struct uinput_setup setup;
struct input_dev *dev;
if (udev->state == UIST_CREATED)
return -EINVAL;
if (copy_from_user(&setup, arg, sizeof(setup)))
return -EFAULT;
if (!setup.name[0])
return -EINVAL;
dev = udev->dev;
dev->id = setup.id;
udev->ff_effects_max = setup.ff_effects_max;
kfree(dev->name);
dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
if (!dev->name)
return -ENOMEM;
udev->state = UIST_SETUP_COMPLETE;
return 0;
}
对比gpio-keys.c 内核注册方式
其实这里操作的就是红框的内容。状态变成 UIST_SETUP_COMPLETE
接着看
ioctl(fd, UI_DEV_CREATE);
对应 input_create_device
static int uinput_create_device(struct uinput_device *udev)
{
struct input_dev *dev = udev->dev;
int error, nslot;
if (udev->state != UIST_SETUP_COMPLETE) {
printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
return -EINVAL;
}
...
dev->event = uinput_dev_event;
input_set_drvdata(udev->dev, udev);
error = input_register_device(udev->dev);
if (error)
goto fail2;
udev->state = UIST_CREATED;
return 0;
fail2: input_ff_destroy(dev);
fail1: uinput_destroy_device(udev);
return error;
}
上面的代码忽略了EV_ABS和EV_FF相关的内容,这里主要看EV_KEY
注意这里的dev->event是被赋值的 uinput_dev_event,先记住
记着就是注册input设备,就像之前分析的,会找到handler,然后注册event,还会注册handle...
最后状态变成了 UIST_CREATED
整个过程中status变化为 UIST_NEW_DEVICE --> UIST_SETUP_COMPLETE --> UIST_CREATED
以上就注册了一个input设备,下面要分析的就是模拟按键上报了
emit(fd, EV_KEY, KEY_SPACE, 1);
对应uinput_write
static ssize_t uinput_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct uinput_device *udev = file->private_data;
int retval;
if (count == 0)
return 0;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
retval = udev->state == UIST_CREATED ?
uinput_inject_events(udev, buffer, count) :
uinput_setup_device_legacy(udev, buffer, count);
mutex_unlock(&udev->mutex);
return retval;
}
对于状态为UIST_CREATED的时候,直接调用 uinput_inject_events,意思就是注入事件。
当状态不是UIST_CREATED的时候,也就是说input设备还没有注册就要上报事件,那么这时候就会执行 uinput_setup_device_legacy,这个函数里面会分配input设备空间注册input,这属于不按规定流程走的方法,所以我们不分析,主要分析正常注册input设备后,输入事件的流程
static ssize_t uinput_inject_events(struct uinput_device *udev,
const char __user *buffer, size_t count)
{
struct input_event ev;
size_t bytes = 0;
if (count != 0 && count < input_event_size())
return -EINVAL;
while (bytes + input_event_size() <= count) {
/*
* Note that even if some events were fetched successfully
* we are still going to return EFAULT instead of partial
* count to let userspace know that it got it's buffers
* all wrong.
*/
if (input_event_from_user(buffer + bytes, &ev))
return -EFAULT;
input_event(udev->dev, ev.type, ev.code, ev.value);
bytes += input_event_size();
cond_resched();
}
return bytes;
}
可以看出来就是拷贝用户空间的数据,通过input_event上报,那么接着的
emit(fd, EV_SYN, SYN_REPORT, 0);
也是执行这个语句,对于内核中的input_sync,其实就是对input_event的封装。
以上就完成了uinput对于模拟按键事件上报的分析。
总的来说,uinput比较容易理解,因为就是基于input实现的,但是想法很巧妙,大牛还是很多的。