内核版本: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的名字是哪里来的,我还是不懂。
额,最后驱动函数执行的图没有保存下来,但是现象很简单,就是按下和弹起按键都会产生中断,同时打印每一个按键的信息。