输入子系统_架构流程及详解

 

较USB和LCD,输入子系统相对要难一些,重在理解。

一、输入子系统的结构流程(之后再详细介绍)

首先必须知道内核中有很多事件处理器(evdev, keyboard, ts 等等),且每个处理器下的handler会放入list_head这个链表。

 

input_subsystem总体流程如下:

(1)  注册设备register_input_device:该函数会在list_head链表中查找与input_dev匹配的handler(可能会成功匹配多个handler,后文详细介绍实现过程),匹配成功就会建立相关联系

(2)  用户通过cat /dev/event*等连续不间断的访问上报数据存放的缓冲区,直到有内容就读取(后文详细介绍实现过程)

(3)  上报事件input_event:该函数会依据device和handler匹配成功建立的相关联系,将得到的数据上报到一个用户可以直接访问的环形缓冲区(循环队列),数据将在缓冲区内供用户将访问。

 

二、输入子系统的结构详细介绍:

 

驱动一开始会input_register_device,里边会去找与input_dev相匹配的input_handler,在事先了解几个结构体是必须的。

input_dev 结构体如下:(其中缩进的很重要、很常用)

[csharp] view plain copy print?

  1. struct input_dev {    
  2.     const char *name;     //名称                                
  3.     const char *phys;  //设备在系统中的物理路径  
  4.     const char *uniq;  //设备唯一识别符  
  5.     struct input_id id; //设备ID,包含总线ID(PCI、USB)、厂商ID,与input_handler匹配的时会用到    
  6.             <span style="color:#cc33cc;"//在input.h中定义了不同的宏,表示不同的意义  
  7.              unsigned long evbit[BITS_TO_LONGS(EV_CNT)];     //支持的所有事件类型  
  8.              unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];   //支持的键盘事件    
  9.              unsigned long relbit[BITS_TO_LONGS(REL_CNT)];   //支持的鼠标相对值事件    
  10.              unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];   //支持的鼠标绝对值事件    
  11.              unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];   //支持的其它事件类型    
  12.              unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];   //支持的LED灯事件    
  13.              unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];   //支持的声效事件   
  14.              unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];     //支持的力反馈事件    
  15.              unsigned long swbit[BITS_TO_LONGS(SW_CNT)];     //支持的开关事件       </span>  
  16.     unsigned int keycodemax;  //keycode表的大小  
  17.     unsigned int keycodesize;  //keycode表中元素个数  
  18.     void *keycode;  //设备的键盘表  
  19.     int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);//配置keycode表    
  20.     int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);//获取keycode表     
  21.     
  22.     unsigned int repeat_key;//保存上一个键值    
  23.     struct timer_list timer;    
  24.     
  25.     int sync;    
  26.     
  27.     int abs[ABS_MAX + 1];             //绝对坐标上报的当前值    
  28.     int rep[REP_MAX + 1];             //这个参数主要是处理重复按键,后面讲    
  29.     unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //按键有两种状态,按下和抬起,这个字段就是记录这两个状态。    
  30.     unsigned long led[BITS_TO_LONGS(LED_CNT)];    
  31.     unsigned long snd[BITS_TO_LONGS(SND_CNT)];    
  32.     unsigned long sw[BITS_TO_LONGS(SW_CNT)];    
  33.     
  34.     int absmax[ABS_MAX + 1];           //绝对坐标的最大值                                       
  35.     int absmin[ABS_MAX + 1];       //绝对坐标的最小值    
  36.     int absfuzz[ABS_MAX + 1];              
  37.     int absflat[ABS_MAX + 1];              
  38.     //操作接口  
  39.     int (*open)(struct input_dev *dev);    
  40.     void (*close)(struct input_dev *dev);    
  41.     int (*flush)(struct input_dev *dev, struct file *file);    
  42.     int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);    
  43.     
  44.     struct input_handle *grab;         //当前使用的handle    
  45.     
  46.     struct device dev;    
  47.     
  48.     struct list_head    h_list;    //h_list是一个链表头,用来把handle挂载在这个上    
  49.     struct list_head    node;      //这个node是用来连到input_dev_list上的    
  50. };   

 

input_handler结构如下:

[csharp] view plain copy print?

  1. struct input_handler {    
  2.     
  3.     void *private;    
  4.     
  5.     void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);    
  6.     int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);    
  7.     void (*disconnect)(struct input_handle *handle);    
  8.     void (*start)(struct input_handle *handle);    
  9.     
  10.     const struct file_operations *fops;    
  11.     int minor;     //每个处理器(evdev\keyboard..)都有自己的handler,对应不同的次设备号,input_open_file依  
  12.                            据此设备号找到对应的handler
  13.     const char *name;    
  14.     
  15.     const struct input_device_id *id_table;  //每个处理器都有自己的id_table,处理器据此与input_dev中的
  16.                                                                     input_id匹配,匹配成功的处理器会与该dev建立联系
  17.     const struct input_device_id *blacklist;    
  18.     
  19.     struct list_head    h_list;    //h_list是一个链表头,用来把handle挂载在这个上    
  20.     struct list_head    node;      //这个node是用来连到input_handler_list上的    
  21. };    

 

 

了解了这两个结构体后,看input_register_device函数的完成过程,这个函数从名字上看注册一个设备,意思就是让我们写的驱动程序能被操作系统识别,所以这个函数一定能够和那8个input_handler中某个创建联系:

[csharp] view plain copy print?

  1. int input_register_device(struct input_dev *dev)    
  2. {    
  3.     static atomic_t input_no = ATOMIC_INIT(0);    
  4.     struct input_handler *handler;    
  5.     const char *path;    
  6.     int error;    
  7.     
  8.     __set_bit(EV_SYN, dev->evbit);    
  9.     
  10.     /*  
  11.      * If delay and period are pre-set by the driver, then autorepeating  
  12.      * is handled by the driver itself and we don't do it in input.c.  
  13.      */    
  14.     
  15.     init_timer(&dev->timer);    
  16.     /*   
  17.      *rep主要是处理重复按键,如果没有定义dev->rep[REP_DELAY]和dev->rep[REP_PERIOD],  
  18.      *则将其赋值为默认值。dev->rep[REP_DELAY]是指第一次按下多久算一次,这里是250ms,  
  19.      *dev->rep[REP_PERIOD]指如果按键没有被抬起,每33ms算一次。  
  20.      */    
  21.     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {    
  22.         dev->timer.data = (long) dev;    
  23.         dev->timer.function = input_repeat_key;    
  24.         dev->rep[REP_DELAY] = 250;    
  25.         dev->rep[REP_PERIOD] = 33;    
  26.     }    
  27.     /*如果dev没有定义getkeycode和setkeycode,则赋默认值。他们的作用一个是获得键的扫描码,一个是设置键的扫描码*/    
  28.     if (!dev->getkeycode)    
  29.         dev->getkeycode = input_default_getkeycode;    
  30.     
  31.     if (!dev->setkeycode)    
  32.         dev->setkeycode = input_default_setkeycode;    
  33.     
  34.     dev_set_name(&dev->dev, "input%ld",    
  35.              (unsigned long) atomic_inc_return(&input_no) - 1);    
  36.     /*将input_dev封装的dev注册到sysfs*/    
  37.     error = device_add(&dev->dev);    
  38.     if (error)    
  39.         return error;    
  40.     
  41.     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);    
  42.     printk(KERN_INFO "input: %s as %s/n",    
  43.         dev->name ? dev->name : "Unspecified device", path ? path : "N/A");    
  44.     kfree(path);    
  45.     
  46.     error = mutex_lock_interruptible(&input_mutex);    
  47.     if (error) {    
  48.         device_del(&dev->dev);    
  49.         return error;    
  50.     }    
  51.     /*将input_dev挂在input_dev_list上*/    
  52.     list_add_tail(&dev->node, &input_dev_list);    
  53.     /*这个函数网上都说很简单,但我还是没看懂,只知道它是个for循环,猜测循环是为了遍历8个handler*/    
  54.     list_for_each_entry(handler, &input_handler_list, node)  
  55.     /*通过其中的match函数遍历每个handler的id,如果成功匹配就调用该handler下的connect函数*/ 
  56.         input_attach_handler(dev, handler);     //这两个函数就是遍历所有handler,找到能和id匹配的handler
  57.     
  58.     input_wakeup_procfs_readers();    
  59.     
  60.     mutex_unlock(&input_mutex);    
  61.     
  62.     return 0;    
  63. }    

该函数主要是通过input_attach_handler()函数来匹配input_dev和input_handle并建立联系

 

input_dev与input_handler的匹配由input_attach_handler决定所以继续跟踪input_attach_handler:

[csharp] view plain copy print?

  1. static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)    
  2. {    
  3.     const struct input_device_id *id;    
  4.     int error;    
  5.     /*handler有一个黑名单,如果存在黑名单,并且这个id匹配就退出*/    
  6.     if (handler->blacklist && input_match_device(handler->blacklist, dev))    
  7.         return -ENODEV;    
  8.     /*匹配id,判断某handler下的id是否与dev相匹配,若匹配就将此handler和dev通过connect建立联系*/    
  9.     id = input_match_device(handler->id_table, dev);    
  10.     if (!id)    
  11.         return -ENODEV;    
  12.     /*如果匹配,则调用具体的handler的connect函数*/    
  13.     error = handler->connect(handler, dev, id);    
  14.     if (error && error != -ENODEV)    
  15.         printk(KERN_ERR    
  16.             "input: failed to attach handler %s to device %s, "    
  17.             "error: %d/n",    
  18.             handler->name, kobject_name(&dev->dev.kobj), error);    
  19.     
  20.     return error;    
  21. }    

如果匹配成功将会调用响应handler的connect函数(handler->connect),下面看能否匹配成功。

 

能否匹配成功由的input_match_device决定,依据handler->id_table和dev,若handler->id和input_dev匹配,表示dev由能匹配handler来处理:

[csharp] view plain copy print?

  1. static const struct input_device_id *input_match_device(const struct input_device_id *id,    
  2.                             struct input_dev *dev)    
  3. {    
  4.     int i;    
  5.     
  6.     for (; id->flags || id->driver_info; id++) {    
  7.     
  8.         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)    
  9.             if (id->bustype != dev->id.bustype)    
  10.                 continue;    
  11.     
  12.         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)    
  13.             if (id->vendor != dev->id.vendor)    
  14.                 continue;    
  15.     
  16.         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)    
  17.             if (id->product != dev->id.product)    
  18.                 continue;    
  19.     
  20.         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)    
  21.             if (id->version != dev->id.version)    
  22.                 continue;    
  23.          
  24.          
  25.         MATCH_BIT(evbit,  EV_MAX);    
  26.         MATCH_BIT(keybit, KEY_MAX);    
  27.         MATCH_BIT(relbit, REL_MAX);    
  28.         MATCH_BIT(absbit, ABS_MAX);    
  29.         MATCH_BIT(mscbit, MSC_MAX);    
  30.         MATCH_BIT(ledbit, LED_MAX);    
  31.         MATCH_BIT(sndbit, SND_MAX);    
  32.         MATCH_BIT(ffbit,  FF_MAX);    
  33.         MATCH_BIT(swbit,  SW_MAX);    
  34.     
  35.         return id;    
  36.     }    
  37.     
  38.     return NULL;    
  39. }    

dev和handler在input_match_device中匹配,可能与多个handler匹配成功,与下文中input_event可能打开多个hanle相对应。如果匹配成功input_attach_handler调用connect函数。值得注意的是不管是dev匹配handler还是handler匹配dev都是可以一对多的,比如:

Ubuntu中,/dev/input/event3 和 /dev/input/mouse1 都是对应鼠标这个设备。

 

connect函数将会创建一个evdev结构体,该结构体中有一个handle结构体成员。

evdev_connect:

[csharp] view plain copy print?

  1. /*  
  2.  * Create new evdev device. Note that input core serializes calls  
  3.  * to connect and disconnect so we don't need to lock evdev_table here.  
  4.  */    
  5. static int evdev_connect(struct input_handler *handler, struct input_dev *dev,    
  6.              const struct input_device_id *id)    
  7. {    
  8.     struct evdev *evdev;    
  9.     int minor;    
  10.     int error;    
  11.     /*evdev_table是evdev结构体指针数组,最大值32 
  12.      *下边的for循环,在evdev_table数组中找一个未使用的地方  
  13.      */  
  14.     for (minor = 0; minor < EVDEV_MINORS; minor++)    
  15.         if (!evdev_table[minor])    
  16.             break;    
  17.     
  18.     if (minor == EVDEV_MINORS) {    
  19.         printk(KERN_ERR "evdev: no more free evdev devices/n");    
  20.         return -ENFILE;    
  21.     }    
  22.     /*下边的代码是为每一个匹配的设备分配一个evdev结构体,并对成员进行初始化*/    
  23.     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);    
  24.     if (!evdev)    
  25.         return -ENOMEM;    
  26.     
  27.     INIT_LIST_HEAD(&evdev->client_list);    
  28.     spin_lock_init(&evdev->client_lock);    
  29.     mutex_init(&evdev->mutex);    
  30.     init_waitqueue_head(&evdev->wait);    
  31.     
  32.     snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);    
  33.     evdev->exist = 1;    
  34.     evdev->minor = minor;    
  35.     
  36.     evdev->handle.dev = input_get_device(dev);    
  37.     evdev->handle.name = evdev->name;    
  38.     evdev->handle.handler = handler;    
  39.     evdev->handle.private = evdev;    
  40.     
  41.     dev_set_name(&evdev->dev, evdev->name);    
  42.     evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);    
  43.     evdev->dev.class = &input_class;    
  44.     evdev->dev.parent = &dev->dev;    
  45.     evdev->dev.release = evdev_free;    
  46.     /**/    
  47.     device_initialize(&evdev->dev);    
  48.     /*  
  49.      *input_register_handle完成的主要功能是:  
  50.          *list_add_tail_rcu(&handle->d_node, &dev->h_list);  
  51.          *list_add_tail(&handle->h_node, &handler->h_list);  
  52.      */    
  53.     error = input_register_handle(&evdev->handle);    
  54.     if (error)    
  55.         goto err_free_evdev;    
  56.     /*evdev_install_chrdev完成的功能是evdev_table[evdev->minor]=evdev;*/    
  57.     error = evdev_install_chrdev(evdev);    
  58.     if (error)    
  59.         goto err_unregister_handle;    
  60.     
  61.     error = device_add(&evdev->dev);  /*添加字符设备节点*/    
  62.     if (error)    
  63.         goto err_cleanup_evdev;    
  64.     
  65.     return 0;    
  66. }    

 

input_register_handle 函数将input_dev和input_handler的链表存放到handle结构体,这俩链表让handle成为了input_dev和input_handler的联系。

 

int input_register_handle(struct input_handle *handle)

{

    struct input_handler *handler = handle->handler;

 

    list_add_tail(&handle->d_node, &handle->dev->h_list);

    list_add_tail(&handle->h_node, &handler->h_list);

 

    if (handler->start)

        handler->start(handle);

 

    return 0;

}

 

 

以上是dev通过input_register_device如何匹配到对应handler,然后如何建立连接的过程。

这里要明白一对应关系:1个上报事---  1个evdev ---   1个handle---1个/dev/event*( * 表示0~256的数字) ,下面有幅图说明以上过程后相关结构体之间的关系:

 

下面讲解如何利用handle这个链接来上报事件input_event:

[csharp] view plain copy print?

  1. void input_event(struct input_dev *dev,    
  2.          unsigned int type, unsigned int code, int value)    
  3. {    
  4.     unsigned long flags;    
  5.     
  6.     if (is_event_supported(type, dev->evbit, EV_MAX)) {    
  7.     
  8.         spin_lock_irqsave(&dev->event_lock, flags);    
  9.         add_input_randomness(type, code, value);    
  10.         input_handle_event(dev, type, code, value);    
  11.         spin_unlock_irqrestore(&dev->event_lock, flags);    
  12.     }    
  13. }    

该函数主要通过input_handle_event上报

 

input_handle_event函数:

[csharp] view plain copy print?

  1. static void input_handle_event(struct input_dev *dev,    
  2.                    unsigned int type, unsigned int code, int value)    
  3. {    
  4.     int disposition = INPUT_IGNORE_EVENT;    
  5.     
  6.     switch (type) {    
  7.     。。。。。。。。。。。。。。。。    
  8.     //将忽略事件  
  9.     if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)    
  10.         dev->sync = 0;    
  11.     //将会把事件传递给input_dev  
  12.     if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)    
  13.         dev->event(dev, type, code, value);    
  14.     //将会把事件传递给input_handler  
  15.     if (disposition & INPUT_PASS_TO_HANDLERS)    
  16.         input_pass_event(dev, type, code, value);    
  17. }    

将事件传递给input_handler的函数为input_pass_event:

[csharp] view plain copy print?

  1. static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)  
  2. {  
  3.     struct input_handler *handler;  
  4.     struct input_handle *handle;  
  5.   
  6.     rcu_read_lock();  
  7.   
  8.     handle = rcu_dereference(dev->grab);  
  9.       
  10.     //如果是绑定的handle,则调用绑定的handler->event函数  
  11.     if (handle)  
  12.         handle->handler->event(handle, type, code, value);  
  13.     else  
  14.     {  
  15.       //如果没有绑定,则遍历dev的h_list链表,寻找handle,如果handle已经打开,说明有进程读取设备关联的evdev。   
  16.         bool filtered = false;  
  17.   
  18.         list_for_each_entry_rcu(handle, &dev->h_list, d_node)   
  19.         {  
  20.             if (!handle->open)  //依据open的值判断handle是否被打开,同一事件可能会适用多个handler  
  21.                 continue;  
  22.   
  23.             handler = handle->handler;  
  24.             if (!handler->filter)   
  25.             {  
  26.                 if (filtered)  
  27.                     break;  
  28.                       
  29.                 // 调用相关的事件处理器的event函数,进行事件的处理    
  30.                 handler->event(handle, type, code, value);  
  31.   
  32.             }  
  33.             else if(handler->filter(handle, type, code, value))  
  34.                 filtered = true;  
  35.         }  
  36.     }  
  37.   
  38.     rcu_read_unlock();  
  39. }  

input_pass_event利用之前建立的联系"handle",利用handle结构体中open的数值找到input_dev对应的handler(open的值在用户打开文件部分会

变化,后边详解),调用handler的event函数。

 

 

调用到某处理器(evdev. keyboard...)的handler-->event,那么这个event做了什么?

下边以evdev中的event为例:

[csharp] view plain copy print?

  1. static void evdev_event(struct input_handle *handle,    
  2.             unsigned int type, unsigned int code, int value)    
  3. {    
  4.     struct evdev *evdev = handle->private;    
  5.     struct evdev_client *client;    
  6.     struct input_event event;    
  7.     
  8.     
  9.     do_gettimeofday(&event.time);    
  10.     event.type = type;    
  11.     event.code = code;    
  12.     event.value = value;    
  13.         //将传过来的事件,赋值给input_event结构    
  14.     rcu_read_lock();    
  15.     
  16.     
  17.     client = rcu_dereference(evdev->grab);    
  18.         //如果evdev绑定了client那么,处理这个客户端,触摸屏驱动没有绑定    
  19.     if (client)    
  20.         evdev_pass_event(client, &event);    
  21.     else    
  22.         //遍历client链表,调用evdev_pass_event函数    
  23.         list_for_each_entry_rcu(client, &evdev->client_list, node)    
  24.             evdev_pass_event(client, &event);    
  25.     
  26.     
  27.     rcu_read_unlock();    
  28.     
  29.     
  30.     wake_up_interruptible(&evdev->wait); //唤醒等待的进程    
  31. }    

该函数主要作用就是将上报事件的数据放到input_event[]等待用户读取。

上报事件结束。

如果数据已经准备到位,那么用户是怎么得到呢?

很明显用户首先得打开文件,所以调用input_open_file再调用其中的ofps-> evdev_open

input_open_file:

[csharp] view plain copy print?

  1. static int input_open_file(struct inode *inode, struct file *file)    
  2. {    
  3.     struct input_handler *handler;    
  4.     const struct file_operations *old_fops, *new_fops = NULL;    
  5.     int err;    
  6.     
  7.     lock_kernel();    
  8.     /* No load-on-demand here? */    
  9.     /*因为 >>5 表示除上32*/    
  10.     handler = input_table[iminor(inode) >> 5];      //根据次设备号找到对应的handler,然后就利用该handler
  11.     if (!handler || !(new_fops = fops_get(handler->fops))) {    
  12.         err = -ENODEV;    
  13.         goto out;    
  14.     }    
  15.     
  16.     /*  
  17.      * That's _really_ odd. Usually NULL ->open means "nothing special",  
  18.      * not "no device". Oh, well...  
  19.      */    
  20.     if (!new_fops->open) {    
  21.         fops_put(new_fops);    
  22.         err = -ENODEV;    
  23.         goto out;    
  24.     }    
  25.     /*保存以前的fops,使用相应的handler的fops*/    
  26.     old_fops = file->f_op;    
  27.     file->f_op = new_fops;    
  28.     
  29.     err = new_fops->open(inode, file);    
  30.     
  31.     if (err) {    
  32.         fops_put(file->f_op);    
  33.         file->f_op = fops_get(old_fops);    
  34.     }    
  35.     fops_put(old_fops);    
  36. out:    
  37.     unlock_kernel();    
  38.     return err;    
  39. }    

input_table[]大小为8,用来存放事件处理器的handler,内核最多支持256个事件,那么1个handler最多支持256/8=32个事件

该函数是通过次设备号为input_table[]下标索引找到对应的handler,每个处理器在register_handler时是以次设备号为索引将handler放入input_table[]中,handler中fops里边有读、写、释放等等。

 

继续调用fops中的evdev_open函数:

[csharp] view plain copy print?

  1. static int evdev_open(struct inode *inode, struct file *file)    
  2. {    
  3.     struct evdev *evdev;    
  4.     struct evdev_client *client;    
  5.     /*因为次设备号是从EVDEV_MINOR_BASE开始的*/    
  6.     int i = iminor(inode) - EVDEV_MINOR_BASE;    
  7.     int error;    
  8.         
  9.     if (i >= EVDEV_MINORS)    
  10.         return -ENODEV;    
  11.     
  12.     error = mutex_lock_interruptible(&evdev_table_mutex);    
  13.     if (error)    
  14.         return error;    
  15.     /*evdev_table一共可容纳32个成员,找到次设备号对应的那个*/    
  16.     evdev = evdev_table[i];    
  17.     if (evdev)    
  18.         get_device(&evdev->dev);    
  19.     mutex_unlock(&evdev_table_mutex);    
  20.     
  21.     if (!evdev)    
  22.         return -ENODEV;    
  23.     /*打开的时候创建一个client*/    
  24.     client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);    
  25.     if (!client) {    
  26.         error = -ENOMEM;    
  27.         goto err_put_evdev;    
  28.     }    
  29.     
  30.     spin_lock_init(&client->buffer_lock);    
  31.     /*下边两句的作用就是将evdev和client绑定到一起*/    
  32.     client->evdev = evdev;    
  33.     evdev_attach_client(evdev, client);    
  34.     
  35.     error = evdev_open_device(evdev);    
  36.     if (error)    
  37.         goto err_free_client;    
  38.     /*将file->private_data指向刚刚建的client,后边会用到的*/    
  39.     file->private_data = client;    
  40.     return 0;    
  41.     
  42.  err_free_client:    
  43.     evdev_detach_client(evdev, client);    
  44.     kfree(client);    
  45.  err_put_evdev:    
  46.     put_device(&evdev->dev);    
  47.     return error;    
  48. }    

该函数将存放数据的数组input_event[]和evdev绑定了起来,后边的读函数会用到

 

在evdev_open最终会调用带到input_open_device:

[csharp] view plain copy print?

  1. int input_open_device(struct input_handle *handle)    
  2. {    
  3.     struct input_dev *dev = handle->dev;    
  4.     int retval;    
  5.     
  6.     retval = mutex_lock_interruptible(&dev->mutex);    
  7.     if (retval)    
  8.         return retval;    
  9.     
  10.     if (dev->going_away) {    
  11.         retval = -ENODEV;    
  12.         goto out;    
  13.     }    
  14.     
  15.     handle->open++;    
  16.     
  17.     if (!dev->users++ && dev->open)    
  18.         retval = dev->open(dev);    
  19.     
  20.     if (retval) {    
  21.         dev->users--;    
  22.         if (!--handle->open) {    
  23.             /*  
  24.              * Make sure we are not delivering any more events  
  25.              * through this handle  
  26.              */    
  27.             synchronize_rcu();    
  28.         }    
  29.     }    
  30.     
  31.  out:    
  32.     mutex_unlock(&dev->mutex);    
  33.     return retval;    
  34. }    

 

这里可以看到上报上来的数据了:

[csharp] view plain copy print?

  1. static ssize_t evdev_read(struct file *file, char __user *buffer,    
  2.               size_t count, loff_t *ppos)    
  3. {    
  4.     /*这个就是刚才在open函数中*/    
  5.     struct evdev_client *client = file->private_data;    
  6.     struct evdev *evdev = client->evdev;    
  7.     struct input_event event;    
  8.     int retval;    
  9.     
  10.     if (count < input_event_size())    
  11.         return -EINVAL;    
  12.     /*如果client的环形缓冲区中没有数据并且是非阻塞的,那么返回-EAGAIN,也就是try again*/    
  13.     if (client->head == client->tail && evdev->exist &&    
  14.         (file->f_flags & O_NONBLOCK))    
  15.         return -EAGAIN;    
  16.     /*如果没有数据,并且是阻塞的,则在等待队列上等待吧*/    
  17.     retval = wait_event_interruptible(evdev->wait,    
  18.         client->head != client->tail || !evdev->exist);    
  19.     if (retval)    
  20.         return retval;    
  21.     
  22.     if (!evdev->exist)    
  23.         return -ENODEV;    
  24.     /*如果获得了数据则取出来,调用evdev_fetch_next_event*/    
  25.     while (retval + input_event_size() <= count &&    
  26.            evdev_fetch_next_event(client, &event)) {    
  27.         /*input_event_to_user调用copy_to_user传入用户程序中,这样读取完成*/    
  28.         if (input_event_to_user(buffer + retval, &event))    
  29.             return -EFAULT;    
  30.     
  31.         retval += input_event_size();    
  32.     }    
  33.     
  34.     return retval;    
  35. }    

[csharp] view plain copy print?

  1. static int evdev_fetch_next_event(struct evdev_client *client,    
  2.                   struct input_event *event)    
  3. {    
  4.     int have_event;    
  5.     
  6.     spin_lock_irq(&client->buffer_lock);    
  7.     /*先判断一下是否有数据*/    
  8.     have_event = client->head != client->tail;    
  9.     /*如果有就从环形缓冲区的取出来,记得是从head存储,tail取出*/    
  10.     if (have_event) {    
  11.         *event = client->buffer[client->tail++];    
  12.         client->tail &= EVDEV_BUFFER_SIZE - 1;    
  13.     }    
  14.     
  15.     spin_unlock_irq(&client->buffer_lock);    
  16.     
  17.     return have_event;    
  18. }    

[csharp] view plain copy print?

  1. int input_event_to_user(char __user *buffer,    
  2.             const struct input_event *event)    
  3. {    
  4.     /*如果设置了标志INPUT_COMPAT_TEST就将事件event包装成结构体compat_event*/    
  5.     if (INPUT_COMPAT_TEST) {    
  6.         struct input_event_compat compat_event;    
  7.     
  8.         compat_event.time.tv_sec = event->time.tv_sec;    
  9.         compat_event.time.tv_usec = event->time.tv_usec;    
  10.         compat_event.type = event->type;    
  11.         compat_event.code = event->code;    
  12.         compat_event.value = event->value;    
  13.         /*将包装成的compat_event拷贝到用户空间*/    
  14.         if (copy_to_user(buffer, &compat_event,    
  15.                  sizeof(struct input_event_compat)))    
  16.             return -EFAULT;    
  17.     
  18.     } else {    
  19.         /*否则,将event拷贝到用户空间*/    
  20.         if (copy_to_user(buffer, eventsizeof(struct input_event)))    
  21.             return -EFAULT;    
  22.     }    
  23.     
  24.     return 0;    
  25. }    

后边的读函数未做详细介绍,内容主要是把input_event的数据读取给用户

 

 

本文参考了两篇博文,都非常好:

linux input 子系统分析:  点击打开链接  

输入子系统架构分析:      点击打开链接

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值