zoukankan      html  css  js  c++  java
  • 中断子系统5_电流层处理

    //  中断电流类型:
    //      边沿型(edge);
    //          通过电位变化触发中断(上升沿/下降沿),如果外设希望触发一个中断,它在irq line上发送一个脉冲,
    //          然后释放irq line恢复到inactive状态。cpu通过检测irq line上的脉冲来触发中断处理函数的执行。
    //      电平型(level):
    //          通过特定电位触发中断,如果外设希望触发一个中断,它会将irq line设置到active level(高电平/低电平),
    //          然后一直保持此irq line为active level,直到中断被处理。cpu通过周期性采样irq line的电平来触发中断处理函数的执行。
    
    //	在2.6.x版本后,中断描述符irq_desc->handle_irq封装对不同电流类型的处理。
    //		struct irq_desc
    //		{
    //			irq_flow_handler_t handle_irq;
    //			...
    //		}
    
    //  电流处理入口:
    //      系统中所有中断统一经过do_IRQ处理:
    //          1.irq_desc提供电流处理例程(irq_desc->handle_irq != NULL),则调用;
    //          2.否则,通过__do_IRQ处理所有电流类型。
    //      do_IRQ(regs)
    //      {   
    //          ....
    //          if(irq_desc->handle_irq)
    //          {
    //              irq_desc->handle_irq(irq, irq_desc);
    //          }else
    //          {
    //              __do_IRQ(irq, regs);
    //          }
    //          ....
    //      }
    //
    
    
    //	设置irq_desc的电流处理例程
    //		参数:
    //			typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc);
    1.1 static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handler)
    {
    	struct irq_desc *desc;
    	desc = irq_to_desc(irq);
    	desc->handle_irq = handler;
    }
    
    
    //	边沿型中断处理
    //  函数主要任务:
    //      1.检查中断是否被禁用,或者中断处理函数是否在运行过程中
    //          1.1 如果是,向芯片屏蔽并确认此中断,设置标志表示有待处理的中断,退出
    //      2.向芯片确认此次中断
    //      3.设置中断处理函数在运行中标志
    //      4.运行中断处理函数
    //      5.检查在运行中断处理函数过程中,是否有新中断(步骤1中会被设置)
    //          5.1 如果有,清除标志,转去步骤4
    //      6.清除中断处理函数在运行中标志,表示所有已经发生的中断都被处理完毕
    //      7.退出
     2.1 void handle_edge_irq(unsigned int irq, struct irq_desc *desc)
     {
            const unsigned int cpu = smp_processor_id();
     
            spin_lock(&desc->lock);
    
            desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
    
            //只有设备的中断请求引脚(中断线)的电平发生跳变时(由高变低或者有低变高),才会发出中断请求,
            //因为跳变是一瞬间,而且不会像电平中断能保持住电平,所以处理不当就特别容易漏掉一次中断请求,
            //为了避免这种情况,屏蔽中断的时间必须越短越好。内核的开发者们显然意识到这一点,在正是处理中断前,
            //判断IRQ_INPROGRESS标志没有被设置的情况下,只是ack irq,并没有mask irq,以便复位设备的中断请求引脚,
            //在这之后的中断处理期间,另外的cpu可以再次响应同一个irq请求,如果IRQ_INPROGRESS已经置位,
            //表明另一个CPU正在处理该irq的上一次请求,这种情况下,他只是简单地设置IRQ_PENDING标志,然后mask_ack_irq后退出,
            //中断请求交由原来的CPU继续处理。因为是mask_ack_irq,所以系统实际上只允许挂起一次中断。
            if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
                        !desc->action)) {
                    desc->status |= (IRQ_PENDING | IRQ_MASKED);
                    mask_ack_irq(desc, irq);
                    goto out_unlock;
            }
    
            //向芯片确认此irq
            desc->chip->ack(irq);
     
            //中断处理函数运行
            desc->status |= IRQ_INPROGRESS;
     
            do {
                    struct irqaction *action = desc->action;
                    irqreturn_t action_ret;
    
                    //如果没有中断处理函数,屏蔽此中断
                    if (unlikely(!action)) {
                            desc->chip->mask(irq);
                            goto out_unlock;
                    }
     
                    //处理中断期间,另一次请求可能由另一个cpu响应后挂起,所以在处理完本次请求后还要判断IRQ_PENDING标志,
                    //如果被置位,当前cpu要接着处理被另一个cpu“委托”的请求。内核在这里设置了一个循环来处理这种情况,
                    //直到IRQ_PENDING标志无效为止,而且因为另一个cpu在响应并挂起irq时,会mask irq,
                    //所以在循环中要再次unmask irq,以便另一个cpu可以再次响应并挂起irq。
                    if (unlikely((desc->status &
                                   (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
                                  (IRQ_PENDING | IRQ_MASKED))) {
                            //解除屏蔽,重新允许中断
                            desc->chip->unmask(irq);
                            desc->status &= ~IRQ_MASKED;
                    }
    
                    //处理在运行中断处理函数过程中发生的中断
                    desc->status &= ~IRQ_PENDING;
                    spin_unlock(&desc->lock);
                    //调用中断处理函数
                    action_ret = handle_IRQ_event(irq, action);
                    spin_lock(&desc->lock);
     
            } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
            //中断全部被处理
            desc->status &= ~IRQ_INPROGRESS;
     out_unlock:
            spin_unlock(&desc->lock);
    }
    
    //  电平型中断
    //      只要设备的中断请求引脚(中断线)保持在预设的触发电平,中断就会一直被请求,
    //        所以,为了避免同一中断被重复响应,必须在处理中断前先把mask irq,然后ack irq,
    //      以便复位设备的中断请求引脚,响应完成后再unmask irq。
    
    //  函数主要任务:
    //      1.向芯片屏蔽并确认此中断
    //      2.运行中断处理函数
    //      3.解除屏蔽
    //      4.退出
    2.2 void handle_level_irq(unsigned int irq, struct irq_desc *desc)
    {
            raw_spin_lock(&desc->lock);
            //确认并屏蔽此中断
            mask_ack_irq(desc);
    
            desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
            
            //没有中断处理函数,或者中断被禁止,退出
            if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
                    goto out_unlock;
            //调用中断处理函数
            handle_irq_event(desc);
            //解除屏蔽
            cond_unmask_irq(desc);
    out_unlock:
            raw_spin_unlock(&desc->lock);
    }
    //  虽然handle_level_irq对电平中断的流控进行了必要的处理,因为电平中断的特性:只要没有ack irq,中断线会一直有效,
    //  所以我们不会错过某次中断请求,但是驱动程序的开发人员如果对该过程理解不透彻,特别容易发生某次中断被多次处理的情况。
    //  特别是使用了中断线程(action->thread_fn)来响应中断的时候:通常mask_ack_irq只会清除中断控制器的pending状态,
    //  很多慢速设备(例如通过i2c或spi控制的设备)需要在中断线程中清除中断线的pending状态,但是未等到中断线程被调度执行的时候,
    //  handle_level_irq早就返回了,这时已经执行过unmask_irq,设备的中断线pending处于有效状态,中断控制器会再次发出中断请求,
    //  结果是设备的一次中断请求,产生了两次中断响应。要避免这种情况,最好的办法就是不要单独使用中断线程处理中断,
    //  而是要实现request_threaded_irq()的第二个参数irq_handler_t:handler,在handle回调中使用disable_irq()
    //  关闭该irq,然后在退出中断线程回调前再enable_irq()。
    
    
    
    
    //  参考:
    //      http://en.wikipedia.org/wiki/Interrupt
    //      http://blog.csdn.net/xiaoxiaomuyu2010/article/details/12162599
    //      http://blog.csdn.net/droidphone/article/details/7489756
    


  • 相关阅读:
    Windows Azure Storage (17) Azure Storage读取访问地域冗余(Read Access – Geo Redundant Storage, RA-GRS)
    SQL Azure (15) SQL Azure 新的规格
    Azure China (5) 管理Azure China Powershell
    Azure China (4) 管理Azure China Storage Account
    Azure China (3) 使用Visual Studio 2013证书发布Cloud Service至Azure China
    Azure China (2) Azure China管理界面初探
    Azure China (1) Azure公有云落地中国
    SQL Azure (14) 将云端SQL Azure中的数据库备份到本地SQL Server
    [New Portal]Windows Azure Virtual Machine (23) 使用Storage Space,提高Virtual Machine磁盘的IOPS
    Android数据库升级、降级、创建(onCreate() onUpgrade() onDowngrade())的注意点
  • 原文地址:https://www.cnblogs.com/pangblog/p/3424280.html
Copyright © 2011-2022 走看看