39866a6327bf9b26fd85e54b1fd691833c468cd6
[platform/core/system/deviced.git] / src / power / power-suspend.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <limits.h>
28 #include <math.h>
29 #include <assert.h>
30 #include <errno.h>
31 #include <dlfcn.h>
32 #include <libsyscommon/libgdbus.h>
33 #include <libsyscommon/ini-parser.h>
34
35 #include "core/log.h"
36 #include "shared/devices.h"
37 #include "shared/common.h"
38 #include "shared/device-notifier.h"
39 #include "shared/time.h"
40 #include "vconf.h"
41 #include "display/display-dpms.h"
42 #include "display/display.h"
43 #include "power.h"
44 #include "power-boot.h"
45 #include "power-suspend.h"
46
47 #define POWER_CONF_FILE             "/etc/deviced/power.conf"
48
49 static int mainlock_status = POWER_UNLOCK;
50 static int vital_service;
51 static bool vital_sleep;
52 static int vital_support = -2;
53 static enum hal_device_power_transition_reason wakeup_reason = HAL_DEVICE_POWER_TRANSITION_REASON_UNKNOWN;
54
55 #ifdef ENABLE_PM_LOG
56
57 typedef struct _pm_history {
58         time_t time;
59         enum pm_log_type log_type;
60         int keycode;
61 } pm_history;
62
63 static int max_history_count = MAX_LOG_COUNT;
64 static pm_history pm_history_log[MAX_LOG_COUNT] = {{0, }, };
65 static int history_count = 0;
66
67 static const char history_string[PM_LOG_MAX][15] = {
68         "PRESS", "LONG PRESS", "RELEASE", "LCD ON", "LCD ON COMPL", "LCD ON FAIL",
69         "LCD DIM", "LCD DIM FAIL", "LCD OFF", "LCD OFF COMPL", "LCD OFF FAIL",
70         "LCD FAIL", "SLEEP"};
71
72 bool timeout_sleep_support = false;
73
74 static void (*__power_suspend) (void);
75 static void (*__power_resume) (void);
76
77 // Leave it for backward compatability. As the signal was implemented when distinction
78 // between power and display is unclear, path and interface is related with display.
79 // Since tizen 7.0, it is able to receive suspend/resume event using device power-internal API,
80 // which is irrelevent with this signal.
81 static void power_broadcast_suspend(void)
82 {
83         int ret;
84         long now;
85
86         _D("PM will be changed to sleep.");
87
88         now = clock_gettime_to_long();
89         ret = gdbus_signal_emit(NULL,
90                 DEVICED_PATH_DISPLAY,
91                 DEVICED_INTERFACE_DISPLAY,
92                 "sleep",
93                 g_variant_new("(t)", (guint64)now));
94         if (ret < 0)
95                 _E("Failed to send dbus signal sleep.");
96 }
97
98 // Leave it for backward compatability. As the signal was implemented when distinction
99 // between power and display is unclear, path and interface is related with display.
100 // Since tizen 7.0, it is able to receive suspend/resume event using device power-internal API,
101 // which is irrelevent with this signal.
102 static void power_broadcast_wakeup(void)
103 {
104         int ret;
105         long now;
106
107          _D("PM is changed to wakeup.");
108
109          now = clock_gettime_to_long();
110          ret = gdbus_signal_emit(NULL,
111                  DEVICED_PATH_DISPLAY,
112                  DEVICED_INTERFACE_DISPLAY,
113                  "wakeup",
114                  g_variant_new("(t)", (guint64)now));
115          if (ret < 0)
116                  _E("Failed to send dbus signal wakeup.");
117 }
118
119 void pm_history_init()
120 {
121         memset(pm_history_log, 0x0, sizeof(pm_history_log));
122         history_count = 0;
123         max_history_count = MAX_LOG_COUNT;
124 }
125
126 void pm_history_save(enum pm_log_type log_type, int code)
127 {
128         time_t now;
129
130         time(&now);
131         pm_history_log[history_count].time = now;
132         pm_history_log[history_count].log_type = log_type;
133         pm_history_log[history_count].keycode = code;
134         history_count++;
135
136         if (history_count >= max_history_count)
137                 history_count = 0;
138 }
139
140 void pm_history_print(int fd, int count)
141 {
142         int start_index, index, i;
143         int ret_val;
144         char buf[255];
145         char time_buf[30];
146
147         if (count <= 0 || count > max_history_count)
148                 return;
149
150         start_index = (history_count - count + max_history_count)
151                     % max_history_count;
152
153         for (i = 0; i < count; i++) {
154                 index = (start_index + i) % max_history_count;
155
156                 if (pm_history_log[index].time == 0)
157                         continue;
158
159                 if (pm_history_log[index].log_type >= PM_LOG_MAX)
160                         continue;
161                 ctime_r(&pm_history_log[index].time, time_buf);
162                 time_buf[strlen(time_buf) - 1] = 0;
163                 snprintf(buf, sizeof(buf), "[%3d] %15s %3d %s\n",
164                         index,
165                         history_string[pm_history_log[index].log_type],
166                         pm_history_log[index].keycode,
167                         time_buf);
168                 ret_val = write(fd, buf, strlen(buf));
169                 if (ret_val < 0)
170                         _E("Write() failed: %d", errno);
171         }
172 }
173 #endif
174
175 bool vital_mode(void)
176 {
177         return vital_sleep;
178 }
179
180 static int vital_mode_support(void)
181 {
182         if (vital_support < 0) {
183                 FILE *fp;
184
185                 fp = fopen(FREEZER_VITAL_WAKEUP_CGROUP, "r");
186                 if (fp == NULL) {
187                         _E("%s open failed", FREEZER_VITAL_WAKEUP_CGROUP);
188                         /* read max 2 times to check if this file exist */
189                         vital_support++;
190                         return 0;
191                 }
192                 vital_support = 1;
193                 fclose(fp);
194         }
195         return vital_support;
196 }
197
198 static int update_wakeup_reason(void)
199 {
200         return hal_device_power_get_wakeup_reason(&wakeup_reason);
201 }
202
203 int suspend_other_process(int type)
204 {
205         int ret = 0;
206
207         if (vital_service == type)
208                 return ret;
209
210         if (type == VITAL_WAKEUP && vital_service > VITAL_SLEEP)
211                 return ret;
212
213         vital_service = type;
214
215         if (!vital_mode_support())
216                 return ret;
217
218         if (type == VITAL_SLEEP) {
219                 gdbus_call_sync_with_reply_timeout(RESOURCED_BUS_NAME,
220                                                 RESOURCED_PATH_FREEZER,
221                                                 RESOURCED_INTERFACE_FREEZER,
222                                                 "SetSuspend",
223                                                 g_variant_new("(s)", "sleep"),
224                                                 NULL,
225                                                 SET_SUSPEND_TIME*1000);
226                 vital_sleep = true;
227         } else if (type == VITAL_WAKEUP) {
228                 ret = gdbus_call_async(RESOURCED_BUS_NAME,
229                                                 RESOURCED_PATH_FREEZER,
230                                                 RESOURCED_INTERFACE_FREEZER,
231                                                 "SetSuspend",
232                                                 g_variant_new("(s)", "wakeup"));
233         } else if (type == VITAL_EXIT) {
234                 ret = gdbus_call_async(RESOURCED_BUS_NAME,
235                                                 RESOURCED_PATH_FREEZER,
236                                                 RESOURCED_INTERFACE_FREEZER,
237                                                 "SetSuspend",
238                                                 g_variant_new("(s)", "exit"));
239                 vital_sleep = false;
240         }
241         return ret;
242 }
243
244 int vital_state_changed(void *data)
245 {
246         int type;
247
248         assert(data);
249
250         type = *(int *)data;
251         if (type == VITAL_EXIT)
252                 suspend_other_process(VITAL_EXIT);
253
254         return 0;
255 }
256
257 int pm_suspend(void)
258 {
259         int ret_val;
260
261         power_broadcast_suspend();
262         _I("system suspend");
263         ret_val = sys_set_str(POWER_STATE_PATH, "mem");
264         _I("System resume: %d", ret_val);
265         power_broadcast_wakeup();
266
267         ret_val = update_wakeup_reason();
268         if (ret_val < 0) {
269                 _E("Failed to update wakeup reason");
270                 wakeup_reason = HAL_DEVICE_POWER_TRANSITION_REASON_UNKNOWN;
271                 return ret_val;
272         }
273
274         return 0;
275 }
276
277 int power_enable_autosleep(void)
278 {
279         _I("System autosleep enabled.");
280         return sys_set_str(POWER_AUTOSLEEP_PATH, "mem");
281 }
282
283 int power_disable_autosleep(void)
284 {
285         _I("System autosleep disabled.");
286         return sys_set_str(POWER_AUTOSLEEP_PATH, "off");
287 }
288
289 int power_acquire_wakelock(void)
290 {
291         if (mainlock_status == POWER_LOCK)
292                 return 0;
293
294         power_broadcast_wakeup();
295
296         _I("system power lock");
297         suspend_other_process(VITAL_WAKEUP);
298         mainlock_status = POWER_LOCK;
299
300         return sys_set_str(POWER_LOCK_PATH, "mainlock");
301 }
302
303 int pm_get_power_lock(void)
304 {
305         return mainlock_status;
306 }
307
308 int pm_get_power_lock_support(void)
309 {
310         static int power_lock_support = -1;
311         int ret_val;
312
313         if (power_lock_support >= 0)
314                 goto out;
315
316         ret_val = sys_check_node(POWER_LOCK_PATH);
317         if (ret_val < 0)
318                 power_lock_support = false;
319         else
320                 power_lock_support = true;
321
322         _I("System power lock: %s",
323                         (power_lock_support ? "support" : "not support"));
324
325 out:
326         return power_lock_support;
327 }
328
329 int power_release_wakelock(void)
330 {
331         if (mainlock_status == POWER_UNLOCK)
332                 return 0;
333
334         power_broadcast_suspend();
335
336         _I("system power unlock");
337         suspend_other_process(VITAL_SLEEP);
338         mainlock_status = POWER_UNLOCK;
339
340         return sys_set_str(POWER_UNLOCK_PATH, "mainlock");
341 }
342
343 enum hal_device_power_transition_reason power_get_wakeup_reason(void)
344 {
345         return wakeup_reason;
346 }
347
348 int check_wakeup_src(void)
349 {
350         /*  TODO if nedded.
351          * return wackeup source. user input or device interrupts? (EVENT_DEVICE or EVENT_INPUT)
352          */
353         return EVENT_DEVICE;
354 }
355
356 int get_wakeup_count(int *cnt)
357 {
358         int ret_val;
359         int wakeup_count;
360
361         if (!cnt)
362                 return -EINVAL;
363
364         ret_val = sys_get_int(POWER_WAKEUP_PATH, &wakeup_count);
365         if (ret_val < 0)
366                 return ret_val;
367
368         *cnt = wakeup_count;
369         return 0;
370 }
371
372 int set_wakeup_count(int cnt)
373 {
374         int ret_val;
375
376         ret_val = sys_set_int(POWER_WAKEUP_PATH, cnt);
377         if (ret_val < 0)
378                 return ret_val;
379
380         return 0;
381 }
382
383 static int load_sleep_config(struct parse_result *result, void *user_data)
384 {
385         if (!MATCH(result->section, "PowerState"))
386                 return 0;
387
388         if (MATCH(result->name, "TimeoutSleepSupport") && MATCH(result->value, "yes")) {
389                 timeout_sleep_support = true;
390                 _D("timeout_sleep_support=%d", timeout_sleep_support);
391         }
392
393         return 0;
394 }
395
396 static void suspend_echo_mem(void)
397 {
398         sys_set_str("/sys/power/state", "mem");
399
400         device_notify(DEVICE_NOTIFIER_POWER_RESUME_FROM_ECHO_MEM, NULL);
401
402         // resume
403         update_wakeup_reason();
404         power_request_change_state(DEVICED_POWER_STATE_NORMAL, (int) wakeup_reason);
405 }
406
407 static void resume_echo_mem(void)
408 {
409         // nothing to do
410 }
411
412 static void suspend_autosleep(void)
413 {
414         sys_set_str("/sys/power/wake_unlock", "mainlock");
415 }
416
417 static void resume_autosleep(void)
418 {
419         sys_set_str("/sys/power/wake_lock", "mainlock");
420 }
421
422 void power_suspend(int reason)
423 {
424         if (__power_suspend)
425                 __power_suspend();
426 }
427
428 void power_resume(int reason)
429 {
430         if (__power_resume)
431                 __power_resume();
432 }
433
434 void power_suspend_init(void)
435 {
436         int retval;
437
438         retval = config_parse(POWER_CONF_FILE, load_sleep_config, NULL);
439         if (retval < 0)
440                 _E("Failed to load sleep config: %d", retval);
441
442         if (pm_get_power_lock_support()) {
443                 __power_suspend = suspend_autosleep;
444                 __power_resume = resume_autosleep;
445         } else {
446                 __power_suspend = suspend_echo_mem;
447                 __power_resume = resume_echo_mem;
448         }
449 }