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
51char 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
58static void *qcopt_handle;
59static int (*perf_lock_acq)(unsigned long handle, int duration,
60    int list[], int numArgs);
61static int (*perf_lock_rel)(unsigned long handle);
62static struct list_node active_hint_list_head;
63
64static 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
83static 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
108static 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
116int 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
144int 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
170int 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
189int 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
207void 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
225int 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
243void release_request(int lock_handle) {
244    if (qcopt_handle && perf_lock_rel)
245        perf_lock_rel(lock_handle);
246}
247
248void 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
302void 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 */
343void undo_initial_hint_action()
344{
345    if (qcopt_handle) {
346        if (perf_lock_rel) {
347            perf_lock_rel(1);
348        }
349    }
350}
351