综述
终于看到这一章了,中断在Android系统源码中太常见了,学好本章十分重要!
中断
1.中断的申请以及释放
#include <linux/sched.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);
参数:
unsigned int irq:
要申请的中断号
irqreturn_t (*handler)(int,void*,struct pt_regs *))
中断处理函数
unsigned long flags
与中断管理有关的掩码选项
(1.SA_INTERRUPT 2.SA_SHIRQ 3.SA_SAMPLE_RANDOM).
const char *dev_name
中断名称,在proc/interrupts中可以显示
void *dev_id
该指针用于共享的中断信号线。它是唯一的标识符,在中断信号线空闲的时候可以使用它。
驱动程序也可以使用执行驱动自己的私有数据区,用来识别是哪个设备产生的中断。
没有强制使用共享方式时,设置为NULL;
void free_irq(unsigned int irq,void *dev_id);
ps:中断处理函数不能向用户空间发送或者接收数据,因为它不是在任何进程的上下文中执行的,不能发生休眠的操作,如调用wait_event、使用不带GFP_ATOMIC分配内存的操作,或者锁住一个信号量。不能调用schdule函数。
中断处理函数的典型任务:如果中断来了,就唤醒相关休眠的进程。
启用和禁用中断
有时驱动程序必须在一个较短的时间内阻塞中断产生,例如必须在拥有自旋锁的时候阻塞中断,以避免系统死锁!
1.禁用单个中断
#include <asm/irq.h>
void disable_irq(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq);
2.禁用和打开所有中断
void local_irq_save(unsigned long flags);
void local_irq_disable(void);
local_irq_save把当前中断状态保存在flags中,然后禁用当前处理器上的中断。
local_irq_disable不保存中断状态而直接关闭中断发送,只有我们知道中断没有在其他地方禁用时,才能使用这个版本。
void local_irq_restore(unsigned long flags);
void local_irq_enable(void);
中断的顶半部和底半部
中断处理的一个主要问题就是如何在中断处理函数中完成耗时任务。
响应一次设备中断需要完成一定数量的工作,但是中断处理函数需要尽快结束,不能阻塞时间过长。
因此,解决方案就是把中断处理函数分成两部分来解决,分别为顶半部和底半部。
“顶半部”是实际相应中断的例程,也就是request_irq注册的中断例程。
即-接收和响应中断请求。
“底半部”是一个被顶半部调用,并在稍后更安全的时间里执行的函数。
即-处理中断的业务逻辑
这样分工允许底半部工作期间,顶半部可以继续接受和响应新的中断!
2种实现底半部处理的方式
1.tasklet通常是底半部处理的优选机制,因为这种方式非常快,但是所有的tasklet的代码必须都是原子的。
1.声明
DECLARE_TASKLET(name,function,data);
name:tasklet的名称
function:执行tasklet时调用的函数
data:传递给tasklet函数的unsigned long 类型的值
2.调用
tasklet_schedule();
2.使用工作队列实现,可以具有更高的延迟和允许休眠。
1.声明并且初始化自己的工作队列
static struct work_struct short_wq;
INIT_WORK(&short_wq,short_do_tasklet,NULL);
2.调用
schedule_work(&short_wq);
共享中断
1.和普通的中断一样,通过irq_request申请。需要注意
1.请求中断时,必须指定flags为SA_SHIRQ.
2.dev_id参数必须是唯一的。任何指向模块地址空间的指针都可以使用,但不能设置为NULL。
3.不能使用enable_irq和disable_irq。