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

本文是摘自《Linux驱动开发入门与实战》第17章输入子系统设计的内容,在这里备份一下,以后查找方便。

第17章  输入子系统设计

本章将介绍Linux输入子系统的驱动开发。Linux的输入子系统不仅支持鼠标、键盘等常规输入设备,而且还支持蜂鸣器、触摸屏等设备。本章将对Linux输入子系统进行详细的分析。

17.1  input子系统入门

输入子系统又叫input子系统。其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现给应用程序。本节将从一个实例开始,介绍编写输入子系统驱动程序的方法。

17.1.1  简单的实例

本节将讲述一个简单的输入设备驱动实例。这个输入设备只有一个按键,按键被连接到一条中断线上,当按键被按下时,将产生一个中断,内核将检测到这个中断,并对其进行处理。该实例的代码如下:

    01  #include <asm/irq.h> 
    
02  #include <asm/io.h> 
    
03  static struct input_dev *button_dev;    /*输入设备结构体*/  
    
04  static irqreturn_t button_interrupt(int irq, void *dummy) /*中断处理函数*/  
    
05  {  
    
06      input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);      
    
/*向输入子系统报告产生按键事件*/  
    
07      input_sync(button_dev);         /*通知接收者,一个报告发送完毕*/  
    
08      return IRQ_HANDLED;  
    
09  }  
    
10  static int __init button_init(void/*加载函数*/  
    
11  {  
    
12      int error;  
    
13      if (request_irq(BUTTON_IRQ, button_interrupt, 0"button", NULL))  /*申请中断处理函数*/  
    
14      {  
    
15          /*申请失败,则打印出错信息*/  
    
16          printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_  
    irq);  
    
17          return -EBUSY;  
    
18      }  
    
19      button_dev = input_allocate_device();   /*分配一个设备结构体*/  
    
20      if (!button_dev)                        /*判断分配是否成功*/  
    
21      {  
    
22          printk(KERN_ERR "button.c: Not enough memory\n");  
    
23          error = -ENOMEM;  
    
24          goto err_free_irq;  
    
25      }  
    
26      button_dev->evbit[0= BIT_MASK(EV_KEY);    /*设置按键信息*/  
    
27      button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);  
    
28      error = input_register_device(button_dev);  /*注册一个输入设备*/  
    
29      if (error)  
    
30      {  
    
31          printk(KERN_ERR "button.c: Failed to register device\n");  
    
32          goto err_free_dev;  
    
33      }  
    
34      return 0;  
    
35      err_free_dev:                               /*以下是错误处理*/  
    
36          input_free_device(button_dev);  
    
37      err_free_irq:  
    
38          free_irq(BUTTON_IRQ, button_interrupt);  
    
39      return error;  
    
40  }  
    
41  static void __exit button_exit(void)            /*卸载函数*/  
    
42  {  
    
43      input_unregister_device(button_dev);        /*注销按键设备*/  
    
44      free_irq(BUTTON_IRQ, button_interrupt); /*释放按键占用的中断线*/  
    
45  }  
    
46  module_init(button_init);  
    
47  module_exit(button_exit);

这个实例程序代码比较简单,在初始化函数button_init()中注册了一个中断处理函数,然后调用 input_allocate_device()函数分配了一个input_dev结构体,并调用input_register_device()函数对 其进行了注册。在中断处理函数button_interrupt()中,实例将接收到的按键信息上报给input子系统。从而通过input子系统,向用 户态程序提供按键输入信息。

 

本实例采用了中断方式,除了中断相关的代码外,实例中包含了一些input子系统提供的函数,现对其中一些重要的函数进行分析。

第19行的input_allocate_device()函数在内存中为输入设备结构体分配一个空间,并对其主要的成员进行了初始化。驱动开发人员为了更深入的了解input子系统,应该对其代码有一点的认识,该函数的代码如下:

    struct input_dev *input_allocate_device(void)  
    {  
        
struct input_dev *dev;  
        dev 
= kzalloc(sizeof(struct input_dev), GFP_KERNEL);              
    
/*分配一个input_dev结构体,并初始化为0*/  
        
if (dev) {  
            dev
->dev.type = &input_dev_type;        /*初始化设备的类型*/  
            dev
->dev.class = &input_class;          /*设置为输入设备类*/  
            device_initialize(
&dev->dev);           /*初始化device结构*/  
            mutex_init(
&dev->mutex);                /*初始化互斥锁*/  
            spin_lock_init(
&dev->event_lock);       /*初始化事件自旋锁*/  
            INIT_LIST_HEAD(
&dev->h_list);           /*初始化链表*/  
            INIT_LIST_HEAD(
&dev->node);             /*初始化链表*/  
            __module_get(THIS_MODULE);              
/*模块引用技术加1*/  
        }  
        
return dev;  
    }
该函数返回一个指向input_dev类型的指针,该结构体是一个输入设备结构体,包含了输入设备的一些相关信息,如设备支持的按键码、设备的名称、设备支持的事件等。在本章用到这个结构体时,将对其进行详细介绍。此处将注意力集中在实例中的函数上。
 

17.1.2  注册函数input_register_device()(1)

button_init()函数中的28行调用了input_register_device()函数注册输入设备结构体。 input_register_device()函数是输入子系统核心(input core)提供的函数。该函数将input_dev结构体注册到输入子系统核心中,input_dev结构体必须由前面讲的 input_allocate_device()函数来分配。input_register_device()函数如果注册失败,必须调用 input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用 input_unregister_device()函数来注销输入设备结构体。

1.input_register_device()函数

input_register_device()函数的代码如下:

    01  int input_register_device(struct input_dev *dev)  
    
02  {  
    
03      static atomic_t input_no = ATOMIC_INIT(0);  
    
04      struct input_handler *handler;  
    
05      const char *path;  
    
06      int error;  
    
07      __set_bit(EV_SYN, dev->evbit);  
    
08      init_timer(&dev->timer);  
    
09      if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {  
    
10          dev->timer.data = (long) dev;  
    
11          dev->timer.function = input_repeat_key;  
    
12          dev->rep[REP_DELAY] = 250;  
    
13          dev->rep[REP_PERIOD] = 33;  
    
14      }  
    
15      if (!dev->getkeycode)  
    
16          dev->getkeycode = input_default_getkeycode;  
    
17      if (!dev->setkeycode)  
    
18          dev->setkeycode = input_default_setkeycode;  
    
19      dev_set_name(&dev->dev, "input%ld",  
    
20               (unsigned long) atomic_inc_return(&input_no) - 1);  
    
21      error = device_add(&dev->dev);  
    
22      if (error)  
    
23          return error;  
    
24      path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);  
    
25      printk(KERN_INFO "input: %s as %s\n",  
    
26          dev->name ? dev->name : "Unspecified device", path ? path:"N/A");  
    
27      kfree(path);  
    
28      error = mutex_lock_interruptible(&input_mutex);  
    
29      if (error) {  
    
30          device_del(&dev->dev);  
    
31          return error;  
    
32      }  
    
33      list_add_tail(&dev->node, &input_dev_list);  
    
34      list_for_each_entry(handler, &input_handler_list, node)  
    
35          input_attach_handler(dev, handler);  
    
36      input_wakeup_procfs_readers();  
    
37      mutex_unlock(&input_mutex);  
    
38      return 0;  
    
39  }

下面对该函数的主要代码进行分析。

第03~06行,定义了一些函数中将要用到的局部变量。

第07行,调用__set_bit()函数设置input_dev所支持的事件类型。事件类型由input_dev的evbit成员来表示,在这里将其EV_SYN置位,表示设备支持所有的事件。注意,一个设备可以支持一种或者多种事件类型。常用的事件类型如下:

    #define EV_SYN          0x00    /*表示设备支持所有的事件*/  
    
#define EV_KEY          0x01    /*键盘或者按键,表示一个键码*/  
    
#define EV_REL          0x02    /*鼠标设备,表示一个相对的光标位置结果*/  
    
#define EV_ABS          0x03    /*手写板产生的值,其是一个绝对整数值*/  
    
#define EV_MSC          0x04    /*其他类型*/  
    
#define EV_LED          0x11    /*LED灯设备*/  
    
#define EV_SND          0x12    /*蜂鸣器,输入声音*/  
    
#define EV_REP          0x14    /*允许重复按键类型*/  
    
#define EV_PWR          0x16    /*电源管理事件*/

第08行,初始化一个timer定时器,这个定时器是为处理重复击键而定义的。

第09~14行,如果dev->rep[REP_DELAY]和dev->rep[REP_PERIOD]没有设值,则将其赋默认值,这主要是为自动处理重复按键定义的。

第15~18行,检查getkeycode()函数和setkeycode()函数是否被定义,如果没定义,则使用默认的处理函数,这两个函数为 input_default_getkeycode()和input_default_setkeycode()。 input_default_getkeycode()函数用来得到指定位置的键值。input_default_setkeycode()函数用来设置 键值。

第19行,设置input_dev中的device的名字,名字以input0、input1、input2、input3、input4等的形式出现在sysfs文件系统中。

第21行,使用device_add()函数将input_dev包含的device结构注册到Linux设备模型中,并可以在sysfs文件系统中表现出来。

第24~27行,打印设备的路径,输出调试信息。

第33行,调用list_add_tail()函数将input_dev加入input_dev_list链表中,input_dev_list链表中包含了系统中所有的input_dev设备。

第34~35行,调用了input_attach_handler()函数,该函数将在下面单独解释。

 

 

17.1.2  注册函数input_register_device()(2)

2.input_attach_handler()函数

input_attach_handler()函数用来匹配input_dev和handler,只有匹配成功,才能进行下一步的关联操作。input_attach_handler()函数的代码如下:

    01  static int input_attach_handler(struct input_dev *dev, 
    
struct   input_handler *handler)  
    
02  {  
    
03      const struct input_device_id *id;       /*输入设备的指针*/  
    
04      int error;  
    
05      if (handler->blacklist && input_match_device(handler->blacklist,dev))  
    
06          return -ENODEV;                     /*设备和处理函数之间的匹配*/  
    
07      id = input_match_device(handler->id_table, dev);  
    
08      if (!id)  
    
09          return -ENODEV;  
    
10      error = handler->connect(handler, dev, id);/*连接设备和处理函数*/  
    
11      if (error && error != -ENODEV)  
    
12          printk(KERN_ERR  
    
13              "input: failed to attach handler %s to device %s, "  
    
14              "error: %d\n",  
    
15              handler->name, kobject_name(&dev->dev.kobj), error);  
    
16      return error;  
    
17  }

下面对该函数进行简要的分析。

第03行,定义了一个input_device_id的指针。该结构体表示设备的标识,标识中存储了设备的信息,其定义如下:

    struct input_device_id {  
     
        kernel_ulong_t flags;           
/*标志信息*/  
        __u16 bustype;                  
/*总线类型*/  
        __u16 vendor;                   
/*制造商ID*/  
        __u16 product;                  
/*产品ID*/  
        __u16 version;                  
/*版本号*/  
        ...  
        kernel_ulong_t driver_info;     
/*驱动额外的信息*/  
    };

第05行,首先判断handle的blacklist是否被赋值,如果被赋值,则匹配blacklist中的数据跟dev->id的数据是否 匹配。blacklist是一个input_device_id*的类型,其指向input_device_ids的一个表,这个表中存放了驱动程序应该 忽略的设备。即使在id_table中找到支持的项,也应该忽略这种设备。

第07~09行,调用input_match_device()函数匹配handle->>id_table和dev->id中 的数据。如果不成功则返回。handle->id_table也是一个input_device_id类型的指针,其表示驱动支持的设备列表。

第10行,如果匹配成功,则调用handler->connect()函数将handler与input_dev连接起来。

3.input_match_device ()函数

input_match_device ()函数用来与input_dev和handler进行匹配。handler的id_table表中定义了其支持的input_dev设备。该函数的代码如下:


    01  static const struct input_device_id *input_match_device(const struct  
        input_device_id 
*id,  
    
02                              struct input_dev *dev)  
    
03  {  
    
04      int i;  
    
05      for (; id->flags || id->driver_info; id++) {  
    
06          if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)  
    
07              if (id->bustype != dev->id.bustype)  
    
08                  continue;  
    
09          if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)  
    
10              if (id->vendor != dev->id.vendor)  
    
11                  continue;  
    
12          if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)  
    
13              if (id->product != dev->id.product)  
    
14                  continue;  
    
15          if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)  
    
16              if (id->version != dev->id.version)  
    
17                  continue;  
    
18          MATCH_BIT(evbit,  EV_MAX);  
    
19          MATCH_BIT(keybit, KEY_MAX);  
    
20          MATCH_BIT(relbit, REL_MAX);  
    
21          MATCH_BIT(absbit, ABS_MAX);  
    
22          MATCH_BIT(mscbit, MSC_MAX);  
    
23          MATCH_BIT(ledbit, LED_MAX);  
    
24          MATCH_BIT(sndbit, SND_MAX);  
    
25          MATCH_BIT(ffbit,  FF_MAX);  
    
26          MATCH_BIT(swbit,  SW_MAX);  
    
27          return id;  
    
28      }  
    
29      return NULL;  
    
30  }

下面对该函数进行简要的解释。

第04行声明一个局部变量i,用于循环。

第05行,是一个for循环,用来匹配id和dev->id中的信息,只要有一项相同则返回。

第06~08行,用来匹配总线类型。id->flags中定义了要匹配的项,其中INPUT_DEVICE_ID_MATCH_BUS如果没有设置,则比较input device和input handler的总线类型。

第09~11行,匹配设备厂商的信息。

第12~14行,分别匹配设备号的信息。

第18~26行,使用MATCH_BIT匹配项。如果id->flags定义的类型匹配成功,或者id->flags没有定义,才会进入到MATCH_BIT的匹配项。MATCH_BIT宏的定义如下:

    #define MATCH_BIT(bit, max) \  
            
for (i = 0; i < BITS_TO_LONGS(max); i++) \  
                
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \  
                    
break; \  
            
if (i != BITS_TO_LONGS(max)) \  
                
continue;

从MATCH_BIT宏的定义可以看出。只有当iput device和input handler的ID成员在evbit、keybit、… swbit项相同才会匹配成功。而且匹配的顺序是从evbit、keybit到swbit。只要有一项不同,就会循环到ID中的下一项进行比较。

简而言之,注册input device的过程就是为input device设置默认值,并将其挂以input_dev_list。与挂载在input_handler_list中的handler相匹配。如果匹配成功,就会调用handler的connect函数。

 

17.1.3  向子系统报告事件(1)

在17.1.1节button_interrupt()函数的06行,调用了input_report_key()函数向输入子系统报告发生的事 件,这里就是一个按键事件。在button_interrupt()中断函数中,不需要考虑重复按键的重复点击情 况,input_report_key()函数会自动检查这个问题,并报告一次事件给输入子系统。该函数的代码如下:

    01  static inline void input_report_key(struct input_dev *dev,
    unsigned 
int     code, int value)  
    
02  {  
    
03      input_event(dev, EV_KEY, code, !!value);  
    
04  }

该函数的第1个参数是产生事件的输入设备,第2个参数是产生的事件,第3个参数是事件的值。需要注意的是,第2个参数可以取类似BTN_0、 BTN_1、BTN_LEFT、BTN_RIGHT等值,这些键值被定义在include/linux/input.h文件中。当第2个参数为按键时,第 3个参数表示按键的状态,value值为0表示按键释放,非0表示按键按下。

1.input_report_key()函数

在input_report_key()函数中正在起作用的函数是input_event()函数,该函数用来向输入子系统报告输入设备产生的事件,这个函数非常重要,它的代码如下:

    01  void input_event(struct input_dev *dev,  
    
02           unsigned int type, unsigned int code, int value)  
    
03  {  
    
04      unsigned long flags;  
    
05      if (is_event_supported(type, dev->evbit, EV_MAX)) {  
    
06          spin_lock_irqsave(&dev->event_lock, flags);  
    
07          add_input_randomness(type, code, value);  
    
08          input_handle_event(dev, type, code, value);  
    
09          spin_unlock_irqrestore(&dev->event_lock, flags);  
    
10      }  
    
11  }

该函数第1个参数是input_device设备,第2个参数是事件的类型,可以取EV_KEY、EV_REL、EV_ABS等值,在上面的按键时 间报告函数input_report_key()中传递的就是EV_KEY值,表示发生一个按键事件。第3、4个函数与 input_report_key()函数的参数相同,下面对这个函数进行简要的分析。

第04行,调用is_event_supported()函数检查输入设备是否支持该事件。该函数的代码如下:

    01  static inline int is_event_supported(unsigned int code,  
    
02                       unsigned long *bm, unsigned int max)  
    
03  {  
    
04      return code <= max && test_bit(code, bm);  
    
05  }

该函数检查input_dev.evbit中的相应位是否设置,如果设置返回1,否则返回0。每一种类型的事件都在input_dev.evbit 中用一个位来表示,构成一个位图,如果某位为1,表示该输入设备支持这类事件,如果为0,表示输入设备不支持这类事件。如图17.1所示,表示各位支持的 事件,其中省略了一些事件类型,目前Linux支持十多种事件类型,所以用一个long型变量就可以全部表示了。

 



(点击查看大图)图17.1  input_dev.evbit支持的事件表示方法
需要注意的是,这里可以回顾一下17.1.1节button_init()函数的第26行,如下所示。
26      button_dev->evbit[0= BIT_MASK(EV_KEY);        /*设置按键信息*/

该行就是设置输入设备button_dev所支持的事件类型,BIT_MASK是用来构造input_dev.evbit这个位图的宏,宏代码如下:

#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG))

 

回到17.1.1节input_event()函数的第06行,调用spin_lock_irqsave()函数对将事件锁锁定。

第07行,add_input_randomness()函数对事件发送没有一点用处,只是用来对随机数熵池增加一些贡献,因为按键输入是一种随机事件,所以对熵池是有贡献的。

第08行,调用input_handle_event()函数来继续输入子系统的相关模块发送数据。该函数较为复杂,下面单独进行分析。

2.input_handle_event()函数

input_handle_event()函数向输入子系统传送事件信息。第1个参数是输入设备input_dev,第2个参数是事件的类型,第3个参数是键码,第4个参数是键值。该函数的代码如下:

 

 

    01  static void input_handle_event(struct input_dev *dev,  
    
02                     unsigned int type, unsigned int code, int value)  
    
03  {  
    
04      int disposition = INPUT_IGNORE_EVENT;  
    
05      switch (type) {  
    
06      case EV_SYN:  
    
07          switch (code) {  
    
08          case SYN_CONFIG:  
    
09              disposition = INPUT_PASS_TO_ALL;  
    
10              break;  
    
11          case SYN_REPORT:  
    
12              if (!dev->sync) {  
    
13                  dev->sync = 1;  
    
14                  disposition = INPUT_PASS_TO_HANDLERS;  
    
15              }  
    
16              break;  
    
17          }  
    
18          break;  
    
19      case EV_KEY:  
    
20          if (is_event_supported(code, dev->keybit, KEY_MAX) &&  
    
21              !!test_bit(code, dev->key) != value) {  
    
22              if (value != 2) {  
    
23                  __change_bit(code, dev->key);  
    
24                  if (value)  
    
25                      input_start_autorepeat(dev, code);  
    
26              }  
    
27              disposition = INPUT_PASS_TO_HANDLERS;  
    
28          }  
    
29          break;  
    
30      case EV_SW:  
    
31          if (is_event_supported(code, dev->swbit, SW_MAX) &&  
    
32              !!test_bit(code, dev->sw) != value) {  
    
33              __change_bit(code, dev->sw);  
    
34              disposition = INPUT_PASS_TO_HANDLERS;  
    
35          }  
    
36          break;  
    
37      case EV_ABS:  
    
38          if (is_event_supported(code, dev->absbit, ABS_MAX)) {  
    
39              value = input_defuzz_abs_event(value,  
    
40                      dev->abs[code], dev->absfuzz[code]);  
    
41              if (dev->abs[code] != value) {  
    
42                  dev->abs[code] = value;  
    
43                  disposition = INPUT_PASS_TO_HANDLERS;  
    
44              }  
    
45          }  
    
46          break;  
    
47      case EV_REL:  
    
48          if (is_event_supported(code, dev->relbit, REL_MAX) && value)  
    
49              disposition = INPUT_PASS_TO_HANDLERS;  
    
50          break;  
    
51      case EV_MSC:  
    
52          if (is_event_supported(code, dev->mscbit, MSC_MAX))  
    
53              disposition = INPUT_PASS_TO_ALL;  
    
54          break;  
    
55      case EV_LED:  
    
56          if (is_event_supported(code, dev->ledbit, LED_MAX) &&  
    
57              !!test_bit(code, dev->led) != value) {  
    
58              __change_bit(code, dev->led);  
    
59              disposition = INPUT_PASS_TO_ALL;  
    
60          }  
    
61          break;  
    
62      case EV_SND:  
    
63          if (is_event_supported(code, dev->sndbit, SND_MAX)) {  
    
64              if (!!test_bit(code, dev->snd) != !!value)  
    
65                  __change_bit(code, dev->snd);  
    
66              disposition = INPUT_PASS_TO_ALL;  
    
67          }  
    
68          break;  
    
69      case EV_REP:  
    
70          if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {  
    
71              dev->rep[code] = value;  
    
72              disposition = INPUT_PASS_TO_ALL;  
    
73          }  
    
74          break;  
    
75      case EV_FF:  
    
76          if (value >= 0)  
    
77              disposition = INPUT_PASS_TO_ALL;  
    
78          break;  
    
79      case EV_PWR:  
    
80          disposition = INPUT_PASS_TO_ALL;  
    
81          break;  
    
82      }  
    
83      if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)  
    
84          dev->sync = 0;  
    
85      if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)  
    
86          dev->event(dev, type, code, value);  
    
87      if (disposition & INPUT_PASS_TO_HANDLERS)  
    
88          input_pass_event(dev, type, code, value);  
    
89  }

 

17.1.3  向子系统报告事件(2)

浏览一下该函数的大部分代码,主要由一个switch结构组成。该结构用来对不同的事件类型,分别处理。其中case语句包含了EV_SYN、 EV_KEY、EV_SW、EV_SW、EV_SND等事件类型。在这么多事件中,本例只要关注EV_KEY事件,因为本节的实例发送的是键盘事件。其 实,只要对一个事件的处理过程了解后,对其他事件的处理过程也就清楚了。下面对input_handle_event()函数进行简要的介绍:

第04行,定义了一个disposition变量,该变量表示使用什么样的方式处理事件。此处初始化为INPUT_IGNORE_EVENT,表示如果后面没有对该变量重新赋值,则忽略这个事件。

第05~82行是一个重要的switch结构,该结构中对各种事件进行了一些必要的检查,并设置了相应的disposition变量的值。其中只需要关心第19~29行的代码    即可。

第19~29行,对EV_KEY事件进行处理。第20行,调用is_event_supported()函数判断是否支持该按键。第21行,调用 test_bit()函数来测试按键状态是否改变。第23行,调用__change_bit()函数改变键的状态。第25行,处理重复按键的情况。第27 行,将disposition变量设置为INPUT_PASS_TO_HANDLERS,表示事件需要handler来处理。disposition的取 值有如下几种:

    #define INPUT_IGNORE_EVENT  0  
    
#define INPUT_PASS_TO_HANDLERS  1  
    
#define INPUT_PASS_TO_DEVICE    2  
    
#define INPUT_PASS_TO_ALL(INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)

INPUT_IGNORE_EVENT表示忽略事件,不对其进行处理。INPUT_PASS_ TO_HANDLERS表示将事件交给handler处理。INPUT_PASS_TO_DEVICE表示将事件          交给input_dev处理。INPUT_PASS_TO_ALL表示将事件交给handler和input_dev共同处理。

第83、84行,处理EV_SYN事件,这里并不对其进行关心。

第85、86行,首先判断disposition等于INPUT_PASS_TO_DEVICE,然后判断dev->event是否对其指定 了一个处理函数,如果这些条件都满足,则调用自定义的dev->event()函数处理事件。有些事件是发送给设备,而不是发送给handler处 理的。event()函数用来向输入子系统报告一个将要发送给设备的事件,例如让LED灯点亮事件、蜂鸣器鸣叫事件等。当事件报告给输入子系统后,就要求 设备处理这个事件。

第87、88行,如果事件需要handler处理,则调用input_pass_event()函数,该函数将在下面详细解释。

3.input_pass_event()函数

input_pass_event()函数将事件传递到合适的函数,然后对其进行处理,该函数的代码如下:

    01  static void input_pass_event(struct input_dev *dev,  
    
02                   unsigned int type, unsigned int code, int value)  
    
03  {  
    
04      struct input_handle *handle;  
    
05      rcu_read_lock();  
    
06      handle = rcu_dereference(dev->grab);  
    
07      if (handle)  
    
08          handle->handler->event(handle, type, code, value);  
    
09      else  
    
10          list_for_each_entry_rcu(handle, &dev->h_list, d_node)  
    
11              if (handle->open)  
    
12                  handle->handler->event(handle,  
    
13                              type, code, value);  
    
14      rcu_read_unlock();  
    
15  }

下面对该函数进行简要的分析。

第04行,分配一个input_handle结构的指针。

第06行,得到dev->grab的指针。grab是强制为input device的handler,这时要调用handler的event函数。

第10~13行,表示如果没有为input device强制指定handler,即为grab赋值,就会遍历input device->h_list上的handle成员。如果该handle被打开,表示该设备已经被一个用户进程使用。就会调用与输入设备对应的 handler的event()函数。注意,只有在handle被打开的情况下才会接收到事件,这就是说,只有设备被用户程序使用时,才有必要向用户空间 导出信息。事件的处理过程如图17.2所示。

 

  (点击查看大图)图17.2  event执行过程
 

 

 

 

 

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