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