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

1实验目的和内容

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

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

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

2知识回顾

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

  1. 定义file_operation结构体,实现open、read、write等接口函数;
  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定义如下:

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

可以支持所有的输入设备。所以本节实验也不需要对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报告结束

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

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

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

编写入口函数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.1、buttons_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\n");  
  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个物理按键对应的按键事件,S2、S3、S4、S5分别对应"L"、"S"、"ENTER"、"LEFTSHIFT"事件;
    4. 调用input_register_device函数注册buttons_dev设备;
    5. 第29~31行,关于定时器的设置,用于按键消抖处理;
    6. 第33~37行,关于中断的注册,采用中断的方式检测按键,为了简化程序,定义一个pins_desc[4]结构体数组用于管理按键相关的信息,其定义如下:

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

    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函数发送对应的按键事件,code和value,再调用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。

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

方法一

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

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

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

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

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

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

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

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

方法二

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

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

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

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

上一篇:Java 提示报错:No enclosing instance of type Example is accessible. Must qualify the allocation with an E


下一篇:使用Java MongoDb驱动程序进行Bson漂亮打印