linux 中断机制浅析
Posted 专注it
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 中断机制浅析相关的知识,希望对你有一定的参考价值。
一、中断相关结构体
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函数
整个中断的处理过程就是
以上是关于linux 中断机制浅析的主要内容,如果未能解决你的问题,请参考以下文章