linux input系统

         在linux中,像TP、G-sensor、按键等模块都会用到input系统上报事件,下面我们看看input系统是如何实现事件上报的。

        Linux对input子系统进行了高度的抽象,将input系统具体分成三层,input核心层、input事件处理层和input设备驱动层。input核心层(input-core)对input设备驱动层(input-device)和input事件处理层(input-handler)进行管理并进行消息转发。如下图:

                              

  • 设备驱动层input-device

input设备驱动层对应的不同的设备驱动,像TP、鼠标、键盘等设备驱动,驱动中会注册input系统,利用input系统上报事件,下面是一个简单的设备驱动注册input系统。

static int tiny210_init_input(void)
{
    int err = 0;
    printk(SF_LOG_LEVEL, "%s(..) enter.\n", __FUNCTION__);
    struct inout_dev *input = input_allocate_device(); //申请input设备

    if (!input) {
        printk(KERN_ERR, "input_allocate_device(..) failed.\n");
        return (-ENOMEM);
    }
	//填充input结构体数据
    input->name = "tiny210-keys";
    __set_bit(EV_KEY  ,   input->evbit );
    __set_bit(KEY_HOME,   input->keybit);
    __set_bit(KEY_MENU,   input->keybit);
    err = input_register_device(input); //注册一个input设备

    if (err) {
        printk(KERN_ERR, "input_register_device(..) = %d.\n", err);
        input_free_device(input);
        input = NULL;
        return (-ENODEV);
    }

    printk(SF_LOG_LEVEL, "%s(..) leave.\n", __FUNCTION__);
    return err;
}

//有中断或者按键事件上报按键事件,一般在中断处理函数中
    key_code = KEY_HOME;
    input_report_key(input, key_code, kevent->value);
    input_sync(input);

      设备驱动层使用主要是去注册一个input子系统,然后通过注册的系统,上报想要上报的事件。从上面可以看出,驱动中使用input一般分为4步,1、input_allocate_device申请input资源;2、填充input结构体,包括name、注册的事件类型(如按键事件、绝对事件等等);3、input_register_device(input)在内核注册一个input事件;4、当有按键或者TP报点产生时,同步上报按键事件,这一步一般会在中断处理函数中进行。

    input_allocate_device和input_register_device都是input-core暴露出来的接口。

int input_register_device(struct input_dev *dev)
{
	static atomic_t input_no = ATOMIC_INIT(0);
	struct input_handler *handler;
	const char *path;
	int error;

	/* Every input device generates EV_SYN/SYN_REPORT events. */
	__set_bit(EV_SYN, dev->evbit);

	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
	__clear_bit(KEY_RESERVED, dev->keybit);

	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
	input_cleanse_bitmasks(dev);

	if (!dev->hint_events_per_packet)
		dev->hint_events_per_packet =
				input_estimate_events_per_packet(dev);

	/*
	 * If delay and period are pre-set by the driver, then autorepeating
	 * is handled by the driver itself and we don't do it in input.c.
	 */
	init_timer(&dev->timer);
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
		dev->timer.data = (long) dev;
		dev->timer.function = input_repeat_key;
		dev->rep[REP_DELAY] = 250;
		dev->rep[REP_PERIOD] = 33;
	}

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;

	dev_set_name(&dev->dev, "input%ld",
		     (unsigned long) atomic_inc_return(&input_no) - 1);

    //设备驱动模型视图中增加一个input设备
	error = device_add(&dev->dev);
	if (error)
		return error;

	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	pr_info("%s as %s\n",
		dev->name ? dev->name : "Unspecified device",
		path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error) {
		device_del(&dev->dev);
		return error;
	}

    //链接到全局输入设备链表
	list_add_tail(&dev->node, &input_dev_list);

    //匹配全局事件处理handler链表,匹配支持该输入设备的handler
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	return 0;
}
  • 事件层input-handler

     从上面的图中可以看出,不同的设备会对应有不同的handler,我们以支持TP的event-handler为例说明,event-handler代码在driver/input/evdev.c中。

static const struct input_device_id evdev_ids[] = { //设备ID
	{ .driver_info = 1 },	/* Matches all devices */
	{ },			/* Terminating zero entry */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

static struct input_handler evdev_handler = {
	.event		= evdev_event,  //消息处理接口
	.connect	= evdev_connect, // input-core匹配到device后调用的接口
	.disconnect	= evdev_disconnect, 
	.fops		= &evdev_fops, //管理改输入操作的接口类
	.minor		= EVDEV_MINOR_BASE, //handler名称
	.name		= "evdev",
	.id_table	= evdev_ids, //设备id,标识支持的device型号
};

static int __init evdev_init(void)
{
    //注册handler,input_register_handler是input-core提供的接口
	return input_register_handler(&evdev_handler);
}

      input_register_handler函数在/driver/input/input.c中,也是input-core暴露出来的接口:

static struct input_handler *input_table[8];
//初始化全局input-handler列表
static LIST_HEAD(input_handler_list);
int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;
	int retval;

	retval = mutex_lock_interruptible(&input_mutex);
	if (retval)
		return retval;

	INIT_LIST_HEAD(&handler->h_list);

	if (handler->fops != NULL) {
		if (input_table[handler->minor >> 5]) {
			retval = -EBUSY;
			goto out;
		}
        //注册的handler添加到input_table中,input_table最多支持8种handler(例如按键、鼠标为不同hander),每种handler最多支持32个设备
		input_table[handler->minor >> 5] = handler; 
	}

    //添加到input-handler全局链表
	list_add_tail(&handler->node, &input_handler_list);

    //遍历input_dev_list全局设备链表,寻找handler支持的设备
	list_for_each_entry(dev, &input_dev_list, node)
		input_attach_handler(dev, handler); 

	input_wakeup_procfs_readers();

 out:
	mutex_unlock(&input_mutex);
	return retval;
}
  • 核心层input-core

      核心层input-core完成的工作包括:

     1) 直接跟字符设备驱动框架交互,字符设备驱动框架根据主设备号来进行管理,而input-core则是依赖于次设备号来进行分类管理。Input子系统的所有输入设备的主设备号都是13,其对应input-core定义的structfile_operations input_fops.驱动架构层通过主设备号13获取到input_fops,之后的处理便交给input_fops进行。

    2) 提供接口供事件处理层(input-handler)和输入设备(input-device)注册,并为输入设备找到匹配的事件处理者。

    3) 将input-device产生的消息(如触屏座标和压力值)转发给input-handler,或者将input-handler的消息传递给input-device(如鼠标的闪灯命令)。

    input-core代码主要在/driver/input/input.c中:

static const struct file_operations input_fops = {
	.owner = THIS_MODULE,
	.open = input_open_file,
	.llseek = noop_llseek,
};

static int __init input_init(void)
{
	int err;

    //sys/class/下面创建input节点
	err = class_register(&input_class);
	if (err) {
		pr_err("unable to register input_dev class\n");
		return err;
	}

	err = input_proc_init();
	if (err)
		goto fail1;

    //dev下面创建设备节点
	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
	if (err) {
		pr_err("unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}
static int __init input_proc_init(void)
{
	struct proc_dir_entry *entry;

	proc_bus_input_dir = proc_mkdir("bus/input", NULL);
	if (!proc_bus_input_dir)
		return -ENOMEM;

    // bus/input下面建立devices节点
	entry = proc_create("devices", 0, proc_bus_input_dir,
			    &input_devices_fileops);
	if (!entry)
		goto fail1;

    // bus/input下面建立handlers节点
	entry = proc_create("handlers", 0, proc_bus_input_dir,
			    &input_handlers_fileops);
	if (!entry)
		goto fail2;

	return 0;

 fail2:	remove_proc_entry("devices", proc_bus_input_dir);
 fail1: remove_proc_entry("bus/input", NULL);
	return -ENOMEM;
}

      除了在/dev下面简历主设备号为13的input设备节点,还会在设备模型/sys/class目录注册设备类,在/proc/bus/input目录产生设备信息,可以在对应的节点下查看支持的device和handler。

  • input-core关联匹配input-device和input-handler

      在上面的input_register_device和input_register_handler接口中,最后都会调用input_attach_handler接口来匹配输入事件与对应的handler。

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
	const struct input_device_id *id;
	int error;

    //匹配handler和device的ID,event_handler默认处理所有的事件类型设备
	id = input_match_device(handler, dev);
	if (!id)
		return -ENODEV;

    //匹配成功后调用handler的connect接口,既evdev_connect
	error = handler->connect(handler, dev, id);
	if (error && error != -ENODEV)
		pr_err("failed to attach handler %s to device %s, error: %d\n",
		       handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}

//每个handler支持的最多设备数
#define EVDEV_MINORS		32
//evdev_table的每一个元素对应一个input-device
static struct evdev *evdev_table[EVDEV_MINORS];
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;
	int minor;
	int error;

    //从0开始找到一个未用的索引号
	for (minor = 0; minor < EVDEV_MINORS; minor++)
		if (!evdev_table[minor])
			break;

	if (minor == EVDEV_MINORS) {
		pr_err("no more free evdev devices\n");
		return -ENFILE;
	}

	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	if (!evdev)
		return -ENOMEM;

	INIT_LIST_HEAD(&evdev->client_list);
	spin_lock_init(&evdev->client_lock);
	mutex_init(&evdev->mutex);
	init_waitqueue_head(&evdev->wait);

    //次设备文件名,这里是我们能看到的最终生成的设备文件event0、event1等
	dev_set_name(&evdev->dev, "event%d", minor);
	evdev->exist = true;
	evdev->minor = minor;
    //填充evdev结构体
	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;

    //在设备驱动视图/sys/class/input和/sys/devices目录下产生eventX设备
    //最终依赖event机制和mdev在/dev目录生成对应的设备文件
	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
	evdev->dev.class = &input_class;
	evdev->dev.parent = &dev->dev;
	evdev->dev.release = evdev_free;
	device_initialize(&evdev->dev);

    //注册handler,关联input-handler和input-device
	error = input_register_handle(&evdev->handle);
	if (error)
		goto err_free_evdev;
	//将evdev放入evdev_table中
	error = evdev_install_chrdev(evdev);
	if (error)
		goto err_unregister_handle;

	error = device_add(&evdev->dev);
	if (error)
		goto err_cleanup_evdev;

	return 0;

 err_cleanup_evdev:
	evdev_cleanup(evdev);
 err_unregister_handle:
	input_unregister_handle(&evdev->handle);
 err_free_evdev:
	put_device(&evdev->dev);
	return error;
}

      evdev_table代表evdev_handler所管理的底层input-device(通过input-handle管理)和应用层已打开该设备的进程、同步的相关结构和消息队列(evdev_client记录)。

struct evdev {
	int open;
	int minor;
    //管理handler对应的device
	struct input_handle handle;
	wait_queue_head_t wait;
    //记录应用层已经open过eventX设备的进程、同步异步相关结构和消息池
	struct evdev_client __rcu *grab;
	struct list_head client_list;
	spinlock_t client_lock; /* protects client_list */
	struct mutex mutex;
	struct device dev;
	bool exist;
};

     input-handler与input-device是通过input-handle来关联的,在input_register_handle函数中,将input-handle中的d_node和h_node分别关联到input-device和input-handler中,这样通过input-handler可以快速找到input-device,通过input-device也可以快速找到input-handler。

    至此,我们可以得到以下evdev-handler管理下的示意图:

                                      

  • 应用层open、read操作

       input-device注册成功后,会生成不同的节点,如下所示:

                                     

      应用层如果要得到相应的数据,例如得到按键操作的键值,会去open、read节点,例如open(dev/input/event0),下面我们看看open过程,设备节点的注册是在input_init实现的,register_chrdev(INPUT_MAJOR, "input", &input_fops);open函数会调用input_open_file函数:

static int input_open_file(struct inode *inode, struct file *file)
{
	struct input_handler *handler;
	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	err = mutex_lock_interruptible(&input_mutex);
	if (err)
		return err;

	/* No load-on-demand here? */
    //iminor(inode)为次设备号,根据次设备号找到注册的input-handler
    //input_table在input_register_handler时会填充
	handler = input_table[iminor(inode) >> 5];
	if (handler)  //handler下面对应的fops
		new_fops = fops_get(handler->fops);

	mutex_unlock(&input_mutex);

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops || !new_fops->open) {
		fops_put(new_fops);
		err = -ENODEV;
		goto out;
	}

	old_fops = file->f_op;
	file->f_op = new_fops; 
    //执行对应的handler下面的open
	err = new_fops->open(inode, file);
	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);
out:
	return err;
}

      input_open_file会通过查找不同的handler,找到对应的open函数,我们继续已evdev_handler为例。

static int evdev_open(struct inode *inode, struct file *file)
{
	struct evdev *evdev;
	struct evdev_client *client;
	int i = iminor(inode) - EVDEV_MINOR_BASE;
	unsigned int bufsize;
	int error;

	if (i >= EVDEV_MINORS)
		return -ENODEV;

	error = mutex_lock_interruptible(&evdev_table_mutex);
	if (error)
		return error;
    //input_attach_handler时填充
	evdev = evdev_table[i];
	if (evdev)
		get_device(&evdev->dev);
	mutex_unlock(&evdev_table_mutex);

	if (!evdev)
		return -ENODEV;

	bufsize = evdev_compute_buffer_size(evdev->handle.dev);

	client = kzalloc(sizeof(struct evdev_client) +
				bufsize * sizeof(struct input_event),
			 GFP_KERNEL);
	if (!client) {
		error = -ENOMEM;
		goto err_put_evdev;
	}

	client->bufsize = bufsize;
	spin_lock_init(&client->buffer_lock);
    //根据当前进程产生client的名称
	snprintf(client->name, sizeof(client->name), "%s-%d",
			dev_name(&evdev->dev), task_tgid_vnr(current));
	wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
    //将代表打开该设备的进程相关的数据结构client和evdev绑定
	client->evdev = evdev;
	evdev_attach_client(evdev, client);

    //执行input-device层的open
	error = evdev_open_device(evdev);
	if (error)
		goto err_free_client;

	file->private_data = client;
	nonseekable_open(inode, file);

	return 0;

 err_free_client:
	evdev_detach_client(evdev, client);
	wake_lock_destroy(&client->wake_lock);
	kfree(client);
 err_put_evdev:
	put_device(&evdev->dev);
	return error;
}

       evdev_open_device会调用到设备驱动中的open,一般像TP、G-sensor注册input系统没有定义open接口。

        上面open获得的fd句柄对应的file_operations是evdev_handler的evdev_fops,所以应用层read的时候最终会调用evdev_read接口。上面open过程中,我们看到evdev_client结构体,evdev_client中的buffer作为一个消息队列会保存input-core转发上来的底层消息:

struct evdev_client {
	unsigned int head;
	unsigned int tail;
	unsigned int packet_head; /* [future] position of the first element of next packet */
	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
	struct wake_lock wake_lock;
	char name[28];
    //异步通知上层等待进程
	struct fasync_struct *fasync;
	struct evdev *evdev;
	struct list_head node;
	unsigned int bufsize;
    //消息队列,input-core转发的底层触屏消息会放到这里
    //上层应用进程也会在这里读取
	struct input_event buffer[];
};

       下面看看evdev_read函数:

static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	struct input_event event;
	int retval;

    //判断读取长度是否小於单个input-event的长度
	if (count < input_event_size())
		return -EINVAL;

    //判断消息队列消息是否为空
	if (!(file->f_flags & O_NONBLOCK)) {
        //等待消息队列不为空事件
        //阻塞在这,等待wait释放,消息更新
		retval = wait_event_interruptible(evdev->wait,
			 client->packet_head != client->tail || !evdev->exist);
		if (retval)
			return retval;
	}

	if (!evdev->exist)
		return -ENODEV;

    //将消息队列的消息取出并通过copy_to_user填到buffer中返回 
	while (retval + input_event_size() <= count &&
	       evdev_fetch_next_event(client, &event)) {

		if (input_event_to_user(buffer + retval, &event))
			return -EFAULT;

		retval += input_event_size();
	}

	if (retval == 0 && file->f_flags & O_NONBLOCK)
		retval = -EAGAIN;
	return retval;
}

       当没有数据时,wait_event_interruptible阻塞,上层进程将进入睡眠。直到有数据更新时,唤醒进程,那什么时候会有数据更新了?我们在前面介绍input设备驱动的时候有说到input_report_key(input, key_code, kevent->value);和input_sync(input);数据就是在这里更新的。以TP为例,TP在产生中断后,会触发中断处理函数,调度到中断下部分后,会去读取TP的报点,然后通过input_report_abs和input_sync接口将数据更新到input-core中。

      input_report_abs、input_report_key、input_sync都是input.h中暴露出来的接口,最后都是调用input-core层的input-event接口:

void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;

	if (is_event_supported(type, dev->evbit, EV_MAX)) {

		spin_lock_irqsave(&dev->event_lock, flags);
		add_input_randomness(type, code, value);
		input_handle_event(dev, type, code, value);
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}
static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{
	int disposition = INPUT_IGNORE_EVENT;

	switch (type) {

	case EV_SYN:
		switch (code) {
		case SYN_CONFIG:
			disposition = INPUT_PASS_TO_ALL;
			break;
	
	case EV_ABS:
        //检测input-device初始化声明是否支持该编码ABS_MAX
		if (is_event_supported(code, dev->absbit, ABS_MAX))
			disposition = input_handle_abs_event(dev, code, &value);

		break;
//非同步事件
	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
		dev->sync = false;
	//不是传递给input-core处理事件
	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
		dev->event(dev, type, code, value);

	if (disposition & INPUT_PASS_TO_HANDLERS)
		input_pass_event(dev, type, code, value);
}
static void input_pass_event(struct input_dev *dev,
			     unsigned int type, unsigned int code, int value)
{
	struct input_handler *handler;
	struct input_handle *handle;

	rcu_read_lock();

    //输入设备对应的input-handle,其handler就是evdev_handler
	handle = rcu_dereference(dev->grab);
	if (handle) //调用到evdev_event
		handle->handler->event(handle, type, code, value);
	……
}

static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	struct input_event event;
	struct timespec ts;

    //填充输入消息结构体
	ktime_get_ts(&ts);
	event.time.tv_sec = ts.tv_sec;
	event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
	event.type = type;
	event.code = code;
	event.value = value;

	rcu_read_lock();

	client = rcu_dereference(evdev->grab);
	if (client) //将消息写入client结构体中的buffer中
		evdev_pass_event(client, &event);
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_event(client, &event);

	rcu_read_unlock();
	//唤醒等待读取消息进程,input_sync函数调用下来就会唤醒进程
	if (type == EV_SYN && code == SYN_REPORT)
		wake_up_interruptible(&evdev->wait);  
}

         wake_up_interruptible(&evdev->wait)调用后会唤醒执行在evdev_read中wait_event_interruptible等待读取消息的进程,继续下面的执行过程,从client的buffer中取出消息,并通过copy_to_user返回给应用程序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章