1.中断注册
#include <linux/interrupt.h>
int request_irq(unsigned int irq,
void (*handler)(int, void*, struct pt_regs *),
unsigned long flags,
const char *devname,
void *dev_id)
返回0表示成功,或者返回一个错误码
参数说明
irq: 中断号 ;
handler:中断处理函数;
flags: 中断管理有关的各种选项;
–>IRQF_DISABLED(SA_INTERRUPT)
如果设置该位,表示是一个“快速”中断处理程序;
如果没有设置这位,那么是一个“慢速”中断处理程
序。
–>IRQF_SHARED(SA_SHIRQ)
该位表明该中断号是多个设备共享的。
–>IRQF_TRIGGER_RISING 上升沿触发
–>IRQF_TRIGGER_FALLING 下降沿触发
–>IRQF_TRIGGER_HIGH 高电平触发
–>IRQF_TRIGGER_LOW 低电平触发
devname:设备名;
dev_id:共享中断时使用;
或者使用函数
int request_threaded_irq(unsigned int irq,
irq_handler_t handler,
irq_handler_t thread_fn,
unsigned long irqflags,
const char *devname,
void *dev_id);
参数说明
irq: 中断号 ;
handler:中断处理函数,可以理解为中断上半部,不能做耗时操作;
thread_fn:可以理解为中断下半部,能做耗时操作,因为这里会建一个线程;
irqflags: 中断管理有关的各种选项,同上;
devname:设备名;
dev_id:共享中断时使用;
注意
如果遇到“Threaded irq requested with handler=NULLand !ONE_SHOT for irq xxx”这种错,irqflags参数就需要或上IRQF_ONESHOT。IRQF_ONESHOT是为了保证thread_fn函数执行完之后才继续接受中断,也就是说该中断在整个thread_fn函数处理过程中都是不会再次触发,直到处理完成。
如:
request_threaded_irq(irq,
NULL,
gpio_keys_gpio_isr,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"sunxi-keyboard-gpio",NULL);
2.中断处理程序
irqreturn_t key_int(int irq, void *dev_id)
{
//1. 检测是否发生了按键中断
//2. 清除已经发生的按键中断
//3. 打印按键值
return 0;
}
注意:中断处理程序不能引起阻塞、不能调度。
3.注销中断
#include <linux/interrupt.h>
void free_irq(unsigned int irq, void *dev_id)
4.一个简单的GPIO按键案例
–>dts配置
&soc {
keygpio {
compatible = "allwinner,sunxi-keygpio";
status = "okay";
button@0 {
label = "Key VOLUME UP";
linux,code = <115>;
gpios = <&r_pio PL 8 6 1 0xffffffff 0xffffffff>;
wakeup-source;
};
button@1 {
label = "KEY VOLUME DOWN";
linux,code = <114>;
gpios = <&r_pio PL 9 6 1 0xffffffff 0xffffffff>;
wakeup-source;
};
button@2 {
label = "Key Menu";
linux,code = <139>;
gpios = <&r_pio PL 10 6 1 0xffffffff 0xffffffff>;
wakeup-source;
};
};
};
简单说明
button@X就是对应的GPIO按键,如果有两个GPIO按键那就是button@0,button@1;
label 就是为了说明这个按键是什么功能键;
gpios就是对应的GPIO口,示例的是全志平台的表示方法;
linux,code就是上报的按键码;
wakeup-source表示该按键对应的GPIO是不是唤醒源,有没有休眠唤醒功能;全志PL口是唤醒源,用用作深度休眠唤醒;
–>驱动代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/keyboard.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#if defined(CONFIG_PM)
#include <linux/pm.h>
#endif
struct key_gpio {
int gpio;
int code;
u8 can_wakeup;
u8 active_low;
u8 is_last_down;
};
static struct key_gpio buttons[5];
static u8 nbuttons = 0;
static struct input_dev *sunxikbd_dev;
#ifdef CONFIG_OF
/*
* Translate OpenFirmware node properties into platform_data
*/
static struct of_device_id const sunxi_keygpio_of_match[] = {
{ .compatible = "allwinner,sunxi-keygpio"},
{ },
};
MODULE_DEVICE_TABLE(of, sunxi_keygpio_of_match);
#else /* !CONFIG_OF */
#endif
static int gpio_keys_get_devtree_pdata(struct device *dev)
{
struct device_node *node, *pp;
struct key_gpio *button;
int error;
int i;
node = dev->of_node;
if (!node)
return -ENODEV;
nbuttons = of_get_available_child_count(node);
if (nbuttons == 0)
return -ENODEV;
i = 0;
for_each_available_child_of_node(node, pp) {
button = &buttons[i++];
button->gpio = of_get_named_gpio_flags(pp, "gpios", 0, NULL);
if (button->gpio < 0) {
error = button->gpio;
if (error != -ENOENT) {
if (error != -EPROBE_DEFER)
dev_err(dev,
"Failed to get gpio flags, error: %d\n",
error);
return error;
}
}
if (!gpio_is_valid(button->gpio)) {
dev_err(dev, "Found button without gpios\n");
return -EINVAL;
}
if (of_property_read_u32(pp, "linux,code", &button->code)) {
dev_err(dev, "Button without keycode: 0x%x\n",
button->gpio);
return -EINVAL;
}
//button->desc = of_get_property(pp, "label", NULL);
button->can_wakeup = of_property_read_bool(pp, "wakeup-source");
//nbuttons++;
printk("buttons[%d]: gpio=%d, code=%d, can_wakeup=%d\n",
i-1,button->gpio,button->code,button->can_wakeup);
}
printk(KERN_ERR"----%s finish, have %d button\n",__func__,nbuttons);
if (nbuttons == 0)
return -EINVAL;
return 0;
}
static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{
int i,value;
for (i = 0; i < nbuttons; i++) {
value = gpio_get_value(buttons[i].gpio);
printk("buttons[%d]:value=%d\n",i,value);
if(value == 0 && buttons[i].is_last_down == 0){
//report down
input_report_key(sunxikbd_dev, buttons[i].code, 1);
input_sync(sunxikbd_dev);
} if(value == 1 && buttons[i].is_last_down == 1) {
//report up
input_report_key(sunxikbd_dev, buttons[i].code, 0);
input_sync(sunxikbd_dev);
}
if(value == 0)
buttons[i].is_last_down = 1;
else
buttons[i].is_last_down = 0;
}
return IRQ_HANDLED;
}
static int sunxi_keygpio_probe(struct platform_device *pdev)
{
int ret = -1;
u8 i;
printk(KERN_ERR"-----%s----\n",__func__);
ret = gpio_keys_get_devtree_pdata(&pdev->dev);
if (ret < 0)
return -1;
/* init input */
sunxikbd_dev = input_allocate_device();
if (!sunxikbd_dev) {
pr_err("sunxikbd: not enough memory for input device\n");
return -ENOMEM;
}
sunxikbd_dev->name = "sunxi-keyboard-gpio";
sunxikbd_dev->phys = "sunxikbd/input1";
sunxikbd_dev->id.bustype = BUS_HOST;
sunxikbd_dev->id.vendor = 0x0001;
sunxikbd_dev->id.product = 0x0001;
sunxikbd_dev->id.version = 0x0100;
sunxikbd_dev->evbit[0] = BIT_MASK(EV_KEY);
for (i = 0; i < nbuttons; i++) {
if (buttons[i].code < KEY_MAX)
set_bit(buttons[i].code, sunxikbd_dev->keybit);
buttons[i].is_last_down = 0;
}
ret = input_register_device(sunxikbd_dev);
if (ret < 0){
goto input_error;
}
for (i = 0; i < nbuttons; i++) {
ret = request_threaded_irq(gpio_to_irq(buttons[i].gpio),NULL,
gpio_keys_gpio_isr,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"sunxi-keyboard-gpio",NULL);
if(ret < 0) {
printk(KERN_ERR"buttons[%d] request irq error!!!\n",i);
}
}
return 0;
input_error:
input_free_device(sunxikbd_dev);
return -1;
}
static int sunxi_keygpio_remove(struct platform_device *pdev)
{
u8 i;
for (i = 0; i < nbuttons; i++) {
free_irq(gpio_to_irq(buttons[i].gpio),NULL);
}
input_unregister_device(sunxikbd_dev);
input_free_device(sunxikbd_dev);
return 0;
}
#ifdef CONFIG_PM
static int sunxi_keygpio_suspend(struct device *dev)
{
u8 i;
for (i = 0; i < nbuttons; i++) {
if (buttons[i].can_wakeup) {
enable_irq_wake(gpio_to_irq(buttons[i].gpio));
} else {
disable_irq(gpio_to_irq(buttons[i].gpio));
}
}
return 0;
}
static int sunxi_keygpio_resume(struct device *dev)
{
u8 i;
for (i = 0; i < nbuttons; i++) {
if (buttons[i].can_wakeup) {
disable_irq_wake(gpio_to_irq(buttons[i].gpio));
} else {
enable_irq(gpio_to_irq(buttons[i].gpio));
}
}
return 0;
}
#endif
#ifdef CONFIG_PM
static const struct dev_pm_ops sunxi_keygpio_pm_ops = {
.suspend = sunxi_keygpio_suspend,
.resume = sunxi_keygpio_resume,
};
#define SUNXI_KEYGPIO_PM_OPS (&sunxi_keygpio_pm_ops)
#endif
static struct platform_driver sunxi_keygpio_driver = {
.probe = sunxi_keygpio_probe,
.remove = sunxi_keygpio_remove,
.driver = {
.name = "sunxi-keygpio",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = SUNXI_KEYGPIO_PM_OPS,
#endif
.of_match_table = of_match_ptr(sunxi_keygpio_of_match),
},
};
module_platform_driver(sunxi_keygpio_driver);
MODULE_DESCRIPTION("sunxi-keygpio driver");
MODULE_LICENSE("GPL");
insmod驱动成功驱动之后,使用getevent看按键的上报情况。