【感谢终结者投递本文】
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);
- }
参数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,
- .name= "evdev",
- .id_table = evdev_ids,
- };
id_talbe
C/C++代码
- static const struct input_device_id evdev_ids[] = {
- {
- .driver_info = 1 },
- { },
- };
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;
-
- 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);
- 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);
- evdev->exist = 1;
-
- 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;
-
- 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);
- if (error)
- goto err_free_evdev;
-
- 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;
- }
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;
- int error;
-
- if (i >= EVDEV_MINORS)
- return -ENODEV;
-
- 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;
-
- 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);
- 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++) {
- 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) {
-
-
-
-
- synchronize_rcu();
- }
- }
-
- out:
- mutex_unlock(&dev->mutex);
- return retval;
- }
至此,关于input子系统的内容已经全部结束!
原文 http://www.ourunix.org/post/298.html