shared: Replace device_notifier_type with the libsyscommon
[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 "device-interface.h"
37 #include "shared/devices.h"
38 #include "shared/common.h"
39 #include "shared/device-notifier.h"
40 #include "shared/time.h"
41 #include "vconf.h"
42 #include "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 power_init_autosleep(void)
258 {
259         _I("System autosleep init.");
260         sys_set_str("/sys/power/wake_lock", "mainlock");
261         return sys_set_str(POWER_AUTOSLEEP_PATH, "mem");
262 }
263
264 int power_exit_autosleep(void)
265 {
266         _I("System autosleep exit.");
267         return sys_set_str(POWER_AUTOSLEEP_PATH, "off");
268 }
269
270 int power_acquire_wakelock(void)
271 {
272         if (mainlock_status == POWER_LOCK)
273                 return 0;
274
275         power_broadcast_wakeup();
276
277         _I("system power lock");
278         suspend_other_process(VITAL_WAKEUP);
279         mainlock_status = POWER_LOCK;
280
281         return sys_set_str(POWER_LOCK_PATH, "mainlock");
282 }
283
284 int pm_get_power_lock(void)
285 {
286         return mainlock_status;
287 }
288
289 int pm_get_power_lock_support(void)
290 {
291         static int power_lock_support = -1;
292         int ret_val;
293
294         if (power_lock_support >= 0)
295                 goto out;
296
297         ret_val = access(POWER_LOCK_PATH, F_OK);
298         if (ret_val < 0)
299                 power_lock_support = false;
300         else
301                 power_lock_support = true;
302
303         _I("System power lock: %s",
304                         (power_lock_support ? "support" : "not support"));
305
306 out:
307         return power_lock_support;
308 }
309
310 int power_release_wakelock(void)
311 {
312         if (mainlock_status == POWER_UNLOCK)
313                 return 0;
314
315         power_broadcast_suspend();
316
317         _I("system power unlock");
318         suspend_other_process(VITAL_SLEEP);
319         mainlock_status = POWER_UNLOCK;
320
321         return sys_set_str(POWER_UNLOCK_PATH, "mainlock");
322 }
323
324 enum hal_device_power_transition_reason power_get_wakeup_reason(void)
325 {
326         return wakeup_reason;
327 }
328
329 int check_wakeup_src(void)
330 {
331         /*  TODO if nedded.
332          * return wackeup source. user input or device interrupts? (EVENT_DEVICE or EVENT_INPUT)
333          */
334         return EVENT_DEVICE;
335 }
336
337 int get_wakeup_count(int *cnt)
338 {
339         int ret_val;
340         int wakeup_count;
341
342         if (!cnt)
343                 return -EINVAL;
344
345         ret_val = sys_get_int(POWER_WAKEUP_PATH, &wakeup_count);
346         if (ret_val < 0)
347                 return ret_val;
348
349         *cnt = wakeup_count;
350         return 0;
351 }
352
353 int set_wakeup_count(int cnt)
354 {
355         int ret_val;
356
357         ret_val = sys_set_int(POWER_WAKEUP_PATH, cnt);
358         if (ret_val < 0)
359                 return ret_val;
360
361         return 0;
362 }
363
364 static int load_sleep_config(struct parse_result *result, void *user_data)
365 {
366         if (!MATCH(result->section, "PowerState"))
367                 return 0;
368
369         if (MATCH(result->name, "TimeoutSleepSupport") && MATCH(result->value, "yes")) {
370                 timeout_sleep_support = true;
371                 _D("timeout_sleep_support=%d", timeout_sleep_support);
372         }
373
374         return 0;
375 }
376
377 static void suspend_echo_mem(void)
378 {
379         if (is_there_pending_transition()) {
380                 /**
381                  * For echo mem, there could be some pending transitions at this point because
382                  * transition is aynchronous. That is, if there were another transition request
383                  * during a sleep transition, the sleep transition should be skipped and take
384                  * the next transition immediately. Otherwise, the next transition will handled
385                  * after wakeup, which is indefinite.
386                  */
387                 _D("Skip echo mem, trigger next transition immediately.");
388                 return;
389         }
390
391         sys_set_str("/sys/power/state", "mem");
392
393         // resume
394         update_wakeup_reason();
395
396         /*
397          * FIXME: If the wakeup reason lingers after wakeup, a transition triggered from other
398          * than the above line would take the lingering wakeup reason as a transition reason,
399          * which might not be correct for that transition. Therefore reset the wakeup reason by UNKNOWN.
400          *
401          * To address this problem, it is necessary to decide transition reason at the time when
402          * the transition is requested, not the time when it is executed. If then, the below resetting
403          * line can be removed.
404          */
405         power_request_change_state_strict(DEVICED_POWER_STATE_SLEEP, DEVICED_POWER_STATE_NORMAL, (int) wakeup_reason, NULL);
406         wakeup_reason = HAL_DEVICE_POWER_TRANSITION_REASON_UNKNOWN;
407 }
408
409 static void resume_echo_mem(void)
410 {
411         syscommon_notifier_emit_notify(DEVICED_NOTIFIER_POWER_RESUME_FROM_ECHO_MEM, NULL);
412 }
413
414 static void suspend_autosleep(void)
415 {
416         sys_set_str("/sys/power/wake_unlock", "mainlock");
417 }
418
419 static void resume_autosleep(void)
420 {
421         sys_set_str("/sys/power/wake_lock", "mainlock");
422 }
423
424 void power_suspend(int reason)
425 {
426         if (__power_suspend)
427                 __power_suspend();
428 }
429
430 void power_resume(int reason)
431 {
432         if (__power_resume)
433                 __power_resume();
434 }
435
436 void power_suspend_init(void)
437 {
438         int retval;
439
440         retval = config_parse(POWER_CONF_FILE, load_sleep_config, NULL);
441         if (retval < 0)
442                 _E("Failed to load sleep config: %d", retval);
443
444         if (pm_get_power_lock_support()) {
445                 __power_suspend = suspend_autosleep;
446                 __power_resume = resume_autosleep;
447         } else {
448                 __power_suspend = suspend_echo_mem;
449                 __power_resume = resume_echo_mem;
450         }
451 }