一般咱们在编写中断唤醒系统的驱动时,常使用如下的处理流程:函数
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计数 }