zoukankan      html  css  js  c++  java
  • 驱动03.输入子系统

    之前我们做的按键驱动程序都是应用程序主动open设备/dev/buttons而现实情况不能来打开这个设备甚至不知道这个设备的存在。

    解决方案:变成通用的驱动程序。接下来我们引入的输入子系统可以完成该任务。

    1.输入子系统的简介

    1.1 引入输入子系统的好处:

    (1)统一了物理形态各异的相似的输入设备的处理功能。例如,各种鼠标,不论PS/2、USB、还是蓝牙,都被同样处理。

    (2)提供了用于分发输入报告给用户应用程序的简单的事件(event)接口。你的驱动不必创建、管理/dev节点以及相关的访问方法。因此它能够很方便的调用输入API以发送鼠标移动、键盘按键,或触摸事件给用户空间。X windows这样的应用程序能够无缝地运行于输入子系统提供的event接口之上。

    (3)抽取出了输入驱动的通用部分,简化了驱动,并提供了一致性。例如,输入子系统提供了一个底层驱动(成为serio)的集合,支持对串口和键盘控制器等硬件输入的访问。

    1.2 分析输入子系统实现的原理

      linux系统将输入子系统分为三层结构,主要是input driver、input core、Input handler。在网上搜刮到的两幅经典的图片,有助于我们理解输入子系统。

     

     

     

    2 代码分析

    (1)/drivers/input/input.c
        input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops)//注册一个input_fops结构体

      static const struct file_operations input_fops = {
        .owner = THIS_MODULE,
        .open = input_open_file,
      };//结构体内只有一个open函数

    (2)怎么去读取按键?

      上述的file_operations结构体内没有read函数,我们需进一步探究。

      input_open_file
           struct input_handler *handler = input_table[iminor(inode) >> 5]
           new_fops = fops_get(handler->fops)                    //这个就是我们实际使用的file_operations结构体
           err = new_fops->open(inode, file)

      input_table这个数组由谁去构造?

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

    (3)注册函数input_register_device和input_register_handler,向input.c注册

      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

     

      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

     

      input_attach_handler

       id = input_match_device(handler->id_table, dev);//如果dev和id匹配,则调用connect函数建立连接
         error = handler->connect(handler, dev, id);

    (4)怎么建立连接?怎么读按键?

    以evdev.c为例

      evdev_connect

            evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//分配一个结构体input_handle结构体


            evdev->handle.dev = dev;
            evdev->handle.name = evdev->name;
            evdev->handle.handler = handler;
            evdev->handle.private = evdev;//设置input_handle结构体handle的值

        

          error = input_register_handle(&evdev->handle);//注册handle结构体
                input_handler->h_list = &input_handle

           input_dev    -> h_list = &input_handle

      evdev_read
          //无数据且非阻塞方式打开,则立刻返回
          if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
                        return -EAGAIN;
          wait_event_interruptible//休眠

    谁来唤醒他?
      evdev_event
          wake_up_interruptible(&evdev->wait)
        
    那evdev_event谁来调用?
      gpio_keys_isr
          input_event(input, type, button->code, !!state)
          input_sync(input)
        
    input_event
        struct input_handle *handle
        list_for_each_entry(handle, &dev->h_list, d_node)
                if (handle->open)
                    handle->handler->event(handle, type, code, value)//调用evdev_event

    3.写代码

    3.1 代码框架

    我们只需要完成input_dev那部分,其他的内核已经为我们写好了。

    (1)分配一个input_dev结构体

    (2)设置

    (3)注册

    (4)硬件相关的操作,eg:在中断程序中上报事件,参考gpio-keys.c

    3.2 驱动源代码

      1 说明:输入子系统(input subsystem)的驱动层的核心结构。  
      2 
      3 头文件:include/linux/input.h
      4 
      5 成员说明:
      6 
      7 void *private;
      8 
      9        //不清楚。
     10 
     11 char *name;
     12 
     13        //设备名字,如键盘名字。
     14 
     15 char *phys;
     16 
     17        //设备文件节点名,如input/kbd0。
     18 
     19 char *uniq;
     20 
     21        //全球唯一的ID号。
     22 
     23 struct input_id id;
     24 
     25        //后文作详细介绍。
     26 
     27 unsigned long evbit[NBITS(EV_MAX);]
     28 
     29        //该设备驱动所能支持的事件。
     30 
     31        //EV_SYN      同步事件
     32 
     33        //EV_KEY       键盘事件
     34 
     35        //EV_REL       相对坐标事件,用于鼠标
     36 
     37        //EV_ABS       绝对坐标事件,用于摇杆
     38 
     39        //EV_MSC      其他事件
     40 
     41        //EV_LED       LED灯事件
     42 
     43        //EV_SND      声音事件
     44 
     45        //EV_REP       重复按键事件
     46 
     47        //EV_FF         受力事件
     48 
     49        //EV_PWR      电源事件
     50 
     51        //EV_FF_STATUS  受力状态事件
     52 
     53 unsigned long keybit[NBITS(KEY_MAX)];
     54 
     55        //键值存放表
     56 
     57 unsigned long relbit[NBITS(REL_MAX)];
     58 
     59        //用于存放相对坐标值等
     60 
     61 unsigned long absbit[NBITS(ABS_MAX)];
     62 
     63        //用于存放绝对坐标值等
     64 
     65 unsigned long mscbit[NBITS(MSC_MAX)];
     66 
     67        //存放其他事件类型
     68 
     69 unsigned long ledbit[NBITS(LED_MAX)];
     70 
     71        //存放表示各种状态的LED值
     72 
     73 unsigned long sndbit[NBITS(SND_MAX)];
     74 
     75        //存放各种事件的声音
     76 
     77 unsigned long ffbit[NBITS(FF_MAX)];
     78 
     79        //存放受力设备的属性
     80 
     81 int ff_effects_max;
     82 
     83        //显然与受力效果有关,具体作用还不大清楚。
     84 
     85 unsigned int keycodemax;
     86 
     87 unsigned int keycodesize;
     88 
     89 void * keycode;
     90 
     91        //这三个不是很清楚,有点模糊理解。
     92 
     93 unsigned int repeat_key;
     94 
     95        //存放重复按键时的键值
     96 
     97 struct timer_list timer;
     98 
     99        //定时器
    100 
    101 struct pm_dev *pm_dev;
    102 
    103        //考虑到有些设备可能有电源管理
    104 
    105 struct pt_regs *regs;
    106 
    107        //不清楚
    108 
    109 int state;
    110 
    111        //显然是表示一个状态,但不清楚具体是谁的状态
    112 
    113 int sync;
    114 
    115        //具体用于什么也不大清楚
    116 
    117 int abs[ABS_MAX + 1];
    118 
    119        //显然是与绝对坐标有关的,但具体的作用不清楚。
    120 
    121 int rep[REP_MAX + 1];
    122 
    123        //存放重复按键时的延时,系统依靠这个延时时间来判断重复按键
    124 
    125        //rep[0]表示开始要重复按键时的延时时间,即第1个键与第2个键(开始重复按键)之间的延时
    126 
    127        //rep[1]此后重复按键之前的延时时间,直到按键抬起
    128 
    129        //通俗解释就是,假如我按了一个“a”,并且一直按着,那么在显示出来的第一个a与第二个a之间的时间延时为rep[0],而此后的相邻两个a之间的延时为rep[1]
    130 
    131   
    132 
    133 unsigned long key[NBITS(KEY_MAX)];
    134 
    135 unsigned long led[NBITS(LED_MAX)];
    136 
    137 unsigned long snd[NBITS(SND_MAX)];
    138 
    139        //不知道有什么用
    140 
    141 int absmax[ABS_MAX + 1];
    142 
    143 int absmin[ABS_MAX + 1];
    144 
    145 int absfuzz[ABS_MAX + 1];
    146 
    147 int absflat[ABS_MAX + 1];
    148 
    149        //显然与绝对坐标值有关,但不知道具体作用
    150 
    151  
    152 
    153 int (*open)(struct input_dev *dev);
    154 
    155 void (*close)(struct input_dev *dev);
    156 
    157 int (*accept)(struct input_dev *dev, struct file *file);
    158 
    159 int (*flush)(struct input_dev *dev, struct file *file);
    160 
    161 int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
    162 
    163 int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
    164 
    165 int (*erase_effect)(struct input_dev *dev, int effect_id);
    166 
    167        //底层与硬件相关的一组操作,若有具体定义,则会在input core层被调用,具体看input.c。
    168 
    169  
    170 
    171 struct input_handle *grab;
    172 
    173        //该结构会在后文做具体介绍,这个指针用于占用输入设备用,如键盘
    174 
    175 struct list_head h_list;
    176 
    177 struct list_head node;
    178 
    179        //h_list链表用于与input_handler相联系
    180 
    181        //node链表:设备向输入子系统(input subsystem)注册后,会将该链表添加到系统维护的一个链表中去,从而系统可以管理这个设备
    input_dev结构体
      1 /*
      2  *输入子系统
      3  *参考gpio-keys.c
      4 */
      5 #include <linux/module.h>
      6 #include <linux/version.h>
      7 
      8 #include <linux/init.h>
      9 #include <linux/fs.h>
     10 #include <linux/interrupt.h>
     11 #include <linux/irq.h>
     12 #include <linux/sched.h>
     13 #include <linux/pm.h>
     14 #include <linux/sysctl.h>
     15 #include <linux/proc_fs.h>
     16 #include <linux/delay.h>
     17 #include <linux/platform_device.h>
     18 #include <linux/input.h>
     19 #include <linux/irq.h>
     20 
     21 
     22 #include <asm/gpio.h>
     23 #include <asm/io.h>
     24 #include <asm/arch/regs-gpio.h>
     25 
     26 
     27 struct pin_desc{
     28     int irq;
     29     char *name;
     30     unsigned int pin;
     31     unsigned int key_val;
     32 };
     33 
     34 struct pin_desc pins_desc[4] = {
     35     {IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},
     36     {IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},
     37     {IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},
     38     {IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},
     39 };
     40 
     41 static struct input_dev *g_ptButtonsInput;
     42 static struct timer_list g_tButtonsTimer;
     43 static struct pin_desc *g_ptPindescTmp;
     44 
     45 
     46 static irqreturn_t buttons_irq(int irq, void *dev_id)
     47 {
     48     /* 10ms后启动定时器 */
     49     g_ptPindescTmp = (struct pin_desc *)dev_id;
     50     mod_timer(&g_tButtonsTimer, jiffies+HZ/100);
     51     return IRQ_RETVAL(IRQ_HANDLED);
     52 }
     53 
     54 static void ButtonsTimerFunc(unsigned long data)
     55 {
     56     struct pin_desc * tPindesc = g_ptPindescTmp;
     57     unsigned int uiPinval;
     58 
     59     if(!tPindesc)
     60         return;
     61     
     62     uiPinval = s3c2410_gpio_getpin(tPindesc->pin);
     63 
     64     if (uiPinval)
     65     {
     66         /* 松开 */
     67         input_event(g_ptButtonsInput, EV_KEY, tPindesc->key_val, 0);
     68         input_sync(g_ptButtonsInput);
     69     }
     70     else
     71     {
     72         /* 按下 */
     73         input_event(g_ptButtonsInput, EV_KEY, tPindesc->key_val, 1);
     74         input_sync(g_ptButtonsInput);
     75 
     76     }
     77 }
     78 
     79 static int buttons_init(void)
     80 {
     81     int iError;
     82     int i;
     83     /*1.分配一个input_dev结构体*/
     84     g_ptButtonsInput = input_allocate_device();
     85     if (!g_ptButtonsInput)
     86         return -ENOMEM;
     87     /*2.设置*/
     88 
     89     /*2.1事件类型*/
     90     set_bit(EV_KEY, g_ptButtonsInput->evbit);
     91     set_bit(EV_REP, g_ptButtonsInput->evbit);
     92 
     93     
     94     /*2.2哪些事件*/
     95     set_bit(KEY_L, g_ptButtonsInput->keybit);
     96     set_bit(KEY_S, g_ptButtonsInput->keybit);
     97     set_bit(KEY_ENTER, g_ptButtonsInput->keybit);
     98     set_bit(KEY_LEFTSHIFT, g_ptButtonsInput->keybit);
     99 
    100 
    101     
    102     /*3.注册*/
    103     iError = input_register_device(g_ptButtonsInput);
    104     if (iError) {
    105         printk("Unable to register buttons input device
    ");
    106     }
    107 
    108     /*4.硬件相关的操作*/
    109     init_timer(&g_tButtonsTimer);
    110     g_tButtonsTimer.function = &ButtonsTimerFunc;    /* timer handler */
    111     add_timer(&g_tButtonsTimer);
    112 
    113     for(i = 0; i < 4; i++)
    114     {
    115         request_irq(pins_desc[i].irq,  buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
    116     }
    117     return 0;
    118 
    119 }
    120 
    121 static void buttons_exit(void)
    122 {
    123     int i;
    124 
    125     for(i = 0;i<4;i++)
    126     {
    127         free_irq(pins_desc[i].irq, &pins_desc[i]);    
    128     }
    129 
    130     del_timer(&g_tButtonsTimer);
    131     input_unregister_device(g_ptButtonsInput);
    132     input_free_device(g_ptButtonsInput);
    133 }
    134 
    135 module_init(buttons_init);
    136 module_exit(buttons_exit);
    137 
    138 MODULE_LICENSE("GPL");
    buttons_input
    //input驱动的测试方法
    1.ls /dev/event* -l 查看现有的/dev/event*设备
    2.insmod buttons_input.ko 安装驱动
    3.ls /dev/event* -l 查看buttons_input对应的设备
    4.cat /dev/tty1,然后在按键,“l”“s”“ENTER”便会出现ls
    5.如果启动了QT,可以点开记事本,按相应的按键“l”“s”“ENTER”便会在记事本上出现ls
    6.也可通过执行exec 0</dev/tty1   //标准输入改为tty1,然后重复上述操作即可。
    测试方法
  • 相关阅读:
    Quagga How to use Quagga
    Quagga Case 4
    Quagga Case 3
    Quagga Case 2
    Quagga Routing Suite
    quagga
    quagga 的原理解析 zebra原理解析
    zebra线程管理源码简析
    【习题 4-8 UVA
    【习题 4-7 UVA
  • 原文地址:https://www.cnblogs.com/Lwd-linux/p/6263458.html
Copyright © 2011-2022 走看看