您的位置:首页 > 运维架构 > Linux

linux驱动编程--中断

2014-03-22 15:47 579 查看
一.中断的基本处理:

        简单的中断处理,主要是使用request_irq()和free_irq()。

         In fact, the kernel create a global array( irq_desc[] ) for manage all information about irq.Included
slot-function, irq_chip and so on. By the way.Each irq is corresponding to a member of
irq_desc. 在kernel建立了一个全局变量数组irq_desc[],用来记录每个中断的信息。

        slot-function is used to record what this irq need to do when a new interrupy arrived.(many user function)

        irq_chip is a structure, record many callback-function about how to control this irq.( i.e how to enable/disable irq, how to set handware status)

        the following is some useful function:

/** 中断处理函数*/
can_request_irq(unsigned int irq,unsigned long irqflags);		//查询是否可以申请
request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char * devname,void * dev_id);	//申请
free_irq(unsigned int irq,void * dev_id);		//释放中断
/** 扩展处理函数*/
disable_irq(unsigned int irq);				//禁止中断,如果有当前中断,会等待该中断执行完
disable_irq_nosync(unsigned int irq);		//禁止中断,会直接返回
enable_irq(unsigned int irq);
local_irq_disable();
local_irq_save(flags);
local_irq_enable();
local_irq_restore(x);
set_fiq_handler(void * start,unsigned int length);

看一下free_irq的源码,发现对irq资源的回收就是从irq_desc[]中找到该irq对应的信息记录变量desc。然后从desc->action的注册函数链表中取出需要注销的函数。

/*
* Internal function to unregister an irqaction - used to free
* regular and special interrupts that are part of the architecture.
*/
static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq);	//kernel建立了一个全局变量数组irq_desc[],用来存放所有的irq中断信息。
struct irqaction *action, **action_ptr; 	//
struct task_struct *irqthread;
unsigned long flags;

WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);

if (!desc)
return NULL;

spin_lock_irqsave(&desc->lock, flags);

/*
* There can be multiple actions per IRQ descriptor, find the right
* one based on the dev_id:
*/
action_ptr = &desc->action;
for (;;) {//根据dev_id找到该irq对应的链表中的部分
action = *action_ptr;

if (!action) {
WARN(1, "Trying to free already-free IRQ %d\n", irq);
spin_unlock_irqrestore(&desc->lock, flags);

return NULL;
}

if (action->dev_id == dev_id)
break;
action_ptr = &action->next;
}

/* Found it - now remove it from the list of entries: */
*action_ptr = action->next;

/* Currently used only by UML, might disappear one day: */
#ifdef CONFIG_IRQ_RELEASE_METHOD
if (desc->chip->release)
desc->chip->release(irq, dev_id);
#endif

/* If this was the last handler, shut down the IRQ line: */
if (!desc->action) {
desc->status |= IRQ_DISABLED;
if (desc->chip->shutdown)
desc->chip->shutdown(irq);
else
desc->chip->disable(irq);
}

irqthread = action->thread;
action->thread = NULL;

spin_unlock_irqrestore(&desc->lock, flags);

unregister_handler_proc(irq, action);

/* Make sure it's not being used on another CPU: */
synchronize_irq(irq);

if (irqthread) {
if (!test_bit(IRQTF_DIED, &action->thread_flags))
kthread_stop(irqthread);
put_task_struct(irqthread);
}

#ifdef CONFIG_DEBUG_SHIRQ
/*
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
* event to happen even now it's being freed, so let's make sure that
* is so by doing an extra call to the handler ....
*
* ( We do this after actually deregistering it, to make sure that a
*	 'real' IRQ doesn't run in * parallel with our fake. )
*/
if (action->flags & IRQF_SHARED) {
local_irq_save(flags);
action->handler(irq, dev_id);
local_irq_restore(flags);
}
#endif
return action;
}


二.中断处理扩展:

        为了改善中断处理效率,中断处理函数被分成了两部分:顶半部和底半部。底半部有三种实现方式:tasklet,工作队列和软中断。

        2.1 Tasklet

                tasklet运行于软中断上下文,所以在其处理函数中不允许睡眠。在内核有一个全局链表用于记录需要完成的任务,对应的处理进程为守护进程 ksoftirqd/0。一般在执 行中断上半部后会执行下半部,到下半部时如果有新的中断到达,则唤醒ksoftirqd/0来执行当前部分,而自己去处理新中断。其函数解析如下:

/** 信息结构体*/
struct tasklet_struct
{
struct tasklet_struct *next;	//信息结构体被做成链表
unsigned long state;			//
atomic_t count;			//
void (*func)(unsigned long);	//回调函数
unsigned long data;		//传给回调函数的参数
};
/** 初始化*/
tasklet_init(struct tasklet_struct * t,void(* func)(unsigned long),unsigned long data);
DECLARE_TASKLET(name,func,data);

#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
/** schedule*/
tasklet_schedule(a);
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;

local_irq_save(flags);
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t;			//??将新task添加到全局task链表中
__get_cpu_var(tasklet_vec).tail = &(t->next);		//
raise_softirq_irqoff(TASKLET_SOFTIRQ);		//挂起TASKLET_SOFTIRQ类型的软中断,并根据需要唤醒守护进程
local_irq_restore(flags);
}
/*
* This function must run with irqs disabled!
*/
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr); //挂起软中断nr

/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.(假如在中断中,该函数一定会被执行)
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.(否则就叫醒守护进程)
*/
if (!in_interrupt())	//假如不在中断上下文
wakeup_softirqd();	//叫醒softirq守护进程,它会保证该函数执行一次
}

        2.2 工作队列

        工作队列是一个直接将一个任务封包挂到全局链表上交给一个worker process处理,所以是工作在进程上下文,允许睡眠以及进程调度操作。

//重要结构体
struct work_struct;
struct workqueue_struct;
struct cpu_workqueue_struct;
//初始化
INIT_WORK(_work,_func);
DECLARE_WORK(n,f);
//常用函数
schedule_work(a);			--将新工作a添加到一个全局的工作队列中,一般由守护进程events复制处理;但如果需要可以创建自定义					的worker process。

 

       2.3 软中断


        处理器的所有状态中有一种是软中断状态,这是一种可被打断的中断状态(其他中断是不允许被打断的)。 one of most important feasure, for softirq, is interruptible.this is very important for ensure the system quickly answer a new interrupt  ,because the rest of all interrupt
can't be interrupt.

        为了方便事物分类处理又在内部进行了逻辑分类,

        for example, tasklet is corresponding to TASKLET_SOFTIRQ. we often raise the softirq( the
TASKLET_SOFTIRQ type) when a new tasklet arrived. 
        the type of softirq is

enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,	// this is a tasklet thing
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,	/* Preferable RCU should always be the last softirq */

NR_SOFTIRQS
};

       In the next is some common function:

//重要结构体
struct softirq_action;
struct softirq_action
{
void	(*action)(struct softirq_action *);
};
//常用函数
open_softirq(int nr,void(* action)(struct softirq_action *));	//reigister a function for a softirq( 注册软中断对应的处理函数)
raise_softirq(unsigned int nr);		//raise a softirq(触发一个软中断)
local_bh_disable(void);			//禁止软中断,tasklet底半部机制
local_bh_enable(void);			//

        一个非常棒的关于软中断的说明在这里,:

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}


 

 

 

a49e
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐