上篇文章我们从内核工程师的角度剖析了内核的外部中断,这节我们从bsp工程师的角度剖析一下外部中断。
外部中断驱动架构
hardirq 部分:
也就是我们在上节内容中看到的,调用bsp工程师注册的handler的部分,它的上下文在中断上下文中,所以不能做带有休眠的动作。
softirq 部分:
就是我们通常说的下半部分,通常作为bsp工程师要处理的详细例程的地方。
bsp工程师外部驱动模板
上半部调用模板
/*中断处理上半部分*/irqreturn_t xxx_interrupt(int irq, void *dev_id) { ... /* 中断处理下半部框架调用,常见的有下面几种: * 1. tasklet 它的执行额上下文是软中断,执行时机通常是上半部返回时 * 2. 工作队列,与tasklet类似,但是工作队列的执行上下文是内核线程,因此可以调度和睡眠。 * 3. softirq ,tasklet是基于softirq实现的,软中断属于原子上下文的一种,因此函数不允许睡眠。 * 4. 线程化的xxx_thread_fn, 它是基于内核线程创建的,因此可以做休眠动作。 */ ... /*这个返回值很重要,决定了线程化时传进去的irq_handler_t thread_fn 是否会被调用 * 可以参考上节课__handle_irq_event_percpu 函数中电信号处理完,调用完bsp工程师 *handler后,判断返回值的switch(第72行)里面执行的case*/ return irq_wake_thread; }//线程化模板时,传递给额需要创建的内核线程处理函数。即线程化的下半部。irqreturn_t xxx_thread_fn(int irq, void *dev_id){ .....} /*设备驱动加载模块*/ int __init xxx_init(void){ ... /*申请中断*/ result = request_irq(xxx_irq,xxx_interrupt,0,”xxx”,null); //或者使用线程化模板申请中断,我们稍后通过剖析源代码可知道 request_irq 与线程化的区别就是传递额xxx_thread_fn是否为空 //result = request_threaded_irq(xxx_irq,xxx_interrupt,xxx_thread_fn,irqflag”xxx”,null); .... return irq_handlerd; } /**设备驱动卸载模块*/ void __exit xxx_exit(void){ ... /*释放中断*/ free_irq(xxx_irq,xxx_interrupt); }下半部调用模板
//工作队列模板struct work_struct xxx_wq;void xxxx_do_work(struct work_struct *work);/**中断处理下半部*/void xxxx_do_work(struct work_struct *wkg){ ...}/*中断处理上半部分*/irqreturn_t xxx_interrupt(int irq, void *dev_id){ ... schedule_work(&xxx_wq); ...}/*设备驱动加载模块*/int __init xxx_init(void){ ... /*申请中断*/ result = request_irq(xxx_irq,xxx_interrupt,0,”xxx”,null); ..... /*初始化工作队列*/ init_work(&xxx_wq,xxx_do_work); return irq_handlerd; }//tasklet模板void xxx_do_tasklet(unsigned long);declare_tasklet(xxx_tasklet,xxx_do_tasklet,0);/**中断处理下半部*/void xxx_do_tasklet(unsigned long){ ...}/*中断处理上半部分*/irqreturn_t xxx_interrupt(int irq, void *dev_id){ ... tasklet_schedule(&xxx_tasklet); ...}isr安装过程
isr注册过程实质就是如何和我们上节内容中irq_descs[] 数组中对应的irqdesc中的action建立数据关系。
//include/linux/interrupt.h//线程化直接调用的方法,request_irq 也是封装了该方法,extern int __must_check request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn,unsigned long flags, const char *name, void *dev);//driver 上半部注册函数, 通过该函数,驱动程序中安装一个设备中断服务例程。//handler就是我们通常说的isr例程。isr由驱动程序实现,即bsp工程师实现。static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev){ //封装了request_threaded_irq,内核线程函数为null return request_threaded_irq(irq, handler, null, flags, name, dev);}//irq/manage.cint request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id){ .... desc = irq_to_desc(irq);//关键函数,通过irq查询irq_desc, 就是上节内容中看到的内核中维护irq_desc数组 .... if (!handler) { if (!thread_fn) return -einval; //当为线程化时,如果上半部handler为null时,系统会默认给一个函数,返回的值就是irq_wake_thread,调用起线程化的下半部内核函数 handler = irq_default_primary_handler; } ... action = kzalloc(sizeof(struct irqaction), gfp_kernel); .... /*通过request_irq api的,这个默认为null, 如果是线程化,这个值可能不为null,handler的返回值会决定thread_fn是否会调用到,参见上一节*soc视角中__handle_irq_event_percpu中switch case irq_wake_thread的处理*/ action- >handler = handler; action- >thread_fn = thread_fn; .... /*构建的关键*/ retval = __setup_irq(irq, desc, action); ....}//irq/manage.cstatic int__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new){ ...... //flag 等各种检查合法性 /*线程化时模板中,传入的thread_fn, 这里创建内核thread, *从判断条件可见,如果是共享中断,只会在第一注册的函数里面创建一次内核线程 */ if (new- >thread_fn && !nested) { ret = setup_irq_thread(new, irq, false); if (ret) goto out_mput; if (new- >secondary) { ret = setup_irq_thread(new- >secondary, irq, true); if (ret) goto out_thread; } } if (!desc- >action) {//第一次安装,需要申请resources ret = irq_request_resources(desc); if (ret) { pr_err(failed to request resources for %s (irq %d) on irqchip %s\\n, new- >name, irq, desc- >irq_data.chip- >name); goto out_bus_unlock; } } old_ptr = &desc- >action; /*添加到对应的irqaction*/ old = *old_ptr; if (old) { /* 多个设备共享中断*/ .... /*共享中断的各种限制性检查,与共享中断中其它设置需要保持一致*/ /*在内核工程师角度我们分析的时候: *__handle_irq_event_percpu 处理具体的信号函数的时候会有一个irqaction链表的遍历,这个遍历就是我们加入的共响中断的情形 */ do { /** or all existing action- >thread_mask bits, * so we can find the next zero bit for this new action. */ thread_mask |= old- >thread_mask; old_ptr = &old- >next; old = *old_ptr; } while (old); } /*将当前的irqaction加入到对应irq_descs[]数组当中对应当中.至此中断控制器的__handle_irq_event_percpu 中对应的handler数据对接完成。*/ *old_ptr = new; ...}
为什么在新一代双向OBC设计中选择SiC而非Si?
如何对工业平板电脑进行维护
中兴奋发图强 坚决自主开发研发5nm芯片
新封装理念:采用紧凑式SIP的QFN封装
九号公司:全地形车市场需求仍将保持增长
Linux内核外部中断解析(下)
支持GSM的iTotal Control灶具,用短信、网络即可完成远程控制
白云机场“5G+智慧机场”脱颖而出,全力打造世界一流航空枢纽
欧菲科技发布关于媒体报道的澄清公告
磁共振对人体有害吗_磁共振对孕妇有影响吗
聚焦云栖大会黑科技 电子商务消亡后如何飞天
英特尔今年资本支出 超越三星台积
自动酶标仪是什么,它的工作原理是怎样的
分析锐龙 4800h与酷睿 10750h在36款游戏中的表现
全球互联网投资活跃度明显回落,中国互联网投融资整体保持平稳
制造业正面临“三难”人才流失是制造业企业面临的共性问题
【洞见芯趋势】实现手机解锁的生物识别技术,到底有几种?
东帝汶副总理一行莅临洲明参观指导 了解洲明科技LED创意显示等产品
Oracle 云基础设施 (OCI) 选择NVIDIA BlueField数据中心加速平台
粮食水分测定仪使用说明和效果分析