一、中断相关结构体
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函数
整个中断的处理过程就是