和Linux设备驱动中中断处理相关
首先是申请和释放IRQ
API request_irq
和free_irq
request_irq
原型为:
request_irq(unsigned irq,
void (*handler)( irq, void *dev_id, struct pt_regs *regs),
unsigned long irqflags,
const char * devname,
void *dev_id);
Irq是要申请
硬件中断号;
Handler是向系统登记
中断处理
是
个回调
中断发生时
系统
这个
dev_id参数将被传递;
Irqflags是中断处理
属性
若设置SA_INTERRUPT
标明中断处理
是快速处理
快速处理
被
时屏蔽所有中断
慢速处理
不屏蔽;若设置SA_SHIRQ
则多个设备共享中断
dev_id在中断共享时会用到
般设置为这个设备
device结构本身或者NULL
Free_irq
原型为:
void free_irq(unsigned irq,void *dev_id);
另外
和Linux中断息息相关
个重要概念是Linux中断分为两个半部:上半部(tophalf)和下半部(bottom half)
上半部
功能是"登记中断"
当
个中断发生时
它进行相应地硬件读写后就把中断例程
下半部挂到该设备
下半部执行队列中去
因此
上半部执行
速度就会很快
可以服务更多
中断请求
但是
仅有"登记中断"是远远不够
中断
事件可能很复杂
因此
Linux引入了
个下半部
来完成中断事件
绝大多数使命
下半部和上半部最大
区别是下半部是可中断
而上半部是不可中断
下半部几乎做了中断处理
所有
事情
而且可以被新
中断打断!下半部则相对来说并不是非常紧急
通常还是比较耗时
因此由系统自行安排运行时机
不在中断服务上下文中执行
Linux实现下半部
机制主要有tasklet和工作队列
Tasklet基于Linux softirq
其使用相当简单
我们只需要定义tasklet及其处理
并将 2者关联:
void my_tasklet_func(unsigned long); //定义个处理:
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data); //定义个tasklet结构my_tasklet和
my_tasklet_func(data)相关联
然后
在需要调度tasklet
时候引用
个简单
API就能使系统在适当
时候进行调度运行:
tasklet_schedule(&my_tasklet);
此外
Linux还提供了另外
些其它
控制tasklet调度和运行
API:
DECLARE_TASKLET_DISABLED(name,function,data); //和DECLARE_TASKLET类似但等待tasklet被使能
tasklet_enable(struct tasklet_struct *); //使能tasklet
tasklet_disble(struct tasklet_struct *); //禁用tasklet
tasklet_init(struct tasklet_struct *,void (*func)(unsigned long),unsigned long); //类似
DECLARE_TASKLET
tasklet_kill(struct tasklet_struct *); // 清除指定tasklet可调度位即不允许调度该tasklet
我们先来看
个tasklet
运行例子
这个例子没有任何实际意义
仅仅为了演示
它
功能是:在globalvar被写入
次后
就调度
个tasklet
中输出"tasklet is executing":
# <linux/errupt.h>
…
//定义和绑定tasklet
void test_tasklet_action(unsigned long t);
DECLARE_TASKLET(test_tasklet, test_tasklet_action, 0);
void test_tasklet_action(unsigned long t)
{
prk("tasklet is executing\n");
}
…
ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
…
(copy_from_user(&global_var, buf, ()))
{
- EFAULT;
}
//调度tasklet执行
tasklet_schedule(&test_tasklet);
();
}
由于中断和真实
硬件息息相关
脱离硬件而空谈中断是毫无意义
我们还是来举
个简单
例子
这个例子来源于SAMSUNG S3C2410嵌入式系统例子
看看其中实时钟
驱动中和中断相关
部分:
struct fasync_struct *rtc_async_queue;
__init rtc_init(void)
{
misc_register(&rtc_dev);
create_proc_read_entry("driver/rtc", 0, 0, rtc_read_proc, NULL);
# RTC_IRQ
(rtc_has_irq 0)
goto no_irq2;
init_timer(&rtc_irq_timer);
rtc_irq_timer.function = rtc_dropped_irq;
spin_lock_irq(&rtc_lock);
/* Initialize periodic freq. to CMOS re default, which is 1024Hz */
CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) &0xF0) | 0x06), RTC_FREQ_SELECT);
spin_unlock_irq(&rtc_lock);
rtc_freq = 1024;
no_irq2:
#end
prk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n");
0;
}
void __exit rtc_exit(void)
{
remove_proc_entry("driver/rtc", NULL);
misc_deregister(&rtc_dev);
release_region(RTC_PORT(0), RTC_IO_EXTENT);
(rtc_has_irq)
free_irq(RTC_IRQ, NULL);
}
void rtc_errupt( irq, void *dev_id, struct pt_regs *regs)
{
/*
* Can be an alarm errupt, update complete errupt,
* or a periodic errupt. We store the status in the
* low and the number of errupts received since
* the last read in the r eder of rtc_irq_data.
*/
spin_lock(&rtc_lock);
rtc_irq_data 0x100;
rtc_irq_data &= ~0xff;
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) &0xF0);
(rtc_status &RTC_TIMER_ON)
mod_timer(&rtc_irq_timer, jfies + HZ / rtc_freq + 2 * HZ / 100);
spin_unlock(&rtc_lock);
/* Now do the rest of the actions */
wake_up_erruptible(&rtc_wait);
kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
}
rtc_fasync ( fd, struct file *filp, _disibledevent=>
RTC中断发生后
激发了
个异步信号
因此本驱动
提供了对第6节异步信号
支持
并不是每个中断都需要
个下半部
如果本身要处理
事情并不复杂
可能只有
个上半部
本例中
RTC驱动就是如此
延伸阅读
最新评论