From 5b385f6b415311ba8dc871e4383894f53e0ff9a2 Mon Sep 17 00:00:00 2001 From: Youngjae Cho Date: Tue, 17 May 2022 11:49:27 +0900 Subject: [PATCH] power: unify power state transition routine Previously, the iot-headless dedicated poweroff was working on top of the core poweroff routine. Integrate them into the single poweroff routine so that all type of poweroff is now take the same subroutine. In addition to this, there two internal functions have been added for deviced to trigger change power state by itself. All power state change thorugh these functions take the same subroutine. - power_request_change_state_strict() - power_request_change_state() Due to the above changes, the deviced is now able to broadcast all types of power change state in the unified manner. Therefore, if someone wants to know the power state change of deviced, just use power API. Change-Id: I6573872eb3aa7c50d2d6af575ccce4dc25cab3de Signed-off-by: Youngjae Cho --- conf/power-profile-iot-headed.conf | 7 - conf/power-profile-iot-headless.conf | 7 - conf/power-profile-mobile.conf | 7 - conf/power-profile-tv.conf | 7 - conf/power-profile-wearable.conf | 7 - plugins/iot-headed/display/core.c | 6 +- plugins/iot-headless/battery/battery-plugin.c | 6 +- plugins/iot-headless/input/input-config.c | 6 +- plugins/mobile/display/core.c | 6 +- plugins/tv/display/core.c | 6 +- plugins/tv/display/state-tv.c | 7 +- plugins/wearable/display/core.c | 6 +- src/battery/lowbat-handler.c | 13 +- src/battery/lowbat-handler.h | 3 +- src/core/main.c | 5 +- src/power/power-boot.c | 28 +- src/power/power-boot.h | 2 +- src/power/power-dbus.c | 2 +- src/power/power-off.c | 526 ++++++-------------------- src/power/power-off.h | 42 +- src/power/power-state-manager.c | 311 --------------- src/power/power-state-manager.h | 62 --- src/power/power-state-wait.c | 55 ++- src/power/power-state-wait.h | 6 +- src/power/power.c | 317 ++++++++++++++++ src/power/power.h | 101 +++++ 26 files changed, 615 insertions(+), 936 deletions(-) delete mode 100644 src/power/power-state-manager.c delete mode 100644 src/power/power-state-manager.h create mode 100644 src/power/power.c create mode 100644 src/power/power.h diff --git a/conf/power-profile-iot-headed.conf b/conf/power-profile-iot-headed.conf index ce213be..b98f41d 100644 --- a/conf/power-profile-iot-headed.conf +++ b/conf/power-profile-iot-headed.conf @@ -1,10 +1,3 @@ -[Reboot] -Option=recovery -Option=download -Option=wdownload -Option=debug -Option=silent - [PowerState] TimeoutSleepSupport=no ChangeStateMaxWaitSecond=10 diff --git a/conf/power-profile-iot-headless.conf b/conf/power-profile-iot-headless.conf index 27c6908..0001706 100644 --- a/conf/power-profile-iot-headless.conf +++ b/conf/power-profile-iot-headless.conf @@ -1,10 +1,3 @@ -[Reboot] -Option=recovery -Option=download -Option=wdownload -Option=debug -Option=silent - [PowerState] TimeoutSleepSupport=yes ChangeStateMaxWaitSecond=10 diff --git a/conf/power-profile-mobile.conf b/conf/power-profile-mobile.conf index 437c672..e659e9f 100644 --- a/conf/power-profile-mobile.conf +++ b/conf/power-profile-mobile.conf @@ -1,10 +1,3 @@ -[Reboot] -Option=recovery -Option=download -Option=wdownload -Option=debug -Option=silent - [PowerState] TimeoutSleepSupport=yes ChangeStateMaxWaitSecond=10 diff --git a/conf/power-profile-tv.conf b/conf/power-profile-tv.conf index 437c672..e659e9f 100644 --- a/conf/power-profile-tv.conf +++ b/conf/power-profile-tv.conf @@ -1,10 +1,3 @@ -[Reboot] -Option=recovery -Option=download -Option=wdownload -Option=debug -Option=silent - [PowerState] TimeoutSleepSupport=yes ChangeStateMaxWaitSecond=10 diff --git a/conf/power-profile-wearable.conf b/conf/power-profile-wearable.conf index 437c672..e659e9f 100644 --- a/conf/power-profile-wearable.conf +++ b/conf/power-profile-wearable.conf @@ -1,10 +1,3 @@ -[Reboot] -Option=recovery -Option=download -Option=wdownload -Option=debug -Option=silent - [PowerState] TimeoutSleepSupport=yes ChangeStateMaxWaitSecond=10 diff --git a/plugins/iot-headed/display/core.c b/plugins/iot-headed/display/core.c index 31d05af..cda404c 100644 --- a/plugins/iot-headed/display/core.c +++ b/plugins/iot-headed/display/core.c @@ -2040,11 +2040,11 @@ static int poweroff_triggered_callback(void *udata) int val = (int)(intptr_t) udata; switch (val) { - case POWEROFF_TYPE_NONE: + case VCONFKEY_SYSMAN_POWER_OFF_NONE: clear_pm_status_flag(PWROFF_FLAG); break; - case POWEROFF_TYPE_DIRECT: - case POWEROFF_TYPE_RESTART: + case VCONFKEY_SYSMAN_POWER_OFF_DIRECT: + case VCONFKEY_SYSMAN_POWER_OFF_RESTART: set_pm_status_flag(PWROFF_FLAG); break; } diff --git a/plugins/iot-headless/battery/battery-plugin.c b/plugins/iot-headless/battery/battery-plugin.c index 2ae0b0b..95a427d 100644 --- a/plugins/iot-headless/battery/battery-plugin.c +++ b/plugins/iot-headless/battery/battery-plugin.c @@ -25,7 +25,7 @@ #include "shared/log.h" #include "battery/battery-ops.h" -#include "power/power-state-manager.h" +#include "power/power.h" #define BATTERY_CONF_PATH "/etc/deviced/battery.conf" @@ -63,8 +63,8 @@ static void add_action_transition_info(struct battery_event_handler *handler, ch * Otherwise, handler->id won't be defined at this point.*/ ti->reason = handler->id; - ti->curr = convert_action_string_to_psm_state(curr); - ti->next = convert_action_string_to_psm_state(next); + ti->curr = convert_action_string_to_power_state(curr); + ti->next = convert_action_string_to_power_state(next); SYS_G_LIST_APPEND(*action_list, ti); } diff --git a/plugins/iot-headless/input/input-config.c b/plugins/iot-headless/input/input-config.c index faeffb8..4635159 100644 --- a/plugins/iot-headless/input/input-config.c +++ b/plugins/iot-headless/input/input-config.c @@ -29,7 +29,7 @@ #include "shared/log.h" #include "input-config.h" -#include "power/power-state-manager.h" +#include "power/power.h" #define INPUT_CONF_PATH "/etc/deviced/input.conf" @@ -137,8 +137,8 @@ static void add_action_transition_info(struct input_event_unit *ieu, char *curr, * Otherwise, ieu->id won't be defined at this point.*/ ti->reason = ieu->id; - ti->curr = convert_action_string_to_psm_state(curr); - ti->next = convert_action_string_to_psm_state(next); + ti->curr = convert_action_string_to_power_state(curr); + ti->next = convert_action_string_to_power_state(next); SYS_G_LIST_APPEND(*action_list, ti); } diff --git a/plugins/mobile/display/core.c b/plugins/mobile/display/core.c index 3011edf..8a0a100 100644 --- a/plugins/mobile/display/core.c +++ b/plugins/mobile/display/core.c @@ -2046,11 +2046,11 @@ static int poweroff_triggered_callback(void *udata) int val = (int)(intptr_t) udata; switch (val) { - case POWEROFF_TYPE_NONE: + case VCONFKEY_SYSMAN_POWER_OFF_NONE: clear_pm_status_flag(PWROFF_FLAG); break; - case POWEROFF_TYPE_DIRECT: - case POWEROFF_TYPE_RESTART: + case VCONFKEY_SYSMAN_POWER_OFF_DIRECT: + case VCONFKEY_SYSMAN_POWER_OFF_RESTART: set_pm_status_flag(PWROFF_FLAG); break; } diff --git a/plugins/tv/display/core.c b/plugins/tv/display/core.c index 3203445..f515e7a 100644 --- a/plugins/tv/display/core.c +++ b/plugins/tv/display/core.c @@ -2037,11 +2037,11 @@ static int poweroff_triggered_callback(void *udata) int val = (int)(intptr_t) udata; switch (val) { - case POWEROFF_TYPE_NONE: + case VCONFKEY_SYSMAN_POWER_OFF_NONE: clear_pm_status_flag(PWROFF_FLAG); break; - case POWEROFF_TYPE_DIRECT: - case POWEROFF_TYPE_RESTART: + case VCONFKEY_SYSMAN_POWER_OFF_DIRECT: + case VCONFKEY_SYSMAN_POWER_OFF_RESTART: set_pm_status_flag(PWROFF_FLAG); break; } diff --git a/plugins/tv/display/state-tv.c b/plugins/tv/display/state-tv.c index 2237011..96d7331 100644 --- a/plugins/tv/display/state-tv.c +++ b/plugins/tv/display/state-tv.c @@ -26,6 +26,7 @@ #include "shared/devices.h" #include "display/display-ops.h" #include "display/display-lock.h" +#include "power/power.h" #include "power/power-off.h" #include "power/power-suspend.h" #include "core.h" @@ -490,11 +491,9 @@ static int poweroff_check(int curr, int next) static int poweroff_action(int timeout) { - static const struct device_ops *ops; + power_request_change_state(POWER_STATE_POWEROFF, 9000); - FIND_DEVICE_INT(ops, "power-state-manager"); - - return ops->execute(POWER_POWEROFF); + return 0; } static int poweroff_trans(int evt) diff --git a/plugins/wearable/display/core.c b/plugins/wearable/display/core.c index d8bfcfe..5b640a3 100644 --- a/plugins/wearable/display/core.c +++ b/plugins/wearable/display/core.c @@ -2329,11 +2329,11 @@ static int poweroff_triggered_callback(void *udata) int val = (int)(intptr_t) udata; switch (val) { - case POWEROFF_TYPE_NONE: + case VCONFKEY_SYSMAN_POWER_OFF_NONE: clear_pm_status_flag(PWROFF_FLAG); break; - case POWEROFF_TYPE_DIRECT: - case POWEROFF_TYPE_RESTART: + case VCONFKEY_SYSMAN_POWER_OFF_DIRECT: + case VCONFKEY_SYSMAN_POWER_OFF_RESTART: set_pm_status_flag(PWROFF_FLAG); break; } diff --git a/src/battery/lowbat-handler.c b/src/battery/lowbat-handler.c index 44b7fd3..3905fe9 100644 --- a/src/battery/lowbat-handler.c +++ b/src/battery/lowbat-handler.c @@ -42,6 +42,7 @@ #include "display/setting.h" #include "display/poll.h" #include "display/display-ops.h" +#include "power/power.h" #include "power/power-off.h" #include "apps/apps.h" #include "power-supply.h" @@ -149,18 +150,16 @@ static int lowbat_add_scenario(int old, int now, int (*func)(void *data)) return 0; } -static int power_execute(void *data) +static int power_execute(int state) { - static const struct device_ops *ops; - if (is_emulator()) { CRITICAL_LOG("Poweroff by lowbattery is disabled at emulator."); return 0; } - FIND_DEVICE_INT(ops, "power-state-manager"); + power_request_change_state(state, LOWBAT_POWEROFF_REASON); - return ops->execute(data); + return 0; } static int delayed_init_done(void *data) @@ -242,7 +241,7 @@ direct_launch: if (launched_poweroff == 1) { _I("Will be foreced power off."); - power_execute(POWER_POWEROFF); + power_execute(POWER_STATE_POWEROFF); return 0; } @@ -296,7 +295,7 @@ static int battery_critical_low_act(void *data) int battery_power_off_act(void *data) { CRITICAL_LOG("Low battery power off."); - return power_execute(POWER_POWEROFF); + return power_execute(POWER_STATE_POWEROFF); } int battery_charge_err_cf_act(void *data) diff --git a/src/battery/lowbat-handler.h b/src/battery/lowbat-handler.h index 26ead12..d7c5551 100644 --- a/src/battery/lowbat-handler.h +++ b/src/battery/lowbat-handler.h @@ -20,7 +20,8 @@ #ifndef __LOWBAT_HANDLER_H__ #define __LOWBAT_HANDLER_H__ -#define RETRY_MAX 5 +#define RETRY_MAX 5 +#define LOWBAT_POWEROFF_REASON 2004 int lowbat_popup(int option); void lowbat_enable_uevent_buffering(void); diff --git a/src/core/main.c b/src/core/main.c index 88a36e4..1f77e48 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -31,6 +31,7 @@ #include "log.h" #include "shared/common.h" #include "shared/devices.h" +#include "power/power.h" #include "power/power-boot.h" #include "power/power-off.h" #include "shared/plugin.h" @@ -106,11 +107,11 @@ static int deviced_main(int argc, char **argv) mainloop = g_main_loop_new(NULL, FALSE); ret = poweroff_check_revived(); - if (ret) { + if (is_poweroff_state(ret)) { /* Restarted: deviced was terminated * in middle of reboot/poweroff - resume procedure */ - poweroff_request_shutdown(); + poweroff_request_shutdown(ret); return 0; } diff --git a/src/power/power-boot.c b/src/power/power-boot.c index 634dd69..f33bbd4 100644 --- a/src/power/power-boot.c +++ b/src/power/power-boot.c @@ -34,17 +34,17 @@ #include "display/display-ops.h" #include "shared/plugin.h" #include "power-doze.h" -#include "power-state-manager.h" +#include "power.h" #define SYSTEMD_DBUS_SIGNAL_SYSTEM_STARTUP_FINISHED "StartupFinished" #define SYSTEMD_DBUS_SIGNAL_USER_STARTUP_FINISHED "UserSessionStartupFinished" #define INIT_CONF_PATH "/etc/deviced/init.conf" -static struct trans_info initial_transition_info = { +static struct trans_info init_ti = { .reason = -1, - .curr = PSM_START, - .next = PSM_NORMAL, + .curr = POWER_STATE_START, + .next = POWER_STATE_NORMAL, }; static guint sig_id[2] = {0, 0}; @@ -125,19 +125,19 @@ static void parse_transition_info(const char *action) char next[16] = { 0, }; if (sscanf(action, "%15[^,],%15s", curr, next) == 2) { - initial_transition_info.curr = convert_action_string_to_psm_state(curr); - initial_transition_info.next = convert_action_string_to_psm_state(next); + init_ti.curr = convert_action_string_to_power_state(curr); + init_ti.next = convert_action_string_to_power_state(next); } } -static void parse_initial_transition_info(const struct parse_result *result) +static void parse_init_ti(const struct parse_result *result) { GList *elem; struct section_property *prop; SYS_G_LIST_FOREACH(result->props, elem, prop) { if (MATCH(prop->key, "Enum")) - initial_transition_info.reason = atoi(prop->value); + init_ti.reason = atoi(prop->value); else if (MATCH(prop->key, "Action")) parse_transition_info(prop->value); } @@ -154,27 +154,21 @@ static int parse_matching_bootreason(const struct parse_result *result, void *da SYS_G_LIST_FOREACH(result->props, elem, prop) { if (MATCH(prop->key, "BootReason") && MATCH(prop->value, bootreason)) - parse_initial_transition_info(result); + parse_init_ti(result); } return 0; } -/* the initial transition by bootreason is defined in init.conf */ -void get_initial_transition_by_bootreason(void *data) +void do_initial_transition_by_bootreason(void) { int retval; char bootreason[64] = "Unknown"; - GList **head = (GList **) data; - - if (!head) - return; retval = hal_device_board_get_boot_reason(bootreason, sizeof(bootreason)); if (retval == 0) libsys_config_parse_by_section(INIT_CONF_PATH, parse_matching_bootreason, bootreason); CRITICAL_LOG("BootReason=%s", bootreason); - - *head = g_list_append(*head, &initial_transition_info); + power_request_change_state_strict(init_ti.curr, init_ti.next, init_ti.reason, NULL); } diff --git a/src/power/power-boot.h b/src/power/power-boot.h index 7c339e0..c0fcda6 100644 --- a/src/power/power-boot.h +++ b/src/power/power-boot.h @@ -21,7 +21,7 @@ void add_delayed_init_done_handler(void *data); void remove_delayed_init_done_handler(void *data); -void get_initial_transition_by_bootreason(void *data); +void do_initial_transition_by_bootreason(void); extern int silent_boot; diff --git a/src/power/power-dbus.c b/src/power/power-dbus.c index e6ec1e9..42b4b06 100644 --- a/src/power/power-dbus.c +++ b/src/power/power-dbus.c @@ -31,7 +31,7 @@ #include "shared/device-notifier.h" #include "power-suspend.h" -#include "power-state-manager.h" +#include "power.h" #include "power-state-wait.h" #ifndef PROCESS_CHECK_TIMEOUT diff --git a/src/power/power-off.c b/src/power/power-off.c index 4f7d402..b7bb97c 100644 --- a/src/power/power-off.c +++ b/src/power/power-off.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -47,6 +48,7 @@ #include "display/core.h" #include "display/display-ops.h" #include "power-off.h" +#include "power.h" #include "apps/apps.h" #include "power-boot.h" #include "shared/plugin.h" @@ -58,51 +60,7 @@ #define POWER_CONF_FILE "/etc/deviced/power.conf" static struct timeval tv_start_poweroff; -static GList *poweroff_options_list; -static enum poweroff_type poweroff_type = POWEROFF_TYPE_REBOOT; -static GList *poweroff_handles; - -static const char *poweroff_type_flagpaths[] = { // index denotes type - [POWEROFF_TYPE_POWEROFF] = POWER_FLAG_POWEROFF, - [POWEROFF_TYPE_REBOOT] = POWER_FLAG_REBOOT, - [POWEROFF_TYPE_EXIT] = POWER_FLAG_EXIT, -}; - -static const char *poweroff_type_names[] = { // index denotes type - [POWEROFF_TYPE_POWEROFF] = POWER_POWEROFF, - [POWEROFF_TYPE_REBOOT] = POWER_REBOOT, - [POWEROFF_TYPE_EXIT] = POWER_EXIT, -}; - -enum poweroff_stage { - POWEROFF_DEFAULT, /* Default stage, poweroff has not been triggered */ - POWEROFF_TRIGGERED, /* Poweroff is triggered. Wait timer can be added up to this stage */ - POWEROFF_WAIT_OTHERS, /* Wait for other processes to clean up their resources */ -}; - static int poweroff_delay_second = 0; -static enum poweroff_stage poweroff_stage; - -static const char *poweroff_type_to_name(enum poweroff_type type) -{ - if (type <= 0 || type >= ARRAY_SIZE(poweroff_type_names)) - return NULL; - - return poweroff_type_names[type]; -} - -static enum poweroff_type poweroff_name_to_type(const char *name) -{ - if (!name) - goto out; - - for (int i = 0; i < ARRAY_SIZE(poweroff_type_names); i++) { - if (poweroff_type_names[i] && strcmp(poweroff_type_names[i], name) == 0) - return i; - } -out: - return POWEROFF_TYPE_INVALID; -} static void poweroff_start_animation(void) { @@ -173,26 +131,25 @@ static bool disable_coredump_handler(void) return is_ok; } -void poweroff_request_shutdown(void) +void poweroff_request_shutdown(int state) { - const char *systemd_poweroff_method = "Reboot"; - - if (poweroff_type == POWEROFF_TYPE_REBOOT) { - systemd_poweroff_method = "Reboot"; - } else if (poweroff_type == POWEROFF_TYPE_POWEROFF) { - systemd_poweroff_method = "PowerOff"; - } else if (poweroff_type == POWEROFF_TYPE_EXIT) { - systemd_poweroff_method = "Exit"; - } else { - _E("Invalid poweroff type=%d", poweroff_type); + const char *systemd_method = "PowerOff"; + + if (!is_poweroff_state(state)) return; - } - CRITICAL_LOG("Requested %s via systemd.", systemd_poweroff_method); + if (state == POWER_STATE_POWEROFF) + systemd_method = "PowerOff"; + else if (state == POWER_STATE_REBOOT) + systemd_method = "Reboot"; + else if (state == POWER_STATE_EXIT) + systemd_method = "Exit"; + + CRITICAL_LOG("Requested %s via systemd.", systemd_method); gdbus_call_sync_with_reply_timeout(SYSTEMD_DBUS_DEST, SYSTEMD_DBUS_PATH, SYSTEMD_DBUS_IFACE_MANAGER, - systemd_poweroff_method, + systemd_method, NULL, NULL, POWEROFF_WAIT_SYSTEMD_MS); @@ -231,158 +188,90 @@ static void poweroff_delay_for_seconds(void) watchdog_notify(); } -void poweroff_prepare(void) -{ - if (poweroff_type == POWEROFF_TYPE_POWEROFF) - CRITICAL_LOG("Prepare PowerOff."); - else if (poweroff_type == POWEROFF_TYPE_REBOOT) - CRITICAL_LOG("Prepare Reboot."); - - poweroff_notify_resourced(); - disable_systemd_journald(); - disable_coredump_handler(); - poweroff_delay_for_seconds(); - disable_display(); - - /* Below functions follow after notifying DEVICE_NOTIFIER_POWEROFF - 1. pmlock - - pmlock_detector_poweroff_cb() - - cleanup_pmlock_statistics() - - do_copy_force() - - save_display_log() - 2. tzip - - tzip_poweroff() - - tzip_server_exit() - 3. udev - - device_change_poweroff() - - uevent_control_stop() - */ - device_notify_once(DEVICE_NOTIFIER_POWEROFF, (void *)(intptr_t) poweroff_type); -} - int poweroff_check_revived(void) { - if (access(POWER_FLAG_POWEROFF, F_OK) == 0) { - poweroff_type = POWEROFF_TYPE_POWEROFF; - return 1; + if (access(POWEROFF_OPTPATH_POWEROFF, F_OK) == 0) { + return POWER_STATE_POWEROFF; } - if (access(POWER_FLAG_REBOOT, F_OK) == 0) { - poweroff_type = POWEROFF_TYPE_REBOOT; - return 1; + if (access(POWEROFF_OPTPATH_REBOOT, F_OK) == 0) { + return POWER_STATE_REBOOT; } - if (access(POWER_FLAG_EXIT, F_OK) == 0) { - poweroff_type = POWEROFF_TYPE_EXIT; - return 1; + if (access(POWEROFF_OPTPATH_EXIT, F_OK) == 0) { + return POWER_STATE_EXIT; } - return 0; + return POWER_STATE_NORMAL; } -static void mark_poweroff_option(struct power_option *opt) +static void mark_poweroff_option(const struct trans_info *ti) { - const char *path; int fd; ssize_t len; - - if (!opt) - return; - - enum poweroff_type type = opt->type; - const char *option = opt->option; - - if (type <= 0 || type >= ARRAY_SIZE(poweroff_type_flagpaths)) + const char *optpath; + const char *option; + + if (ti->next == POWER_STATE_POWEROFF) + optpath = POWEROFF_OPTPATH_POWEROFF; + else if (ti->next == POWER_STATE_REBOOT) + optpath = POWEROFF_OPTPATH_REBOOT; + else if (ti->next == POWER_STATE_EXIT) + optpath = POWEROFF_OPTPATH_EXIT; + else return; - path = poweroff_type_flagpaths[type]; - if (!path) - return; + option = (char *) ti->data; - fd = open(path, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR); + fd = open(optpath, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { - _E("Failed to create '%s'.", path); + _E("Failed to create '%s'.", optpath); return; } - if (option) { - len = write(fd, option, strlen(option)); - if (len <= 0) - _E("Failed to store option: %zd", len < 0 ? errno : len); - } + len = option ? write(fd, option, strlen(option)) : 0; + if (len < 0) + _E("Failed to store option: %zd", len < 0 ? errno : len); close(fd); } -static void poweroff_remove_handle(pid_t pid) -{ - struct poweroff_handle *handle; - GList *elem; - - SYS_G_LIST_FOREACH(poweroff_handles, elem, handle) { - if (handle->pid == pid) - break; - } - - assert(handle); - - _D("Remove handle pid=%d(%s) timeout=%d timeout_id=%d.", handle->pid, handle->comm, handle->timeout, handle->timeout_id); - SYS_G_LIST_REMOVE(poweroff_handles, handle); - - if (handle->timeout_id) { - g_source_remove(handle->timeout_id); - handle->timeout = 0; - handle->timeout_id = 0; - } - - free(handle); -} - -static int poweroff_add_handle(pid_t pid) -{ - struct poweroff_handle *handle; - GList *l; - - SYS_G_LIST_FOREACH(poweroff_handles, l, handle) { - if (handle->pid == pid) - break; - } - - if (handle) - poweroff_remove_handle(pid); - - _D("Make a new handle."); - handle = (struct poweroff_handle *)malloc(sizeof(struct poweroff_handle)); - if (handle == NULL) { - _E("Not enough memory."); - return -ENOMEM; - } - - handle->pid = pid; - handle->timeout_id = 0; - handle->timeout = POWEROFF_WAIT_MAX; - get_command(pid, handle->comm, sizeof(handle->comm)); - - SYS_G_LIST_APPEND(poweroff_handles, handle); - _D("Add a new poweroff timer. pid=%d(%s) timeout=%d", handle->pid, handle->comm, handle->timeout); - - return 0; -} - static gboolean __poweroff_main(gpointer data) { - CRITICAL_LOG("Starting poweroff sequence."); + int state = (int)(intptr_t) data; + + CRITICAL_LOG("Starting poweroff sequence"); // Watchdog timeout 90 -> 30 sec to reduce delay from unexpected poweroff failure. sd_notifyf(0, "WATCHDOG_USEC=%llu", (unsigned long long)POWEROFF_WAIT_SYSTEMD_MS*1000); - poweroff_prepare(); - poweroff_request_shutdown(); + poweroff_notify_resourced(); + disable_systemd_journald(); + disable_coredump_handler(); + poweroff_delay_for_seconds(); + disable_display(); + + /* Below functions follow after notifying DEVICE_NOTIFIER_POWEROFF + 1. pmlock + - pmlock_detector_poweroff_cb() + - cleanup_pmlock_statistics() + - do_copy_force() + - save_display_log() + 2. tzip + - tzip_poweroff() + - tzip_server_exit() + 3. udev + - device_change_poweroff() + - uevent_control_stop() + */ + device_notify_once(DEVICE_NOTIFIER_POWEROFF, data); + + poweroff_request_shutdown(state); return G_SOURCE_REMOVE; } -static void poweroff_main(void) +void poweroff_main(void *udata) { static guint poweroff_id = 0; @@ -392,61 +281,7 @@ static void poweroff_main(void) /* Terminate this subroutine at this point. The procedure returns to the caller, therefore * the RemovePowerOffWait caller would not be blocked, if they invoked method synchronously. * And the deviced enter poweroff_main on the next gmainloop iteration. */ - poweroff_id = g_idle_add(__poweroff_main, NULL); -} - -static gboolean poweroff_wait_timeout_cb(void *data) -{ - pid_t pid = (pid_t)((intptr_t)data); - - poweroff_remove_handle(pid); - - if (poweroff_stage < POWEROFF_WAIT_OTHERS) - return G_SOURCE_REMOVE; - - /* All other processes finished cleanup. Poweroff is now on standby */ - if (SYS_G_LIST_LENGTH(poweroff_handles) == 0) { - _D("The last poweroff wait timer for pid %d is expired. Poweroff is now on standby.", pid); - poweroff_main(); - } else { - _D("Poweroff wait timer for pid %d is expired, but keep waiting for others...", pid); - } - - return G_SOURCE_REMOVE; -} - -static gboolean poweroff_start_timers(void *data) -{ - struct poweroff_handle *handle = NULL; - GList *l, *l_next; - bool timer_exist = false; - - _D("POWEROFF_STAGE=WAIT_OTHERS"); - poweroff_stage = POWEROFF_WAIT_OTHERS; - - SYS_G_LIST_FOREACH_SAFE(poweroff_handles, l, l_next, handle) { - if (kill(handle->pid, 0) == -1) { - _D("Pid=%d(%s) is dead.", handle->pid, handle->comm); - SYS_G_LIST_REMOVE(poweroff_handles, handle); - continue; - } - - _D("Run timer, pid=%d(%s) timeout=%d", handle->pid, handle->comm, handle->timeout); - - handle->timeout_id = g_timeout_add_seconds(handle->timeout, - poweroff_wait_timeout_cb, - (void *)((intptr_t)(handle->pid))); - - timer_exist = true; - } - - if (timer_exist) - return G_SOURCE_REMOVE; - - // No need to wait. Strat main poweroff procedure immediately. - poweroff_main(); - - return G_SOURCE_REMOVE; + poweroff_id = g_idle_add(__poweroff_main, udata); } static void system_shutdown_send_system_event(void) @@ -459,87 +294,32 @@ static void system_shutdown_send_system_event(void) bundle_free(b); } -static void poweroff_send_broadcast(int status) +void poweroff_prepare(const struct trans_info *ti) { - static int old = 0; - int ret_dbus; + int vconf = VCONFKEY_SYSMAN_POWER_OFF_NONE; - if (old == status) + if (!is_poweroff_state(ti->next)) return; - _D("Broadcast poweroff %d.", status); - - old = status; + mark_poweroff_option(ti); - /* Need to notify to deviced-vibrator. deviced-vibrator receives ChangeState signal for POWEROFF_TYPE_DIRECT and POWEROFF_TYPE_RESTART */ - ret_dbus = gdbus_signal_emit(NULL, - DEVICED_PATH_POWEROFF, - DEVICED_INTERFACE_POWEROFF, - SIGNAL_POWEROFF_STATE, - g_variant_new("(i)", status)); - if (ret_dbus < 0) - _E("Failed to send dbus signal(%s)", SIGNAL_POWEROFF_STATE); -} - -static int __poweroff_trigger_poweroff(const char *typename, const char *option) -{ - int ret_val; - GList *l; - struct power_option *opt = NULL; + if (ti->next == POWER_STATE_POWEROFF) + vconf = VCONFKEY_SYSMAN_POWER_OFF_DIRECT; + else if (ti->next == POWER_STATE_REBOOT) + vconf = VCONFKEY_SYSMAN_POWER_OFF_RESTART; + vconf_set_int(VCONFKEY_SYSMAN_POWER_OFF_STATUS, vconf); - if (poweroff_stage >= POWEROFF_TRIGGERED) { - _E("Duplicate poweroff request. Poweroff was already triggered."); - return -EINVAL; - } - - enum poweroff_type type = poweroff_name_to_type(typename); - if (type == POWEROFF_TYPE_INVALID) { - _E("Failed to get type enum value(%d).", type); - return -EINVAL; - } - - poweroff_type = type; - - if (option) { - SYS_G_LIST_FOREACH(poweroff_options_list, l, opt) { - if (opt->type == type && strncmp(opt->option, option, sizeof(opt->option)) == 0) - break; - } - - /* It writes option onto file /run/reboot. - * The deviced shutdown binary, which is exec of systemd, refers the file. */ - if (opt) - mark_poweroff_option(opt); - } - - ret_val = vconf_set_int(VCONFKEY_SYSMAN_POWER_OFF_STATUS, type); - if (ret_val < 0) - _E("Failed to set vconf value for power off status: %d", vconf_get_ext_errno()); - - _D("POWEROFF_STAGE=TRIGGERED"); - poweroff_stage = POWEROFF_TRIGGERED; power_disable_autosleep(); - device_notify_once(DEVICE_NOTIFIER_POWEROFF_TRIGGERED, (void *)(intptr_t) type); + device_notify_once(DEVICE_NOTIFIER_POWEROFF_TRIGGERED, (void *)(intptr_t) vconf); /* Poweroff event broadcasting */ system_shutdown_send_system_event(); - poweroff_send_broadcast(type); - - /* Skip running animation if option is silent */ - if (opt && strncmp(opt->option, "silent", sizeof("silent")) == 0) - _D("Skip running poweroff animation."); - else - poweroff_start_animation(); - - /* Spare time for AddPowerOffWait requests */ - g_timeout_add_seconds(1, poweroff_start_timers, NULL); - return 0; -} + /* Skip running animation if it is silent reboot */ + if (ti->data && strncmp(ti->data, "silent", sizeof("silent")) == 0) + return; -int poweroff_trigger_poweroff(void *data) -{ - return __poweroff_trigger_poweroff((char *)data, NULL); + poweroff_start_animation(); } static int check_sender_process(GDBusConnection *conn, const char *sender) @@ -560,15 +340,16 @@ static int check_sender_process(GDBusConnection *conn, const char *sender) return pid; } -static GVariant *dbus_power_handler(GDBusConnection *conn, +static GVariant *dbus_poweroff_handler(GDBusConnection *conn, const gchar *sender, const gchar *path, const gchar *iface, const gchar *name, GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data) { int ret; char comm[128] = "Unknown"; - char *type_str; + char *type; + int next; - g_variant_get(param, "(s)", &type_str); + g_variant_get(param, "(s)", &type); ret = check_sender_process(conn, sender); if (ret < 0) @@ -576,21 +357,33 @@ static GVariant *dbus_power_handler(GDBusConnection *conn, get_command(ret, comm, sizeof(comm)); - CRITICAL_LOG("Poweroff pid=%d(%s) requests %s.", ret, comm, type_str); - ret = __poweroff_trigger_poweroff(type_str, NULL); + if (strncmp(type, "poweroff", sizeof("poweroff")) == 0) + next = POWER_STATE_POWEROFF; + else if (strncmp(type, "reboot", sizeof("reboot")) == 0) + next = POWER_STATE_REBOOT; + else if (strncmp(type, "exit", sizeof("exit")) == 0) + next = POWER_STATE_EXIT; + else { + ret = -EINVAL; + goto out; + } + + CRITICAL_LOG("Poweroff pid=%d(%s) requests %s.", ret, comm, type); + power_request_change_state_strict(POWER_STATE_ALL, next, 9000, NULL); out: - g_free(type_str); + g_free(type); return g_variant_new("(i)", ret); } -static GVariant *dbus_power_option_handler(GDBusConnection *conn, +static GVariant *dbus_poweroff_option_handler(GDBusConnection *conn, const gchar *sender, const gchar *path, const gchar *iface, const gchar *name, GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data) { int ret; char comm[128] = "Unknown"; char *type, *option; + int next; g_variant_get(param, "(ss)", &type, &option); @@ -600,8 +393,19 @@ static GVariant *dbus_power_option_handler(GDBusConnection *conn, get_command(ret, comm, sizeof(comm)); + if (strncmp(type, "poweroff", sizeof("poweroff")) == 0) + next = POWER_STATE_POWEROFF; + else if (strncmp(type, "reboot", sizeof("reboot")) == 0) + next = POWER_STATE_REBOOT; + else if (strncmp(type, "exit", sizeof("exit")) == 0) + next = POWER_STATE_EXIT; + else { + ret = -EINVAL; + goto out; + } + CRITICAL_LOG("Poweroff pid=%d(%s) requests type=%s option=%s.", ret, comm, type, option); - ret = __poweroff_trigger_poweroff(type, option); + power_request_change_state_strict(POWER_STATE_ALL, next, 9000, option); out: g_free(type); @@ -609,70 +413,26 @@ out: return g_variant_new("(i)", ret); } -/* timer can be added before the stage POWEROFF_WAIT_OTHERS */ static GVariant *add_poweroff_time(GDBusConnection *conn, const gchar *sender, const gchar *path, const gchar *iface, const gchar *name, GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data) { - int ret; - pid_t pid; - - if (poweroff_stage >= POWEROFF_WAIT_OTHERS) { - _E("It's too late. Poweroff is already in waiting stage."); - ret = -1; - goto out; - } - - ret = check_sender_process(conn, sender); - if (ret < 0) - goto out; - - pid = (pid_t)ret; - ret = poweroff_add_handle(pid); - -out: - return g_variant_new("(i)", ret); + return g_variant_new("(i)", 0); } static GVariant *remove_poweroff_time(GDBusConnection *conn, const gchar *sender, const gchar *path, const gchar *iface, const gchar *name, GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data) { - struct poweroff_handle *handle; - GList *l, *l_next; - int ret = 0; - pid_t pid; - - ret = check_sender_process(conn, sender); - if (ret < 0) - goto out; - - pid = (pid_t)ret; - - SYS_G_LIST_FOREACH_SAFE(poweroff_handles, l, l_next, handle) { - if (handle->pid == pid) { - _D("Removed poweroff timer. pid=%d(%s)", handle->pid, handle->comm); - SYS_G_LIST_REMOVE(poweroff_handles, handle); - break; - } - } - - if (poweroff_stage == POWEROFF_WAIT_OTHERS && SYS_G_LIST_LENGTH(poweroff_handles) == 0) - poweroff_main(); - -out: - return g_variant_new("(i)", ret); + return g_variant_new("(i)", 0); } static const dbus_method_s dbus_methods[] = { - { "PowerOff" , "s" , "i", dbus_power_handler }, - { "PowerOffWithOption", "ss", "i", dbus_power_option_handler }, + { "PowerOff" , "s" , "i", dbus_poweroff_handler }, + { "PowerOffWithOption", "ss", "i", dbus_poweroff_option_handler }, /* Public API device_power_reboot() calls this dbus method. */ - { "AddPowerOffWait" , NULL, "i", add_poweroff_time }, - /* It is recommended to invoke RemovePowerOffWait by async as the invocation - * could be the last removal of poweroff waitings. And if it is, the deviced - * immediately fall into poweroff main sequence without returning to the caller. */ - { "RemovePowerOffWait", NULL, "i", remove_poweroff_time }, + { "AddPowerOffWait" , NULL, "i", add_poweroff_time }, /* deprecated */ + { "RemovePowerOffWait", NULL, "i", remove_poweroff_time }, /* deprecated */ /* Add methods here */ }; @@ -683,51 +443,9 @@ static const dbus_interface_u dbus_interface = { .nr_methods = ARRAY_SIZE(dbus_methods), }; -static int add_poweroff_option(enum poweroff_type type, const char *option) -{ - struct power_option *opt; - const char *name; - - name = poweroff_type_to_name(type); - if (!name) { - _E("Invalid type(%d).", type); - return -EINVAL; - } - - opt = calloc(1, sizeof(struct power_option)); - if (!opt) { - _E("Failed to calloc()."); - return -ENOMEM; - } - - opt->type = type; - if (option) - strncpy(opt->option, option, sizeof(opt->option) - 1); - - SYS_G_LIST_APPEND(poweroff_options_list, opt); - - _D("Add %s option=%s", name, opt->option); - - return 0; -} - -int poweroff_add_wait(pid_t pid) -{ - return poweroff_add_handle(pid); -} - -void poweroff_remove_wait(pid_t pid) -{ - g_idle_add(poweroff_wait_timeout_cb, (void *)(intptr_t) pid); -} - static int load_config(struct parse_result *result, void *user_data) { - if (MATCH(result->section, "PowerOff") && MATCH(result->name, "Option")) { - add_poweroff_option(POWEROFF_TYPE_POWEROFF, result->value); - } else if (MATCH(result->section, "Reboot") && MATCH(result->name, "Option")) { - add_poweroff_option(POWEROFF_TYPE_REBOOT, result->value); - } else if (MATCH(result->section, "PowerState") && MATCH(result->name, "PowerOffDelaySecond")) { + if (MATCH(result->section, "PowerState") && MATCH(result->name, "PowerOffDelaySecond")) { sscanf(result->value, "%d", &poweroff_delay_second); } @@ -759,13 +477,7 @@ void power_off_init(void) register_notifier(DEVICE_NOTIFIER_DELAYED_INIT, delayed_init_done); - add_poweroff_option(POWEROFF_TYPE_POWEROFF, NULL); - add_poweroff_option(POWEROFF_TYPE_RESTART, NULL); - add_poweroff_option(POWEROFF_TYPE_EXIT, NULL); - ret_val = config_parse(POWER_CONF_FILE, load_config, NULL); if (ret_val < 0) _E("Failed to load power off config: %d", ret_val); - - poweroff_stage = POWEROFF_DEFAULT; } diff --git a/src/power/power-off.h b/src/power/power-off.h index c6f8ebf..a3c3466 100644 --- a/src/power/power-off.h +++ b/src/power/power-off.h @@ -19,6 +19,7 @@ #ifndef __DEVICED_POWER_OFF_H__ #define __DEVICED_POWER_OFF_H__ +#include "power.h" #include #define POWER_POWEROFF "poweroff" @@ -26,43 +27,14 @@ #define POWER_OFF_POPUP "pwroff-popup" #define POWER_EXIT "exit" -#define POWER_FLAG_POWEROFF "/run/"POWER_POWEROFF -#define POWER_FLAG_REBOOT "/run/"POWER_REBOOT -#define POWER_FLAG_EXIT "/run/deviced-shutdown-exit" - -// 0: None -// 1: Power Off Popup is launched (not supported since Tizen5.5) -// 2: Poweroff -// 3: Restart -// 4: Exit -enum poweroff_type { - POWEROFF_TYPE_INVALID = 0, - POWEROFF_TYPE_NONE = POWEROFF_TYPE_INVALID, // compat only - POWEROFF_TYPE_POWEROFF = 2, - POWEROFF_TYPE_DIRECT = POWEROFF_TYPE_POWEROFF, // compat only - POWEROFF_TYPE_REBOOT, - POWEROFF_TYPE_RESTART = POWEROFF_TYPE_REBOOT, // compat only - POWEROFF_TYPE_EXIT, -}; - -struct power_option { - enum poweroff_type type; - char option[32]; -}; - -struct poweroff_handle { - pid_t pid; - int timeout; - char comm[128]; - guint timeout_id; -}; +#define POWEROFF_OPTPATH_POWEROFF "/run/"POWER_POWEROFF +#define POWEROFF_OPTPATH_REBOOT "/run/"POWER_REBOOT +#define POWEROFF_OPTPATH_EXIT "/run/deviced-shutdown-exit" void power_off_init(void); - -int poweroff_trigger_poweroff(void *data); int poweroff_check_revived(void); -void poweroff_request_shutdown(void); -int poweroff_add_wait(pid_t pid); -void poweroff_remove_wait(pid_t pid); +void poweroff_request_shutdown(int state); +void poweroff_prepare(const struct trans_info *ti); +void poweroff_main(void *udata); #endif /* __DEVICED_POWER_OFF_H__ */ diff --git a/src/power/power-state-manager.c b/src/power/power-state-manager.c deleted file mode 100644 index 52c622c..0000000 --- a/src/power/power-state-manager.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * deviced - * - * Copyright (c) 2022 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include - -#include "shared/devices.h" -#include "shared/device-notifier.h" -#include "shared/log.h" -#include "power-state-manager.h" -#include "power-suspend.h" -#include "power-dbus.h" -#include "power-boot.h" -#include "power-off.h" -#include "power-state-wait.h" -#include "power-event-lock.h" - -#define EVENT_TYPE_SLEEP 0 -#define EVENT_TYPE_WAKEUP 1 - -char *psm_name[PSM_MAX] = { - [PSM_START] = "PSM_START", - [PSM_NORMAL] = "PSM_NORMAL", - [PSM_SLEEP] = "PSM_SLEEP", - [PSM_POWEROFF] = "PSM_POWEROFF", - [PSM_REBOOT] = "PSM_REBOOT", -}; - -static int delayed_init_done = 0; -static guint64 state_transition_counter = 0; -static enum psm_state current = PSM_START; - -/* hold trans_info until delayed_init_done */ -static GList *deferred_transition_list; - -static void psm_wake_unlock(void) -{ - /* for PSM_NORMAL, PSM_POWEROFF, do not wake unlock */ - if (current != PSM_SLEEP) { - _E("Ignore sleep wait done, current=%s", psm_name[current]); - return; - } - - power_release_wakelock(); -} - -static void psm_trigger_poweroff(void) -{ - poweroff_trigger_poweroff("poweroff"); -} - -static void broadcast_transition_info(const struct trans_info *ti) -{ - // mapping deviced state to capi signame - static const char *capi_signame[PSM_MAX] = { - [PSM_START] = SIGNAME_CHANGE_STATE_TO_START, - [PSM_NORMAL] = SIGNAME_CHANGE_STATE_TO_NORMAL, - [PSM_SLEEP] = SIGNAME_CHANGE_STATE_TO_SLEEP, - [PSM_POWEROFF] = SIGNAME_CHANGE_STATE_TO_POWEROFF, - [PSM_REBOOT] = SIGNAME_CHANGE_STATE_TO_REBOOT, - }; - - // mapping deviced state to capi state - static const guint64 capi_state[PSM_MAX] = { - [PSM_START] = POWER_STATE_START, - [PSM_NORMAL] = POWER_STATE_NORMAL, - [PSM_SLEEP] = POWER_STATE_SLEEP, - [PSM_POWEROFF] = POWER_STATE_POWEROFF, - [PSM_REBOOT] = POWER_STATE_REBOOT, - }; - - gdbus_signal_emit(NULL, DEVICED_PATH_POWER, DEVICED_INTERFACE_POWER, capi_signame[ti->next], - g_variant_new("(ttti)", capi_state[ti->curr], capi_state[ti->next], state_transition_counter, ti->reason)); -} - -static void psm_transition_start_to_normal(const struct trans_info *ti) -{ - current = PSM_NORMAL; - - power_acquire_wakelock(); - broadcast_transition_info(ti); -} - -static void psm_transition_start_to_sleep(const struct trans_info *ti) -{ - int waiting; - - current = PSM_SLEEP; - - power_acquire_wakelock(); - broadcast_transition_info(ti); - waiting = update_change_state_wait(state_transition_counter, ti, psm_wake_unlock); - if (waiting > 0) { - _D("Defer wake unlock."); - return; - } - - psm_wake_unlock(); -} - -static void psm_transition_normal_to_normal(const struct trans_info *ti) -{ - broadcast_transition_info(ti); - update_change_state_wait(state_transition_counter, ti, NULL); -} - -static void psm_transition_normal_to_sleep(const struct trans_info *ti) -{ - int waiting; - - current = PSM_SLEEP; - - broadcast_transition_info(ti); - waiting = update_change_state_wait(state_transition_counter, ti, psm_wake_unlock); - if (waiting > 0) { - _D("Defer wake unlock."); - return; - } - - psm_wake_unlock(); -} - -static void psm_transition_sleep_to_normal(const struct trans_info *ti) -{ - current = PSM_NORMAL; - - power_acquire_wakelock(); - broadcast_transition_info(ti); - update_change_state_wait(state_transition_counter, ti, NULL); -} - -static void psm_transition_sleep_to_sleep(const struct trans_info *ti) -{ - int waiting; - - power_acquire_wakelock(); - broadcast_transition_info(ti); - waiting = update_change_state_wait(state_transition_counter, ti, psm_wake_unlock); - if (waiting > 0) { - _D("Defer wake unlock."); - return; - } - - psm_wake_unlock(); -} - -static void psm_transition_normal_to_poweroff(const struct trans_info *ti) -{ - int waiting; - - current = PSM_POWEROFF; - - broadcast_transition_info(ti); - waiting = update_change_state_wait(state_transition_counter, ti, psm_trigger_poweroff); - if (waiting > 0) { - _D("Defer poweroff."); - return; - } - - psm_trigger_poweroff(); -} - -static void transition_state(const struct trans_info *ti) -{ - enum psm_state next; - - next = ti->next; - - _D("Transition power state: %s -> %s, reason=%d", - psm_name[current], psm_name[next], ti->reason); - - /* transition */ - ++state_transition_counter; - if (current == PSM_START) { - if (next == PSM_NORMAL) - psm_transition_start_to_normal(ti); - else if (next == PSM_SLEEP) - psm_transition_start_to_sleep(ti); - } else if (current == PSM_NORMAL) { - if (next == PSM_NORMAL) - psm_transition_normal_to_normal(ti); - else if (next == PSM_SLEEP) - psm_transition_normal_to_sleep(ti); - else if (next == PSM_POWEROFF) - psm_transition_normal_to_poweroff(ti); - } else if (current == PSM_SLEEP) { - if (next == PSM_NORMAL) - psm_transition_sleep_to_normal(ti); - else if (next == PSM_SLEEP) - psm_transition_sleep_to_sleep(ti); - } -} - -static void deferred_transition_state(gpointer data) -{ - transition_state(data); - free(data); -} - -static int psm_transition_state_cb(void *data) -{ - GList *action_list, *elem; - const struct trans_info *ti = NULL; - - if (!data) - return 0; - - action_list = (GList *) data; - - /* look for transition defined on the current state */ - SYS_G_LIST_FOREACH(action_list, elem, ti) { - if (ti->curr == current) - break; - } - - if (!ti) - return 0; - - /* defer state transition until delayed_init_done */ - if (!delayed_init_done) { - struct trans_info *deferred_ti = calloc(1, sizeof(struct trans_info)); - if (!deferred_ti) { - CRITICAL_LOG("Failed to defer transition."); - return 0; - } - - // Pseudo state transition. - current = ti->next; - - // Reserve the trans_info. - // Those are used on receiving delayed_init_done for real transitioning state. - memcpy(deferred_ti, ti, sizeof(struct trans_info)); - deferred_transition_list = g_list_append(deferred_transition_list, deferred_ti); - _D("Defer state transition %s->%s until delayed init done.", psm_name[ti->curr], psm_name[ti->next]); - - return 0; - } - - transition_state(ti); - - return 0; -} - -static int delayed_init_cb(void *data) -{ - delayed_init_done = 1; - - _D("Start deferred state transition."); - - /* rewind current state to initial state and do the deferred transition */ - current = PSM_START; - g_list_free_full(g_steal_pointer(&deferred_transition_list), deferred_transition_state); - - /* Enable autosleep at this point. - * This prevents system go suspend(autosleep) before booting done */ - _D("Finished deferred state transition. Enable autosleep."); - power_enable_autosleep(); - - return 0; -} - -void power_state_manager_init(void *data) -{ - GList *initial_ti = NULL; - - register_notifier(DEVICE_NOTIFIER_DELAYED_INIT, delayed_init_cb); - register_notifier(DEVICE_NOTIFIER_REQUEST_TRANSITION_STATE, psm_transition_state_cb); - - power_dbus_init(); - power_off_init(); - power_suspend_init(); - power_event_lock_init(); - power_state_wait_init(); - - /* Take the first transition from PSM_START. - * It is determined by bootreason to which state to transition, PSM_NORMAL or PSM_SLEEP. */ - get_initial_transition_by_bootreason(&initial_ti); - psm_transition_state_cb(initial_ti); - g_list_free(initial_ti); -} - -static const struct device_ops power_state_manager_device_ops = { - DECLARE_NAME_LEN("power-state-manager"), - .init = power_state_manager_init, - /* It should be initilalized earlier than the almost other modules so that - * it can receive and handle power request from the other modules. Therefore - * give a high enough priority. */ - .priority = 990, - .execute = poweroff_trigger_poweroff, -}; - -DEVICE_OPS_REGISTER(&power_state_manager_device_ops) diff --git a/src/power/power-state-manager.h b/src/power/power-state-manager.h deleted file mode 100644 index db3bd6b..0000000 --- a/src/power/power-state-manager.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * deviced - * - * Copyright (c) 2022 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __POWER_STATE_MANAGER_H__ -#define __POWER_STATE_MANAGER_H__ - -#include -#include - -#include "shared/log.h" - -enum psm_state { - PSM_START, - PSM_NORMAL, - PSM_SLEEP, - PSM_POWEROFF, - PSM_REBOOT, - PSM_MAX, -}; -extern char *psm_name[PSM_MAX]; - -struct trans_info { - int reason; - enum psm_state curr; - enum psm_state next; -}; - -typedef void (*psm_transfunc) (const struct trans_info *info); -void power_state_manager_init(void *data); - -static inline enum psm_state convert_action_string_to_psm_state(const char *str) -{ - if (MATCH(str, "start")) - return PSM_START; - else if (MATCH(str, "sleep")) - return PSM_SLEEP; - else if (MATCH(str, "normal")) - return PSM_NORMAL; - else if (MATCH(str, "poweroff")) - return PSM_POWEROFF; - - _W("Invalid psm_state=%s", str); - - return PSM_MAX; -} - -#endif //__POWER_STATE_MANAGER_H__ diff --git a/src/power/power-state-wait.c b/src/power/power-state-wait.c index 44b6ffb..a1c3f65 100644 --- a/src/power/power-state-wait.c +++ b/src/power/power-state-wait.c @@ -26,8 +26,8 @@ #include "shared/device-notifier.h" #include "shared/common.h" +#include "power.h" #include "power-state-wait.h" -#include "power-state-manager.h" #define POWER_CONF_FILE "/etc/deviced/power.conf" #define MAX_WAIT_SECOND 5 /* second */ @@ -44,18 +44,19 @@ struct proc_info { struct change_state_wait { struct proc_info *pi; guint64 id; - enum psm_state state; + int state; }; static GList *proc_list; static GList *waiting_list; static int max_wait_timer; -static void (*__change_state_wait_done) (void); -static enum psm_state waiting_state; +static void (*__change_state_wait_done) (void *); +static void *udata; +static int waiting_state = POWER_STATE_START; static void change_state_wait_done(void) { - _D("%s wait done", psm_name[waiting_state]); + _D("%s wait done", state_name(waiting_state)); if (max_wait_timer) { g_source_remove(max_wait_timer); @@ -63,7 +64,7 @@ static void change_state_wait_done(void) } if (__change_state_wait_done) { - __change_state_wait_done(); + __change_state_wait_done(udata); } } @@ -79,7 +80,7 @@ static gboolean max_wait_expired_cb(void *data) if (kill(csw->pi->pid, 0) != 0) csw->pi->killed = 1; else - _E("pid=%d(%s) hasn't confirmed id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]); + _E("pid=%d(%s) hasn't confirmed id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->id, state_name(csw->state)); waiting_list = g_list_remove_link(waiting_list, elem); g_list_free(elem); free(csw); @@ -152,7 +153,7 @@ int confirm_change_state_wait(pid_t pid, guint64 id) SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) { if (csw->pi->pid == pid && csw->id == id) { - _D("pid=%d(%s) confirmed id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]); + _D("pid=%d(%s) confirmed id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->id, state_name(csw->state)); waiting_list = g_list_remove_link(waiting_list, elem); g_list_free(elem); free(csw); @@ -167,29 +168,12 @@ int confirm_change_state_wait(pid_t pid, guint64 id) return 0; } -static int check_proc_requested_wait_for_state(struct proc_info *pi, enum psm_state state) -{ - if (!pi) - return 0; - - if (state == PSM_NORMAL) - return (pi->state_bitmap & POWER_STATE_NORMAL); - if (state == PSM_SLEEP) - return (pi->state_bitmap & POWER_STATE_SLEEP); - if (state == PSM_POWEROFF) - return (pi->state_bitmap & POWER_STATE_POWEROFF); - if (state == PSM_REBOOT) - return (pi->state_bitmap & POWER_STATE_REBOOT); - - return 0; -} - -int update_change_state_wait(guint64 id, const struct trans_info *ti, change_state_wait_done_cb callback) +int update_change_state_wait(guint64 id, const struct trans_info *ti, change_state_wait_done_cb callback, void *user_data) { struct proc_info *pi; struct change_state_wait *csw; GList *elem, *elem_next; - int n_waiting; + static int n_waiting = 0; // initialize timer if (max_wait_timer) { @@ -197,23 +181,29 @@ int update_change_state_wait(guint64 id, const struct trans_info *ti, change_sta max_wait_timer = 0; } + if (is_poweroff_state(waiting_state)) { + _W("No need to wait if poweroff had been triggered."); + return n_waiting; + } + // we are waiting for confirm of transition to the next state waiting_state = ti->next; - _D("%s wait is triggered, id=%"PRIu64, psm_name[waiting_state], id); + + _D("%s wait is triggered, id=%"PRIu64, state_name(waiting_state), id); // cancel all ongoing csw that is not a waiting for the next state SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) { - if (csw->state != waiting_state) { + if ((csw->state & waiting_state) == 0) { waiting_list = g_list_remove_link(waiting_list, elem); g_list_free(elem); - _D("Cancel waiting: pid=%d(%s) for id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]); + _D("Cancel waiting: pid=%d(%s) for id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->id, state_name(csw->state)); free(csw); } } // add wait list SYS_G_LIST_FOREACH(proc_list, elem, pi) { - if (check_proc_requested_wait_for_state(pi, waiting_state)) { + if (pi->state_bitmap & waiting_state) { csw = calloc(1, sizeof(struct change_state_wait)); if (!csw) continue; @@ -227,12 +217,13 @@ int update_change_state_wait(guint64 id, const struct trans_info *ti, change_sta n_waiting = SYS_G_LIST_LENGTH(waiting_list); if (n_waiting == 0) { - _D("There were no csw requests for %s, skip waiting", psm_name[waiting_state]); + _D("There were no csw requests for %s, skip waiting", state_name(waiting_state)); __change_state_wait_done = NULL; return 0; } __change_state_wait_done = callback; + udata = user_data; _D("The number of pending wait confirm=%d", n_waiting); max_wait_timer = g_timeout_add_seconds(max_wait_timeout, max_wait_expired_cb, NULL); diff --git a/src/power/power-state-wait.h b/src/power/power-state-wait.h index 9fc7359..3a2660f 100644 --- a/src/power/power-state-wait.h +++ b/src/power/power-state-wait.h @@ -20,14 +20,14 @@ #define __POWER_STATE_WAIT_H__ #include -#include "power-state-manager.h" +#include "power.h" -typedef void (*change_state_wait_done_cb) (void); +typedef void (*change_state_wait_done_cb) (void *); int add_change_state_wait(pid_t pid, guint64 state); void remove_change_state_wait(pid_t pid, guint64 state); int confirm_change_state_wait(pid_t pid, guint64 id); -int update_change_state_wait(guint64 id, const struct trans_info *ti, change_state_wait_done_cb cb); +int update_change_state_wait(guint64 id, const struct trans_info *ti, change_state_wait_done_cb cb, void *user_data); void power_state_wait_init(void); #endif //__POWER_STATE_WAIT_H__ diff --git a/src/power/power.c b/src/power/power.c new file mode 100644 index 0000000..fca5ba3 --- /dev/null +++ b/src/power/power.c @@ -0,0 +1,317 @@ +/* + * deviced + * + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "shared/devices.h" +#include "shared/device-notifier.h" +#include "shared/log.h" +#include "power.h" +#include "power-suspend.h" +#include "power-dbus.h" +#include "power-boot.h" +#include "power-off.h" +#include "power-state-wait.h" +#include "power-event-lock.h" + +#define EVENT_TYPE_SLEEP 0 +#define EVENT_TYPE_WAKEUP 1 + +static int delayed_init_done = 0; +static uint64_t state_transition_counter = 0; +static uint64_t current = POWER_STATE_START; + +/* hold trans_info until delayed_init_done */ +static GList *deferred_transition_list; + +static int power_transition_state(void *data); + +static void power_wake_unlock(void *udata) +{ + /* for POWER_STATE_NORMAL, POWER_STATE_POWEROFF, do not wake unlock */ + if (current != POWER_STATE_SLEEP) { + _E("Ignore sleep wait done, current=%s", state_name(current)); + return; + } + + power_release_wakelock(); +} + +static void broadcast_transition_info(const struct trans_info *ti) +{ + const char *signame; + + if (ti->next == POWER_STATE_START) + signame = SIGNAME_CHANGE_STATE_TO_START; + else if (ti->next == POWER_STATE_NORMAL) + signame = SIGNAME_CHANGE_STATE_TO_NORMAL; + else if (ti->next == POWER_STATE_SLEEP) + signame = SIGNAME_CHANGE_STATE_TO_SLEEP; + else if (ti->next == POWER_STATE_POWEROFF) + signame = SIGNAME_CHANGE_STATE_TO_POWEROFF; + else if (ti->next == POWER_STATE_REBOOT) + signame = SIGNAME_CHANGE_STATE_TO_REBOOT; + else if (ti->next == POWER_STATE_EXIT) + signame = SIGNAME_CHANGE_STATE_TO_EXIT; + else + return; + + gdbus_signal_emit(NULL, DEVICED_PATH_POWER, DEVICED_INTERFACE_POWER, signame, + g_variant_new("(ttti)", ti->curr, ti->next, state_transition_counter, ti->reason)); +} + +static void power_transition_start_to_normal(const struct trans_info *ti) +{ + current = POWER_STATE_NORMAL; + + power_acquire_wakelock(); + broadcast_transition_info(ti); +} + +static void power_transition_start_to_sleep(const struct trans_info *ti) +{ + int waiting; + + current = POWER_STATE_SLEEP; + + power_acquire_wakelock(); + broadcast_transition_info(ti); + waiting = update_change_state_wait(state_transition_counter, ti, power_wake_unlock, NULL); + if (waiting > 0) { + _D("Defer wake unlock."); + return; + } + + power_wake_unlock(NULL); +} + +static void power_transition_normal_to_normal(const struct trans_info *ti) +{ + broadcast_transition_info(ti); + update_change_state_wait(state_transition_counter, ti, NULL, NULL); +} + +static void power_transition_normal_to_sleep(const struct trans_info *ti) +{ + int waiting; + + current = POWER_STATE_SLEEP; + + broadcast_transition_info(ti); + waiting = update_change_state_wait(state_transition_counter, ti, power_wake_unlock, NULL); + if (waiting > 0) { + _D("Defer wake unlock."); + return; + } + + power_wake_unlock(NULL); +} + +static void power_transition_sleep_to_normal(const struct trans_info *ti) +{ + current = POWER_STATE_NORMAL; + + power_acquire_wakelock(); + broadcast_transition_info(ti); + update_change_state_wait(state_transition_counter, ti, NULL, NULL); +} + +static void power_transition_sleep_to_sleep(const struct trans_info *ti) +{ + int waiting; + + power_acquire_wakelock(); + broadcast_transition_info(ti); + waiting = update_change_state_wait(state_transition_counter, ti, power_wake_unlock, NULL); + if (waiting > 0) { + _D("Defer wake unlock."); + return; + } + + power_wake_unlock(NULL); +} + +static void power_transition_to_poweroff(const struct trans_info *ti) +{ + int waiting; + + _D("Transition power state: %s -> %s, option=%s, reason=%d", + state_name(ti->curr), state_name(ti->next), (char *) ti->data, ti->reason); + + current = ti->next; + + // do not transition anymore after poweroff + unregister_notifier(DEVICE_NOTIFIER_REQUEST_TRANSITION_STATE, power_transition_state); + + poweroff_prepare(ti); + broadcast_transition_info(ti); + waiting = update_change_state_wait(state_transition_counter, ti, poweroff_main, (void *)(intptr_t) ti->next); + if (waiting > 0) { + _D("Defer poweroff."); + return; + } + + poweroff_main((void *)(intptr_t) ti->next); +} + +static void transition_state(const struct trans_info *ti) +{ + int next; + struct trans_info __ti = { 0, }; + + if (ti->curr == POWER_STATE_ALL) { + /* replace POWER_STATE_ALL to the current state */ + memcpy(&__ti, ti, sizeof(struct trans_info)); + __ti.curr = current; + ti = &__ti; + } + + _D("Transition power state: %s -> %s, reason=%d", + state_name(ti->curr), state_name(ti->next), ti->reason); + + next = ti->next; + + /* transition */ + ++state_transition_counter; + if (current == POWER_STATE_START) { + if (next == POWER_STATE_NORMAL) + power_transition_start_to_normal(ti); + else if (next == POWER_STATE_SLEEP) + power_transition_start_to_sleep(ti); + else if (is_poweroff_state(next)) + power_transition_to_poweroff(ti); + } else if (current == POWER_STATE_NORMAL) { + if (next == POWER_STATE_NORMAL) + power_transition_normal_to_normal(ti); + else if (next == POWER_STATE_SLEEP) + power_transition_normal_to_sleep(ti); + else if (is_poweroff_state(next)) + power_transition_to_poweroff(ti); + } else if (current == POWER_STATE_SLEEP) { + if (next == POWER_STATE_NORMAL) + power_transition_sleep_to_normal(ti); + else if (next == POWER_STATE_SLEEP) + power_transition_sleep_to_sleep(ti); + else if (is_poweroff_state(next)) + power_transition_to_poweroff(ti); + } +} + +static void deferred_transition_state(gpointer data) +{ + transition_state(data); + free(data); +} + +static int power_transition_state(void *data) +{ + GList *ti_list, *elem; + const struct trans_info *ti = NULL; + + if (!data) + return 0; + + ti_list = (GList *) data; + + // look for trans_info defined on the current state + SYS_G_LIST_FOREACH(ti_list, elem, ti) { + if (ti->curr & current) + break; + } + + if (!ti) + return 0; + + // defer state transition until delayed_init_done unless it is transition to poweroff. + // poweroff shall be handled immediately regardless of the delayed_init_done. + if (!delayed_init_done && !is_poweroff_state(ti->next)) { + struct trans_info *deferred_ti = calloc(1, sizeof(struct trans_info)); + if (!deferred_ti) { + CRITICAL_LOG("Failed to defer transition."); + return 0; + } + + // Pseudo state transition. + current = ti->next; + + // Reserve the trans_info. + // Those are used on receiving delayed_init_done for real transitioning state. + memcpy(deferred_ti, ti, sizeof(struct trans_info)); + deferred_transition_list = g_list_append(deferred_transition_list, deferred_ti); + _D("Defer state transition %s->%s until delayed init done.", state_name(ti->curr), state_name(ti->next)); + + return 0; + } + + transition_state(ti); + + return 0; +} + +static int delayed_init_cb(void *data) +{ + delayed_init_done = 1; + + _D("Start deferred state transition."); + + /* rewind current state to initial state and do the deferred transition */ + current = POWER_STATE_START; + g_list_free_full(g_steal_pointer(&deferred_transition_list), deferred_transition_state); + + /* Enable autosleep at this point. + * This prevents system go suspend(autosleep) before booting done */ + _D("Finished deferred state transition. Enable autosleep."); + power_enable_autosleep(); + + return 0; +} + +void power_state_init(void *data) +{ + register_notifier(DEVICE_NOTIFIER_DELAYED_INIT, delayed_init_cb); + register_notifier(DEVICE_NOTIFIER_REQUEST_TRANSITION_STATE, power_transition_state); + + power_dbus_init(); + power_off_init(); + power_suspend_init(); + power_event_lock_init(); + power_state_wait_init(); + + /* Take the first transition. + * + * It is determined by bootreason to which state to transition, POWER_STATE_NORMAL or POWER_STATE_SLEEP. + * Normally, it will be transitioned to the POWER_STATE_NORMAL unless there is configuration for + * the initial state in init.conf. */ + do_initial_transition_by_bootreason(); +} + +static const struct device_ops power_state_device_ops = { + DECLARE_NAME_LEN("power-state"), + .init = power_state_init, + /* It should be initilalized earlier than the almost other modules so that + * it can receive and handle power request from the other modules. Therefore + * give a high enough priority. */ + .priority = 990, +}; + +DEVICE_OPS_REGISTER(&power_state_device_ops) diff --git a/src/power/power.h b/src/power/power.h new file mode 100644 index 0000000..366b8a4 --- /dev/null +++ b/src/power/power.h @@ -0,0 +1,101 @@ +/* + * deviced + * + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __POWER_STATE_MANAGER_H__ +#define __POWER_STATE_MANAGER_H__ + +#include +#include +#include +#include + +#include "shared/log-macro.h" +#include "shared/device-notifier.h" + +#define POWER_STATE_NONE 0 + +struct trans_info { + uint64_t curr; + uint64_t next; + int reason; + void *data; +}; + +void power_state_manager_init(void *data); + +static inline uint64_t convert_action_string_to_power_state(const char *str) +{ + if (MATCH(str, "start")) + return POWER_STATE_START; + else if (MATCH(str, "sleep")) + return POWER_STATE_SLEEP; + else if (MATCH(str, "normal")) + return POWER_STATE_NORMAL; + else if (MATCH(str, "poweroff")) + return POWER_STATE_POWEROFF; + else if (MATCH(str, "reboot")) + return POWER_STATE_REBOOT; + else if (MATCH(str, "exit")) + return POWER_STATE_EXIT; + else if (MATCH(str, "current")) + return POWER_STATE_ALL; + + _W("Invalid power state=%s", str); + + return POWER_STATE_NONE; +} + +static inline const char *state_name(uint64_t state) +{ + if (state == POWER_STATE_START) + return "POWER_STATE_START"; + if (state == POWER_STATE_NORMAL) + return "POWER_STATE_NORMAL"; + if (state == POWER_STATE_SLEEP) + return "POWER_STATE_SLEEP"; + if (state == POWER_STATE_POWEROFF) + return "POWER_STATE_POWEROFF"; + if (state == POWER_STATE_REBOOT) + return "POWER_STATE_REBOOT"; + if (state == POWER_STATE_EXIT) + return "POWER_STATE_EXIT"; + if (state == POWER_STATE_ALL) + return "POWER_STATE_ALL"; + + return "POWER_STATE_INVALID"; +} + +static inline int is_poweroff_state(uint64_t state) +{ + return !!(state & (POWER_STATE_POWEROFF | POWER_STATE_REBOOT | POWER_STATE_EXIT)); +} + +static inline void power_request_change_state_strict(uint64_t curr, uint64_t next, int reason, void *udata) +{ + struct trans_info ti = { curr, next, reason, udata }; + GList l = { &ti, NULL, NULL }; + + device_notify(DEVICE_NOTIFIER_REQUEST_TRANSITION_STATE, &l); +} + +static inline void power_request_change_state(uint64_t next, int reason) +{ + power_request_change_state_strict(POWER_STATE_ALL, next, reason, NULL); +} + +#endif //__POWER_STATE_MANAGER_H__ -- 2.7.4