linux input 子系统分析 二

来源:互联网 发布:C语言画树 编辑:程序博客网 时间:2024/04/28 08:24

一. 各种注册函数

    因为分析一所讲的每种数据结构都代表一类对象,所以每种数据结构都会对应一个注册函数,他们都定义在子系统核心的input.c文件中。主要有三个注册函数
     input_register_device    向内核注册一个input设备
     input_register_handle    向内核注册一个handle结构
     input_register_handler   注册一个事件处理器
  1. input_register_device 注册一个input输入设备,这个注册函数在三个注册函数中是驱动程序唯一调用的。下面分析这个函数:

[cpp] view plaincopy
  1. int input_register_device(struct input_dev *dev)  
  2. {  
  3.     static atomic_t input_no = ATOMIC_INIT(0);    
  4.         //这个原子变量,代表总共注册的input设备,每注册一个加1,因为是静态变量,所以每次调用都不会清零的  
  5.     struct input_handler *handler;  
  6.     const char *path;  
  7.     int error;  
  8.   
  9.     __set_bit(EV_SYN, dev->evbit);  //EN_SYN 这个是设备都要支持的事件类型,所以要设置  
  10.   
  11.     /* 
  12.      * If delay and period are pre-set by the driver, then autorepeating 
  13.      * is handled by the driver itself and we don't do it in input.c. 
  14.      */  
  15.         // 这个内核定时器是为了重复按键而设置的  
  16.     init_timer(&dev->timer);  
  17.     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {  
  18.         dev->timer.data = (long) dev;  
  19.         dev->timer.function = input_repeat_key;  
  20.         dev->rep[REP_DELAY] = 250;  
  21.         dev->rep[REP_PERIOD] = 33;  
  22.         //如果没有定义有关重复按键的相关值,就用内核默认的  
  23.     }  
  24.   
  25.     if (!dev->getkeycode)  
  26.         dev->getkeycode = input_default_getkeycode;  
  27.     if (!dev->setkeycode)  
  28.         dev->setkeycode = input_default_setkeycode;  
  29.         //以上设置的默认函数由input核心提供  
  30.     dev_set_name(&dev->dev, "input%ld",  
  31.              (unsigned long) atomic_inc_return(&input_no) - 1);  
  32.         //设置input_dev中device的名字,这个名字会在/class/input中出现  
  33.     error = device_add(&dev->dev);  
  34.         //将device加入到linux设备模型中去  
  35.     if (error)  
  36.         return error;  
  37.   
  38.     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);  
  39.     printk(KERN_INFO "input: %s as %s\n",  
  40.         dev->name ? dev->name : "Unspecified device", path ? path : "N/A");  
  41.     kfree(path);  
  42.         //这个得到路径名称,并打印出来  
  43.     error = mutex_lock_interruptible(&input_mutex);  
  44.     if (error) {  
  45.         device_del(&dev->dev);  
  46.         return error;  
  47.     }  
  48.   
  49.     list_add_tail(&dev->node, &input_dev_list);  
  50.         // 将新分配的input设备连接到input_dev_list链表上  
  51.     list_for_each_entry(handler, &input_handler_list, node)  
  52.         input_attach_handler(dev, handler);  
  53.         //遍历input_handler_list链表,配对 input_dev 和 input_handler  
  54.         //input_attach_handler 这个函数是配对的关键,下面将详细分析  
  55.     input_wakeup_procfs_readers();  
  56.         // 和proc文件系统有关,暂时不考虑  
  57.     mutex_unlock(&input_mutex);  
  58.   
  59.     return 0;  
  60.    }  
   input_register_device完成的主要功能就是:初始化一些默认的值,将自己的device结构添加到linux设备模型当中,将input_dev添加到input_dev_list链表中,然后寻找合适的handler与input_handler配对,配对的核心函数是input_attach_handler。下面分析input_attach_handler函数:
[cpp] view plaincopy
  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.     if (handler->blacklist && input_match_device(handler->blacklist, dev))  
  7.         return -ENODEV;  
  8.         //blacklist是handler因该忽略的input设备类型,如果应该忽略的input设备也配对上了,那就出错了  
  9.     id = input_match_device(handler->id_table, dev);  
  10.         //这个是主要的配对函数,主要比较id中的各项,下面详细分析  
  11.     if (!id)  
  12.         return -ENODEV;  
  13.   
  14.     error = handler->connect(handler, dev, id);  
  15.         //配对成功调用handler的connect函数,这个函数在事件处理器中定义,主要生成一个input_handle结构,并初始化,还生成一个事件处理器相关的设备结构,后面详细分析  
  16.     if (error && error != -ENODEV)  
  17.         printk(KERN_ERR  
  18.             "input: failed to attach handler %s to device %s, "  
  19.             "error: %d\n",  
  20.             handler->name, kobject_name(&dev->dev.kobj), error);  
  21.         //出错处理  
  22.     return error;  
  23.  }  
    input_attach_handler的主要功能就是调用了两个函数,一个input_match_device进行配对,一个connect处理配对成功后续工作。
   下面分析input_match_device函数:
[cpp] view plaincopy
  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.         //函数传入的参数是所要配对handler的id_table,下面遍历这个id_table寻找合适的id进行配对  
  6.     for (; id->flags || id->driver_info; id++) {  
  7.         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)  
  8.             if (id->bustype != dev->id.bustype)  
  9.                 continue;  
  10.                 ......  
  11.                 //针对handler->id->flag,比较不同的类型  
  12.                 //如果比较成功进入下面的宏,否则进入下一个id  
  13.                 MATCH_BIT(evbit,  EV_MAX);  
  14.             ......    
  15.         MATCH_BIT(swbit,  SW_MAX);  
  16.   
  17.   
  18.         return id;  
  19.     }  
  20.  }  
    此函数主要是比较input_dev中的id和handler支持的id,这个存放在handler的id_table中。首先看id->driver_info有没有设置,如果设置了说明它匹配所有的id,evdev就是这个样的handler
    然后依据id->flag来比较内容,如果都比较成功进入MATCH_BIT,这个宏是用来按位进行比较的,功能是比较所支持事件的类型,只有所有的位都匹配才成功返回,否则进行下一个id的比较。
[cpp] view plaincopy
  1. #define MATCH_BIT(bit, max) \  
  2. for (i = 0; i < BITS_TO_LONGS(max); i++) \  
  3.     if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \  
  4.         break; \  
  5. if (i != BITS_TO_LONGS(max)) \  
  6.     continue;  
    这个宏对于每种事件类型,以及每种事件类型支持的编码所有的位都比较一次,看handler的id是否支持,如果有一个不支持就不会比较成功,进入下一个id进行比较。
    对于connect函数,每种事件处理器的实现都有差异,但原理都相同,因为触摸屏用的事件处理器为evdev,下面分析evdev的connect函数evdev_connect
[cpp] view plaincopy
  1. static int evdev_connect(struct input_handler *handler, struct input_dev *dev,  
  2.              const struct input_device_id *id)  
  3. {  
  4.         //此函数传入三个参数,分别是:handler,dev,id  
  5.     struct evdev *evdev;  
  6.     int minor;  
  7.     int error;  
  8.   
  9.   
  10.     for (minor = 0; minor < EVDEV_MINORS; minor++)  
  11.         if (!evdev_table[minor])  
  12.             break;  
  13.         //EVDEV_MINORS为32,说明evdev这个handler可以同时有32个输入设备和他配对,evdev_table中以minor(非次设备号,但是有一个换算关系)存放evdev结构体,后面要详细分析这个结构体  
  14.     if (minor == EVDEV_MINORS) {  
  15.         printk(KERN_ERR "evdev: no more free evdev devices\n");  
  16.         return -ENFILE;  
  17.     }  
  18.         //这个说明32个位置全都被占用了,连接失败  
  19.     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);  
  20.         //分配一个evdev结构体,这个结构体是evdev事件处理器特有的,后面会详细分析  
  21.     if (!evdev)  
  22.         return -ENOMEM;  
  23.   
  24.   
  25.     INIT_LIST_HEAD(&evdev->client_list);  
  26.     spin_lock_init(&evdev->client_lock);  
  27.     mutex_init(&evdev->mutex);  
  28.     init_waitqueue_head(&evdev->wait);  
  29.         //初始化结构体的一些成员  
  30.     dev_set_name(&evdev->dev, "event%d", minor);  
  31.         //这个是设置evdev中device的名字,他将出现在/class/input中。  
  32.         //前面也有一个device是input_dev的,名字是input(n),注意与他的不同  
  33.         //这个结构是配对后的虚拟设备结构,没有对应的硬件,但是通过它可以找到相关的硬件  
  34.     evdev->exist = 1;  
  35.     evdev->minor = minor;  
  36.   
  37.   
  38.     evdev->handle.dev = input_get_device(dev);  
  39.     evdev->handle.name = dev_name(&evdev->dev);  
  40.     evdev->handle.handler = handler;  
  41.     evdev->handle.private = evdev;  
  42.         //因为evdev中包含handle了,所以初始化它就可以了,这样就连接了input_handler与input_dev  
  43.     evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //注意:这个minor不是真正的次设备号,还要加上EVDEV_MINOR_BASE  
  44.     evdev->dev.class = &input_class;  
  45.     evdev->dev.parent = &dev->dev;  
  46.         //配对生成的device,父设备是与他相关连的input_dev  
  47.     evdev->dev.release = evdev_free;  
  48.     device_initialize(&evdev->dev);  
  49.   
  50.   
  51.     error = input_register_handle(&evdev->handle);  
  52.         //注册handle结构体,这个函数后面详细分析  
  53.     if (error)  
  54.         goto err_free_evdev;  
  55.   
  56.   
  57.     error = evdev_install_chrdev(evdev);  
  58.         //这个函数只做了一件事,就是把evdev结构保存到evdev_table中,这个数组也minor为索引  
  59.     if (error)  
  60.         goto err_unregister_handle;  
  61.   
  62.   
  63.     error = device_add(&evdev->dev);  
  64.         //注册到linux设备模型中  
  65.     if (error)  
  66.         goto err_cleanup_evdev;  
  67.   
  68.   
  69.     return 0;  
  70.   
  71.   
  72.   err_cleanup_evdev:  
  73.     evdev_cleanup(evdev);  
  74.   err_unregister_handle:  
  75.     input_unregister_handle(&evdev->handle);  
  76.   err_free_evdev:  
  77.     put_device(&evdev->dev);  
  78.     return error;  
  79. }  
    evdev_connect函数做配对后的善后工作,分配一个evdev结构体,并初始化相关成员,evdev结构体中有input_handle结构,初始化并注册之。
 2. input_register_handle 注册一个input_handle结构体,比较简单
[cpp] view plaincopy
  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.     /* 
  9.      * We take dev->mutex here to prevent race with 
  10.      * input_release_device(). 
  11.      */  
  12.     error = mutex_lock_interruptible(&dev->mutex);  
  13.     if (error)  
  14.         return error;  
  15.     list_add_tail_rcu(&handle->d_node, &dev->h_list);  
  16.         //将handle的d_node,链接到其相关的input_dev的h_list链表中  
  17.     mutex_unlock(&dev->mutex);  
  18.   
  19.   
  20.     list_add_tail(&handle->h_node, &handler->h_list);  
  21.         //将handle的h_node,链接到其相关的input_handler的h_list链表中  
  22.     if (handler->start)  
  23.         handler->start(handle);  
  24.   
  25.   
  26.     return 0;  
  27. }  
    这个函数基本没做什么事,就是把一个handle结构体通过d_node链表项,分别链接到input_dev的h_list,input_handler的h_list上。以后通过这个h_list就可以遍历相关的input_handle了。
 3. input_register_handler 注册一个input_handler结构体
[cpp] view plaincopy
  1. int input_register_handler(struct input_handler *handler)  
  2.  {  
  3.     struct input_dev *dev;  
  4.     int retval;  
  5.   
  6.   
  7.     retval = mutex_lock_interruptible(&input_mutex);  
  8.     if (retval)  
  9.         return retval;  
  10.   
  11.   
  12.     INIT_LIST_HEAD(&handler->h_list);  
  13.   
  14.   
  15.     if (handler->fops != NULL) {  
  16.         if (input_table[handler->minor >> 5]) {  
  17.             retval = -EBUSY;  
  18.             goto out;  
  19.         }  
  20.         input_table[handler->minor >> 5] = handler;  
  21.     }  
  22.         //input_table,每个注册的handler都会将自己保存到这里,索引值为handler->minor右移5为,也就是除以32  
  23.         //为什么会这样呢,因为每个handler都会处理最大32个input_dev,所以要以minor的32为倍数对齐,这个minor是传进来的handler的MINOR_BASE  
  24.         //每一个handler都有一个这一个MINOR_BASE,以evdev为例,EVDEV_MINOR_BASE = 64,可以看出系统总共可以注册8个handler  
  25.     list_add_tail(&handler->node, &input_handler_list);  
  26.         //连接到input_handler_list链表中  
  27.     list_for_each_entry(dev, &input_dev_list, node)  
  28.         input_attach_handler(dev, handler);  
  29.         //又是配对,不过这次遍历input_dev,和注册input_dev过程一样的  
  30.     input_wakeup_procfs_readers();  
  31.   
  32.   
  33.  out:  
  34.     mutex_unlock(&input_mutex);  
  35.     return retval;  
  36. }  
    这个函数其实和input_register_device大同小异,都是注册,都要配对。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 买到假货淘宝商家已关店怎么办 手机刷错系统了怎么办 苹果手机成砖了怎么办 苹果6p变砖头怎么办 苹果刷成石头了怎么办 苹果手机更新成了砖头怎么办 京东售后好慢怎么办 京东商品超过售后期怎么办 京东过了售后期怎么办 京东售后不处理怎么办 京东售后不让退货怎么办 天猫盒子遥控器丢了怎么办 淘宝店铺的客服不理人怎么办 淘宝假货下架了怎么办 淘宝不让发布本地生活服务了怎么办 淘宝删除差评后店家不返现怎么办 天猫店家迟迟不发货怎么办 淘宝下单后店家说缺货怎么办 用淘宝把话费冲到空号上怎么办 d速快递没有网点怎么办 京东买的货没收到怎么办 淘宝物流显示已揽件就是不动怎么办 淘宝查不到物流信息怎么办 快递物流信息更新错怎么办 淘宝上查不到物流怎么办 微信买的东西不给退怎么办 微信购物已收货怎么办 微信买东西不退怎么办 银行经营贷款资金回流怎么办 淘宝有运费险换货怎么办 淘宝有运费险的换货怎么办 淘宝换货一直不发货怎么办 淘宝申请换货卖家不发货怎么办 淘宝买家泄露卖家信息怎么办 高仿苹果没内存怎么办 高仿苹果7太卡怎么办 天猫客服处理不了怎么办 美团顾客电话打不通怎么办 美团众包顾客电话打不通怎么办 天猫退货商家拒绝退款怎么办 中关村买电脑被骗了怎么办