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 <semaphore.h>
     21 #include <stddef.h>
     22 #include <stdbool.h>
     23 #include <string.h>
     24 #include <sys/stat.h>
     25 #include <sys/types.h>
     26 #include <unistd.h>
     27 
     28 #define LOG_TAG "libsuspend"
     29 //#define LOG_NDEBUG 0
     30 #include <cutils/log.h>
     31 
     32 #include "autosuspend_ops.h"
     33 
     34 #define SYS_POWER_STATE "/sys/power/state"
     35 #define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
     36 
     37 static int state_fd;
     38 static int wakeup_count_fd;
     39 static pthread_t suspend_thread;
     40 static sem_t suspend_lockout;
     41 static const char *sleep_state = "mem";
     42 static void (*wakeup_func)(bool success) = NULL;
     43 
     44 static void *suspend_thread_func(void *arg __attribute__((unused)))
     45 {
     46     char buf[80];
     47     char wakeup_count[20];
     48     int wakeup_count_len;
     49     int ret;
     50     bool success;
     51 
     52     while (1) {
     53         usleep(100000);
     54         ALOGV("%s: read wakeup_count\n", __func__);
     55         lseek(wakeup_count_fd, 0, SEEK_SET);
     56         wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
     57                 sizeof(wakeup_count)));
     58         if (wakeup_count_len < 0) {
     59             strerror_r(errno, buf, sizeof(buf));
     60             ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
     61             wakeup_count_len = 0;
     62             continue;
     63         }
     64         if (!wakeup_count_len) {
     65             ALOGE("Empty wakeup count\n");
     66             continue;
     67         }
     68 
     69         ALOGV("%s: wait\n", __func__);
     70         ret = sem_wait(&suspend_lockout);
     71         if (ret < 0) {
     72             strerror_r(errno, buf, sizeof(buf));
     73             ALOGE("Error waiting on semaphore: %s\n", buf);
     74             continue;
     75         }
     76 
     77         success = true;
     78         ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
     79         ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
     80         if (ret < 0) {
     81             strerror_r(errno, buf, sizeof(buf));
     82             ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
     83         } else {
     84             ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
     85             ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
     86             if (ret < 0) {
     87                 success = false;
     88             }
     89             void (*func)(bool success) = wakeup_func;
     90             if (func != NULL) {
     91                 (*func)(success);
     92             }
     93         }
     94 
     95         ALOGV("%s: release sem\n", __func__);
     96         ret = sem_post(&suspend_lockout);
     97         if (ret < 0) {
     98             strerror_r(errno, buf, sizeof(buf));
     99             ALOGE("Error releasing semaphore: %s\n", buf);
    100         }
    101     }
    102     return NULL;
    103 }
    104 
    105 static int autosuspend_wakeup_count_enable(void)
    106 {
    107     char buf[80];
    108     int ret;
    109 
    110     ALOGV("autosuspend_wakeup_count_enable\n");
    111 
    112     ret = sem_post(&suspend_lockout);
    113 
    114     if (ret < 0) {
    115         strerror_r(errno, buf, sizeof(buf));
    116         ALOGE("Error changing semaphore: %s\n", buf);
    117     }
    118 
    119     ALOGV("autosuspend_wakeup_count_enable done\n");
    120 
    121     return ret;
    122 }
    123 
    124 static int autosuspend_wakeup_count_disable(void)
    125 {
    126     char buf[80];
    127     int ret;
    128 
    129     ALOGV("autosuspend_wakeup_count_disable\n");
    130 
    131     ret = sem_wait(&suspend_lockout);
    132 
    133     if (ret < 0) {
    134         strerror_r(errno, buf, sizeof(buf));
    135         ALOGE("Error changing semaphore: %s\n", buf);
    136     }
    137 
    138     ALOGV("autosuspend_wakeup_count_disable done\n");
    139 
    140     return ret;
    141 }
    142 
    143 void set_wakeup_callback(void (*func)(bool success))
    144 {
    145     if (wakeup_func != NULL) {
    146         ALOGE("Duplicate wakeup callback applied, keeping original");
    147         return;
    148     }
    149     wakeup_func = func;
    150 }
    151 
    152 struct autosuspend_ops autosuspend_wakeup_count_ops = {
    153         .enable = autosuspend_wakeup_count_enable,
    154         .disable = autosuspend_wakeup_count_disable,
    155 };
    156 
    157 struct autosuspend_ops *autosuspend_wakeup_count_init(void)
    158 {
    159     int ret;
    160     char buf[80];
    161 
    162     state_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_STATE, O_RDWR));
    163     if (state_fd < 0) {
    164         strerror_r(errno, buf, sizeof(buf));
    165         ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf);
    166         goto err_open_state;
    167     }
    168 
    169     wakeup_count_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_WAKEUP_COUNT, O_RDWR));
    170     if (wakeup_count_fd < 0) {
    171         strerror_r(errno, buf, sizeof(buf));
    172         ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
    173         goto err_open_wakeup_count;
    174     }
    175 
    176     ret = sem_init(&suspend_lockout, 0, 0);
    177     if (ret < 0) {
    178         strerror_r(errno, buf, sizeof(buf));
    179         ALOGE("Error creating semaphore: %s\n", buf);
    180         goto err_sem_init;
    181     }
    182     ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);
    183     if (ret) {
    184         strerror_r(ret, buf, sizeof(buf));
    185         ALOGE("Error creating thread: %s\n", buf);
    186         goto err_pthread_create;
    187     }
    188 
    189     ALOGI("Selected wakeup count\n");
    190     return &autosuspend_wakeup_count_ops;
    191 
    192 err_pthread_create:
    193     sem_destroy(&suspend_lockout);
    194 err_sem_init:
    195     close(wakeup_count_fd);
    196 err_open_wakeup_count:
    197     close(state_fd);
    198 err_open_state:
    199     return NULL;
    200 }
    201