From f7474d115c92f18bd1f447414620aef428258431 Mon Sep 17 00:00:00 2001 From: Youngjae Cho Date: Tue, 31 May 2022 14:03:30 +0900 Subject: [PATCH] power: add device_power_change_state() The API, device_power_change_state(), send a request to deviced for changing power state. It simply sends request and receives result of the request. If the changed state is sleep/poweroff/reboot, then the ongoing async callback might be ceased. To ensure the whole execution of subroutine, use device_power_add_change_state_wait_callback(). Change-Id: Id7479a08d56505a0bc6bfba3e9971cb635e1276e Signed-off-by: Youngjae Cho --- include/power-internal.h | 29 +++++++++++- src/power-internal.c | 115 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/include/power-internal.h b/include/power-internal.h index 0393310..fac6b59 100644 --- a/include/power-internal.h +++ b/include/power-internal.h @@ -82,7 +82,9 @@ struct device_change_state_info { typedef void (*change_state_wait_callback) (const struct device_change_state_info *info, void *user_data); /** - * Add callback for states to be transitioned. + * Add callback for states to be transitioned. The action caused by the transition, + * e.g., wake unlock for sleep or shutdown for reboot, would be defered until calling + * device_power_change_state_wait_done(). * * uint64_t state_bits: bitwise-ORing of interesting state. * change_state_wait_callback cb: For every state transition to the state_bit, the cb will be invoked. @@ -102,6 +104,31 @@ int device_power_change_state_wait_done(uint64_t id); */ void device_power_remove_change_state_wait_callback(uint64_t state_bits); +/** + * Async callback of device_power_change_state() + * + * uint64_t state: which state it has transitioned. + * int retval: result of the method call. + * void *user_data: user data for the callback. + */ +typedef void (*change_state_callback) (uint64_t state, int retval, void *user_data); + +/** + * Send a request to the deviced for changing power state. It doesn't ensure the whole execution + * of a callback subroutine - think of a request for sleep/poweroff/reboot state. To ensure + * the whole execution of a callback, use device_power_add_change_state_wait_callback() additionally. + * If both change_state_wait_callback and change_state_callback has registered to a state, + * it is unpredictable which callback will be invoked first. + * + * uint64_t state: which state to transition to + * int timeout_sec: maximum timeout of async method call. if it expires, then the second parameter of + * a callback function, int retval, gives -ETIMEDOUT error. If it is 0 or negative, it is set to + * the default, 10 seconds. + * change_state_callback cb: async callback function, nullable + * void *user_data: user data for the callback. + */ +int device_power_change_state(uint64_t state, int timeout_sec, change_state_callback cb, void *user_data); + #ifdef __cplusplus } #endif diff --git a/src/power-internal.c b/src/power-internal.c index 8cb5029..7244368 100644 --- a/src/power-internal.c +++ b/src/power-internal.c @@ -8,18 +8,24 @@ #define DBUS_METHOD_SYNC_CALL_TIMEOUT_MS 10000 /* 10 second */ -struct userdata { +struct change_state_wait_handler { change_state_wait_callback callback; - void *data; + void *user_data; +}; + +struct change_state_handler { + change_state_callback callback; + void *user_data; + uint64_t state; }; static int change_state_signal_id[POWER_STATE_MAX_INDEX]; static void signal_unsubscribed_callback(void *data) { - struct userdata *ud = (struct userdata *) data; + struct change_state_wait_handler *h = (struct change_state_wait_handler *) data; - free(ud); + free(h); } static void signal_callback(GDBusConnection *connection, @@ -31,16 +37,16 @@ static void signal_callback(GDBusConnection *connection, gpointer user_data) { struct device_change_state_info info; - struct userdata *ud; + struct change_state_wait_handler *h; - ud = (struct userdata *) user_data; + h = (struct change_state_wait_handler *) user_data; - if (!ud || !ud->callback) + if (!h || !h->callback) return; g_variant_get(parameters, "(ttti)", &info.prev_state, &info.next_state, &info.id, &info.reason); - ud->callback(&info, ud->data); + h->callback(&info, h->user_data); } static int __device_power_add_change_state_wait_callback(GDBusConnection *connection, @@ -54,19 +60,19 @@ static int __device_power_add_change_state_wait_callback(GDBusConnection *connec [POWER_STATE_REBOOT_INDEX] = SIGNAME_CHANGE_STATE_TO_REBOOT, [POWER_STATE_EXIT_INDEX] = SIGNAME_CHANGE_STATE_TO_EXIT, }; - struct userdata *ud; + struct change_state_wait_handler *h; if (change_state_signal_id[index] > 0) return DEVICE_ERROR_ALREADY_IN_PROGRESS; - ud = malloc(sizeof(struct userdata)); - if (!ud) { + h = malloc(sizeof(struct change_state_wait_handler)); + if (!h) { _E("Failed to alloc user data"); return DEVICE_ERROR_OPERATION_FAILED; } - ud->callback = cb; - ud->data = data; + h->callback = cb; + h->user_data = data; change_state_signal_id[index] = g_dbus_connection_signal_subscribe(connection, DEVICED_BUS_NAME, @@ -76,7 +82,7 @@ static int __device_power_add_change_state_wait_callback(GDBusConnection *connec NULL, G_DBUS_SIGNAL_FLAGS_NONE, signal_callback, - ud, + h, signal_unsubscribed_callback); return DEVICE_ERROR_NONE; @@ -205,3 +211,84 @@ void device_power_remove_change_state_wait_callback(uint64_t state_bits) NULL, NULL); } + +static void change_state_async_callback(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GError *err = NULL; + GDBusConnection *connection; + GVariant *retgv; + int retval; + struct change_state_handler *h; + + h = (struct change_state_handler *) user_data; + if (!h) + return; + + if (!h->callback) + goto cleanup; + + connection = (GDBusConnection *) source_object; + retgv = g_dbus_connection_call_finish(connection, res, &err); + if (err) { + retval = -ECOMM; + if (err->code == G_IO_ERROR_TIMED_OUT) + retval = -ETIMEDOUT; + + _E("Failed to finish async call, %s(%d)", err->message, err->code); + h->callback(h->state, retval, h->user_data); + + goto cleanup; + } + + g_variant_get(retgv, "(i)", &retval); + h->callback(h->state, retval, h->user_data); + +cleanup: + if (err) + g_clear_error(&err); + free(h); +} + +int device_power_change_state(uint64_t state, int timeout_sec, change_state_callback cb, void *user_data) +{ + GError *err = NULL; + GDBusConnection *connection; + struct change_state_handler *h = NULL; + + if (cb) { + h = (struct change_state_handler *) calloc(1, sizeof(struct change_state_handler)); + if (!h) { + _E("Failed to alloc change_state_handler."); + return DEVICE_ERROR_OPERATION_FAILED; + } + + h->state = state; + h->callback = cb; + h->user_data = user_data; + } + + if (timeout_sec <= 0) + timeout_sec = 10; + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err); + if (!connection) { + _E("Failed to get dbus connection, %s", err->message); + g_clear_error(&err); + return DEVICE_ERROR_OPERATION_FAILED; + } + + g_dbus_connection_call(connection, + DEVICED_BUS_NAME, + DEVICED_PATH_POWER, + DEVICED_INTERFACE_POWER, + "PowerChangeState", + g_variant_new("(t)", state), + NULL, + G_DBUS_CALL_FLAGS_NONE, + timeout_sec * 1000, + NULL, + change_state_async_callback, + h); + + return DEVICE_ERROR_NONE; +} -- 2.7.4