之前一直学习的是Linux系统下面的字符驱动开发,但是那仅仅是Linux驱动下面的冰山一角,Linux驱动开发还包括什么网络设备开发、原子锁、块设备开发、网络设备开发等等部分,本篇文章将介绍linux架构下面中断注册。中断是任何一个状态机下面的重要部分,不管是windows大型系统还是threadx-rtos、freertos等实时操作操作系统,亦或者是裸机程序开发,中断部分都是系统开发的重要部分,它能够保证你的系统实时性以及紧急任务处理的需求。这里介绍Linux中断注册。
代操作系统的中断程序从我的经验来看包涵一下几个过程:
1、中断产生时候 保护系统当前状态,也就是把cpu当前的状态保存起来
2、执行中断程序,执行的中断程序应该尽量短,不去过多占用cpu时长,此处引入Linux的頂半部机制以及底半部机制,頂半部是指中断代码需要立即执行的情况,对于情况不紧急的情况,会将中断程序注册到队列,在cpu运行状态不是那么繁忙的情况下再去执行程序,底半部机制是指中带代码需要立即执行,不得延误(后面会详细介绍頂半部机制以及底半部机制)。
3、恢复现场工作,中断执行后,跳出中断程序,并恢复中断前cpu状态,继续执行之前的任务。
下图能够在 cat /proc/interrupt下面查看,系统当前注册的中断
中断注册最重要的就是request_irq函数,其输入参数如下,分别是:中断号,中断回调函数,中断条件(上升沿还是下降沿),中断的名字、依托的设备文件 ///中断号和平台有关
//文件路径 linu/irq/manage.c
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
我这里使用的是source insght软件查看代码
request_threaded_irq(irq, handler, NULL, flags, name, dev)跟踪进去,代码如下
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)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc))//该中断是否已被注册
return -EINVAL;
}
这个东西谅解个大概就行,知道一下中断是怎么样被注册的就OK!最主要的就是把它用起来。
下面还有一个重要的东西
void free_irq(unsigned int irq, void *dev_id)//对应中断号以及设备ID
如何获取中断号需要用到这个宏 IRQ_EINT(),在下面这个目录下
arch/arm/mach-s3c2410/include/mach/irqs.h
IRQ_EINT()这个宏定义和硬件平台有关,不同的硬件有不同的中断号,这个需要在datasheet中查找。
我打算使用开发板上面的两个按钮,一个是home按键,另外一个是back按键,
在数据手册上面对应
现在我们要做的就是注册这两个中断号,现在现在平台文件中注册设备,需要重新编译内核。
平台文件中添加如下代码
struct platform_device key_irq_fxq = {
.name = "key_irq_fxq",
.id = -1,
};
&key_irq_fxq,
重新编译内核。
现在上源代码
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
//#include "gps.h"
#include
//中断头文件
#include
#include
#define DPRINTK(x...) printk("keyirq DEBUG:" x)
#define DRIVER_NAME "keyirq"
static irqreturn_t eint9_interrupt(int irq,void *dev_id)
{
printk("receive a interrupt 9!\n");
return IRQ_HANDLED;
}
static irqreturn_t eint10_interrupt(int irq,void *dev_id)
{
printk("receive a interrupt 10!\n");
return IRQ_HANDLED;
}
static int keyirq_probe(struct platform_device *pdev)
{
//int ret, i;
char *banner = "keyirq Initialize\n";
printk(banner);
//注册中断
request_irq(IRQ_EINT(9),eint9_interrupt,IRQ_TYPE_EDGE_FALLING,"my_eint9",pdev);
request_irq(IRQ_EINT(10),eint10_interrupt,IRQ_TYPE_EDGE_FALLING,"my_eint10",pdev);
return 0;
}
static int keyirq_remove (struct platform_device *pdev)
{
free_irq(IRQ_EINT(9),pdev);
free_irq(IRQ_EINT(10),pdev);
return 0;
}
static int keyirq_suspend (struct platform_device *pdev, pm_message_t state)
{
DPRINTK("keyirq suspend:power off!\n");
return 0;
}
static int keyirq_resume (struct platform_device *pdev)
{
DPRINTK("keyirq resume:power on!\n");
return 0;
}
static struct platform_driver keyirq_driver = {
.probe = keyirq_probe,
.remove = keyirq_remove,
.suspend = keyirq_suspend,
.resume = keyirq_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static void __exit keyirq_exit(void)
{
platform_driver_unregister(&keyirq_driver);
}
static int __init keyirq_init(void)
{
return platform_driver_register(&keyirq_driver);
}
module_init(keyirq_init);
module_exit(keyirq_exit);
MODULE_LICENSE("Dual BSD/GPL");
重新烧录最小系统后,编译以上源码,以模块的方式加载进内核。
使用命令 cat /proc/interrupts 查看中断信息。
点击按键,出现以下信息:
OK,中断程序正常工作!