您的位置:首页 > 其它

输入子系统设计(4)

2011-09-12 23:19 169 查看
17.4 evdev输入事件驱动分析

evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev 输入事件驱动从底层接收事件信息,将其反映到sys文件系统中,用户程序通过对sys文件系统的操作,就能够达到处理事件的能力。下面先对evdev的初 始化进行简要的分析。

17.4.1 evdev的初始化

evdev以模块的方式被组织在内核中,与其他模块一样,也具有初始化函数和卸载函数。evdev的初始化主要完成一些注册工作,使内核认识evdev的存在。

1.evdev_init()初始化函数

evdev模块定义在/drivers/input/evdev.c文件中,该模块的初始化函数是evdev_init()。在初始化函数中注册了一个evdev_handler结构体,用来对一些通用的抽象事件进行统一处理,该函数的代码如下:

01 static int __init evdev_init(void)
02 {
03 return input_register_handler(&evdev_handler);
04 }
第03行,调用input_register_handler()函数注册了evdev_handler事件处理器,input_register_handler()函数在前面已经详细解释过,这里将对其参数evdev_handler进行分析,其定义如下:

01 static struct input_handler evdev_handler = {
02 .event = evdev_event,
03 .connect = evdev_connect,
04 .disconnect = evdev_disconnect,
05 .fops = &evdev_fops,
06 .minor = EVDEV_MINOR_BASE,
07 .name = "evdev",
08 .id_table = evdev_ids,
09 };
第06行,定义了minor为EVDEV_MINOR_BASE(64)。因为一个handler可以处理32个设备,所以evdev_handler所能处理的设备文件范围为(13,64)~(13,64+32),其中13是所有输入设备的主设备号。

第08行,定义了id_table结构。回忆前面几节的内容,由input_attach_handler()函数可知,input_dev与 handler匹配成功的关键,在于handler中的blacklist和id_talbe.。Evdev_handler只定义了id_table, 其定义如下:

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()函数,对该函数的介绍如下:

2.evdev_connect()函数

evdev_handler的第3行定义了evdev_connect()函数。evdev_connect()函数主要用来连接input_dev和input_handler,这样事件的流通链才能建立。流通链建立后,事件才知道被谁处理,或者处理后将向谁返回结果。

01 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
02 const struct input_device_id *id)
03 {
04 struct evdev *evdev;
05 int minor;
06 int error;
07 for (minor = 0; minor < EVDEV_MINORS; minor++)
08 if (!evdev_table[minor])
09 break;
10 if (minor == EVDEV_MINORS) {
11 printk(KERN_ERR "evdev: no more free evdev devices\n");
12 return -ENFILE;
13 }
14 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
15 if (!evdev)
16 return -ENOMEM;
17 INIT_LIST_HEAD(&evdev->client_list);
18 spin_lock_init(&evdev->client_lock);
19 mutex_init(&evdev->mutex);
20 init_waitqueue_head(&evdev->wait);
21 snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
22 evdev->exist = 1;
23 evdev->minorminor = minor;
24 evdev->handle.dev = input_get_device(dev);
25 evdev->handle.name = evdev->name;
26 evdev->handle.handler = handler;
27 evdev->handle.private = evdev;
28 dev_set_name(&evdev->dev, evdev->name);
29 evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
30 evdev->dev.class = &input_class;
31 evdev->dev.parent = &dev->dev;
32 evdev->dev.release = evdev_free;
33 device_initialize(&evdev->dev);
34 error = input_register_handle(&evdev->handle);
35 if (error)
36 goto err_free_evdev;
37 error = evdev_install_chrdev(evdev);
38 if (error)
39 goto err_unregister_handle;
40 error = device_add(&evdev->dev);
41 if (error)
42 goto err_cleanup_evdev;
43 return 0;
44 err_cleanup_evdev:
45 evdev_cleanup(evdev);
46 err_unregister_handle:
47 input_unregister_handle(&evdev->handle);
48 err_free_evdev:
49 put_device(&evdev->dev);
50 return error;
51 }
下面对该函数进行简要的分析。

第04~06行,声明了一些必要的局部变量。

第07~13行,for循环中的EVDEV_MINORS定义为32,表示evdev_handler所表示的32个设备文件。 evdev_talbe是一个struct evdev类型的数组,struct evdev是模块使用的封装结构,与具体的输入设备有关。第08行,这一段代码的在evdev_talbe找到为空的那一项,当找到为空的一项,便结束 for循环。这时,minor就是数组中第一项为空的序号。第10到13行,如果没有空闲的表项,则退出。

第14~16行,分配一个struct evdev的空间,如果分配失败,则退出。

第17~20行,对分配的evdev结构进行初始化,主要对链表、互斥锁和等待队列做必要的初始化。在evdev中,封装了一个handle结构, 这个结构与handler是不同的。可以把handle看成是handler和input device的信息集合体,这个结构用来联系匹配成功的handler和input device。

第21行,对evdev命一个名字,这个设备的名字形如eventx,例如event1、event2和event3等。最大有32个设备,这个设备将在/dev/input/目录下显示。

第23~27行,对evdev进行必要的初始化。其中,主要对handle进行初始化,这些初始化的目的是使input_dev和input_handler联系起来。

第28~33行,在设备驱动模型中注册一个evdev->dev的设备,并初始化一个evdev->dev的设备。这里,使 evdev->dev所属的类指向input_class。这样在/sysfs中创建的设备目录就会在/sys/class/input/下显示。

第34行,调用input_register_handle()函数注册一个input_handle结构体。

第37行,注册handle,如果成功,那么调用evdev_install_chrdev将evdev_table的minor项指向evdev.。

第40行,将evdev->device注册到sysfs文件系统中。

第41~50行,进行一些必要的错误处理。

17.4.2 evdev设备的打开

用户程序通过输入子系统创建的设备结点函数open()、read()和write()等,打开和读写输入设备。创建的设备结点显示在/dev/input/目录下,由eventx表示。

1.evdev_open()函数

对主设备号为INPUT_MAJOR的设备结点进行操作,会将操作集转换成handler的操作集。在evdev_handler中定义了一个fops集合,被赋值为evdev_fops的指针。evdev_fops就是设备结点的操作集,其定义代码如下:

01 static const struct file_operations evdev_fops = {
02 .owner = THIS_MODULE,
03 .read = evdev_read,
04 .write = evdev_write,
05 .poll = evdev_poll,
06 .open = evdev_open,
07 .release = evdev_release,
08 .unlocked_ioctl = evdev_ioctl,
09 .fasync = evdev_fasync,
10 .flush = evdev_flush
11 };
evdev_fops结构体是一个file_operations的类型。当用户层调用类似代码open("/dev/input/event1",O_RDONLY)函数打开设备结点时,会调用evdev_fops中的evdev_read()函数,该函数的代码如下:

01 static int evdev_open(struct inode *inode, struct file *file)
02 {
03 struct evdev *evdev;
04 struct evdev_client *client;
05 int i = iminor(inode) - EVDEV_MINOR_BASE;
06 int error;
07 if (i >= EVDEV_MINORS)
08 return -ENODEV;
09 error = mutex_lock_interruptible(&evdev_table_mutex);
10 if (error)
11 return error;
12 evdev = evdev_table[i];
13 if (evdev)
14 get_device(&evdev->dev);
15 mutex_unlock(&evdev_table_mutex);
16 if (!evdev)
17 return -ENODEV;
18 client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
19 if (!client) {
20 error = -ENOMEM;
21 goto err_put_evdev;
22 }
23 spin_lock_init(&client->buffer_lock);
24 client->evdevevdev = evdev;
25 evdev_attach_client(evdev, client);
26 error = evdev_open_device(evdev);
27 if (error)
28 goto err_free_client;
29 file->private_data = client;
30 return 0;
31 err_free_client:
32 evdev_detach_client(evdev, client);
33 kfree(client);
34 err_put_evdev:
35 put_device(&evdev->dev);
36 return error;
37 }
下面对该函数进行简要的分析。

第03、04行,定义了一些局部变量。

第05行,iminor(inode) - EVDEV_MINOR_BASE得到了在evdev_table[]中的序号,赋给变量i。

第09~17行,将数组evdev_table[]中对应的evdev取出,并调用get_device()增加引用计数。

第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_oper(struct inode *inode, struct file *file)
第26行,调用evdev_open_device()函数,打开输入设备。该函数的具体功能将在下面详细介绍。

第31~37行,进行一些错误处理。

2.evdev_open_device()函数

evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数 先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用input_open_device()函数打开设备。 evdev_open_device()函数的代码如下:

01 static int evdev_open_device(struct evdev *evdev)
02 {
03 int retval;
04 retval = mutex_lock_interruptible(&evdev->mutex);
05 if (retval)
06 return retval;
07 if (!evdev->exist)
08 retval = -ENODEV;
09 else if (!evdev->open++) {
10 retval = input_open_device(&evdev->handle);
11 if (retval)
12 evdev->open--;
13 }
14 mutex_unlock(&evdev->mutex);
15 return retval;
16 }
下面对该函数进行简要的分析。

第07行,判断该设备是否存在,如果不存在则返回设备不存在。

第09~12行,如果evdev是第一次打开,就会调用input_open_device()打开evdev对应的handle;否则不做任何操作返回。

3.input_open_device()函数

在这个函数中,递增handle的打开计数。如果是第一次打开,则调用input_dev的open()函数。

01 int input_open_device(struct input_handle *handle)
02 {
03 struct input_dev *dev = handle->dev;
04 int retval;
05 handle->open++;
06 ...
07 if (!dev->users++ && dev->open)
08 retval = dev->open(dev);
09 ...
10 return retval;
11 }
17.5 小结

在本章中,分析了整个输入子系统的架构。Linux设备驱动采用了分层的模式,从最下层的设备模型到设备、驱动、总线再到input子系统最后到 input device。这样的分层结构使得最上层的驱动不必关心下层是怎么实现的,而下层驱动又为多种型号同样功能的驱动提供了一个统一的接口。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: