现在的位置: 首页 > 综合 > 正文

linux 中断机制浅析

2013年01月21日 ⁄ 综合 ⁄ 共 26190字 ⁄ 字号 评论关闭

一、中断相关结构体

1.irq_desc中断描述符

struct irq_desc {
#ifdef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED
	struct irq_data	irq_data;
#else
	union {
		struct irq_data	irq_data;	//中断数据
		struct {
			unsigned int	irq;	//中断号
			unsigned int	node;	//节点号
			struct irq_chip	*chip;	//irq_chip
			void	*handler_data;	
			void	*chip_data;	
			struct msi_desc	*msi_desc;
#ifdef CONFIG_SMP
			cpumask_var_t	affinity;
#endif
		};
	};
#endif
	struct timer_rand_state *timer_rand_state;
	unsigned int	*kstat_irqs;
	irq_flow_handler_t	handle_irq;	//中断处理句柄
	struct irqaction	*action;	/* 中断动作列表 */
	unsigned int	status;		/* 中断状态 */
	unsigned int	depth;		/* nested irq disables */
	unsigned int	wake_depth;	/* nested wake enables */
	unsigned int	irq_count;	/* For detecting broken IRQs */
	unsigned long	last_unhandled;	/* Aging timer for unhandled count */
	unsigned int	irqs_unhandled;
	raw_spinlock_t	lock;
#ifdef CONFIG_SMP
	const struct cpumask	*affinity_hint;
#ifdef CONFIG_GENERIC_PENDING_IRQ
	cpumask_var_t	pending_mask;
#endif
#endif
	atomic_t	threads_active;
	wait_queue_head_t	wait_for_threads;
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;	//proc接口目录
#endif
	const char	*name;	//名字
} ____cacheline_internodealigned_in_smp;

2.irq_chip 芯片相关的处理函数集合

struct irq_chip {	//芯片相关的处理函数集合
	const char	*name;	//"proc/interrupts/name"
#ifndef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED
	unsigned int	(*startup)(unsigned int irq);	
	void	(*shutdown)(unsigned int irq);	
	void	(*enable)(unsigned int irq);	
	void	(*disable)(unsigned int irq);	
	void	(*ack)(unsigned int irq);
	void	(*mask)(unsigned int irq);
	void	(*mask_ack)(unsigned int irq);
	void	(*unmask)(unsigned int irq);
	void	(*eoi)(unsigned int irq);
	void	(*end)(unsigned int irq);
	int		(*set_affinity)(unsigned int irq,const struct cpumask *dest);
	int		(*retrigger)(unsigned int irq);
	int		(*set_type)(unsigned int irq, unsigned int flow_type);
	int		(*set_wake)(unsigned int irq, unsigned int on);
	void	(*bus_lock)(unsigned int irq);
	void	(*bus_sync_unlock)(unsigned int irq);
#endif
	unsigned int	(*irq_startup)(struct irq_data *data);	//中断开始
	void	(*irq_shutdown)(struct irq_data *data);	//中断关闭
	void	(*irq_enable)(struct irq_data *data);	//中断使能
	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);
	int		(*irq_retrigger)(struct irq_data *data);
	int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);
	int		(*irq_set_wake)(struct irq_data *data, unsigned int on);
	void	(*irq_bus_lock)(struct irq_data *data);
	void	(*irq_bus_sync_unlock)(struct irq_data *data);
#ifdef CONFIG_IRQ_RELEASE_METHOD
	void	(*release)(unsigned int irq, void *dev_id);
#endif
};

3.irqaction中断行动结构体

struct irqaction {
	irq_handler_t handler;	//中断处理函数
	unsigned long flags;	//中断标志
	const char *name;		//中断名
	void *dev_id;		//设备id号,共享中断用
	struct irqaction *next;	//共享中断类型指向下一个irqaction
	int irq;			//中断号
	struct proc_dir_entry *dir;	//proc接口目录
	irq_handler_t thread_fn;	//中断处理函数(线程)
	struct task_struct *thread;	//线程任务
	unsigned long thread_flags;	//线程标志
};

 在整个中断系统中将勾勒出以下的关系框图

二、中断初始化工作

 从start_kernel看起,大致按以下的分支顺序初始化

start_kernel
	setup_arch			//设置全局init_arch_irq函数
		early_trap_init	//搬移向量表
	early_irq_init();	//初始化全局irq_desc数组
	init_IRQ();			//调用init_arch_irq函数
		[	//板级中断初始化常用到的API
		set_irq_chip
		set_irq_handler
		set_irq_chained_handler
		set_irq_flags
		set_irq_type
		set_irq_chip_data
		set_irq_data
		]

1.在setup_arch中主要是设置全局init_arch_irq函数

void __init setup_arch(char **cmdline_p)
{
	struct tag *tags = (struct tag *)&init_tags;
	struct machine_desc *mdesc;
	char *from = default_command_line;

	init_tags.mem.start = PHYS_OFFSET;
	unwind_init();
	setup_processor();
	mdesc = setup_machine(machine_arch_type);
	machine_name = mdesc->name;
	if (mdesc->soft_reboot)
		reboot_setup("s");
	if (__atags_pointer)
		tags = phys_to_virt(__atags_pointer);
	else if (mdesc->boot_params) {
#ifdef CONFIG_MMU
		if (mdesc->boot_params < PHYS_OFFSET ||mdesc->boot_params >= PHYS_OFFSET + SZ_1M) {
			printk(KERN_WARNING"Default boot params at physical 0x%08lx out of reach\n",mdesc->boot_params);
		} 
		else
#endif
		{
			tags = phys_to_virt(mdesc->boot_params);
		}
	}
#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
	if (tags->hdr.tag != ATAG_CORE)
		convert_to_tag_list(tags);
#endif
	if (tags->hdr.tag != ATAG_CORE)
		tags = (struct tag *)&init_tags;
	if (mdesc->fixup)
		mdesc->fixup(mdesc, tags, &from, &meminfo);
	if (tags->hdr.tag == ATAG_CORE) {
		if (meminfo.nr_banks != 0)
			squash_mem_tags(tags);
		save_atags(tags);
		parse_tags(tags);
	}
	init_mm.start_code = (unsigned long) _text;
	init_mm.end_code   = (unsigned long) _etext;
	init_mm.end_data   = (unsigned long) _edata;
	init_mm.brk	   = (unsigned long) _end;
	strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
	strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
	*cmdline_p = cmd_line;
	parse_early_param();
	arm_memblock_init(&meminfo, mdesc);
	paging_init(mdesc);
	request_standard_resources(&meminfo, mdesc);
#ifdef CONFIG_SMP
	if (is_smp())
		smp_init_cpus();
#endif
	reserve_crashkernel();
	cpu_init();
	tcm_init();
	arch_nr_irqs = mdesc->nr_irqs;
	init_arch_irq = mdesc->init_irq;	//设置全局init_arch_irq函数
	//void (*init_arch_irq)(void) __initdata = NULL;
	system_timer = mdesc->timer;
	init_machine = mdesc->init_machine;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
	conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
	conswitchp = &dummy_con;
#endif
#endif
	early_trap_init();//调用early_trap_init函数
}

1.1.early_trap_init主要挪移了中断向量表到特定位置

void __init early_trap_init(void)
{
	unsigned long vectors = CONFIG_VECTORS_BASE;	//0xffff0000
	extern char __stubs_start[], __stubs_end[];
	extern char __vectors_start[], __vectors_end[];
	extern char __kuser_helper_start[], __kuser_helper_end[];
	int kuser_sz = __kuser_helper_end - __kuser_helper_start;

	memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);	//移动中断向量表
	memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);	//移动__stubs_start段
	memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
	kuser_get_tls_init(vectors);
	memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,sizeof(sigreturn_codes));
	memcpy((void *)KERN_RESTART_CODE, syscall_restart_code,sizeof(syscall_restart_code));
	flush_icache_range(vectors, vectors + PAGE_SIZE);
	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}

2.early_irq_init 初始化全局irq_desc数组
在(kernel/irqs/irqdesc.c)中定义了全局irq_desc数组

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {	
	[0 ... NR_IRQS-1] = {
		.status		= IRQ_DEFAULT_INIT_FLAGS,
		.handle_irq	= handle_bad_irq,
		.depth		= 1,
		.lock		= __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
	}
};

获取irq_desc数组项的宏

#define irq_to_desc(irq)	(&irq_desc[irq])

early_irq_init函数

int __init early_irq_init(void)	//初始化全局irq_desc数组
{
	int count, i, node = first_online_node;
	struct irq_desc *desc;

	init_irq_default_affinity();
	printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);
	desc = irq_desc;	//获取全局irq_desc数组
	count = ARRAY_SIZE(irq_desc);	//获取全局irq_desc数组项个数
	for (i = 0; i < count; i++) {	//初始化全局irq_desc数组
		desc[i].irq_data.irq = i;
		desc[i].irq_data.chip = &no_irq_chip;
		desc[i].kstat_irqs = kstat_irqs_all[i];
		alloc_masks(desc + i, GFP_KERNEL, node);
		desc_smp_init(desc + i, node);
		lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
	}
	return arch_early_irq_init();
}

3.init_IRQ函数调用全局init_arch_irq函数,进入板级初始化

void __init init_IRQ(void)
{
	init_arch_irq();	//调用板级驱动的中断初始化函数
}

4.板级中断初始化常用到的API

1.set_irq_chip 通过irq中断号获取对应全局irq_desc数组项,并设置其irq_data.chip指向传递进去的irq_chip指针

int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
	struct irq_desc *desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	unsigned long flags;

	if (!desc) {
		WARN(1, KERN_ERR "Trying to install chip for IRQ%d\n", irq);
		return -EINVAL;
	}
	if (!chip)						//若irq_chip不存在
		chip = &no_irq_chip;		//设置为no_irq_chip
	raw_spin_lock_irqsave(&desc->lock, flags);
	irq_chip_set_defaults(chip);	//初始化irq_chip
	desc->irq_data.chip = chip;		//设置irq_desc->irq_data.chip
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	return 0;
}
EXPORT_SYMBOL(set_irq_chip);

1.1 irq_chip_set_defaults 根据irq_chip的实际情况初始化默认方法

void irq_chip_set_defaults(struct irq_chip *chip)	//根据irq_chip的实际情况初始化默认方法
{
#ifndef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED
	if (chip->enable)	
		chip->irq_enable = compat_irq_enable;
	if (chip->disable)	
		chip->irq_disable = compat_irq_disable;
	if (chip->shutdown)	
		chip->irq_shutdown = compat_irq_shutdown;
	if (chip->startup)	
		chip->irq_startup = compat_irq_startup;
#endif
	if (!chip->irq_enable)		//使能中断
		chip->irq_enable = default_enable;
	if (!chip->irq_disable)		//禁用中断
		chip->irq_disable = default_disable;
	if (!chip->irq_startup)		//中断开始
		chip->irq_startup = default_startup;
	if (!chip->irq_shutdown)	//关闭中断
		chip->irq_shutdown = chip->irq_disable != default_disable ? chip->irq_disable : default_shutdown;

#ifndef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED
	if (!chip->end)
		chip->end = dummy_irq_chip.end;
	if (chip->bus_lock)
		chip->irq_bus_lock = compat_bus_lock;
	if (chip->bus_sync_unlock)
		chip->irq_bus_sync_unlock = compat_bus_sync_unlock;
	if (chip->mask)
		chip->irq_mask = compat_irq_mask;
	if (chip->unmask)
		chip->irq_unmask = compat_irq_unmask;
	if (chip->ack)
		chip->irq_ack = compat_irq_ack;
	if (chip->mask_ack)
		chip->irq_mask_ack = compat_irq_mask_ack;
	if (chip->eoi)
		chip->irq_eoi = compat_irq_eoi;
	if (chip->set_affinity)
		chip->irq_set_affinity = compat_irq_set_affinity;
	if (chip->set_type)
		chip->irq_set_type = compat_irq_set_type;
	if (chip->set_wake)
		chip->irq_set_wake = compat_irq_set_wake;
	if (chip->retrigger)
		chip->irq_retrigger = compat_irq_retrigger;
#endif
}

2.set_irq_handler和set_irq_chained_handler

这两个函数的功能是:根据irq中断号,获取对应全局irq_desc数组项,并将数组项描述符的handle_irq中断处理函数指针指向handle
如果是chain类型则添加数组项的status状态IRQ_NOREQUEST和IRQ_NOPROBE属性

static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
	__set_irq_handler(irq, handle, 0, NULL);	//调用__set_irq_handler
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static inline void set_irq_chained_handler(unsigned int irq,irq_flow_handler_t handle)
{
	__set_irq_handler(irq, handle, 1, NULL);	//调用__set_irq_handler
}

2.1 __set_irq_handler函数

void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,const char *name)
{
	struct irq_desc *desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	unsigned long flags;

	if (!desc) {
		printk(KERN_ERR"Trying to install type control for IRQ%d\n", irq);
		return;
	}
	if (!handle)	//若没指定handle
		handle = handle_bad_irq;	//则设置为handle_bad_irq
	else if (desc->irq_data.chip == &no_irq_chip) {
		printk(KERN_WARNING "Trying to install %sinterrupt handler for IRQ%d\n", is_chained ? "chained " : "", irq);
		desc->irq_data.chip = &dummy_irq_chip;	//设置desc->irq_data.chip为dummy_irq_chip(空操作的irq_chip)
	}
	chip_bus_lock(desc);
	raw_spin_lock_irqsave(&desc->lock, flags);
	if (handle == handle_bad_irq) {
		if (desc->irq_data.chip != &no_irq_chip)
			mask_ack_irq(desc);
		desc->status |= IRQ_DISABLED;
		desc->depth = 1;
	}
	desc->handle_irq = handle;	//高级中断处理句柄
	desc->name = name;
	if (handle != handle_bad_irq && is_chained) {	//链接
		desc->status &= ~IRQ_DISABLED;
		desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
		desc->depth = 0;
		desc->irq_data.chip->irq_startup(&desc->irq_data);
	}
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	chip_bus_sync_unlock(desc);
}
EXPORT_SYMBOL_GPL(__set_irq_handler);

3.set_irq_flags 根据irq号获取全局irq_desc数组项,并设置其status标志(中断标志)

void set_irq_flags(unsigned int irq, unsigned int iflags)
{
	struct irq_desc *desc;
	unsigned long flags;

	if (irq >= nr_irqs) {
		printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
		return;
	}
	desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	raw_spin_lock_irqsave(&desc->lock, flags);
	desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
	if (iflags & IRQF_VALID)
		desc->status &= ~IRQ_NOREQUEST;
	if (iflags & IRQF_PROBE)
		desc->status &= ~IRQ_NOPROBE;
	if (!(iflags & IRQF_NOAUTOEN))
		desc->status &= ~IRQ_NOAUTOEN;
	raw_spin_unlock_irqrestore(&desc->lock, flags);
}

4.set_irq_type根据irq号获取全局irq_desc数组项,并设置其status标志(中断触发类型)

int set_irq_type(unsigned int irq, unsigned int type)
{
	struct irq_desc *desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	unsigned long flags;
	int ret = -ENXIO;

	if (!desc) {
		printk(KERN_ERR "Trying to set irq type for IRQ%d\n", irq);
		return -ENODEV;
	}
	type &= IRQ_TYPE_SENSE_MASK;
	if (type == IRQ_TYPE_NONE)
		return 0;
	raw_spin_lock_irqsave(&desc->lock, flags);
	ret = __irq_set_trigger(desc, irq, type);	//调用__irq_set_trigger
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	return ret;
}
EXPORT_SYMBOL(set_irq_type);

4.1 __irq_set_trigger函数

int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,unsigned long flags)
{
	int ret;
	struct irq_chip *chip = desc->irq_data.chip;

	if (!chip || !chip->irq_set_type) {
		pr_debug("No set_type function for IRQ %d (%s)\n", irq,chip ? (chip->name ? : "unknown") : "unknown");
		return 0;
	}
	ret = chip->irq_set_type(&desc->irq_data, flags);
	if (ret)
		pr_err("setting trigger mode %lu for irq %u failed (%pF)\n",flags, irq, chip->irq_set_type);
	else {
		if (flags & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
			flags |= IRQ_LEVEL;
		/* note that IRQF_TRIGGER_MASK == IRQ_TYPE_SENSE_MASK */
		desc->status &= ~(IRQ_LEVEL | IRQ_TYPE_SENSE_MASK);
		desc->status |= flags;
		if (chip != desc->irq_data.chip)
			irq_chip_set_defaults(desc->irq_data.chip);
	}
	return ret;
}

5.set_irq_chip_data 根据irq号获取全局irq_desc数组项,并设置其irq_data的chip_data

int set_irq_chip_data(unsigned int irq, void *data)
{
	struct irq_desc *desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	unsigned long flags;

	if (!desc) {
		printk(KERN_ERR"Trying to install chip data for IRQ%d\n", irq);
		return -EINVAL;
	}
	if (!desc->irq_data.chip) {
		printk(KERN_ERR "BUG: bad set_irq_chip_data(IRQ#%d)\n", irq);
		return -EINVAL;
	}
	raw_spin_lock_irqsave(&desc->lock, flags);
	desc->irq_data.chip_data = data;
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	return 0;
}
EXPORT_SYMBOL(set_irq_chip_data);

6.set_irq_data

int set_irq_data(unsigned int irq, void *data)
{
	struct irq_desc *desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	unsigned long flags;

	if (!desc) {
		printk(KERN_ERR"Trying to install controller data for IRQ%d\n", irq);
		return -EINVAL;
	}
	raw_spin_lock_irqsave(&desc->lock, flags);
	desc->irq_data.handler_data = data;
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	return 0;
}
EXPORT_SYMBOL(set_irq_data);

三、中断的申请与释放request_irq

1.申请中断(主要是分配设置irqaction结构体)

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);
}

1.1 request_threaded_irq函数

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;

	if ((irqflags & IRQF_SHARED) && !dev_id)	//共享中断但没指定中断id
		return -EINVAL;
	desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	if (!desc)
		return -EINVAL;
	if (desc->status & IRQ_NOREQUEST)	//中断不能被请求
		return -EINVAL;
	if (!handler) {	//没指定handler
		if (!thread_fn)	//但存在thread_fn
			return -EINVAL;
		handler = irq_default_primary_handler;	//则设置irq_default_primary_handler
	}
	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);	//分配irqaction内存
	if (!action)
		return -ENOMEM;
	action->handler = handler;	//设置处理句柄
	action->thread_fn = thread_fn;	//设置线程函数NULL
	action->flags = irqflags;	//设置中断标志
	action->name = devname;		//设置设备名
	action->dev_id = dev_id;	//设置设备id
	chip_bus_lock(desc);
	retval = __setup_irq(irq, desc, action);	//-->__setup_irq
	chip_bus_sync_unlock(desc);
	if (retval)
		kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ
	if (!retval && (irqflags & IRQF_SHARED)) {
		unsigned long flags;
		disable_irq(irq);
		local_irq_save(flags);
		handler(irq, dev_id);
		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}
EXPORT_SYMBOL(request_threaded_irq);

 

1.2 __setup_irq函数

static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
	struct irqaction *old, **old_ptr;
	const char *old_name = NULL;
	unsigned long flags;
	int nested, shared = 0;
	int ret;

	if (!desc)
		return -EINVAL;
	if (desc->irq_data.chip == &no_irq_chip)
		return -ENOSYS;
	if (new->flags & IRQF_SAMPLE_RANDOM) {
		rand_initialize_irq(irq);
	}
	if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
		return -EINVAL;
	nested = desc->status & IRQ_NESTED_THREAD;	//嵌套标志
	if (nested) {	//嵌套
		if (!new->thread_fn)	//且存在线程处理句柄
			return -EINVAL;
		new->handler = irq_nested_primary_handler;		//嵌套处理的句柄
	}
	if (new->thread_fn && !nested) {	//非嵌套且存在线程函数
		struct task_struct *t;

		t = kthread_create(irq_thread, new, "irq/%d-%s", irq,new->name);	//创建线程
		if (IS_ERR(t))
			return PTR_ERR(t);
		get_task_struct(t);
		new->thread = t;	//设置线程任务结构体
	}
	raw_spin_lock_irqsave(&desc->lock, flags);
	old_ptr = &desc->action;
	old = *old_ptr;
	if (old) {
		if (!((old->flags & new->flags) & IRQF_SHARED) || ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
			old_name = old->name;
			goto mismatch;
		}
#if defined(CONFIG_IRQ_PER_CPU)
		if ((old->flags & IRQF_PERCPU) != (new->flags & IRQF_PERCPU))
			goto mismatch;
#endif
		do {
			old_ptr = &old->next;
			old = *old_ptr;
		} while (old);
		shared = 1;	//共享中断标志
	}
	if (!shared) {	//非共享中断
		irq_chip_set_defaults(desc->irq_data.chip);		//设置默认的芯片处理函数
		init_waitqueue_head(&desc->wait_for_threads);
		if (new->flags & IRQF_TRIGGER_MASK) {	//设置触发方式
			ret = __irq_set_trigger(desc, irq,new->flags & IRQF_TRIGGER_MASK);
			if (ret)
				goto out_thread;
		} 
		else
			compat_irq_chip_set_default_handler(desc);
#if defined(CONFIG_IRQ_PER_CPU)
		if (new->flags & IRQF_PERCPU)
			desc->status |= IRQ_PER_CPU;
#endif
		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
		if (new->flags & IRQF_ONESHOT)
			desc->status |= IRQ_ONESHOT;
		if (!(desc->status & IRQ_NOAUTOEN)) {
			desc->depth = 0;
			desc->status &= ~IRQ_DISABLED;
			desc->irq_data.chip->irq_startup(&desc->irq_data);
		} 
		else
			desc->depth = 1;
		if (new->flags & IRQF_NOBALANCING)
			desc->status |= IRQ_NO_BALANCING;
		setup_affinity(irq, desc);
	} 
	else if ((new->flags & IRQF_TRIGGER_MASK)&& (new->flags & IRQF_TRIGGER_MASK)!= (desc->status & IRQ_TYPE_SENSE_MASK)) {
		pr_warning("IRQ %d uses trigger mode %d; requested %d\n",
				irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK),(int)(new->flags & IRQF_TRIGGER_MASK));
	}
	new->irq = irq;	//设置中断号
	*old_ptr = new;
	desc->irq_count = 0;
	desc->irqs_unhandled = 0;
	if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {	//共享中断
		desc->status &= ~IRQ_SPURIOUS_DISABLED;
		__enable_irq(desc, irq, false);
	}
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	if (new->thread)
		wake_up_process(new->thread);
	register_irq_proc(irq, desc);	//注册proc irq接口
	new->dir = NULL;
	register_handler_proc(irq, new);	//注册proc handler接口
	return 0;
mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
	if (!(new->flags & IRQF_PROBE_SHARED)) {
		printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
		if (old_name)
			printk(KERN_ERR "current handler: %s\n", old_name);
		dump_stack();
	}
#endif
	ret = -EBUSY;
out_thread:
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	if (new->thread) {
		struct task_struct *t = new->thread;
		new->thread = NULL;
		if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))
			kthread_stop(t);
		put_task_struct(t);
	}
	return ret;
}

代码可以去细究,主要功能是填充irqaction

在设备驱动程序中申请中断可以这么申请

(eg:request_irq(1, &XXX_interrupt,IRQF_TRIGGER_RISING,"nameXXX", (void*)0))

第一个参数是中断号,第二个参数是中断处理函数,第三个参数是中断标志(上升沿),第四个是名字,第五个是设备id(非共享中断设置成(void*)0)即可

共享中断情况下要将第三个参数添加IRQF_SHARED标志,同时要给他制定第五个参数设备id

触发方式宏

#define IRQ_TYPE_NONE		0x00000000	/* Default, unspecified type */
#define IRQ_TYPE_EDGE_RISING	0x00000001	//上升沿触发
#define IRQ_TYPE_EDGE_FALLING	0x00000002	//下降沿触发
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)	//双边沿触发
#define IRQ_TYPE_LEVEL_HIGH	0x00000004	//高电平有效
#define IRQ_TYPE_LEVEL_LOW	0x00000008	//低电平有效
#define IRQ_TYPE_SENSE_MASK	0x0000000f	/* Mask of the above */
#define IRQ_TYPE_PROBE		0x00000010	/* Probing in progress */

然后设计中断函数

static irqreturn_t XXX_interrupt(int irq, void *arg){

    ......

    return IRQ_HANDLED;

}

2.释放中断

void free_irq(unsigned int irq, void *dev_id)
{
	struct irq_desc *desc = irq_to_desc(irq);	//获取全局irq_desc数组项

	if (!desc)
		return;
	chip_bus_lock(desc);
	kfree(__free_irq(irq, dev_id));
	chip_bus_sync_unlock(desc);
}
EXPORT_SYMBOL(free_irq);

2.1 __free_irq

static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
	struct irq_desc *desc = irq_to_desc(irq);	//获取全局irq_desc数组项
	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;
	raw_spin_lock_irqsave(&desc->lock, flags);
	action_ptr = &desc->action;
	for (;;) {
		action = *action_ptr;
		if (!action) {
			WARN(1, "Trying to free already-free IRQ %d\n", irq);
			raw_spin_unlock_irqrestore(&desc->lock, flags);
			return NULL;
		}
		if (action->dev_id == dev_id)	//找到匹配的id项
			break;
		action_ptr = &action->next;
	}
	*action_ptr = action->next;
#ifdef CONFIG_IRQ_RELEASE_METHOD
	if (desc->irq_data.chip->release)
		desc->irq_data.chip->release(irq, dev_id);
#endif
	if (!desc->action) {
		desc->status |= IRQ_DISABLED;
		if (desc->irq_data.chip->irq_shutdown)
			desc->irq_data.chip->irq_shutdown(&desc->irq_data);
		else
			desc->irq_data.chip->irq_disable(&desc->irq_data);
	}
#ifdef CONFIG_SMP
	if (WARN_ON_ONCE(desc->affinity_hint))
		desc->affinity_hint = NULL;
#endif
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	unregister_handler_proc(irq, action);
	synchronize_irq(irq);
#ifdef CONFIG_DEBUG_SHIRQ
	if (action->flags & IRQF_SHARED) {
		local_irq_save(flags);
		action->handler(irq, dev_id);
		local_irq_restore(flags);
	}
#endif
	if (action->thread) {
		if (!test_bit(IRQTF_DIED, &action->thread_flags))
			kthread_stop(action->thread);
		put_task_struct(action->thread);
	}
	return action;
}

 

四、中断处理过程

 1.当有中断发生时,程序会到__vectors_star去查找向量表(arch/arm/kernel/entry-armv.S)

	.globl	__vectors_start
__vectors_start:
 ARM(	swi	SYS_ERROR0	)	/* swi指令 */
 THUMB(	svc	#0		)
 THUMB(	nop			)
	W(b)	vector_und + stubs_offset
	W(ldr)	pc, .LCvswi + stubs_offset
	W(b)	vector_pabt + stubs_offset
	W(b)	vector_dabt + stubs_offset
	W(b)	vector_addrexcptn + stubs_offset
	W(b)	vector_irq + stubs_offset	/* 中断向量表 */
	W(b)	vector_fiq + stubs_offset
	.globl	__vectors_end
__vectors_end:

2.vector_irq的定义声明

	.globl	__stubs_start
__stubs_start:
/*
 * Interrupt dispatcher
 */
	vector_stub irq, IRQ_MODE, 4	/*参看下面vector_stub宏的定义*/

	.long	__irq_usr			@  0  (USR_26 / USR_32)		/*usr模式下中断处理(见下面)*/
	.long	__irq_invalid		@  1  (FIQ_26 / FIQ_32)
	.long	__irq_invalid		@  2  (IRQ_26 / IRQ_32)
	.long	__irq_svc			@  3  (SVC_26 / SVC_32)		/*svc模式下中断处理(见下面)*/
	.long	__irq_invalid		@  4
	.long	__irq_invalid		@  5
	.long	__irq_invalid		@  6
	.long	__irq_invalid		@  7
	.long	__irq_invalid		@  8
	.long	__irq_invalid		@  9
	.long	__irq_invalid		@  a
	.long	__irq_invalid		@  b
	.long	__irq_invalid		@  c
	.long	__irq_invalid		@  d
	.long	__irq_invalid		@  e
	.long	__irq_invalid		@  f

3.vector_stub宏的定义

	/*vector_stub irq, IRQ_MODE, 4*/
	.macro	vector_stub, name, mode, correction=0	
	.align	5

vector_\name:						/*构造了vector_irq*/
	.if \correction					/*if 4*/
	sub	lr, lr, #\correction
	.endif

	@ 
	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
	@ (parent CPSR)
	@
	stmia	sp, {r0, lr}		@ save r0, lr
	mrs	lr, spsr
	str	lr, [sp, #8]		@ save spsr

	@
	@ Prepare for SVC32 mode.  IRQs remain disabled.	准备切到svc模式
	@
	mrs	r0, cpsr
	eor	r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
	msr	spsr_cxsf, r0

	@ /*分支表必须紧接着这段代码*/
	@ the branch table must immediately follow this code
	@
	and	lr, lr, #0x0f
 THUMB(	adr	r0, 1f			)
 THUMB(	ldr	lr, [r0, lr, lsl #2]	)
	mov	r0, sp
 ARM(	ldr	lr, [pc, lr, lsl #2]	)
	movs	pc, lr			@ branch to handler in SVC mode	跳到分支表处
ENDPROC(vector_\name)

	.align	2
	@ handler addresses follow this label
1:
	.endm

这几段汇编的大致意思是中断发生会跳到vector_irq去执行,vector_irq根据情况会跳到__irq_usr或__irq_svc执行

4.__irq_usr

__irq_usr:
		usr_entry
		kuser_cmpxchg_check
	
		get_thread_info tsk
#ifdef CONFIG_PREEMPT
		ldr r8, [tsk, #TI_PREEMPT]		@ get preempt count
		add r7, r8, #1			@ increment it
		str r7, [tsk, #TI_PREEMPT]
#endif
	
		irq_handler		/*跳转到irq_handler处理*/
#ifdef CONFIG_PREEMPT
		ldr r0, [tsk, #TI_PREEMPT]
		str r8, [tsk, #TI_PREEMPT]
		teq r0, r7
	 ARM(	strne	r0, [r0, -r0]	)
	 THUMB( movne	r0, #0		)
	 THUMB( strne	r0, [r0]	)
#endif
	
		mov why, #0
		b	ret_to_user
	 UNWIND(.fnend		)
	ENDPROC(__irq_usr)

5.__irq_svc

__irq_svc:
		svc_entry
	
#ifdef CONFIG_TRACE_IRQFLAGS
		bl	trace_hardirqs_off
#endif
#ifdef CONFIG_PREEMPT
		get_thread_info tsk
		ldr r8, [tsk, #TI_PREEMPT]		@ get preempt count
		add r7, r8, #1			@ increment it
		str r7, [tsk, #TI_PREEMPT]
#endif
	
		irq_handler			/*跳转到irq_handler处理*/
#ifdef CONFIG_PREEMPT
		str r8, [tsk, #TI_PREEMPT]		@ restore preempt count
		ldr r0, [tsk, #TI_FLAGS]		@ get flags
		teq r8, #0				@ if preempt count != 0
		movne	r0, #0				@ force flags to 0
		tst r0, #_TIF_NEED_RESCHED
		blne	svc_preempt
#endif
		ldr r4, [sp, #S_PSR]		@ irqs are already disabled
#ifdef CONFIG_TRACE_IRQFLAGS
		tst r4, #PSR_I_BIT
		bleq	trace_hardirqs_on
#endif
		svc_exit r4 			@ return from exception
	 UNWIND(.fnend		)
	ENDPROC(__irq_svc)

6.不管是__irq_svc或是__irq_usr都会调用到irqhandler

	.macro	irq_handler
	get_irqnr_preamble r5, lr
1:	get_irqnr_and_base r0, r6, r5, lr
	movne	r1, sp
	@ r0保存了中断号,r1保存了保留现场的寄存器指针
	@ routine called with r0 = irq number, r1 = struct pt_regs *
	@
	adrne	lr, BSYM(1b)
	bne	asm_do_IRQ	/*********************跳转到asm_do_IRQ函数处理*/

#ifdef CONFIG_SMP
	/*
	 * XXX
	 *
	 * this macro assumes that irqstat (r6) and base (r5) are
	 * preserved from get_irqnr_and_base above
	 */
	ALT_SMP(test_for_ipi r0, r6, r5, lr)
	ALT_UP_B(9997f)
	movne	r0, sp
	adrne	lr, BSYM(1b)
	bne	do_IPI

#ifdef CONFIG_LOCAL_TIMERS
	test_for_ltirq r0, r6, r5, lr
	movne	r0, sp
	adrne	lr, BSYM(1b)
	bne	do_local_timer
#endif
9997:
#endif

	.endm

7.就这样进入了c处理的阶段asm_do_IRQ

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);

	irq_enter();

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (unlikely(irq >= nr_irqs)) {	//中断号大于中断的个数
		if (printk_ratelimit())
			printk(KERN_WARNING "Bad IRQ%u\n", irq);
		ack_bad_irq(irq);
	} 
	else {
		generic_handle_irq(irq);	//通用中断处理函数
	}

	/* AT91 specific workaround */
	irq_finish(irq);

	irq_exit();
	set_irq_regs(old_regs);
}

8.generic_handle_irq函数

static inline void generic_handle_irq(unsigned int irq)
{
	generic_handle_irq_desc(irq, irq_to_desc(irq));	//调用了irq_to_desc获取全局irq_desc[irq]项
}

9.generic_handle_irq_desc函数

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ	//根据板级配置的设置若定义了
	desc->handle_irq(irq, desc);	//则只能用指定的handle_irq方法
#else
	if (likely(desc->handle_irq))	//若中断处理函数存在
		desc->handle_irq(irq, desc);	//则调用注册的中断处理函数(irq_desc[irq]->handle_irq(irq,desc))
	else
		__do_IRQ(irq);	//没指定中断处理函数的处理分支
#endif
}

这里有了分支关键看CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ的设置

如果设置为1,则只调用中断描述符的handle_irq方法

如果设置为0,则如果中断描述符存在handle_irq方法则调用该方法,如果没有则调用__do_IRQ()

中断描述符handle_irq方法,一般是芯片厂商写好的,先看看__do_IRQ()吧

10.__do_IRQ函数

unsigned int __do_IRQ(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);
	struct irqaction *action;
	unsigned int status;

	kstat_incr_irqs_this_cpu(irq, desc);
	if (CHECK_IRQ_PER_CPU(desc->status)) {
		irqreturn_t action_ret;
		if (desc->irq_data.chip->ack)
			desc->irq_data.chip->ack(irq);
		if (likely(!(desc->status & IRQ_DISABLED))) {
			action_ret = handle_IRQ_event(irq, desc->action);//调用handle_IRQ_event函数
			if (!noirqdebug)
				note_interrupt(irq, desc, action_ret);
		}
		desc->irq_data.chip->end(irq);
		return 1;
	}
	raw_spin_lock(&desc->lock);
	if (desc->irq_data.chip->ack)
		desc->irq_data.chip->ack(irq);
	status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
	status |= IRQ_PENDING; /* we _want_ to handle it */
	action = NULL;
	if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
		action = desc->action;
		status &= ~IRQ_PENDING; /* we commit to handling */
		status |= IRQ_INPROGRESS; /* we are handling it */
	}
	desc->status = status;
	if (unlikely(!action))
		goto out;
	for (;;) {
		irqreturn_t action_ret;
		raw_spin_unlock(&desc->lock);
		action_ret = handle_IRQ_event(irq, action);//调用handle_IRQ_event函数
		if (!noirqdebug)
			note_interrupt(irq, desc, action_ret);
		raw_spin_lock(&desc->lock);
		if (likely(!(desc->status & IRQ_PENDING)))
			break;
		desc->status &= ~IRQ_PENDING;
	}
	desc->status &= ~IRQ_INPROGRESS;
out:
	desc->irq_data.chip->end(irq);
	raw_spin_unlock(&desc->lock);
	return 1;
}

.__do_IRQ函数主要是调用handle_IRQ_event来处理中断

11.handle_IRQ_event函数

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
	irqreturn_t ret, retval = IRQ_NONE;
	unsigned int status = 0;

	do {
		trace_irq_handler_entry(irq, action);
		ret = action->handler(irq, action->dev_id);//调用了irqaction的handler方法
		trace_irq_handler_exit(irq, action, ret);
		switch (ret) {
		case IRQ_WAKE_THREAD:
			ret = IRQ_HANDLED;
			if (unlikely(!action->thread_fn)) {
				warn_no_thread(irq, action);
				break;
			}
			if (likely(!test_bit(IRQTF_DIED,
					     &action->thread_flags))) {
				set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
				wake_up_process(action->thread);
			}
		case IRQ_HANDLED:
			status |= action->flags;
			break;

		default:
			break;
		}
		retval |= ret;
		action = action->next;
	} while (action);

	if (status & IRQF_SAMPLE_RANDOM)
		add_interrupt_randomness(irq);
	local_irq_disable();
	return retval;
}

这里调用的irqaction的handler方法就是调用了之前设备驱动中用request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)

申请中断时传递进来的第二个参数的函数
其实很多芯片厂商在编写中断描述符handle_irq方法的时候也会调用到handle_IRQ_event函数

整个中断的处理过程就是

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

抱歉!评论已关闭.