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