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 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 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 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 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 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 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 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 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 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 243 void release_request(int lock_handle) { 244 if (qcopt_handle && perf_lock_rel) 245 perf_lock_rel(lock_handle); 246 } 247 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 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 */ 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