• 6.分析request_irq和free_irq函数如何注册注销中断(详解)


    上一节讲了如何实现运行中断,这些都是系统给做好的,当我们想自己写个中断处理程序,去执行自己的代码,就需要写irq_desc->action->handler,然后通过request_irq()来向内核申请注册中断

    本节目标:

         分析request_irq()如何申请注册中断,free_irq()如何注销中断

    1.request_irq()位于kernel/irq/ manage .c,函数原型如下:

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)

    参数说明:

    unsigned int  irq:为要注册中断服务函数的中断号,比如外部中断0就是16,定义在mach/irqs.h

    irq_handler_t  handler:为要注册的中断服务函数,就是(irq_desc+ irq )->action->handler

    unsigned long  irqflags: 触发中断的参数,比如边沿触发, 定义在linux/interrupt.h。         

    const char  *devname:中断程序的名字,使用cat /proc/interrupt 可以查看中断程序名字

    void  *dev_id:传入中断处理程序的参数,注册共享中断时不能为NULL,因为卸载时需要这个做参数,避免卸载其它中断服务函数

    1.1request_irq代码如下:

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
    {
           struct irqaction *action;
           ... ...
           action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);  //注册irqaction结构体类型的action
           if (!action)
                       return -ENOMEM;
    
    /* 将带进来的参数赋给action   */
             action->handler = handler;     
             action->flags = irqflags;
             cpus_clear(action->mask);
             action->name = devname;
             action->next = NULL;
             action->dev_id = dev_id;
    
             select_smp_affinity(irq);
         ... ...
             retval = setup_irq(irq, action);   // 进入setup_irq(irq, action),设置irq_ desc[irq]->action
       
    if (retval) kfree(action); return retval; }

    从上面分析,request_irq()函数主要注册了一个irqaction型action,然后把参数都赋给这个action,最后进入setup_irq(irq, action)设置irq_ desc[irq]->action 

    1.2我们来看看setup_irq(irq, action)如何设置irq_ desc[irq]->action的:

    int setup_irq(unsigned int irq, struct irqaction *new)
    {
            struct irq_desc *desc = irq_desc + irq;   //根据中断号找到irq_ desc[irq]
            ... ...
            p = &desc->action;                 //指向desc->action
            old = *p;
             if (old) {                 //判断action是否为空
                        /*判断这个中断是否支持共享 (IRQF_SHARED)*/
                       if (!((old->flags & new->flags) & IRQF_SHARED) ||
                           ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
                                old_name = old->name;
                                goto mismatch;                  //不支持,则跳转
                       }
    
    #if defined(CONFIG_IRQ_PER_CPU)
                       /* All handlers must agree on per-cpuness */
                       if ((old->flags & IRQF_PERCPU) !=
                           (new->flags & IRQF_PERCPU))
                                goto mismatch;
    #endif
    
                       /*找到action链表尾处,后面用于添加 新的中断服务函数(*new) */
                       do {
                                p = &old->next;
                                old = *p;
                       } while (old);
                       shared = 1;        //表示该中断支持共享,添加新的action,否则直接赋值新的action
             }
    
             *p = new;             //指向新的action
    
     ... ...
    
             if (!shared) {                  //若该中断不支持共享
                       irq_chip_set_defaults(desc->chip);    //更新desc->chip,将为空的成员设置默认值       
    
    #if defined(CONFIG_IRQ_PER_CPU)
                         if (new->flags & IRQF_PERCPU)
                                  desc->status |= IRQ_PER_CPU;
    #endif
    
                       /* Setup the type (level, edge polarity) if configured: */
                       if (new->flags & IRQF_TRIGGER_MASK) {
                         if (desc->chip && desc->chip->set_type)        // desc->chip->set_type设置为中断引脚
                              desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);
                                else
                                         printk(KERN_WARNING "No IRQF_TRIGGER set_type "
    
                                                "function for IRQ %d (%s)
    ", irq,
    
                                                desc->chip ? desc->chip->name :
    
                                                "unknown");
                       } else
                                compat_irq_chip_set_default_handler(desc);
    
                       desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
                                           IRQ_INPROGRESS);
    
     
                       if (!(desc->status & IRQ_NOAUTOEN)) {
                                desc->depth = 0;
                                desc->status &= ~IRQ_DISABLED;
                                if (desc->chip->startup)
                                         desc->chip->startup(irq);     //开启中断
                                else
                                         desc->chip->enable(irq);     //使能中断
    
                       } else
    
                               /* Undo nested disables: */
                                desc->depth = 1;
             }

    从上面可以看出setup_irq(irq, action)主要是将action中断服务函数放在irq_ desc[irq]->action中,

    然后设置中断引脚:   

    desc->chip->set_type(irq,new->flags & IRQF_TRIGGER_MASK);

    最后[开启/使能]中断:

     desc->chip->[startup(irq) /enable(irq)];     //[开启/使能]中断

    我们以外部中断0的desc[16]->chip->set_type为例,来看看它是如何初始化中断引脚的:

    s3c_irqext_type(unsigned int irq, unsigned int type)
    {
             void __iomem *extint_reg;
             void __iomem *gpcon_reg;
             unsigned long gpcon_offset, extint_offset;
             unsigned long newvalue = 0, value;
           if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))    //找到寄存器
             {
                       gpcon_reg = S3C2410_GPFCON;      
                       extint_reg = S3C24XX_EXTINT0;      // EXTINT0对应中断0~中断7
                       gpcon_offset = (irq - IRQ_EINT0) * 2;    //找到gpcon寄存器的相应位偏移量
                       extint_offset = (irq - IRQ_EINT0) * 4;    //找到extint寄存器的相应位偏移量
             }
        else if(... ...)                    //找到其它的EINT4~23的寄存器
    
    /*将GPIO引脚设为中断引脚*/
    value = __raw_readl(gpcon_reg);  
    value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);  //相应位设置0x02
    switch (type)          //设置EXTINT0中断模式
    {
    case IRQT_NOEDGE:            //未指定的中断模式
                        printk(KERN_WARNING "No edge setting!
    ");
                        break; 
    
               case IRQT_RISING:           //上升沿触发,设置EXTINT0相应位为0x04
                        newvalue = S3C2410_EXTINT_RISEEDGE;
                        break;
    
               case IRQT_FALLING:     //下降沿触发,设置EXTINT0相应位为0x02
                        newvalue = S3C2410_EXTINT_FALLEDGE;
                        break;
     
               case IRQT_BOTHEDGE:  //双边沿触发,设置EXTINT0相应位为0x06
                        newvalue = S3C2410_EXTINT_BOTHEDGE;
                        break;
    
               case IRQT_LOW:                   //低电平触发,设置EXTINT0相应位为0x00
                        newvalue = S3C2410_EXTINT_LOWLEV;
                        break;
    
               case IRQT_HIGH:                 //高电平触发,设置EXTINT0相应位为0x01
                        newvalue = S3C2410_EXTINT_HILEV;
                        break;
               default:          
    }
    
    /*更新EXTINT0相应位*/
    value = __raw_readl(extint_reg);
    value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);  //相应位设置
    __raw_writel(value, extint_reg);    //向extint_reg写入value值
    return 0;
    }

    通过上面分析,就是将action->flags带入到desc[16]->chip->set_type里面,根据不同的中断来设置寄存器模式

    2.request_irq()是注册中断,同样的卸载中断的函数是free_irq()

    free_irq()也位于kernel/irq/ manage .c,函数原型如下:

    free_irq(unsigned int irq, void *dev_id);

    参数说明:

    unsigned int  irq:要卸载的中断号

    void  *dev_id:这个是要卸载的中断action下的哪个服务函数,

    2.1 free_irq()代码如下:

    void free_irq(unsigned int irq, void *dev_id)
    {
       struct irq_desc *desc;
       struct irqaction **p;
       unsigned long flags;
       irqreturn_t (*handler)(int, void *) = NULL;
     
       WARN_ON(in_interrupt());
       if (irq >= NR_IRQS)
              return;
    
       desc = irq_desc + irq;                //根据中断号,找到数组
       spin_lock_irqsave(&desc->lock, flags);
       p = &desc->action;          //p指向中断里的action链表
    
       for (;;) {
            struct irqaction *action = *p;
    
              if (action) {        //在action链表中找到与参数dev_id相等的中断服务函数
                    struct irqaction **pp = p;
                    p = &action->next;       
                    if (action->dev_id != dev_id)    //直到找dev_id才执行下面,进行卸载
                      continue;          
                    *pp = action->next;      //指向下个action成员,将当前的action释放掉
                    #ifdef CONFIG_IRQ_RELEASE_METHOD
                       if (desc->chip->release)   //执行chip->release释放中断服务函数相关的东西
                             desc->chip->release(irq, dev_id);
                    #endif
              if (!desc->action) {   //判断当前action成员是否为空,表示没有中断服务函数
                           desc->status |= IRQ_DISABLED;
                           if (desc->chip->shutdown)       //执行chip->shutdown关闭中断
                  desc->chip->shutdown(irq);
                     else                          //执行chip-> disable禁止中断
                   desc->chip->disable(irq);
                                        }
    
                      spin_unlock_irqrestore(&desc->lock, flags);
                      unregister_handler_proc(irq, action);
                synchronize_irq(irq);
                if (action->flags & IRQF_SHARED)
                  handler = action->handler;
                    kfree(action);
                     return;
    
                   }
    
       printk(KERN_ERR "Trying to free already-free IRQ %d
    ", irq);//没有找到要卸载的action成员
    
       spin_unlock_irqrestore(&desc->lock, flags);
    
       return;
    
           }
    
    #ifdef CONFIG_DEBUG_SHIRQ
             if (handler) {
    
                       /*
    
                        * It's a shared IRQ -- the driver ought to be prepared for it
    
                        * to happen even now it's being freed, so let's make sure....
    
                        * We do this after actually deregistering it, to make sure that
    
                        * a 'real' IRQ doesn't run in parallel with our fake
    
                        */
                       handler(irq, dev_id);
    
             }
    #endif
    }

    从上面分析,free_irq()函数主要通过irq和dev_id来找要释放的中断action

    若释放的中断action不是共享的中断(为空),则执行:

    *pp = action->next;      //指向下个action成员,将当前的action释放掉
    desc->chip->release(irq, dev_id);    //执行chip->release释放中断服务函数相关的东西
    
    desc->status |= IRQ_DISABLED;            //设置desc[irq]->status标志位
    desc->chip->[shutdown(irq)/ desible(irq)];    //关闭/禁止中断

    若释放的中断action是共享的中断(还有其它中断服务函数)的话就只执行:

    *pp = action->next;      //指向下个action成员,将当前的action释放掉
    desc->chip->release(irq, dev_id);    //执行chip->release释放中断服务函数相关的东西

    request_irq()和free_irq()分析完毕后,接下来开始编写中断方式的按键驱动

     

  • 相关阅读:
    多测师杭州拱墅校区肖sir_高级金牌讲师_项目讲解讲解和注意方式
    多测师杭州拱墅校区肖sir_高级金牌讲师_redis(参考)
    多测师杭州拱墅校区肖sir_高级金牌讲师_测试环境搭建问题
    【ArangoDb踩坑】新增Edge类型数据,from和to为document的_id
    firmware file rtl_bt/rtl8761b_fw.bin not found
    非递归前序遍历
    ELK & EFK
    边缘计算 controller提供的功能
    oran code go through
    hugepages configure
  • 原文地址:https://www.cnblogs.com/lifexy/p/7506613.html
Copyright © 2020-2023  润新知