Home | History | Annotate | Download | only in power
      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 "729600"
     51 #define LOW_POWER_MIN_FREQ "300000"
     52 #define NORMAL_MAX_FREQ "2265600"
     53 #define UEVENT_STRING "online@/devices/system/cpu/"
     54 
     55 static int client_sockfd;
     56 static struct sockaddr_un client_addr;
     57 static int last_state = -1;
     58 
     59 static struct pollfd pfd;
     60 static 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 };
     66 static 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 };
     72 static bool freq_set[TOTAL_CPUS];
     73 static bool low_power_mode = false;
     74 static pthread_mutex_t low_power_mode_lock = PTHREAD_MUTEX_INITIALIZER;
     75 
     76 static 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 
     90 static 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 
    113 static 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 
    166 void *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 
    186 static 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 
    206 static void power_init(__attribute__((unused)) struct power_module *module)
    207 {
    208     ALOGI("%s", __func__);
    209     socket_init();
    210     uevent_init();
    211 }
    212 
    213 static 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 
    241 static 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 
    269 static 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 
    300 static 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 
    321 static 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 
    341 static 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 
    387 static struct hw_module_methods_t power_module_methods = {
    388     .open = NULL,
    389 };
    390 
    391 struct 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 = "Hammerhead 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