/*
 * Copyright (c) 2014 Google, Inc.  All Rights Reserved.
 * Copyright (c) 2015 NVIDIA, Inc.  All Rights Reserved.
 *
 */
#include "timed_qos_manager.h"
#include <fcntl.h>
#include <assert.h>

#undef LOG_TAG
#define LOG_TAG "powerHAL::TimedQosManager"

void SysfsQosObject::enter()
{
    sysfs_write(mNodeName, mEnterCmd);
}

void SysfsQosObject::exit()
{
    sysfs_write(mNodeName, mExitCmd);
}

bool TimedQosManager::threadLoop()
{
    AutoMutex lock(mLock);

    ALOGI("threadLoop [%s] starting\n", mName);

    while (1) {
        if (exitPending()) {
            ALOGV("threadLoop [%s] exiting\n", mName);
            break;
        }
        if (mTargetTime == 0) {
            // wait for something to do
            ALOGV("threadLoop [%s] nothing to do, waiting\n", mName);
            mCondition.wait(mLock);
            ALOGV("threadLoop [%s] woke from wait\n", mName);
        } else {
            // open qos file if not already open
            mQosObject->enter();

            // wait for target time to expire
            nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
            ALOGV("threadLoop [%s] waiting with relative time %lld\n",
                    mName, mTargetTime - currentTime);
            mCondition.waitRelative(mLock, mTargetTime - currentTime);

            // check if we're done.  if not (typically because
            // someone extended our time while we were blocked)
            // just loop again and sleep until new target time
            currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
            if (currentTime >= mTargetTime) {
                mQosObject->exit();
                mTargetTime = 0;
            } else {
                ALOGV("threadLoop [%s] timeout extended\n");
            }
        }
    }
    return false;
}

void TimedQosManager::requestTimedQos(nsecs_t reltime)
{
    AutoMutex lock(mLock);
    nsecs_t targetTime = systemTime() + reltime;

    /* new target time should always be ahead of current one */
    assert(mTargetTime <= targetTime);
    mTargetTime = targetTime;
    ALOGV("threadLoop [%s] requesting reltime %lld, mTargetTime set to %lld\n",
          mName, reltime, mTargetTime);

    /* wake the Thread.  if it's already waiting on a different
     * timeout, this will just wake it early and it'll wait again.
     */
    mCondition.signal(Condition::WAKE_UP_ALL);
}