标签:mem 汇编 节点 设备id nop reads 位置 user 机制
一、中断相关结构体
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;
- 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;
- unsigned int wake_depth;
- unsigned int irq_count;
- unsigned long last_unhandled;
- 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;
- #endif
- const char *name;
- } ____cacheline_internodealigned_in_smp;
2.irq_chip 芯片相关的处理函数集合
- struct irq_chip {
- const char *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;
- struct irqaction *next;
- int irq;
- struct proc_dir_entry *dir;
- irq_handler_t thread_fn;
- struct task_struct *thread;
- unsigned long thread_flags;
- };
在整个中断系统中将勾勒出以下的关系框图
二、中断初始化工作
从start_kernel看起,大致按以下的分支顺序初始化
- start_kernel
- setup_arch
- early_trap_init
- early_irq_init();
- init_IRQ();
- [
- 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;
-
- 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();
- }
1.1.early_trap_init主要挪移了中断向量表到特定位置
- void __init early_trap_init(void)
- {
- unsigned long vectors = CONFIG_VECTORS_BASE;
- 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);
- 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)
- {
- 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;
- count = ARRAY_SIZE(irq_desc);
- for (i = 0; i < count; i++) {
- 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);
- unsigned long flags;
-
- if (!desc) {
- WARN(1, KERN_ERR "Trying to install chip for IRQ%d\n", irq);
- return -EINVAL;
- }
- if (!chip)
- chip = &no_irq_chip;
- raw_spin_lock_irqsave(&desc->lock, flags);
- irq_chip_set_defaults(chip);
- desc->irq_data.chip = 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)
- {
- #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);
- }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- static inline void set_irq_chained_handler(unsigned int irq,irq_flow_handler_t handle)
- {
- __set_irq_handler(irq, handle, 1, NULL);
- }
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);
- unsigned long flags;
-
- if (!desc) {
- printk(KERN_ERR"Trying to install type control for IRQ%d\n", irq);
- return;
- }
- if (!handle)
- handle = 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;
- }
- 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);
- 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);
- 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);
- 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;
-
- 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);
- 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);
- 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)
- return -EINVAL;
- desc = irq_to_desc(irq);
- if (!desc)
- return -EINVAL;
- if (desc->status & IRQ_NOREQUEST)
- return -EINVAL;
- if (!handler) {
- if (!thread_fn)
- return -EINVAL;
- handler = irq_default_primary_handler;
- }
- action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
- if (!action)
- return -ENOMEM;
- 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);
- 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);
- new->dir = NULL;
- register_handler_proc(irq, new);
- 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);
-
- 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);
- 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)
- 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 )
- 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:
- vector_stub irq, IRQ_MODE, 4
-
- .long __irq_usr @ 0 (USR_26 / USR_32)
- .long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
- .long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
- .long __irq_svc @ 3 (SVC_26 / SVC_32)
- .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宏的定义
-
- .macro vector_stub, name, mode, correction=0
- .align 5
-
- vector_\name:
- .if \correction
- 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
- #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
- #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
-
- #ifdef CONFIG_SMP
-
- 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();
-
-
- 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);
- }
-
-
- 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));
- }
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);
- #else
- if (likely(desc->handle_irq))
- desc->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);
- 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;
- action = NULL;
- if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
- action = desc->action;
- status &= ~IRQ_PENDING;
- status |= IRQ_INPROGRESS;
- }
- 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);
- 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);
- 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 中断机制浅析
标签:mem 汇编 节点 设备id nop reads 位置 user 机制
原文地址:http://www.cnblogs.com/wanghuaijun/p/6321944.html