zoukankan      html  css  js  c++  java
  • 驱动开发 —— 输入子系统(工作逻辑分析)

    输入子系统的工作原理和代码分析
    目的:
    a,学会如何分析内核中子系统的代码,从而可以举一反三
    b,整体把握框架思想,理解分层中各层的配合方式
    c,掌握子系统,增强排错能力

     分析代码

    1、input核心层:input.c

     1 subsys_initcall(input_init);  //优先级比module高
     2 module_exit(input_exit);
     3 
     4     input_init()
     5         |
     6         //注册类,类似于class_create();
     7         err = class_register(&input_class);
     8         //在/proc创建相关文件 bus/input/devices  handlers
     9         err = input_proc_init();
    10         //申请设备号
    11         err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
    12                      INPUT_MAX_CHAR_DEVICES, "input");

    第11行通过register_chrdev_region创建驱动设备,其中变量INPUT_MAJOR=13,所以就是创建

    了一个主设备为13的“input”设备。在新版本的内核中,fops接口在input_handler层的evdev.c中。

    总结:
      1),注册了主设备号
      2),注册input class

    2、input handler层:evdev.c

    进入evdev.c的初始化函数,

    static int __init evdev_init(void)
    {
           return input_register_handler(&evdev_handler);  //注册
    }

    在内核中搜索一下input_register_handler,看看这个函数都被谁调用:

     如上图所示,有evdev.c(事件设备),tsdev.c(触摸屏设备),joydev.c(joystick操作杆设备),keyboard.c(键盘设备),mousedev.c(鼠标设备) 这5个内核自带的设备处理函数注册到input子系统中

    进入input_register_handler分析(这个函数在核心层input.c定义,提供接口给设备层使用):

     1 module_init(evdev_init);
     2 module_exit(evdev_exit);
     3 
     4 static struct input_handler evdev_handler = {
     5     .event         = evdev_event,
     6     .events        = evdev_events,
     7     .connect       = evdev_connect,
     8     .disconnect    = evdev_disconnect,
     9     .legacy_minors = true,
    10     .minor         = EVDEV_MINOR_BASE,
    11     .name          = "evdev",
    12     .id_table      = evdev_ids,
    13 };
    14 
    15         evdev_init(void)
    16             |
    17             input_register_handler(&evdev_handler);
    18                     |
    19                     //初始化h_list  
    20                     INIT_LIST_HEAD(&handler->h_list);
    21 
    22                     //将当前的handler加入到一个input_handler_list
    23                     list_add_tail(&handler->node, &input_handler_list);
    24 
    25                     //遍历链表input_dev_list
    26                     list_for_each_entry(dev, &input_dev_list, node)
    27                         input_attach_handler(dev, handler);
    28                             |
    29                             // 将当前的hanler和input dev进行匹配, event handler能够匹配所有的input dev
    30                             id = input_match_device(handler, dev);
    31 
    32                             //匹配成功,之后要调用handler中connect方法
    33                             // 实际就是event handler,实际调用了evdev_connect
    34                             error = handler->connect(handler, dev, id);
    35 
    36                     //将当前的handler加入到/proc/bus/input/handlers文件中
    37                     input_wakeup_procfs_readers();

    在事件设备处理驱动中,调用了核心层的input_register_handler来注册evdev_handler。

    1)我们先来看看这个evdev_handler变量是个什么东西?

     1 /**
     2  * struct input_handler - implements one of interfaces for input devices
     3  * @private: driver-specific data
     4  * @event: event handler. This method is being called by input core with
     5  *    interrupts disabled and dev->event_lock spinlock held and so
     6  *    it may not sleep
     7  * @events: event sequence handler. This method is being called by
     8  *    input core with interrupts disabled and dev->event_lock
     9  *    spinlock held and so it may not sleep
    10  * @filter: similar to @event; separates normal event handlers from
    11  *    "filters".
    12  * @match: called after comparing device's id with handler's id_table
    13  *    to perform fine-grained matching between device and handler
    14  * @connect: called when attaching a handler to an input device
    15  * @disconnect: disconnects a handler from input device
    16  * @start: starts handler for given handle. This function is called by
    17  *    input core right after connect() method and also when a process
    18  *    that "grabbed" a device releases it
    19  * @legacy_minors: set to %true by drivers using legacy minor ranges
    20  * @minor: beginning of range of 32 legacy minors for devices this driver
    21  *    can provide
    22  * @name: name of the handler, to be shown in /proc/bus/input/handlers
    23  * @id_table: pointer to a table of input_device_ids this driver can
    24  *    handle
    25  * @h_list: list of input handles associated with the handler
    26  * @node: for placing the driver onto input_handler_list
    27  *
    28  * Input handlers attach to input devices and create input handles. There
    29  * are likely several handlers attached to any given input device at the
    30  * same time. All of them will get their copy of input event generated by
    31  * the device.
    32  *
    33  * The very same structure is used to implement input filters. Input core
    34  * allows filters to run first and will not pass event to regular handlers
    35  * if any of the filters indicate that the event should be filtered (by
    36  * returning %true from their filter() method).
    37  *
    38  * Note that input core serializes calls to connect() and disconnect()
    39  * methods.
    40  */
    41 struct input_handler {
    42 
    43     void *private;
    44 
    45     void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    46     void (*events)(struct input_handle *handle,
    47                const struct input_value *vals, unsigned int count);
    48     bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    49     bool (*match)(struct input_handler *handler, struct input_dev *dev);
    50     int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    51     void (*disconnect)(struct input_handle *handle);
    52     void (*start)(struct input_handle *handle);
    53 
    54     bool legacy_minors;
    55     int minor;
    56     const char *name;
    57 
    58     const struct input_device_id *id_table;
    59 
    60     struct list_head    h_list;
    61     struct list_head    node;
    62 };
    input.h -- input_handler

    如上代码:evdev_handler是由input_handler初始化后的一个对象,描述了事件设备处理的相关信息:

      .minor:用来存放次设备号,EVDEV_MINOR_BASE=64;

         .id_table : 表示能支持哪些输入设备,比如某个驱动设备的input_dev的id和某个input_handler的id_table相匹配,就会调用.connect连接函数

      .connect:连接函数,将设备input_dev和某个input_handler建立连接

    2)进入input_register_handler

       初始化链表input_handler_list,将handler加入链表,遍历链表,匹配对眼的handler与dev,匹配成功后调用connect;

    总结:
      1,注册了evdev_handler
      2, 遍历input dev list,并行匹配,恒匹配成功,自动会调用handler中connect方法--- evdev_connect

    3、input dev层:simple_input.c

     1 input_register_device(inputdev);
     2         |
     3         //将input dev加入到链表input_dev_list
     4         list_add_tail(&dev->node, &input_dev_list);
     5 
     6         //遍历input handler链表,进行匹配
     7         list_for_each_entry(handler, &input_handler_list, node)
     8             input_attach_handler(dev, handler);
     9                     |
    10                 //匹配成功,之后要调用handler中connect方法
    11                 // 实际就是event handler,实际调用了evdev_connect
    12                 error = handler->connect(handler, dev, id);

    在设备注册函数input_register_device中做着与处理函数input_register_handler相同的事:

    将要注册的设备放在input_dev_list链表中,遍历handler链表,调用匹配函数对两者id_table进行

    判断,若支持就connect。

         所以,不管先注册input_dev还是input_handler最终都会调用到匹配函数进行判断与connect。对于内核自带的handler驱动,一般都是先注册进去的。

    4、分析evdev_handler->connect函数是怎样建立连接的,如下:

     1 evdev_connect(struct input_handler *handler, struct input_dev *dev,
     2              const struct input_device_id *id)
     3     |
     4     //找到一个没有被使用的次设备号, 从64开始, 65,66 
     5     minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
     6 
     7     // 实例化 一个evdev对象
     8     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
     9     //初始化evdev对象
    10     INIT_LIST_HEAD(&evdev->client_list);
    11     spin_lock_init(&evdev->client_lock);
    12     mutex_init(&evdev->mutex);
    13     //等待队列是完成阻塞
    14     init_waitqueue_head(&evdev->wait);
    15     evdev->exist = true;
    16 
    17     dev_no = minor;
    18     dev_no -= EVDEV_MINOR_BASE; //减去了64
    19 
    20     // 创建设备文件/dev/event0,1,2
    21     dev_set_name(&evdev->dev, "event%d", dev_no);
    22     evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);// 12, 64
    23     evdev->dev.class = &input_class;
    24     evdev->dev.parent = &dev->dev;
    25     evdev->dev.release = evdev_free;
    26     device_initialize(&evdev->dev)
    27     device_add(&evdev->dev); 
    28     //以上代码和device_create的内部实现是一样的
    29 
    30     //利用handle记录input device和input handler
    31     evdev->handle.dev = input_get_device(dev);
    32     evdev->handle.name = dev_name(&evdev->dev);
    33     evdev->handle.handler = handler;  //将handler存入handle的成员handler中
    34     //你中有我,我中有你,指向自己,可以通过此成员找到结构体
    35     evdev->handle.private = evdev;
    36 
    37     
    38     //将儿子handle关联到父亲(input handler)和母亲(input dev)
    39     error = input_register_handle(&evdev->handle);
    40                     |
    41                 list_add_tail_rcu(&handle->d_node, &dev->h_list);
    42                 list_add_tail_rcu(&handle->h_node, &handler->h_list);
    43 
    44     
    45     //初始化了cdev,完成了fops, 为用户提供文件io
    46     cdev_init(&evdev->cdev, &evdev_fops);
    47     evdev->cdev.kobj.parent = &evdev->dev.kobj;
    48     error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);

      

    总结:
      1,分配evdev,并初始化,记录input device和handler的关系
      2,创建设备节点/dev/event0
      3, 注册cdev,并实现fops

      4,关系:
        多个input device可以对应一个event handler
        一个input device对应一个 evdev,对应于一个设备节点/dev/event0,1,2
      5, 所有的设备节点调用open,read,write文件io的时候
        实际是调用cdev中fops中各个接口,最终都调用了

     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                 };

    着重分析一下input_dev和input_handler与handle之间的关系:

    结合connect的代码中可以看到:

     1 struct input_handle {   //input.h
     2     void *private;
     3     int open;
     4     const char *name;
     5 
     6     struct input_dev *dev;
     7     struct input_handler *handler;
     8 
     9     struct list_head    d_node;
    10     struct list_head    h_node;
    11 };
    12 
    13 struct input_handle handle;   //evdev.c
    14 //我们在这把handle比作input_handler和input_dev的儿子,
    15 //handle的存在意义就是作为父母间的联结,通过儿子,父母间可
    16 //以相互沟通寻找。
    17 //可以看到handle体内有着指向父母dev与handler的指针,通过指
    18 //针可以记录父母的信息
    19 //两者的链表h_list都指向同一个handle结构体,即分别指向
    20 //d_node,h_node,然后通过h_list来找到handle的成员dev和
    21 //handler,便能找到双方,建立了连接
    22 //eg:input_dev:h_list-> handle.d_node ->handle.handler ->input_handler
    1 //将儿子handle关联到父亲(input handler)和母亲(input dev)
    2      error = input_register_handle(&evdev->handle);
    3                      |
    4                  list_add_tail_rcu(&handle->d_node, &dev->h_list);
    5                  list_add_tail_rcu(&handle->h_node, &handler->h_list);
    6 
    7 //在第4行中, 因为handle->dev指向input_dev驱动设备,所以就是将handle->d_node放入到
    //input_dev驱动设备的h_list链表中,即input_dev驱动设备的h_list链表就指向handle->d_node
    8 //在第5行中, 同样, input_handler驱动处理结构体的h_list也指向了handle->h_node

    最终如下图所示:

    更加直观的图示:

    5、应用程序是如何调用到输入子系统中去的

     以应用程序调用open为例:

    在open之后会返回一个文件描述符,文件描述符实际上是一个fd_table数组的一个下标,

    其中0/1/2被系统的标准输入输出与出错占用了。数组里面存放着struct file *file。

    在每次open之后,系统都会创建一个struct file结构体,里面包含文件属性,IO接口等(path、flag、mode、fops、fpos等)。

    cdev详解

    1)每一个文件都会有inode,里面包含文件的权限、类型、大小以及主次设备号等;

    当应用层调用open一个设备节点时,系统会找到对应的inode,进而读取到主次设备号,

    遍历cdev链表,找到设备号对应的cdev;

    2)在输入子系统中,会通过register_chrdev和cdev_add将cdev插入到链表中,cdev中包含

    了设备号与在输入子系统中初始化的xx_ops操作函数;

    3)若应用层找到了对应的cdev,就会调用在输入子系统中的xx_open;

    4)由上可以看到,应用层open调用过程:先找到inode获取设备号,在VFS的cdev链表中遍历

      进而调用到cdev中的ops操作函数,这个过程是比较长的。如果接下来要调用read,又得重复以上过程。

      内核为了在后面几次调用ops操作函数时,能快速调用,就在第一次调用open时,把cdev->ops复制到

      struct file结构体里面的f_ops中,并将fd与file结构体关联。这样在下次调用read(fd)等其他函数时,就可以通过

      fd_table[]的下标 --- fd,直接调用file中的fops就可以了。

     由上可以应用层调用open最终调用到cdev->ops中的xx_open。对于输入子系统,cdev在哪里定义?

    对于evdev事件驱动,input_handler层的evdev.c中,

    evdev.c

     1 //匹配成功,调用connect建立input_dev与input_handler的连接
     2 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
     3              const struct input_device_id *id)
     4 {
     5     .....
     6     cdev_init(&evdev->cdev, &evdev_fops);
     7     evdev->cdev.kobj.parent = &evdev->dev.kobj;
     8     error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
     9    .....
    10 }
     1 input handler 层:evdev.c
     2         cdev;
     3        xxx_ops = {
     4             .open = xxx_open,
     5             .write = xxx_write,
     6        }
     7 
     8 
     9      static const struct file_operations evdev_fops = {
    10                     .owner        = THIS_MODULE,
    11                     .read        = evdev_read,
    12                     .write        = evdev_write,
    13                     .poll        = evdev_poll,
    14                     .open        = evdev_open,
    15      }
    16     
    17     实际最终调用了evdev_open();
    18         |
    19         // 实际cdev是谁,就是evdev_connect注册的那个
    20         struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);   (1)
    21 
    22         // 通过儿子,找到老母input device  bufsize为handle里面dev的个数
    23         unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
    24 
    25         // size就包含了很多input_event
    26         unsigned int size = sizeof(struct evdev_client) +
    27                         bufsize * sizeof(struct input_event);
    28 
    29         struct evdev_client *client;
    30         //client不是客户端,而是一个内存
    31         // 分配一个client对象,描述一个缓冲队列,存放的就是input_event
    32         client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
    33 
    34         // client中有一个缓冲区
    35         client->bufsize = bufsize;
    36         spin_lock_init(&client->buffer_lock);
    37         //在client中记录evdev
    38         client->evdev = evdev;
    39         // 将client加入到evdev中一个小链表
    40         evdev_attach_client(evdev, client);
    41                 |
    42                 list_add_tail_rcu(&client->node, &evdev->client_list);
    43 
    44         // 将client记录到file,方面其他的接口使用
    45         file->private_data = client;

    总结:
      1,为输入设备分配一个缓冲区evdev_client,用户存放input device层上报的数据
      2,evdev_client记录到evdev中
      3,evdev_client记录到file中,方面其他的接口使用

     第20行(1);用程序在open时,会根据主次设备号找到cdev,cdev属于evdev对象的一部分,通过cdev就可找到evdev

    由此可以了解到对应关系:一个设备节点/dev/evnet0对应一个cdev,一个cdev对应一个evdev,通过connect,evdev对应一个input_dev,

    一个input_dev对应一个input_handler,input_handler可以对应多个input_dev

    evdev里面包含:cdev、client、dev、handler、connect。它的作用就是将所有相关的信息记录下来,多个层次联系起来,相当于一个大的全局设备对象

    结合下图理解:

    6、应用程序中read,是如何获取到数据的

     1      read(fd, &event, sizeof(struct input_event));
     2     ---------------------------------------------
     3     vfs
     4         sys_read();
     5             file->f_ops->read();
     6     -------------------------------------------
     7     evdev.c
     8     static const struct file_operations evdev_fops = {
     9                     .read        = evdev_read,
    10         evdev_read(struct file *file, char __user *buffer,
    11               size_t count, loff_t *ppos)
    12             |
    13         // 获取到open中分配的缓冲区对象
    14         struct evdev_client *client = file->private_data;
    15         //获取到evdev
    16         struct evdev *evdev = client->evdev;
    17         //表示一个数据包,要给用户
    18         struct input_event event;
    19 
    20         for (;;) {
    21             // 实现非阻塞
    22             if (client->packet_head == client->tail &&
    23                 (file->f_flags & O_NONBLOCK))
    24                 return -EAGAIN;
    25 
    26                 while (read + input_event_size() <= count &&
    27                 // 1从缓冲区获取数据,存放在 input_event数据包
    28                    evdev_fetch_next_event(client, &event)) {
    29                             |
    30                             *event = client->buffer[client->tail++];
    31                 // 2, 将数据上报给用户
    32                 if (input_event_to_user(buffer + read, &event))
    33                             |
    34                             copy_to_user(buffer, event, sizeof(struct input_event)
    35 
    36                 // 3,统计上报多少数据
    37                 read += input_event_size();
    38             }
    39 
    40             // 如果当前是阻塞模式
    41             if (!(file->f_flags & O_NONBLOCK)) {
    42                 //等待---休眠,需要被唤醒,有数据时候唤醒
    43                 error = wait_event_interruptible(evdev->wait,
    44                         client->packet_head != client->tail ||
    45                         !evdev->exist || client->revoked);
    46         }

    总结:
      1,如果没有数据,就会休眠等待
      2,如果有数据,就从缓冲区client->buffer[client->tail++]拿数据
        通过copy_to_user上报给用户

    疑问:
      数据到底是如何存放在缓冲区的
      等待队列是谁唤醒的
        input_report_key(inputdev, pdesc->key_code, 0);
        input_sync(inputdev);//上报数据结束

    7、input_event上报数据的过程

    在我们写的simple_input_drv.c中(input_dev层),上报数据的代码如下;

     1 //中断处理函数
     2 irqreturn_t input_irq_handler(int irqno, void *devid)
     3 {
     4     //区分不同的按键
     5     ...
     6     //在设备树中获取gpio号
     7     ...
     8     //通过gpio号获取gpio引脚状态
     9     ...
    10     if(value){
    11         //上报数据,输入子系统默认抬起为0
    12         input_report_key(inputdev, pdesc->key_code, 0);  //为什么是1?
    13             //调用:input_event(dev, EV_KEY, code, !!value);
    14         input_sync(inputdev);  //同步,上报数据结束
    15     }else{
    16         //上报数据,输入子系统默认按下为1
    17         input_report_key(inputdev, pdesc->key_code, 1);
    18         input_sync(inputdev);  //同步,上报数据结束
    19     }
    20 
    21     //阻塞的相关操作,上层会去实现
    22     return IRQ_HANDLED;
    23 }

    上报数据的过程是怎么实现的?

    进入input_report_key (最终调用到核心层input.c的input_event)

     1     input_report_key(inputdev, pdesc->key_code, 0);
     2     input_sync(inputdev);//上报数据结束
     3         |  //input_event(dev, EV_KEY, code, !!value);
     4         input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);// 上报数据
     5                 |
     6                 input_handle_event(dev, type, code, value);
     7                         |
     8                         // 如果将数据交给input handler去处理
     9                         if (disposition & INPUT_PASS_TO_HANDLERS) {    //disposition:处理
    10                             struct input_value *v;
    11                             //将input device获取到数据暂存一下input value
    12                             v = &dev->vals[dev->num_vals++];
    13                             v->type = type;
    14                             v->code = code;
    15                             v->value = value;
    16 
    17 
    18                             input_pass_values(dev, dev->vals, dev->num_vals)
    19                                 |
    20                                 // 从input device中获取到input handle
    21                             else {
    22     
    23                             list_for_each_entry_rcu(handle, &dev->h_list, d_node)
    24                                 if (handle->open)
    25                                     count = input_to_handler(handle, vals, count);
    26                                             |
    27                                         // 通过handle儿子找到handler父亲
    28                                         struct input_handler *handler = handle->handler;
    29                                         // 如果有events函数指针,那就调用
    30                                         if (handler->events)
    31                                             handler->events(handle, vals, count);
    32                                         else if (handler->event)//否则就调用event();
    33                                             for (v = vals; v != end; v++)
    34                                                 handler->event(handle, v->type, v->code, v->value);                       
    37                         }

    总结: 如果将数据上报,最终是调用handler中events()或者event()

      其中evdev_event()是evdev.c(事件驱动) 的evdev_handler->.event成员,如下所示:

    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 };
    evdev_handler

    8、evdev填充缓冲区数据的过程

     1 将数据上报,最终是调用handler中events()或者event()
     2 实际是evdev.c
     3 .event   = evdev_event,
     4 .events  = evdev_events,
     5 static void evdev_events(struct input_handle *handle,
     6              const struct input_value *vals, unsigned int count)
     7 {
     8     // 拿到evdev,肯定要拿到缓冲区
     9     struct evdev *evdev = handle->private;
    10     struct evdev_client *client;
    11 
    12     // 获取到缓冲evdev_client
    13     if (client)
    14         evdev_pass_values(client, vals, count, time_mono, time_real);
    15     else  //进入else
    16     list_for_each_entry_rcu(client, &evdev->client_list, node)
    17         evdev_pass_values(client, vals, count,
    18                   time_mono, time_real);
    19             |
    20             // 通过client获取到evdev
    21             struct evdev *evdev = client->evdev;
    22             
    23             const struct input_value *v;
    24             struct input_event event;
    25 
    26             event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real);
    27             for (v = vals; v != vals + count; v++) {
    28                 // 将input device上报的数据获取到,并且封装成input event对象
    29                 event.type = v->type;
    30                 event.code = v->code;
    31                 event.value = v->value;
    32                 // 将input event数据存放到缓冲区中
    33                 __pass_event(client, &event);
    34                         |
    35                         client->buffer[client->head++] = *event;
    36                         client->head &= client->bufsize - 1;
    37                 46                     // 如果调用 input_sync()
    47                     if (v->type == EV_SYN && v->code == SYN_REPORT)
    48                         wakeup = true;
    49                 }
    50 
    51                     // 唤醒等待队列
    52                     if (wakeup)
    53                         wake_up_interruptible(&evdev->wait);

    总结:
      input_report_key(inputdev, pdesc->key_code, 0);
        //将输入设备产生数据交给input handler,调用events();将数据存放在缓冲区client->buffer[client->head++] = *event;
      input_sync(inputdev);//上报数据结束
        // 唤醒等待队列,表示输入设备上报数据完毕

    9、本节分析总结:

    1.注册输入子系统,进入input_init():

      1)创建主设备号为13的"input"字符设备

    err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

     

    2.open打开驱动,进入input_open_file():

      1)更新设备的file_oprations

    file->f_op=fops_get(handler->fops);

      2)执行file_oprations->open函数

    err = new_fops->open(inode, file);

    3.注册input_handler,进入input_register_handler():

      1)添加到input_table[]处理数组中

    input_table[handler->minor >> 5] = handler;

      2)添加到input_handler_list链表中

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

      3)判断input_dev的id,是否有支持这个驱动的设备

    list_for_each_entry(dev, &input_dev_list, node)   //遍历查找input_dev_list链表里所有input_dev
    input_attach_handler(dev, handler);         //判断两者id,若两者支持便进行连接。

    4.注册input_dev,进入input_register_device():

       1)放在input_dev_list链表中

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

      2)判断input_handler的id,是否有支持这个设备的驱动

    list_for_each_entry(handler, &input_handler_list, node)  //遍历查找input_handler_list链表里所有input_handler
    input_attach_handler(dev, handler);                      //判断两者id,若两者支持便进行连接。

    5.判断input_handlerinput_devid,进入input_attach_handler():

      1)匹配两者id,

    input_match_device(handler->id_table, dev);        //匹配input_handler和dev的id,不成功退出函数

      2)匹配成功调用input_handler ->connect

    handler->connect(handler, dev, id);              //建立连接

    6.建立input_handlerinput_dev的连接,进入input_handler->connect():

        1)创建全局结构体,通过input_handle结构体连接双方

    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);    //创建两者连接的input_handle全局结构体
    list_add_tail(&handle->d_node, &handle->dev->h_list); //连接input_dev->h_list
    list_add_tail(&handle->h_node, &handler->h_list);    // 连接input_handle->h_list

    7.有事件发生时,比如按键中断,在中断函数中需要进入input_event()上报事件:

       1)找到驱动处理结构体,然后执行input_handler->event()

    list_for_each_entry(handle, &dev->h_list, d_node)     // 通过input_dev ->h_list链表找到input_handle驱动处理结构体
    if (handle->open)  //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体(有可能一个驱动设备在不同情况下有不同的驱动处理方式)
        handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数

      

  • 相关阅读:
    Abp通用配置模块的设计
    Windbg分析高内存占用问题
    IdentityServer4 知多少
    找不到对象,『空对象模式』来帮忙
    EF Core中避免贫血模型的三种行之有效的方法(翻译)
    Razor Page Library:开发独立通用RPL(内嵌wwwroot资源文件夹)
    .NET Core 源码导航(按程序集链接)
    ASP.NET Core Web App应用第三方Bootstrap模板
    给ASP.NET Core Web发布包做减法
    Azure Web连接到Azure MySql Db
  • 原文地址:https://www.cnblogs.com/y4247464/p/12441502.html
Copyright © 2011-2022 走看看