pm_stay_awake()和pm_relax()的意義

一般咱們在編寫中斷喚醒系統的驅動時,常使用如下的處理流程:函數

static int xxx_suspend()
{
...
    spin_lock_irqsave(&ltr->irq_lock, irqflags);//加spin lock保護
    enable_irq(irq);
    spin_unlock_irqrestore(&ltr->irq_lock, irqflags);

    atomic_inc(&irq_count); 
...
}

static int xxx_resume()
{
...
    if(atomic_read(&irq_count)){
        spin_lock_irqsave(&ltr->irq_lock, irqflags);
        disable_irq_nosync(irq);
        spin_unlock_irqrestore(&ltr->irq_lock, irqflags);

        atomic_dec(&irq_count);
    }
...
}

static irqreturn_t xxx_irq_handler()
{
    spin_lock_irqsave(&ltr->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(&ltr->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(&ltr->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(&ltr->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計數
}
相關文章
相關標籤/搜索