输入事件驱动---evdev_handler的大致实现流程(修整版)
一、input输入子系统框架
下 图是input输入子系统框架,输入子系统由输入子系统核心层(input core),驱动层和事件处理层(Event Handler)三部分组成。一个输入事件,比如滑动触摸屏都是通过input driver -> input core -> event handler -> user space 到达用户空间传给应用程序。
event hander事件处理层主要和用户空间交互,接收用户空间下发的file operation操作命令,生成/dev/input/xx设备节点供用户空间进行file operations操作;
input core层负责管理系统中的input dev设备 和input hander事件处理,并起到承上启下作用,负责输入设备和input handler之间信息传输;
input driver为具体用户设备驱动,输入设备由struct input-dev 结构表示,并由input_register_device和input_unregister_device来注册和卸载;
输入子系统结构方框图如下图:
二、输入事件驱动--->evdev_handler的实现大致分析
Linux 输入子系统已经建立好了几个handler,用来处理几类常见的事件,如鼠标、键盘、摇杆等。其中最为基础的是evdev_handler,它是在 driver/input/evdev.c中实现的。它能够接收任意类型的事件,任意id的设备都可以和它匹配连接,它对应的设备节点为/dev/eventX,次设备号的范围为64~95。
evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。
module_exit(evdev_exit);//初始化evdev_handlerstatic int __init evdev_init(void){
return input_register_handler(&evdev_handler);}
/**一般事件驱动定义了evdev_handler结构表示自己,
*并尝试去找到与handler中id_table相匹配的输入设备。*/static struct input_handler evdev_handler = {.event = evdev_event,
其中有:#define EVDEV_MINOR_BASE 64
.id_table = evdev_ids,};
MODULE_DEVICE_TABLE(input, evdev_ids);
static const struct input_device_id evdev_ids[] = {{ .driver_info = 1 },/* Matches all devices */{ },/* Terminating zero entry */};/**不管是事件驱动还是输入设备驱动在初始化时注册自身到全局链表中,并尝试与对应的输入设备驱动或事件驱动连接,*最终的操作都是调用事件驱动的XX_connect(...)完成两者的连接的。*/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;
for (minor = 0; minor < EVDEV_MINORS; minor++)if (!evdev_table[minor])break;
其中有定义:int open;
char name[16];
wait_queue_head_t wait;
spinlock_t client_lock; /* protects client_list */
struct device dev;
表示evdev_handler所表示的32个设备,这个循环为了找到空的一项
if (minor == EVDEV_MINORS) { //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);
dev_set_name(&evdev->dev, "event%d", minor); //event对应节点号,event0/evdev->minor = minor;
evdev->handle.dev = input_get_device(dev); //挂载到对evdev中handle的初始化,这些初始化的目的是使input_dev和input_handler联系起来。evdev->handle.name = dev_name(&evdev->dev);evdev->handle.handler = handler;evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //生成设备号evdev->dev.class = &input_class; //input的class类下evdev->dev.parent = &dev->dev;evdev->dev.release = evdev_free;device_initialize(&evdev->dev); //设备初始化
/*input_register_handle()最终完成了两者的连接,具体实现可研究其实现源码。input_register_handle -登记一个新的 input handle,此功能将一个新的而一旦打开使用input_open_device(),events事件可以随时跟随。*/error = input_register_handle(&evdev->handle); //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;}
对主设备号为INPUT_MAJOR的设备结点进行操作,会将操作集转换成handler的操作集。在evdev_handler中定义了一个fops集合,被赋值为evdev_fops的指针,static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE; //得到了在evdev_table[]中的序号
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
if (evdev)
mutex_unlock(&evdev_table_mutex);
其中:
int head;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct list_head node;
evdev_open_device(evdev); //打开输入设备
static int evdev_open_device(struct evdev *evdev)
{
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist) //判断设备的存在
retval = -ENODEV;
input_open_device打开evdev对应的handle
if (retval)
}
return retval;
if (error)
goto err_free_client;
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);}
static long evdev_ioctl_handler(struct file *file, unsigned int cmdvoid __user *p, int compat_mode){struct evdev_client *client = file->private_data;struct evdev *evdev = client->evdev;int retval;retval = mutex_lock_interruptible(&evdev->mutex);if (retval)return retval;if (!evdev->exist) {retval = -ENODEV;goto out;}retval = evdev_do_ioctl(file, cmd, p, compat_mode); out:mutex_unlock(&evdev->mutex);return retval;}
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
struct ff_effect effect;
int __user *ip = (int __user *)p;
unsigned int i, t, u, v;
unsigned int size;
int error;
/* First we check for fixed-length commands */
switch (cmd) {
case EVIOCGVERSION:
/* 设置重复类事件 */
%