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
38static int state_fd;
39static int wakeup_count_fd;
40static pthread_t suspend_thread;
41static sem_t suspend_lockout;
42static const char *sleep_state = "mem";
43static void (*wakeup_func)(bool success) = NULL;
44
45static 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
106static 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
125static 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
144void 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
153struct autosuspend_ops autosuspend_wakeup_count_ops = {
154        .enable = autosuspend_wakeup_count_enable,
155        .disable = autosuspend_wakeup_count_disable,
156};
157
158struct 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
193err_pthread_create:
194    sem_destroy(&suspend_lockout);
195err_sem_init:
196    close(wakeup_count_fd);
197err_open_wakeup_count:
198    close(state_fd);
199err_open_state:
200    return NULL;
201}
202