diff --git a/libsuspend/Android.mk b/libsuspend/Android.mk new file mode 100644 index 000000000..45cb70116 --- /dev/null +++ b/libsuspend/Android.mk @@ -0,0 +1,23 @@ +# Copyright 2012 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) + +libsuspend_src_files := \ + autosuspend.c \ + autosuspend_autosleep.c \ + autosuspend_earlysuspend.c \ + autosuspend_wakeup_count.c \ + +libsuspend_libraries := \ + liblog libcutils + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(libsuspend_src_files) +LOCAL_MODULE := libsuspend +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_SHARED_LIBRARIES := $(libsuspend_libraries) +#LOCAL_CFLAGS += -DLOG_NDEBUG=0 +include $(BUILD_SHARED_LIBRARY) diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c new file mode 100644 index 000000000..7d1d97333 --- /dev/null +++ b/libsuspend/autosuspend.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define LOG_TAG "libsuspend" +#include + +#include + +#include "autosuspend_ops.h" + +static struct autosuspend_ops *autosuspend_ops; +static bool autosuspend_enabled; +static bool autosuspend_inited; + +static int autosuspend_init(void) +{ + if (autosuspend_inited) { + return 0; + } + + autosuspend_inited = true; + + autosuspend_ops = autosuspend_earlysuspend_init(); + if (autosuspend_ops) { + goto out; + } + + autosuspend_ops = autosuspend_autosleep_init(); + if (autosuspend_ops) { + goto out; + } + + autosuspend_ops = autosuspend_wakeup_count_init(); + if (autosuspend_ops) { + goto out; + } + + if (!autosuspend_ops) { + ALOGE("failed to initialize autosuspend\n"); + return -1; + } + +out: + ALOGV("autosuspend initialized\n"); + return 0; +} + +int autosuspend_enable(void) +{ + int ret; + + ret = autosuspend_init(); + if (ret) { + return ret; + } + + ALOGV("autosuspend_enable\n"); + + if (autosuspend_enabled) { + return 0; + } + + ret = autosuspend_ops->enable(); + if (ret) { + return ret; + } + + autosuspend_enabled = true; + return 0; +} + +int autosuspend_disable(void) +{ + int ret; + + ret = autosuspend_init(); + if (ret) { + return ret; + } + + ALOGV("autosuspend_disable\n"); + + if (!autosuspend_enabled) { + return 0; + } + + ret = autosuspend_ops->disable(); + if (ret) { + return ret; + } + + autosuspend_enabled = false; + return 0; +} diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c new file mode 100644 index 000000000..dd0dfefe7 --- /dev/null +++ b/libsuspend/autosuspend_autosleep.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "libsuspend" +#include + +#include "autosuspend_ops.h" + +#define SYS_POWER_AUTOSLEEP "/sys/power/autosleep" + +static int autosleep_fd; +static const char *sleep_state = "mem"; +static const char *on_state = "off"; + +static int autosuspend_autosleep_enable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_autosleep_enable\n"); + + ret = write(autosleep_fd, sleep_state, strlen(sleep_state)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); + goto err; + } + + ALOGV("autosuspend_autosleep_enable done\n"); + + return 0; + +err: + return ret; +} + +static int autosuspend_autosleep_disable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_autosleep_disable\n"); + + ret = write(autosleep_fd, on_state, strlen(on_state)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); + goto err; + } + + ALOGV("autosuspend_autosleep_disable done\n"); + + return 0; + +err: + return ret; +} + +struct autosuspend_ops autosuspend_autosleep_ops = { + .enable = autosuspend_autosleep_enable, + .disable = autosuspend_autosleep_disable, +}; + +struct autosuspend_ops *autosuspend_autosleep_init(void) +{ + int ret; + char buf[80]; + + autosleep_fd = open(SYS_POWER_AUTOSLEEP, O_WRONLY); + if (autosleep_fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", SYS_POWER_AUTOSLEEP, buf); + return NULL; + } + + ALOGI("Selected autosleep\n"); + return &autosuspend_autosleep_ops; +} diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c new file mode 100644 index 000000000..2c2aa3661 --- /dev/null +++ b/libsuspend/autosuspend_earlysuspend.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "libsuspend" +#include + +#include "autosuspend_ops.h" + +#define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state" +#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 int autosuspend_earlysuspend_enable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_earlysuspend_enable\n"); + + ret = write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err; + } + + ALOGV("autosuspend_earlysuspend_enable done\n"); + + return 0; + +err: + return ret; +} + +static int autosuspend_earlysuspend_disable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_earlysuspend_disable\n"); + + ret = write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err; + } + + ALOGV("autosuspend_earlysuspend_disable done\n"); + + return 0; + +err: + return ret; +} + +struct autosuspend_ops autosuspend_earlysuspend_ops = { + .enable = autosuspend_earlysuspend_enable, + .disable = autosuspend_earlysuspend_disable, +}; + +struct autosuspend_ops *autosuspend_earlysuspend_init(void) +{ + char buf[80]; + int ret; + + ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK); + if (ret < 0) { + return NULL; + } + + ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK); + if (ret < 0) { + return NULL; + } + + 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); + return NULL; + } + + ALOGI("Selected early suspend\n"); + return &autosuspend_earlysuspend_ops; +} diff --git a/libsuspend/autosuspend_ops.h b/libsuspend/autosuspend_ops.h new file mode 100644 index 000000000..698e25be8 --- /dev/null +++ b/libsuspend/autosuspend_ops.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBSUSPEND_AUTOSUSPEND_OPS_H_ +#define _LIBSUSPEND_AUTOSUSPEND_OPS_H_ + +struct autosuspend_ops { + int (*enable)(void); + int (*disable)(void); +}; + +struct autosuspend_ops *autosuspend_autosleep_init(void); +struct autosuspend_ops *autosuspend_earlysuspend_init(void); +struct autosuspend_ops *autosuspend_wakeup_count_init(void); + +#endif diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c new file mode 100644 index 000000000..b038e20f5 --- /dev/null +++ b/libsuspend/autosuspend_wakeup_count.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "libsuspend" +#include + +#include "autosuspend_ops.h" + +#define SYS_POWER_STATE "/sys/power/state" +#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count" + +static int state_fd; +static int wakeup_count_fd; +static pthread_t suspend_thread; +static sem_t suspend_lockout; +static const char *sleep_state = "mem"; + +static void *suspend_thread_func(void *arg) +{ + char buf[80]; + char wakeup_count[20]; + int wakeup_count_len; + int ret; + + while (1) { + usleep(100000); + ALOGV("%s: read wakeup_count\n", __func__); + lseek(wakeup_count_fd, 0, SEEK_SET); + wakeup_count_len = read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count)); + if (wakeup_count_len < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); + wakeup_count_len = 0; + continue; + } + if (!wakeup_count_len) { + ALOGE("Empty wakeup count\n"); + continue; + } + + ALOGV("%s: wait\n", __func__); + ret = sem_wait(&suspend_lockout); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error waiting on semaphore: %s\n", buf); + continue; + } + + ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count); + ret = write(wakeup_count_fd, wakeup_count, wakeup_count_len); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); + } else { + ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE); + ret = write(state_fd, sleep_state, strlen(sleep_state)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf); + } + } + + ALOGV("%s: release sem\n", __func__); + ret = sem_post(&suspend_lockout); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error releasing semaphore: %s\n", buf); + } + } + return NULL; +} + +static int autosuspend_wakeup_count_enable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_wakeup_count_enable\n"); + + ret = sem_post(&suspend_lockout); + + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error changing semaphore: %s\n", buf); + } + + ALOGV("autosuspend_wakeup_count_enable done\n"); + + return ret; +} + +static int autosuspend_wakeup_count_disable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_wakeup_count_disable\n"); + + ret = sem_wait(&suspend_lockout); + + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error changing semaphore: %s\n", buf); + } + + ALOGV("autosuspend_wakeup_count_disable done\n"); + + return ret; +} + +struct autosuspend_ops autosuspend_wakeup_count_ops = { + .enable = autosuspend_wakeup_count_enable, + .disable = autosuspend_wakeup_count_disable, +}; + +struct autosuspend_ops *autosuspend_wakeup_count_init(void) +{ + int ret; + char buf[80]; + + state_fd = open(SYS_POWER_STATE, O_RDWR); + if (state_fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf); + goto err_open_state; + } + + wakeup_count_fd = open(SYS_POWER_WAKEUP_COUNT, O_RDWR); + if (wakeup_count_fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); + goto err_open_wakeup_count; + } + + ret = sem_init(&suspend_lockout, 0, 0); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error creating semaphore: %s\n", buf); + goto err_sem_init; + } + ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL); + if (ret) { + strerror_r(ret, buf, sizeof(buf)); + ALOGE("Error creating thread: %s\n", buf); + goto err_pthread_create; + } + + ALOGI("Selected wakeup count\n"); + return &autosuspend_wakeup_count_ops; + +err_pthread_create: + sem_destroy(&suspend_lockout); +err_sem_init: + close(wakeup_count_fd); +err_open_wakeup_count: + close(state_fd); +err_open_state: + return NULL; +} diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h new file mode 100644 index 000000000..f56fc6acb --- /dev/null +++ b/libsuspend/include/suspend/autosuspend.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBSUSPEND_AUTOSUSPEND_H_ +#define _LIBSUSPEND_AUTOSUSPEND_H_ + +#include + +__BEGIN_DECLS + +/* + * autosuspend_enable + * + * Turn on autosuspend in the kernel, allowing it to enter suspend if no + * wakelocks/wakeup_sources are held. + * + * + * + * Returns 0 on success, -1 if autosuspend was not enabled. + */ +int autosuspend_enable(void); + +/* + * autosuspend_disable + * + * Turn off autosuspend in the kernel, preventing suspend and synchronizing + * with any in-progress resume. + * + * Returns 0 on success, -1 if autosuspend was not disabled. + */ +int autosuspend_disable(void); + +__END_DECLS + +#endif