板牙  
失败是什么?没有什么,只是更走近成功一步;而成功是走过了所有通向失败的路...只要你心够决!

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。这样的分层结构使得最上层的驱动不必关心下层是怎么实现的,而下层驱动又为多种型号同样功能的驱动提供了一个统一的接口。

posted on 2011-09-12 23:19  板牙  阅读(453)  评论(0编辑  收藏  举报