input输入子系统之按键驱动

内核版本: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后的景象。

input输入子系统之按键驱动_第1张图片

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

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

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

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

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

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

input输入子系统之按键驱动_第2张图片

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

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

input输入子系统之按键驱动_第3张图片

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

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

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

你可能感兴趣的:(input输入子系统之按键驱动)