在驱动程序初始化时,若使用到中断,通常调用函数reqeust_irq() 建立该驱动程序对应的irqaction结构体,并登记到irq_desc[irq_num]->action链表中去。
当发生中断后,首先获取触发中断的HW interrupt ID,然后通过irq domain翻译成IRQ number,(1.找到root interrupt controller对应的irq_domian;2.根据HW寄存器信息和irq_domain获取HW interrupt ID;3.调用irq_find_mapping找到对应的irq number)就可以获取对应的中断描述符。调用中断描述符中饿irq_events_handler来进行中断处理就OK了。
在device tree初始化的时候,形成了系统内所有的device node的树状结构(当然其中包括所有和中断外设的node)
在machine driver初始化的时候会调用of_irq_init函数,在该函数中会扫描所有interrupt controller的节点,并调用合适的interrupt controller driver进行初始化。
中断控制器是连接外设中断系统和CPU系统的桥梁。
驱动程序在请求中断服务时,会使用IRQ编号注册该中断,中断发生时,cpu通常会从中断控制器中获得相关信息,然后计算出相应的IRQ编号,然后把该IRQ编号传递给驱动程序。
对底层的封装主要包括:
实现不同i体系结构中断入口,通常用汇编实现
中断控制器进行封装和实现
系统启动阶段,中断子系统完成了必要的初始化操作,为驱动程序申请中断服务做好了准备。
实际上软中断更多的是在中断的退出阶段执行(irq_exit),以便达到更快的相应,加入守护进程机制,只是担心一旦有大量的软中断等待执行,会使得内核过长的停留在中断上下文中。
几个比较关键的数据结构
struct irqaction //
struct irqaction { irq_handler_t handler; unsigned long flags; void *dev_id; struct irqaction *next; int irq; irq_handler_t thread_fn; struct task_struct *thread; unsigned long thread_flags; unsigned long thread_mask; const char *name; struct proc_dir_entry *dir; } ____cacheline_internodealigned_in_smp;
struct irq_desc //interrupt desciptor
struct irq_desc { struct irq_data irq_data; struct timer_rand_state *timer_rand_state; unsigned int __percpu *kstat_irqs; irq_flow_handler_t handle_irq; #ifdef CONFIG_IRQ_PREFLOW_FASTEOI irq_preflow_handler_t preflow_handler; #endif struct irqaction *action; /* IRQ action list */ unsigned int status_use_accessors; unsigned int core_internal_state__do_not_mess_with_it; 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; struct irq_affinity_notify *affinity_notify; #ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_var_t pending_mask; #endif #endif unsigned long threads_oneshot; 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;
struct irq_chip //hardware interrupt chip descriptor中断控制器
struct irq_chip { const char *name; 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); void (*irq_cpu_online)(struct irq_data *data); void (*irq_cpu_offline)(struct irq_data *data); void (*irq_suspend)(struct irq_data *data); void (*irq_resume)(struct irq_data *data); void (*irq_pm_shutdown)(struct irq_data *data); void (*irq_print_chip)(struct irq_data *data, struct seq_file *p); unsigned long flags; /* Currently used only by UML, might disappear one day.*/ #ifdef CONFIG_IRQ_RELEASE_METHOD void (*release)(unsigned int irq, void *dev_id); #endif };request_irq().
request_thread_irq().//分配中断资源,使能中断线和中断处理函数。
irq_to_desc();//将中断号变成中断描述符指针,主要是通过1.radix_tree_lookup(&irq_desc_tree, irq)获得 2.irq_desc[irq]获得。两者的区别在是否配置CONFIG_SPARSE_IRQ
之后进行一系列的参数赋值。
最终调用_setup_irq()去注册中断处理函数。
对不同的irq flags做对应的处理
register_irq_proc();
register_handler_proc();
在init/main.c中的strat_kernel函数中有early_init_irq()和init_IRQ(),softirq_init().
init_IRQ().是cpu厂商自己编写的,不同的cpu代码不同。
先将每个中断向量号设置为noprobe。(irq_set_noprobe)
再调用硬件相关代码,mach_init_IRQ(),该函数值之前setup_arch()中的config_BSP()已经被注册回调函数,
void __init config_BSP(void) { printk("C-SKY Silan IVS2 Board\n"); mach_time_init = xxx_timer_init; mach_hwclk = xxx_hwclk; mach_init_IRQ = xxx_init_IRQ; mach_get_auto_irqno = xxx_get_auto_irqno; mach_reset = xxx_machine_restart; #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET mach_tick = xxx_tick; mach_gettimeoffset = xxx_timer_offset; #endif #ifdef CONFIG_CPU_USE_FIQ mach_init_FIQ = xxx_init_FIQ; #endif prom_meminit(); }
最后调用在arch/xxx_cpu/xxx_sys/irq.c下的xxx_init_IRQ()
irq_set_chip_and_handler(i, xxx_irq_chip, handle_level_irq).
参数中有一个函数指针handle_level_irq表示是电平中断流控.
void irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle, const char *name) { irq_set_chip(irq, chip); __irq_set_handler(irq, handle, 0, name); }
最终调用两个函数
irq_set_chip().将中断控制器irq_chip赋给irq_desc->irq_data.chip;
__irq_set_handler().将中断触发方式赋给irq_desc->handle_irq.
软中断更多的是在中断的退出阶段执行,以便达到更快的响应,加入守护进程机制,只是担心一旦有大量的软中断等待执行,会使得内核过长地留在中断上下文中。
softirq_init().中关键的是两个函数
open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
想要说明一点,tasklet是建立在软中断上的一种延迟执行机制,它的实现基于TASKLET_SOFTIRQ和HI_SOFTIRQ这两种软中断类型。
中断向量号在arcj/xxx_cpu/xxx_sys/include/mach/xxx_irq.h中