内核中断实现
Linux中断处理子系统
1.根据中断号找到正确的中断处理代码
2.Linux定义 了名字为irq_desc的中断例程描述符表:(include/inux/irq.h)该表struct irgdesc结构组成
struct irq_desc irg_desc[NR_IROST];NR_ _IRQS表示中断源的数目
irg_desc结构体中的成员action指向该中断号对应的irgaction结构体链表
。irgaction结构体定义如下:
//include/linux/interrupt.h
struct irgaction {
irq_hander_t handler; /指向中断服务程序
unsigned long flags;//中断标志
unsigned long mask; //中断掩码
const char *name; //I/O设备名
void *dev_id;//设备标识
struct irgaction *next;//指向下一个描述符
int irq; //IRQ线
struct proc_dir_entry *dir; //指向IRQn相关的/proc/irg/n目录的描述符
}
全局中断控制:
全局中断控制包括启用和禁用中断(ARM)
raw_local_irg_save(x) //用来禁用所有的中断(一般不使用)
raw_local_irg_enable //用来取消中断禁用
尽量不要对全局中断进行操作
static inline int atomic s1ub return(int i atomic↑*v)
{
unsigned long flags;
int val;
raw_local_irq_save(flags);
val = v-> counter;
v-> counter = val-= i;
raw_local_irq_restore(flags);
return val;
}
申请IRQ
1.中断处理程序注册的两个功能:注册中断号和注册中断
处理函数
typedef irgreturn_t (*irq_handler_t)(int, void *);
int request_irg(
unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev_ id);
2.驱动程序可以选择在初始化的时候安装中断处理程序,也可以在用户打开设备时再安装
3.参数说明:
1)irq是要申请的中断号
2)handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev参数将被传递给它。
3) flags是中断标志位。若设置了IRQF_DISABLED,则表示中断处理程序被调用时屏蔽所有中断;若设置了IRQF_SHARED,则表示多个设备共享中断;
4) name设置中断名称,在cat /proc/interrupts中可以看到此名称(自定义)。
5)dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
中断实现示例
内核部分:
在设备树文件中添加设备声明:vim linux-3.14/arch/arm/boot/dts/exynos4412-origen.dts
注意:需要在该文件中加入头文件否则无法识别IRQ_TYPE_EDGE_FALLING关键字
#include <dt-bindings\interrupt-controller\irq.h>
key{
compatible = "superme,mykey";
interrupt-parent = <&gpx1>;
interrupts = <1 IRQ_TYPE_EDGE_FALLING>;
};
参数说明:
compatible为设备匹配字,必须确保字符和驱动中注册字符完全一致
interrupt-parent为中断的父节点,需要查询芯片手册来查看所操作的硬件设备的分组,比如示例设备为按键2为gpx1_1,为第一组所以填写&gpx1,具体是否有该分组需要查看exynos4412-origen.dts所包含的头文件
interrupts第一个参数为分组的编号比如按键2为gpx1_1,则是gpx1组的1,
IRQ_TYPE_EDGE_FALLING意为下降沿触发,
IRQ_TYPE_EDGE_RISING意为上升沿触发,
IRQ_TYPE_LEVEL_HIGH意为高电平触发
IRQ_TYPE_LEVEL_LOW意为低电平触发
IRQ_TYPE_EDGE_BOTH意为上升和下降沿都触发
驱动部分:
//中断处理函数
irqreturn_t myfun(int irqno,void* dev_id)
{
printk("this interrupt no is %d\n",irqno);
return IRQ_HANDLED;
}
static int mykey_probe(struct platform_device* pdev)
{
int ret = -1;
/*************kernel****************/
//1.reg
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,num,name);
if(ret<0)
return ret;
//2.init
cdev_init(&mydev,&myops);
//3.add
cdev_add(&mydev,no,num);
/*************hw ****************/
struct resource* irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);//获取中断信息
int ret = request_irq(
irqres->start,//中断起始地址
myfun,//中断函数
irqres->flags | IRQF_ DISABLED,
"myirq",
NULL);
return 0;
}
static int mybee_remove(struct platform_device* pdev)
{
//kernel
cdev_del(&mydev);
unregister_chrdev_region(devno,devNum);
//hw
free_irq(irqres->start,NULL);//释放中断
return 0;
}
Linux中断半部机制
1.两个半部的理念:
解决既要中断执行快,又要做的事情多的矛盾。
2.下半部机制的实现:
1)软中断(同类型的中断可以同时执行)
2)Tasklet(同类型的中断不能同时执行)
定义一个处理函数
void my_tasklet_func(unsigned long);
定义一个tasklet结构my_tasklet,与my_tasklet_func(data)函数相联
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
调度tasklet
tasklet_schedule(&my_tasklet);
3)工作队列(作为进程被调度)
定义一个工作队列
struct work_struct my_wq;
定义一个处理函数
void my_wq_func(unsigned long) ;
初始化工作队列并将其与处理函数绑定
INIT_WORK(&my_wq, my_wq_func);
调度工作队列执行
schedule_work(&my_wq);