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 <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