Home | History | Annotate | Download | only in libsuspend
      1 /*
      2  * Copyright (C) 2012 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 
     17 #include <errno.h>
     18 #include <fcntl.h>
     19 #include <pthread.h>
     20 #include <stdbool.h>
     21 #include <stddef.h>
     22 #include <string.h>
     23 #include <sys/types.h>
     24 #include <sys/stat.h>
     25 #include <unistd.h>
     26 
     27 #define LOG_TAG "libsuspend"
     28 #include <cutils/log.h>
     29 
     30 #include "autosuspend_ops.h"
     31 
     32 #define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state"
     33 #define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep"
     34 #define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake"
     35 
     36 static int sPowerStatefd;
     37 static const char *pwr_state_mem = "mem";
     38 static const char *pwr_state_on = "on";
     39 static pthread_t earlysuspend_thread;
     40 static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER;
     41 static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER;
     42 static bool wait_for_earlysuspend;
     43 static enum {
     44     EARLYSUSPEND_ON,
     45     EARLYSUSPEND_MEM,
     46 } earlysuspend_state = EARLYSUSPEND_ON;
     47 
     48 int wait_for_fb_wake(void)
     49 {
     50     int err = 0;
     51     char buf;
     52     int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0));
     53     // if the file doesn't exist, the error will be caught in read() below
     54     err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
     55     ALOGE_IF(err < 0,
     56             "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
     57     close(fd);
     58     return err < 0 ? err : 0;
     59 }
     60 
     61 static int wait_for_fb_sleep(void)
     62 {
     63     int err = 0;
     64     char buf;
     65     int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0));
     66     // if the file doesn't exist, the error will be caught in read() below
     67     err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
     68     ALOGE_IF(err < 0,
     69             "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
     70     close(fd);
     71     return err < 0 ? err : 0;
     72 }
     73 
     74 static void *earlysuspend_thread_func(void __unused *arg)
     75 {
     76     while (1) {
     77         if (wait_for_fb_sleep()) {
     78             ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n");
     79             return NULL;
     80         }
     81         pthread_mutex_lock(&earlysuspend_mutex);
     82         earlysuspend_state = EARLYSUSPEND_MEM;
     83         pthread_cond_signal(&earlysuspend_cond);
     84         pthread_mutex_unlock(&earlysuspend_mutex);
     85 
     86         if (wait_for_fb_wake()) {
     87             ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n");
     88             return NULL;
     89         }
     90         pthread_mutex_lock(&earlysuspend_mutex);
     91         earlysuspend_state = EARLYSUSPEND_ON;
     92         pthread_cond_signal(&earlysuspend_cond);
     93         pthread_mutex_unlock(&earlysuspend_mutex);
     94     }
     95 }
     96 static int autosuspend_earlysuspend_enable(void)
     97 {
     98     char buf[80];
     99     int ret;
    100 
    101     ALOGV("autosuspend_earlysuspend_enable\n");
    102 
    103     ret = write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem));
    104     if (ret < 0) {
    105         strerror_r(errno, buf, sizeof(buf));
    106         ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
    107         goto err;
    108     }
    109 
    110     if (wait_for_earlysuspend) {
    111         pthread_mutex_lock(&earlysuspend_mutex);
    112         while (earlysuspend_state != EARLYSUSPEND_MEM) {
    113             pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
    114         }
    115         pthread_mutex_unlock(&earlysuspend_mutex);
    116     }
    117 
    118     ALOGV("autosuspend_earlysuspend_enable done\n");
    119 
    120     return 0;
    121 
    122 err:
    123     return ret;
    124 }
    125 
    126 static int autosuspend_earlysuspend_disable(void)
    127 {
    128     char buf[80];
    129     int ret;
    130 
    131     ALOGV("autosuspend_earlysuspend_disable\n");
    132 
    133     ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on)));
    134     if (ret < 0) {
    135         strerror_r(errno, buf, sizeof(buf));
    136         ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
    137         goto err;
    138     }
    139 
    140     if (wait_for_earlysuspend) {
    141         pthread_mutex_lock(&earlysuspend_mutex);
    142         while (earlysuspend_state != EARLYSUSPEND_ON) {
    143             pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
    144         }
    145         pthread_mutex_unlock(&earlysuspend_mutex);
    146     }
    147 
    148     ALOGV("autosuspend_earlysuspend_disable done\n");
    149 
    150     return 0;
    151 
    152 err:
    153     return ret;
    154 }
    155 
    156 struct autosuspend_ops autosuspend_earlysuspend_ops = {
    157         .enable = autosuspend_earlysuspend_enable,
    158         .disable = autosuspend_earlysuspend_disable,
    159 };
    160 
    161 void start_earlysuspend_thread(void)
    162 {
    163     char buf[80];
    164     int ret;
    165 
    166     ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK);
    167     if (ret < 0) {
    168         return;
    169     }
    170 
    171     ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK);
    172     if (ret < 0) {
    173         return;
    174     }
    175 
    176     wait_for_fb_wake();
    177 
    178     ALOGI("Starting early suspend unblocker thread\n");
    179     ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL);
    180     if (ret) {
    181         strerror_r(errno, buf, sizeof(buf));
    182         ALOGE("Error creating thread: %s\n", buf);
    183         return;
    184     }
    185 
    186     wait_for_earlysuspend = true;
    187 }
    188 
    189 struct autosuspend_ops *autosuspend_earlysuspend_init(void)
    190 {
    191     char buf[80];
    192     int ret;
    193 
    194     sPowerStatefd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR));
    195 
    196     if (sPowerStatefd < 0) {
    197         strerror_r(errno, buf, sizeof(buf));
    198         ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
    199         return NULL;
    200     }
    201 
    202     ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, "on", 2));
    203     if (ret < 0) {
    204         strerror_r(errno, buf, sizeof(buf));
    205         ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
    206         goto err_write;
    207     }
    208 
    209     ALOGI("Selected early suspend\n");
    210 
    211     start_earlysuspend_thread();
    212 
    213     return &autosuspend_earlysuspend_ops;
    214 
    215 err_write:
    216     close(sPowerStatefd);
    217     return NULL;
    218 }
    219