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