4 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
24 #include <libsyscommon/libgdbus.h>
25 #include <libsyscommon/list.h>
26 #include <libsyscommon/file.h>
27 #include <device/power-internal.h>
29 #include "shared/devices.h"
30 #include "shared/log.h"
31 #include "shared/device-notifier.h"
33 #include "power-state-manager.h"
34 #include "sleep-wait.h"
36 #ifndef PROCESS_CHECK_TIMEOUT
37 #define PROCESS_CHECK_TIMEOUT 600000 /* milisecond, 10 minute */
40 static GList *lock_list;
50 static void print_lock_node(void)
53 struct lock_node *node;
55 _D("List of remaining CPU locks");
56 SYS_G_LIST_FOREACH(lock_list, elem, node)
57 _D(" pid=%d(%s), locktime=%s, timeout=%dms",
58 node->pid, node->comm, node->locktime, node->timeout);
61 /* check the lock_list is empty.
62 * if it is, then request wake unlock */
63 static void try_wake_unlock(void)
65 if (SYS_G_LIST_LENGTH(lock_list) == 0)
66 device_notify(DEVICE_NOTIFIER_REQUEST_WAKE_UNLOCK, NULL);
71 static void try_wake_lock(void)
73 device_notify(DEVICE_NOTIFIER_REQUEST_WAKE_LOCK, NULL);
76 /* remove given node from lock list.
77 * after removing the node, request wake unlock if possible */
78 static void remove_lock_node(struct lock_node *node)
84 g_source_remove(node->timer_id);
88 SYS_G_LIST_REMOVE(lock_list, node);
94 static gboolean lock_expired_cb(void *data)
96 struct lock_node *this = (struct lock_node *) data;
98 _D("Powerlock of pid=%d(%s) for %dms is expired", this->pid, this->comm, this->timeout);
99 remove_lock_node(this);
101 return G_SOURCE_REMOVE;
104 /* check existance of process for every PROCESS_CHECK_TIMEOUT
105 * if the process has requested infinite CPU lock */
106 static gboolean process_check_cb(void *data)
108 struct lock_node *this = (struct lock_node *) data;
110 if (kill(this->pid, 0) != 0) {
111 _D("pid=%d(%s) is not found, deviced release the CPU lock", this->pid, this->comm);
112 remove_lock_node(this);
113 return G_SOURCE_REMOVE;
116 return G_SOURCE_CONTINUE;
119 static GVariant *dbus_power_lock_cpu(GDBusConnection *conn,
120 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
121 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
127 struct lock_node *node;
130 struct tm current_tm;
133 /* lock timeout in milisecond */
134 g_variant_get(param, "(i)", &timeout);
140 pid = gdbus_connection_get_sender_pid(conn, sender);
141 if (pid == -1 || kill(pid, 0) == -1) {
142 _E("%d process does not exist, dbus ignored.", pid);
147 SYS_G_LIST_FOREACH(lock_list, elem, node) {
148 if (node->pid == pid)
157 node = calloc(1, sizeof(struct lock_node));
164 snprintf(commpath, sizeof(commpath), "/proc/%d/comm", pid);
165 retval = sys_get_str(commpath, node->comm, sizeof(node->comm));
167 snprintf(node->comm, sizeof(node->comm), "Unknown");
169 /* remove whitesapce */
170 sep = strpbrk(node->comm, " \n\t\r");
174 SYS_G_LIST_APPEND(lock_list, node);
177 /* set current time for logging */
178 current = time(NULL);
179 rettm = localtime_r(¤t, ¤t_tm);
181 snprintf(node->locktime, sizeof(node->locktime), "Unknown");
183 strftime(node->locktime, sizeof(node->locktime), "%Y-%m-%d %H:%M:%S", ¤t_tm);
185 node->timeout = timeout;
187 if (node->timer_id) {
188 g_source_remove(node->timer_id);
192 _D("pid=%d(%s) request CPU lock for %dms", pid, node->comm, timeout);
196 node->timer_id = g_timeout_add(timeout, lock_expired_cb, node);
197 else if (timeout == 0) /* endless CPU lock: add checker that monitors the process*/
198 node->timer_id = g_timeout_add(PROCESS_CHECK_TIMEOUT, process_check_cb, node);
201 return g_variant_new("(i)", ret);
204 static GVariant *dbus_power_unlock_cpu(GDBusConnection *conn,
205 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
206 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
211 struct lock_node *node;
213 pid = gdbus_connection_get_sender_pid(conn, sender);
214 if (pid == -1 || kill(pid, 0) == -1) {
215 _E("%d process does not exist, dbus ignored.", pid);
220 SYS_G_LIST_FOREACH(lock_list, elem, node) {
221 if (node->pid == pid)
226 _D("pid=%d(%s) release CPU lock", node->pid, node->comm);
227 remove_lock_node(node);
231 return g_variant_new("(i)", ret);
234 static GVariant *dbus_power_add_sleep_wait(GDBusConnection *conn,
235 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
236 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
241 pid = gdbus_connection_get_sender_pid(conn, sender);
242 if (pid == -1 || kill(pid, 0) == -1) {
243 _E("%d process does not exist, dbus ignored.", pid);
248 ret = add_sleep_wait(pid);
251 return g_variant_new("(i)", ret);
254 static GVariant *dbus_power_remove_sleep_wait(GDBusConnection *conn,
255 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
256 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
260 pid = gdbus_connection_get_sender_pid(conn, sender);
261 if (pid == -1 || kill(pid, 0) == -1) {
262 _E("%d process does not exist, dbus ignored.", pid);
266 remove_sleep_wait(pid);
269 return gdbus_new_g_variant_tuple();
272 static GVariant *dbus_power_confirm_sleep_wait(GDBusConnection *conn,
273 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
274 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
280 pid = gdbus_connection_get_sender_pid(conn, sender);
281 if (pid == -1 || kill(pid, 0) == -1) {
282 _E("%d process does not exist, dbus ignored.", pid);
287 g_variant_get(param, "(i)", &sleep_id);
289 ret = confirm_sleep_wait(pid, sleep_id);
292 return g_variant_new("(i)", ret);
295 static const dbus_method_s dbus_methods[] = {
296 { "LockCpu", "i", "i", dbus_power_lock_cpu },
297 { "UnlockCpu", NULL, "i", dbus_power_unlock_cpu },
298 { "AddSleepWait", NULL, "i", dbus_power_add_sleep_wait },
299 { "RemoveSleepWait", NULL, NULL, dbus_power_remove_sleep_wait },
300 { "ConfirmSleepWait", "i", "i", dbus_power_confirm_sleep_wait },
301 /* Add methods here */
304 static const dbus_interface_u dbus_interface = {
306 .name = DEVICED_INTERFACE_POWER,
307 .methods = dbus_methods,
308 .nr_methods = ARRAY_SIZE(dbus_methods),
311 void power_plugin_dbus_init(void *data)
315 retval = gdbus_add_object(NULL, DEVICED_PATH_POWER, &dbus_interface);
317 _E("Failed to init dbus method.");