您的位置:首页 > 其它

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子系统的内容已经全部结束!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: