一叶浮萍归大海,人生何处不相逢。——吴承恩《西游记》
前面学习了非内核自带的按键驱动,而内核自带的按键驱动呢,其实分析的过程和其他的LED,LCD,等等有关input输入子系统的驱动差不多,我这里就不详细分析代码了,直接根据朋友的代码学习一波吧。
本文取自:http://blog.csdn.net/qicheng777/article/details/70745409
顺便为我朋友打句广告,他的声卡驱动写得挺好,有兴趣的可以去瞧一瞧。
进入正题:
内核版本:3.0.54
OS:centos 6.7
linux按键驱动主要是中断的处理,中断分为两个部分,前半部分是实际相应中断的函数,需要用request_irq注册,后半部分由前半部分调度,并在一个更安全的时间来执行函数,该函数即为负责执行中断的内容,前半部分不允许被其他中断干扰,后半部分则可以,这种机制的好处是可以迅速的响应中断。
按键驱动具体的驱动程序是:kernel/linux-3.0.54/drivers/input/keyboard/gpio_keys.c
有兴趣的朋友可以深入研究。下面是我们的修改过程:
1.修改mach-smdk2440.c
根据我们的驱动程序可以找到我们的相关设备信息:
.name = "gpio-keys"
将其添加到我们的mach-smdk2440.c
中:
+++ mach-smdk2440.c 2017-04-22 15:36:58.078925861 -0800
@@ -52,6 +52,15 @@
#include <plat/common-smdk.h>
#include <linux/dm9000.h> //添加DM9000网卡的头文件
+
+//add for bubtton
+#include <linux/input.h> //添加输入子系统头文件
+
static struct map_desc smdk2440_iodesc[] __initdata = {
/* ISA IO Space map (memory space selected by A24) */
@@ -231,13 +240,102 @@
.id= 0,
};
+#if 1
+//add for button
+
+ static struct gpio_keys_button rx1950_gpio_keys_table[] = {
+ {
+ .code = KEY_1,
+ .gpio = S3C2410_GPF(0),
+ .active_low = 1,
+ .desc = "Button_1",
+ .wakeup = 1,
+ },
+ {
+ .code = KEY_2,
+ .gpio = S3C2410_GPF(2),
+ .active_low = 1,
+ .desc = "Button_2",
+ },
+ {
+ .code = KEY_3,
+ .gpio = S3C2410_GPF(3),
+ .active_low = 1,
+ .desc = "Button_3",
+ },
+ {
+ .code = KEY_4,
+ .gpio = S3C2410_GPF(4),
+ .active_low = 1,
+ .desc = "Button_4",
+ },
+};
+
+static struct gpio_keys_platform_data rx1950_gpio_keys_data = {
+ .buttons = rx1950_gpio_keys_table,
+ .nbuttons = ARRAY_SIZE(rx1950_gpio_keys_table),
+};
+
+static struct platform_device rx1950_device_gpiokeys = {
+ .name = "gpio-keys",
+ .dev.platform_data = &rx1950_gpio_keys_data,
+};
+#endif
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_ohci,
@@ -253,6 +351,16 @@
//add for beeper
&s3c_device_timer,
&smdk2440_beeper_device,
+ //add for button
+ &rx1950_device_gpiokeys,
+
};
static void __init smdk2440_map_io(void)
添加完以后呢直接编译即可。而我们的menuconfig不用修改,因为内核默认支持按键。
2.在开发板上搞
~>:ls -l /proc/bus/input
这里可以看见handler这个处理输入设备的接口。
~>:cat /proc/bus/input/devices
可以看见为按键时间分配了一个设备节点:
sysfs=/devices/platform/gpio-keys.0/input/input0
Handlers=kbd event0
接下来可以用hexdupm进行简单的测试:
>:hexdump /dev/event0
可以发现每按一个键都会出现四行数据,因为每一次按键包括按下和抬起两个动作,而每个动作结束后还会发生一个同步事件发生。
每行的倒数第四个数据到倒数第二个数据分别对应***input_event***数据结构:
struct input_event{
struct timeval time;
_u16 type; //按键类型
_u16 code; //按键代码
_u16 value; //按键的值
};
倒数第三行是我们的按键代码,每个按键代码是唯一的,进过测试得到每个按键代码值为:
KEY_1:2
KEY_2:3
KEY_3:4
KEY_4:5
接下来就根据code值写程序来控制led灯咯。
#include <stdio.h>
#include<stdint.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<linux/input.h>
#include<unistd.h>
#include <stdlib.h>
#include <linux/input.h>
#define EV_PRESS 1
#define EV_RELEASE 0
int main(void)
{
char buf[50];
int fd_button;
int fd_led[4];
int i;
struct input_event ev_key;
fd_button= open("/dev/event0", 666);
if(fd_button < 0)
{
perror("open device buttons");
exit(1);
}
for(i=1;i<=4;i++)
{
snprintf((char *)buf,sizeof(buf),"/sys/class/leds/led%d/brightness",i);
fd_led[i] = open(buf, O_RDWR);
if(fd_led[i]<0)
{
printf("can't open the file led%d",i);
return -1;
}
}
while(1)
{
read(fd_button,&ev_key,sizeof(struct input_event));
if(EV_KEY==ev_key.type && EV_PRESS==ev_key.value) //value=1 表示现在是按下
{
switch(ev_key.code) //通过对code传值来确定是哪个按键
{ static int count_key1 = 0; //定义一个静态变量count_key1 ,来计算按键按下次数
case KEY_1:
if(count_key1%2==0)
{
write(fd_led[1],"1",1);
count_key1++;
}
else
{
write(fd_led[1],"0",1);
count_key1++;
}
break;
case KEY_2:
write(fd_led[2],"1",1);
sleep(1);
write(fd_led[2],"0",1);
break;
case KEY_3:
write(fd_led[3],"1",1);
sleep(1);
write(fd_led[3],"0",1);
break;
case KEY_4:
write(fd_led[4],"1",1);
sleep(1);
write(fd_led[4],"0",1);
break;
default:
break;
}
}
else if(EV_KEY==ev_key.type && EV_RELEASE==ev_key.value) //value=0表示现在按键释放
{
printf("relase the key!\n");
}
}
for(i=1;i<=4;i++)
{
close(fd_led[i]);
}
close(fd_button);
return 0;
}
snprintf( )函数从源码字符串中拷贝n-1到目标串中,再在后面加个0,这里要防止溢出哦。
循环打开led1,led2,led3,led4。
char buf[50];
int led[4];
for(i=0; i<=4; i++)
{
snprintf((char *)buf,sizeof(buf),"/sys/class/leds/led%d/brightness",i);
fd_led[i] = open(buf, O_RDWR);
if(fd_led[i]<0)
{
printf("can't open the file led%d",i);
return -1;
}
}