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 "shared/devices.h"
37 #include "shared/common.h"
38 #include "shared/device-notifier.h"
39 #include "shared/time.h"
41 #include "display/display-dpms.h"
42 #include "display/display.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);
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();
267 ret_val = update_wakeup_reason();
269 _E("Failed to update wakeup reason");
270 wakeup_reason = HAL_DEVICE_POWER_TRANSITION_REASON_UNKNOWN;
277 int power_enable_autosleep(void)
279 _I("System autosleep enabled.");
280 return sys_set_str(POWER_AUTOSLEEP_PATH, "mem");
283 int power_disable_autosleep(void)
285 _I("System autosleep disabled.");
286 return sys_set_str(POWER_AUTOSLEEP_PATH, "off");
289 int power_acquire_wakelock(void)
291 if (mainlock_status == POWER_LOCK)
294 power_broadcast_wakeup();
296 _I("system power lock");
297 suspend_other_process(VITAL_WAKEUP);
298 mainlock_status = POWER_LOCK;
300 return sys_set_str(POWER_LOCK_PATH, "mainlock");
303 int pm_get_power_lock(void)
305 return mainlock_status;
308 int pm_get_power_lock_support(void)
310 static int power_lock_support = -1;
313 if (power_lock_support >= 0)
316 ret_val = sys_check_node(POWER_LOCK_PATH);
318 power_lock_support = false;
320 power_lock_support = true;
322 _I("System power lock: %s",
323 (power_lock_support ? "support" : "not support"));
326 return power_lock_support;
329 int power_release_wakelock(void)
331 if (mainlock_status == POWER_UNLOCK)
334 power_broadcast_suspend();
336 _I("system power unlock");
337 suspend_other_process(VITAL_SLEEP);
338 mainlock_status = POWER_UNLOCK;
340 return sys_set_str(POWER_UNLOCK_PATH, "mainlock");
343 enum hal_device_power_transition_reason power_get_wakeup_reason(void)
345 return wakeup_reason;
348 int check_wakeup_src(void)
351 * return wackeup source. user input or device interrupts? (EVENT_DEVICE or EVENT_INPUT)
356 int get_wakeup_count(int *cnt)
364 ret_val = sys_get_int(POWER_WAKEUP_PATH, &wakeup_count);
372 int set_wakeup_count(int cnt)
376 ret_val = sys_set_int(POWER_WAKEUP_PATH, cnt);
383 static int load_sleep_config(struct parse_result *result, void *user_data)
385 if (!MATCH(result->section, "PowerState"))
388 if (MATCH(result->name, "TimeoutSleepSupport") && MATCH(result->value, "yes")) {
389 timeout_sleep_support = true;
390 _D("timeout_sleep_support=%d", timeout_sleep_support);
396 static void suspend_echo_mem(void)
398 sys_set_str("/sys/power/state", "mem");
400 device_notify(DEVICE_NOTIFIER_POWER_RESUME_FROM_ECHO_MEM, NULL);
403 update_wakeup_reason();
404 power_request_change_state(DEVICED_POWER_STATE_NORMAL, (int) wakeup_reason);
407 static void resume_echo_mem(void)
412 static void suspend_autosleep(void)
414 sys_set_str("/sys/power/wake_unlock", "mainlock");
417 static void resume_autosleep(void)
419 sys_set_str("/sys/power/wake_lock", "mainlock");
422 void power_suspend(int reason)
428 void power_resume(int reason)
434 void power_suspend_init(void)
438 retval = config_parse(POWER_CONF_FILE, load_sleep_config, NULL);
440 _E("Failed to load sleep config: %d", retval);
442 if (pm_get_power_lock_support()) {
443 __power_suspend = suspend_autosleep;
444 __power_resume = resume_autosleep;
446 __power_suspend = suspend_echo_mem;
447 __power_resume = resume_echo_mem;