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