[置顶] 输入子系统

驱动框架
核心层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. /* 参考drivers\input\keyboard\gpio_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. /* 参考drivers\input\keyboard\gpio_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
posted @ 2017-03-30 18:32  隔壁王叔叔a  阅读(156)  评论(0编辑  收藏  举报