linux 中断机制浅析

来源:互联网 发布:电机数据大全安装尺寸 编辑:程序博客网 时间:2024/06/02 17:47

一、中断相关结构体

1.irq_desc中断描述符

struct irq_desc {#ifdef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATEDstruct irq_datairq_data;#elseunion {struct irq_datairq_data;//中断数据struct {unsigned intirq;//中断号unsigned intnode;//节点号struct irq_chip*chip;//irq_chipvoid*handler_data;void*chip_data;struct msi_desc*msi_desc;#ifdef CONFIG_SMPcpumask_var_taffinity;#endif};};#endifstruct timer_rand_state *timer_rand_state;unsigned int*kstat_irqs;irq_flow_handler_thandle_irq;//中断处理句柄struct irqaction*action;/* 中断动作列表 */unsigned intstatus;/* 中断状态 */unsigned intdepth;/* nested irq disables */unsigned intwake_depth;/* nested wake enables */unsigned intirq_count;/* For detecting broken IRQs */unsigned longlast_unhandled;/* Aging timer for unhandled count */unsigned intirqs_unhandled;raw_spinlock_tlock;#ifdef CONFIG_SMPconst struct cpumask*affinity_hint;#ifdef CONFIG_GENERIC_PENDING_IRQcpumask_var_tpending_mask;#endif#endifatomic_tthreads_active;wait_queue_head_twait_for_threads;#ifdef CONFIG_PROC_FSstruct proc_dir_entry*dir;//proc接口目录#endifconst char*name;//名字} ____cacheline_internodealigned_in_smp;


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

struct irq_chip {//芯片相关的处理函数集合const char*name;//"proc/interrupts/name"#ifndef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATEDunsigned 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);#endifunsigned 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_METHODvoid(*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;//共享中断类型指向下一个irqactionint irq;//中断号struct proc_dir_entry *dir;//proc接口目录irq_handler_t thread_fn;//中断处理函数(线程)struct task_struct *thread;//线程任务unsigned long thread_flags;//线程标志};

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

二、中断初始化工作

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

start_kernelsetup_arch//设置全局init_arch_irq函数early_trap_init//搬移向量表early_irq_init();//初始化全局irq_desc数组init_IRQ();//调用init_arch_irq函数[//板级中断初始化常用到的APIset_irq_chipset_irq_handlerset_irq_chained_handlerset_irq_flagsset_irq_typeset_irq_chip_dataset_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_MMUif (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);#endifif (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_SMPif (is_smp())smp_init_cpus();#endifreserve_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#endifearly_trap_init();//调用early_trap_init函数}

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

void __init early_trap_init(void){unsigned long vectors = CONFIG_VECTORS_BASE;//0xffff0000extern 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_chipraw_spin_lock_irqsave(&desc->lock, flags);irq_chip_set_defaults(chip);//初始化irq_chipdesc->irq_data.chip = chip;//设置irq_desc->irq_data.chipraw_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_DEPRECATEDif (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;#endifif (!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_DEPRECATEDif (!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)//若没指定handlehandle = handle_bad_irq;//则设置为handle_bad_irqelse 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_triggerraw_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)//共享中断但没指定中断idreturn -EINVAL;desc = irq_to_desc(irq);//获取全局irq_desc数组项if (!desc)return -EINVAL;if (desc->status & IRQ_NOREQUEST)//中断不能被请求return -EINVAL;if (!handler) {//没指定handlerif (!thread_fn)//但存在thread_fnreturn -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;//设置线程函数NULLaction->flags = irqflags;//设置中断标志action->name = devname;//设置设备名action->dev_id = dev_id;//设置设备idchip_bus_lock(desc);retval = __setup_irq(irq, desc, action);//-->__setup_irqchip_bus_sync_unlock(desc);if (retval)kfree(action);#ifdef CONFIG_DEBUG_SHIRQif (!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);}#endifreturn 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;#endifdo {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;} elsecompat_irq_chip_set_default_handler(desc);#if defined(CONFIG_IRQ_PER_CPU)if (new->flags & IRQF_PERCPU)desc->status |= IRQ_PER_CPU;#endifdesc->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);} elsedesc->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_SHIRQif (!(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();}#endifret = -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_NONE0x00000000/* Default, unspecified type */#define IRQ_TYPE_EDGE_RISING0x00000001//上升沿触发#define IRQ_TYPE_EDGE_FALLING0x00000002//下降沿触发#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)//双边沿触发#define IRQ_TYPE_LEVEL_HIGH0x00000004//高电平有效#define IRQ_TYPE_LEVEL_LOW0x00000008//低电平有效#define IRQ_TYPE_SENSE_MASK0x0000000f/* Mask of the above */#define IRQ_TYPE_PROBE0x00000010/* 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_METHODif (desc->irq_data.chip->release)desc->irq_data.chip->release(irq, dev_id);#endifif (!desc->action) {desc->status |= IRQ_DISABLED;if (desc->irq_data.chip->irq_shutdown)desc->irq_data.chip->irq_shutdown(&desc->irq_data);elsedesc->irq_data.chip->irq_disable(&desc->irq_data);}#ifdef CONFIG_SMPif (WARN_ON_ONCE(desc->affinity_hint))desc->affinity_hint = NULL;#endifraw_spin_unlock_irqrestore(&desc->lock, flags);unregister_handler_proc(irq, action);synchronize_irq(irq);#ifdef CONFIG_DEBUG_SHIRQif (action->flags & IRQF_SHARED) {local_irq_save(flags);action->handler(irq, dev_id);local_irq_restore(flags);}#endifif (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(swiSYS_ERROR0)/* swi指令 */ THUMB(svc#0) THUMB(nop)W(b)vector_und + stubs_offsetW(ldr)pc, .LCvswi + stubs_offsetW(b)vector_pabt + stubs_offsetW(b)vector_dabt + stubs_offsetW(b)vector_addrexcptn + stubs_offsetW(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*/.macrovector_stub, name, mode, correction=0.align5vector_\name:/*构造了vector_irq*/.if \correction/*if 4*/sublr, lr, #\correction.endif@ @ Save r0, lr_<exception> (parent PC) and spsr_<exception>@ (parent CPSR)@stmiasp, {r0, lr}@ save r0, lrmrslr, spsrstrlr, [sp, #8]@ save spsr@@ Prepare for SVC32 mode.  IRQs remain disabled.准备切到svc模式@mrsr0, cpsreorr0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)msrspsr_cxsf, r0@ /*分支表必须紧接着这段代码*/@ the branch table must immediately follow this code@andlr, lr, #0x0f THUMB(adrr0, 1f) THUMB(ldrlr, [r0, lr, lsl #2])movr0, sp ARM(ldrlr, [pc, lr, lsl #2])movspc, lr@ branch to handler in SVC mode跳到分支表处ENDPROC(vector_\name).align2@ handler addresses follow this label1:.endm

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

4.__irq_usr

__irq_usr:usr_entrykuser_cmpxchg_checkget_thread_info tsk#ifdef CONFIG_PREEMPTldr r8, [tsk, #TI_PREEMPT]@ get preempt countadd r7, r8, #1@ increment itstr r7, [tsk, #TI_PREEMPT]#endifirq_handler/*跳转到irq_handler处理*/#ifdef CONFIG_PREEMPTldr r0, [tsk, #TI_PREEMPT]str r8, [tsk, #TI_PREEMPT]teq r0, r7 ARM(strner0, [r0, -r0]) THUMB( movner0, #0) THUMB( strner0, [r0])#endifmov why, #0bret_to_user UNWIND(.fnend)ENDPROC(__irq_usr)


5.__irq_svc

__irq_svc:svc_entry#ifdef CONFIG_TRACE_IRQFLAGSbltrace_hardirqs_off#endif#ifdef CONFIG_PREEMPTget_thread_info tskldr r8, [tsk, #TI_PREEMPT]@ get preempt countadd r7, r8, #1@ increment itstr r7, [tsk, #TI_PREEMPT]#endifirq_handler/*跳转到irq_handler处理*/#ifdef CONFIG_PREEMPTstr r8, [tsk, #TI_PREEMPT]@ restore preempt countldr r0, [tsk, #TI_FLAGS]@ get flagsteq r8, #0@ if preempt count != 0movner0, #0@ force flags to 0tst r0, #_TIF_NEED_RESCHEDblnesvc_preempt#endifldr r4, [sp, #S_PSR]@ irqs are already disabled#ifdef CONFIG_TRACE_IRQFLAGStst r4, #PSR_I_BITbleqtrace_hardirqs_on#endifsvc_exit r4 @ return from exception UNWIND(.fnend)ENDPROC(__irq_svc)


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

.macroirq_handlerget_irqnr_preamble r5, lr1:get_irqnr_and_base r0, r6, r5, lrmovner1, sp@ r0保存了中断号,r1保存了保留现场的寄存器指针@ routine called with r0 = irq number, r1 = struct pt_regs *@adrnelr, BSYM(1b)bneasm_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)movner0, spadrnelr, BSYM(1b)bnedo_IPI#ifdef CONFIG_LOCAL_TIMERStest_for_ltirq r0, r6, r5, lrmovner0, spadrnelr, BSYM(1b)bnedo_local_timer#endif9997:#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方法#elseif (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函数

整个中断的处理过程就是

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

原创粉丝点击