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