• 2linux输入子系统-按键驱动程序


    1实验目的和内容

    实验目的:(1)通过实验,了解在linux输入子系统框架中编写输入设备驱动程序的步骤;

    2)体会与之前章节讲的编写驱动的方法之间的差异。

    实验内容:在linux输入子系统中编写按键驱动程序,按键S2S3S4S5按下时,代表"L"、"S"、"ENTER"、"LEFTSHIFT"等操作功能。

    2知识回顾

    前面章节讲到的自己编写驱动的方法,主要包含以下步骤:

    1. 定义file_operation结构体,实现openreadwrite等接口函数;
    2. 调用register_chrdev函数注册设备;
    3. 定义入口函数;
    4. 定义出口函数。

    上一章节分析的输入子系统的结构,共分为三层:Input driver、InputCore、EventHandler。其中Input driver层实现对硬件设备的读写访问,中断设置,并将硬件产生的事件转换为InputCore层定义的规范提交给EventHandler。因此,是本节编写驱动程序时需要重点实现的部分。InputCore层,也就是我们的drivers/input/input.c文件,内核已经提供了完整的代码程序,不用再做修改。EventHandler层主要是用于支持输入设备与用户空间之间的交互,linux内核已经自带了一部分事件处理器,可支持大部分的输入设备,例如:Evdev.c、mousedev.c等。其中Evdev.c中的evdev_handler的id_table:evdev_ids定义如下:

    可以支持所有的输入设备。所以本节实验也不需要对EventHandler层的代码再做任何修改。那么接下来将重点放到Input driver层。

    3实验原理简介

    Input driver设备驱动层是与硬件紧密相关的,其主要工作:向InputCore报告同步、按键等事件,让驱动事件经由inputcore和Eventhandler到达用户空间。

    input_dev结构体

    1. struct input_dev {  
    2.     
    3.     void *private;  
    4.     
    5.     const char *name;  //输入设备的名称
    6.     const char *phys;  //输入设备节点的名称
    7.     const char *uniq;  //输入设备的唯一ID号,类似于mac地址 
    8.     struct input_id id; // 输入设备的唯一标识,用于和eventhandler层进行匹配
    9.     
    10.     unsigned long evbit[NBITS(EV_MAX)];  //设备支持的事件类型
    11.     unsigned long keybit[NBITS(KEY_MAX)];  //设备支持的按键类型
    12.     unsigned long relbit[NBITS(REL_MAX)];  //可产生的相对位移事件
    13.     unsigned long absbit[NBITS(ABS_MAX)];  
    14.     unsigned long mscbit[NBITS(MSC_MAX)];  
    15.     unsigned long ledbit[NBITS(LED_MAX)];  
    16.     unsigned long sndbit[NBITS(SND_MAX)];  
    17.     unsigned long ffbit[NBITS(FF_MAX)];  
    18.     unsigned long swbit[NBITS(SW_MAX)];  
    19.     
    20.     unsigned int keycodemax;  
    21.     unsigned int keycodesize;  
    22.     void *keycode;  
    23.         ......  
    24.     
    25.     struct list_head    h_list;  
    26.     struct list_head    node;  
    27. };  

    驱动层的主要函数接口如下表:

    接口

    功能

    struct input_dev *input_allocate_device(void)

    为一个新的输入设备申请空间

    static inline void set_bit(int nr, volatile void * addr)

    设置设备支持哪些事件

    eg: set_bit(EV_KEY, buttons_dev->evbit);

    int input_register_device(struct input_dev *dev)

    注册输入设备

    void input_unregister_device(struct input_dev *dev)

    卸载输入设备

    void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

    报告新的输入事件

    dev:要上报的输入设备

    type:上报的事件类型

    code:上报的事件code

    value:上报的事件值

    static inline void input_sync(struct input_dev *dev)

    报告同步事件,通知InputCore层子系统input_event报告结束

    结合上述函数接口,我们需要在驱动层完成如下工作:分配、设置、注册一个结构体,并完成硬件相关的操作。

    按照驱动的要求,定义模块的入口函数和出口函数。

    编写入口函数buttons_init

    1. int buttons_init(void)  
    2. {  
    3.     int i, error;  
    4.         
    5.     /* 1、分配一个input_dev空间*/  
    6.     buttons_dev = input_allocate_device();  
    7.     if (!buttons_dev)  
    8.         return -ENOMEM;  
    9.         
    10.     /* 2、设置input_dev结构体 */  
    11.     /* 2.1buttons_dev可以产生按键类的事件 */  
    12.     set_bit(EV_KEY, buttons_dev->evbit);  
    13.     set_bit(EV_REP, buttons_dev->evbit);  
    14.         
    15.     /* 2.2 能产生按键类事件中的哪一类事件:L S ENTER LEFTSHIFT */  
    16.     set_bit(KEY_L,         buttons_dev->keybit);  
    17.     set_bit(KEY_S,         buttons_dev->keybit);  
    18.     set_bit(KEY_ENTER,     buttons_dev->keybit);  
    19.     set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);  
    20.         
    21.     /* 3、注册buttons_dev */     
    22.     error = input_register_device(buttons_dev);  
    23.     if (error) {  
    24.         printk(KERN_ERR "Unable to register buttons_dev ");  
    25.         goto fail;  
    26.     }  
    27.     
    28.     /* 4、硬件相关的配置 */  
    29.     init_timer(&button_timer);  
    30.     button_timer.function = button_timer_func;  
    31.     add_timer(&button_timer);  
    32.         
    33.     for(i=0; i<4; i++)  
    34.     {  
    35.         request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE,   
    36.                     pins_desc[i].devname, &pins_desc[i]);  
    37.     }  
    38.         
    39.     return 0;  
    40.     fail:  
    41.         for(i=0; i<4; i++)  
    42.             free_irq(pins_desc[i].irq, &pins_desc[i]);  
    43.         input_free_device(buttons_dev);  
    44. }  
      1. 6行,为按键输入设备buttons_dev申请空间;
      2. 12行,设置buttons_dev支持按键事件;
      3. 16~19行,设置4个物理按键对应的按键事件,S2S3S4S5分别对应"L"、"S"、"ENTER"、"LEFTSHIFT"事件;
      4. 调用input_register_device函数注册buttons_dev设备;
      5. 29~31行,关于定时器的设置,用于按键消抖处理;
      6. 33~37行,关于中断的注册,采用中断的方式检测按键,为了简化程序,定义一个pins_desc[4]结构体数组用于管理按键相关的信息,其定义如下:

      7. 40行,一些错误处理。

    出口函数代码如下:

    1. static void buttons_exit(void)  
    2. {  
    3.     int i;  
    4.     for(i=0; i<4; i++)  
    5.         free_irq(pins_desc[i].irq, &pins_desc[i]);  
    6.     del_timer(&button_timer);  
    7.     input_unregister_device(buttons_dev);  
    8.     input_free_device(buttons_dev);  
    9. }  

    驱动层检测到按键时,需要向上层发送按键事件。这里在定时器中断服务函数中,调用input_event函数发送对应的按键事件,codevalue,再调用input_sync函数结束报告。

    1. void button_timer_func(unsigned long arg)  
    2. {  
    3.     struct pin_desc * pindesc = irq_pd;  
    4.     unsigned int pinval ;  
    5.         
    6.     if(pindesc)  
    7.     {  
    8.         pinval = s3c2410_gpio_getpin(pindesc->pin);  
    9.             
    10.         if(pinval)/* 最后一个参数 松开:0  按下:1 */  
    11.         {  
    12.             input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);  
    13.             input_sync(buttons_dev);  
    14.         }  
    15.         else  
    16.         {  
    17.             input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);  
    18.             input_sync(buttons_dev);  
    19.         }  
    20.     }  
    21. }  

    至此,基于输入子系统的按键驱动程序就已编写结束。

    4实验验证

    将编写的驱动代码进行编译、挂载。通过ls -l /dev/event*命令,可以查看到已经挂载的驱动设备/dev/event1

    方法一

    使用hexdump /dev/event1命令,查看/dev/event1的十六进制编码。

    这里我们需要查看这些数据都表示些什么含义?

    首先找到evdev_read函数,该函数中通过evdev_event_to_user函数将input_event事件发送至用户空间,其中input_event结构体定义如下:

    对应到显示的数据的含义如图所示。

    从读出的16进制数据显示,说明按键驱动可以正常工作。

    方法二

    若未使用QT,可执行cat /dev/tty1命令,再按下按键时,就会输出对应的按键值。

    若在执行exec 0</dev/tty1命令,将/dev/tty1设置为标准输入,此时就可通过按键输入ls命令,并在终端上显示执行结果。

  • 相关阅读:
    Metadata Lock原理5
    Seconds_Behind_Master
    Metadata Lock原理4
    MySQL Troubleshoting:Waiting on query cache mutex 腾讯数据库工程师:幕南风
    Metadata Lock原理2
    Metadata Lock原理1
    Online DDL与pt-online-schema-change
    Solaris 安装JDK
    RAID 概述
    4K Block Size的Device和 Aligned IO
  • 原文地址:https://www.cnblogs.com/beijiqie1104/p/11424982.html
Copyright © 2020-2023  润新知