linux驅動編程--中斷

一.中斷的基本處理:

        簡單的中斷處理,主要是使用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;
	}



 




 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章