zoukankan      html  css  js  c++  java
  • apm-emulation driver framework

    apm-emulation driver framework

    在suspend_prepare()里会call error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);

    因为apm-emulation有注册关于此的callback func,所以会call到apm_suspend_notifier,在此函数里,将会发一个apm_event_t类型的event APM_USER_SUSPEND(通过queue_add_event),此时suspend_state进去SUSPEND_PENDING。然后wakeup apm_waitqueue,userspace如果有进程来read此event,将会读到此event,然后返回,userspace read系统调用会走到apm_read(),这是一个阻塞系统调用,没有event将会阻塞,有event读到event后返回。

    此后apm_user suspend_state进入SUSPEND_READ状态

    drivers/char/apm-emulation.c

    static int apm_suspend_notifier(struct notifier_block *nb,
                    unsigned long event,
                    void *dummy)
    {
        struct apm_user *as;
        int err;
        unsigned long apm_event;
    
        /* short-cut emergency suspends */
        if (atomic_read(&userspace_notification_inhibit))
            return NOTIFY_DONE;
    
        switch (event) {
        case PM_SUSPEND_PREPARE:
        case PM_HIBERNATION_PREPARE:
            apm_event = (event == PM_SUSPEND_PREPARE) ?
                APM_USER_SUSPEND : APM_USER_HIBERNATION;
            /*
             * Queue an event to all "writer" users that we want
             * to suspend and need their ack.
             */
            mutex_lock(&state_lock);
            down_read(&user_list_lock);
    
            list_for_each_entry(as, &apm_user_list, list) {
                if (as->suspend_state != SUSPEND_WAIT && as->reader &&
                    as->writer && as->suser) {
                    as->suspend_state = SUSPEND_PENDING;
                    atomic_inc(&suspend_acks_pending);
                    queue_add_event(&as->queue, apm_event);
                }
            }
    
            up_read(&user_list_lock);
            mutex_unlock(&state_lock);
            wake_up_interruptible(&apm_waitqueue);
    
            /*
             * Wait for the the suspend_acks_pending variable to drop to
             * zero, meaning everybody acked the suspend event (or the
             * process was killed.)
             *
             * If the app won't answer within a short while we assume it
             * locked up and ignore it.
             */
            err = wait_event_interruptible_timeout(
                apm_suspend_waitqueue,
                atomic_read(&suspend_acks_pending) == 0,
                5*HZ);
         if(err == 0)
         {
           //timeout
         }
    	/* let suspend proceed */
    	if (err >= 0)
    	return NOTIFY_OK;

     userspace space进程(一班是power hal service)收到了APM_USER_SUSPEND event后,将会做一些想做的事情,然后会call APM_IOC_SUSPEND ioctl:

    static long
    apm_ioctl(struct file *filp, u_int cmd, u_long arg)
    {
        struct apm_user *as = filp->private_data;
        int err = -EINVAL;
    
        if (!as->suser || !as->writer)
            return -EPERM;
    
        switch (cmd) {
        case APM_IOC_SUSPEND:
            mutex_lock(&state_lock);
    
            as->suspend_result = -EINTR;
    
            switch (as->suspend_state) {
            case SUSPEND_READ:
                /*
                 * If we read a suspend command from /dev/apm_bios,
                 * then the corresponding APM_IOC_SUSPEND ioctl is
                 * interpreted as an acknowledge.
                 */
                as->suspend_state = SUSPEND_ACKED;
                atomic_dec(&suspend_acks_pending);
                mutex_unlock(&state_lock);
    
                /*
                 * suspend_acks_pending changed, the notifier needs to
                 * be woken up for this
                 */
                wake_up(&apm_suspend_waitqueue);
    
                /*
                 * Wait for the suspend/resume to complete.  If there
                 * are pending acknowledges, we wait here for them.
                 * wait_event_freezable() is interruptible and pending
                 * signal can cause busy looping.  We aren't doing
                 * anything critical, chill a bit on each iteration.
                 */
                while (wait_event_freezable(apm_suspend_waitqueue,
                        as->suspend_state != SUSPEND_ACKED))
                    msleep(10);
                break;

    上面会将apm_user suspend_state设置为SUSPEND_ACKED,并将suspend_acks_pending原子变量减1,然后唤醒apm_suspend_waitqueue,这个将唤醒在apm_suspend_notifier()里处理PM_SUSPEND_PREPARE时而wait的线程,这里wait的条件是suspend_acks_pending等于0。唤醒后,PM_SUSPEND_PREARE notifier call返回。

    STR resume时,apm-emulation将收到PM_POST_SUSPEND callback,这个notifier callback是在STR resume时执行的,在suspend_finish()里callback的:

    static int apm_suspend_notifier(struct notifier_block *nb,
                    unsigned long event,
                    void *dummy)
        case PM_POST_SUSPEND:
        case PM_POST_HIBERNATION:
            apm_event = (event == PM_POST_SUSPEND) ?
                APM_NORMAL_RESUME : APM_HIBERNATION_RESUME;
            /*
             * Anyone on the APM queues will think we're still suspended.
             * Send a message so everyone knows we're now awake again.
             */
            queue_event(apm_event);
    
            /*
             * Finally, wake up anyone who is sleeping on the suspend.
             */
            mutex_lock(&state_lock);
            down_read(&user_list_lock);
            list_for_each_entry(as, &apm_user_list, list) {
                if (as->suspend_state == SUSPEND_ACKED) {
                    /*
                     * TODO: maybe grab error code, needs core
                     * changes to push the error to the notifier
                     * chain (could use the second parameter if
                     * implemented)
                     */
                    as->suspend_result = 0;
                    as->suspend_state = SUSPEND_DONE;
                }
            }
            up_read(&user_list_lock);
            mutex_unlock(&state_lock);
    
            wake_up(&apm_suspend_waitqueue);
            return NOTIFY_OK;

     上述函数会queue一个APM_NORMAL_RESUME event。

    然后,当前suspend_state为SUSPEND_ACKED,然后将apm_user suspend_state改为SUSPEND_DONE,表示完整的suspend-resume流程完成了。

    然后将apm_suspend_waitqueue wakeup,这个将唤醒user space的power hal service,这个service正wait在APM_IOC_SUSPEND ioctl处。然后此service将会return。

    之后此service将会再去读apm_event_t,此时会读到上面的APM_NORMAL_RESUME event,读到之后,做一些想做的事情。

    从上面所述suspend-resume完整过程来看apm_user suspend_state变化顺序为:

    SUSPEND_PENDING

    SUSPEND_READ

    SUSPEND_ACKED

    SUSPEND_DONE

    上述分析基于kernel 4.19

  • 相关阅读:
    管理者的存在 说明了企业文化的匮乏【20140124】
    Sublime Text2(ST2)点滴积累及使用技巧_持续更新【20130320】【最近修改20130516】
    WebStorm 点滴积累及使用技巧_持续更新【20130323】【最近修改20130604】
    码农,企业,和资本
    关于赛车游戏的一点体会
    从艺感悟
    三种糟糕的程序员
    关于ios单机盗版
    汽车加速性,功率和扭矩
    ExtJS之面向对象的概念
  • 原文地址:https://www.cnblogs.com/aspirs/p/15406162.html
Copyright © 2011-2022 走看看