blob: 5bfc44ca5370ae0f8d206235cedf67f80bf8c92c [file] [log] [blame]
/*
* Copyright (c) 2011-2013 NVIDIA Corporation. All Rights Reserved.
*
* NVIDIA Corporation and its licensors retain all intellectual property and
* proprietary rights in and to this software and related documentation. Any
* use, reproduction, disclosure or distribution of this software and related
* documentation without an express license agreement from NVIDIA Corporation
* is strictly prohibited.
*/
#include "timeoutpoker.h"
#include <fcntl.h>
#undef LOG_TAG
#define LOG_TAG "powerHAL::TimeoutPoker"
enum {
CLIENT_FD,
SERVER_FD
};
TimeoutPoker::TimeoutPoker(Barrier* readyToRun)
{
mPokeHandler = new PokeHandler(this, readyToRun);
}
//Called usually from IPC thread
void TimeoutPoker::pushEvent(QueuedEvent* event)
{
mPokeHandler->sendEventDelayed(0, event);
}
int TimeoutPoker::PokeHandler::createHandleForFd(int fd)
{
int pipefd[2];
int res = pipe(pipefd);
if (res) {
ALOGE("unabled to create handle for fd");
close(fd);
return -1;
}
listenForHandleToCloseFd(pipefd[SERVER_FD], fd);
if (res) {
close(fd);
close(pipefd[SERVER_FD]);
close(pipefd[CLIENT_FD]);
return -1;
}
return pipefd[CLIENT_FD];
}
int TimeoutPoker::PokeHandler::openPmQosNode(const char* filename, int val)
{
int pm_qos_fd = open(filename, O_RDWR);;
if (pm_qos_fd < 0) {
ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
return -1;
}
write(pm_qos_fd, &val, sizeof(val));
return pm_qos_fd;
}
int TimeoutPoker::PokeHandler::openPmQosNode(const char* filename, int priority, int max, int min)
{
int pm_qos_fd = open(filename, O_RDWR);;
if (pm_qos_fd < 0) {
ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
return -1;
}
char command[COMMAND_SIZE];
int size = createConstraintCommand((char*)command, COMMAND_SIZE, priority, max, min);
write(pm_qos_fd, command, size);
return pm_qos_fd;
}
int TimeoutPoker::PokeHandler::createHandleForPmQosRequest(const char* filename, int val)
{
int fd = openPmQosNode(filename, val);
if (fd < 0) {
return -1;
}
return createHandleForFd(fd);
}
int TimeoutPoker::PokeHandler::createHandleForPmQosRequest(const char* filename, int priority, int max, int min)
{
int fd = openPmQosNode(filename, priority, max, min);
if (fd < 0) {
return -1;
}
return createHandleForFd(fd);
}
int TimeoutPoker::createPmQosHandle(const char* filename,
int val)
{
Barrier done;
int ret;
pushEvent(new PmQosOpenHandleEvent(
filename, val, &ret, &done));
done.wait();
return ret;
}
void TimeoutPoker::requestPmQosTimed(const char* filename,
int val, nsecs_t timeout)
{
pushEvent(new PmQosOpenTimedEvent(
filename, val, timeout));
}
int TimeoutPoker::requestPmQos(const char* filename, int val)
{
int pm_qos_fd = open(filename, O_RDWR);
if (pm_qos_fd < 0) {
ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
return -1;
}
write(pm_qos_fd, &val, sizeof(val));
return pm_qos_fd;
}
int TimeoutPoker::createPmQosHandle(const char* filename,
int priority, int max, int min)
{
Barrier done;
int ret;
pushEvent(new PmQosOpenHandleEvent(
filename, priority, max, min, &ret, &done));
done.wait();
return ret;
}
void TimeoutPoker::requestPmQosTimed(const char* filename,
int priority, int max, int min, nsecs_t timeout)
{
pushEvent(new PmQosOpenTimedEvent(
filename, priority, max, min, timeout));
}
int TimeoutPoker::requestPmQos(const char* filename, int priority, int max, int min)
{
int pm_qos_fd = open(filename, O_RDWR);
if (pm_qos_fd < 0) {
ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
return -1;
}
char command[COMMAND_SIZE];
int size = createConstraintCommand((char*)command, COMMAND_SIZE, priority, max, min);
write(pm_qos_fd, command, size);
return pm_qos_fd;
}
/*
* PokeHandler
*/
void TimeoutPoker::PokeHandler::sendEventDelayed(
nsecs_t delay, TimeoutPoker::QueuedEvent* ev) {
Mutex::Autolock _l(mEvLock);
int key = generateNewKey();
mQueuedEvents.add(key, ev);
mWorker->mLooper->sendMessageDelayed(delay, this, key);
}
TimeoutPoker::QueuedEvent*
TimeoutPoker::PokeHandler::removeEventByKey(
int what) {
Mutex::Autolock _l(mEvLock);
// msg.what contains a key to retrieve the message parameters
TimeoutPoker::QueuedEvent* e =
mQueuedEvents.valueFor(what);
mQueuedEvents.removeItem(what);
return e;
}
int TimeoutPoker::PokeHandler::generateNewKey(void)
{
return mKey++;
}
TimeoutPoker::PokeHandler::PokeHandler(TimeoutPoker* poker, Barrier* readyToRun) :
mPoker(poker),
mKey(0),
mSpamRefresh(false)
{
mWorker = new LooperThread(readyToRun);
mWorker->run("TimeoutPoker::PokeHandler::LooperThread", PRIORITY_FOREGROUND);
readyToRun->wait();
}
void TimeoutPoker::PokeHandler::handleMessage(const Message& msg)
{
assert(!mQueuedEvents->isEmpty());
// msg.what contains a key to retrieve the message parameters
TimeoutPoker::QueuedEvent* e =
removeEventByKey(msg.what);
if (!e)
return;
e->run(this);
delete e;
}
void TimeoutPoker::PokeHandler::openPmQosTimed(const char* filename,
int val, nsecs_t timeout)
{
int fd = openPmQosNode(filename, val);
if (fd < 0) {
return;
}
sendEventDelayed(timeout, new TimeoutEvent(fd));
}
void TimeoutPoker::PokeHandler::openPmQosTimed(const char* filename,
int priority, int max, int min, nsecs_t timeout)
{
int fd = openPmQosNode(filename, priority, max, min);
if (fd < 0) {
return;
}
sendEventDelayed(timeout, new TimeoutEvent(fd));
}
void TimeoutPoker::PokeHandler::timeoutRequest(int fd)
{
close(fd);
}
status_t TimeoutPoker::PokeHandler::LooperThread::readyToRun()
{
mLooper = Looper::prepare(0);
if (mLooper == 0)
return NO_MEMORY;
mReadyToRun->open();
return NO_ERROR;
}
bool TimeoutPoker::PokeHandler::LooperThread::threadLoop()
{
int res = mLooper->pollAll(99999);
if (res == ALOOPER_POLL_ERROR)
ALOGE("Poll returned an error!");
return true;
}
class CallbackContext {
public:
CallbackContext(sp<Looper> looper, int fd) : looper(looper), fd(fd) {}
sp<Looper> looper;
int fd;
};
static int pipeCloseCb(int handle, int events, void* data)
{
CallbackContext* ctx = (CallbackContext*)data;
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
ctx->looper->removeFd(handle);
close(handle);
close(ctx->fd);
delete ctx;
return 0;
}
return 1;
}
//Reverse arity of result to match call-site usage
int TimeoutPoker::PokeHandler::listenForHandleToCloseFd(int handle, int fd)
{
//This func is threadsafe
return !mWorker->mLooper->addFd(handle, ALOOPER_POLL_CALLBACK,
ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP,
pipeCloseCb, new CallbackContext(mWorker->mLooper, fd));
}
int createConstraintCommand(char* command, int size, int priority, int max, int min) {
snprintf(command, size, "%d %d %d 0", max, min, priority);
return strlen(command);
}