Fork Me on GitHub

源码开放学ARM

LASO - Learn ARM with Source Open

首页         目录索引         资料下载         代码下载         联系作者        
下载PDF打印版本

中断相关数据结构

Linux中断数据结构

上面我们已经讲述了 SoC 中断控制器可以支持多个设备。当一个设备产生中断后,CPU进入中断状态,Linux处理流程如下:

保护现场;
通过中断控制寄存器获取产生中断的设备;
找到设备的处理程序;
调用设备处理程序;
返回中断

那么我们就需要有一个表来存放设备的中断处理程序。这个表在Linux里叫做irq_desc

每个IRQ中断线,Linux都用一个irq_desc_t数据结构来描述,我们把它叫做 IRQ描述符, NR_IRQS 个IRQ形成一个全局数组 irq_desc,其定义在 include/linux/irq.h 中:

数据结构irq_desc里有一项叫做 irqaction。它里面就包含着 handler() 函数。

irq_desc_t 的 action 成员实际是一个 irqaction 结构的链表,因为多个设备可能共享一个中断源, 每个设备都必须提供一个 irqction 结构挂入 action 链表。

内核中的定义:

/**
 * struct irq_desc - interrupt descriptor
 *
 * @handle_irq:		highlevel irq-events handler [if NULL, __do_IRQ()]
 * @chip:		low level interrupt hardware access
 * @msi_desc:		MSI descriptor
 * @handler_data:	per-IRQ data for the irq_chip methods
 * @chip_data:		platform-specific per-chip private data for the chip
 *			methods, to allow shared chip implementations
 * @action:		the irq action chain
 * @status:		status information
 * @depth:		disable-depth, for nested irq_disable() calls
 * @wake_depth:		enable depth, for multiple set_irq_wake() callers
 * @irq_count:		stats field to detect stalled irqs
 * @irqs_unhandled:	stats field for spurious unhandled interrupts
 * @last_unhandled:	aging timer for unhandled count
 * @lock:		locking for SMP
 * @affinity:		IRQ affinity on SMP
 * @cpu:		cpu index useful for balancing
 * @pending_mask:	pending rebalanced interrupts
 * @dir:		/proc/irq/ procfs entry
 * @affinity_entry:	/proc/irq/smp_affinity procfs entry on SMP
 * @name:		flow handler name for /proc/interrupts output
 */
struct irq_desc {
	irq_flow_handler_t	handle_irq;
	struct irq_chip		*chip;
	struct msi_desc		*msi_desc;
	void			*handler_data;
	void			*chip_data;
	struct irqaction	*action;	/* IRQ action list */
	unsigned int		status;		/* IRQ status */

	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned int		irqs_unhandled;
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	spinlock_t		lock;
#ifdef CONFIG_SMP
	cpumask_t		affinity;
	unsigned int		cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
	cpumask_t		pending_mask;
#endif
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;
#endif
	const char		*name;
} ____cacheline_internodealigned_in_smp;

extern struct irq_desc irq_desc[NR_IRQS];

/*
 * Migration helpers for obsolete names, they will go away:
 */
#define hw_interrupt_type	irq_chip
typedef struct irq_chip		hw_irq_controller;
#define no_irq_type		no_irq_chip
typedef struct irq_desc		irq_desc_t;

hw_interrupt_type 中断控制器相关的操作。

此结构体用来描述中断控制器,它是一个抽象的中断控制器,其成员是一系列指向函数的指针。
typename:给相应的控制器起一个便于理解的名字。
startup:允许从给定的控制器的IRQ所产生的事件。(基本上与enable相同)
shutdown:禁止从给定的控制器的IRQ所产生的事件。(基本上与disable相同)

struct irqaction 结构记录当中断发生时具体的处理函数,定义在 include/linux/interrupt.h 文件中。

struct irqaction {
	irq_handler_t           handler;		/* 具体的处理函数 */
	void                    *dev_id;
	void __percpu           *percpu_dev_id;
	struct irqaction        *next;			/* 利用该成员形成链表 */
	irq_handler_t           thread_fn;
	struct task_struct      *thread;
	unsigned int            irq;
	unsigned int            flags;
	unsigned long           thread_flags;
	unsigned long           thread_mask;
	const char              *name;
	struct proc_dir_entry   *dir;
};

其中 irqreturn_t 是一个函数指针类型,通过 typedef 来进行了新类型声明。 typedef irqreturn_t (irq_handler_t)(int, void);

irqreturn_t 是一个枚举类型,定义在 include/linux/irqreturn.h 中。正常中断处理返回 IRQ_HANDLED 。

/**
* enum irqreturn
* @IRQ_NONE            interrupt was not from this device
* @IRQ_HANDLED         interrupt was handled by this device
* @IRQ_WAKE_THREAD     handler requests to wake the handler thread
*/
enum irqreturn {
	IRQ_NONE                = (0 << 0),
	IRQ_HANDLED             = (1 << 0),
	IRQ_WAKE_THREAD         = (1 << 1),
};

typedef enum irqreturn irqreturn_t;

中断号 irq 的确定

查看 /proc/interrupt 文件

$ cat /proc/interrupt | grep uart

查看内核源码定义

$ grep -rns "IRQ" arch/arm/* | grep "UART" | grep "16"

$ vi arch/arm/plat-s5p/include/plat/irqs.h 

我们可以获取如下信息:

/* UART interrupts, each UART has 4 intterupts per channel so
 * use the space between the ISA and S3C main interrupts. Note, these
 * are not in the same order as the S3C24XX series! */

#define IRQ_S5P_UART_BASE0	(16)
#define IRQ_S5P_UART_BASE1	(20)
#define IRQ_S5P_UART_BASE2	(24)
#define IRQ_S5P_UART_BASE3	(28)

#define UART_IRQ_RXD		(0)
#define UART_IRQ_ERR		(1)
#define UART_IRQ_TXD		(2)

#define IRQ_S5P_UART_RX0	(IRQ_S5P_UART_BASE0 + UART_IRQ_RXD)
#define IRQ_S5P_UART_TX0	(IRQ_S5P_UART_BASE0 + UART_IRQ_TXD)
#define IRQ_S5P_UART_ERR0	(IRQ_S5P_UART_BASE0 + UART_IRQ_ERR)

#define IRQ_S5P_UART_RX1	(IRQ_S5P_UART_BASE1 + UART_IRQ_RXD)
#define IRQ_S5P_UART_TX1	(IRQ_S5P_UART_BASE1 + UART_IRQ_TXD)
#define IRQ_S5P_UART_ERR1	(IRQ_S5P_UART_BASE1 + UART_IRQ_ERR)

#define IRQ_S5P_UART_RX2	(IRQ_S5P_UART_BASE2 + UART_IRQ_RXD)

...
	
	
#define IRQ_EINT(x)		((x) < 16 ? ((x) + S5P_EINT_BASE1) \
					: ((x) - 16 + S5P_EINT_BASE2))

#define EINT_OFFSET(irq)	((irq) < S5P_EINT_BASE2 ? \
						((irq) - S5P_EINT_BASE1) : \
						((irq) + 16 - S5P_EINT_BASE2))

#define IRQ_EINT_BIT(x)		EINT_OFFSET(x)

上一节 | 目录索引 | 下一节

blog comments powered by Disqus