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)\n", 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!\n");
                    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\n", 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()分析完毕后,接下来开始编写中断方式的按键驱动

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员互动联盟

【专业技术】linux中驱动异步通知探秘

驱动程序运行在内核空间中,应用程序运行在用户空间中,两者是不能直接通信的。 但在实际应用中,在设备已经准备好的时候,我们希望通知用户程序设备已经ok,用户程序可...

2786
来自专栏Play & Scala 技术分享

原 荐 关于 Scala 隐式转换必读的一篇

2102
来自专栏Linyb极客之路

工作流引擎之activiti6实时流程图追踪

有接触过activiti的朋友可能知道,在activiti5.x的追踪流程节点查找,可以用

3052
来自专栏前端杂货铺

沙箱

简介   js中很容易出现全局变量污染的情况,全局变量需要依赖全局环境的命名空间,如果为了避免这种情况, 大多数采用多重命名空间的方式来定义变量,但是此种方式名...

2714
来自专栏Java学习之路

12 Spring框架 SpringDAO的事务管理

上一节我们说过Spring对DAO的两个支持分为两个知识点,一个是jdbc模板,另一个是事务管理。 事务是数据库中的概念,但是在一般情况下我们需要将事务提到业...

3167
来自专栏个人随笔

Java 面向对象三大特征之一:封装

面向对象三大特征之一:封装 概念: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法类实现对隐藏信息的操作和访问 封装的好处: 隐藏...

3387
来自专栏Albert陈凯

Hadoop数据分析平台实战——260用户数据ETL离线数据分析平台实战——260用户数据ETL

离线数据分析平台实战——260用户数据ETL ETL目标 解析我们收集的日志数据,将解析后的数据保存到hbase中。 这里选择hbase来存储数据的主要原因就...

3646
来自专栏BeJavaGod

如何限制用户在某一时间段多次访问接口

要知道,如今很多平台的接口都是可以同时被门户网站,手机端,移动浏览器访问,因为接口是通用的,而为了安全起见,有些接口都会设置一个门槛,那就是限制访问次数,也就是...

3226
来自专栏王磊的博客

动态类型var和dynamic和传统确定类型区别和效率

    伴随着vs2010的出现,c#4.0的诞生,与之而来的动态类型dynamic更是给net程序员们锦上添花,为自己的程序书写上带来了给大的便利。可到底怎么...

34011
来自专栏pangguoming

ES6 主要的新特性

本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用。 ES6(ECMAScript 6)是即将到来的新版本JavaSc...

4139

扫码关注云+社区