一般咱們在編寫中斷喚醒系統的驅動時,常使用如下的處理流程:函數
static int xxx_suspend() { ... spin_lock_irqsave(<r->irq_lock, irqflags);//加spin lock保護 enable_irq(irq); spin_unlock_irqrestore(<r->irq_lock, irqflags); atomic_inc(&irq_count); ... } static int xxx_resume() { ... if(atomic_read(&irq_count)){ spin_lock_irqsave(<r->irq_lock, irqflags); disable_irq_nosync(irq); spin_unlock_irqrestore(<r->irq_lock, irqflags); atomic_dec(&irq_count); } ... } static irqreturn_t xxx_irq_handler() { spin_lock_irqsave(<r->irq_lock, irqflags); disable_irq_nosync(irq); ---->關閉中斷 //do something //調度一個delay work,將耗時操做放到中斷下半部執行,若是此work涉及到與外設通信的話,一般還須要延時幾毫秒後再 //來執行work,由於這個時候,總線可能還未喚醒 schedule_delayed_work(&xxx_work,msecs_to_jiffies(5)); spin_unlock_irqrestore(<r->irq_lock, irqflags); atomic_dec(&irq_count); } static xxx_delay_work() { //communicate with peripheral device input_report_key(my_dev, KEY_WAKEUP, 1);//模擬KEY_WAKEUP事件 input_sync(my_dev); input_report_key(my_dev, KEY_WAKEUP, 0); input_sync(my_dev); } static int xxx_setup_irq() { irq = gpio_to_irq(pin_num); request_irq(irq,xxx_irq_handler,IRQ_TYPE_LEVEL_LOW | IRQF_NO_SUSPEND,"PS_EINT",NULL); //enable irq wake when setup enable_irq_wake(irq); }
中斷處理程序中,首先調用disable_irq_nosync()關閉中斷,而後調度一個delay work,將耗時操做放到中斷下半部執行,若是此work涉及到與外設通信的話,一般還須要延時幾毫秒後再來執行work,由於這個時候,總線可能還未喚醒。atom
此種作法存在一個風險,想象一種場景,應用請求休眠,系統進入休眠流程,此時若是設備觸發了中斷,中斷處理程序中首先關閉中斷,而後調度內核線程去處理work,但假如這個時候此work還未被調度到,系統就進入休眠了,那麼這個設備就被永久關閉中斷了,不再能喚醒系統。pm_stay_awake()和pm_relax()的設計就是用來解決這個問題。線程
pm_stay_awake()和pm_relax()維護着combined_event_count的原子計數,pm_wakeup_pending()主要是判斷combined_event_count變量在suspend的過程當中是否改變,若是改變suspend就abort。所以在中斷handler開始處增長combined_event_count計數,工做隊列函數結尾位置減少combined_event_count計數便可設計
static irqreturn_t xxx_irq_handler() { pm_stay_awake(&xxx_dev);//增長combined_event_count計數 spin_lock_irqsave(<r->irq_lock, irqflags); disable_irq_nosync(irq); ---->關閉中斷 //do something //調度一個delay work,將耗時操做放到中斷下半部執行,若是此work涉及到與外設通信的話,一般還須要延時幾毫秒後再 //來執行work,由於這個時候,總線可能還未喚醒 schedule_delayed_work(&xxx_work,msecs_to_jiffies(5)); spin_unlock_irqrestore(<r->irq_lock, irqflags); atomic_dec(&irq_count); } static xxx_delay_work() { //communicate with peripheral device input_report_key(my_dev, KEY_WAKEUP, 1);//模擬KEY_WAKEUP事件 input_sync(my_dev); input_report_key(my_dev, KEY_WAKEUP, 0); input_sync(my_dev); pm_relax(&xxx_dev);//減少combined_event_count計數 }