黑客方向盘驱动:黑客进阶的Linux设备驱动编程中断处理

和Linux设备驱动中中断处理相关首先是申请和释放IRQAPI request_irq和free_irqrequest_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驱动就是如此

Tags:  linux显卡驱动 linux设备驱动程序 linux驱动 黑客方向盘驱动

延伸阅读

最新评论

发表评论