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