中断子系统(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);

        //1.只有设备的中断请求引脚(中断线)的电平发生跳变时(由高变低或者有低变高),才会发出中断请求,
        //因为跳变是一瞬间,而且不会像电平中断能保持住电平,所以处理不当就容易漏掉一次中断请求,
        //2.为了避免这种情况,屏蔽中断的时间必须越短越好。内核的开发者们显然意识到这一点,在正是处理中断前,
        //判断IRQ_INPROGRESS标志没有被设置的情况下,只是ack irq,并没有mask irq,以便复位设备的中断请求引脚,
        //3.在这之后的中断处理期间,另外的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);
                //向芯片屏蔽并确认此中断,设置APPENDING表示有中断等待处理
                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);
}

//  参考:
//      http://en.wikipedia.org/wiki/Interrupt
//      http://blog.youkuaiyun.com/xiaoxiaomuyu2010/article/details/12162599
//      http://blog.youkuaiyun.com/droidphone/article/details/7489756



//  执行驱动注册的中断处理例程
//  函数调用路径:
//      __do_IRQ->handle_IRQ_event
//      handle_level_irq->handle_IRQ_event
//      handle_edge_irq->handle_IRQ_event
//  函数主要任务:
//      1.遍历驱动注册的中断处理例程
//      2.关中断
3.1 fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
                struct irqaction *action)
{
    int ret, retval = 0, status = 0;

    //遍历所有已注册的irqaction
    do {
        //调用驱动注册的处理程序
        ret = action->handler(irq, action->dev_id, regs);
        if (ret == IRQ_HANDLED)
            status |= action->flags;
        retval |= ret;
        action = action->next;
    } while (action);
    //中断处理完毕,在中断返回路径上先关中断,iret会恢复eflags(恢复中断标志)
    local_irq_disable();
    return retval;
}


//  关于handle_IRQ_event末尾关中断的思考:
//      do_IRQ->...->handle_IRQ_event,关中断->ret_from_intr
//      do_IRQ->...->handle_IRQ_event,关中断->do_softirq 保存中断状态,关中断->...->do_softirq 恢复中断状态->ret_from_intr
//      在中断处理过程中防止丢失中断,因此开中断,允许中断嵌套;
//      在中断处理结束时,关中断,避免没必要的中断嵌套
//      当iret中断返回时,会自动恢复中断状态(eflags)。

//  参考:
//      http://www.unixresources.net/linux/clf/linuxK/archive/00/00/58/82/588249.html
//      http://blog.youkuaiyun.com/normalnotebook/article/details/1649770






转载 http://blog.youkuaiyun.com/nerdx/article/details/16105355#comments
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值