zoukankan      html  css  js  c++  java
  • 输入子系统分析

    参考文章:https://www.cnblogs.com/lifexy/p/7542989.html

    输入子系统本身也是个驱动,在input_init入口函数可以看到他注册了input类,并且注册了input这个字符设备:

    也就是说输入子系统作为编译进内核的一部分,在启动内核的时候就被加载了,input_init函数会被调用,生成input类和input设备,为后续注册进输入子系统的设备做好准备。

    可以看到注册input设备的时候,一起注册了一个input_fops:

     input_fops定义如下:

     里面只注册了一个.open函数。

    之前自己定义fops的时候,会自己注册open、read、write函数,当应用程序调用open函数时,内核就会调用驱动中相应的open函数。

    这个input_open_file函数应该也是同理,当我们使用输入子系统时(在驱动程序中向输入子系统注册了设备),当应用程序调用open函数时,内核会调用input_open_file函数:

     input_open_file函数从input_table[]中根据次设备号取出一项,赋值给handler:

    再通过handler找到他的成员fops复制给new_fops:

     最终执行这个new_fops的open函数:

     这里的关键点在于,要从input_table[]中找到对应的handler。

    在input.c里查找代码可以发现,input_table[]在input_register_handler中被赋值:

     那么什么时候会调用input_register_handler函数,给input_table赋值?在内核文件中搜索可以发现:

     

     

    他们都是在驱动程序的入口被注册的。

    猜想:和input子系统类似,evdev、joydev、tsdev等驱动程序在源码阶段就已经编译进内核,因此内核启动时会像input那样注册设备,执行各自的init程序,从而填充input_table[]数组,这部分代码不需要自己实现。

    输入子系统的框架如下图:

     之前介绍的是内核自带的驱动处理相关的代码,要让这部分代码执行,还需要和具体的设备“配对”,这样当具体的设备产生事件时,才能执行相应的驱动处理函数。

    举个例子,看看evdev_handler的内容,evdev_handler是input_handler类型的结构体,属于上图右边驱动处理的一种:

    假设内核已经启动,上述各种内核自带的驱动程序已经被加载,也就是说input_table[]数组中已经填充了各种handler,包括evdev_handler。这时装载自己写的模块,向输入子系统注册input_dev。

    对输入子系统来说,如果有设备(input_dev)注册(设备注册由我们自己实现),就会根据input_handler的.id_table判断这个handler是否支持这个dev,如果支持的话就执行.connect函数将设备input_dev和某个input_handler建立连接。

    这个将input_dev和input_handler匹配的过程可以通过比对他们各自的注册函数知道细节。

    对于input_dev来说(input_register_device函数):

     从input_handler_list这个链表中取出每一项input_handler结构体赋值给handler这个局部变量,再通过input_attach_handler函数判断handler和dev能否匹配。

    对于input_handler来说(input_register_handler函数):

     从input_dev_list这个链表中取出每一项input_dev结构体赋值给dev这个局部变量,再通过input_attach_handler函数判断handler和dev能否匹配。

    可以看出,不管是向子系统注册input_dev还是input_handler,都会从对面的链表中取出每一项,来判断能否支持自己。

    看一下input_attach_handler函数的具体实现:

    可以看到先调用了input_match_device函数,如果匹配,再调用handler中的.connect成员进行连接,和之前说的相符。

    再看看.connnect是如何连接双方的:

     1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
     2 {
     3   ... ...
     4   for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); //查找驱动设备的子设备号
     5     if (minor == EVDEV_MINORS) {  // EVDEV_MINORS=32,所以该事件下的驱动设备最多存32个,
     6         printk(KERN_ERR "evdev: no more free evdev devices
    ");
     7         return -ENFILE;                //没找到驱动设备
     8     }
     9    ... ...
    10    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //分配一个input_handle,包含在evdev结构体里
    11    ... ...
    12   evdev->handle.dev = dev;              //指向input_dev
    13   evdev->handle.name = evdev->name;
    14   evdev->handle.handler = handler;    //指向input_handler
    15   evdev->handle.private = evdev;
    16   sprintf(evdev->name, "event%d", minor);    //保存驱动设备名字, event%d
    17   ... ...
    18   devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),  //将主设备号和次设备号转换成dev_t类型
    19   cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name); 
    //在input类下创建驱动设备(在配对成功后才会创建设备驱动,合理) 20 21   ... ... 22   error = input_register_handle(&evdev->handle); //注册这个input_handle结构体 23 24   ... ... 25 }

    再看看input_register_handle是怎么注册的:

    把handle->d_node(对应dev)添加到input_dev的h_list的末尾

    把handle->h_node(对应handler)添加到input_dev的h_list的末尾

    猜测:某一设备不一定只对应一个驱动程序,某个驱动程序也不一定只支持一个设备,所以这里用链表。

    这样input_dev可以通过input_handle找到input_handler,input_handler也能通过input_handle找到input_dev,由此建立了连接。

    参考文章:https://www.cnblogs.com/lifexy/p/7542989.html

  • 相关阅读:
    JUC-ThreadPool线程池
    JUC-JUC强大的辅助类讲解(Semaphore、CyclicBarrier、CountDownLatch)
    JUC—Callable接口
    集合与数组之间相互转化
    [UnityShader基础]07.MaterialPropertyDrawer
    [UnityShader基础]06.#pragma multi_compile
    [Unity优化]UI优化(三):GraphicRebuild
    [UnityAPI]SerializedObject类 & SerializedProperty类
    [Unity算法]点是否在多边形范围内
    [UGUI]圆形Image
  • 原文地址:https://www.cnblogs.com/physworld/p/14979557.html
Copyright © 2011-2022 走看看