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