linux内核驱动中断编程的接口使用方法

1.中断顶半部,中断底半部分概念

中断顶半部:响应中断,启动底半部(作用相当于裸机设置标志 )。
                    一般用来做响应,以及耗时短的,比较紧急的事件,不太紧急的事件就把它拆出来写在中断底半部。
                                   
中断底半部:发生中断时应该执行主体代码。耗时的代码。这部分代码会在适合时候,被内核执行。
只要延后执行不会出问题的代码都 可以放在中断底半部分处理。

2. 中断相关内核 API 接口。

 中断注册函数: 这个函数一旦注册一中断,其实已经默认使能该中断。
 int   request_irq(unsigned int irq,             //中断编号
                            irq_handler_t handler,     //指向中断服务函数的指针
                            unsigned long flags,      //该中断的特征,标志(如触发边沿等)
                            const char *name,         //中断名,随便。
                            void *dev)                      //该中断惟一的标识指针,只要是惟一就可以了。

功能:注册一个中断

参数:

irq:中断编号    ,不是看芯片手册 ,而是由linux内核定义好的 中断号,通常使用宏来定义。

不同的芯片中断数量,不一样,对应的外设也不相同,所以中断号定义一般是存放的具体芯片相关的目录中。一般定义一般在:arch\构架\mach-芯片型号\include\mach\Irqs.h

     部分定义:
    #define EXYNOS4_IRQ_EINT0        IRQ_SPI(16)
    #define EXYNOS4_IRQ_EINT1        IRQ_SPI(17)
    #define EXYNOS4_IRQ_EINT2        IRQ_SPI(18)
    #define EXYNOS4_IRQ_EINT3        IRQ_SPI(19)
    #define EXYNOS4_IRQ_EINT4        IRQ_SPI(20)

对于外部中断:一般不直接使用上面的EXYNOS4_IRQ_EINTX 宏,因为外部中断取得中断号一般使用一个 gpio_to_irq 这个函数取得它对应中断号。

要编写中断编程,使用定义 好中断号需要包含#include头文件。

handler:处理者,就是中断服务函数,是一个函数指针。是一个指向 返回值类型为 irqreturn_t (Irqreturn.h (include\linux)),

/**
* enum irqreturn
* @IRQ_NONE                表示中断没有被处理,比较少用,只会在共享中断中出现 
* @IRQ_HANDLED        表示中断被正确处理了,常用。
* @IRQ_WAKE_THREAD    表示去唤醒中断处理者的线程。比较少用。
*/
enum irqreturn {
    IRQ_NONE        = (0 << 0),
    IRQ_HANDLED        = (1 << 0),
    IRQ_WAKE_THREAD        = (1 << 1),
}; 有两形式参数,分别为int和void*的函数指针。

typedef irqreturn_t (*irq_handler_t)(int, void *);

补全参数:typedef irqreturn_t (*irq_handler_t)(int irq, void *dev);

中断服务调用时候,传递的实际参数就是 request_irq 时的第1个参数(irq)和最后一个参数(dev)。

所在,中断服务程序模板:                
irqreturn_t  isr_name(int irq,void *dev)
{
     //只会共享中断中出现下面的判断。一般应用中比较少使用共享中断。
     if(不是本设备产生中断信号)
                  return     IRQ_NONE;
        .....

        return     IRQ_HANDLED;
}

flags: 中断标志,特征,(如触发边沿等),其值由内核源码定义。只能使用内核源码中定义好的宏。

定义在Interrupt.h (include\linux)    
#define IRQF_TRIGGER_NONE    0x00000000          //没有设置触发边沿
#define IRQF_TRIGGER_RISING    0x00000001     //设置触发边沿为上升沿
#define IRQF_TRIGGER_FALLING    0x00000002     //设置触发边沿为下降沿
#define IRQF_TRIGGER_HIGH    0x00000004         //设置触发方式 为高电平
#define IRQF_TRIGGER_LOW    0x00000008         //设置触发方式 为低电平

以上还有别名:
   IRQ_TYPE_NONE                对应IRQF_TRIGGER_NONE
 * IRQ_TYPE_EDGE_RISING        - rising edge triggered
 * IRQ_TYPE_EDGE_FALLING    - falling edge triggered
 * IRQ_TYPE_EDGE_BOTH        - rising and falling edge triggered
 * IRQ_TYPE_LEVEL_HIGH        - high level triggered
 * IRQ_TYPE_LEVEL_LOW        - low level triggered

   组合:
    IRQ_TYPE_EDGE_BOTH    = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
    IRQ_TYPE_LEVEL_MASK    = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)

以下的标志仅用于外部中断,可以组合使用(只要硬件支持,不冲突,可使用  |  符号组合,如 IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING)。

以下标志可以用于外部中断和内部中断
#define IRQF_DISABLED                    0x00000020  //指明该中断是独占中断,只能注册一次
#define IRQF_SAMPLE_RANDOM    0x00000040 //对内核产生产生随机数有帮助,这个值实际中没太多价值。
#define IRQF_SHARED                         0x00000080  //指明该中断是共享中断,可以注册多次

name:中断名,    随便。用来给内核记录下这个号已经被使用了。注册后会在,会产生  /proc/irq/中断名/  目录,目录中会有一些和中断相关的文件。

dev:在内核所有注册的中断函数的dev参数,代表该中断惟一的标识,只要是惟一就可以了。

1)如果 flags 指定为共享中断(IRQF_SHARED),则不能为NULL,并且保证惟一性。
     很多人认为指定为共享中断时,dev 用来标识发生中断时具体是哪一个中断.
     实际上,这个dev并不具体标识具体 是哪一个中断的能力。这个参数真正的用途是用来注销共享中断中的具体哪一个中断。
     
     Linux下共享中断,只要注册了,当发生中断,会把所关联的中断服务函数运行一次。
     
     当注销一个共享中断时候,
     
2)如果 flags 指定为独占中断(IRQF_DISABLED),此参数可以是NULL

    Linux 下共享中断的意义:可以多次调用  request_irq 注册同一个中断号。
    Linux 下独占中断的意义:同一个中断号只能被request_irq  注册一次。


注销函数:void free_irq(unsigned int irq,void * dev_id)

irq: 要注销的中断号

dev_id:其实就是要 注册时候使用的dev参数,在共享中断必不可少。不能传递NULL。

为了防止在注销时同时发生中断,调用时候,先禁止中断。


禁止中断:void disable_irq_nosync(unsigned int irq);

void disable_irq(unsigned int irq);

参数:irq,要禁止的中断对应 的编号 

注意:在中断服务程序中不能使用 disable_irq 这个函数,否则内核崩溃,可以使用 disable_irq_nosync。

disable_irq:函数调用后,函数不会马上返回,而等待中断程序执行完成才返回,在中断调用会导致死锁。
disable_irq_nosync:调用后,函数马上返回。


使能中断:
void enable_irq(unsigned int irq);
参数:irq,要使能的中断对应 的编号 


总结:
中断编程步骤:
1)看硬件原理图,确定使用的中断
2)确定功能编写中断服务函数
3)在适当的地方调用 request_irq注册中断
4)知相反的地方调用 free_irq 注销中断(先disable_irq或disable_irq_nosync)
补充:如果在注册过程没有完成整个工作流程,也需要注销已经注册的中断。

你可能感兴趣的:(linux驱动)