1 /*
2 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 #define LOG_NDEBUG 1
30
31 #include <dlfcn.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <sys/stat.h>
38
39 #include "utils.h"
40 #include "list.h"
41 #include "hint-data.h"
42 #include "power-common.h"
43
44 #define LOG_TAG "QCOM PowerHAL"
45 #include <utils/Log.h>
46
47 #ifndef INTERACTION_BOOST
48 #define INTERACTION_BOOST
49 #endif
50
51 char scaling_gov_path[4][80] ={
52 "sys/devices/system/cpu/cpu0/cpufreq/scaling_governor",
53 "sys/devices/system/cpu/cpu1/cpufreq/scaling_governor",
54 "sys/devices/system/cpu/cpu2/cpufreq/scaling_governor",
55 "sys/devices/system/cpu/cpu3/cpufreq/scaling_governor"
56 };
57
58 static void *qcopt_handle;
59 static int (*perf_lock_acq)(unsigned long handle, int duration,
60 int list[], int numArgs);
61 static int (*perf_lock_rel)(unsigned long handle);
62 static struct list_node active_hint_list_head;
63
get_qcopt_handle()64 static void *get_qcopt_handle()
65 {
66 char qcopt_lib_path[PATH_MAX] = {0};
67 void *handle = NULL;
68
69 dlerror();
70
71 if (property_get("ro.vendor.extension_library", qcopt_lib_path,
72 NULL)) {
73 handle = dlopen(qcopt_lib_path, RTLD_NOW);
74 if (!handle) {
75 ALOGE("Unable to open %s: %s\n", qcopt_lib_path,
76 dlerror());
77 }
78 }
79
80 return handle;
81 }
82
initialize(void)83 static void __attribute__ ((constructor)) initialize(void)
84 {
85 qcopt_handle = get_qcopt_handle();
86
87 if (!qcopt_handle) {
88 ALOGE("Failed to get qcopt handle.\n");
89 } else {
90 /*
91 * qc-opt handle obtained. Get the perflock acquire/release
92 * function pointers.
93 */
94 perf_lock_acq = dlsym(qcopt_handle, "perf_lock_acq");
95
96 if (!perf_lock_acq) {
97 ALOGE("Unable to get perf_lock_acq function handle.\n");
98 }
99
100 perf_lock_rel = dlsym(qcopt_handle, "perf_lock_rel");
101
102 if (!perf_lock_rel) {
103 ALOGE("Unable to get perf_lock_rel function handle.\n");
104 }
105 }
106 }
107
cleanup(void)108 static void __attribute__ ((destructor)) cleanup(void)
109 {
110 if (qcopt_handle) {
111 if (dlclose(qcopt_handle))
112 ALOGE("Error occurred while closing qc-opt library.");
113 }
114 }
115
sysfs_read(char * path,char * s,int num_bytes)116 int sysfs_read(char *path, char *s, int num_bytes)
117 {
118 char buf[80];
119 int count;
120 int ret = 0;
121 int fd = open(path, O_RDONLY);
122
123 if (fd < 0) {
124 strerror_r(errno, buf, sizeof(buf));
125 ALOGE("Error opening %s: %s\n", path, buf);
126
127 return -1;
128 }
129
130 if ((count = read(fd, s, num_bytes - 1)) < 0) {
131 strerror_r(errno, buf, sizeof(buf));
132 ALOGE("Error writing to %s: %s\n", path, buf);
133
134 ret = -1;
135 } else {
136 s[count] = '\0';
137 }
138
139 close(fd);
140
141 return ret;
142 }
143
sysfs_write(char * path,char * s)144 int sysfs_write(char *path, char *s)
145 {
146 char buf[80];
147 int len;
148 int ret = 0;
149 int fd = open(path, O_WRONLY);
150
151 if (fd < 0) {
152 strerror_r(errno, buf, sizeof(buf));
153 ALOGE("Error opening %s: %s\n", path, buf);
154 return -1 ;
155 }
156
157 len = write(fd, s, strlen(s));
158 if (len < 0) {
159 strerror_r(errno, buf, sizeof(buf));
160 ALOGE("Error writing to %s: %s\n", path, buf);
161
162 ret = -1;
163 }
164
165 close(fd);
166
167 return ret;
168 }
169
get_scaling_governor(char governor[],int size)170 int get_scaling_governor(char governor[], int size)
171 {
172 if (sysfs_read(SCALING_GOVERNOR_PATH, governor,
173 size) == -1) {
174 // Can't obtain the scaling governor. Return.
175 return -1;
176 } else {
177 // Strip newline at the end.
178 int len = strlen(governor);
179
180 len--;
181
182 while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r'))
183 governor[len--] = '\0';
184 }
185
186 return 0;
187 }
188
get_scaling_governor_check_cores(char governor[],int size,int core_num)189 int get_scaling_governor_check_cores(char governor[], int size,int core_num)
190 {
191
192 if (sysfs_read(scaling_gov_path[core_num], governor,
193 size) == -1) {
194 // Can't obtain the scaling governor. Return.
195 return -1;
196 }
197
198 // Strip newline at the end.
199 int len = strlen(governor);
200 len--;
201 while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r'))
202 governor[len--] = '\0';
203
204 return 0;
205 }
206
interaction(int duration,int num_args,int opt_list[])207 void interaction(int duration, int num_args, int opt_list[])
208 {
209 #ifdef INTERACTION_BOOST
210 static int lock_handle = 0;
211
212 if (duration < 0 || num_args < 1 || opt_list[0] == 0)
213 return;
214
215 if (qcopt_handle) {
216 if (perf_lock_acq) {
217 lock_handle = perf_lock_acq(lock_handle, duration, opt_list, num_args);
218 if (lock_handle == -1)
219 ALOGE("Failed to acquire lock.");
220 }
221 }
222 #endif
223 }
224
interaction_with_handle(int lock_handle,int duration,int num_args,int opt_list[])225 int interaction_with_handle(int lock_handle, int duration, int num_args, int opt_list[])
226 {
227 #ifdef INTERACTION_BOOST
228 if (duration < 0 || num_args < 1 || opt_list[0] == 0)
229 return 0;
230
231 if (qcopt_handle) {
232 if (perf_lock_acq) {
233 lock_handle = perf_lock_acq(lock_handle, duration, opt_list, num_args);
234 if (lock_handle == -1)
235 ALOGE("Failed to acquire lock.");
236 }
237 }
238 return lock_handle;
239 #endif
240 return 0;
241 }
242
release_request(int lock_handle)243 void release_request(int lock_handle) {
244 if (qcopt_handle && perf_lock_rel)
245 perf_lock_rel(lock_handle);
246 }
247
perform_hint_action(int hint_id,int resource_values[],int num_resources)248 void perform_hint_action(int hint_id, int resource_values[], int num_resources)
249 {
250 if (qcopt_handle) {
251 struct hint_data temp_hint_data = {
252 .hint_id = hint_id
253 };
254 struct list_node *found_node = find_node(&active_hint_list_head,
255 &temp_hint_data);
256 if (found_node) {
257 ALOGE("hint ID %d already active", hint_id);
258 return;
259 }
260 if (perf_lock_acq) {
261 /* Acquire an indefinite lock for the requested resources. */
262 int lock_handle = perf_lock_acq(0, 0, resource_values,
263 num_resources);
264
265 if (lock_handle == -1) {
266 ALOGE("Failed to acquire lock.");
267 } else {
268 /* Add this handle to our internal hint-list. */
269 struct hint_data *new_hint =
270 (struct hint_data *)malloc(sizeof(struct hint_data));
271
272 if (new_hint) {
273 if (!active_hint_list_head.compare) {
274 active_hint_list_head.compare =
275 (int (*)(void *, void *))hint_compare;
276 active_hint_list_head.dump = (void (*)(void *))hint_dump;
277 }
278
279 new_hint->hint_id = hint_id;
280 new_hint->perflock_handle = lock_handle;
281
282 if (add_list_node(&active_hint_list_head, new_hint) == NULL) {
283 free(new_hint);
284 /* Can't keep track of this lock. Release it. */
285 if (perf_lock_rel)
286 perf_lock_rel(lock_handle);
287
288 ALOGE("Failed to process hint.");
289 }
290 } else {
291 /* Can't keep track of this lock. Release it. */
292 if (perf_lock_rel)
293 perf_lock_rel(lock_handle);
294
295 ALOGE("Failed to process hint.");
296 }
297 }
298 }
299 }
300 }
301
undo_hint_action(int hint_id)302 void undo_hint_action(int hint_id)
303 {
304 if (qcopt_handle) {
305 if (perf_lock_rel) {
306 /* Get hint-data associated with this hint-id */
307 struct list_node *found_node;
308 struct hint_data temp_hint_data = {
309 .hint_id = hint_id
310 };
311
312 found_node = find_node(&active_hint_list_head,
313 &temp_hint_data);
314
315 if (found_node) {
316 /* Release this lock. */
317 struct hint_data *found_hint_data =
318 (struct hint_data *)(found_node->data);
319
320 if (found_hint_data) {
321 if (perf_lock_rel(found_hint_data->perflock_handle) == -1)
322 ALOGE("Perflock release failed: %d", hint_id);
323 }
324
325 if (found_node->data) {
326 /* We can free the hint-data for this node. */
327 free(found_node->data);
328 }
329
330 remove_list_node(&active_hint_list_head, found_node);
331 ALOGV("Undo of hint ID %d succeeded", hint_id);
332 } else {
333 ALOGE("Invalid hint ID: %d", hint_id);
334 }
335 }
336 }
337 }
338
339 /*
340 * Used to release initial lock holding
341 * two cores online when the display is on
342 */
undo_initial_hint_action()343 void undo_initial_hint_action()
344 {
345 if (qcopt_handle) {
346 if (perf_lock_rel) {
347 perf_lock_rel(1);
348 }
349 }
350 }
351