linux 驱动开发之 中断注册

    之前一直学习的是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;


if (!handler) {    //中断回调函数是否为空
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}


action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);  ///分配内存空间
if (!action)
return -ENOMEM;


action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;


chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action);   ///注册到内核
chip_bus_sync_unlock(desc);


if (retval)
kfree(action);


#ifdef CONFIG_DEBUG_SHIRQ_FIXME
if (!retval && (irqflags & IRQF_SHARED)) {
/*
* It's a shared IRQ -- the driver ought to be prepared for it
* to happen immediately, so let's make sure....
* We disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;


disable_irq(irq);
local_irq_save(flags);


handler(irq, dev_id);


local_irq_restore(flags);
enable_irq(irq);

}

这个东西谅解个大概就行,知道一下中断是怎么样被注册的就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 <linux/init.h>
#include <linux/module.h>


#include <linux/kernel.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
//#include <mach/gpio-bank.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
//#include "gps.h"
#include <linux/delay.h>


//中断头文件
#include <linux/irq.h>
#include <linux/interrupt.h>


#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,中断程序正常工作!




猜你喜欢

转载自blog.csdn.net/a13698709128/article/details/80644817