input输入子系统之按键驱动

来源:互联网 发布:君将哀而生之乎的乎 编辑:程序博客网 时间:2024/06/08 05:47

内核版本:linux-2.6.32.2

关于input子系统的基础知识看上一篇博客,网上的资料也是大同小异,也是linux设备驱动模型之一。

下面开始介绍这一个按键驱动程序。

①dev_init

static int __init dev_init(void){       /*request irq*/       s3c24xx_request_irq();     /* Initialise input stuff */    button_dev = input_allocate_device();//普通的出错判断。    if (!button_dev) {        printk(KERN_ERR "Unable to allocate the input device !!\n");        return -ENOMEM;    }//填充button_dev这个结构体    button_dev->name = "cyx";    button_dev->id.bustype = BUS_RS232;    button_dev->id.vendor = 0xDEAD;    button_dev->id.product = 0xBEEF;    button_dev->id.version = 0x0100;     button_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SYN);    //set_bit(EV_KEY, button_dev->evbit)//支持EV_KEY事件    /*设置支持哪些按键这里设置支持6个按键设置按键的类型和所支持的键,EV_KEY为按键类型,KEY_ESC、KEY_1-5为所支持的键,由于我希望我的按键取1-6,从include/linux/input.h中查询相关的宏可以知道我选择支持的键为1-6,当然你也可以选择你自己喜欢的键值,如果你选择的不是1-6,应用程序取得的按键号ev_key.code也会根据你的设置而改变*/    set_bit(KEY_1,   button_dev->keybit);    set_bit(KEY_2,   button_dev->keybit);    set_bit(KEY_3,   button_dev->keybit);    set_bit(KEY_4,   button_dev->keybit);    set_bit(KEY_5,   button_dev->keybit);    set_bit(KEY_6,   button_dev->keybit);    //printk("KEY_RESERVED=%d ,KEY_1=%d",KEY_RESERVED,KEY_1);    input_register_device(button_dev);   //注册input设备     printk ("initialized\n");     return 0; }

在dev_init中,调用了函数s3c24xx_request_irq,其实这一个函数实现的功能完全可以在init中来实现。其中初始化函数中有以下几个地方需要注意。

button_dev = input_allocate_device();这个函数是给button_dev这个结构体分配空间,这里的button_dev这个结构体就是input子系统中的input_dev这个结构体。是整个input子系统的信息存储结构体。

②s3c24xx_request_irq每一按键的中断注册函数

//注册每一个按键的中断。/* 依次申请中断,并设置六个中断类型标识为IRQ_TYPE_EDGE_BOTH,代表高低电平都会触发中断。 request_irq其它几个参数分别是中断号;中断处理函数;设备名;最后一个参数为dev_id, 共享中断时使用,必须唯一,用每个结构体的起始地址来表示能够保证id的唯一性。*/ static int s3c24xx_request_irq(void){    int i;    int err = 0;         for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {                      if (button_irqs[i].irq < 0) {                                continue;                    }    /* IRQ_TYPE_EDGE_FALLING,IRQ_TYPE_EDGE_RISING,IRQ_TYPE_EDGE_BOTH */        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,                         button_irqs[i].name, (void *)&button_irqs[i]);        if (err)            break;    }/*错误处理*/    if (err) {        i--;        for (; i >= 0; i--) {        if (button_irqs[i].irq < 0) {        continue;        }        disable_irq(button_irqs[i].irq);            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);        }        return -EBUSY;    }          return 0;}

每一个按键的中断注册函数也就不说太多,程序的注释应该可以说明白了。需要注意的是这个参数IRQ_TYPE_EDGE_BOTH,这个参数决定了按键在按下和弹起的过程中都会产生中断,具体的现象一会上图。

③从②中的中断注册函数中可以知道,中断的实现函数是buttons_interrupt

//在注册中断的函数中实现了这个函数。static irqreturn_t buttons_interrupt(int irq, void *dev_id){    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;    int down;    udelay(0);    /*获取按键值*/    down = !s3c2410_gpio_getpin(button_irqs->pin);   //down: 1(按下),0(弹起)    if (!down) {          /*报告事件*/    key_values = button_irqs->number;    //printk("====>rising key_values=%d\n",key_values);   if(key_values==0)//这里的button_dev,其实就是input_dev,这个时候已经开始将整个input的信息交给上层了。       input_report_key(button_dev, KEY_1, 0);   if(key_values==1)       input_report_key(button_dev, KEY_2, 0);   if(key_values==2)       input_report_key(button_dev, KEY_3, 0);   if(key_values==3)       input_report_key(button_dev, KEY_4, 0);   if(key_values==4)       input_report_key(button_dev, KEY_5, 0);   if(key_values==5)       input_report_key(button_dev, KEY_6, 0);   /*报告结束这个函数的名字起的太狗血,说是同步,其实就是告诉报告结束的意思。   */    input_sync(button_dev);          }       else {              key_values = button_irqs->number;         //printk("====>falling key_values=%d\n",key_values);   if(key_values==0)         input_report_key(button_dev, KEY_1, 1);   if(key_values==1)         input_report_key(button_dev, KEY_2, 1);   if(key_values==2)         input_report_key(button_dev, KEY_3, 1);   if(key_values==3)         input_report_key(button_dev, KEY_4, 1);   if(key_values==4)         input_report_key(button_dev, KEY_5, 1);   if(key_values==5)         input_report_key(button_dev, KEY_6, 1);   input_sync(button_dev);             }        return IRQ_RETVAL(IRQ_HANDLED);}

主要结构式通过一个if的判断来实现了事件的发生判断和报告。最后使用input_sync(button_dev); 这个函数来结束报告。这里需要说一下参数的传递方向。

关于if(key_values==0)
       input_report_key(button_dev, KEY_1, 0);
    这一部分检测的值传递流程:
    ①key_values = button_irqs->number;
    ②static struct button_irq_desc button_irqs [] = {
    {IRQ_EINT8 , S3C2410_GPG(0) ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG(3) ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},
    {IRQ_EINT13, S3C2410_GPG(5) ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},
    {IRQ_EINT14, S3C2410_GPG(6) ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},
    {IRQ_EINT15, S3C2410_GPG(7) ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},
    {IRQ_EINT19, S3C2410_GPG(11),  S3C2410_GPG11_EINT19, 5, "KEY5"},
};
OK,到这里就是针对寄存器的读取了,所以就可以判断按键的状态了。
④button_irq_desc这个结构体如下所示。

struct button_irq_desc {    int irq; //中断号    int pin; //按键引脚    int pin_setting; //按键引脚设置    int number; //按键编号    char *name;    //按键名称};

这个结构体的填充:

static struct button_irq_desc button_irqs [] = {    {IRQ_EINT8 , S3C2410_GPG(0) ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},    {IRQ_EINT11, S3C2410_GPG(3) ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},    {IRQ_EINT13, S3C2410_GPG(5) ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},    {IRQ_EINT14, S3C2410_GPG(6) ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},    {IRQ_EINT15, S3C2410_GPG(7) ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},    {IRQ_EINT19, S3C2410_GPG(11),  S3C2410_GPG11_EINT19, 5, "KEY5"},};

⑤dev_exit驱动退出函数

static void __exit dev_exit(void){    int i;         for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {        if (button_irqs[i].irq < 0) {            continue;            }        free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);    }     input_unregister_device(button_dev);}

中断退出函数中主要实现了中断的释放和input设备的释放。

下面粘出应用程序:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/select.h>#include <sys/time.h>#include <errno.h>#include <linux/input.h>int main(void){int fd;int key_value,i=0,count;struct input_event ev_key;fd = open("/dev/input/event1", 666);//根据实际情况来设置文件,文件名根据/sys/class/input目录下的设备号来确定if (fd < 0) {perror("open device buttons");exit(1);}for (;;) {count = read(fd,&ev_key,sizeof(struct input_event));for(i=0; i<(int)count/sizeof(struct input_event); i++)if(EV_KEY==ev_key.type)    printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);if(EV_SYN==ev_key.type)    printf("syn event\n\n"); } close(fd); return 0;}



需要注意的是fd = open("/dev/input/event1", 666);这一个部分,大家先记住,我马上上图。

现在开始说一说程序在编译过程中出现的问题还没有弄懂的问题:

①还是老问题,告诉这个那个没有定义,我去国嵌linux-2.6.32.2的内核中找了一下,这次不幸了,没有找到关于input-button的成型程序,但是根据错误的类型找到了这个路径下的文件——————/drivers/char/Keyboard.c这个目录下定义的很多变量和名称和我的程序都很类似,菜鸟的我直接把这个文件的头文件全部拷贝到了自己的程序里。再进行make编译内核模块之后顺利通过。增加的头文件如下:

#include <linux/kbd_kern.h>#include <linux/kbd_diacr.h>#include <linux/vt_kern.h>#include <linux/sysrq.h>#include <linux/input.h>#include <linux/reboot.h>#include <linux/notifier.h>#include <linux/jiffies.h>

②内核模块顺利编译过了,那么久insmod吧,下图是insmod后的景象。


自我感觉异常良好,顺利的insmod了。

那么就开始第二部./app吧。但是在执行应用程序之后开始提示的是找不到文件。回头去应用程序中看fd = open("/dev/input/event1", 666);不知道这里应该写什么。

但是如上图中的在insmod成功之后出现了一个路径/sys/devices/virtual/input/input1,我在应用程序中打开了这个路径 /sys/devices/virtual/input/input1,但是提示这不是一个文件。

这就是问题的所在,我现在还不知道怎么寻找input子系统的设备文件。

但是后来想到了一个办法来排除。

下图是在没有insmod这个模块之前/dev/input目录下的情况。

这个时候/dev/input目录下只有两个文event0和mice。

再让我们看一下insmod之后/dev/input目录下的内容。



insmod之后出现了event1,我可以确定只有这一个驱动被insmod(由于前面的其他驱动可以确定没有出现其他的驱动)。所以应用程序fd = open("/dev/input/event1", 666);最后定型。

老谢没有仔细说这里到底是怎么回事,所以我索性先记住这里的驱动程序就是挂接到/dev/input目录下,但是这个event1的名字是哪里来的,我还是不懂。

额,最后驱动函数执行的图没有保存下来,但是现象很简单,就是按下和弹起按键都会产生中断,同时打印每一个按键的信息。

原创粉丝点击