Home | History | Annotate | Download | only in power
      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