2. 例-中断(GPIO按键事件)

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看按键的上报情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值