Linux设备驱动之中断---介绍

中断

中断是cpu在执行过程中,出现了某些突发情况,cpu必须暂停当前的任务,去处理紧急的事件,处理结束后继续处理刚才暂停的任务。

分类:

从中断源看:

内部中断:来源于cpu内部,例如软件中断指令、溢出、除法错误等
外部中断:来源于cpu外部,外设提出请求

从是否可屏蔽看:

可屏蔽中断:中断被屏蔽后不再相应
不可屏蔽中断:NMI

从跳转入口看:

向量中断:cpu通常为不同的中断分配不同的中断入口地址,当检测到某个中断来临,自动跳转到该中断对应的地址执行
非向量中断:多个中断共享一个入口地址,中断响应后,要通过软件判断具体是哪个中断,软件中断较多。

irq_handler()
{
    int int_src = read_int_status();
    switch (int_src){
        case DEV_A:
            dev_a_handler();
            break;
        case DEV_B:
            DEV_b_handler();
            break;
        default:
            break;
    }
}

中断的arm架构介绍:

ARM多核处理中最常用的GIC(generic interrupt controller),支持3种类型中断:
1. SGI(Software generic interrupt):软件产生的中断,可以用于多核之间的通信,一个cpu可以通过写GIC的寄存器给另外一个cpu产生中断。
2. PPI(Private peripheral interrupt):某个CPU私有外设的中断,这类外设的中断只能发给绑定的cpu
3. SPI(Shared peripheral interrupt):共享外设中断,这类外设的中断可以绑定到任意的cpu

extern int irq_set_aff inity (unsigned int irq, const struct cpumask *m);
例如irq_set_aff inity(irq, cpumask_of(i)); 把中断绑定到cpui上,由这个cpu处理

linux中断处理程序架构:

设备的中断会打断内核进程的正常调度,要求中断处理函数要尽可能的短小,但是实际上中断处理函数可能需要很长时间,linux将中断处理函数分为上下部分。
1.中断上半部(Top Half) :处理比较紧急短小的事情,比如读取寄存器等。 清除中断标志以后就“登记中断”工作,意味着将中断下半部处理程序挂到下半部执行队列中。不可被中断打断
2.中断下半部(Bottom Half) :处理比较耗时的事情。可以被中断打断。

中断处理函数就是中断上半部
                       调度

——-中断——->中断上半部——->中断下半部

cat /proc/interrupt 查看中断信息

linux中断编程:

1. 申请中断

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)

                    irq :中断号
                    handler:中断处理函数
                        typedef irqreturn_t (*irq_handler_t)(int, void *);
                    flags:中断处理属性
                        触发方式:
                            #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
                            #define IRQF_TRIGGER_MASK   (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
                            #define IRQF_TRIGGER_PROBE  0x00000010
                        处理方式:
                            #define IRQF_SHARED     0x00000080          //表示多个设备共享中断
                            #define IRQF_PROBE_SHARED   0x00000100
                            #define __IRQF_TIMER        0x00000200
                            #define IRQF_PERCPU     0x00000400
                            #define IRQF_NOBALANCING    0x00000800
                            #define IRQF_IRQPOLL        0x00001000
                            #define IRQF_ONESHOT        0x00002000
                            #define IRQF_NO_SUSPEND     0x00004000
                            #define IRQF_FORCE_RESUME   0x00008000
                            #define IRQF_NO_THREAD      0x00010000
                            #define IRQF_EARLY_RESUME   0x00020000
                            #define IRQF_COND_SUSPEND   0x00040000
                    name:中断名称,在/proc/interrupt 中看到的名称
                    dev:是要传递给中断服务程序的私有数据,一般设置为中断设备的结构体或者指针NULL

                    return0           表示成功
                        -EINVAL     表示中断号无效或者处理函数指针为NULL
                        -EBUSY      表示中断已经被占用且不能共享
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
//devm_开头的api表示系统管理的资源,系统自动回收,不需要使用free_irq,就像java中的内存回收机制一样

2. 释放中断

void free_irq(unsigned int irq, void *dev_id)

3. 使能和屏蔽中断

屏蔽一个中断源

                    void disable_irq(int irq)           立刻返回
                    void disable_irq_nosync(int irq)    等待中断函数执行结束返回
                    void enable_irq(int irq)

屏蔽本cpu内所有中断

                    #define local_irq_save(flags) ...   //禁止中断前,将目前cpu的中断状态保存在flags中    flags为unsigned log
                    void local_irq_disable(void)        //直接禁止中断而不保存

对应恢复如下

                    #define local_irq_restore(flags) ...
                    void local_irq_enable(void)

下半部机制:

实现机制:tasklet 工作队列 软中断 线程化irq

tasklet

执行上下文:软中断
使用: 定义-关联-使用

1. void my_tasklet_func(unsigned long) //定义一个处理函数
2. DECLARE_TASKLET(my_tasklet, my_tasklet_func, data)  //定义一个tasklet结构体,与my_tasklet_func(data)函数关联
3.tasklet_schedule(&my_tasklet);

工作队列:

执行上下文:进程上下文,可睡眠、调度
使用: 定义-关联-使用

1.struct work_struct my_wq;          //定义一个工作队列
2.void my_wq_func(struct work_struct *work)  //定义一个处理函数
3.INIT_WORK(&my_wq, my_wq_func)     //绑定
4.schedule_work(&my_wq)             //使用工作队列

软中断:

执行上下文:软中断
使用:

softirq_atcion //结构体表示一个软中断
open_softirq() //注册软中断对应的处理函数
local_bh_disable()
local_bh_enable()
//内核中用于禁止和使能软中断以及tasklet底半步机制的函数

threaded_irq

int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)     //中断上下文
int devm_request_threaded_irq(struct device *dev, unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)    //进程上下文

//上半部函数 handler
//下半部函数 thread_fn

使用方法:当上半部返回 IRQ_WAKE_THREAD 时,内核调度会执行thread_fn函数

request_threaded_irq和devm_request_threaded_irq支持标记IRQF_ONESHOT
内核会帮助我们在中断上下文时屏蔽该中断号,在执行thread_fn的时候重新使能该中断号,对于在上半部无法清除中断的情况,IRQF_ONESHOT有特别的用处

中断共享:

linux支持多个设备共享一个中断线的情况。

  1. 共享中断的设备在申请中断时要加上 IRQF_SHARED标志
    1.1 该中断未被申请
    1.2 该中断已经被申请,但是被申请的都是以IRQF_SHARED为标志的

  2. 中断到来时,会编译执行共享此中断的所有中断处理函数,直到某个函数返回IRQ_HANDLED.
    在中断处理函数顶半部中,应根据硬件寄存器中的信息比对传入的dev_id参数,迅速判断是否为本设备的中断,如果不是,应该迅速返回IRQ_NONE.

接下来三篇文章讲一次介绍4种使用方法和示例代码

你可能感兴趣的:(linux,device,driver)