4 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <sys/types.h>
32 #include <libsyscommon/libgdbus.h>
33 #include <libsyscommon/ini-parser.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"
44 #include "power-boot.h"
45 #include "power-suspend.h"
47 #define POWER_CONF_FILE "/etc/deviced/power.conf"
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;
57 typedef struct _pm_history {
59 enum pm_log_type log_type;
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;
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",
72 bool timeout_sleep_support = false;
74 static void (*__power_suspend) (void);
75 static void (*__power_resume) (void);
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)
86 _D("PM will be changed to sleep.");
88 now = clock_gettime_to_long();
89 ret = gdbus_signal_emit(NULL,
91 DEVICED_INTERFACE_DISPLAY,
93 g_variant_new("(t)", (guint64)now));
95 _E("Failed to send dbus signal sleep.");
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)
107 _D("PM is changed to wakeup.");
109 now = clock_gettime_to_long();
110 ret = gdbus_signal_emit(NULL,
111 DEVICED_PATH_DISPLAY,
112 DEVICED_INTERFACE_DISPLAY,
114 g_variant_new("(t)", (guint64)now));
116 _E("Failed to send dbus signal wakeup.");
119 void pm_history_init()
121 memset(pm_history_log, 0x0, sizeof(pm_history_log));
123 max_history_count = MAX_LOG_COUNT;
126 void pm_history_save(enum pm_log_type log_type, int code)
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;
136 if (history_count >= max_history_count)
140 void pm_history_print(int fd, int count)
142 int start_index, index, i;
147 if (count <= 0 || count > max_history_count)
150 start_index = (history_count - count + max_history_count)
153 for (i = 0; i < count; i++) {
154 index = (start_index + i) % max_history_count;
156 if (pm_history_log[index].time == 0)
159 if (pm_history_log[index].log_type >= PM_LOG_MAX)
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",
165 history_string[pm_history_log[index].log_type],
166 pm_history_log[index].keycode,
168 ret_val = write(fd, buf, strlen(buf));
170 _E("Write() failed: %d", errno);
175 bool vital_mode(void)
180 static int vital_mode_support(void)
182 if (vital_support < 0) {
185 fp = fopen(FREEZER_VITAL_WAKEUP_CGROUP, "r");
187 _E("%s open failed", FREEZER_VITAL_WAKEUP_CGROUP);
188 /* read max 2 times to check if this file exist */
195 return vital_support;
198 static int update_wakeup_reason(void)
200 return hal_device_power_get_wakeup_reason(&wakeup_reason);
203 int suspend_other_process(int type)
207 if (vital_service == type)
210 if (type == VITAL_WAKEUP && vital_service > VITAL_SLEEP)
213 vital_service = type;
215 if (!vital_mode_support())
218 if (type == VITAL_SLEEP) {
219 gdbus_call_sync_with_reply_timeout(RESOURCED_BUS_NAME,
220 RESOURCED_PATH_FREEZER,
221 RESOURCED_INTERFACE_FREEZER,
223 g_variant_new("(s)", "sleep"),
225 SET_SUSPEND_TIME*1000);
227 } else if (type == VITAL_WAKEUP) {
228 ret = gdbus_call_async(RESOURCED_BUS_NAME,
229 RESOURCED_PATH_FREEZER,
230 RESOURCED_INTERFACE_FREEZER,
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,
238 g_variant_new("(s)", "exit"));
244 int vital_state_changed(void *data)
251 if (type == VITAL_EXIT)
252 suspend_other_process(VITAL_EXIT);
257 int power_init_autosleep(void)
259 _I("System autosleep init.");
260 sys_set_str("/sys/power/wake_lock", "mainlock");
261 return sys_set_str(POWER_AUTOSLEEP_PATH, "mem");
264 int power_exit_autosleep(void)
266 _I("System autosleep exit.");
267 return sys_set_str(POWER_AUTOSLEEP_PATH, "off");
270 int power_acquire_wakelock(void)
272 if (mainlock_status == POWER_LOCK)
275 power_broadcast_wakeup();
277 _I("system power lock");
278 suspend_other_process(VITAL_WAKEUP);
279 mainlock_status = POWER_LOCK;
281 return sys_set_str(POWER_LOCK_PATH, "mainlock");
284 int pm_get_power_lock(void)
286 return mainlock_status;
289 int pm_get_power_lock_support(void)
291 static int power_lock_support = -1;
294 if (power_lock_support >= 0)
297 ret_val = access(POWER_LOCK_PATH, F_OK);
299 power_lock_support = false;
301 power_lock_support = true;
303 _I("System power lock: %s",
304 (power_lock_support ? "support" : "not support"));
307 return power_lock_support;
310 int power_release_wakelock(void)
312 if (mainlock_status == POWER_UNLOCK)
315 power_broadcast_suspend();
317 _I("system power unlock");
318 suspend_other_process(VITAL_SLEEP);
319 mainlock_status = POWER_UNLOCK;
321 return sys_set_str(POWER_UNLOCK_PATH, "mainlock");
324 enum hal_device_power_transition_reason power_get_wakeup_reason(void)
326 return wakeup_reason;
329 int check_wakeup_src(void)
332 * return wackeup source. user input or device interrupts? (EVENT_DEVICE or EVENT_INPUT)
337 int get_wakeup_count(int *cnt)
345 ret_val = sys_get_int(POWER_WAKEUP_PATH, &wakeup_count);
353 int set_wakeup_count(int cnt)
357 ret_val = sys_set_int(POWER_WAKEUP_PATH, cnt);
364 static int load_sleep_config(struct parse_result *result, void *user_data)
366 if (!MATCH(result->section, "PowerState"))
369 if (MATCH(result->name, "TimeoutSleepSupport") && MATCH(result->value, "yes")) {
370 timeout_sleep_support = true;
371 _D("timeout_sleep_support=%d", timeout_sleep_support);
377 static void suspend_echo_mem(void)
379 if (is_there_pending_transition()) {
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.
387 _D("Skip echo mem, trigger next transition immediately.");
391 sys_set_str("/sys/power/state", "mem");
394 update_wakeup_reason();
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.
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.
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;
409 static void resume_echo_mem(void)
411 syscommon_notifier_emit_notify(DEVICED_NOTIFIER_POWER_RESUME_FROM_ECHO_MEM, NULL);
414 static void suspend_autosleep(void)
416 sys_set_str("/sys/power/wake_unlock", "mainlock");
419 static void resume_autosleep(void)
421 sys_set_str("/sys/power/wake_lock", "mainlock");
424 void power_suspend(int reason)
430 void power_resume(int reason)
436 void power_suspend_init(void)
440 retval = config_parse(POWER_CONF_FILE, load_sleep_config, NULL);
442 _E("Failed to load sleep config: %d", retval);
444 if (pm_get_power_lock_support()) {
445 __power_suspend = suspend_autosleep;
446 __power_resume = resume_autosleep;
448 __power_suspend = suspend_echo_mem;
449 __power_resume = resume_echo_mem;