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.
*/
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
#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,
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,
[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,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
signal_callback,
- ud,
+ h,
signal_unsubscribed_callback);
return DEVICE_ERROR_NONE;
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;
+}