1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <errno.h>
17#include <string.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <sys/socket.h>
21#include <sys/un.h>
22#include <fcntl.h>
23#include <dlfcn.h>
24#include <cutils/uevent.h>
25#include <errno.h>
26#include <sys/poll.h>
27#include <pthread.h>
28#include <linux/netlink.h>
29#include <stdlib.h>
30#include <stdbool.h>
31
32#define LOG_TAG "PowerHAL"
33#include <utils/Log.h>
34
35#include <hardware/hardware.h>
36#include <hardware/power.h>
37
38#define STATE_ON "state=1"
39#define STATE_OFF "state=0"
40#define STATE_HDR_ON "state=2"
41#define STATE_HDR_OFF "state=3"
42
43#define MAX_LENGTH         50
44#define BOOST_SOCKET       "/dev/socket/pb"
45
46#define UEVENT_MSG_LEN 2048
47#define TOTAL_CPUS 4
48#define RETRY_TIME_CHANGING_FREQ 20
49#define SLEEP_USEC_BETWN_RETRY 200
50#define LOW_POWER_MAX_FREQ "1026000"
51#define LOW_POWER_MIN_FREQ "384000"
52#define NORMAL_MAX_FREQ "1512000"
53#define UEVENT_STRING "online@/devices/system/cpu/"
54
55static int client_sockfd;
56static struct sockaddr_un client_addr;
57static int last_state = -1;
58
59static struct pollfd pfd;
60static char *cpu_path_min[] = {
61    "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq",
62    "/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq",
63    "/sys/devices/system/cpu/cpu2/cpufreq/scaling_min_freq",
64    "/sys/devices/system/cpu/cpu3/cpufreq/scaling_min_freq",
65};
66static char *cpu_path_max[] = {
67    "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq",
68    "/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq",
69    "/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq",
70    "/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq",
71};
72static bool freq_set[TOTAL_CPUS];
73static bool low_power_mode = false;
74static pthread_mutex_t low_power_mode_lock = PTHREAD_MUTEX_INITIALIZER;
75
76static void socket_init()
77{
78    if (!client_sockfd) {
79        client_sockfd = socket(PF_UNIX, SOCK_DGRAM, 0);
80        if (client_sockfd < 0) {
81            ALOGE("%s: failed to open: %s", __func__, strerror(errno));
82            return;
83        }
84        memset(&client_addr, 0, sizeof(struct sockaddr_un));
85        client_addr.sun_family = AF_UNIX;
86        snprintf(client_addr.sun_path, UNIX_PATH_MAX, BOOST_SOCKET);
87    }
88}
89
90static int sysfs_write(const char *path, char *s)
91{
92    char buf[80];
93    int len;
94    int fd = open(path, O_WRONLY);
95
96    if (fd < 0) {
97        strerror_r(errno, buf, sizeof(buf));
98        ALOGE("Error opening %s: %s\n", path, buf);
99        return -1;
100    }
101
102    len = write(fd, s, strlen(s));
103    if (len < 0) {
104        strerror_r(errno, buf, sizeof(buf));
105        ALOGE("Error writing to %s: %s\n", path, buf);
106        return -1;
107    }
108
109    close(fd);
110    return 0;
111}
112
113static int uevent_event()
114{
115    char msg[UEVENT_MSG_LEN];
116    char *cp;
117    int n, cpu, ret, retry = RETRY_TIME_CHANGING_FREQ;
118
119    n = recv(pfd.fd, msg, UEVENT_MSG_LEN, MSG_DONTWAIT);
120    if (n <= 0) {
121        return -1;
122    }
123    if (n >= UEVENT_MSG_LEN) {   /* overflow -- discard */
124        return -1;
125    }
126
127    cp = msg;
128
129    if (strstr(cp, UEVENT_STRING)) {
130        n = strlen(cp);
131        errno = 0;
132        cpu = strtol(cp + n - 1, NULL, 10);
133
134        if (errno == EINVAL || errno == ERANGE || cpu < 0 || cpu >= TOTAL_CPUS) {
135            return -1;
136        }
137
138        pthread_mutex_lock(&low_power_mode_lock);
139        if (low_power_mode && !freq_set[cpu]) {
140            while (retry) {
141                sysfs_write(cpu_path_min[cpu], LOW_POWER_MIN_FREQ);
142                ret = sysfs_write(cpu_path_max[cpu], LOW_POWER_MAX_FREQ);
143                if (!ret) {
144                    freq_set[cpu] = true;
145                    break;
146                }
147                usleep(SLEEP_USEC_BETWN_RETRY);
148                retry--;
149           }
150        } else if (!low_power_mode && freq_set[cpu]) {
151             while (retry) {
152                  ret = sysfs_write(cpu_path_max[cpu], NORMAL_MAX_FREQ);
153                  if (!ret) {
154                      freq_set[cpu] = false;
155                      break;
156                  }
157                  usleep(SLEEP_USEC_BETWN_RETRY);
158                  retry--;
159             }
160        }
161        pthread_mutex_unlock(&low_power_mode_lock);
162    }
163    return 0;
164}
165
166void *thread_uevent(__attribute__((unused)) void *x)
167{
168    while (1) {
169        int nevents, ret;
170
171        nevents = poll(&pfd, 1, -1);
172
173        if (nevents == -1) {
174            if (errno == EINTR)
175                continue;
176            ALOGE("powerhal: thread_uevent: poll_wait failed\n");
177            break;
178        }
179        ret = uevent_event();
180        if (ret < 0)
181            ALOGE("Error processing the uevent event");
182    }
183    return NULL;
184}
185
186static void uevent_init()
187{
188    struct sockaddr_nl client;
189    pthread_t tid;
190    pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
191
192    if (pfd.fd < 0) {
193        ALOGE("%s: failed to open: %s", __func__, strerror(errno));
194        return;
195    }
196    memset(&client, 0, sizeof(struct sockaddr_nl));
197    pthread_create(&tid, NULL, thread_uevent, NULL);
198    client.nl_family = AF_NETLINK;
199    client.nl_pid = tid;
200    client.nl_groups = -1;
201    pfd.events = POLLIN;
202    bind(pfd.fd, (void *)&client, sizeof(struct sockaddr_nl));
203    return;
204}
205
206static void power_init(__attribute__((unused)) struct power_module *module)
207{
208    ALOGI("%s", __func__);
209    socket_init();
210    uevent_init();
211}
212
213static void sync_thread(int off)
214{
215    int rc;
216    pid_t client;
217    char data[MAX_LENGTH];
218
219    if (client_sockfd < 0) {
220        ALOGE("%s: boost socket not created", __func__);
221        return;
222    }
223
224    client = getpid();
225
226    if (!off) {
227        snprintf(data, MAX_LENGTH, "2:%d", client);
228        rc = sendto(client_sockfd, data, strlen(data), 0,
229            (const struct sockaddr *)&client_addr, sizeof(struct sockaddr_un));
230    } else {
231        snprintf(data, MAX_LENGTH, "3:%d", client);
232        rc = sendto(client_sockfd, data, strlen(data), 0,
233            (const struct sockaddr *)&client_addr, sizeof(struct sockaddr_un));
234    }
235
236    if (rc < 0) {
237        ALOGE("%s: failed to send: %s", __func__, strerror(errno));
238    }
239}
240
241static void enc_boost(int off)
242{
243    int rc;
244    pid_t client;
245    char data[MAX_LENGTH];
246
247    if (client_sockfd < 0) {
248        ALOGE("%s: boost socket not created", __func__);
249        return;
250    }
251
252    client = getpid();
253
254    if (!off) {
255        snprintf(data, MAX_LENGTH, "5:%d", client);
256        rc = sendto(client_sockfd, data, strlen(data), 0,
257            (const struct sockaddr *)&client_addr, sizeof(struct sockaddr_un));
258    } else {
259        snprintf(data, MAX_LENGTH, "6:%d", client);
260        rc = sendto(client_sockfd, data, strlen(data), 0,
261            (const struct sockaddr *)&client_addr, sizeof(struct sockaddr_un));
262    }
263
264    if (rc < 0) {
265        ALOGE("%s: failed to send: %s", __func__, strerror(errno));
266    }
267}
268
269static void process_video_encode_hint(void *metadata)
270{
271
272    socket_init();
273
274    if (client_sockfd < 0) {
275        ALOGE("%s: boost socket not created", __func__);
276        return;
277    }
278
279    if (metadata) {
280        if (!strncmp(metadata, STATE_ON, sizeof(STATE_ON))) {
281            /* Video encode started */
282            sync_thread(1);
283            enc_boost(1);
284        } else if (!strncmp(metadata, STATE_OFF, sizeof(STATE_OFF))) {
285            /* Video encode stopped */
286            sync_thread(0);
287            enc_boost(0);
288        }  else if (!strncmp(metadata, STATE_HDR_ON, sizeof(STATE_HDR_ON))) {
289            /* HDR usecase started */
290        } else if (!strncmp(metadata, STATE_HDR_OFF, sizeof(STATE_HDR_OFF))) {
291            /* HDR usecase stopped */
292        }else
293            return;
294    } else {
295        return;
296    }
297}
298
299
300static void touch_boost()
301{
302    int rc;
303    pid_t client;
304    char data[MAX_LENGTH];
305
306    if (client_sockfd < 0) {
307        ALOGE("%s: boost socket not created", __func__);
308        return;
309    }
310
311    client = getpid();
312
313    snprintf(data, MAX_LENGTH, "1:%d", client);
314    rc = sendto(client_sockfd, data, strlen(data), 0,
315        (const struct sockaddr *)&client_addr, sizeof(struct sockaddr_un));
316    if (rc < 0) {
317        ALOGE("%s: failed to send: %s", __func__, strerror(errno));
318    }
319}
320
321static void power_set_interactive(__attribute__((unused)) struct power_module *module, int on)
322{
323    if (last_state == -1) {
324        last_state = on;
325    } else {
326        if (last_state == on)
327            return;
328        else
329            last_state = on;
330    }
331
332    ALOGV("%s %s", __func__, (on ? "ON" : "OFF"));
333    if (on) {
334        sync_thread(0);
335        touch_boost();
336    } else {
337        sync_thread(1);
338    }
339}
340
341static void power_hint( __attribute__((unused)) struct power_module *module,
342                      power_hint_t hint, __attribute__((unused)) void *data)
343{
344    int cpu, ret;
345
346    switch (hint) {
347        case POWER_HINT_INTERACTION:
348            ALOGV("POWER_HINT_INTERACTION");
349            touch_boost();
350            break;
351#if 0
352        case POWER_HINT_VSYNC:
353            ALOGV("POWER_HINT_VSYNC %s", (data ? "ON" : "OFF"));
354            break;
355#endif
356        case POWER_HINT_VIDEO_ENCODE:
357            process_video_encode_hint(data);
358            break;
359
360        case POWER_HINT_LOW_POWER:
361             pthread_mutex_lock(&low_power_mode_lock);
362             if (data) {
363                 low_power_mode = true;
364                 for (cpu = 0; cpu < TOTAL_CPUS; cpu++) {
365                     sysfs_write(cpu_path_min[cpu], LOW_POWER_MIN_FREQ);
366                     ret = sysfs_write(cpu_path_max[cpu], LOW_POWER_MAX_FREQ);
367                     if (!ret) {
368                         freq_set[cpu] = true;
369                     }
370                 }
371             } else {
372                 low_power_mode = false;
373                 for (cpu = 0; cpu < TOTAL_CPUS; cpu++) {
374                     ret = sysfs_write(cpu_path_max[cpu], NORMAL_MAX_FREQ);
375                     if (!ret) {
376                         freq_set[cpu] = false;
377                     }
378                 }
379             }
380             pthread_mutex_unlock(&low_power_mode_lock);
381             break;
382        default:
383             break;
384    }
385}
386
387static struct hw_module_methods_t power_module_methods = {
388    .open = NULL,
389};
390
391struct power_module HAL_MODULE_INFO_SYM = {
392    .common = {
393        .tag = HARDWARE_MODULE_TAG,
394        .module_api_version = POWER_MODULE_API_VERSION_0_2,
395        .hal_api_version = HARDWARE_HAL_API_VERSION,
396        .id = POWER_HARDWARE_MODULE_ID,
397        .name = "Mako Power HAL",
398        .author = "The Android Open Source Project",
399        .methods = &power_module_methods,
400    },
401
402    .init = power_init,
403    .setInteractive = power_set_interactive,
404    .powerHint = power_hint,
405};
406