Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145123
  • 博文数量: 89
  • 博客积分: 71
  • 博客等级: 民兵
  • 技术积分: 490
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-19 19:39
文章分类
文章存档

2013年(3)

2012年(86)

分类:

2012-11-28 18:00:37

原文地址:第10章 中断处理 作者:iibull

  1. 安装中断

<linux/interrupt.h>, 实现中断注册接口:

int request_irq(unsigned int irq,
                irqreturn_t (*handler)(int, void *, struct pt_regs *),
                unsigned long flags,
                const char *dev_name,
                void *dev_id);

注册的中断信息可以在 /proc/interrupts 中查看

void free_irq(unsigned int irq, void *dev_id);

从 request_irq 返回给请求函数的返回值是 0 指示成功, 或是一个负的错误码. 函数返回 -EBUSY 来指示另一个驱动已经使用请求的中断. 函数的参数如下:

unsigned int irq
    请求的中断号

irqreturn_t (*handler)
    安装的处理函数指针. 

unsigned long flags
    一个与中断管理相关的选项的位掩码(后面描述).


const char *dev_name
    这个传递给 request_irq 的字串用在 /proc/interrupts 来显示中断的拥有者


void *dev_id
    用作共享中断线的指针. 它是一个独特的标识, 用在当释放中断线时以及可能还被驱动用来指向它自己的私有数据区(来标识哪个设备在中断). 如果中断没有被共享, dev_id 可以设置为 NULL, 但是使用这个项指向设备结构不管如何是个好主意. 我们将在"实现一个处理"一节中看到 dev_id 的一个实际应用.

flags 中可以设置的位如下:
SA_INTERRUPT
    当置位了, 这表示一个"快速"中断处理. 即在当前处理器上禁止中断来执行
 
    SA_INTERRUPT 只是打算用在几个, 特殊的情况例如时钟中断, 其他情况不建议使用它。
SA_SHIRQ
    这个位表示中断可以在设备间共享.

中断处理可以在驱动初始化时或者在设备第一次打开时安装. 但不建议在模块的初始化函数中安装

调用 request_irq 的正确位置是当设备第一次打开时, 在硬件被指示来产生中断前. 调用 free_irq 的位置是设备最后一次被关闭时, 在硬件被告知不要再中断处理器之后. 这个技术的缺点是你需要保持一个每设备的打开计数, 以便于你知道什么时候中断可以被禁止.



实现一个中断处理


irqreturn_t short_interrupt(int irq, void *dev_id)
{
   return IRQ_HANDLED;
}
中断函数有一些处理上的限制。
  • 不能传递数据到或者从用户空间, 因为它不在进程上下文执行.
  • 不能做任何可能睡眠的事情, 例如调用 wait_event, 使用除 GFP_ATOMIC 之外任何东西来分配内存, 或者加锁一个旗标.
  • 不能调用调度(schedue).
  • 尽量在短时间内完成。 如果需要长时间,最好使用tasklet 或者 workqueue 来调度。
处理的步骤
  • 清除"中断挂起"位
  • 唤醒睡眠在设备上的进程。

使能和禁止中断

中断处理函数当持有一个自旋锁来避免死锁系统时,中断必须被阻塞,否则造成中断处理函数的并发死锁。

不能禁止共享的中断号

如果 disable_irq 被连续调用 2 次, 需要 2 个 enable_irq 调用使能

void disable_irq(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq);

若调用 disable_irq 的线程持有中断处理需要的任何资源(例如自旋锁), 系统可能死锁. disable_irq_nosync 与 disable_irq 不同, 它立刻返回.


禁止所有中断。关闭所有中断的开销太高, 如没有必要无须调用
<asm/system.h>):

void local_irq_save(unsigned long flags);
void local_irq_disable(void);

void local_irq_restore(unsigned long flags);
void local_irq_enable(void);


阅读(517) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~