diff options
Diffstat (limited to 'libsuspend/autosuspend_earlysuspend.c')
-rw-r--r-- | libsuspend/autosuspend_earlysuspend.c | 124 |
1 files changed, 119 insertions, 5 deletions
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c index 2c2aa36..1df8c6a 100644 --- a/libsuspend/autosuspend_earlysuspend.c +++ b/libsuspend/autosuspend_earlysuspend.c @@ -16,6 +16,8 @@ #include <errno.h> #include <fcntl.h> +#include <pthread.h> +#include <stdbool.h> #include <stddef.h> #include <string.h> #include <sys/types.h> @@ -31,11 +33,75 @@ #define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep" #define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake" - static int sPowerStatefd; static const char *pwr_state_mem = "mem"; static const char *pwr_state_on = "on"; +static pthread_t earlysuspend_thread; +static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER; +static bool wait_for_earlysuspend; +static enum { + EARLYSUSPEND_ON, + EARLYSUSPEND_MEM, +} earlysuspend_state = EARLYSUSPEND_ON; + +int wait_for_fb_wake(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static int wait_for_fb_sleep(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static void *earlysuspend_thread_func(void *arg) +{ + char buf[80]; + char wakeup_count[20]; + int wakeup_count_len; + int ret; + while (1) { + if (wait_for_fb_sleep()) { + ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_MEM; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + + if (wait_for_fb_wake()) { + ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_ON; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + } +} static int autosuspend_earlysuspend_enable(void) { char buf[80]; @@ -50,6 +116,14 @@ static int autosuspend_earlysuspend_enable(void) goto err; } + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_MEM) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + ALOGV("autosuspend_earlysuspend_enable done\n"); return 0; @@ -72,6 +146,14 @@ static int autosuspend_earlysuspend_disable(void) goto err; } + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_ON) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + ALOGV("autosuspend_earlysuspend_disable done\n"); return 0; @@ -85,29 +167,61 @@ struct autosuspend_ops autosuspend_earlysuspend_ops = { .disable = autosuspend_earlysuspend_disable, }; -struct autosuspend_ops *autosuspend_earlysuspend_init(void) +void start_earlysuspend_thread(void) { char buf[80]; int ret; ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK); if (ret < 0) { - return NULL; + return; } ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK); if (ret < 0) { - return NULL; + return; } + wait_for_fb_wake(); + + ALOGI("Starting early suspend unblocker thread\n"); + ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL); + if (ret) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error creating thread: %s\n", buf); + return; + } + + wait_for_earlysuspend = true; +} + +struct autosuspend_ops *autosuspend_earlysuspend_init(void) +{ + char buf[80]; + int ret; + sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR); if (sPowerStatefd < 0) { strerror_r(errno, buf, sizeof(buf)); - ALOGE("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); return NULL; } + ret = write(sPowerStatefd, "on", 2); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err_write; + } + ALOGI("Selected early suspend\n"); + + start_earlysuspend_thread(); + return &autosuspend_earlysuspend_ops; + +err_write: + close(sPowerStatefd); + return NULL; } |