字符设备驱动笔记——中断方式按键驱动之linux中断处理结构(五)

一、单片机下的中断处理
  1)分辨是哪一个中断
  2)调用处理函数
  3)清中断
二、linux下的中断处理

1)/arch/arm/kernel/irq.c
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc = irq_desc + irq;

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (irq >= NR_IRQS)
        desc = &bad_irq_desc;

    irq_enter();

    desc_handle_irq(irq, desc);

    /* AT91 specific workaround */
    irq_finish(irq);

    irq_exit();
    set_irq_regs(old_regs);
}

2)/kernel/irq/handle.c
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

3)/kernel/irq/chip.c
__set_irq_handler{
    ...
    desc->handle_irq = handle;
    ...
}

4)/include/linux/irq.h
static inline void
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
    __set_irq_handler(irq, handle, 0, NULL);
}

5)/arch/arm/plat-s3c24xx/irq.c
void __init s3c24xx_init_irq(void)

6)/arch/arm/plat-s3c24xx/irq.c
set_irq_handler(irqno, handle_edge_irq);

7)/kernel/irq/chip.c
  调用4)
  调用3)
8)在s3c24xx_init_irq这个函数里面,初始化irq_desc结构体
struct irq_desc {
    irq_flow_handler_t    handle_irq;        //set_irq_handler(irqno, handle_edge_irq);
    struct irq_chip        *chip;               //set_irq_chip(irqno, &s3c_irq_eint0t4);
    struct msi_desc        *msi_desc;
    void            *handler_data;
    void            *chip_data;
    struct irqaction    *action;    /* IRQ action list */
    unsigned int        status;        /* IRQ status */

....
9)/kernel/irq/chip.c
分析handle_edge_irq:
void fastcall
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);

    /*
     * If we're currently running this IRQ, or its disabled,
     * we shouldn't process the IRQ. Mark it pending, handle
     * the necessary masking and go out
     */
    if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
            !desc->action)) {
        desc->status |= (IRQ_PENDING | IRQ_MASKED);
        mask_ack_irq(desc, irq);
        goto out_unlock;
    }

    kstat_cpu(cpu).irqs[irq]++;            //发生中断的次数

    /* Start handling the irq */        //开始处理中断
    desc->chip->ack(irq);        //desc->chip chip是之前初始化的s3c_irqext_chip

    /* Mark the IRQ currently in progress.*/
    desc->status |= IRQ_INPROGRESS;

    do {
        struct irqaction *action = desc->action;
        irqreturn_t action_ret;

        if (unlikely(!action)) {    //判断链表是否为空
            desc->chip->mask(irq);
            goto out_unlock;
        }

        /*
         * When another irq arrived while we were handling
         * one, we could have masked the irq.
         * Renable it, if it was not disabled in meantime.
         */
        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);//真正的处理过程
        if (!noirqdebug)
            note_interrupt(irq, desc, action_ret);
        ......
}

10)
/arch/arm/plat-s3c24xx/irq.c
s3c_irqext_chip中的.ack
static struct irq_chip s3c_irqext_chip = {
    .name        = "s3c-ext",
    .mask        = s3c_irqext_mask,
    .unmask        = s3c_irqext_unmask,
    .ack        = s3c_irqext_ack,
    .set_type    = s3c_irqext_type,
    .set_wake    = s3c_irqext_wake
};
s3c_irqext_ack分析:
static void
s3c_irqext_ack(unsigned int irqno)
{
    unsigned long req;
    unsigned long bit;
    unsigned long mask;

    bit = 1UL << (irqno - EXTINT_OFF);

    mask = __raw_readl(S3C24XX_EINTMASK);

    __raw_writel(bit, S3C24XX_EINTPEND);

    req = __raw_readl(S3C24XX_EINTPEND);
    req &= ~mask;

    /* not sure if we should be acking the parent irq... */

    if (irqno <= IRQ_EINT7 ) {
        if ((req & 0xf0) == 0)
            s3c_irq_ack(IRQ_EINT4t7);
    } else {
        if ((req >> 8) == 0)
            s3c_irq_ack(IRQ_EINT8t23);
    }
}

static inline void
s3c_irq_ack(unsigned int irqno)
{
    unsigned long bitval = 1UL << (irqno - IRQ_EINT0);

    __raw_writel(bitval, S3C2410_SRCPND);
    __raw_writel(bitval, S3C2410_INTPND);
}

①desc->chip->ack(irq);//上面是清理中断
============================================================
②handle_edge_irq取出action链表中的成员,执行action->handler

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
    irqreturn_t ret, retval = IRQ_NONE;
    unsigned int status = 0;

    handle_dynamic_tick(action);

    if (!(action->flags & IRQF_DISABLED))
        local_irq_enable_in_hardirq();

    do {
        ret = action->handler(irq, action->dev_id);
        if (ret == IRQ_HANDLED)
            status |= action->flags;
        retval |= ret;
        action = action->next;
    } while (action);

    if (status & IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);
    local_irq_disable();

    return retval;
}

总结:
按下按键
1>cup进入异常处理模式
    b vector_irq + ...
2>__irq_user
3>asm_do_IRQ
4>irq_des[irq]以中断号为下标取出一项 ->handle_irq
  struct irq_desc {
    irq_flow_handler_t    handle_irq;        //set_irq_handler(irqno, handle_edge_irq);
                                        //handle_edge_irq取出action链表中的成员,
                                        //执行action->handler(自定义实现)
    struct irq_chip        *chip;               //set_irq_chip(irqno, &s3c_irq_eint0t4);
    //芯片相关的一些操作
    ...
    }
5>handle_irq = handle_edge_irq
6>handle_edge_irq的操作:
  (1)desc->chip->ack(irq);
  (2)handle_IRQ_event(irq, action);
  
===========================================================================================
自定义异常处理函数action->handler,注册进内核

request_irq


1./kernel/irq/manage.c
int request_irq(unsigned int irq, irq_handler_t handler,
        unsigned long irqflags, const char *devname, void *dev_id)
{
    //1)分配了一个结构,结构中的成员指向传递进来的参数
    action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
    //2)设置irq
    retval = setup_irq(irq, action);
}

2.setup_irq()函数分析:
  1)irq_des[irq] 已irq为下标找到数组项
  2)在irq_des[irq]链表里面加入传递进来的参数action
  3)desc->chip->settype()设置为中断引脚                        
  4)desc->chip->startup / desc->chip->enable 使能中断
  
free_irq(irq, dev_id)
  1)从链表中除去
  2)禁止中断
  
  
  
  
  
  
  
  
原文地址:https://www.cnblogs.com/liulipeng/p/3331424.html