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_NIDEBUG 0 30 31 #include <dlfcn.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/stat.h> 37 38 #include "utils.h" 39 #include "list.h" 40 #include "hint-data.h" 41 #include "power-common.h" 42 43 #define LOG_TAG "QCOM PowerHAL" 44 #include <utils/Log.h> 45 46 #define INTERACTION_BOOST 47 48 char scaling_gov_path[4][80] ={ 49 "sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", 50 "sys/devices/system/cpu/cpu1/cpufreq/scaling_governor", 51 "sys/devices/system/cpu/cpu2/cpufreq/scaling_governor", 52 "sys/devices/system/cpu/cpu3/cpufreq/scaling_governor" 53 }; 54 55 static void *qcopt_handle; 56 static int (*perf_lock_acq)(unsigned long handle, int duration, 57 int list[], int numArgs); 58 static int (*perf_lock_rel)(unsigned long handle); 59 static struct list_node active_hint_list_head; 60 61 static void *get_qcopt_handle() 62 { 63 char qcopt_lib_path[PATH_MAX] = {0}; 64 void *handle = NULL; 65 66 dlerror(); 67 68 if (property_get("ro.vendor.extension_library", qcopt_lib_path, 69 NULL)) { 70 handle = dlopen(qcopt_lib_path, RTLD_NOW); 71 if (!handle) { 72 ALOGE("Unable to open %s: %s\n", qcopt_lib_path, 73 dlerror()); 74 } 75 } 76 77 return handle; 78 } 79 80 static void __attribute__ ((constructor)) initialize(void) 81 { 82 qcopt_handle = get_qcopt_handle(); 83 84 if (!qcopt_handle) { 85 ALOGE("Failed to get qcopt handle.\n"); 86 } else { 87 /* 88 * qc-opt handle obtained. Get the perflock acquire/release 89 * function pointers. 90 */ 91 perf_lock_acq = dlsym(qcopt_handle, "perf_lock_acq"); 92 93 if (!perf_lock_acq) { 94 ALOGE("Unable to get perf_lock_acq function handle.\n"); 95 } 96 97 perf_lock_rel = dlsym(qcopt_handle, "perf_lock_rel"); 98 99 if (!perf_lock_rel) { 100 ALOGE("Unable to get perf_lock_rel function handle.\n"); 101 } 102 } 103 } 104 105 static void __attribute__ ((destructor)) cleanup(void) 106 { 107 if (qcopt_handle) { 108 if (dlclose(qcopt_handle)) 109 ALOGE("Error occurred while closing qc-opt library."); 110 } 111 } 112 113 int sysfs_read(char *path, char *s, int num_bytes) 114 { 115 char buf[80]; 116 int count; 117 int ret = 0; 118 int fd = open(path, O_RDONLY); 119 120 if (fd < 0) { 121 strerror_r(errno, buf, sizeof(buf)); 122 ALOGE("Error opening %s: %s\n", path, buf); 123 124 return -1; 125 } 126 127 if ((count = read(fd, s, num_bytes - 1)) < 0) { 128 strerror_r(errno, buf, sizeof(buf)); 129 ALOGE("Error writing to %s: %s\n", path, buf); 130 131 ret = -1; 132 } else { 133 s[count] = '\0'; 134 } 135 136 close(fd); 137 138 return ret; 139 } 140 141 int sysfs_write(char *path, char *s) 142 { 143 char buf[80]; 144 int len; 145 int ret = 0; 146 int fd = open(path, O_WRONLY); 147 148 if (fd < 0) { 149 strerror_r(errno, buf, sizeof(buf)); 150 ALOGE("Error opening %s: %s\n", path, buf); 151 return -1 ; 152 } 153 154 len = write(fd, s, strlen(s)); 155 if (len < 0) { 156 strerror_r(errno, buf, sizeof(buf)); 157 ALOGE("Error writing to %s: %s\n", path, buf); 158 159 ret = -1; 160 } 161 162 close(fd); 163 164 return ret; 165 } 166 167 int get_scaling_governor(char governor[], int size) 168 { 169 if (sysfs_read(SCALING_GOVERNOR_PATH, governor, 170 size) == -1) { 171 // Can't obtain the scaling governor. Return. 172 return -1; 173 } else { 174 // Strip newline at the end. 175 int len = strlen(governor); 176 177 len--; 178 179 while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r')) 180 governor[len--] = '\0'; 181 } 182 183 return 0; 184 } 185 186 int get_scaling_governor_check_cores(char governor[], int size,int core_num) 187 { 188 189 if (sysfs_read(scaling_gov_path[core_num], governor, 190 size) == -1) { 191 // Can't obtain the scaling governor. Return. 192 return -1; 193 } 194 195 // Strip newline at the end. 196 int len = strlen(governor); 197 len--; 198 while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r')) 199 governor[len--] = '\0'; 200 201 return 0; 202 } 203 204 void interaction(int duration, int num_args, int opt_list[]) 205 { 206 #ifdef INTERACTION_BOOST 207 static int lock_handle = 0; 208 209 if (duration < 0 || num_args < 1 || opt_list[0] == NULL) 210 return; 211 212 if (qcopt_handle) { 213 if (perf_lock_acq) { 214 lock_handle = perf_lock_acq(lock_handle, duration, opt_list, num_args); 215 if (lock_handle == -1) 216 ALOGE("Failed to acquire lock."); 217 } 218 } 219 #endif 220 } 221 222 int interaction_with_handle(int lock_handle, int duration, int num_args, int opt_list[]) 223 { 224 #ifdef INTERACTION_BOOST 225 if (duration < 0 || num_args < 1 || opt_list[0] == NULL) 226 return 0; 227 228 if (qcopt_handle) { 229 if (perf_lock_acq) { 230 lock_handle = perf_lock_acq(lock_handle, duration, opt_list, num_args); 231 if (lock_handle == -1) 232 ALOGE("Failed to acquire lock."); 233 } 234 } 235 return lock_handle; 236 #endif 237 return 0; 238 } 239 240 void release_request(int lock_handle) { 241 if (qcopt_handle && perf_lock_rel) 242 perf_lock_rel(lock_handle); 243 } 244 245 void perform_hint_action(int hint_id, int resource_values[], int num_resources) 246 { 247 if (qcopt_handle) { 248 struct hint_data temp_hint_data = { 249 .hint_id = hint_id 250 }; 251 struct list_node *found_node = find_node(&active_hint_list_head, 252 &temp_hint_data); 253 if (found_node) { 254 ALOGE("hint ID %d already active", hint_id); 255 return; 256 } 257 if (perf_lock_acq) { 258 /* Acquire an indefinite lock for the requested resources. */ 259 int lock_handle = perf_lock_acq(0, 0, resource_values, 260 num_resources); 261 262 if (lock_handle == -1) { 263 ALOGE("Failed to acquire lock."); 264 } else { 265 /* Add this handle to our internal hint-list. */ 266 struct hint_data *new_hint = 267 (struct hint_data *)malloc(sizeof(struct hint_data)); 268 269 if (new_hint) { 270 if (!active_hint_list_head.compare) { 271 active_hint_list_head.compare = 272 (int (*)(void *, void *))hint_compare; 273 active_hint_list_head.dump = (void (*)(void *))hint_dump; 274 } 275 276 new_hint->hint_id = hint_id; 277 new_hint->perflock_handle = lock_handle; 278 279 if (add_list_node(&active_hint_list_head, new_hint) == NULL) { 280 free(new_hint); 281 /* Can't keep track of this lock. Release it. */ 282 if (perf_lock_rel) 283 perf_lock_rel(lock_handle); 284 285 ALOGE("Failed to process hint."); 286 } 287 } else { 288 /* Can't keep track of this lock. Release it. */ 289 if (perf_lock_rel) 290 perf_lock_rel(lock_handle); 291 292 ALOGE("Failed to process hint."); 293 } 294 } 295 } 296 } 297 } 298 299 void undo_hint_action(int hint_id) 300 { 301 if (qcopt_handle) { 302 if (perf_lock_rel) { 303 /* Get hint-data associated with this hint-id */ 304 struct list_node *found_node; 305 struct hint_data temp_hint_data = { 306 .hint_id = hint_id 307 }; 308 309 found_node = find_node(&active_hint_list_head, 310 &temp_hint_data); 311 312 if (found_node) { 313 /* Release this lock. */ 314 struct hint_data *found_hint_data = 315 (struct hint_data *)(found_node->data); 316 317 if (found_hint_data) { 318 if (perf_lock_rel(found_hint_data->perflock_handle) == -1) 319 ALOGE("Perflock release failed: %d", hint_id); 320 } 321 322 if (found_node->data) { 323 /* We can free the hint-data for this node. */ 324 free(found_node->data); 325 } 326 327 remove_list_node(&active_hint_list_head, found_node); 328 ALOGV("Undo of hint ID %d succeeded", hint_id); 329 } else { 330 ALOGE("Invalid hint ID: %d", hint_id); 331 } 332 } 333 } 334 } 335 336 /* 337 * Used to release initial lock holding 338 * two cores online when the display is on 339 */ 340 void undo_initial_hint_action() 341 { 342 if (qcopt_handle) { 343 if (perf_lock_rel) { 344 perf_lock_rel(1); 345 } 346 } 347 } 348