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