内核驱动:中断机制

A9处理器:EXYNOS4412

中断:硬件产生(总是需要硬件电路上的一根中断线):USB线是有VCC、GND、D+、D-四根线组成,所以是不能产生中断的;

中断产生的一般过程:中断源产生中断------>中断控制器(根据中断优先级)------>ARM处理器

对于4412处理器来说,搞清中断源和中断号的对应关系是一个很复杂的工作具体可参考arch/arm/mach-exynos/include/mach/irqs.h头文件

ARM平台查找中断号:

1.芯片内部外设:利用设备的名字在irqs.h中查找中断号;

2.芯片外部外设:设备的中断连接到4412的GPIO引脚,找到GPIO引脚的GPIO号,然后调用函数gpio_to_irq();

中断函数的注册:

1.需要包含头文件<mach/irqs.h>,如果要找4412内部设备的中断号,只能看文件<mach/irqs.h>

 

=======================================================

 

linux的中断处理机制

GPIO产生中断,将中断给GPIO控制器,GPIO控制器再将中断交给GIC处理,最后GIC交与cpu做处理:如下图:

 

mask:设备上级组织(GIC或者GPIO控制器)一般在中断到达的过程中屏蔽中断;同时,CPU也可以通过控制CPSR寄存器来屏蔽来自GIC的所有中断; 

中断处理机制

  当中断产生后,内核会保护当前进程的执行现场,并进入异常处理,通过异常像量表找到产生中断的中断号,并由中断号,在内核中找到中断号为100的irq_desc:主要用于描述中断信息,并执行中断处理函数(需要用户有注册行为)。用户在处理中断的过程中,首先要先写好中断处理函数,封装到iqaction并由iqaction想内核进行注册,从而是handler函数有效;

ps:

1.linux内核在启动时分配了一个irq_desc的数组,数组中共有NR_IRQS个成员。每个irq_desc中记录对应中断的各类信息,比如中断的处理函数,中断的发生次数等。irq_desc 结构体定义在<linux/irqdesc.h中>

2.irqaction定义在<linux/interrupt.h>每个irqaction用于封装一个中断处理函数。结构体由驱动人员负责分配。irqaction中包含中断号,中断处理函数指针,中断的执行标志,中断名等。

3.中断处理函数的返回值为 irq_handler_t类型;定义在<linux/interrupt.h>,如下:

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

中断处理函数。由驱动负责实现,记录在irqaction中。

irqreturn_t只有两个值,IRQ_NONE/IRQ_HANDLED。如果中断不是由本设备引起的,则返回IRQ_NONE,否则返回IRQ_HANDLED。
函数参数irq为中断号,void *为传递给中断处理函数的参数,对应irqaction->dev_id。

 

中断处理函数的编写与注册

驱动人员在设计中断处理函数时,要遵循的要求是:
(1)可嵌套不可重入
(2)不能睡眠
(3)如果硬件有中断的状态寄存器,软件要负责清除中断的标志位。一般来说,如果不清除标志位,设备无法再次产生中断

(4)中断处理函数的注册和注销

0.前提

  需要包含的头文件

  #include <linux/interrupt.h>
  #include <mach/irqs.h> //片内外设
  #include <linux/gpio.h> //片外外设
  #include <mach/gpio.h>

1.确定中断号

  正如刚才如上所述:如果是外部IO中断,那么我们可以通过GPIO号获取中断号:

  eg:#define KEY_IRQ     gpio_to_irq(gpio号);

2.中断处理函数编写

  中断处理函数的格式:

  eg:

  //参数1:int irq :中断号

  //参数2:void * dev_id :为传递给中断处理函数的参数

  //返回值:IRQ_NONE:中断不是由本设备引起的

                   IRQ_HANDLED:中断是由本设备引起的

  static irqreturn_t  key_service(int irq, void *dev_id)

  {
  //根据硬件要求完成相应工作
  ...
  return IRQ_HANDLED 或 IRQ_NONE;
  }

3.注册中断处理函数(必须检查返回值)

  u32 flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;//定义中断的标志:下降沿/上升沿/低电平/高电平触发中断

  int ret;
  ret = request_irq(  KEY_IRQ, /* 中断号 */
             key_service, /* 中断处理函数 */
             flags, /* 中断的标志 */
              "xxx", /* 中断处理函数的名字 */
              dev_id);/* dev_id为传给中断处理函数的参数,一般会设置为私有结构体的指针,非共享>中断可以为NULL */

  if (ret) {
    printk("Cannot register interrupt handler\n");
    return -1;
   }

4.注销中断处理函数 

  free_irq(irq, dev_id); //参数为中断号和dev_id。 dev_id一定要和request_irq中的最后一个参数一致

 

5.屏蔽中断

  1.可以人为关闭(mask)/打开某个中断:
    disable_irq(int irq);
    enable_irq(int irq);
    这两个函数是可以嵌套的,也就是说,如果对一个中断disable了3次,需要调用enable函数3次才能打开对该中断的屏蔽;

  2.还可以屏蔽本cpu的中断:
    local_irq_disable();
    local_irq_enable();

posted @ 2016-02-28 10:12  muzihuan  阅读(5941)  评论(0编辑  收藏  举报