blob: 135e6d3275dbd68e8c7e8361a41c74c244ab9125 [file] [log] [blame]
Anurag Singh6ec12062012-10-02 09:59:01 -07001/*
2 * Copyright (c) 2012, 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.
Duy Truong70222452013-02-10 06:35:11 -080013 * * Neither the name of The Linux Foundation nor the names of its
Anurag Singh6ec12062012-10-02 09:59:01 -070014 * 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
30#define LOG_NIDEBUG 0
31
32#include <dlfcn.h>
33#include <fcntl.h>
34#include <errno.h>
35
36#include "utils.h"
37#include "list.h"
38#include "hint-data.h"
39
40#define LOG_TAG "QCOM PowerHAL"
41#include <utils/Log.h>
42
43static int qcopt_handle_unavailable;
44static void *qcopt_handle;
45static int (*perf_vote_turnoff_ondemand_io_busy)(int vote);
46static int perf_vote_ondemand_io_busy_unavailable;
47static int (*perf_vote_lower_ondemand_sdf)(int vote);
48static int perf_vote_ondemand_sdf_unavailable;
49static int (*perf_lock_acq)(unsigned long handle, int duration,
50 int list[], int numArgs);
51static int perf_lock_acq_unavailable;
52static int (*perf_lock_rel)(unsigned long handle);
53static int perf_lock_rel_unavailable;
54static struct list_node active_hint_list_head;
55
56static void *get_qcopt_handle()
57{
58 if (qcopt_handle_unavailable) {
59 return NULL;
60 }
61
62 if (!qcopt_handle) {
63 char qcopt_lib_path[PATH_MAX] = {0};
64 dlerror();
65
66 if (property_get("ro.vendor.extension_library", qcopt_lib_path,
67 NULL) != 0) {
68 if((qcopt_handle = dlopen(qcopt_lib_path, RTLD_NOW)) == NULL) {
69 qcopt_handle_unavailable = 1;
70 ALOGE("Unable to open %s: %s\n", qcopt_lib_path,
71 dlerror());
72 }
73 } else {
74 qcopt_handle_unavailable = 1;
75 ALOGE("Property ro.vendor.extension_library does not exist.");
76 }
77 }
78
79 return qcopt_handle;
80}
81
82int sysfs_read(char *path, char *s, int num_bytes)
83{
84 char buf[80];
85 int count;
86 int ret = 0;
87 int fd = open(path, O_RDONLY);
88
89 if (fd < 0) {
90 strerror_r(errno, buf, sizeof(buf));
91 ALOGE("Error opening %s: %s\n", path, buf);
92
93 return -1;
94 }
95
96 if ((count = read(fd, s, num_bytes - 1)) < 0) {
97 strerror_r(errno, buf, sizeof(buf));
98 ALOGE("Error writing to %s: %s\n", path, buf);
99
100 ret = -1;
101 } else {
102 s[count] = '\0';
103 }
104
105 close(fd);
106
107 return ret;
108}
109
110int sysfs_write(char *path, char *s)
111{
112 char buf[80];
113 int len;
114 int ret = 0;
115 int fd = open(path, O_WRONLY);
116
117 if (fd < 0) {
118 strerror_r(errno, buf, sizeof(buf));
119 ALOGE("Error opening %s: %s\n", path, buf);
120 return -1 ;
121 }
122
123 len = write(fd, s, strlen(s));
124 if (len < 0) {
125 strerror_r(errno, buf, sizeof(buf));
126 ALOGE("Error writing to %s: %s\n", path, buf);
127
128 ret = -1;
129 }
130
131 close(fd);
132
133 return ret;
134}
135
136int get_scaling_governor(char governor[], int size)
137{
138 if (sysfs_read(SCALING_GOVERNOR_PATH, governor,
139 size) == -1) {
140 // Can't obtain the scaling governor. Return.
141 return -1;
142 } else {
143 // Strip newline at the end.
144 int len = strlen(governor);
145
146 len--;
147
148 while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r'))
149 governor[len--] = '\0';
150 }
151
152 return 0;
153}
154
155void perform_hint_action(int hint_id, int resource_values[], int num_resources)
156{
157 void *handle;
158
159 if ((handle = get_qcopt_handle())) {
160 if (!perf_lock_acq_unavailable && !perf_lock_rel_unavailable) {
161 perf_lock_acq = dlsym(handle, "perf_lock_acq");
162
163 if (perf_lock_acq) {
164 /* Acquire an indefinite lock for the requested resources. */
165 int lock_handle = perf_lock_acq(0, 0, resource_values,
166 num_resources);
167
168 if (lock_handle == -1) {
169 ALOGE("Failed to acquire lock.");
170 } else {
171 /* Add this handle to our internal hint-list. */
172 struct hint_data *new_hint =
173 (struct hint_data *)malloc(sizeof(struct hint_data));
174
175 if (new_hint) {
176 if (!active_hint_list_head.compare) {
177 active_hint_list_head.compare =
178 (int (*)(void *, void *))hint_compare;
179 active_hint_list_head.dump = (void (*)(void *))hint_dump;
180 }
181
182 new_hint->hint_id = hint_id;
183 new_hint->perflock_handle = lock_handle;
184
185 if (add_list_node(&active_hint_list_head, new_hint) == NULL) {
186 free(new_hint);
187 /* Can't keep track of this lock. Release it. */
188 perf_lock_rel(lock_handle);
189 ALOGE("Failed to process hint.");
190 }
191 } else {
192 /* Can't keep track of this lock. Release it. */
193 perf_lock_rel(lock_handle);
194 ALOGE("Failed to process hint.");
195 }
196 }
197 } else {
198 perf_lock_acq_unavailable = 1;
199 ALOGE("Can't commit hint action. Lock acquire function is not available.");
200 }
201 }
202 }
203}
204
205void undo_hint_action(int hint_id)
206{
207 void *handle;
208
209 if ((handle = get_qcopt_handle())) {
210 if (!perf_lock_acq_unavailable && !perf_lock_rel_unavailable) {
211 perf_lock_rel = dlsym(handle, "perf_lock_rel");
212
213 if (perf_lock_rel) {
214 /* Get hint-data associated with this hint-id */
215 struct list_node *found_node;
216 struct hint_data temp_hint_data = {
217 .hint_id = hint_id
218 };
219
220 if ((found_node = find_node(&active_hint_list_head,
221 &temp_hint_data)) != NULL) {
222 /* Release this lock. */
223 struct hint_data *found_hint_data =
224 (struct hint_data *)(found_node->data);
225
226 if (found_hint_data &&
227 (perf_lock_rel(found_hint_data->perflock_handle) == -1)) {
228 ALOGE("Perflock release failed.");
229 } else {
230 ALOGI("Perflock released.");
231 }
232
233 if (found_node->data) {
234 /* We can free the hint-data for this node. */
235 free(found_node->data);
236 }
237
238 remove_list_node(&active_hint_list_head, found_node);
239 } else {
240 ALOGE("Invalid hint ID.");
241 }
242 } else {
243 perf_lock_rel_unavailable = 1;
244 ALOGE("Can't undo hint action. Lock release function is not available.");
245 }
246 }
247 }
248}
249
250void vote_ondemand_io_busy_off()
251{
252 void *handle;
253
254 if ((handle = get_qcopt_handle())) {
255 if (!perf_vote_ondemand_io_busy_unavailable) {
256 perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
257 "perf_vote_turnoff_ondemand_io_busy");
258
259 if (perf_vote_turnoff_ondemand_io_busy) {
260 /* Vote to turn io_is_busy off */
261 perf_vote_turnoff_ondemand_io_busy(1);
262 } else {
263 perf_vote_ondemand_io_busy_unavailable = 1;
264 ALOGE("Can't set io_busy_status.");
265 }
266 }
267 }
268}
269
270void unvote_ondemand_io_busy_off()
271{
272 void *handle;
273
274 if ((handle = get_qcopt_handle())) {
275 if (!perf_vote_ondemand_io_busy_unavailable) {
276 perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
277 "perf_vote_turnoff_ondemand_io_busy");
278
279 if (perf_vote_turnoff_ondemand_io_busy) {
280 /* Remove vote to turn io_busy off. */
281 perf_vote_turnoff_ondemand_io_busy(0);
282 } else {
283 perf_vote_ondemand_io_busy_unavailable = 1;
284 ALOGE("Can't set io_busy_status.");
285 }
286 }
287 }
288}
289
290void vote_ondemand_sdf_low()
291{
292 void *handle;
293
294 if ((handle = get_qcopt_handle())) {
295 if (!perf_vote_ondemand_sdf_unavailable) {
296 perf_vote_lower_ondemand_sdf = dlsym(handle,
297 "perf_vote_lower_ondemand_sdf");
298
299 if (perf_vote_lower_ondemand_sdf) {
300 perf_vote_lower_ondemand_sdf(1);
301 } else {
302 perf_vote_ondemand_sdf_unavailable = 1;
303 ALOGE("Can't set sampling_down_factor.");
304 }
305 }
306 }
307}
308
309void unvote_ondemand_sdf_low() {
310 void *handle;
311
312 if ((handle = get_qcopt_handle())) {
313 if (!perf_vote_ondemand_sdf_unavailable) {
314 perf_vote_lower_ondemand_sdf = dlsym(handle,
315 "perf_vote_lower_ondemand_sdf");
316
317 if (perf_vote_lower_ondemand_sdf) {
318 /* Remove vote to lower sampling down factor. */
319 perf_vote_lower_ondemand_sdf(0);
320 } else {
321 perf_vote_ondemand_sdf_unavailable = 1;
322 ALOGE("Can't set sampling_down_factor.");
323 }
324 }
325 }
326}