uinput模块分析 -- 3 结合应用代码分析

 应用代码如下:

#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实现的,但是想法很巧妙,大牛还是很多的。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值