linux驱动子系统之输入子系统(1)

时间:2022-07-13 17:54:06

linux驱动子系统之输入子系统(1)

1.输入子系统框架

1.1 概述

内核提供的输入子系统是对分散的、多种不同类别的输入设备(键盘、鼠标、触摸屏、加速计、跟踪球、操纵杆等)进行统一处理的驱动程序。

 

输入子系统带来的好处:

l  抽象底层形态各异的硬件输入设备,为上层提供了统一的操作接口

l  提高了代码重用率和效率,减少了bug

 

 

1.2 系统框架

linux驱动子系统之输入子系统(1)

输入子系统的三层结构:

l  事件驱动层

         负责和应用程序的接口

l  核心层

         提供事件驱动层和设备驱动层所需的函数接口

l  设备驱动层

         负责和底层输入设备通信

 

事件驱动层和核心层都是通用的,我们需要实现的是设备驱动层。输入设备驱动会把硬件产生的事件信息用统一的格式(struct input_event)上报给核心层,然后核心层进行分类后,再上报给相应的事件处理驱动程序,最后通过事件层传递到用户空间,应用程序可以通过设备节点来获取事件信息。比如底层报上来的是一个按键事件,核心层会报给evdev来处理;如果报的是一个鼠标事件,核心层会报给mousedev来处理

 

1.3 统一的格式

底层驱动可以通过struct input_event上报事件信息,上层应用也可以通过此结构体来获取事件信息

struct input_event {

         structtimeval time;   /* 事件产生时间 */

         __u16type;               /* 事件类型 */

         __u16code;              /* 事件代码 */

         __s32value;             /* 事件值 */

};

 

l  事件类型,定义在linux/input.h中

1.  #define EV_SYN          0x00    /*表示设备支持所有的事件*/  
2.  #define EV_KEY          0x01    /*键盘或者按键,表示一个键码*/  
3.  #define EV_REL          0x02    /*鼠标设备,表示一个相对的光标位置结果*/  
4.  #define EV_ABS          0x03    /*手写板产生的值,其是一个绝对整数值*/  
5.  #define EV_MSC          0x04    /*其他类型*/  
6.  #define EV_LED          0x11    /*LED灯设备*/  
7.  #define EV_SND          0x12    /*蜂鸣器,输入声音*/  
8.  #define EV_REP          0x14    /*允许重复按键类型*/  
9.  #define EV_PWR          0x16    /*电源管理事件*/ 

l  事件代码,不同事件类型包含了多种事件代码

EV_KEY                                KEY_1,KEY_2,KEY_3…

EV_REL                                REL_X,REL_Y, REL_Z…  

EV_ABS                                ABS_X,ABS_Y, ABS_Z…

And so on….

l  事件值,由硬件输入设备产生的,比如按键设备驱动会去获取GPIO引脚的状态(假设低电平有效),按下时是0,弹起时是1

 

1.4 输入设备驱动的实现

l  子系统中用input_dev结构体来描述一个输入设备

struct input_dev{

         /* 导出到用户空间的相关信息,在sys文件可以看到*/

         const char *name;

         const char *phys;

         const char *uniq;

         struct input_id id;

 

         unsigned longevbit[BITS_TO_LONGS(EV_CNT)];  /*输入设备的事件支持位图*/

         unsigned longkeybit[BITS_TO_LONGS(KEY_CNT)];/*按键设备支持的事件代码位图*/

         ……

}

Evbit的位图,设置了相应的位表示设备对此事件的支持

        

l  设置输入设备所支持的事件类型和事件代码,也就是输入设备对哪些事件具有处理能力,通常我们使用set_bit函数来设置,而不采用直接赋值

n  设置支持的事件类型(支持按键事件)

          set_bit(EV_KEY, input_dev->evbit);

 

n  设置支持的事件代码(支持按键1)

          set_bit(KEY_1, input_dev->keybit);

 

n  也可以直接调用input_set_capability来完成

input_set_capability(input_dev,EV_KEY, KEY_1);

 

l  内核为我们提供了一组函数接口,来实现输入设备驱动

n  分配/释放一个输入设备

struct input_dev*input_allocate_device(void)

voidinput_free_device(struct input_dev *dev)

input_allocate_device分配成功后,会返回一个Input_dev的结构体

 

n  注册和注销输入设备

          int input_register_device(struct input_dev*dev)

          void input_unregister_device(struct input_dev*dev)

注册函数用于将Input_dev注册进核心层中,注销则相反。参数dev是input_allocate_device的返回值

 

n  报告输入事件

报告按键值

voidinput_report_key(struct input_dev *dev, unsigned int code, int value)

报告相对坐标

voidinput_report_rel(struct input_dev *dev, unsigned int code, int value)

报告绝对坐标

voidinput_report_abs(struct input_dev *dev, unsigned int code, int value)

用于事件同步 ,防止数据混乱

voidinput_sync(struct input_dev *dev)

以上都是对Input_event的封装

voidinput_event(struct input_dev *dev,unsigned int type, unsigned int code, intvalue)

and so on…

 

        

1.5 实例解析

基于input子系统实现按键驱动代码,硬件平台为s3c2440

#include <linux/input.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/kernel.h>

#include <linux/irq.h>

#include <linux/interrupt.h>

#include <mach/gpio.h>

#include <mach/regs-gpio.h>

 

#include <asm/irq.h>

#include <asm/io.h>

 

#define DEV_NAME         "KEY1"

#define BUTTON_IRQ      IRQ_EINT8

 

static struct input_dev *button_dev;

 

static irqreturn_t button_interrupt(intirq, void *p)

{

         /*get pin value <down 0, up 1> */

         intval = s3c2410_gpio_getpin(S3C2410_GPG(0));

 

         input_report_key(button_dev,KEY_1, val);

         input_sync(button_dev);

         returnIRQ_RETVAL(IRQ_HANDLED);

}

 

static int __init button_init(void)

{

         interr;

   /* 申请中断 */

         if(request_irq(BUTTON_IRQ, button_interrupt, 

                                               IRQ_TYPE_EDGE_BOTH,DEV_NAME, NULL)) {

                   printk(KERN_ERR"cannotallocate irq");

                   return- EBUSY;

         }

         /*分配input_dev */

         button_dev= input_allocate_device();

         if(button_dev == NULL) {

                   printk(KERN_ERR"notenough memory\n");

                   err= - ENOMEM;

                   gotoerr_free_irq;

         }

         /*设置输入设备支持的事件类型和事件代码 */

         set_bit(EV_KEY,button_dev->evbit);

         set_bit(KEY_1,button_dev->keybit);

        

         /*把输入设备注册进核心层 */

         err= input_register_device(button_dev);

         if(err) {

                   printk(KERN_ERR"failedto register device\n");

                   gotoerr_free_dev;

         }

         printk("initialized\n");

         return0;

 

err_free_dev:

         input_free_device(button_dev);

err_free_irq:

         free_irq(BUTTON_IRQ,NULL);

         returnerr;

}

 

static void __exit button_exit(void)

{

         input_unregister_device(button_dev);

 input_free_device(button_dev);

         free_irq(BUTTON_IRQ,NULL);

}

 

module_init(button_init);

module_exit(button_exit);

 

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");