zoukankan      html  css  js  c++  java
  • 输入子系统(二)函数调用分析

    很多分析输入子系统的文章已经讲得很清楚了,这里主要是记录自己的学习过程。参考的几篇文章:

    输入子系统学习笔记之按键实例编程

    输入子系统学习笔记之源码分析1_框架性分析

    输入子系统学习笔记之源码分析2_数据结构分析

    输入子系统学习笔记之源码分析3_流程分析

    三个重要结构体的说明参考输入子系统学习笔记之源码分析2_数据结构分析,很重要,输入子系统就是靠这三个结构体建立联系的。

    drivers/input/input.c中有如下定义:

    a. 声明了input_dev_list链表头,在input_register_device()函数中将input_dev结构体加入链表中。

    input_register_device(struct input_dev *dev)

      -->list_add_tail(&dev->node, &input_dev_list);

    b. 声明了input_handler_list链表头,在input_register_handler()函数中将input_handler结构体加入链表中。

    input_register_handler(struct input_handler *handler)

      -->list_add_tail(&handler->node, &input_handler_list);

    c. 定义input_handler指针数组,每个input_handler支持32个input_dev设备。

    1 #define INPUT_DEVICES    256
    2 static LIST_HEAD(input_dev_list);
    3 static LIST_HEAD(input_handler_list);
    4 static struct input_handler *input_table[8];

    1. input_register_handler()的函数分析

    事件处理程序是标准的,对于所有的输入类是通用的。所以一般而言我们要实现的设备驱动程序,而不是事件处理程序。

    一般情况下,内核启动后就已经构造好了input_handler_list链表,如下图左侧部分。

    调用input_register_handler()的函数一般位于evdev.c,joydev.c ,mousedev.c...等函数中的 xxx_init(void)函数中。

     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 int __init evdev_init(void)
    12 {
    13     return input_register_handler(&evdev_handler);
    14     
    15 }
     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 
    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 
    20     list_add_tail(&handler->node, &input_handler_list);
    21 
    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 }

    2. input_register_device()函数分析

     写一个符合输入子系统框架的驱动一般分为四步:a. 分配一个input_dev结构体;b. 设置;c. input_register_device()注册;d. 硬件相关的代码

     1 struct input_dev *input;
     2 static int gq_key_init(void)
     3 {
     4     // 1 allocate input_dev struct
     5     input = input_allocate_device();
     6 
     7     // 2 set
     8     // 2.1 set which event to generate
     9     __set_bit(EV_KEY, input->evbit);
    10     // 2.2 set which keys to generate
    11     __set_bit(KEY_L,input->keybit);
    12     __set_bit(KEY_S,input->keybit);
    13     __set_bit(KEY_ENTER,input->keybit);
    14     __set_bit(KEY_LEFTSHIFT,input->keybit);
    15 
    16     // 3 register
    17     input_register_device(input);
    18 
    19     // 4 hardware 
    20     return 0;
    21 }

    系统没调用一次input_register_device()函数就在input_dev_list链表上添加一个input_dev结构体,如下图左侧。

     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);
    10 
    11     /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    12     __clear_bit(KEY_RESERVED, dev->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 =
    19                 input_estimate_events_per_packet(dev);
    20 
    21     /*
    22      * If delay and period are pre-set by the driver, then autorepeating
    23      * is handled by the driver itself and we don't do it in input.c.
    24      */
    25     init_timer(&dev->timer);
    26     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
    27         dev->timer.data = (long) dev;
    28         dev->timer.function = input_repeat_key;
    29         dev->rep[REP_DELAY] = 250;
    30         dev->rep[REP_PERIOD] = 33;
    31     }
    32 
    33     if (!dev->getkeycode)
    34         dev->getkeycode = input_default_getkeycode;
    35 
    36     if (!dev->setkeycode)
    37         dev->setkeycode = input_default_setkeycode;
    38 
    39     dev_set_name(&dev->dev, "input%ld",
    40              (unsigned long) atomic_inc_return(&input_no) - 1);
    41 
    42     error = device_add(&dev->dev);
    43     if (error)
    44         return error;
    45 
    46     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    47     pr_info("%s as %s
    ",
    48         dev->name ? dev->name : "Unspecified device",
    49         path ? path : "N/A");
    50     kfree(path);
    51 
    52     error = mutex_lock_interruptible(&input_mutex);
    53     if (error) {
    54         device_del(&dev->dev);
    55         return error;
    56     }
    57 
    58     list_add_tail(&dev->node, &input_dev_list);
    59 
    60     list_for_each_entry(handler, &input_handler_list, node)
    61         input_attach_handler(dev, handler);
    62 
    63     input_wakeup_procfs_readers();
    64 
    65     mutex_unlock(&input_mutex);
    66 
    67     return 0;
    68 }
    input_register_device()

    3. input_attach_handler()函数分析

    input_register_handler()与input_register_device()函数中都会调用input_attach_handler()函数来匹配input_handler与input_device。

    在input_register_handler()中会遍历整个input_dev_list中的input_device与当前注册的input_handler相匹配。

    在input_register_device()中会遍历整个input_handler_list中的input_handler与当前注册的input_dev相匹配。

     input_attach_handler()中做两件事情:a.查看input_handler与input_device是否匹配;b.若匹配则调用handler->connect(handler,dev,id);

     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
    ",
    13                handler->name, kobject_name(&dev->dev.kobj), error);
    14 
    15     return error;
    16 }

    4. input_match_device()函数分析

    这里用到两个结构体,input_device结构体中有struct input_id id;

    struct input_handler 结构体中有const struct input_device_id *id_table;

    是否匹配主要是检测input_device_id中的数组成员与input_device中对应的数组成员是否一致。

    struct input_id {
        __u16 bustype;
        __u16 vendor;
        __u16 product;
        __u16 version;
    };
    struct input_device_id {
    
        kernel_ulong_t flags;
    
        __u16 bustype;
        __u16 vendor;
        __u16 product;
        __u16 version;
    
        kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
        kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
    
        kernel_ulong_t driver_info;
    };
     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     {
     9 
    10         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
    11             if (id->bustype != dev->id.bustype)
    12                 continue;
    13 
    14         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
    15             if (id->vendor != dev->id.vendor)
    16                 continue;
    17 
    18         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
    19             if (id->product != dev->id.product)
    20                 continue;
    21 
    22         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
    23             if (id->version != dev->id.version)
    24                 continue;
    25 
    26         MATCH_BIT(evbit,  EV_MAX);
    27         MATCH_BIT(keybit, KEY_MAX);
    28         MATCH_BIT(relbit, REL_MAX);
    29         MATCH_BIT(absbit, ABS_MAX);
    30         MATCH_BIT(mscbit, MSC_MAX);
    31         MATCH_BIT(ledbit, LED_MAX);
    32         MATCH_BIT(sndbit, SND_MAX);
    33         MATCH_BIT(ffbit,  FF_MAX);
    34         MATCH_BIT(swbit,  SW_MAX);
    35 
    36         if (!handler->match || handler->match(handler, dev))
    37             return id;
    38     }
    39 
    40     return NULL;
    41 }
    input_match_device()

    5. handler->connect(handler, dev, id)函数分析

    这里以evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)为例分析连接建立的过程。

    static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)

      -->evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//分配一个struct input_handle

      -->evdev->handle.dev = input_get_device(dev);//指向input_dev

      -->evdev->handle.handler = handler;//指向input_handler

      -->error = input_register_handle(&evdev->handle);

        -->list_add_tail_rcu(&handle->d_node, &dev->h_list);//与input_dev关联

        -->list_add_tail_rcu(&handle->h_node, &handler->h_list);//与input_handler关联

     建立连接后handler与device就通过handle关联起来了

     1 struct input_handle {
     2 
     3     void *private;
     4 
     5     int open;
     6     const char *name;
     7 
     8     struct input_dev *dev;
     9     struct input_handler *handler;
    10 
    11     struct list_head    d_node;
    12     struct list_head    h_node;
    13 };

  • 相关阅读:
    dbcc练习1
    查看底层硬件信息
    mkfs.xfs命令没找到
    Linux清空内存缓存
    常用CentOS 6/7 扩展源
    CentOS 6.x安装gcc 4.8/4.9/5.2
    根据SSH私钥(private key)计算SSH公钥(public key)
    Mac下Sublime Text Vim模式 方向键无法长按
    【转载】Hadoop和大数据:60款顶级大数据开源工具
    Linux Shell产生16进制随机数
  • 原文地址:https://www.cnblogs.com/yangjiguang/p/6042197.html
Copyright © 2011-2022 走看看