源码欣赏
/**
三个重要的数据结构: irq_desc 、irq_chip 、irqaction
这个函数主要做了两件事情:
1 : 根据传入的参数(irq,handler,flags)构建一个irqaction,并把这个irqaction添加到irq_desc的action链表(单链表)
2 : 做一些初始化的工作,如: 设置中断触发方式、开启中断..............
参数 : irq 要安装的中断处理函数对应的中断号
handler 中断处理函数,这个是设备驱动来提供的。
flags 中断标识
dev 如果是共享中断,那么必需要保证唯一,因为在free_irq()的时候需要根据dev来区分释放的是那一个irqaction
这个参数还有一个作用 :在被中断的进程 与 中断处理程序中传递数据
*/
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
/**
第三个参数为 NULL,如果用户调用request_irq那么就没有机会使用irq_thread机制了,它是一个中断处理的内核线程
kthread_create(irq_thread, new, "irq/%d-%s", irq,new->name); 还没有被激活
在irq_thread中会调用在request_thread_irq()函数注册的 thread_fn函数,这个函数运行与进程上下文。
看在什么时候会调用到这个NULL,
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
struct irqaction *action = desc->action;
...
ret = handle_irq_event_percpu(desc, action);
{
do {
调用中断处理函数
res = action->handler(irq, action->dev_id);
switch (res) {
case IRQ_WAKE_THREAD:
内核的注释说,虽然你的中断处理程序返回了IRQ_WAKE_THREAD
但是你没有设置 线程函数,
这里仍然不给你唤醒内核线程,而是直接break
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}
irq_wake_thread(desc, action);
{
....
看 唤醒了把。
wake_up_process(action->thread);
}
ISR直接返回这个,内核不会唤醒内核线程,会break,然后处理链表中的下一个action
case IRQ_HANDLED:
flags |= action->flags;
break;
default:
break;
}
retval |= res;
他是单链表,进行链表的下一个action
action = action->next;
} while (action);
}
}
*/
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
/**
下面会为这个结构体分配内存空间,并根据传入的参数填充它,并把它放入到irq_desc中的action链表上
irqaction中包含了 用户注册的中断处理函数,中断标志 ........
当中断发生的时候,内核会逐个执行action链表中用户注册的中断处理函数,如果是共享中断,不是你的设备发生了中断,
那么对应的中断处理程序也会被调用,所以在中断处理函数中要判断一下是不是自己的设备发生了中断
这个可以通过读取中断状态寄存器就知道是不是自己的设备发生了中断
*/
struct irqaction *action;
/**
下面会根据irq,来从irq_desc[]中 查找对应的irq_desc,
irq_desc是一个核心结构,每一个外设的irq都用一个irq_desc来表示下面会详细讲解
*/
struct irq_desc *desc;
int retval;
/**
判断安装的是不是共享中断,如果是共享中断的话,那么 dev_id 必需提供,请看实验 A
为什么dev_id必须要提供,请看下面free_irq()的分析。
但是它并没有检查dev_id的唯一性。
*/
if((irqflags & IRQF_SHARED) && !dev_id)
{
return -EINVAL;
}
/**
从irq_desc[]中获取对应的irq_desc
所有的irq_desc对象被组织在一起,形成irq_desc[] 中断描述符数组
struct irq_desc irq_desc[NR_IRQS];
NR_IRQS: 可以处理外部中断的数量,和具体的平台有关
*/
desc = irq_to_desc(irq);
if(!desc)
{
return EINVAL;
}
/**
看它的源码 :
enum{
...
IRQ_NOREQUEST = (1 << 11) Interrupt cannot berequested via request_irq()
...
}
return !(desc->status_use_accessors & _IRQ_NOREQUEST);
就是检查irq_desc的status_use_accessors 的 第11 位 是否被置 1,
如果被置1,那么是不允许通过rquest_irq()注册中断处理程序的,
请看实验 B
*/
if (!irq_settings_can_request(desc) ||WARN_ON(irq_settings_is_per_cpu_devid(desc)))
{
return -EINVAL;
}
/**
如果注册的时候没有提供中断处理函数,
那么就使用默认的irq_default_primary_handler
*/
if(!handler)
{
/**
如果handler、thread_fn都没有提供,那么就出错返回
*/
if(!thread_fn)
{
return -EINVAL;
}
handler = irq_default_primary_handler;
}
/**
为irqaction分配内存空间
*/
action = kzalloc(sizeof(struct irqaction),GFP_KERNEL);
if(!action)
{
return -ENOMEM;
}
/**
根据传入的参数,对irqaction进行赋值初始化
*/
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(desc);
/**
会将irqaction添加到irq_desc的action链表上,
如果这时链表为空,那么不做检查,直接链入。
如果链表不为空,那么:
添加到链表之前要做检查
1 : 是否是共享中断
2 : 要添加的和链表中原有的触发方式是否相同
如果一致,才允许加入
并通过 __irq_set_trigger()设置中断触发类型,实际上是调用了中断控制器
的irq_set_type()函数,
chip->irq_set_type(&desc->irq_data, flags);
*/
retval = __setup_irq(irq,desc,action);
chip_bus_sync_unlock(desc);
/**
如果__setup_irq调用失败,则释放irqaction分配的内存空间
*/
if(retval)
{
kfree(action);
}
}
软中断 – tasklet
我再软中断初始化函数softirq_init ( 路径:linux/kernel/softirq.c) 函数中 使用了dump_stack(),看一下log
下面是它的调用流程图:
由上面可知,再软中断初始化的时候,就已经为tasklet安装了对应的处理函数 tasklet_action。
问:
tasklet_action 会在什么时候、什么情况下 被调用 ?