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