Input子系统的研究与分析

Linux内核为所有的输入事件架构了一个input子系统,在input子系统中,集成了所有的输入事件的处理,包括按键、触摸屏、键盘和鼠标等。

Input系统由驱动层、核心层和事件处理层三部分组成。如下是一张事件的处理顺序图,图中驱动层部分捕捉到事件输入,如按键、移动鼠标等,然后将事件传递至核心层,接着核心层将事件交由事件处理层处理,最后再传递至用户空间层。


在内核中,input_dev表示一个input设备,input_handler表示input设备的接口,所有的input_dev都会使用双向链表input_dev_list连起来,在设备驱动中,可以使用input_register_device将input_dev加入这个链表中。而所有的input_handler也使用双向链表input_handler_list链接起来,在调用input_register_handler的时候会将input_handler加入链表中。而input_dev和input_handler都需要彼此关联,如同device和driver一样,当注册新的input_dev时,就会遍历input_handler_list,找到匹配的input_handler,然后调用input_handler的connect函数将它们联系起来,而注册input_handler时也会遍历input_dev_list,然后将dev和handler联系起来。而在input_handler的connect函数中,会创建input_handle结构体,input_handle就负责将input_dev和input_handler联系在一起。

当设备产生一个输入事件,如按下按键,驱动程序就会调用匹配的input_handler中的event函数记录该事件,当用户层需要获取该事件时,就会调用input_handler中的文件操作函数fops来获取这个事件,例如read。

下面来介绍input子系统的具体实现。

首先是input设备的注册函数input_register_device

[cpp]  view plain copy
  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.     /* Every input device generates EV_SYN/SYN_REPORT events. */  
  9.     __set_bit(EV_SYN, dev->evbit); // evbit表示该设备支持的事件,每个设备都支持该事件,  
  10.                    // EV_SYN表示事件结束之后的同步事件  
  11.     /* KEY_RESERVED is not supposed to be transmitted to userspace. */  
  12.     __clear_bit(KEY_RESERVED, dev->keybit); // keybit表示事件的按键类型  
  13.   
  14.     /* Make sure that bitmasks not mentioned in dev->evbit are clean. */  
  15.     input_cleanse_bitmasks(dev);  
  16.   
  17.     if (!dev->hint_events_per_packet)  
  18.     dev->hint_events_per_packet = input_estimate_events_per_packet(dev);  
  19.   
  20.     /* 
  21.     * If delay and period are pre-set by the driver, then autorepeating 
  22.     * is handled by the driver itself and we don't do it in input.c. 
  23.     */ // 主要用于处理重复按键  
  24.     init_timer(&dev->timer);  
  25.     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {  
  26.     dev->timer.data = (long) dev;  
  27.     dev->timer.function = input_repeat_key;  
  28.     dev->rep[REP_DELAY] = 250;  
  29.     dev->rep[REP_PERIOD] = 33;  
  30.     }  
  31.   
  32.     if (!dev->getkeycode)  
  33.     dev->getkeycode = input_default_getkeycode;  
  34.   
  35.     if (!dev->setkeycode)  
  36.     dev->setkeycode = input_default_setkeycode;  
  37.   
  38.     dev_set_name(&dev->dev, "input%ld",   
  39.     (unsigned long) atomic_inc_return(&input_no) - 1);  
  40.   
  41.     error = device_add(&dev->dev); // 注册device  
  42.     if (error)  
  43.     return error;  
  44.   
  45.     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);  
  46.     pr_info("%s as %s\n",  
  47.     dev->name ? dev->name : "Unspecified device",  
  48.     path ? path : "N/A");  
  49.     kfree(path);  
  50.   
  51.     error = mutex_lock_interruptible(&input_mutex);  
  52.     if (error) {  
  53.     device_del(&dev->dev);  
  54.     return error;  
  55.     }  
  56.     // 增加设备至input_dev_list中  
  57.     list_add_tail(&dev->node, &input_dev_list);  
  58.     // 遍历input_handler_list,寻找匹配的input_handler,使用input_attach_handler匹配  
  59.     list_for_each_entry(handler, &input_handler_list, node)  
  60.     input_attach_handler(dev, handler);  
  61.   
  62.     input_wakeup_procfs_readers();  
  63.   
  64.     mutex_unlock(&input_mutex);  
  65.   
  66.     return 0;  
  67. }  
  68. EXPORT_SYMBOL(input_register_device);  

在注册函数input_register_device中,主要是对input_dev进行设置,然后将input_dev加入input_dev_list链表中,最后就遍历input_handler_list链表,使用input_attach_handler函数进行匹配,下面看看input_attach_handler函数的实现。

[cpp]  view plain copy
  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.   
  6.     id = input_match_device(handler, dev);  
  7.     if (!id)  
  8.     return -ENODEV;  
  9.   
  10.     error = handler->connect(handler, dev, id);  
  11.     if (error && error != -ENODEV)  
  12.     pr_err("failed to attach handler %s to device %s, error: %d\n",  
  13.         handler->name, kobject_name(&dev->dev.kobj), error);  
  14.   
  15.     return error;  
  16. }  

该函数会首先使用input_match_device函数进行匹配操作,如果匹配成功,则使用handler->connect回调函数将handler和dev联系起来。下面看看input_match_device的实现。

[cpp]  view plain copy
  1. static const struct input_device_id *input_match_device(struct input_handler *handler,  
  2.                             struct input_dev *dev)  
  3. {  
  4.     const struct input_device_id *id;  
  5.     int i;  
  6.   
  7.     for (id = handler->id_table; id->flags || id->driver_info; id++) {  
  8.     // 比较dev和handler的总线类型、设备厂商、设备号和设备版本  
  9.     if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)  
  10.         if (id->bustype != dev->id.bustype)  
  11.         continue;  
  12.   
  13.     if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)  
  14.         if (id->vendor != dev->id.vendor)  
  15.         continue;  
  16.   
  17.     if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)  
  18.         if (id->product != dev->id.product)  
  19.         continue;  
  20.   
  21.     if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)  
  22.         if (id->version != dev->id.version)  
  23.         continue;  
  24.     // 比较dev和handler的各个比特项,如果有1项不同,则匹配失败  
  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.     // 如果上述全部匹配成功,则再使用handler的match,如果也匹配成功,则返回id,表明完全匹配成功  
  35.     if (!handler->match || handler->match(handler, dev))  
  36.         return id;  
  37.     }  
  38.   
  39.     return NULL;  
  40. }  

函数input_match_device会匹配dev和handler的总线类型、设备厂商、设备号、设备版本,然后匹配各个比特项,最后使用handler的match进行匹配,全部成功后即匹配成功。
总之,在input设备的注册函数中,会将设备加入设备链表,然后就遍历handler链表进行匹配,如果完全匹配成功则调用handler的connect函数将input_dev和input_handler联系起来。

接下来分析handler的注册函数input_register_handler。

[cpp]  view plain copy
  1. int input_register_handler(struct input_handler *handler)  
  2. {  
  3.     struct input_dev *dev;  
  4.     int retval;  
  5.   
  6.     retval = mutex_lock_interruptible(&input_mutex);  
  7.     if (retval)  
  8.     return retval;  
  9.   
  10.     INIT_LIST_HEAD(&handler->h_list);  
  11.     // 如果存在文件操作函数fops,则将handler加入input_table中  
  12.     if (handler->fops != NULL) {  
  13.     if (input_table[handler->minor >> 5]) {  
  14.         retval = -EBUSY;  
  15.         goto out;  
  16.     }  
  17.     input_table[handler->minor >> 5] = handler;  
  18.     }  
  19.     // 将handler加入input_handler_list链表中  
  20.     list_add_tail(&handler->node, &input_handler_list);  
  21.     // 遍历input_dev_list链表,匹配dev和handler  
  22.     list_for_each_entry(dev, &input_dev_list, node)  
  23.     input_attach_handler(dev, handler);  
  24.   
  25.     input_wakeup_procfs_readers();  
  26.   
  27. out:  
  28.     mutex_unlock(&input_mutex);  
  29.     return retval;  
  30. }  
  31. EXPORT_SYMBOL(input_register_handler);  

该注册函数input_register_handler会首先判断handler是否存在文件操作函数fops,如果存在则将handler加入input_table中,然后会将handler加入input_handler_list链表中,最后再遍历input_dev_list,使用input_attach_handler来匹配dev和handler。
当使用input_register_device或者input_register_handler注册dev或handler时,都会遍历链表进行匹配操作,如果匹配成功会调用handler的connect函数,在该函数中会注册一个handle来将dev和handler联系起来,下面看看handle的注册函数input_register_handle。

[cpp]  view plain copy
  1. int input_register_handle(struct input_handle *handle)  
  2. {  
  3.     struct input_handler *handler = handle->handler;  
  4.     struct input_dev *dev = handle->dev;  
  5.     int error;  
  6.   
  7.     /* 
  8.     * We take dev->mutex here to prevent race with 
  9.     * input_release_device(). 
  10.     */  
  11.     error = mutex_lock_interruptible(&dev->mutex);  
  12.     if (error)  
  13.     return error;  
  14.   
  15.     /* 
  16.     * Filters go to the head of the list, normal handlers 
  17.     * to the tail. 
  18.     */ // 将handle加入dev链表上  
  19.     if (handler->filter)  
  20.     list_add_rcu(&handle->d_node, &dev->h_list);  
  21.     else  
  22.     list_add_tail_rcu(&handle->d_node, &dev->h_list);  
  23.   
  24.     mutex_unlock(&dev->mutex);  
  25.   
  26.     /* 
  27.     * Since we are supposed to be called from ->connect() 
  28.     * which is mutually exclusive with ->disconnect() 
  29.     * we can't be racing with input_unregister_handle() 
  30.     * and so separate lock is not needed here. 
  31.     */ // 将handle加入handler链表上  
  32.     list_add_tail_rcu(&handle->h_node, &handler->h_list);  
  33.     // 如果handle有start回调函数,则调用start回调函数  
  34.     if (handler->start)  
  35.     handler->start(handle);  
  36.   
  37.     return 0;  
  38. }  
  39. EXPORT_SYMBOL(input_register_handle);  

该注册函数input_register_handle主要是将handle加入对应的input_dev的h_list上和对应的input_handler的h_list上,如果handle有start回调函数则调用handle的回调函数start。
以上就是dev、handler和handle的注册函数,通过上述3个注册函数可以建立这三个结构体的关系,有了这个对应关系,就可以了解到event的处理过程,下面来进行分析。
在驱动层中如果接收到某个事件的中断,就会使用input_report_xxx函数(input_report_key、input_report_rel等等)来通知input子系统,然后使用input_sync来通知事件结束,而函数input_report_xxx和input_sync都是调用input_event来完成通知的,下面来看看input_event。

[cpp]  view plain copy
  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.     spin_lock_irqsave(&dev->event_lock, flags);  
  8.     add_input_randomness(type, code, value);  
  9.     input_handle_event(dev, type, code, value); // 传递事件  
  10.     spin_unlock_irqrestore(&dev->event_lock, flags);  
  11.     }  
  12. }  
  13. EXPORT_SYMBOL(input_event);  

input_event方法首先判断设备产生的这个事件是否合法,如果合法则调用input_handle_event传递事件。

[cpp]  view plain copy
  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.     case EV_SYN:  
  9.     ......  
  10.     break;  
  11.   
  12.     case EV_KEY:  
  13.     ......  
  14.     break;  
  15.   
  16.     case EV_SW:  
  17.     ......  
  18.     break;  
  19.   
  20.     case EV_ABS:  
  21.     ......  
  22.     break;  
  23.   
  24.     case EV_REL:  
  25.     ......  
  26.     break;  
  27.   
  28.     case EV_MSC:  
  29.     ......  
  30.     break;  
  31.   
  32.     case EV_LED:  
  33.     ......  
  34.     break;  
  35.   
  36.     case EV_SND:  
  37.     ......  
  38.     break;  
  39.   
  40.     case EV_REP:  
  41.     ......  
  42.     break;  
  43.   
  44.     case EV_FF:  
  45.     ......  
  46.     break;  
  47.   
  48.     case EV_PWR:  
  49.     ......  
  50.     break;  
  51.     }  
  52.   
  53.     if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)  
  54.     dev->sync = false;  
  55.   
  56.     if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)  
  57.     dev->event(dev, type, code, value);  
  58.   
  59.     if (disposition & INPUT_PASS_TO_HANDLERS)  
  60.     input_pass_event(dev, type, code, value);  
  61. }  

在input_handle_event函数中,会首先根据事件的类型来设置disposition的值,disposition有四个选项,INPUT_IGNORE_EVENT忽视事件,INPUT_PASS_TO_HANDLERS传递事件至handler,INPUT_PASS_TO_DEVICE传递事件至device,INPUT_PASS_TO_ALL传递事件至handler和device。如果disposition为INPUT_PASS_TO_DEVICE则调用dev的event函数,如果disposition为INPUT_PASS_TO_HANDLERS则调用input_pass_event函数,下面分析input_pass_event函数。

[cpp]  view plain copy
  1. static void input_pass_event(struct input_dev *dev,  
  2.                  unsigned int type, unsigned int code, int value)  
  3. {  
  4.     struct input_handler *handler;  
  5.     struct input_handle *handle;  
  6.     int i;  
  7.     bool not_gsensor = true;  
  8.     ......  
  9.     rcu_read_lock();  
  10.       
  11.     handle = rcu_dereference(dev->grab);  
  12.     if (handle)  
  13.     handle->handler->event(handle, type, code, value);  
  14.     else {  
  15.     bool filtered = false;  
  16.   
  17.     list_for_each_entry_rcu(handle, &dev->h_list, d_node) {  
  18.         if (!handle->open)  
  19.         continue;  
  20.   
  21.     handler = handle->handler;  
  22.     if (!handler->filter) {  
  23.         if (filtered)  
  24.         break;  
  25.   
  26.         handler->event(handle, type, code, value);  
  27.   
  28.     } else if (handler->filter(handle, type, code, value))  
  29.         filtered = true;  
  30.     }  
  31.     }  
  32.   
  33.     rcu_read_unlock();  
  34. }  

函数input_pass_event会先判断dev是否强制指定了handle,如果有则调用handle->handler的event函数,如果没有则遍历dev的h_list上的所有handle再调用handle->handler的event函数。

下面来具体的看看handler是如何connect、event之类的操作的,在kernel/drivers/input/文件夹下有evdev.c文件,其中描述了evdev模块,该模块注册了一个handler,所有的input_dev都会匹配上该handler。下面进行简要的分析,首先是模块初始化函数evdev_init(),其调用了input_register_handler注册了一个handler,evdev_handler。定义如下:

[cpp]  view plain copy
  1. static struct input_handler evdev_handler = {  
  2.     .event  = evdev_event,  
  3.     .connect    = evdev_connect,  
  4.     .disconnect = evdev_disconnect,  
  5.     .fops   = &evdev_fops,  
  6.     .minor  = EVDEV_MINOR_BASE,  
  7.     .name   = "evdev",  
  8.     .id_table   = evdev_ids,  
  9. };  
  10.   
  11. static const struct input_device_id evdev_ids[] = {  
  12.     { .driver_info = 1 },   /* Matches all devices */  
  13.     { },            /* Terminating zero entry */  
  14. };  

而进行dev和handler匹配时,主要是看id_table,该id_table中没有定义flags,也没有定义匹配的属性值,所以这个handler可以匹配所有的input_dev。
当匹配成功之后会调用handler中的connect回调函数。

[cpp]  view plain copy
  1. static int evdev_connect(struct input_handler *handler, struct input_dev *dev,  
  2.              const struct input_device_id *id)  
  3. {  
  4.     struct evdev *evdev;  
  5.     int minor;  
  6.     int error;  
  7.   
  8.     for (minor = 0; minor < EVDEV_MINORS; minor++)  
  9.     if (!evdev_table[minor])  
  10.         break;  
  11.   
  12.     if (minor == EVDEV_MINORS) {  
  13.     pr_err("no more free evdev devices\n");  
  14.     return -ENFILE;  
  15.     }  
  16.     // evdev结构体声明空间  
  17.     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);  
  18.     if (!evdev)  
  19.     return -ENOMEM;  
  20.   
  21.     INIT_LIST_HEAD(&evdev->client_list);  
  22.     spin_lock_init(&evdev->client_lock);  
  23.     mutex_init(&evdev->mutex);  
  24.     init_waitqueue_head(&evdev->wait); // 初始化等待队列  
  25.   
  26.     dev_set_name(&evdev->dev, "event%d", minor);  
  27.     evdev->exist = true;  
  28.     evdev->minor = minor;  
  29.     // 初始化evdev中的handle  
  30.     evdev->handle.dev = input_get_device(dev);  
  31.     evdev->handle.name = dev_name(&evdev->dev);  
  32.     evdev->handle.handler = handler;  
  33.     evdev->handle.private = evdev;  
  34.   
  35.     evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);  
  36.     evdev->dev.class = &input_class;  
  37.     evdev->dev.parent = &dev->dev;  
  38.     evdev->dev.release = evdev_free;  
  39.     device_initialize(&evdev->dev);  
  40.     // 使用input_register_handle注册handle  
  41.     error = input_register_handle(&evdev->handle);  
  42.     if (error)  
  43.     goto err_free_evdev;  
  44.   
  45.     error = evdev_install_chrdev(evdev);  
  46.     if (error)  
  47.     goto err_unregister_handle;  
  48.   
  49.     error = device_add(&evdev->dev);  
  50.     if (error)  
  51.     goto err_cleanup_evdev;  
  52.   
  53.     return 0;  
  54.   
  55.  err_cleanup_evdev:  
  56.     evdev_cleanup(evdev);  
  57.  err_unregister_handle:  
  58.     input_unregister_handle(&evdev->handle);  
  59.  err_free_evdev:  
  60.     put_device(&evdev->dev);  
  61.     return error;  
  62. }  

在evdev_connect连接函数中,会声明一个evdev的结构体,然后使用input_register_handle注册一个handle,最后将该evdev设备注册至sysfs中。而当有事件发生时会调用匹配handler的event方法,在此模块中即为evdev_event方法

[cpp]  view plain copy
  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.     struct timespec ts;  
  8.   
  9.     ktime_get_ts(&ts);  
  10.     event.time.tv_sec = ts.tv_sec;  
  11.     event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;  
  12.     event.type = type;  
  13.     event.code = code;  
  14.     event.value = value;  
  15.   
  16.     rcu_read_lock();  
  17.   
  18.     client = rcu_dereference(evdev->grab);  
  19.     if (client)  
  20.     evdev_pass_event(client, &event);  
  21.     else  
  22.     list_for_each_entry_rcu(client, &evdev->client_list, node)  
  23.         evdev_pass_event(client, &event);  
  24.   
  25.     rcu_read_unlock();  
  26.   
  27.     if (type == EV_SYN && code == SYN_REPORT)  
  28.     wake_up_interruptible(&evdev->wait);  
  29. }  

在该方法中,会初始化一个input_event结构体,并通过evdev_pass_event将该事件放入client的buffer之中,最后使用wake_up_interruptible来唤醒一个等待事件,而此等待事件发生在上层的读取事件回调函数中,下面来具体的分析下input子系统是如何供上层调用的。
首先是input子系统的初始化函数input_init()。

[cpp]  view plain copy
  1. static int __init input_init(void)  
  2. {  
  3.     int err;  
  4.   
  5.     err = class_register(&input_class);  
  6.     if (err) {  
  7.     pr_err("unable to register input_dev class\n");  
  8.     return err;  
  9.     }  
  10.   
  11.     err = input_proc_init();  
  12.     if (err)  
  13.     goto fail1;  
  14.   
  15.     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);  
  16.     if (err) {  
  17.     pr_err("unable to register char major %d", INPUT_MAJOR);  
  18.     goto fail2;  
  19.     }  
  20.   
  21. #ifdef CONFIG_CPU_FREQ_USR_EVNT_NOTIFY  
  22.     cpufreq_usrevent = create_workqueue("cpufreq_uevent");  
  23.     if (!cpufreq_usrevent) {  
  24.     printk(KERN_ERR "Creation of cpufreq_usrevent failed\n");  
  25.     goto fail3;  
  26.     }  
  27. #endif  
  28.   
  29.     return 0;  
  30.   
  31. #ifdef CONFIG_CPU_FREQ_USR_EVNT_NOTIFY  
  32. fail3: unregister_chrdev(INPUT_MAJOR, "input");  
  33. #endif  
  34.   
  35. fail2:  input_proc_exit();  
  36. fail1:  class_unregister(&input_class);  
  37.     return err;  
  38. }  

在此初始化函数中,首先会注册一个名为“input”的类,所有的input device都属于这个类。即在sysfs中,所有的输入设备所代表的目录都在/dev/class/input下面;然后再调用input_proc_init()在/proc下面建立相关的交互文件;最后使用register_chrdev注册了主设备号为INPUT_MAJOR(13),次设备号为0~255的字符设备,其操作函数指针为input_fops。

[cpp]  view plain copy
  1. static const struct file_operations input_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .open = input_open_file,  
  4.     .llseek = noop_llseek,  
  5. };  

上层可以通过open设备节点来打开设备,其会调用到这个回调函数input_open_file。

[cpp]  view plain copy
  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.     err = mutex_lock_interruptible(&input_mutex);  
  8.     if (err)  
  9.     return err;  
  10.     // 获取input_table当中的handler,如果存在则将其fops赋值给new_fops  
  11.     /* No load-on-demand here? */  
  12.     handler = input_table[iminor(inode) >> 5];  
  13.     if (handler)  
  14.     new_fops = fops_get(handler->fops);  
  15.   
  16.     mutex_unlock(&input_mutex);  
  17.   
  18.     /* 
  19.     * That's _really_ odd. Usually NULL ->open means "nothing special", 
  20.     * not "no device". Oh, well... 
  21.     */  
  22.     if (!new_fops || !new_fops->open) {  
  23.     fops_put(new_fops);  
  24.     err = -ENODEV;  
  25.     goto out;  
  26.     }  
  27.     // 将new_fops赋值给这个file的f_op  
  28.     old_fops = file->f_op;  
  29.     file->f_op = new_fops;  
  30.     // 调用新的open函数  
  31.     err = new_fops->open(inode, file);  
  32.     if (err) {  
  33.     fops_put(file->f_op);  
  34.     file->f_op = fops_get(old_fops);  
  35.     }  
  36.     fops_put(old_fops);  
  37. out:  
  38.     return err;  
  39. }  

在input_open_file打开函数中,会首先根据minor来获取input_table中的handler,然后将handler中fops赋值给file作为该file新的fops,之后调用新的open方法。而在evdev模块中,有fops如下:

[cpp]  view plain copy
  1. static const struct file_operations evdev_fops = {  
  2.     .owner      = THIS_MODULE,  
  3.     .read       = evdev_read,  
  4.     .write      = evdev_write,  
  5.     .poll       = evdev_poll,  
  6.     .open       = evdev_open,  
  7.     .release        = evdev_release,  
  8.     .unlocked_ioctl = evdev_ioctl,  
  9. #ifdef CONFIG_COMPAT  
  10.     .compat_ioctl   = evdev_ioctl_compat,  
  11. #endif  
  12.     .fasync     = evdev_fasync,  
  13.     .flush      = evdev_flush,  
  14.     .llseek     = no_llseek,  
  15. };  

则当open文件时会调用到evdev_fops中的open方法evdev_open,然后上层空间可以通过read sysfs下的设备节点来调用回调函数read获取事件信息,在evdev模块中的read回调函数即为evdev_read。

[cpp]  view plain copy
  1. static ssize_t evdev_read(struct file *file, char __user *buffer,  
  2.               size_t count, loff_t *ppos)  
  3. {  
  4.     struct evdev_client *client = file->private_data;  
  5.     struct evdev *evdev = client->evdev;  
  6.     struct input_event event;  
  7.     int retval = 0;  
  8.   
  9.     if (count < input_event_size())  
  10.     return -EINVAL;  
  11.   
  12.     if (!(file->f_flags & O_NONBLOCK)) {  
  13.     retval = wait_event_interruptible(evdev->wait,  
  14.         client->packet_head != client->tail || !evdev->exist);  
  15.     if (retval)  
  16.         return retval;  
  17.     }  
  18.   
  19.     if (!evdev->exist)  
  20.     return -ENODEV;  
  21.   
  22.     while (retval + input_event_size() <= count &&  
  23.     evdev_fetch_next_event(client, &event)) {  
  24.   
  25.     if (input_event_to_user(buffer + retval, &event))  
  26.         return -EFAULT;  
  27.   
  28.     retval += input_event_size();  
  29.     }  
  30.   
  31.     if (retval == 0 && file->f_flags & O_NONBLOCK)  
  32.     retval = -EAGAIN;  
  33.     return retval;  
  34. }  

该方法会使用wait_event_interruptible等待事件的发生,此方法会中断在这里等待,当有时间发生时,最终会使用evdev_event来将时间存在client中,之后使用wake_up_interruptible唤醒此处等待事件,然后会通过input_event_to_user方法将client中的事件信息传递至用户层空间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值