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计数
}
相关文章
相关标签/搜索