上一节我们学习了在linux中,中断的初始化流程以及调用流程。
本节我们对中断的框架进行说明。
开始之前我们先把前面说过的一个数据结构拿出来分析一下。
1.中断描述符
/**
* struct irq_desc - interrupt descriptor
* @irq_data: per irq and chip data passed down to chip functions
* @kstat_irqs: irq stats per cpu
* @handle_irq: highlevel irq-events handler
* @preflow_handler: handler called before the flow handler (currently used by sparc)
* @action: the irq action chain
* @status: status information
* @core_internal_state__do_not_mess_with_it: core internal status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @threads_handled: stats field for deferred spurious detection of threaded handlers
* @threads_handled_last: comparator field for deferred spurious detection of theraded handlers
* @lock: locking for SMP
* @affinity_hint: hint to user space for preferred irq affinity
* @affinity_notify: context for notification of affinity changes
* @pending_mask: pending rebalanced interrupts
* @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc { /* 中断描述符结构体 */
struct irq_data irq_data; /* 每个irq和芯片数据传递给芯片功能 */
unsigned int __percpu *kstat_irqs; /* 每个cpu的irq stats */
irq_flow_handler_t handle_irq; /* 高级irq-events处理程序 */
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list irq动作链表 */
unsigned int status_use_accessors; /* 状态信息 */
unsigned int core_internal_state__do_not_mess_with_it; /* 核心内部状态信息 */
unsigned int depth; /* nested irq disables 用于嵌套的irq_disable() 调用*/
unsigned int wake_depth; /* nested wake enables 为多个irq_set_irq_wake()呼叫者启用深度*/
unsigned int irq_count; /* For detecting broken IRQs stats字段用于检测停滞的irqs*/
unsigned long last_unhandled; /* Aging timer for unhandled count 未处理计数的老化计时器*/
unsigned int irqs_unhandled; /* 虚假未处理中断的统计字段 */
atomic_t threads_handled; /* 用于线程处理程序的延迟虚假检测的stats字段 */
int threads_handled_last; /* 比较器字段,用于对已处理的处理程序进行延迟虚假检测 */
raw_spinlock_t lock; /* 锁定SMP */
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint; /* 提示用户空间以获得首选的irq亲和力 */
struct irq_affinity_notify *affinity_notify; /* 用于通知亲和力变化的上下文 */
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask; /* 待定重新平衡的中断 */
#endif
#endif
unsigned long threads_oneshot; /* 用于处理共享的onehot线程的bitfield */
atomic_t threads_active; /* 当前正在运行的irqaction线程数 */
wait_queue_head_t wait_for_threads; /* sync_irq的等待队列等待线程处理程序 */
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir; /* / proc / irq / procfs条目 */
#endif
int parent_irq;
struct module *owner;
const char *name; /* / proc / interrupts输出的流程处理程序名称 */
} ____cacheline_internodealigned_in_smp;
#ifndef CONFIG_SPARSE_IRQ
extern struct irq_desc irq_desc[NR_IRQS]; /* 声明了NR_IRQS个irq描述符表 */
#endif
2.irq_data
/**
* struct irq_data - per irq and irq chip data passed down to chip functions
* @mask: precomputed bitmask for accessing the chip registers
* @irq: interrupt number
* @hwirq: hardware interrupt number, local to the interrupt domain
* @node: node index useful for balancing
* @state_use_accessors: status information for irq chip functions.
* Use accessor functions to deal with it
* @chip: low level interrupt hardware access
* @domain: Interrupt translation domain; responsible for mapping
* between hwirq number and linux irq number.
* @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
* @msi_desc: MSI descriptor
* @affinity: IRQ affinity on SMP
*
* The fields here need to overlay the ones in irq_desc until we
* cleaned up the direct references and switched everything over to
* irq_data.
*/
struct irq_data {
u32 mask; /* 预先计算的位掩码,用于访问芯片寄存器 */
unsigned int irq; /* 中断号 */
unsigned long hwirq; /* 硬件中断号,中断域本地 */
unsigned int node; /* 用于平衡的节点索引 */
unsigned int state_use_accessors; /* irq芯片功能的状态信息。 */
struct irq_chip *chip; /* 低级中断硬件访问 */
struct irq_domain *domain; /* 中断翻译域; 负责制图 */
void *handler_data; /* irq_chip方法的per-IRQ数据 */
void *chip_data; /* 芯片的平台特定的每芯片私有数据方法,允许共享芯片实现 */
struct msi_desc *msi_desc; /* MSI描述符 */
cpumask_var_t affinity; /* SMP上的IRQ亲和力 */
};
3.handle_irq中断处理函数,定义的是一个函数指针形式
typedef void (*irq_flow_handler_t)(unsigned int irq,
struct irq_desc *desc);
4.irqaction ,真正的中断的动作行为
/* 函数指针,通常是我们要实现的 */
typedef irqreturn_t (*irq_handler_t)(int, void *);
/**
* struct irqaction - per interrupt action descriptor
* @handler: interrupt handler function
* @name: name of the device
* @dev_id: cookie to identify the device
* @percpu_dev_id: cookie to identify the device
* @next: pointer to the next irqaction for shared interrupts
* @irq: interrupt number
* @flags: flags (see IRQF_* above)
* @thread_fn: interrupt handler function for threaded interrupts
* @thread: thread pointer for threaded interrupts
* @thread_flags: flags related to @thread
* @thread_mask: bitmask for keeping track of @thread activity
* @dir: pointer to the proc/irq/NN/name entry
*/
struct irqaction { /* 每个中断动作描述符 */
irq_handler_t handler; /* 中断处理函数 */
void *dev_id; /* 用于识别设备的cookie */
void __percpu *percpu_dev_id; /* 用于识别设备的cookie */
struct irqaction *next; /* 指向共享中断的下一个irqaction的指针 */
irq_handler_t thread_fn; /* 用于线程中断的中断处理函数 */
struct task_struct *thread; /* 线程中断的线程指针 */
unsigned int irq; /* 中断号码 */
unsigned int flags; /* flags(参见下面的IRQF_ *) */
unsigned long thread_flags; /* 与@thread相关的标志 */
unsigned long thread_mask; /* 用于跟踪@thread活动的位掩码 */
const char *name; /* 设备名称 */
struct proc_dir_entry *dir; /* 指向proc / irq / NN / name条目的指针 */
} ____cacheline_internodealigned_in_smp;
5.中断标志位
/* 下面几个是io相关的中断
* These correspond to the IORESOURCE_IRQ_* defines in
* linux/ioport.h to select the interrupt line behaviour. When
* requesting an interrupt without specifying a IRQF_TRIGGER, the
* setting should be assumed to be "as already configured", which
* may be as per machine or firmware initialisation.
*/
#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 /* 低电平中断 */
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler.
* DEPRECATED. This flag is a NOOP and scheduled to be removed
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for
* performance reasons)
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
* IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
* IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
* IRQF_NO_THREAD - Interrupt cannot be threaded
* IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
* resume time.
*/
#define IRQF_DISABLED 0x00000020 /* 调用动作处理程序时禁用irqs,已弃用。此标志是NOOP并计划删除 */
#define IRQF_SHARED 0x00000080 /* 允许在多个设备之间共享irq */
#define IRQF_PROBE_SHARED 0x00000100 /* 当呼叫者期望发生共享不匹配时由呼叫者设置 */
#define __IRQF_TIMER 0x00000200 /* 将此中断标记为定时器中断的标志 */
#define IRQF_PERCPU 0x00000400 /* 中断是每个CPU */
#define IRQF_NOBALANCING 0x00000800 /* 用于从irq平衡中排除此中断的标志 */
#define IRQF_IRQPOLL 0x00001000 /* 中断用于轮询(仅限中断)考虑在共享中断中首先注册 */
#define IRQF_ONESHOT 0x00002000 /* 执行一次,在hardirq处理程序完成后,不会重新启用中断 */
#define IRQF_NO_SUSPEND 0x00004000 /* 暂停期间不要禁用此IRQ */
#define IRQF_FORCE_RESUME 0x00008000 /* 即使设置了IRQF_NO_SUSPEND,强制在恢复时启用它 */
#define IRQF_NO_THREAD 0x00010000 /* 中断无法进行线程化 */
#define IRQF_EARLY_RESUME 0x00020000 /* 在syscore而不是在设备期间提前恢复IRQ */
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)
6.chip
/**
* struct irq_chip - hardware interrupt chip descriptor
*
* @name: name for /proc/interrupts
* @irq_startup: start up the interrupt (defaults to ->enable if NULL)
* @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
* @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
* @irq_disable: disable the interrupt
* @irq_ack: start of a new interrupt
* @irq_mask: mask an interrupt source
* @irq_mask_ack: ack and mask an interrupt source
* @irq_unmask: unmask an interrupt source
* @irq_eoi: end of interrupt
* @irq_set_affinity: set the CPU affinity on SMP machines
* @irq_retrigger: resend an IRQ to the CPU
* @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
* @irq_set_wake: enable/disable power-management wake-on of an IRQ
* @irq_bus_lock: function to lock access to slow bus (i2c) chips
* @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
* @irq_cpu_online: configure an interrupt source for a secondary CPU
* @irq_cpu_offline: un-configure an interrupt source for a secondary CPU
* @irq_suspend: function called from core code on suspend once per chip
* @irq_resume: function called from core code on resume once per chip
* @irq_pm_shutdown: function called from core code on shutdown once per chip
* @irq_calc_mask: Optional function to set irq_data.mask for special cases
* @irq_print_chip: optional to print special chip info in show_interrupts
* @irq_request_resources: optional to request resources before calling
* any other callback related to this irq
* @irq_release_resources: optional to release resources acquired with
* irq_request_resources
* @flags: chip specific flags
*/
struct irq_chip { /* 硬件中断芯片描述符 */
const char *name; /* / proc / interrupts的名称 */
unsigned int (*irq_startup)(struct irq_data *data); /* 启动中断(默认为如果为NULL则启用) */
void (*irq_shutdown)(struct irq_data *data); /* 关闭中断(默认为 - >禁用,如果为NULL) */
void (*irq_enable)(struct irq_data *data); /* 启用中断(如果为NULL,则默认为chip-> unmask) */
void (*irq_disable)(struct irq_data *data); /* 禁用中断 */
void (*irq_ack)(struct irq_data *data); /* 开始新的中断 */
void (*irq_mask)(struct irq_data *data); /* 屏蔽中断源 */
void (*irq_mask_ack)(struct irq_data *data); /* 确认并屏蔽中断源 */
void (*irq_unmask)(struct irq_data *data); /* 取消屏蔽中断源 */
void (*irq_eoi)(struct irq_data *data); /* 中断结束 */
int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force); /* 在SMP机器上设置CPU亲和力 */
int (*irq_retrigger)(struct irq_data *data); /* 向CPU重新发送IRQ */
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type); /* 设置IRQ的流类型(IRQ_TYPE_LEVEL / etc.) */
int (*irq_set_wake)(struct irq_data *data, unsigned int on); /* 启用/禁用IRQ的电源管理唤醒 */
void (*irq_bus_lock)(struct irq_data *data); /* 用于锁定对慢速总线(i2c)芯片的访问的功能 */
void (*irq_bus_sync_unlock)(struct irq_data *data); /* 用于同步和解锁慢速总线(i2c)芯片的功能 */
void (*irq_cpu_online)(struct irq_data *data); /* 为辅助CPU配置中断源 */
void (*irq_cpu_offline)(struct irq_data *data); /* 取消配置辅助CPU的中断源 */
void (*irq_suspend)(struct irq_data *data); /* 每个芯片暂停一次从核心代码调用的函数 */
void (*irq_resume)(struct irq_data *data); /* 每个芯片在恢复时从核心代码调用的函数 */
void (*irq_pm_shutdown)(struct irq_data *data); /* 每个芯片在关闭一次时从核心代码调用的函数 */
void (*irq_calc_mask)(struct irq_data *data); /* 为特殊情况设置irq_data.mask的可选功能 */
void (*irq_print_chip)(struct irq_data *data, struct seq_file *p); /* 可选择在show_interrupts中打印特殊芯片信息 */
int (*irq_request_resources)(struct irq_data *data); /* 在调用之前请求资源的可选项与此irq相关的任何其他回调 */
void (*irq_release_resources)(struct irq_data *data); /* 可选用于释放使用的资源 */
unsigned long flags; /* 芯片特定标志 */
};
通常我们所说的中断分为两种,一种是内部中断(定时器,串口,I2C等),另一种是外部中断(GPIO+触发类型)
一般来说,内部中断是有固定的独立的中断号的。
而外部中断,因为数量比较多,每一个单独占用一个的话是特别浪费中断资源的,所以,外部中断通常会设计一些是有单独中断号的(对响应速度要求比较高),一些是多个io端口共享一个中断号。
首先看一下我们的S5PV210有多少个物理中断。
包括空闲的等也就一百零6个物理中断
接下来看一下内核中给我们分配了多少个。
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
/* Set the default NR_IRQS中断数量 */
#define NR_IRQS (IRQ_EINT(31) + S5P_GPIOINT_COUNT + 1) /* IRQ_EINT(31) + 33 = 175 + 33 = 208 */
/**********************把31带入IRQ_EINT(x)*************************************/
#define S5P_GPIOINT_GROUP_COUNT 4
#define S5P_GPIOINT_GROUP_SIZE 8
#define S5P_GPIOINT_COUNT (S5P_GPIOINT_GROUP_COUNT * S5P_GPIOINT_GROUP_SIZE) /* 4*8 */
#define IRQ_EINT(x) ((x) < 16 ? ((x) + S5P_EINT_BASE1) \
: ((x) - 16 + S5P_EINT_BASE2)) /* 15 + S5P_EINT_BASE2 = 160 + 15 = 175 */
#define S5P_EINT_BASE1 (S5P_IRQ_VIC0(0))
#define S5P_EINT_BASE2 (IRQ_VIC_END + 1) /* 159 + 1 = 160*/
#define IRQ_VIC_END S5P_IRQ_VIC3(31) /* 128 + 31 */
#define S5P_IRQ_VIC3(x) (S5P_VIC3_BASE + (x)) /* 128 + x */
#define S5P_VIC3_BASE S5P_IRQ(96)
#define S5P_IRQ_OFFSET (32)
#define S5P_IRQ(x) ((x) + S5P_IRQ_OFFSET) /* 32 + 96 */
/* Set the default NR_IRQS */
#define NR_IRQS (IRQ_EINT(31) + S5P_GPIOINT_COUNT + 1) /* 208 */
经过计算,总共208个,查看启动信息,也是208个,说明我们的计算没错。
为什么这么多呢?
多在那里了呢?仔细看一下上面的定义,可以发现。
#define S5P_IRQ_OFFSET (32)
最开始预留了32个中断。
/* Typically only a few gpio chips require gpio interrupt support.
To avoid memory waste irq descriptors are allocated only for
S5P_GPIOINT_GROUP_COUNT chips, each with total number of
S5P_GPIOINT_GROUP_SIZE pins/irqs. Each GPIOINT group can be assiged
to any gpio chip with the s5p_register_gpio_interrupt() function */
#define S5P_GPIOINT_GROUP_COUNT 4
#define S5P_GPIOINT_GROUP_SIZE 8
#define S5P_GPIOINT_COUNT (S5P_GPIOINT_GROUP_COUNT * S5P_GPIOINT_GROUP_SIZE)
/* Set the default NR_IRQS */
#define NR_IRQS (IRQ_EINT(31) + S5P_GPIOINT_COUNT + 1)
这里又为gpiolib预留了32个中断。
上面注释写的很明白,使用这32个中断号的,用下面这个函数注册就可以,
int __init s5p_register_gpio_interrupt(int pin)
这个属于gpiolib库中的一部分内容。
那么208 - 128 - 64 = 16,剩下的用给那些了呢
再仔细看一下上面的
#define IRQ_VIC_END S5P_IRQ_VIC3(31) /* 最后一个物理中断127 */
#define S5P_EINT_BASE1 (S5P_IRQ_VIC0(0)) /* 物理中断0 */
#define S5P_EINT_BASE2 (IRQ_VIC_END + 1) /* 物理中断128(硬件实现了寄存器,但没有这个中断) */
#define IRQ_EINT(x) ((x) < 16 ? ((x) + S5P_EINT_BASE1) \
: ((x) - 16 + S5P_EINT_BASE2))
可以发现如果外部中断大于等于16,则会把这个中断安置在所有物理中断紧接着的后面位置
先分析一下外部中断,后面分析内部中断。
1.s5p_init_irq_eint(外部中断三星把其实现为架构相关的初始化时调用)
/* 外部中断0-15属于vic,他们每个是独立的chip */
static struct irq_chip s5p_irq_vic_eint = {
.name = "s5p_vic_eint",
.irq_mask = s5p_irq_vic_eint_mask,
.irq_unmask = s5p_irq_vic_eint_unmask,
.irq_mask_ack = s5p_irq_vic_eint_maskack,
.irq_ack = s5p_irq_vic_eint_ack,
.irq_set_type = s5p_irq_eint_set_type,
#ifdef CONFIG_PM
.irq_set_wake = s3c_irqext_wake,
#endif
};
static struct irq_chip s5p_irq_eint = {
.name = "s5p-eint",
.irq_mask = s5p_irq_eint_mask,
.irq_unmask = s5p_irq_eint_unmask,
.irq_mask_ack = s5p_irq_eint_maskack,
.irq_ack = s5p_irq_eint_ack,
.irq_set_type = s5p_irq_eint_set_type,
#ifdef CONFIG_PM
.irq_set_wake = s3c_irqext_wake,
#endif
};
static int __init s5p_init_irq_eint(void)
{
int irq;
if (of_have_populated_dt()) /* 我们没用device tree默认直接返回false */
return -ENODEV;
/* 设置前15个独立的外部中断的chip */
for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
irq_set_chip(irq, &s5p_irq_vic_eint);
/* 设置16-31联合中断的chip和handle,并设置好高级处理程序(这里是设置在128号实际存在在中断后面的,
系统并不会响应这个,必须要真正的16-31联合的中断发生后,根据中断状态,计算出在linux中断里的中断号,即在irq_desc数组中的下标) */
for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}
/* 外部中断16_31只有一个中断号,所以要设置成多路中断函数(即在这个函数中再分辨是那个中断源) */
irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
return 0;
}
arch_initcall(s5p_init_irq_eint); /* 在架构相关的初始化时调用 */
上面的注释写的很明白,在irq_desc所有中断的数组中填充我们的外部中断的32个中断。
其中0-16共17个属于硬件中就存在的中断,中断发生后,就会执行。(其中16号属于16~31的联合)
而16~31号中断虽然在irq_desc数组里,但需要通过先调用注册到IRQ_EINT16_31号里面的s5p_irq_demux_eint16_31函数找到再执行。
irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
仔细分析过上面的可以知道,小于16号的外部中断是单独的,大于等于16号的是放在irq_desc数组后面位置。
for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
set_irq_flags(irq, IRQF_VALID);
}
从s5p_init_irq_eint函数分析,真正的16_31的联合中断,没有设置chip。而系统单独分开做的的16~31的16个外部中断每个都设置了chip.
可以看到IRQ_EINT16_31是真正的16号中断
下面我们看一下代码
1.设置irq_handler
/* 先看这句是怎么把s5p_irq_demux_eint16_31注册到 */
irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
static inline void
irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
{
__irq_set_handler(irq, handle, 1, NULL); /* 设置handle */
}
void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0); /* 根据irq的num返回irq_desc[irq] */
if (!desc) /* 错误判断 */
return;
if (!handle) { /* 错误判断 */
handle = handle_bad_irq;
} else {
if (WARN_ON(desc->irq_data.chip == &no_irq_chip))
goto out;
}
/* Uninstall? */
if (handle == handle_bad_irq) {
if (desc->irq_data.chip != &no_irq_chip)
mask_ack_irq(desc);
irq_state_set_disabled(desc);
desc->depth = 1;
}
desc->handle_irq = handle; /* 添加s5p_irq_demux_eint16_31到16-31外部中断里 */
desc->name = name;
if (handle != handle_bad_irq && is_chained) {
irq_settings_set_noprobe(desc);
irq_settings_set_norequest(desc);
irq_settings_set_nothread(desc);
irq_startup(desc, true);
}
out:
irq_put_desc_busunlock(desc, flags);
}
2.外部中断15~31的处理函数
/* 注意大于等于16,就已经是在irq_desc数组后面位置了 */
#define IRQ_EINT(x) ((x) < 16 ? ((x) + S5P_EINT_BASE1) \
: ((x) - 16 + S5P_EINT_BASE2))
static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
{
s5p_irq_demux_eint(IRQ_EINT(16)); /* 从16~23共8个 */
s5p_irq_demux_eint(IRQ_EINT(24)); /* 从24~31共8个 */
}
/* s5p_irq_demux_eint
*
* This function demuxes the IRQ from the group0 external interrupts,
* from EINTs 16 to 31. It is designed to be inlined into the specific
* handler s5p_irq_demux_eintX_Y.
*
* Each EINT pend/mask registers handle eight of them.
*/
static inline void s5p_irq_demux_eint(unsigned int start)
{
u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start))); /* 读中断挂起寄存器 */
u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start))); /* 读屏蔽寄存器 */
unsigned int irq;
status &= ~mask;
status &= 0xff;
/* 如果没有屏蔽中断,则执行当前所有中断位挂起的中断函数 */
while (status) {
irq = fls(status) - 1; /* 找到从bit0到bit7,返回输入参数的最高有效bit位(从低位往左数最后的有效bit位)的序号 */
generic_handle_irq(irq + start); /* IRQ_EINT(x) + irq = 真正的中断号,传入,执行中断处理函数 */
status &= ~(1 << irq); /* 清除已经执行完的中断标记(可能有多个外部中断,所以每次都是清除已经执行了中断处理程序的那一个) */
}
}
/**
* fls - find last (most-significant) bit set
* @x: the word to search
*
* This is defined the same way as ffs.
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
*/
/* 可以看到上面函数的注释0x80000000,的第31bit是1,所以是32
* 我们如果是0x24,则 fls(0x42) = 3,因为2是低端第一个1所在的bit
static __always_inline int fls(int x);
中断处理程序我们上一篇文章已经分析过了
struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < NR_IRQS) ? irq_desc + irq : NULL; /* 根据中断号,找到数组项 */
}
int generic_handle_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
generic_handle_irq_desc(irq, desc); /* 根据数组项,执行函数 */
return 0;
}
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc); /* 执行数组项里面注册填充的函数 */
}
关于我的下面这个函数为什么要调用两次,传入不同的中断号
static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
{
s5p_irq_demux_eint(IRQ_EINT(16));
s5p_irq_demux_eint(IRQ_EINT(24));
}
是因为我们的中断挂起寄存器和中断屏蔽寄存器,每个寄存器都是存放8个中断的,而16~31总共有16个,在两个寄存器中存放挂起和屏蔽标志位,所以要分两次执行。
上面把外部中断全部分析了,接下来分析一下内部的中断。
2.内部中断。
上一篇文章我我们分析了中断的初始化过程。其中最重要的是初始化了irq_desc数组,以及调用了我们三星的具体处理器实现的中断的初始化。
其中初始化中最终要的就是把我们的中断处理程序,放在了handle_arch_irq标号的位置。
其实register_vic里面还做了很多其他的事情。
2.1
/* CONFIG_ARM_VIC_NR = 4 */
static struct vic_device vic_devices[CONFIG_ARM_VIC_NR];
/**
* vic_register() - Register a VIC.
* @base: The base address of the VIC.
* @parent_irq: The parent IRQ if cascaded, else 0.
* @irq: The base IRQ for the VIC.
* @valid_sources: bitmask of valid interrupts
* @resume_sources: bitmask of interrupts allowed for resume sources.
* @node: The device tree node associated with the VIC.
*
* Register the VIC with the system device tree so that it can be notified
* of suspend and resume requests and ensure that the correct actions are
* taken to re-instate the settings on resume.
*
* This also configures the IRQ domain for the VIC.
*/
static void __init vic_register(void __iomem *base, unsigned int parent_irq,
unsigned int irq,
u32 valid_sources, u32 resume_sources,
struct device_node *node)
{
struct vic_device *v;
int i;
if (vic_id >= ARRAY_SIZE(vic_devices)) { /* 最多有4组 */
printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__);
return;
}
/* 注册vic各种信息到vic_devices结构体数组 */
v = &vic_devices[vic_id];
v->base = base;
v->valid_sources = valid_sources;
v->resume_sources = resume_sources;
set_handle_irq(vic_handle_irq); /* 这句就是我们关注的,设置中断函数执行的代码 */
vic_id++;
if (parent_irq) { /* parent_irq = 0 */
irq_set_handler_data(parent_irq, v);
irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded);
}
/* 这里我们注册了每个irq的中断domain */
v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
&vic_irqdomain_ops, v);
/* create an IRQ mapping for each valid IRQ */
for (i = 0; i < fls(valid_sources); i++)
if (valid_sources & (1 << i))
irq_create_mapping(v->domain, i);
/* If no base IRQ was passed, figure out our allocated base */
if (irq) /* 在我们没设置irq的情况下,系统会初始化成默认的,否则要是中断了,可能会跑飞掉 */
v->irq = irq;
else
v->irq = irq_find_mapping(v->domain, 0);
}
这个是irq_domain_add_simple的最后一个参数
static struct irq_domain_ops vic_irqdomain_ops = {
.map = vic_irqdomain_map,
.xlate = irq_domain_xlate_onetwocell,
};
2.2、增加domain信息
/**
* irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs
* @of_node: pointer to interrupt controller's device tree node.
* @size: total number of irqs in mapping
* @first_irq: first number of irq block assigned to the domain,
* pass zero to assign irqs on-the-fly. If first_irq is non-zero, then
* pre-map all of the irqs in the domain to virqs starting at first_irq.
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
* Allocates an irq_domain, and optionally if first_irq is positive then also
* allocate irq_descs and map all of the hwirqs to virqs starting at first_irq.
*
* This is intended to implement the expected behaviour for most
* interrupt controllers. If device tree is used, then first_irq will be 0 and
* irqs get mapped dynamically on the fly. However, if the controller requires
* static virq assignments (non-DT boot) then it will set that up correctly.
*/
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
/* 分配和初始化和irq_domain结构 */
domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
if (!domain)
return NULL;
if (first_irq > 0) {
if (IS_ENABLED(CONFIG_SPARSE_IRQ)) { /* 我们没定义使用稀疏IRQ(利用基数树实现) */
/* attempt to allocated irq_descs */
int rc = irq_alloc_descs(first_irq, first_irq, size,
of_node_to_nid(of_node));
if (rc < 0)
pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
first_irq);
}
irq_domain_associate_many(domain, first_irq, 0, size); /* irq和domain关联(比较重要) */
}
return domain;
}
2.2.1、__irq_domain_add
/**
* __irq_domain_add() - Allocate a new irq_domain data structure
* @of_node: optional device-tree node of the interrupt controller
* @size: Size of linear map; 0 for radix mapping only
* @hwirq_max: Maximum number of interrupts supported by controller
* @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
* direct mapping
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
* Allocates and initialize and irq_domain structure.
* Returns pointer to IRQ domain, or NULL on failure.
*/
struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
/* 申请domian和基数树相关的空间 */
domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
GFP_KERNEL, of_node_to_nid(of_node));
if (WARN_ON(!domain))
return NULL;
/* Fill structure 初始化domain中的基数树*/
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
/* 初始化domian */
domain->ops = ops; /* 把我们定义的ops绑定到domain上 */
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list); /* 把这个新的domain加入irq_domain_list链表 */
mutex_unlock(&irq_domain_mutex);
pr_debug("Added domain %s\n", domain->name);
return domain;
}
2.2.2、irq_domain_associate_many,一次关联一组VIC,32个irq和domain
void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
irq_hw_number_t hwirq_base, int count)
{
int i;
pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
/* 我们的4个vic最初都是对0取反,所以count是32 ,即对每个vic的每个bit都要执行这个irq和domian的关联 */
for (i = 0; i < count; i++) {
irq_domain_associate(domain, irq_base + i, hwirq_base + i);
}
}
2.2.3。关联每一个irq和domian
int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq)
{
struct irq_data *irq_data = irq_get_irq_data(virq); /* 通过vic中的irq号,找到irq_desc数组,返回里面的irq_data */
int ret;
/* 错误检查 */
if (WARN(hwirq >= domain->hwirq_max,
"error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
return -EINVAL;
if (WARN(!irq_data, "error: virq%i is not allocated", virq))
return -EINVAL;
if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
return -EINVAL;
/* 初始化irq_data */
mutex_lock(&irq_domain_mutex);
irq_data->hwirq = hwirq;
irq_data->domain = domain;
if (domain->ops->map) {
ret = domain->ops->map(domain, virq, hwirq); /* 这一句很重要,后面分析 */
if (ret != 0) {
/*
* If map() returns -EPERM, this interrupt is protected
* by the firmware or some other service and shall not
* be mapped. Don't bother telling the user about it.
*/
if (ret != -EPERM) {
pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
domain->name, hwirq, virq, ret);
}
irq_data->domain = NULL;
irq_data->hwirq = 0;
mutex_unlock(&irq_domain_mutex);
return ret;
}
/* If not already assigned, give the domain the chip's name */
if (!domain->name && irq_data->chip)
domain->name = irq_data->chip->name;
}
/* 我们这里因为是静态一次分配好所有的中断,所以没有用基数树 */
if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = virq;
} else {
mutex_lock(&revmap_trees_mutex);
radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
mutex_unlock(&revmap_trees_mutex);
}
mutex_unlock(&irq_domain_mutex);
irq_clear_status_flags(virq, IRQ_NOREQUEST); /* 清中断 */
return 0;
}
2.2.4、初始化每个irq
static struct irq_domain_ops vic_irqdomain_ops = {
.map = vic_irqdomain_map,
.xlate = irq_domain_xlate_onetwocell,
};
这个实在前面就定义的,后来绑定到domian的ops上,在上面函数中执行
static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
struct vic_device *v = d->host_data;
/* Skip invalid IRQs, only register handlers for the real ones */
if (!(v->valid_sources & (1 << hwirq)))
return -EPERM;
irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq); /* 设置chip */
irq_set_chip_data(irq, v->base); /* 设置chip_data */
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); /* 设备flags */
return 0;
}
可以看到这里为每一个irq都绑定了相同的vic_chip,和handler_level_irqy以及chip_data
我分析一下这几个的作用。
首先vic_chip,比较代码中比较奇葩,定义了两遍,我以为我看错了。检查了好久,确实是定义了两遍。
static struct irq_chip vic_chip;
static struct irq_chip vic_chip = {
.name = "VIC",
.irq_ack = vic_ack_irq, /* 清中断 */
.irq_mask = vic_mask_irq, /* 屏蔽中断 */
.irq_unmask = vic_unmask_irq, /* 使能中断 */
.irq_set_wake = vic_set_wake, /* 使能恢复(之前可能挂起了) */
};
后来我自己按上面的形式也测试了一下。gcc居然可以自动优化掉,我以为会告重复定义。
这边举例看几个
static void vic_ack_irq(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
unsigned int irq = d->hwirq;
writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); /* clear enable就是清中断 */
/* moreover, clear the soft-triggered, in case it was the reason */
writel(1 << irq, base + VIC_INT_SOFT_CLEAR); /* 清软触发 */
}
static void vic_mask_irq(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
unsigned int irq = d->hwirq;
writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); /* 清中断(为什么不是屏蔽??) */
}
static void vic_unmask_irq(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
unsigned int irq = d->hwirq;
writel(1 << irq, base + VIC_INT_ENABLE); /* 使能中断 */
}
handle_level_irq每个中断注册的执行函数
/**
* handle_level_irq - Level type irq handler
* @irq: the interrupt number
* @desc: the interrupt description structure for this irq
*
* Level type interrupts are active as long as the hardware line has
* the active level. This may require to mask the interrupt and unmask
* it after the associated handler has acknowledged the device, so the
* interrupt line is back to inactive.
*/
void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
raw_spin_lock(&desc->lock); /* desc上锁 */
mask_ack_irq(desc); /* 清中断 */
if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
if (!irq_check_poll(desc))
goto out_unlock;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); /* 清重复和等待标志位 */
kstat_incr_irqs_this_cpu(irq, desc); /* 增加随机数状态 */
/*
* If its disabled or no action available
* keep it masked and get out of here 屏蔽irq或action无效则挂起
*/
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
desc->istate |= IRQS_PENDING;
goto out_unlock;
}
handle_irq_event(desc); /* 处理中断程序 */
cond_unmask_irq(desc);
out_unlock:
raw_spin_unlock(&desc->lock); /* desc解锁 */
}
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
struct irqaction *action = desc->action;
irqreturn_t ret;
desc->istate &= ~IRQS_PENDING;
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); /* 标记中断正在运行 */
raw_spin_unlock(&desc->lock); /* desc解锁 */
ret = handle_irq_event_percpu(desc, action); /* 中断处理 */
raw_spin_lock(&desc->lock); /* desc上锁 */
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); /* 清中断正在运行标记 */
return ret;
}
irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
irqreturn_t retval = IRQ_NONE;
unsigned int flags = 0, irq = desc->irq_data.irq; /* 得到中断号 */
do {
irqreturn_t res;
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id); /* 指行cation链表中的handler */
trace_irq_handler_exit(irq, action, res);
if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
irq, action->handler))
local_irq_disable();
switch (res) {
case IRQ_WAKE_THREAD:
/*
* Catch drivers which return WAKE_THREAD but 驱动程序返回弱线程但是
* did not set up a thread function 没有设置线程函数
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}
__irq_wake_thread(desc, action); /* 中断函数线程话 */
/* Fall through to add to randomness 通过添加到随机性*/
case IRQ_HANDLED:
flags |= action->flags;
break;
default:
break;
}
retval |= res;
action = action->next; /* 链表的下一个中断 */
} while (action);
add_interrupt_randomness(irq, flags); /* 增加系统的随机数种子(有多个肯定就是共享中断了) */
if (!noirqdebug)
note_interrupt(irq, desc, retval);
return retval;
}
现在中断框架部分基本就差不多了,现在总结一下一个中断从产生到执行的流程。
vector_irq /* 中断向量表 */
保存现场
__irq_usr/__irq_svc
usr_entry /* 保存现场 */
irq_handler
handle_arch_irq /* 直接调转到handle_arch_irq标号处,在初始化阶段设置的中断处理函数中 */
vic_handle_irq /* 我们是在vic_register_里面注册的到handle_arch_irq标号处的 */
handle_one_vic /* 我们有四个vic,要调用四次handle_one_vic */
readl_relaxed /* 读该vic的状态寄存器,如果不为0表示由中断,读出,检查并每次执行其中一个 */
handle_IRQ /* 执行handle_IRQ */
generic_handle_irq(irq) /* 执行通用的注册的中断 */
generic_handle_irq_desc(irq, desc) /* */
desc->handle_irq(irq, desc) /* 执行初始化阶段注册的通用的 */
handle_level_irq(unsigned int irq, struct irq_desc *desc) /* 这个就是初始化阶段为每个中断都绑定的 */
handle_irq_event(desc);
handle_irq_event_percpu(desc, action);
action->handler(irq, action->dev_id) /* 由驱动工程师实现的驱动函数 */
action = action->next; /* 共享中断要执行相同中断号的cation链表的所有中断 */
对比上一节分析的中断的初始化。可以发现基本系统已经都帮我们实现了。
我们要实现的就是最后一个的cation结构体就可以了。
当然这个的百分之九十的工作系统也已经帮我们做好了,提供了接口函数,我们只要调用相应函数就可以了。
申请并注册中断,内部实现基本差不多
extern int __must_check
request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn,
unsigned long flags, const char *name, void *dev);
static inline int __must_check
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);
}
extern int __must_check
request_any_context_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev_id);
extern int __must_check
request_percpu_irq(unsigned int irq, irq_handler_t handler,
const char *devname, void __percpu *percpu_dev_id);
我们以最常用的request_irq来分析
/* 申请并注册一个中断 */
static inline int __must_check
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);
}
/* 内部函数,默认thread_fn为0,即没有使用线程化 */
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). 共享中断必须设置dev_id
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq); /* 根据中断号,得到irq_desc数组下标 */
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) || /* 错误检验 */
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) { /* 非线程化和线程化必须要选一个,两个都有默认非线程化 */
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
/* 分配cation */
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
/* 填充cation结构 */
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); /* 把cation加入到desc的irq相的链表中去 */
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);
}
#endif
return retval;
}
释放申请的irq
extern void free_irq(unsigned int, void *);
extern void free_percpu_irq(unsigned int, void __percpu *);
我们也用最常见的free_irq来分析
void free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq); /* 根据中断号得到对应的desc */
if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return;
#ifdef CONFIG_SMP
if (WARN_ON(desc->affinity_notify))
desc->affinity_notify = NULL;
#endif
kfree(__free_irq(irq, dev_id)); /* 释放掉action结构体 */
}
static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction *action, **action_ptr;
unsigned long flags;
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
if (!desc)
return NULL;
/* 上锁 */
chip_bus_lock(desc);
raw_spin_lock_irqsave(&desc->lock, flags);
/*
* There can be multiple actions per IRQ descriptor, find the right
* one based on the dev_id:
*/
action_ptr = &desc->action; /* 得到action链表头 */
for (;;) {
action = *action_ptr;
if (!action) { /* 下面也说明了,释放已经释放了的中断或没找到dev_id对应的 */
WARN(1, "Trying to free already-free IRQ %d\n", irq);
raw_spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(desc);
return NULL;
}
if (action->dev_id == dev_id) /* 链表中找到对应的dev_id标记的action */
break;
action_ptr = &action->next; /* 遍历链表查找dev_id */
}
/* Found it - now remove it from the list of entries: */
*action_ptr = action->next; /* 链表中删除action */
/* If this was the last handler, shut down the IRQ line: */
if (!desc->action) {
irq_shutdown(desc); /* 如果不是共享中断或共享中断这个是最后一个,则要关闭中断 */
irq_release_resources(desc);
}
#ifdef CONFIG_SMP
/* make sure affinity_hint is cleaned up */
if (WARN_ON_ONCE(desc->affinity_hint))
desc->affinity_hint = NULL;
#endif
/* 解锁 */
raw_spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(desc);
unregister_handler_proc(irq, action); /* 更新proc中的irq */
/* Make sure it's not being used on another CPU: */
synchronize_irq(irq); /* 多核cpu需要同步一下 */
#ifdef CONFIG_DEBUG_SHIRQ
/*
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
* event to happen even now it's being freed, so let's make sure that
* is so by doing an extra call to the handler ....
*
* ( We do this after actually deregistering it, to make sure that a
* 'real' IRQ doesn't run in * parallel with our fake. )
*/
if (action->flags & IRQF_SHARED) {
local_irq_save(flags);
action->handler(irq, dev_id);
local_irq_restore(flags);
}
#endif
if (action->thread) {
kthread_stop(action->thread); /* 释放的这个action如果是有线程化,也要关闭这个线程 */
put_task_struct(action->thread);
}
module_put(desc->owner);
return action; /* 返回cation指针,下一个函数释放掉内存 */
}
中断就暂时分析到这里,下一节我们来写一个简单的测试程序来使用一下中断。