158 Linux中断基础概念

一、回顾

裸机开发中:

  • 通用中断控制器(GIC)
    中断类型、硬件中断号、分发器和cpu接口单元

  • 中断向量表
    一级查表、二级查表

  • 中断处理流程
    进入irq模式、保护现场、获取硬件中断编号、执行中断处理函数、还原现场

二、设备树中GIC中断控制器节点

arch/arm/boot/dts/imx6ull.dtsi

初始化中断控制器、设置其他中断控制器节点的描述格式

intc: interrupt-controller@a01000 {
		compatible = "arm,cortex-a7-gic";
		// 描述下一级中断信息节点所需要的单元个数
		#interrupt-cells = <3>;
		// 表示该设备是一个中断控制器,外设可以连接在该中断控制器上
		interrupt-controller;
		// GIC的分发器和cpu接口单元寄存器地址
		reg = <0xa01000 0x1000>,
		      <0xa02000 0x100>;
	};

因为中断控制器是linux系统中必不可少的,因此配置gic的代码在内核中并不是以驱动模块的形式存在的。因此搜索compatilble属性值匹配到的不是一个驱动模块文件。

三、外设中断控制器节点

arch/arm/boot/dts/imx6ull.dtsi
某个外设和中断密切相关
管理某一种具体中断

gpio5: gpio@20ac000 {
				compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
				reg = <0x20ac000 0x4000>;
				// 负责管理的中断类型,上面父节点的#interrupt-cells = <3>从,此属性的每个单元用3个值来描述
				interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
					     <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
				clocks = <&clks IMX6UL_CLK_GPIO5>;
				// 和上面一样,有此属性表示此节点是一个中断控制器,负责管理某一种的中断
				gpio-controller;
				#gpio-cells = <2>;
				interrupt-controller;
				// 其它外设节点想要使用这个中断控制器上的中断时的描述格式。使用范例见下。
				#interrupt-cells = <2>;
				gpio-ranges = <&iomuxc 0 7 10>, <&iomuxc 10 5 2>;
			};
  • interrupts属性:
    GIC_SPI:中断类型:0 表示 SPI 中断,1 表示 PPI 中断
    74:中断号,对于 SPI 中断来说中断号的范围为 0 ~ 987,对于 PPI 中断来说中断号的范围为 0~15
    IRQ_TYPE_LEVEL_HIGH:中断类型,高电平触发

三、其他设备使用中断节点

使用某一种具体中断(控制器)

button_interrupt {
    compatible = "button_interrupt";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_button>;
    button_gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;
    status = "okay";
    // 表明归属的上一级中断,使用上面描述的gpio5中断控制器
    interrupt-parent = <&gpio5>;
    // gpio5上有32个外部中断,button节点只用到其中一个,1表示gpio5-1;上升沿触发中断
    interrupts = <1 IRQ_TYPE_EDGE_RISING>;
};

1、中断类型

include/linux/irq.h
标志, bit[3:0]表示中断触发类型,为 1 的时候表示上升沿触发,为 2 的时候表示下降沿触发,为 4 的时候表示高电平触发,为 8 的时候表示低电平触发。 bit[15:8]为 PPI 中断的 CPU 掩码。

// 常用的五个触发类型
enum {
	IRQ_TYPE_NONE		= 0x00000000,
	IRQ_TYPE_EDGE_RISING	= 0x00000001,
	IRQ_TYPE_EDGE_FALLING	= 0x00000002,
	IRQ_TYPE_EDGE_BOTH	= (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
	IRQ_TYPE_LEVEL_HIGH	= 0x00000004,
	IRQ_TYPE_LEVEL_LOW	= 0x00000008,
	...
	}

四、常用函数

1、request_irq()函数

申请中断,若申请成功会自动激活(使能)中断,不再需要我们手动去使能中断。
此函数可能会导致睡眠。

include/linux/interrupt.h

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)
  • 参数:
    irq:要申请的中断号
    handler:函数指针,指向中断处理函数,详见下(1)
    flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,详见下(2)
    name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
    dev:传递给中断处理函数的第二个参数,一般指向 device结构体变量,区分不同设备共用同一中断。如果将 flags 设置为 IRQF_SHARED的话, dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。

  • 返回值:
    成功:0
    失败:负数

(1)irq_handler_t

中断处理函数的数据类型

typedef irqreturn_t (*irq_handler_t)(int, void *);

参数1:中断处理函数对应的中断号
参数2:通用指针,需要与request_irq函数的dev参数保持一致

返回值详见下

irqreturn_t
enum irqreturn {
	// 不是本驱动程序的中断,不处理
    IRQ_NONE                = (0 << 0),
    // 正常处理
    IRQ_HANDLED             = (1 << 0),
    // 不使用中断处理函数来处理,使用中断下半部处理
    IRQ_WAKE_THREAD         = (1 << 1),
};

typedef enum irqreturn irqreturn_t;
(2)flags

include/linux/interrupt.h
描述中断的特征
标志, bit[3:0]表示中断触发类型,为 1 的时候表示上升沿触发,为 2 的时候表示下降沿触发,为 4 的时候表示高电平触发,为 8 的时候表示低电平触发。 bit[15:8]为 PPI 中断的 CPU 掩码。

// 共享中断,可以有多个外设共同使用这个中断
#define IRQF_SHARED		0x00000080
// 此中断只出发一次
#define IRQF_ONESHOT		0x00002000
#define IRQF_TRIGGER_NONE	0x00000000
// 下面四个描述的就是中断的触发方式
#define IRQF_TRIGGER_RISING	0x00000001
#define IRQF_TRIGGER_FALLING	0x00000002
#define IRQF_TRIGGER_HIGH	0x00000004
#define IRQF_TRIGGER_LOW	0x00000008

2、free_irq()函数

释放中断

include/linux/interrupt.h

const void *free_irq(unsigned int irq, void *dev_id)
  • 参数:
    irq:要释放的中断号
    dev:传递给中断处理函数的第二个参数。如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。

3、enable_irq()函数

使能中断

kernel/irq/manage.c

void enable_irq(unsigned int irq)
  • 参数:
    irq:要使能的中断号

4、disable_irq()函数

禁止中断,但是要等待中断执行完毕再禁止

kernel/irq/manage.c

void disable_irq(unsigned int irq)
  • 参数:
    irq:要禁止的中断号

5、disable_irq_nosync()函数

立即禁止中断,不等待中断执行完

kernel/irq/manage.c

void disable_irq_nosync(unsigned int irq)
  • 参数:
    irq:要禁止的中断号

6、local_irq_disable()宏

include/linux/irqflags.h

禁止处理器中断,就是禁止全部中断

#define local_irq_disable()	do { raw_local_irq_disable(); } while (0)

7、local_irq_enable()宏

include/linux/irqflags.h

打开处理器中断,就是使能所有中断

#define local_irq_enable()	do { raw_local_irq_enable(); } while (0)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【摘要】本文详解了 Linux 内核的中断实现机制。首先介绍了中断的一些基本概念,然后分 析了面向对象的 Linux 中断的组织形式、三种主要数据结构及其之间的关系。随后介绍了 Linux 处理异常和中断的基本流程, 在此基础上分析了中断处理的详细流程, 包括保存现场、 中断处理、中断退出时的软中断执行及中断返回时的进程切换等问题。最后介绍了中断相关 的 API,包括中断注册和释放、中断关闭和使能、如何编写中断 ISR、共享中断中断上下 文中断状态等。 【关键字】中断,异常,hw_interrupt_type,irq_desc_t,irqaction,asm_do_IRQ,软中断, 进程切换,中断注册释放 request_irq,free_irq,共享中断,可重入,中断上下文 1 中断概述 1.1 为什么需要中断? 处理器的速度跟外围硬件设备的速度往往不在一个数量级上,因此,如果内核采取让处理器 向硬件发出一个请求,然后专门等待回应的办法,显然差强人意。既然硬件的响应这么慢, 那么内核就应该在此期间处理其他事务,等到硬件真正完成了请求的操作之后,再回过头来 对它进行处理。想要实现这种功能,轮询(polling)可能会是一种解决办法。可以让内核定期 对设备的状态进行查询, 然后做出相应的处理。 不过这种方法很可能会让内核做不少无用功, 因为无论硬件设备是正在忙碌着完成任务还是已经大功告成,轮询总会周期性地重复执行。 更好的办法是由我们来提供一种机制,让硬件在需要的时候再向内核发出信号(变内核主动 为硬件主动)。这就是中断机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值