input子系统学习笔记九 evdev输入事件驱动分析
2012-07-24 17:03
543 查看
evdev 输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev 输入事件驱动从底层接收事件信息,将其反映到 sys 文件系统中,用户程序通过对 sys 文件系统的操作,就能够达到处理事件的能力。下面先对 evdev 的初始化进行简要的分析。
evdev的初始化
evdev 以模块的方式被组织在内核中,与其他模块一样,也具有初始化函数和卸载函数。evdev的初始化主要完成一些注册工作,使内核认识 evdev 的存在。
evdev_init()初始化函数
evdev 模块定义在/drivers/input/evdev.c 文件中,该模块的初始化函数是 evdev_init()。在初始化函数中注册了一个 evdev_handler 结构体,用来对一些通用的抽象事件进行统一处理,该函数的代码如下:
C/C++代码
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);/*调 用input_register_handler() 函数注册了evdev_handler事件处理器,input_register_handler()函数在前面已经详细解释过,这里将对其参数evdev_handler进行分析,其定义如链接:*/
}
参数evdev_handler
结构体定义如下:
C/C++代码
static struct input_handler evdev_handler = {
.event= evdev_event,
.connect= evdev_connect,
.disconnect = evdev_disconnect,
.fops= &evdev_fops,
.minor= EVDEV_MINOR_BASE,/*定义了 minor 为 EVDEV_MINOR_BASE(64) 。因为一个 handler 可以处理 32 个设备,所以 evdev_handler 所能处理的设备文件范围为(13,64)~(13,64+32) 中 13 是所有输入设备的主设备号。*/
.name= "evdev",
.id_table = evdev_ids,/*定义了 id_table 结构。回忆前面几节的内容,由 input_attach_handler()函数可知,input_dev 与 handler 匹配成功的关键,在于 handler 中的 blacklist 和 id_talbe.。Evdev_handler只定义了 id_table,其定义如链接: */
};
id_talbe
C/C++代码
static const struct input_device_id evdev_ids[] = {
{
.driver_info = 1 }, /* Matches all devices */
{ },/* Terminating zero entry */
};
evdev_ids 没有定义 flags,也没有定义匹配属性值。这个 evdev_ids 的意思就是:evdev_handler可以匹配所有 input_dev 设备,也就是所有的 input_dev 发出的事件,都可以由 evdev_handler来处理。另外,从前面的分析可以知道,匹配成功之后会调用 handler->connect()函数
evdev_connect()函数
evdev_connect()函数主要用来连接input_dev 和 input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后将向谁返回结果。代码如下:
C/C++代码
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;
/*第 07~13 行,for 循环中的 EVDEV_MINORS 定义为 32,表示 evdev_handler 所表示的 32 个设备文件。 evdev_talbe 是一个 struct evdev 类型的数组,struct evdev 是模块使用的封装结构,与具体的输入设备有关。第 08 行,这一段代码的在 evdev_talbe 找到为空的那一项,当找到为空的一项,便结束 for 循环。这时,minor 就是数组中第一项为空的序号。第 10 到13 行,如果没有空闲的表项,则退出。*/
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);/*第 14~16 行,分配一个 struct evdev 的空间,如果分配失败,则退出。*/
if (!evdev)
return -ENOMEM;
/*第 17~20 行,对分配的 evdev 结构进行初始化,主要对链表、互斥锁和等待队列做必要的封装了一个 handle 结构,这个结构与 handler 是不同的。可以把 handle初始化。 evdev 中,在这个结构用来联系匹配成功的 handler 和 input 看成是 handler 和 input device 的信息集合体,device。*/
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
/*第 21 行,对 evdev 命一个名字,这个设备的名字形如 eventx, 如 event1、 event2 和 event3等。最大有 32 个设备,这个设备将在/dev/input/目录下显示。*/
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = 1;
/*第 23~27 行,对 evdev 进行必要的初始化。其中,主要对 handle 进行初始化,这些初始化的目的是使 input_dev 和 input_handler 联系起来。*/
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
/*第 28~33 行,在设备驱动模型中注册一个 evdev->dev 的设备,并初始化一个 evdev->dev 的设备。这里,使 evdev->dev 所属的类指向 input_class。这样在/sysfs 中创建的设备目录就会在/sys/class/input/下显示。*/
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);
error = input_register_handle(&evdev->handle);/*第 34 行,调用 input_register_handle()函数注册一个 input_handle 结构体。*/
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev);/*第 37 行,注册 handle,如果成功,那么调用 evdev_install_chrdev 将 evdev_table 的 minor项指向 evdev.。*/
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);/*将 evdev->device 注册到 sysfs 文件系统中。*/
/*第 41~50 行,进行一些必要的错误处理。*/
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 设备的打开
用户程序通过输入子系统创建的设备结点函数 open()、read()和 write()等,打开和读写输入设备。创建的设备结点显示在/dev/input/目录下,由 eventx 表示。
evdev_open()函数
对主设备号为 INPUT_MAJOR 的设备结点进行操作,会将操作集转换成 handler 的操作集。在 evdev_handler 中定义了一个 fops 集合,被赋值为 evdev_fops 的指针。evdev_fops 就是设备结点的操作集,其定义代码如下:
C/C++代码
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,
.fasync= evdev_fasync,
.flush= evdev_flush
};
evdev_fops 结 构 体 是 一 个 file_operations 的 类 型 。 当 用 户 层 调 用 类 似 代 码open("/dev/input/event1" , O_RDONLY) 函 数 打 开 设 备 结 点 时 , 会 调 用 evdev_fops 中 的evdev_read()函数,该函数的代码如下:
C/C++代码
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;/*iminor(inode) - EVDEV_MINOR_BASE 得到了在 evdev_table[]中的序号,赋给变量 i。*/
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
/*第 09~17 行,将数组 evdev_table[]中对应的 evdev 取出,并调用 get_device()增加引用计数。*/
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
evdev = evdev_table[i];
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
/*第 18~30 行,分配并初始化一个 client 结构体,并将它和 evdev 关联起来。 关联的内容是,将 client->evdev 指 向 它 所 表 示 的 evdev , 调 用 evdev_attach_client() 将 client 挂 到evdev->client_list 上。第 29 行,将 client 赋给 file 的 private_data。在 evdev 中,这个操作集就是 evdev_fops,对应的 open()函数如下:static int evder_open(struct inode *inode, struct file *file) */
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
evdev_attach_client(evdev, client);
error = evdev_open_device(evdev);/* evdev_open_device()函数 */
if (error)
goto err_free_client;
file->private_data = client;
nonseekable_open(inode, file);
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
evdev_open_device()函数
evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用 input_open_device()函数打开设备。 evdev_open_device()函数的代码如下:
C/C++代码
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;
else if (!evdev->open++) {/*第 09~12 行,如果 evdev 是第一次打开,就会调用 input_open_device()打开 evdev 对应的handle;否则不做任何操作返回。*/
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
input_open_device()函数
在这个函数中,递增 handle 的打开计数。如果是第一次打开,则调用 input_dev 的 open()函数。
Java代码
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);
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;
}
至此,关于input子系统的内容已经全部结束!
evdev的初始化
evdev 以模块的方式被组织在内核中,与其他模块一样,也具有初始化函数和卸载函数。evdev的初始化主要完成一些注册工作,使内核认识 evdev 的存在。
evdev_init()初始化函数
evdev 模块定义在/drivers/input/evdev.c 文件中,该模块的初始化函数是 evdev_init()。在初始化函数中注册了一个 evdev_handler 结构体,用来对一些通用的抽象事件进行统一处理,该函数的代码如下:
C/C++代码
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);/*调 用input_register_handler() 函数注册了evdev_handler事件处理器,input_register_handler()函数在前面已经详细解释过,这里将对其参数evdev_handler进行分析,其定义如链接:*/
}
参数evdev_handler
结构体定义如下:
C/C++代码
static struct input_handler evdev_handler = {
.event= evdev_event,
.connect= evdev_connect,
.disconnect = evdev_disconnect,
.fops= &evdev_fops,
.minor= EVDEV_MINOR_BASE,/*定义了 minor 为 EVDEV_MINOR_BASE(64) 。因为一个 handler 可以处理 32 个设备,所以 evdev_handler 所能处理的设备文件范围为(13,64)~(13,64+32) 中 13 是所有输入设备的主设备号。*/
.name= "evdev",
.id_table = evdev_ids,/*定义了 id_table 结构。回忆前面几节的内容,由 input_attach_handler()函数可知,input_dev 与 handler 匹配成功的关键,在于 handler 中的 blacklist 和 id_talbe.。Evdev_handler只定义了 id_table,其定义如链接: */
};
id_talbe
C/C++代码
static const struct input_device_id evdev_ids[] = {
{
.driver_info = 1 }, /* Matches all devices */
{ },/* Terminating zero entry */
};
evdev_ids 没有定义 flags,也没有定义匹配属性值。这个 evdev_ids 的意思就是:evdev_handler可以匹配所有 input_dev 设备,也就是所有的 input_dev 发出的事件,都可以由 evdev_handler来处理。另外,从前面的分析可以知道,匹配成功之后会调用 handler->connect()函数
evdev_connect()函数
evdev_connect()函数主要用来连接input_dev 和 input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后将向谁返回结果。代码如下:
C/C++代码
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;
/*第 07~13 行,for 循环中的 EVDEV_MINORS 定义为 32,表示 evdev_handler 所表示的 32 个设备文件。 evdev_talbe 是一个 struct evdev 类型的数组,struct evdev 是模块使用的封装结构,与具体的输入设备有关。第 08 行,这一段代码的在 evdev_talbe 找到为空的那一项,当找到为空的一项,便结束 for 循环。这时,minor 就是数组中第一项为空的序号。第 10 到13 行,如果没有空闲的表项,则退出。*/
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);/*第 14~16 行,分配一个 struct evdev 的空间,如果分配失败,则退出。*/
if (!evdev)
return -ENOMEM;
/*第 17~20 行,对分配的 evdev 结构进行初始化,主要对链表、互斥锁和等待队列做必要的封装了一个 handle 结构,这个结构与 handler 是不同的。可以把 handle初始化。 evdev 中,在这个结构用来联系匹配成功的 handler 和 input 看成是 handler 和 input device 的信息集合体,device。*/
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
/*第 21 行,对 evdev 命一个名字,这个设备的名字形如 eventx, 如 event1、 event2 和 event3等。最大有 32 个设备,这个设备将在/dev/input/目录下显示。*/
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = 1;
/*第 23~27 行,对 evdev 进行必要的初始化。其中,主要对 handle 进行初始化,这些初始化的目的是使 input_dev 和 input_handler 联系起来。*/
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
/*第 28~33 行,在设备驱动模型中注册一个 evdev->dev 的设备,并初始化一个 evdev->dev 的设备。这里,使 evdev->dev 所属的类指向 input_class。这样在/sysfs 中创建的设备目录就会在/sys/class/input/下显示。*/
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);
error = input_register_handle(&evdev->handle);/*第 34 行,调用 input_register_handle()函数注册一个 input_handle 结构体。*/
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev);/*第 37 行,注册 handle,如果成功,那么调用 evdev_install_chrdev 将 evdev_table 的 minor项指向 evdev.。*/
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);/*将 evdev->device 注册到 sysfs 文件系统中。*/
/*第 41~50 行,进行一些必要的错误处理。*/
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 设备的打开
用户程序通过输入子系统创建的设备结点函数 open()、read()和 write()等,打开和读写输入设备。创建的设备结点显示在/dev/input/目录下,由 eventx 表示。
evdev_open()函数
对主设备号为 INPUT_MAJOR 的设备结点进行操作,会将操作集转换成 handler 的操作集。在 evdev_handler 中定义了一个 fops 集合,被赋值为 evdev_fops 的指针。evdev_fops 就是设备结点的操作集,其定义代码如下:
C/C++代码
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,
.fasync= evdev_fasync,
.flush= evdev_flush
};
evdev_fops 结 构 体 是 一 个 file_operations 的 类 型 。 当 用 户 层 调 用 类 似 代 码open("/dev/input/event1" , O_RDONLY) 函 数 打 开 设 备 结 点 时 , 会 调 用 evdev_fops 中 的evdev_read()函数,该函数的代码如下:
C/C++代码
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;/*iminor(inode) - EVDEV_MINOR_BASE 得到了在 evdev_table[]中的序号,赋给变量 i。*/
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
/*第 09~17 行,将数组 evdev_table[]中对应的 evdev 取出,并调用 get_device()增加引用计数。*/
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
evdev = evdev_table[i];
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
/*第 18~30 行,分配并初始化一个 client 结构体,并将它和 evdev 关联起来。 关联的内容是,将 client->evdev 指 向 它 所 表 示 的 evdev , 调 用 evdev_attach_client() 将 client 挂 到evdev->client_list 上。第 29 行,将 client 赋给 file 的 private_data。在 evdev 中,这个操作集就是 evdev_fops,对应的 open()函数如下:static int evder_open(struct inode *inode, struct file *file) */
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
evdev_attach_client(evdev, client);
error = evdev_open_device(evdev);/* evdev_open_device()函数 */
if (error)
goto err_free_client;
file->private_data = client;
nonseekable_open(inode, file);
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
evdev_open_device()函数
evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用 input_open_device()函数打开设备。 evdev_open_device()函数的代码如下:
C/C++代码
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;
else if (!evdev->open++) {/*第 09~12 行,如果 evdev 是第一次打开,就会调用 input_open_device()打开 evdev 对应的handle;否则不做任何操作返回。*/
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
input_open_device()函数
在这个函数中,递增 handle 的打开计数。如果是第一次打开,则调用 input_dev 的 open()函数。
Java代码
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);
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;
}
至此,关于input子系统的内容已经全部结束!
相关文章推荐
- input子系统学习笔记九 evdev输入事件驱动分析
- input子系统学习笔记九 evdev输入事件驱动分析
- input子系统学习笔记九 evdev输入事件驱动分析
- input子系统学习笔记五 按键驱动实例分析上
- input子系统学习笔记六 按键驱动实例分析下
- input子系统学习笔记六 按键驱动实例分析下
- input子系统学习笔记五 按键驱动实例分析上
- input子系统学习笔记五 按键驱动实例分析上
- Linux驱动学习笔记----------input输入子系统(基本概念与流程)
- input子系统学习笔记六 按键驱动实例分析下
- input子系统学习笔记 按键驱动实例分析下
- input子系统学习笔记六 按键驱动实例分析下
- input子系统学习笔记八 input子系统的详细分析
- input子系统学习笔记八 input子系统的详细分析
- input子系统学习笔记三 驱动的分层及设备驱动层实现原理
- input子系统学习 按键驱动实例分析上
- input子系统学习笔记七 handler处理器注册分析
- input子系统学习笔记七 handler处理器注册分析
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- linux input输入子系统分析《二》:s3c2440的ADC简单驱动实例分析