linux驱动input子系统学习四(输入事件驱动层)

输入事件层目前系统帮我们区分了三种,

通用各种类型输入类设备的evdev,主流,也是将来大方向

mousedev,joydev。

我们学习就以evdev来学习,将来使用通用性也更高。

一、首先是框架性的调用输入核心层实现的input_register_handler和input_unregister_handler来注册evdev层。

static struct input_handler evdev_handler = {	//事件驱动核心结构体
	.event		= evdev_event,			//打包数据,并上报事件(发送一个事件)
	.events		= evdev_events,			//发送多个事件
	.connect	= evdev_connect,		//找到匹配 dev 后进行连接
	.disconnect	= evdev_disconnect,		//断开连接时使用
	.legacy_minors	= true,
	.minor		= EVDEV_MINOR_BASE,		//次设备号基数 64	
	.name		= "evdev",				//处理程序的名称,在/proc/bus/input/handlers 中显示
	.id_table	= evdev_ids,			//匹配规则,evdev是所有都可以匹配
};

static int __init evdev_init(void)					//注册事件驱动
{
	return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)					//卸载事件驱动
{
	input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

evdev有个重要的数据结构,当输入设备和事件驱动匹配的时候,就会在connect函数创建dvdev设备,最后注册成字符设备开放到用户空间。

struct evdev {								// evdev 相当于一个桥梁,链接 input_dev 和 handler
	int open;								//打开引用计数
	struct input_handle handle;				//关联的input_handle
	wait_queue_head_t wait;					//等待队列
	struct evdev_client __rcu *grab;
	struct list_head client_list;			//evdev_client 链表,说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问
	spinlock_t client_lock; /* protects client_list */
	struct mutex mutex;
	struct device dev;						//dev 是 evdev 的父设备
	struct cdev cdev;						//cdev字符设备
	bool exist;								//判断此 evdev 释放存在
};

二、接口实现

1.首先我们看一下connect函数,它在注册handler和dev时,如果匹配上了就会调用。(具体可以查看我的输入子系统学习三)

/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect.
 */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)								//创建新的 evdev 设备。输入内核有序连接和断开
{
	struct evdev *evdev;
	int minor;
	int dev_no;
	int error;

	minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);		//申请未使用的minor
	if (minor < 0) {
		error = minor;
		pr_err("failed to reserve new minor: %d\n", error);
		return error;
	}

	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);						//申请 sizeof(struct evdev) 的空间
	if (!evdev) {
		error = -ENOMEM;
		goto err_free_minor;
	}

	INIT_LIST_HEAD(&evdev->client_list);									//初始化 client_list 链表
	spin_lock_init(&evdev->client_lock);									//初始化 client_lock 自旋锁
	mutex_init(&evdev->mutex);												//初始化 evdev 的互斥量
	init_waitqueue_head(&evdev->wait);										//初始化 evdev 的等待队列
	evdev->exist = true;													//此 evdev 为存在

	dev_no = minor;															//申请到的次设备号
	/* Normalize device number if it falls into legacy range */
	if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
		dev_no -= EVDEV_MINOR_BASE;											//判断此 evdev 设备申请的是 32 个中的哪一个
	dev_set_name(&evdev->dev, "event%d", dev_no);							//设置此 evdev->dev 的名字

	evdev->handle.dev = input_get_device(dev);								//增加 evdev->dev 的引用计数
	evdev->handle.name = dev_name(&evdev->dev);								//设置名字 event0,1,2,3....
	evdev->handle.handler = handler;										//指向匹配到的 handler
	evdev->handle.private = evdev;											//指向自己 evdev

	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);							//由输入设备的主设备号13和此设备号合成一个设备号				
	evdev->dev.class = &input_class;										//设置 evdev 的类为 input_class
	evdev->dev.parent = &dev->dev;											//输入设备 input_dev 是 evdev 是的父设备
	evdev->dev.release = evdev_free;										//释放 evdev 资源
	device_initialize(&evdev->dev);											//内部 device 初始化

	error = input_register_handle(&evdev->handle);							//注册 handle
	if (error)
		goto err_free_evdev;

	cdev_init(&evdev->cdev, &evdev_fops);									//用 evdev_fops 初始化一个 cdev 字符设备
	evdev->cdev.kobj.parent = &evdev->dev.kobj;
	error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);						//添加一个字符设备到系统中
	if (error)
		goto err_unregister_handle;

	error = device_add(&evdev->dev);										//真正的创建设备层次和attribute在sysfs中
	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);
 err_free_minor:
	input_free_minor(minor);
	return error;
}

2.disconnect,和connect相反

static void evdev_disconnect(struct input_handle *handle)
{
	struct evdev *evdev = handle->private;					//handle 的私有数据就是 handle 本身 evdev

	device_del(&evdev->dev);
	evdev_cleanup(evdev);
	input_free_minor(MINOR(evdev->dev.devt));
	input_unregister_handle(handle);
	put_device(&evdev->dev);
}

3.对一个设备驱动层发送过来的事件打包

/*
 * Pass incoming events to all connected clients.					传递来到的事件给所有连接的客户端
 */
static void evdev_events(struct input_handle *handle,
			 const struct input_value *vals, unsigned int count)	
{
	struct evdev *evdev = handle->private;							//此处的私有数据就是 evdev 本身
	struct evdev_client *client;
	ktime_t ev_time[EV_CLK_MAX];

	ev_time[EV_CLK_MONO] = ktime_get();								//设置对应的时间
	ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);
	ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],
						 TK_OFFS_BOOT);

	rcu_read_lock();

	client = rcu_dereference(evdev->grab);

	if (client)														//如果该evdev有个专用的client,那么就将事件发给它如果该evdev
		evdev_pass_values(client, vals, count, ev_time);
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_values(client, vals, count, ev_time);		//否则,发给 evdev 客户链表上的所有客户

	rcu_read_unlock();
}

/*
 * Pass incoming event to all connected clients.
 */
static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)	//传递输入进来的事件给所有的客户
{
	struct input_value vals[] = { { type, code, value } };

	evdev_events(handle, vals, 1);
}

发送数据给client函数

static void __pass_event(struct evdev_client *client,
			 const struct input_event *event)						//使用异步通知函数通知上层
{
	client->buffer[client->head++] = *event;						//把 *event 放在 struct evdev_client 的缓冲区中
	client->head &= client->bufsize - 1;

	if (unlikely(client->head == client->tail)) {
		/*
		 * This effectively "drops" all unconsumed events, leaving
		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
		 */
		client->tail = (client->head - 2) & (client->bufsize - 1);

		client->buffer[client->tail].time = event->time;
		client->buffer[client->tail].type = EV_SYN;
		client->buffer[client->tail].code = SYN_DROPPED;
		client->buffer[client->tail].value = 0;

		client->packet_head = client->tail;
	}

	if (event->type == EV_SYN && event->code == SYN_REPORT) {		//事件的类型是同步类型,事件的编码是只上报一次
		client->packet_head = client->head;							//下一次从缓冲区的第一个位置取数据
		kill_fasync(&client->fasync, SIGIO, POLL_IN);				//发送一个异步通知,通知该打开该client的应用程序,执行信号处理函数
	}
}

static void evdev_pass_values(struct evdev_client *client,
			const struct input_value *vals, unsigned int count,
			ktime_t *ev_time)										//把事件发给 evdev_client,唤醒等待队列
{
	struct evdev *evdev = client->evdev;							//获取此客户端要访问的 evdev 设备
	const struct input_value *v;
	struct input_event event;
	bool wakeup = false;											//默认不唤醒

	if (client->revoked)											//如果保留了,直接返回
		return;

	event.time = ktime_to_timeval(ev_time[client->clk_type]);		//根据客户端的时间类型,获取对应的时间

	/* Interrupts are disabled, just acquire the lock. */			//中断被关闭,获取锁
	spin_lock(&client->buffer_lock);

	for (v = vals; v != vals + count; v++) {						//循环发送
		event.type = v->type;
		event.code = v->code;
		event.value = v->value;
		__pass_event(client, &event);								//使用异步通知函数通知上层
		if (v->type == EV_SYN && v->code == SYN_REPORT)				//事件的类型是同步类型,事件的编码是只上报一次,此时要唤醒等待队列
			wakeup = true;
	}

	spin_unlock(&client->buffer_lock);

	if (wakeup)
		wake_up_interruptible(&evdev->wait);						//唤醒 evdev 的等待队列
}

4.应用层的调用接口file_operation

static const struct file_operations evdev_fops = {
	.owner		= THIS_MODULE,
	.read		= evdev_read,
	.write		= evdev_write,
	.poll		= evdev_poll,
	.open		= evdev_open,
	.release	= evdev_release,
	.unlocked_ioctl	= evdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= evdev_ioctl_compat,
#endif
	.fasync		= evdev_fasync,
	.flush		= evdev_flush,
	.llseek		= no_llseek,
};

操作函数比较多,我们主要分析连个最常用的,open,和read

 

当我们使用open打开时,会创建 evdev_client 和 ecdev字符设备建立联系,通过evdev的handle找到对应的device,打开此设备。

static int evdev_open(struct inode *inode, struct file *file)
{
	struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);	//由 inode->i_cdev 成员获取 evdev 设备
	unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
	unsigned int size = sizeof(struct evdev_client) +
					bufsize * sizeof(struct input_event);
	struct evdev_client *client;
	int error;

	client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);				//申请 size 大小的内存
	if (!client)
		client = vzalloc(size);
	if (!client)
		return -ENOMEM;

	client->bufsize = bufsize;										//设置事件缓冲区的大小
	spin_lock_init(&client->buffer_lock);
	client->evdev = evdev;											//此次打开字符设备所使用的 evdev 
	evdev_attach_client(evdev, client);								//把此 dvdev 附着在 evdev_client

	error = evdev_open_device(evdev);								//打开此 evdev 字符设备
	if (error)
		goto err_free_client;

	file->private_data = client;									//file 的私有数据设置为 evdev_client
	nonseekable_open(inode, file);

	return 0;

 err_free_client:
	evdev_detach_client(evdev, client);
	kvfree(client);
	return error;
}
int input_open_device(struct input_handle *handle)
{
	struct input_dev *dev = handle->dev;
	int retval;

	retval = mutex_lock_interruptible(&dev->mutex);
	if (retval)
		return retval;

	if (dev->going_away) {
		retval = -ENODEV;
		goto out;
	}

	handle->open++;				//增加引用计数

	if (!dev->users++ && dev->open)
		retval = dev->open(dev);		//.open		= input_proc_devices_open, 打开输入设备

	if (retval) {
		dev->users--;
		if (!--handle->open) {
			/*
			 * Make sure we are not delivering any more events
			 * through this handle
			 */
			synchronize_rcu();
		}
	}

 out:
	mutex_unlock(&dev->mutex);
	return retval;
}

对应应用层的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;
	size_t read = 0;
	int error;

	if (count != 0 && count < input_event_size())
		return -EINVAL;

	for (;;) {
		if (!evdev->exist || client->revoked)
			return -ENODEV;
		
		/* 当client缓冲区无数据;evdev不存在;文件非阻塞打开,那个read直接返回错误 */
		if (client->packet_head == client->tail &&
		    (file->f_flags & O_NONBLOCK))
			return -EAGAIN;

		/*
		 * count == 0 is special - no IO is done but we check
		 * for error conditions (see above).
		 */
		if (count == 0)
			break;

		/* 当要读取的数据大于struct input_event且client里面的buffer有数据,则把数据拷贝到用户空间*/
		while (read + input_event_size() <= count &&
		       evdev_fetch_next_event(client, &event)) {

			if (input_event_to_user(buffer + read, &event))	/* 对copy_to_user的封装 */
				return -EFAULT;

			read += input_event_size();
		}

		if (read)
			break;

		if (!(file->f_flags & O_NONBLOCK)) {				//以阻塞的方式等待事件的发生
			error = wait_event_interruptible(evdev->wait,
					client->packet_head != client->tail ||
					!evdev->exist || client->revoked);
			if (error)
				return error;
		}
	}

	return read;
}

 

上一篇:linux – 未检测到触控板


下一篇:12.Linux之输入子系统分析(详解)