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