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 syscommon_deviced_power_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[SYSCOMMON_DEVICED_POWER_LOG_TYPE_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 syscommon_deviced_power_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 >= SYSCOMMON_DEVICED_POWER_LOG_TYPE_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 == SYSCOMMON_DEVICED_VITAL_WAKEUP && vital_service > SYSCOMMON_DEVICED_VITAL_SLEEP)
213 vital_service = type;
215 if (!vital_mode_support())
218 if (type == SYSCOMMON_DEVICED_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 == SYSCOMMON_DEVICED_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 == SYSCOMMON_DEVICED_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(int mode)
246 if (mode == SYSCOMMON_DEVICED_VITAL_EXIT)
247 suspend_other_process(SYSCOMMON_DEVICED_VITAL_EXIT);
252 int power_init_autosleep(void)
254 _I("System autosleep init.");
255 sys_set_str("/sys/power/wake_lock", "mainlock");
256 return sys_set_str(POWER_AUTOSLEEP_PATH, "mem");
259 int power_exit_autosleep(void)
261 _I("System autosleep exit.");
262 return sys_set_str(POWER_AUTOSLEEP_PATH, "off");
265 int power_acquire_wakelock(void)
267 if (mainlock_status == POWER_LOCK)
270 power_broadcast_wakeup();
272 _I("system power lock");
273 suspend_other_process(SYSCOMMON_DEVICED_VITAL_WAKEUP);
274 mainlock_status = POWER_LOCK;
276 return sys_set_str(POWER_LOCK_PATH, "mainlock");
279 int pm_get_power_lock(void)
281 return mainlock_status;
284 int pm_get_power_lock_support(void)
286 static int power_lock_support = -1;
289 if (power_lock_support >= 0)
292 ret_val = access(POWER_LOCK_PATH, F_OK);
294 power_lock_support = false;
296 power_lock_support = true;
298 _I("System power lock: %s",
299 (power_lock_support ? "support" : "not support"));
302 return power_lock_support;
305 int power_release_wakelock(void)
307 if (mainlock_status == POWER_UNLOCK)
310 power_broadcast_suspend();
312 _I("system power unlock");
313 suspend_other_process(SYSCOMMON_DEVICED_VITAL_SLEEP);
314 mainlock_status = POWER_UNLOCK;
316 return sys_set_str(POWER_UNLOCK_PATH, "mainlock");
319 enum hal_device_power_transition_reason power_get_wakeup_reason(void)
321 return wakeup_reason;
324 int check_wakeup_src(void)
327 * return wackeup source. user input or device interrupts? (EVENT_DEVICE or EVENT_INPUT)
332 int get_wakeup_count(int *cnt)
340 ret_val = sys_get_int(POWER_WAKEUP_PATH, &wakeup_count);
348 int set_wakeup_count(int cnt)
352 ret_val = sys_set_int(POWER_WAKEUP_PATH, cnt);
359 static int load_sleep_config(struct parse_result *result, void *user_data)
361 if (!MATCH(result->section, "PowerState"))
364 if (MATCH(result->name, "TimeoutSleepSupport") && MATCH(result->value, "yes")) {
365 timeout_sleep_support = true;
366 _D("timeout_sleep_support=%d", timeout_sleep_support);
372 static void suspend_echo_mem(void)
374 if (is_there_pending_transition()) {
376 * For echo mem, there could be some pending transitions at this point because
377 * transition is aynchronous. That is, if there were another transition request
378 * during a sleep transition, the sleep transition should be skipped and take
379 * the next transition immediately. Otherwise, the next transition will handled
380 * after wakeup, which is indefinite.
382 _D("Skip echo mem, trigger next transition immediately.");
386 sys_set_str("/sys/power/state", "mem");
389 update_wakeup_reason();
392 * FIXME: If the wakeup reason lingers after wakeup, a transition triggered from other
393 * than the above line would take the lingering wakeup reason as a transition reason,
394 * which might not be correct for that transition. Therefore reset the wakeup reason by UNKNOWN.
396 * To address this problem, it is necessary to decide transition reason at the time when
397 * the transition is requested, not the time when it is executed. If then, the below resetting
398 * line can be removed.
400 power_request_change_state_strict(DEVICED_POWER_STATE_SLEEP, DEVICED_POWER_STATE_NORMAL, (int) wakeup_reason, NULL);
401 wakeup_reason = HAL_DEVICE_POWER_TRANSITION_REASON_UNKNOWN;
404 static void resume_echo_mem(void)
406 syscommon_notifier_emit_notify(DEVICED_NOTIFIER_POWER_RESUME_FROM_ECHO_MEM, NULL);
409 static void suspend_autosleep(void)
411 sys_set_str("/sys/power/wake_unlock", "mainlock");
414 static void resume_autosleep(void)
416 sys_set_str("/sys/power/wake_lock", "mainlock");
419 void power_suspend(int reason)
425 void power_resume(int reason)
431 void power_suspend_init(void)
435 retval = config_parse(POWER_CONF_FILE, load_sleep_config, NULL);
437 _E("Failed to load sleep config: %d", retval);
439 if (pm_get_power_lock_support()) {
440 __power_suspend = suspend_autosleep;
441 __power_resume = resume_autosleep;
443 __power_suspend = suspend_echo_mem;
444 __power_resume = resume_echo_mem;