今天,我们要来实现一个基于tiny4412开发板上的最简本的按键中断驱动程序,那么,写这个程序之前,我们先来了解下Linux中断的基本知识。
在Linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线。所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,我们可以来看下4412上与板子上相连的按键。
下面这张电路图,也就是4412板子上按键的电路图和CPU的连接关系图:
我们明显可以看到,4个按键分别接在GPX3这几个引脚上,对应着引脚,接下来我们就可以看数据手册,配置寄存器了
在4412的板级文件配置中,已经提供了IO口的定义,在以下文件中可以找到,这样我们就不用去配置寄存器了,内核已经帮我们配好了:
找到这个文件:
arch/arm/mach-exynos/include/mach/gpio.h
然后找到我们要配置按键的宏:
#define EXYNOS4_GPX3(_nr) (EXYNOS4_GPIO_X3_START + (_nr))
对应的,我们的按键nr就是2,3,4,5 。 因为板子上有4个按键。
那么如何来写一个按键中断程序?Linux内核为我们提供了大量的有关编写中断驱动程序的API函数,我们只需要会使用就行了,不用去看着手册一个个配置,很方便,API列表如下:
#include <linux/interrupt.h> int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) irq: 中断号 arch/arm/plat-s3c64xx/include/plat/irqs.h handler: 中断处理函数 irqreturn_t handler(int irq, void *dev_id); irqreturn_t: See include/linux/irqreturn.h irqflags: See line 21-59 in include/linux/interrupt.h 使用IRQF_SHARED共享irq时, irqflags必须相同 如: request_irq(IRQ_EINT(0), handler1, IRQF_TRIGGER_FALLING | IRQF_SHARED, "dev1", &dev1); request_irq(IRQ_EINT(0), handler2, IRQF_TRIGGER_FALLING | IRQF_SHARED, "dev2", &dev2); devname: 设备名, cat /proc/interrupts dev_id: 发生中断时将dev_id传递给handler函数, irqflags含有IRQF_SHARED时dev_id不能为NULL, 并且要保证唯一 dev_id一般采用当前设备的结构体指针 void free_irq ( unsigned int irq, void * dev_id); 释放匹配irq和dev_id的中断, 如果irq有多个相同的dev_id, 将释放第一个 So, 共享中断的dev_id不是唯一时, 可能会释放到其它设备的中断 void disable_irq(unsigned int irq); 关闭irq号中断 void enable_irq(unsigned int irq); 开启irq号中断 void local_irq_save(unsigned long flags); 关闭当前CPU中断并保存当前状态到flags void local_irq_restore(unsigned long flags); 恢复flags到当前CPU void local_irq_disable(void); 关闭当前CPU中断 void local_irq_enable(void); 开启当前CPU中断 注: 没有关闭和开启所有CPU中断的函数(没必要)
而申请中断号,需要使用这个函数来实现:
static inline int gpio_to_irq(unsigned int gpio)
只要往里面传一个参数,这个参数就是我们上面说的板级文件里的那个宏,它就用自动帮你申请中断号。
接下来,我们就来实现这个简单的按键驱动程序:
步骤:
1、申请中断号
2、注册中断服务
代码:
/* * linux/drivers/char/tiny4412_pwm.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/fb.h> #include <linux/backlight.h> #include <linux/err.h> #include <linux/pwm.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #include <linux/timer.h> /*timer*/ #include <asm/uaccess.h> /*jiffies*/ #include <linux/delay.h> #include <linux/interrupt.h> //中断服务函数 static irqreturn_t irq_fuction(int irq, void *dev_id) { printk("key_irq:%d\n",irq); return IRQ_HANDLED ; } static int __init tiny4412_Key_irq_test_init(void) { int err = 0 ; int irq_num1 ; int irq_num2 ; int irq_num3 ; int irq_num4 ; printk("irq_key init\n"); //为4个按键都申请中断号 irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2)); irq_num2 = gpio_to_irq(EXYNOS4_GPX3(3)); irq_num3 = gpio_to_irq(EXYNOS4_GPX3(4)); irq_num4 = gpio_to_irq(EXYNOS4_GPX3(5)); //请求中断 request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key1",(void *)"key1"); request_irq(irq_num2,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key2",(void *)"key2"); request_irq(irq_num3,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key3",(void *)"key3"); request_irq(irq_num4,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key4",(void *)"key4"); return 0 ; } static void __exit tiny4412_Key_irq_test_exit(void) { //释放中断irq int irq_num1 ; int irq_num2 ; int irq_num3 ; int irq_num4 ; printk("irq_key init\n"); irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2)); irq_num2 = gpio_to_irq(EXYNOS4_GPX3(3)); irq_num3 = gpio_to_irq(EXYNOS4_GPX3(4)); irq_num4 = gpio_to_irq(EXYNOS4_GPX3(5)); free_irq(irq_num1,(void *)"key1"); free_irq(irq_num2,(void *)"key2"); free_irq(irq_num3,(void *)"key3"); free_irq(irq_num4,(void *)"key4"); } module_init(tiny4412_Key_irq_test_init); module_exit(tiny4412_Key_irq_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("YYX"); MODULE_DESCRIPTION("Exynos4 KEY Driver");
编译好下zImage到开发板上,按下对应的按键可以看到Log信息:
还要注意一点,内核里,要将原先友善之臂实现的对应的按键驱动程序给屏蔽掉。
如果是配置android的内核,就需要将板级文件配置中的下面这个初始化给干掉,这个按键驱动才能够注册成功,才能够看到以上效果:
板级驱动文件位于:
arch/arm/mach/exynos/mach-tiny4412.c
找到这个结构体:
static struct platform_device *smdk4x12_devices[] __initdata = {
。。。。。
#ifdef CONFIG_SAMSUNG_DEV_KEYPAD
&samsung_device_keypad,
#endif
&tiny4412_device_1wire,
&tiny4412_device_adc,
#ifdef CONFIG_INPUT_GPIO
&tiny4412_input_device,
#endif
};
将以上的&tiny4412_input_device,注释,再重新编译即可。