blob: 5bfc44ca5370ae0f8d206235cedf67f80bf8c92c [file] [log] [blame]
Aaron Klingb2b3dd02015-11-12 09:25:04 -06001/*
2 * Copyright (c) 2011-2013 NVIDIA Corporation. All Rights Reserved.
3 *
4 * NVIDIA Corporation and its licensors retain all intellectual property and
5 * proprietary rights in and to this software and related documentation. Any
6 * use, reproduction, disclosure or distribution of this software and related
7 * documentation without an express license agreement from NVIDIA Corporation
8 * is strictly prohibited.
9 */
10#include "timeoutpoker.h"
11#include <fcntl.h>
12
13#undef LOG_TAG
14#define LOG_TAG "powerHAL::TimeoutPoker"
15
16enum {
17 CLIENT_FD,
18 SERVER_FD
19};
20
21TimeoutPoker::TimeoutPoker(Barrier* readyToRun)
22{
23 mPokeHandler = new PokeHandler(this, readyToRun);
24}
25
26//Called usually from IPC thread
27void TimeoutPoker::pushEvent(QueuedEvent* event)
28{
29 mPokeHandler->sendEventDelayed(0, event);
30}
31
32int TimeoutPoker::PokeHandler::createHandleForFd(int fd)
33{
34 int pipefd[2];
35 int res = pipe(pipefd);
36 if (res) {
37 ALOGE("unabled to create handle for fd");
38 close(fd);
39 return -1;
40 }
41
42 listenForHandleToCloseFd(pipefd[SERVER_FD], fd);
43 if (res) {
44 close(fd);
45 close(pipefd[SERVER_FD]);
46 close(pipefd[CLIENT_FD]);
47 return -1;
48 }
49
50 return pipefd[CLIENT_FD];
51}
52
53int TimeoutPoker::PokeHandler::openPmQosNode(const char* filename, int val)
54{
55 int pm_qos_fd = open(filename, O_RDWR);;
56 if (pm_qos_fd < 0) {
57 ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
58 return -1;
59 }
60 write(pm_qos_fd, &val, sizeof(val));
61 return pm_qos_fd;
62}
63
64int TimeoutPoker::PokeHandler::openPmQosNode(const char* filename, int priority, int max, int min)
65{
66 int pm_qos_fd = open(filename, O_RDWR);;
67 if (pm_qos_fd < 0) {
68 ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
69 return -1;
70 }
71 char command[COMMAND_SIZE];
72 int size = createConstraintCommand((char*)command, COMMAND_SIZE, priority, max, min);
73
74 write(pm_qos_fd, command, size);
75 return pm_qos_fd;
76}
77
78int TimeoutPoker::PokeHandler::createHandleForPmQosRequest(const char* filename, int val)
79{
80 int fd = openPmQosNode(filename, val);
81 if (fd < 0) {
82 return -1;
83 }
84
85 return createHandleForFd(fd);
86}
87
88int TimeoutPoker::PokeHandler::createHandleForPmQosRequest(const char* filename, int priority, int max, int min)
89{
90 int fd = openPmQosNode(filename, priority, max, min);
91 if (fd < 0) {
92 return -1;
93 }
94
95 return createHandleForFd(fd);
96}
97
98int TimeoutPoker::createPmQosHandle(const char* filename,
99 int val)
100{
101 Barrier done;
102 int ret;
103 pushEvent(new PmQosOpenHandleEvent(
104 filename, val, &ret, &done));
105
106 done.wait();
107 return ret;
108}
109
110void TimeoutPoker::requestPmQosTimed(const char* filename,
111 int val, nsecs_t timeout)
112{
113 pushEvent(new PmQosOpenTimedEvent(
114 filename, val, timeout));
115}
116
117int TimeoutPoker::requestPmQos(const char* filename, int val)
118{
119 int pm_qos_fd = open(filename, O_RDWR);
120 if (pm_qos_fd < 0) {
121 ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
122 return -1;
123 }
124 write(pm_qos_fd, &val, sizeof(val));
125 return pm_qos_fd;
126}
127
128int TimeoutPoker::createPmQosHandle(const char* filename,
129 int priority, int max, int min)
130{
131 Barrier done;
132 int ret;
133 pushEvent(new PmQosOpenHandleEvent(
134 filename, priority, max, min, &ret, &done));
135
136 done.wait();
137 return ret;
138}
139
140void TimeoutPoker::requestPmQosTimed(const char* filename,
141 int priority, int max, int min, nsecs_t timeout)
142{
143 pushEvent(new PmQosOpenTimedEvent(
144 filename, priority, max, min, timeout));
145}
146
147int TimeoutPoker::requestPmQos(const char* filename, int priority, int max, int min)
148{
149 int pm_qos_fd = open(filename, O_RDWR);
150 if (pm_qos_fd < 0) {
151 ALOGE("unable to open pm_qos file for %s: %s", filename, strerror(errno));
152 return -1;
153 }
154
155 char command[COMMAND_SIZE];
156 int size = createConstraintCommand((char*)command, COMMAND_SIZE, priority, max, min);
157
158 write(pm_qos_fd, command, size);
159 return pm_qos_fd;
160}
161
162/*
163 * PokeHandler
164 */
165void TimeoutPoker::PokeHandler::sendEventDelayed(
166 nsecs_t delay, TimeoutPoker::QueuedEvent* ev) {
167 Mutex::Autolock _l(mEvLock);
168
169 int key = generateNewKey();
170 mQueuedEvents.add(key, ev);
171 mWorker->mLooper->sendMessageDelayed(delay, this, key);
172}
173
174TimeoutPoker::QueuedEvent*
175TimeoutPoker::PokeHandler::removeEventByKey(
176int what) {
177 Mutex::Autolock _l(mEvLock);
178
179 // msg.what contains a key to retrieve the message parameters
180 TimeoutPoker::QueuedEvent* e =
181 mQueuedEvents.valueFor(what);
182 mQueuedEvents.removeItem(what);
183 return e;
184}
185
186int TimeoutPoker::PokeHandler::generateNewKey(void)
187{
188 return mKey++;
189}
190
191TimeoutPoker::PokeHandler::PokeHandler(TimeoutPoker* poker, Barrier* readyToRun) :
192 mPoker(poker),
193 mKey(0),
194 mSpamRefresh(false)
195{
196 mWorker = new LooperThread(readyToRun);
197 mWorker->run("TimeoutPoker::PokeHandler::LooperThread", PRIORITY_FOREGROUND);
198 readyToRun->wait();
199}
200
201void TimeoutPoker::PokeHandler::handleMessage(const Message& msg)
202{
203 assert(!mQueuedEvents->isEmpty());
204
205 // msg.what contains a key to retrieve the message parameters
206 TimeoutPoker::QueuedEvent* e =
207 removeEventByKey(msg.what);
208
209 if (!e)
210 return;
211
212 e->run(this);
213
214 delete e;
215}
216
217void TimeoutPoker::PokeHandler::openPmQosTimed(const char* filename,
218 int val, nsecs_t timeout)
219{
220 int fd = openPmQosNode(filename, val);
221 if (fd < 0) {
222 return;
223 }
224
225 sendEventDelayed(timeout, new TimeoutEvent(fd));
226}
227
228void TimeoutPoker::PokeHandler::openPmQosTimed(const char* filename,
229 int priority, int max, int min, nsecs_t timeout)
230{
231 int fd = openPmQosNode(filename, priority, max, min);
232 if (fd < 0) {
233 return;
234 }
235
236 sendEventDelayed(timeout, new TimeoutEvent(fd));
237}
238
239
240void TimeoutPoker::PokeHandler::timeoutRequest(int fd)
241{
242 close(fd);
243}
244
245status_t TimeoutPoker::PokeHandler::LooperThread::readyToRun()
246{
247 mLooper = Looper::prepare(0);
248 if (mLooper == 0)
249 return NO_MEMORY;
250 mReadyToRun->open();
251 return NO_ERROR;
252}
253
254bool TimeoutPoker::PokeHandler::LooperThread::threadLoop()
255{
256 int res = mLooper->pollAll(99999);
257 if (res == ALOOPER_POLL_ERROR)
258 ALOGE("Poll returned an error!");
259 return true;
260}
261
262class CallbackContext {
263public:
264 CallbackContext(sp<Looper> looper, int fd) : looper(looper), fd(fd) {}
265
266 sp<Looper> looper;
267 int fd;
268};
269
270static int pipeCloseCb(int handle, int events, void* data)
271{
272 CallbackContext* ctx = (CallbackContext*)data;
273
274 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
275
276 ctx->looper->removeFd(handle);
277 close(handle);
278 close(ctx->fd);
279 delete ctx;
280 return 0;
281 }
282 return 1;
283}
284
285//Reverse arity of result to match call-site usage
286int TimeoutPoker::PokeHandler::listenForHandleToCloseFd(int handle, int fd)
287{
288 //This func is threadsafe
289 return !mWorker->mLooper->addFd(handle, ALOOPER_POLL_CALLBACK,
290 ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP,
291 pipeCloseCb, new CallbackContext(mWorker->mLooper, fd));
292}
293
294int createConstraintCommand(char* command, int size, int priority, int max, int min) {
295 snprintf(command, size, "%d %d %d 0", max, min, priority);
296 return strlen(command);
297}