zoukankan      html  css  js  c++  java
  • [置顶] 输入子系统

    驱动框架
    核心层input.c
    1.输入子系统核心对应与/drivers/input/input.c文件,这个也是作为一个模块注册到内核的。所以首先分析模块初始化函数

    点击(此处)折叠或打开

    1. input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

    2. static const struct file_operations input_fops = {
    3. .owner = THIS_MODULE,
    4. .open = input_open_file,
    5. };
    问:没有read函数怎样都按键?
    app: read > ... > file->f_op->read 
    进入input_open_file
    定义一个input_handler指针

    点击(此处)折叠或打开

    1. struct input_handler *handler = input_table[iminor(inode) >> 5];
    2. file->f_op = new_fops;

    点击(此处)折叠或打开

    1. if (handler)
    2.         new_fops = fops_get(handler->fops);//从input_handler中提取出new_fops
    3.         file->f_op = new_fops;//将new_fops赋值给当前文件的file_operations
    4.         err = new_fops->open(inode, file);
     
    经过上面的操作之后,当应用程序再来调用read,open,write等函数的时候,最终都会调用到file->f_op中的read,write,open函数。
    那么input_open_file函数中的input_table 又是从何而来的呢?
    可见在input.c的input_register_handler函数中构造了input_table

    点击(此处)折叠或打开

    1. int input_register_handler(struct input_handler *handler)
    2. {
    3.     struct input_dev *dev;

    4.     INIT_LIST_HEAD(&handler->h_list);

    5.     if (handler->fops != NULL) {
    6.         if (input_table[handler->minor >> 5])
    7.             return -EBUSY;

    8.         input_table[handler->minor >> 5] = handler;
    9.     }

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

    11.     list_for_each_entry(dev, &input_dev_list, node)
    12.         input_attach_handler(dev, handler);

    13.     input_wakeup_procfs_readers();
    14.     return 0;
    15. }

    又是谁在调用input_register_handler呢?   
    搜索内核代码可知:evdev.c,tsdev.c,joydev.c,keyboard.c,mousedev.c等文件调用了    input_register_handler,我们以evdev.c为例   
    在drivers/input/evdev.c中:   
    进入模块入口函数evdev_init:     

    点击(此处)折叠或打开

    1. static int __init evdev_init(void)
    2. {
    3.     return input_register_handler(&evdev_handler);
    4. }
    evdev_handler如下:

    点击(此处)折叠或打开

    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. };
    这里需要先学习下输入子系统的架构
    当通过input_register_device注册一个input_dev设备或者通过input_register_handler注册一个input_handler时,input_dev与input_handler会进行匹配,如何匹配?    
    答:在input_handler中有一个id_table,里面包含的就是这个input_handler能处理的input_dev,当input_handler与input_dev匹配成功的话,则会调用input_handler里的connect函数来建立连接。 
    那么他们怎样建立连接呢?  
    下面我们来分析下input_register_device和input_register_handler分别做了什么:
    input_register_device:
    1.将刚注册的input_dev放入input_dev_list链表

    点击(此处)折叠或打开

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

    点击(此处)折叠或打开

    1. /*对 input_handler_list中的每一个input_handler都调用 */
    2.         *input_attach_handler(dev, handler);这个函数,来查看是否有input_handler *合适该input_dev
    3.         */ 
    4.         list_for_each_entry(handler, &input_handler_list, node)
    5.         input_attach_handler(dev, handler);
    6.         input_register_handler:
    7.         //将刚注册的input_handler放入input_table中
    8.         input_table[handler->minor >> 5] = handler;
    9.         //将刚注册的input_handler放入 input_handler_list链表中
    10.         list_add_tail(&handler->node, &input_handler_list);
    11.         /*对 input_dev_list 中的每一个input_dev都调用 
    12.         *input_attach_handler(dev, handler);这个函数,来查看是否有input_dev合适该input_handler
    13.         */
    14.         list_for_each_entry(dev, &input_dev_list, node)
    15.         input_attach_handler(dev, handler);
    由上面可以看出,无论是先注册input_handler还是先注册input_dev最终都会调用来input_attach_handler(dev, handler),来进行两两匹配。现在我们来看看input_attach_handler是如何将input_handler和input_dev进行匹配的。    
     在input_attach_handler函数中:        
    /*
    **先进行匹配,匹配的依据就是input_handler中的id_table与 *input_dev中的id里的信息是否相同*/        
    id = input_match_device(handler, dev);            
    /*再调用input_handler中的connect函数完成连接,具体如何连接,需要分析connect函数*/        
    error = handler->connect(handler, dev, id);        
    我们以evdev.c中的input_handler结构中的connect函数为例,分析connect函数做了些什么。   
    在evdev_connect函数中:

    点击(此处)折叠或打开

    1. /*分配一个evdev结构体,该结构体中含一个input_handle结构
    2.         *注意:不同于input_handler结构
    3.         */
    4.         evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    5.         
    6.         /*设置evdev结构中的input_handle
    7.         *@input_handle的dev变量指向input_dev结构
    8.         *@input_handle的handler变量指向input_handler结构
    9.         */
    10.         evdev->handle.dev = input_get_device(dev);
    11.         evdev->handle.name = dev_name(&evdev->dev);
    12.         evdev->handle.handler = handler;
    13.         evdev->handle.private = evdev;
    14.       
    15.         /*向内核注册input_handle*/
    16.         error = input_register_handle(&evdev->handle);

    下面我们来看注册 input_register_handle做了些什么:        
    复制代码      
     /*将input_handle添加到input_dev中的h_list链表中
            *以后当input_dev需要使用对应的input_handler时就可以通过自身的*h_list链表找到input_handle,从而找到匹配的input_handler。
            */
            list_add_tail_rcu(&handle->d_node, &dev->h_list);
            /*将input_handle添加到input_handler中的h_list链表中,
             *以后当input_handler需要使用对应的input_dev时就可以通过自身的
             *h_list链表找到input_handle,从而找到匹配的input_dev
             */
            list_add_tail_rcu(&handle->h_node, &handler->h_list);
    对于输入设备,我们知道一般需要读取其数据,那么经过上述的一些列动作    后又是如何读取到数据的呢?我们来看下evdev.c中的input_handler中的fops 中的read函数
    在evdev_read函数中:
    //如果没有数据且nonblock的话,则EAGAIN
           if (client->head == client->tail && evdev->exist &&
             (file->f_flags & O_NONBLOCK))
            return -EAGAIN;
            //否则,睡眠
            retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !        evdev->exist);
         既然有睡眠,那么何时被唤醒,搜索代码。    
         在evdev_event函数中:        //唤醒        
         wake_up_interruptible(&evdev->wait);    
         evdev_event是input_handler中的成员,当有数据可读(如触摸屏被按下,按键被按下)时,event函数会被调用。而event函数是怎么被调用到的?
    这就得看设备层了,设备层的驱动做了如下工作:    
         <1>在中断函数中确定判断发生了什么事件,并且相应的值是多少    
         <2>通过input_event()函数上报事件    
         <3>通过input_sync()函数表明上报结束    
         分析input_event函数我们就可以知道input_handler中的event函数是如何被调用到的了。    
         在input_event中,调用了input_handle_event函数,在input_handle_event函数中调用了input_pass_event函数;    
         在input_pass_event函数中:
       struct input_handler *handler;
            /*从注册的input_dev的h_list中将input_handle一个个拿出来*/
            list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
                if (!handle->open)
                    continue;
                //如果该input_handle被打开,则该input_handle->input_handler即为可            //处理该input_dev的handler
                handler = handle->handler;
                if (!handler->filter) {
                    if (filtered)
                        break;
                    /*最终调用到event函数*/
                    handler->event(handle, type, code, value);
                } else if (handler->filter(handle, type, code, value))
                    filtered = true;
            }
     最后,回归到如何写符合输入子系统框架的驱动程序,四个步骤:
     <1>分配一个input_dev结构体
     <2>设置input_dev   
     <3>注册input_dev
     <4>在中断函数中上报事件    
    接下来就是编写一个符合输入子系统的驱动程序了
    安装上面的方法:
     <1>分配一个input_dev结构体
     <2>设置input_dev   
     <3>注册input_dev
     <4>在中断函数中硬件相关的代码
    那么怎样来写这个符合要求的驱动程序呢?
    A.先参考gpio_keys.c添加头文件

    点击(此处)折叠或打开

    1. /* 参考driversinputkeyboardgpio_keys.*/

    2. #include <linux/module.h>
    3. #include <linux/version.h>

    4. #include <linux/init.h>
    5. #include <linux/fs.h>
    6. #include <linux/interrupt.h>
    7. #include <linux/irq.h>
    8. #include <linux/sched.h>
    9. #include <linux/pm.h>
    10. #include <linux/sysctl.h>
    11. #include <linux/proc_fs.h>
    12. #include <linux/delay.h>
    13. #include <linux/platform_device.h>
    14. #include <linux/input.h>
    15. #include <linux/irq.h>

    16. #include <asm/gpio.h>
    17. #include <asm/io.h>
    18. #include <asm/arch/regs-gpio.h>

    B.入口函数,出口函数
    module_init(buttons_init);

    点击(此处)折叠或打开

    1. static int buttons_init(void)
    2. {
    3.     int i;
    4.     
    5.     /* 1. 分配一个input_dev结构体 */
    6.     buttons_dev = input_allocate_device();;

    7.     /* 2. 设置 */
    8.     /* 2.1 能产生哪类事件 */
    9.     set_bit(EV_KEY, buttons_dev->evbit);
    10.     set_bit(EV_REP, buttons_dev->evbit);
    11.     
    12.     /* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
    13.     set_bit(KEY_L, buttons_dev->keybit);
    14.     set_bit(KEY_S, buttons_dev->keybit);
    15.     set_bit(KEY_ENTER, buttons_dev->keybit);
    16.     set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

    17.     /* 3. 注册 */
    18.     input_register_device(buttons_dev);
    19.     
    20.     /* 4. 硬件相关的操作 */
    21.     init_timer(&buttons_timer);
    22.     buttons_timer.function = buttons_timer_function;
    23.     add_timer(&buttons_timer);
    24.     
    25.     for (= 0; i < 4; i )
    26.     {
    27.         request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
    28.     }
    29.     
    30.     return 0;
    31. }

    出口函数:
    module_exit(buttons_exit);

    点击(此处)折叠或打开

    1. static void buttons_exit(void)
    2. {
    3.     int i;
    4.     for (= 0; i < 4; i )
    5.     {
    6.         free_irq(pins_desc[i].irq, &pins_desc[i]);
    7.     }

    8.     del_timer(&buttons_timer);
    9.     input_unregister_device(buttons_dev);
    10.     input_free_device(buttons_dev);    
    11. }
    中断处理函数:

    点击(此处)折叠或打开

    1. static irqreturn_t buttons_irq(int irq, void *dev_id)
    2. {
    3.     /* 10ms后启动定时器 */
    4.     irq_pd = (struct pin_desc *)dev_id;
    5.     mod_timer(&buttons_timer, jiffies HZ/100);
    6.     return IRQ_RETVAL(IRQ_HANDLED);
    7. }

    以下是全部的代码:

    点击(此处)折叠或打开

    1. /* 参考driversinputkeyboardgpio_keys.*/

    2. #include <linux/module.h>
    3. #include <linux/version.h>

    4. #include <linux/init.h>
    5. #include <linux/fs.h>
    6. #include <linux/interrupt.h>
    7. #include <linux/irq.h>
    8. #include <linux/sched.h>
    9. #include <linux/pm.h>
    10. #include <linux/sysctl.h>
    11. #include <linux/proc_fs.h>
    12. #include <linux/delay.h>
    13. #include <linux/platform_device.h>
    14. #include <linux/input.h>
    15. #include <linux/irq.h>

    16. #include <asm/gpio.h>
    17. #include <asm/io.h>
    18. #include <asm/arch/regs-gpio.h>

    19. struct pin_desc{
    20.     int irq;
    21.     char *name;
    22.     unsigned int pin;
    23.     unsigned int key_val;
    24. };

    25. struct pin_desc pins_desc[4] = {
    26.     {IRQ_EINT1, "K1", S3C2410_GPF1, KEY_L},
    27.     {IRQ_EINT4, "K2", S3C2410_GPF4, KEY_S},
    28.     {IRQ_EINT2, "K3", S3C2410_GPF2, KEY_ENTER},
    29.     {IRQ_EINT0, "K4", S3C2410_GPF0, KEY_LEFTSHIFT},
    30. };

    31. static struct input_dev *buttons_dev;
    32. static struct pin_desc *irq_pd;
    33. static struct timer_list buttons_timer;

    34. static irqreturn_t buttons_irq(int irq, void *dev_id)
    35. {
    36.     /* 10ms后启动定时器 */
    37.     irq_pd = (struct pin_desc *)dev_id;
    38.     mod_timer(&buttons_timer, jiffies HZ/100);
    39.     return IRQ_RETVAL(IRQ_HANDLED);
    40. }

    41. static void buttons_timer_function(unsigned long data)
    42. {
    43.     struct pin_desc * pindesc = irq_pd;
    44.     unsigned int pinval;

    45.     if (!pindesc)
    46.         return;
    47.     
    48.     pinval = s3c2410_gpio_getpin(pindesc->pin);

    49.     if (pinval)
    50.     {
    51.         /* 松开 : 最后一个参数: 0-松开, 1-按下 */
    52.         input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
    53.         input_sync(buttons_dev);
    54.     }
    55.     else
    56.     {
    57.         /* 按下 */
    58.         input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
    59.         input_sync(buttons_dev);
    60.     }
    61. }

    62. static int buttons_init(void)
    63. {
    64.     int i;
    65.     
    66.     /* 1. 分配一个input_dev结构体 */
    67.     buttons_dev = input_allocate_device();;

    68.     /* 2. 设置 */
    69.     /* 2.1 能产生哪类事件 */
    70.     set_bit(EV_KEY, buttons_dev->evbit);
    71.     set_bit(EV_REP, buttons_dev->evbit);
    72.     
    73.     /* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
    74.     set_bit(KEY_L, buttons_dev->keybit);
    75.     set_bit(KEY_S, buttons_dev->keybit);
    76.     set_bit(KEY_ENTER, buttons_dev->keybit);
    77.     set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

    78.     /* 3. 注册 */
    79.     input_register_device(buttons_dev);
    80.     
    81.     /* 4. 硬件相关的操作 */
    82.     init_timer(&buttons_timer);
    83.     buttons_timer.function = buttons_timer_function;
    84.     add_timer(&buttons_timer);
    85.     
    86.     for (= 0; i < 4; i )
    87.     {
    88.         request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
    89.     }
    90.     
    91.     return 0;
    92. }

    93. static void buttons_exit(void)
    94. {
    95.     int i;
    96.     for (= 0; i < 4; i )
    97.     {
    98.         free_irq(pins_desc[i].irq, &pins_desc[i]);
    99.     }

    100.     del_timer(&buttons_timer);
    101.     input_unregister_device(buttons_dev);
    102.     input_free_device(buttons_dev);    
    103. }

    104. module_init(buttons_init);

    105. module_exit(buttons_exit);

    106. MODULE_LICENSE("GPL");
    然后编译驱动程序:
    放到网络文件系统中,然后再终端输入查看信息
    cat /dev/tty1(按回车)
    cat /etc/inittab

    转自:http://blog.chinaunix.net/uid-26859745-id-3190337.html
  • 相关阅读:
    BZOJ2199[Usaco2011 Jan]奶牛议会——2-SAT+tarjan缩点
    BZOJ3862Little Devil I——树链剖分+线段树
    BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树
    BZOJ1018[SHOI2008]堵塞的交通——线段树
    BZOJ2733[HNOI2012]永无乡——线段树合并+并查集+启发式合并
    BZOJ4127Abs——树链剖分+线段树
    bzoj 4753 最佳团体
    bzoj 4472 salesman
    bzoj 5369 最大前缀和
    bzoj 1226 学校食堂Dining
  • 原文地址:https://www.cnblogs.com/alan666/p/8311797.html
Copyright © 2011-2022 走看看