linux驱动-中断

Linux中断简述:

实现一个linux中断,需要经过申请注册中断处理函数(安装中断),然后分别实现linux中断的前半部分和后半部分。前半部分,就是申请注册的中断处理函数(中断服务程序);中断后半部分,就是中断服务函数结束后,接着处理中断还没处理完的部分。前半部分是必须的,后部分不是必须的。前半部分,处理的任务要快时间短,后半部分是处理更多数据更多耗时的任务。

纲要:

本文将按照下面几点来讲述:

  • 申请注册中断处理函数
  • 中断使能和禁止
  • 中断前半部分
  • 中断后半部分
  • 查看系统中断情况
  • 如何确定中断号

申请注册中断处理函数:

  • 1、申请注册中断处理函数的内核API:
  • (1)API要包含的头文件:
#include <linux/interrupt.h>
  • (2)API:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)

功能:
向系统申请注册中断处理函数

第一个参数:
irq中断号

第二个参数:
handler中断处理函数指针,这个函数就是前半部分。handler这个函数,是判断最终是否为有效中断,如果是有效中断,需要返回IRQ_HANDLED,如果判断是无效中断需要返回IRQ_NONE。

第四个参数:
一个与中断管理相关的选项的位掩码,定义都在interrupt.h里面,是列举部分:
IRQF_TRIGGER_RISING:
上升沿触发中断

IRQF_TRIGGER_FALLING:
下降沿触发中断

IRQF_TRIGGER_HIGH:
高电平触发中断

IRQF_TRIGGER_LOW:
低电平触发中断

IRQF_DISABLED:
注册是,禁止中断。没有这个标志,就表示注册完后,中断就处于使能状态。

IRQF_SHARED:
表示共享中断,加这个标志表示,我们可以用同一个irq注册多个不同的handler,并且每个handler对应一个不同的dev,且dev不能为NULL。当同一个irq产生中断,注册在这个irq上的handler都会得到处理。这就是共享中断。

第五个参数:
name是字符串指针,申请这个中断的名字,在/proc/interrputs会显示

第六个参数:
设置SA_SHIRQ时,才使用这个参数。

返回值:
返回0表示成功,非零表示失败。

  • 2、释放注册的中断
void free_irq(unsigned int, void *);

功能: 释放 request_irq()注册的中断
第一个参数: 中断号,是request_irq()第一个参数
第二个参数: 有共享中断时用,用来释放同一个中断号上,其中一个handler

中断使能和禁止:

  • 1、使能和禁止中断
  • (1)API包含的头文件:
#include <linux/interrupt.h>
  • (2)单个中断的禁止使能
void disable_irq(int irq);//禁止irq中断,irq要禁止的中断号
void disable_irq_nosync(int irq);//禁止irq中断,irq要禁止的中断号。
void enable_irq(int irq);

disable_irq_nosync相对disable_irq立即返回,因此, 使用disable_irq_nosync 快一点, 但是可能使你的设备有竞争情况。

  • (3)所有中断的禁止和使能
void local_irq_save(unsigned long flags);//禁止所有中断,并将中断状态保存在flags中
void local_irq_restore(unsigned long flags);//使能所有中断,并恢复中断状态

void local_irq_disable(void);//禁止所有中断
void local_irq_enable(void);//使能所有中断

local_irq_save使用它时,一般定义一个unsigned long全局变量做为参数。

中断前半部分:

中断前半部分,就是中断服务函数,是request_irq注册的handler。在注册的这个函数里面,需要用Tasklet的tasklet_schedule,或者工作队列的queue_work,将后半部分任务交给系统。

关于tasklet和workqueue可以看:linux驱动—等待队列、工作队列、Tasklets

中断后半部分:

中断后部分,就是前半部分用tasklet,workqueue注册的函数。

查看系统中断情况:

  • 1、/proc/interrupts
    直接cat /proc/interrupts会出现:
    这里写图片描述

第一列0,2,8,10,11…表示中断号
第二列,第三列表示相应的中断在这个CPU上产生中断次数。对多核CPU,是几核这里就有几列。
第三列,表示中断控制器的一些信息
第四列,中断名字,就是request_irq参数name的字符串。

  • 2、/proc/stat
    直接cat /proc/stat出现:
    这里写图片描述

其中以intr开始的,已ctxt结束的,这段信息表示中断情况。
intr后面的第一个数表示所有中断产生的次数,再后面第二个数表是0号中断产生的次数,第三个表示1号中断产生的次数,第四个表示2号中断产生的次数,以此类推。

总的有多少个中断号,从这里也可以看出,intr到ctxt之间数字个数减去1,再加1,就是总的中断号数。

  • 3、/proc/irq/n/smp_affinity
    直接cat /proc/irq/n/smp_affinity(其中n是中断号数字)会出现一个数字,数字含义如下理解:
    smp_affinity 自身是一个位掩码(bitmask),特定的位对应特定的 CPU,这样,01 就意味着只有第一个 CPU 可以处理对应的中断,而 0f(0x1111)意味着四个 CPU 都会参与中断处理。

如何确定中断号

request_irq这个函数第一个参数irq中断号,怎么确定,当然是dts。

/{
    interrupt-parent = <0x2>;

    gpio@ff750000{
        interrupts = <0x0 0x51 0x4>;
        interrupt-controller;
        #interrupt-cell = <0x2>;
        phanle = <0x98>;
    };

    spi@ff110000{
        interrupts = <0x0 0x2c 0x4>;
    };

    interrupt-controller@ffc01000{
        interrupt-controller;
        #interrupt-celll = <0x3>;
        phandle = <0x2>;        
    };
};

以上dts只是截取了中断部分。

interrupt-parent:指定本节点中断控制器
interrupt-controller:有这个属性的节点,表示本节点带有中断控制器。
#interrupt-celll:表示中断属性interrupts有几个信息
interrupts:这个属性的值,表示了中断信息

先看spi节点解析:
没有interrupt-parent,而又有interrupts属性,表示本节点没有中断控制器,spi节点没有用interrupt-parent指定中断控制器,那么spi节点的默认中断控制器,就是用父结点(如果父节点也没有指定中断控制器,就会一直找下去,父的父的…直到根节点)的中断控制器,父节点是个根节点,带有interrupt-parent = <0x2>,指定了中断控制器,其中interrupt-parent的值是0x2,就是指phandle为0x2的节点(phandle的值,在dts文件中是唯一的),查找下来就是“interrupt-controller@ffc01000”,这个节点,有interrupt-controller属性,表明本节点是个中断控制器,#interrupt-celll = <0x3>表示,所属
“interrupt-controller@ffc01000”这个中断控制器下的设备spi的interrupts属性信息有三个值,spi的interrupts = <0x0 0x2c 0x4>,表示中断类型为0x0,中断号是0x2c(44),0x4表示高电平触发。

如果是ARM体系的,关于interrupts的说明可以看Documentation/devicetree/bindings/arm/gic.txt文档下面截取一部分:
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
interrupts.

The 2nd cell contains the interrupt number for the interrupt type.
SPI interrupts are in the range [0-987]. PPI interrupts are in the
range [0-15].

The 3rd cell is the flags, encoded as follows:
bits[3:0] trigger type and level flags.
1 = low-to-high edge triggered
2 = high-to-low edge triggered
4 = active high level-sensitive
8 = active low level-sensitive
bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of
the 8 possible cpus attached to the GIC. A bit set to ‘1’ indicated
the interrupt is wired to that CPU. Only valid for PPI interrupts.

spi所属控制器#interrupt-celll = <0x3>,spi自己interrupts = <0x0 0x2c 0x4>,根据上面说明,0x0表示中断类型是SPI interrupt,中断号是0x2c,触发类型是高电平触发。

SPI interrupts:共享中断,表示如果禁止这类中断时,多核CPU,所有的CPU都不响应被禁止的中断。

PPI interrupts:独立中断,每个处理器拥有独立中断,如禁止其中某一个CPU的中断(本CPU是不响应这个中断了),其他CPU仍然响应这个中断。

现在来看gpio这个节点,没有interrupt-parent属性,表示它也是属于“interrupt-controller@ffc01000”这个中断控制器之下的,因此也可以看出interrupts= <0x0 0x51 0x4>用的也是SPI interrupts类型中断,中断号是0x51,高电平触发。但是它有interrupt-controller属性,表示gpio这个节点也是中断控制器,是属于“interrupt-controller@ffc01000”下的中断控制器,这就是中断控制器的级联,中断控制器的级联会形成中断树。在gpio节点下也设置了#interrupt-cell = <0x2>,表示属于gpio这个中断控制器下的中断设备interrupts属性值,只有两个,第一个是中断号,第二个是触发类型,去掉了中断类型,因为gpio中断控制被设置成了SPI interrupts类型,所以在它中断控制器之下的,当然也是SPI interrupts。

gpio节点,可以看出,gpio配置成中断模式后,产生的中断都是0x51号中断,可以用request_irq申请一个0x51号中断的处理函数(假设这个中断处理函数是handlerAA),注册之后,任何一个gpio端口产生了中断,都进入到这个中断处理函数handlerAA,然后在handlerAA中,再去读取gpio中断状态控制寄存器,查出是那个gpio产生了中断,再调用相应函数处理。

handlerAA这个函数,我们可以用request_irq来注册,但是具体那个gpio中断函数的注册,就要我们自己去实现。一般情况,CPU平台厂商会实现int gpio_to_irq(unsigned int gpio)这样一个函数,我们按照linux 标准的gpio使用方法申请后,只要将申请来的gpio号,带入这个函数,就会返回中断号给我们,有了中断号,我们就可以调用中断申请函数,安装中断了。注意,个人理解,这个时候应该申请为共享中断,并且中断申请函数的第六个参数要用起来,不能为NULL。

总结,确定设备用几号中断,只需要在dts中查找interrupts属性,就可以知道。

附加:
在arch/x/kernel下都有irq.c,在这个文件里基本都带有“do_IRQ”字符的函数,也都会调用generic_handle_irq()这个函数,

generic_handle_irq()定义在kernel/irq/irqdesc.c里面

struct irq_desc这个结构体,是描述中断号与具体硬件中断号绑定的结果,在开机初始化时,就已经创建了系统所有中断号与具体硬件绑定,irq_desc结构体数组。

irq_to_desc函数是用中断号,来获取irq_desc结构体。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值