zoukankan      html  css  js  c++  java
  • 输入子系统笔记!

    学完第一期基础驱动入门后,接着开始讲input子系统。看着视频,心里有个大概对输入子系统的认识,但是说不出来。百度一下,摘抄吧。

    https://blog.csdn.net/zouleideboke/article/details/70650001

    1.什么是子系统?

            内核是操作系统的核心。Linux内核提供很多基本功能,如虚拟内存、多任务、共享库、需求加载、共享写时拷贝(Copy-On-Write)以及网络功能等。增加各种不同功能导致内核代码不断增加。 
              Linux内核把不同功能分成不同的子系统,通过一种整体的结构把各种功能集合在一起,提高了工作效率。同时还提供动态加载模块的方式,为动态修改内核功能提供了灵活性。

        2.linux 驱动子系统

             linux内核中自带了很多的驱动子系统,其中比较典型的有:input子系统,led子系统,framebuffer子系统(LCD),I2c子系统等,这些子系统它是通过一层一层的函数传递与封装,它实现了设备驱动的注册,以及定义了file_operations结构体里面的各种函数操作等,不需要在单独的设备驱动代码中进行注册,定义,直接调用相应的的子系统即可,

          3.linux input子系统

          以前学了单独的按键设备驱动以及led驱动,实际上,在linux中实现这些设备驱动,有一种更为推荐的方法,就是input输入子系统。平常我们的按键,触摸屏,鼠标等输入型设备都可以利用input接口(这个接口是什么呢?可以看我对按键驱动分析就一目了然)来简化驱动程序并实现设备驱动。Input子系统处理输入事务,任何输入设备的驱动程序都可以通过Input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互.

    我对input 子系统的理解是:其实input 子系统是对linux输入设备驱动进行了高度抽象最终分成了三层:包括input设备驱动层,核心层,事件处理层,也就是说我们之前移植的按键,usb,触摸屏驱动代码也只是子系统的一部分,起初我们自己编写的那些驱动代码都是分散的,按键是按键,led是led,都没有对这些不同类别的输入设备驱动统一起来,也就是说input 子系统这种机制的出现,最大的优点我觉得就是为内核大大的简化了驱动程序!!!这个input子系统最神秘之处就是它的核心层部分,这个核心层做了什么呢?它在内核中是怎么定义的?

    2.linux输入子系统原理

    linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层

    设备驱动层:主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。

     核心层:       为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。

     事件处理层:事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。

    对于linux输入子系统的框架结构图:

    分析:/dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。

    事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。

    输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。

    linux输入子系统的事件处理机制

    分析:由上图可知输入子系统核心层提供的支持以及如何上报事件到input event drivers。

    作为输入设备的驱动开发者,需要做以下几步:

    1. 在驱动加载模块中,设置你的input设备支持的事件类型。(事件类型有:EV_SYN     0x00    同步事件;

                                      EV_KEY     0x01    按键事件;

                                     EV_LED     0x11    按键/设备灯等)

    2. 注册中断处理函数,例如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等)

    3.将输入设备注册到输入子系统中

    3.input 核心层

    input核心层位于/drivers/input/input.c

    3.1 入口和出口

     1 static const struct file_operations input_fops = {
     2     .owner = THIS_MODULE,
     3     .open = input_open_file,
     4 };
     5 static int __init input_init(void)
     6 {
     7     int err;
     8     err = class_register(&input_class);//注册input类,这里就是生成了sys/class/input目录,在该目录下会看到驱动链接文件
     9     if (err) {
    10         printk(KERN_ERR "input: unable to register input_dev class
    ");
    11         return err;
    12     }
    13     err = input_proc_init();//在proc目录下建立input子系统目录及交互文件,即/proc/bus/input目录下的devices文件和handlers文件 
    14     if (err)
    15         goto fail1;
    16 
    17     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册一个主设备号为INPUT_MAJOR(13),次设备号为0~255,文件操作符为input_fops的字符设备
    18     if (err) {
    19         printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
    20         goto fail2;
    21     }
    22     return 0;
    23  fail2:    input_proc_exit();
    24  fail1:    class_unregister(&input_class);
    25     return err;
    26 }
    27 static void __exit input_exit(void)
    28 {
    29     input_proc_exit();
    30     unregister_chrdev(INPUT_MAJOR, "input");
    31     class_unregister(&input_class);
    32 }
    33 subsys_initcall(input_init);
    34 module_exit(input_exit);

      这个初始化函数中,有一个注册字符设备函数register_chrdev(第17行),它用到了input_fops,这个结构体为struct file_operations,但这里他只有一个input_open_file——open成员。

    3.2 input_open_file函数

     1 static int input_open_file(struct inode *inode, struct file *file)
     2 {
     3     struct input_handler *handler = input_table[iminor(inode) >> 5];//根据所打开的驱动的次设备号inode,查找对应input_handler
     4     const struct file_operations *old_fops, *new_fops = NULL;
     5     int err;
     6     /* No load-on-demand here? */
     7     if (!handler || !(new_fops = fops_get(handler->fops)))//new_fops赋值为该驱动的file_operations
     8         return -ENODEV;
          ...

    17 old_fops = file->f_op; 18 file->f_op = new_fops;//所打开的文件的f_op等于新的new_fops 19 err = new_fops->open(inode, file);//打开新的驱动      ...25 return err; 26 }

    分析:根据inode,在input_table数组中查找的对应的input_handler,然后从中提取出.fops成员复制给new_fops,并调用其成员下的.open函数。old_fops是为了防止出错做备份用的。

    3.3 input_table的分析,input_table由谁构造?

    首先input_table在Input.c中定义

    1 static struct input_handler *input_table[8];

    2 构造

    Evbug.c/Evdev.c/Joydev.c/Keyboard.c/Mousedev.c....的入口函数

      input_register_handler //向上注册

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

     //以Evdev.c为例
    static int __init evdev_init(void)
    { 
       return input_register_handler(&evdev_handler); 
    }
    static struct input_handler evdev_handler = {
          .event =    evdev_event,
          .connect =    evdev_connect,
          .disconnect =    evdev_disconnect,
          .fops =        &evdev_fops,//这个结构体里有各种读写操作函数
          .minor =    EVDEV_MINOR_BASE,//次设备号 宏定义64,handler->minor >> 5后,就是2 所以它在第三项
          .name =        "evdev",
          .id_table =    evdev_ids,//用于比较是否要打开的设备是否匹配
     };
    int input_register_handler(struct input_handler *handler)
    {
        ...
        if (handler->fops != NULL) {
            if (input_table[handler->minor >> 5])
                return -EBUSY;
            input_table[handler->minor >> 5] = handler;//这里evdev_handler添加到input_table[2]中
        }
        ...
        return 0;
    }

    分析:input_table由input_register_handler函数构造。

    input_register_handler

      input_table[handler->minor >> 5] = handler;//放入数组

      list_add_tail(&handler->node, &input_handler_list);//放入链表

      list_for_each_entry(dev, &input_dev_list, node)//对于每个input_dev,调用input_attach_handler
              input_attach_handler(dev, handler);//根据input_handler的id_table判断是否支持这个input_dev

    以上Evbug.c/Evdev.c/Joydev.c/Keyboard.c/Mousedev.c....向input.c注册handler,为输入设备实现一个接口

    查看evdev_handler如下

    static struct input_handler evdev_handler = {
        .event =    evdev_event,
        .connect =    evdev_connect,
        .disconnect =    evdev_disconnect,
        .fops =        &evdev_fops,
        .minor =    EVDEV_MINOR_BASE,
        .name =        "evdev",
        .id_table =    evdev_ids,//表示这个接口能够支持那些输入设备,如果能够支持就调用.connect函数,
    };
    

    3.4注册输入设备——input_register_device

     input_register_device

        list_add_tail(&dev->node, &input_dev_list);//放入链表

        list_for_each_entry(handler, &input_handler_list, node)//对于每一个input_handler,都调用input_attach_handler函数

          input_attach_handler(dev, handler);//根据input_handler的id_table判断是否支持这个input_dev

    3.5input_attach_handler函数作用

    static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
    {
       ...
        id = input_match_device(handler->id_table, dev);//根据id_table和dev判断是否匹配
        if (!id)
           return -ENODEV;
         error = handler->connect(handler, dev, id);//如果匹配,执行.conect函数
         ...
         return error;
    }

    总结:注册input_dev或input_handler时,会两两比较,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能够支持,则调用input_handler的connect函数建立连接。

    3.6怎么建立连接 

     1.分配一个input_handle结构

     2 input_handle.dev = input_dev;//指向左边的input_dev

        input_handle.handler = input_handler;//指向右边的input_handler

      3注册:

      input_handler->h_list = &input_handle;

      input_dev->h_list = &input_handle;

    以evdev.c为例

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

        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //分配一个input_handle
             evdev->handle.dev = dev;//指向input_dev

                         evdev->handle.name = evdev->name;

                         evdev->handle.private = evdev;

                         error = input_register_handle(&evdev->handle);//注册

    懒惰不会让你一下子跌到 但会在不知不觉中减少你的收获; 勤奋也不会让你一夜成功 但会在不知不觉中积累你的成果 越努力,越幸运。
  • 相关阅读:
    Windows Server2008 R2下安装Oracle 10g
    用R和BioConductor进行基因芯片数据分析(五):芯片间归一化
    用R和BioConductor进行基因芯片数据分析(四):芯片内归一化
    用R和BioConductor进行基因芯片数据分析(三):计算median
    R语言安装R package的2种方法
    用R和BioConductor进行基因芯片数据分析(二):缺失值填充
    Redhat EL4 install gcc 4.2
    Codeforces Round #166 (Div. 1) && (Div. 2)
    美国历任总统
    【JAXWS入门系列】第04章_SOAP异常处理和Handler处理
  • 原文地址:https://www.cnblogs.com/Rainingday/p/9048893.html
Copyright © 2011-2022 走看看