+++ /dev/null
-/*
- * 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 <unistd.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <libsyscommon/list.h>
-#include <device/power-internal.h>
-
-#include "shared/log.h"
-#include "shared/device-notifier.h"
-#include "shared/common.h"
-
-#include "power.h"
-#include "power-state-wait.h"
-
-#define POWER_CONF_FILE "/etc/deviced/power.conf"
-#define MAX_WAIT_SECOND 5 /* second */
-
-static int max_wait_timeout = 5; /* second */
-
-struct proc_info {
- pid_t pid;
- char comm[128];
- guint64 state_bitmap;
- int killed;
-};
-
-struct change_state_wait {
- struct proc_info *pi;
- guint64 id;
- int state;
-};
-
-static GList *proc_list;
-static GList *waiting_list;
-static int max_wait_timer;
-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", state_name(waiting_state));
-
- if (max_wait_timer) {
- g_source_remove(max_wait_timer);
- max_wait_timer = 0;
- }
-
- if (__change_state_wait_done) {
- __change_state_wait_done(udata);
- }
-}
-
-static gboolean max_wait_expired_cb(void *data)
-{
- struct change_state_wait *csw;
- struct proc_info *pi;
- GList *elem, *elem_next;
-
- max_wait_timer = 0;
-
- SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) {
- 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, state_name(csw->state));
- waiting_list = g_list_remove_link(waiting_list, elem);
- g_list_free(elem);
- free(csw);
- }
-
- SYS_G_LIST_FOREACH_SAFE(proc_list, elem, elem_next, pi) {
- if (pi->killed) {
- _D("Clean up not existing pid=%d(%s)", pi->pid, pi->comm);
- proc_list = g_list_remove_link(proc_list, elem);
- g_list_free(elem);
- free(pi);
- }
- }
-
- change_state_wait_done();
-
- return G_SOURCE_REMOVE;
-}
-
-int add_change_state_wait(pid_t pid, guint64 state)
-{
- struct proc_info *pi;
- GList *elem;
-
- SYS_G_LIST_FOREACH(proc_list, elem, pi) {
- if (pi->pid == pid) {
- pi->state_bitmap |= state;
- _D("pid=%d(%s) updated csw for %#"PRIx64, pid, pi->comm, state);
- return 0;
- }
- }
-
- pi = calloc(1, sizeof(struct proc_info));
- if (!pi)
- return -ENOMEM;
-
- pi->pid = pid;
- pi->state_bitmap = state;
- get_command(pid, pi->comm, sizeof(pi->comm));
- SYS_G_LIST_APPEND(proc_list, pi);
-
- _D("pid=%d(%s) added csw for %#"PRIx64, pid, pi->comm, state);
-
- return 0;
-}
-
-void remove_change_state_wait(pid_t pid, guint64 state)
-{
- struct proc_info *pi;
- GList *elem, *elem_next;
-
- _D("pid=%d removed csw for %#"PRIx64, pid, state);
-
- SYS_G_LIST_FOREACH_SAFE(proc_list, elem, elem_next, pi) {
- if (pi->pid == pid) {
- pi->state_bitmap &= ~state;
- if (pi->state_bitmap == 0) {
- proc_list = g_list_remove_link(proc_list, elem);
- g_list_free(elem);
- free(pi);
- }
- }
- }
-}
-
-int confirm_change_state_wait(pid_t pid, guint64 id)
-{
- struct change_state_wait *csw;
- GList *elem, *elem_next;
-
- 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, state_name(csw->state));
- waiting_list = g_list_remove_link(waiting_list, elem);
- g_list_free(elem);
- free(csw);
-
- if (SYS_G_LIST_LENGTH(waiting_list) == 0) {
- change_state_wait_done();
- return 0;
- }
- }
- }
-
- return 0;
-}
-
-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;
- static int n_waiting = 0;
-
- // initialize timer
- if (max_wait_timer) {
- g_source_remove(max_wait_timer);
- 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, 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) == 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, state_name(csw->state));
- free(csw);
- }
- }
-
- // add wait list
- SYS_G_LIST_FOREACH(proc_list, elem, pi) {
- if (pi->state_bitmap & waiting_state) {
- csw = calloc(1, sizeof(struct change_state_wait));
- if (!csw)
- continue;
-
- csw->pi = pi;
- csw->id = id;
- csw->state = waiting_state;
- SYS_G_LIST_APPEND(waiting_list, csw);
- }
- }
-
- n_waiting = SYS_G_LIST_LENGTH(waiting_list);
- if (n_waiting == 0) {
- _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);
-
- return n_waiting;
-}
-
-static int load_max_wait_timeout(struct parse_result *result, void *user_data)
-{
- if (MATCH(result->section, "PowerState")
- && MATCH(result->name, "ChangeStateMaxWaitSecond"))
- max_wait_timeout = atoi(result->value);
-
- return 0;
-}
-
-void power_state_wait_init(void)
-{
- config_parse(POWER_CONF_FILE, load_max_wait_timeout, NULL);
-
- CRITICAL_LOG("Change state max wait timeout=%ds", max_wait_timeout);
-}
#include <stdio.h>
#include <stdint.h>
+#include <assert.h>
#include <inttypes.h>
#include <linux/input.h>
#include <libsyscommon/libgdbus.h>
#include "shared/devices.h"
#include "shared/device-notifier.h"
#include "shared/log.h"
+#include "shared/common.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
+#define POWER_CONF_FILE "/etc/deviced/power.conf"
+#define POWER_STATE_INDEX(state) ((state) ? 63 - __builtin_clzll(state) : 0)
+#define DEFAULT_MAX_WAIT_SECOND 5 /* second */
+#define MAX_DESC_LEN 256
static int delayed_init_done = 0;
-static uint64_t state_transition_counter = 0;
-static uint64_t current = POWER_STATE_START;
+static uint64_t current = POWER_STATE_START; /* current power state */
+static int max_wait_timeout = DEFAULT_MAX_WAIT_SECOND;
+static GQueue *transition_queue;
+static struct {
+ int id;
+ int ongoing;
+ struct trans_info ti;
+ GList *waitings;
+ int max_wait_timer;
+} transition_context;
+
+struct proc_info {
+ pid_t pid;
+ char comm[128];
+ guint64 state_bitmap;
+};
+static GList *proc_list;
-/* hold trans_info until delayed_init_done */
-static GList *deferred_transition_list;
+struct change_state_wait {
+ struct proc_info *pi;
+ guint64 id;
+ int state;
+};
-static int power_transition_state(void *data);
+static int transition_request_callback(void *data);
+static void prepare_transition(const struct trans_info *ti);
+static void action_transition(void);
+
+static void power_action_normal(void *data);
+static void power_action_sleep(void *data);
+static void power_action_poweroff(void *data);
+static void (*const action_on_state[POWER_STATE_MAX_INDEX]) (void *) = {
+ [POWER_STATE_NORMAL_INDEX] = power_action_normal,
+ [POWER_STATE_SLEEP_INDEX] = power_action_sleep,
+ [POWER_STATE_POWEROFF_INDEX] = power_action_poweroff,
+ [POWER_STATE_REBOOT_INDEX] = power_action_poweroff,
+ [POWER_STATE_EXIT_INDEX] = power_action_poweroff,
+};
uint64_t power_get_state(void)
{
return current;
}
-static void power_wake_unlock(void *udata)
+static void cleanup_waiting_list(gpointer data)
+{
+ struct change_state_wait *csw = (struct change_state_wait *) data;
+
+ _E("%s(pid=%d) hasn't confirmed id=%"PRIu64"(%s)", csw->pi->comm, csw->pi->pid, csw->id, state_name(csw->state));
+
+ if (kill(csw->pi->pid, 0) != 0) {
+ _E("cleanup not existing process: %s(pid=%d)", csw->pi->comm, csw->pi->pid);
+ proc_list = g_list_remove(proc_list, csw->pi);
+ free(csw->pi);
+ }
+
+ free(csw);
+}
+
+static gboolean max_wait_expired_cb(void *data)
+{
+ transition_context.max_wait_timer = 0;
+
+ g_list_free_full(g_steal_pointer(&transition_context.waitings), cleanup_waiting_list);
+
+ action_transition();
+
+ return G_SOURCE_REMOVE;
+}
+
+static gint find_pi_by_pid(gconstpointer data, gconstpointer udata)
+{
+ const struct proc_info *pi = (const struct proc_info *) data;
+ const pid_t pid = *(const pid_t *) udata;
+
+ if (pi->pid == pid)
+ return 0;
+
+ return -1;
+}
+
+int add_change_state_wait(pid_t pid, guint64 state)
+{
+ struct proc_info *pi = NULL;
+ GList *l = NULL;
+
+ l = g_list_find_custom(proc_list, &pid, find_pi_by_pid);
+ if (l && l->data) {
+ pi = l->data;
+ pi->state_bitmap |= state;
+ _D("pid=%d(%s) updated csw for %#"PRIx64, pi->pid, pi->comm, state);
+ return 0;
+ }
+
+ pi = calloc(1, sizeof(struct proc_info));
+ if (!pi)
+ return -ENOMEM;
+
+ pi->pid = pid;
+ pi->state_bitmap = state;
+ get_command(pid, pi->comm, sizeof(pi->comm));
+ proc_list = g_list_append(proc_list, pi);
+
+ _D("pid=%d(%s) added csw for %#"PRIx64, pid, pi->comm, state);
+
+ return 0;
+}
+
+void remove_change_state_wait(pid_t pid, guint64 state)
+{
+ struct proc_info *pi = NULL;
+ GList *l = NULL;
+
+ l = g_list_find_custom(proc_list, &pid, find_pi_by_pid);
+ if (!l)
+ return;
+
+ if (!l->data)
+ return;
+
+ pi = l->data;
+
+ _D("pid=%d(%s) removed csw for %#"PRIx64, pi->pid, pi->comm, state);
+
+ pi->state_bitmap &= ~state;
+ if (pi->state_bitmap == 0) {
+ proc_list = g_list_remove_link(proc_list, l);
+ free(g_steal_pointer(&l->data));
+ g_list_free(l);
+ }
+}
+
+static gint find_csw_by_pid_id(gconstpointer data, gconstpointer udata)
+{
+ const struct change_state_wait *csw = (const struct change_state_wait *) data;
+ const struct change_state_wait *target = (const struct change_state_wait *) udata;
+
+ if (csw->pi->pid == target->pi->pid && csw->id == target->id)
+ return 0;
+
+ return -1;
+}
+
+int confirm_change_state_wait(pid_t pid, guint64 id)
+{
+ struct proc_info target_pi = { .pid = pid };
+ struct change_state_wait target_csw = { .pi = &target_pi, .id = id };
+ struct change_state_wait *csw = NULL;
+ GList *l = NULL;
+
+ l = g_list_find_custom(transition_context.waitings, &target_csw, find_csw_by_pid_id);
+ if (!l)
+ return 0;
+
+ if (!l->data)
+ return 0;
+
+ csw = l->data;
+ _D("pid=%d(%s) confirmed id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->id, state_name(csw->state));
+
+ transition_context.waitings = g_list_remove_link(transition_context.waitings, l);
+ free(g_steal_pointer(&l->data));
+ g_list_free(l);
+
+ // continue waiting
+ if (g_list_length(transition_context.waitings) != 0)
+ return 0;
+
+ // the last csw is cleared
+ g_source_remove(transition_context.max_wait_timer);
+ transition_context.max_wait_timer = 0;
+
+ action_transition();
+
+ return 0;
+}
+
+static int alloc_unique_id(void)
+{
+ static int id = 0;
+
+ return ++id;
+}
+
+static void power_action_normal(void *udata)
+{
+ power_acquire_wakelock();
+}
+
+static void power_action_sleep(void *udata)
{
/* for POWER_STATE_NORMAL, POWER_STATE_POWEROFF, do not wake unlock */
if (current != POWER_STATE_SLEEP) {
power_release_wakelock();
}
-static void broadcast_transition_info(const struct trans_info *ti)
+static void power_action_poweroff(void *data)
+{
+ // do not transition anymore after poweroff
+ unregister_notifier(DEVICE_NOTIFIER_REQUEST_TRANSITION_STATE, transition_request_callback);
+
+ poweroff_prepare(current, data);
+ poweroff_main((void *)(intptr_t) current);
+}
+
+static void broadcast_transition_info(void)
{
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;
+ if (transition_context.ti.next == POWER_STATE_START)
+ signame = DEVICED_SIGNAL_POWER_CHANGE_STATE_TO_START;
+ else if (transition_context.ti.next == POWER_STATE_NORMAL)
+ signame = DEVICED_SIGNAL_POWER_CHANGE_STATE_TO_NORMAL;
+ else if (transition_context.ti.next == POWER_STATE_SLEEP)
+ signame = DEVICED_SIGNAL_POWER_CHANGE_STATE_TO_SLEEP;
+ else if (transition_context.ti.next == POWER_STATE_POWEROFF)
+ signame = DEVICED_SIGNAL_POWER_CHANGE_STATE_TO_POWEROFF;
+ else if (transition_context.ti.next == POWER_STATE_REBOOT)
+ signame = DEVICED_SIGNAL_POWER_CHANGE_STATE_TO_REBOOT;
+ else if (transition_context.ti.next == POWER_STATE_EXIT)
+ signame = DEVICED_SIGNAL_POWER_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));
+ g_variant_new("(ttti)",
+ transition_context.ti.curr,
+ transition_context.ti.next,
+ transition_context.id,
+ transition_context.ti.reason));
}
-static void power_transition_start_to_normal(const struct trans_info *ti)
+static uint64_t available_starting_state(void)
{
- current = POWER_STATE_NORMAL;
+ struct trans_info *ti = NULL;
- power_acquire_wakelock();
- broadcast_transition_info(ti);
+ // If there is no ongoing transition, the available state is the current state.
+ if (!transition_context.ongoing)
+ return current;
+
+ // If there is ongoing transition and there are also some pending transitions,
+ // the available state is the last state of pending transitions.
+ // Otherwise, if there is ongoing transition but there are no pending transitions,
+ // the available state is the destination of ongoing transition.
+ return (ti = g_queue_peek_head(transition_queue)) ? ti->next : transition_context.ti.next;
}
-static void power_transition_start_to_sleep(const struct trans_info *ti)
+static int dequeue_transition(struct trans_info *ti)
{
- int waiting;
+ struct trans_info *tail = NULL;
- current = POWER_STATE_SLEEP;
+ if (!ti)
+ return -1;
- 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;
- }
+ tail = g_queue_pop_tail(transition_queue);
+ if (!tail)
+ return -1;
+
+ *ti = (struct trans_info) {
+ .curr = tail->curr,
+ .next = tail->next,
+ .reason = tail->reason,
+ .data = tail->data,
+ };
+
+ free(tail);
- power_wake_unlock(NULL);
+ return 0;
}
-static void power_transition_normal_to_normal(const struct trans_info *ti)
+static void enqueue_transition(const struct trans_info *ti)
{
- broadcast_transition_info(ti);
- update_change_state_wait(state_transition_counter, ti, NULL, NULL);
+ struct trans_info *ti_new = NULL;
+
+ if (!ti)
+ return;
+
+ ti_new = calloc(1, sizeof(struct trans_info));
+ if (!ti_new)
+ return;
+
+ g_queue_push_head(transition_queue, memcpy(ti_new, ti, sizeof(struct trans_info)));
}
-static void power_transition_normal_to_sleep(const struct trans_info *ti)
+static void init_waiting_list(gpointer data, gpointer udata)
{
- int waiting;
+ uint64_t waiting_state;
+ struct proc_info *pi = (struct proc_info *) data;
+ struct change_state_wait *csw = NULL;
- current = POWER_STATE_SLEEP;
+ waiting_state = transition_context.ti.next;
- broadcast_transition_info(ti);
- waiting = update_change_state_wait(state_transition_counter, ti, power_wake_unlock, NULL);
- if (waiting > 0) {
- _D("Defer wake unlock.");
+ if ((pi->state_bitmap & waiting_state) == 0)
return;
- }
- power_wake_unlock(NULL);
+ csw = calloc(1, sizeof(struct change_state_wait));
+ if (!csw)
+ return;
+
+ *csw = (struct change_state_wait) {
+ .pi = pi,
+ .id = transition_context.id,
+ .state = waiting_state,
+ };
+
+ *(int *) udata += 1;
+
+ transition_context.waitings = g_list_append(transition_context.waitings, csw);
}
-static void power_transition_sleep_to_normal(const struct trans_info *ti)
+static char *state_abbr_name(uint64_t state)
{
- current = POWER_STATE_NORMAL;
+ if (state == POWER_STATE_START)
+ return "START";
+ if (state == POWER_STATE_NORMAL)
+ return "NORMAL";
+ if (state == POWER_STATE_SLEEP)
+ return "SLEEP";
+ if (state == POWER_STATE_POWEROFF)
+ return "POWEROFF";
+ if (state == POWER_STATE_REBOOT)
+ return "REBOOT";
+ if (state == POWER_STATE_EXIT)
+ return "EXIT";
+
+ return "INVALID";
+}
- power_acquire_wakelock();
- broadcast_transition_info(ti);
- update_change_state_wait(state_transition_counter, ti, NULL, NULL);
+static void reload_transition_sequence(GList **sequence)
+{
+ GList *new_sequence = NULL;
+
+ g_list_free(g_steal_pointer(sequence));
+
+ new_sequence = g_list_append(new_sequence, state_abbr_name(transition_context.ti.curr));
+ new_sequence = g_list_append(new_sequence, state_abbr_name(transition_context.ti.next));
+
+ *sequence = new_sequence;
}
-static void power_transition_sleep_to_sleep(const struct trans_info *ti)
+static const char* build_description(GList *sequence, const int seqnum)
{
- int waiting;
+ static char buffer[MAX_DESC_LEN] = { 0, };
+ char *head = buffer;
+ // leave 1 byte for terminating null character
+ char *const tail = buffer + sizeof(buffer) - 1;
- 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;
+ // additional (length-1) placeholders for transitioning arrow between each state
+ int max_sequence = g_list_length(sequence) * 2 - 1;
+
+ GList *iter = sequence;
+ int n = 0;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ while (iter) {
+ GList *next = iter->next;
+
+ if (n == seqnum)
+ head += snprintf(head, tail - head, "[%s] ", (const char *) iter->data);
+ else
+ head += snprintf(head, tail - head, " %s ", (const char *) iter->data);
+
+ if (head >= tail)
+ break;
+
+ ++n;
+ if (n >= max_sequence)
+ break;
+
+ if (n == seqnum)
+ head += snprintf(head, tail - head, "[=>] ");
+ else
+ head += snprintf(head, tail - head, " => ");
+
+ if (head >= tail)
+ break;
+
+ ++n;
+ iter = next;
}
- power_wake_unlock(NULL);
+ return buffer;
}
-static void power_transition_to_poweroff(const struct trans_info *ti)
+static void transition_description(int reload)
{
- int waiting;
+ static GList *transition_sequence = NULL;
+ static int seqnum = 0;
- _D("Transition power state: %s -> %s, option=%s, reason=%d",
- state_name(ti->curr), state_name(ti->next), (char *) ti->data, ti->reason);
+ if (reload) {
+ seqnum = 0;
+ reload_transition_sequence(&transition_sequence);
+ }
- current = ti->next;
+ _D("%s", build_description(transition_sequence, seqnum++));
+}
- // do not transition anymore after poweroff
- unregister_notifier(DEVICE_NOTIFIER_REQUEST_TRANSITION_STATE, power_transition_state);
+static void trigger_transition(void)
+{
+ int waiting = 0;
- 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;
+ assert(transition_context.max_wait_timer == 0);
+ assert(is_poweroff_state(current) == 0);
+
+ transition_context.id = alloc_unique_id();
+
+ if (!transition_context.ongoing) {
+ // hold secondary wakelock not to fall asleep during transition
+ event_wake_lock(EL_POWER_TRANSITION_STATE);
+ transition_context.ongoing = 1;
}
- poweroff_main((void *)(intptr_t) ti->next);
+ transition_description(0);
+
+ broadcast_transition_info();
+ g_list_foreach(proc_list, init_waiting_list, &waiting);
+ if (waiting > 0)
+ transition_context.max_wait_timer = g_timeout_add_seconds(max_wait_timeout, max_wait_expired_cb, NULL);
+ else
+ action_transition();
}
-static void transition_state(const struct trans_info *ti)
+static void action_transition(void)
{
- 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;
+ struct trans_info ti = { 0 , };
+ int retval;
+
+ transition_description(0);
+
+ current = transition_context.ti.next;
+
+ if (action_on_state[POWER_STATE_INDEX(current)])
+ action_on_state[POWER_STATE_INDEX(current)](transition_context.ti.data);
+
+ // transition end
+ transition_context.ongoing = 0;
+ event_wake_unlock(EL_POWER_TRANSITION_STATE);
+
+ // trigger next transition if exist
+ retval = dequeue_transition(&ti);
+ if (retval == 0) {
+ prepare_transition(&ti);
+ trigger_transition();
}
+}
- _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 prepare_transition(const struct trans_info *ti)
+{
+ assert(transition_context.waitings == NULL);
+ assert(transition_context.max_wait_timer == 0);
+
+ if (ti) {
+ transition_context.ti = (struct trans_info) {
+ .curr = ti->curr,
+ .next = ti->next,
+ .reason = ti->reason,
+ .data = ti->data,
+ };
}
- if (current != ti->next)
- _W("Not defined transition. The current still stays at %s", state_name(current));
+ transition_context.ongoing = 0;
+
+ transition_description(1);
}
-static void deferred_transition_state(gpointer data)
+static gint find_ti_by_state(gconstpointer data, gconstpointer udata)
{
- transition_state(data);
- free(data);
+ const struct trans_info *ti = (const struct trans_info *) data;
+ const uint64_t state = *(const uint64_t *) udata;
+
+ if (ti->curr & state)
+ return 0;
+
+ return -1;
}
-static int power_transition_state(void *data)
+static int transition_request_callback(void *data)
{
- GList *ti_list, *elem;
- const struct trans_info *ti = NULL;
+ GList *ti_list, *l;
+ const struct trans_info *t = NULL;
+ struct trans_info ti = { 0 , };
+ uint64_t available;
if (!data)
return 0;
+ available = available_starting_state();
+
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;
- }
+ l = g_list_find_custom(ti_list, &available, find_ti_by_state);
+ if (!l)
+ return 0;
- if (!ti)
+ if (!l->data)
return 0;
+ t = l->data;
+
// check invalid next state
- if (__builtin_popcountll(ti->next & POWER_STATE_ALL) != 1) {
- _E("Invalid next state, curr=%"PRIx64", next=%"PRIx64", reason=%d", ti->curr, ti->next, ti->reason);
+ if (__builtin_popcountll(t->next & POWER_STATE_ALL) != 1) {
+ _E("Invalid next state, curr=%"PRIx64", next=%"PRIx64", reason=%d", t->curr, t->next, t->reason);
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_E("Failed to defer transition.");
- return 0;
- }
-
- // Pseudo state transition.
- current = ti->next;
+ ti = (struct trans_info) {
+ .curr = available,
+ .next = t->next,
+ .reason = t->reason,
+ .data = t->data,
+ };
- // 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));
+ // TODO: immediate handling of poweroff?
+ if (!delayed_init_done || transition_context.ongoing) {
+ enqueue_transition(&ti);
return 0;
}
- transition_state(ti);
+ prepare_transition(&ti);
+ trigger_transition();
return 0;
}
-static int delayed_init_cb(void *data)
+static int delayed_init_callback(void *data)
{
+ int retval;
+ struct trans_info ti = { 0 , };
+
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);
+ retval = dequeue_transition(&ti);
+ if (retval == 0) {
+ prepare_transition(&ti);
+ trigger_transition();
+ }
- /* 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;
}
+static int load_max_wait_timeout(struct parse_result *result, void *user_data)
+{
+ if (MATCH(result->section, "PowerState")
+ && MATCH(result->name, "ChangeStateMaxWaitSecond"))
+ max_wait_timeout = atoi(result->value);
+
+ 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);
+ transition_queue = g_queue_new();
+
+ config_parse(POWER_CONF_FILE, load_max_wait_timeout, NULL);
+
+ register_notifier(DEVICE_NOTIFIER_DELAYED_INIT, delayed_init_callback);
+ register_notifier(DEVICE_NOTIFIER_REQUEST_TRANSITION_STATE, transition_request_callback);
power_dbus_init();
power_off_init();
power_suspend_init();
power_event_lock_init();
- power_state_wait_init();
/* Take the first transition.
*