[PATCH 2/7] Store all the pm_method related functions in fwts_pm_method.c

Alberto Milone alberto.milone at canonical.com
Fri Aug 1 10:20:35 UTC 2014


This allows sharing Logind functions between s3.c and s4.c

Signed-off-by: Alberto Milone <alberto.milone at canonical.com>
---
 src/lib/include/fwts_framework.h |   2 +-
 src/lib/include/fwts_pm_method.h |  74 ++++++-
 src/lib/include/fwts_types.h     |   7 +
 src/lib/src/Makefile.am          |   6 +-
 src/lib/src/fwts_framework.c     |  10 +-
 src/lib/src/fwts_pm_method.c     | 447 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 532 insertions(+), 14 deletions(-)
 create mode 100644 src/lib/src/fwts_pm_method.c

diff --git a/src/lib/include/fwts_framework.h b/src/lib/include/fwts_framework.h
index ca6d41e..02fc931 100644
--- a/src/lib/include/fwts_framework.h
+++ b/src/lib/include/fwts_framework.h
@@ -27,7 +27,7 @@
 #include "fwts_log.h"
 #include "fwts_list.h"
 #include "fwts_acpica_mode.h"
-#include "fwts_pm_method.h"
+#include "fwts_types.h"
 
 #define FWTS_FRAMEWORK_MAGIC	0x2af61aec
 
diff --git a/src/lib/include/fwts_pm_method.h b/src/lib/include/fwts_pm_method.h
index 8a69719..f2dade5 100644
--- a/src/lib/include/fwts_pm_method.h
+++ b/src/lib/include/fwts_pm_method.h
@@ -20,11 +20,73 @@
 #ifndef __FWTS_PM_METHOD_MODE_H__
 #define __FWTS_PM_METHOD_MODE_H__
 
-typedef enum {
-	logind,
-	pm_utils,
-	sysfs,
-	undefined
-} fwts_pm_method;
+#include <glib.h>
+#include <gio/gio.h>
+#include <time.h>
+#include <stdbool.h>
+
+#include "fwts_types.h"
+
+typedef struct
+{
+	fwts_framework *fw;
+	time_t t_start;
+	time_t t_end;
+	GDBusProxy *logind_proxy;
+	GDBusConnection *logind_connection;
+	GMainLoop *gmainloop;
+	char *action;
+	int  min_delay;
+} fwts_pm_method_vars;
+
+static inline void free_pm_method_vars(void *);
+
+#define _cleanup_free_pm_vars_ __attribute__((cleanup(free_pm_method_vars)))
+
+#define PM_SUSPEND_LOGIND		"Suspend"
+#define PM_SUSPEND_HYBRID_LOGIND	"HybridSleep"
+#define PM_HIBERNATE_LOGIND		"Hibernate"
+
+#define FWTS_SUSPEND		"FWTS_SUSPEND"
+#define FWTS_RESUME		"FWTS_RESUME"
+#define FWTS_HIBERNATE	"FWTS_HIBERNATE"
+#define FWTS_RESUME	"FWTS_RESUME"
+
+static inline void free_pm_method_vars(void *vars)
+{
+	fwts_pm_method_vars *var = *(void**)vars;
+
+	if (var) {
+		if (var->logind_proxy) {
+			g_object_unref(var->logind_proxy);
+			var->logind_proxy = NULL;
+		}
+		if (var->logind_connection) {
+			g_object_unref(var->logind_connection);
+			var->logind_connection = NULL;
+		}
+		if (var->gmainloop) {
+			g_main_loop_unref(var->gmainloop);
+			var->gmainloop = NULL;
+		}
+		if (var->action) {
+			free(var->action);
+			var->action = NULL;
+		}
+	}
+	free(var);
+	var = NULL;
+}
+
+int fwts_logind_init_proxy(fwts_pm_method_vars *fwts_settings);
+int fwts_logind_wait_for_resume_from_action(fwts_pm_method_vars *fwts_settings,	const char *action,	int minimum_delay);
+bool fwts_logind_can_suspend(fwts_pm_method_vars *fwts_settings);
+bool fwts_logind_can_hybrid_suspend(fwts_pm_method_vars *fwts_settings);
+bool fwts_logind_can_hibernate(fwts_pm_method_vars *fwts_settings);
+bool fwts_sysfs_can_suspend(const fwts_pm_method_vars *fwts_settings);
+bool fwts_sysfs_can_hybrid_suspend(const fwts_pm_method_vars *fwts_settings);
+bool fwts_sysfs_can_hibernate(const fwts_pm_method_vars *fwts_settings);
+int fwts_sysfs_do_suspend(const fwts_pm_method_vars *fwts_settings, bool s3_hybrid);
+int fwts_sysfs_do_hibernate(const fwts_pm_method_vars *fwts_settings);
 
 #endif
diff --git a/src/lib/include/fwts_types.h b/src/lib/include/fwts_types.h
index f4b67d0..6dc1cdb 100644
--- a/src/lib/include/fwts_types.h
+++ b/src/lib/include/fwts_types.h
@@ -45,4 +45,11 @@ typedef enum {
 	FWTS_BOOL_ERROR = -1
 } fwts_bool;
 
+typedef enum {
+	FWTS_PM_LOGIND,
+	FWTS_PM_PMUTILS,
+	FWTS_PM_SYSFS,
+	FWTS_PM_UNDEFINED
+} fwts_pm_method;
+
 #endif
diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
index 4391841..1385c68 100644
--- a/src/lib/src/Makefile.am
+++ b/src/lib/src/Makefile.am
@@ -2,6 +2,7 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib/include 		\
 	-I$(top_srcdir)/src/acpica/source/include	\
 	-I$(top_srcdir)/src/acpica/source/compiler	\
+	`pkg-config --cflags glib-2.0 gio-2.0` \
 	-DDATAROOTDIR=\"$(datarootdir)\"		\
 	-Wall -Werror -Wextra
 
@@ -11,7 +12,7 @@ libfwts_la_LDFLAGS = 			\
 	-lm -lpcre -lpthread 		\
 	-version-info 1:0:0 		\
 	-L$(top_builddir)/src/acpica/source/compiler \
-	-lfwtsiasl
+	-lfwtsiasl `pkg-config --libs glib-2.0 gio-2.0`
 
 libfwts_la_CPPFLAGS = $(AM_CPPFLAGS) -DACPI_DEBUG_OUTPUT
 
@@ -71,4 +72,5 @@ libfwts_la_SOURCES = 		\
 	fwts_text_list.c 	\
 	fwts_tty.c 		\
 	fwts_uefi.c 		\
-	fwts_wakealarm.c 
+	fwts_wakealarm.c \
+	fwts_pm_method.c
diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c
index 475e9a4..7229af5 100644
--- a/src/lib/src/fwts_framework.c
+++ b/src/lib/src/fwts_framework.c
@@ -1006,11 +1006,11 @@ static int fwts_framework_acpica_parse(fwts_framework *fw, const char *arg)
 static int fwts_framework_pm_method_parse(fwts_framework *fw, const char *arg)
 {
 	if (strcmp(arg, "logind") == 0)
-		fw->pm_method = logind;
+		fw->pm_method = FWTS_PM_LOGIND;
 	else if (strcmp(arg, "pm-utils") == 0)
-		fw->pm_method = pm_utils;
+		fw->pm_method = FWTS_PM_PMUTILS;
 	else if (strcmp(arg, "sysfs") == 0)
-		fw->pm_method = sysfs;
+		fw->pm_method = FWTS_PM_SYSFS;
 	else {
 		fprintf(stderr, "--pm-method only supports logind, pm-utils, and sysfs methods\n");
 		return FWTS_ERROR;
@@ -1256,8 +1256,8 @@ int fwts_framework_args(const int argc, char **argv)
 	if ((fw = (fwts_framework *)calloc(1, sizeof(fwts_framework))) == NULL)
 		return FWTS_ERROR;
 
-	/* Set the power method to undefined before we parse arguments */
-	fw->pm_method = undefined;
+	/* Set the power method to FWTS_PM_UNDEFINED before we parse arguments */
+	fw->pm_method = FWTS_PM_UNDEFINED;
 
 	ret = fwts_args_add_options(fwts_framework_options,
 		fwts_framework_options_handler, NULL);
diff --git a/src/lib/src/fwts_pm_method.c b/src/lib/src/fwts_pm_method.c
new file mode 100644
index 0000000..846aeff
--- /dev/null
+++ b/src/lib/src/fwts_pm_method.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2014 Canonical
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "fwts.h"
+#include "fwts_pm_method.h"
+
+/*
+ *  logind_do()
+ *  call Logind to perform an action
+ */
+static gboolean logind_do(gpointer data)
+{
+	GError *error = NULL;
+	GVariant *reply;
+	fwts_pm_method_vars *fwts_settings = (fwts_pm_method_vars *)data;
+
+	/* If the loop is not running, return TRUE so as to repeat the operation */
+	if (g_main_loop_is_running (fwts_settings->gmainloop)) {
+		fwts_log_info(fwts_settings->fw, "Requesting %s action\n", fwts_settings->action);
+		reply = g_dbus_proxy_call_sync(fwts_settings->logind_proxy,
+			fwts_settings->action,
+			g_variant_new ("(b)",
+					FALSE),
+			G_DBUS_CALL_FLAGS_NONE,
+			-1,
+			NULL,
+			&error);
+
+		if (reply != NULL) {
+			g_variant_unref(reply);
+		}
+		else {
+			fwts_log_error(fwts_settings->fw,
+				"Error from Logind: %s\n",
+				error->message);
+			g_error_free(error);
+		}
+
+		return FALSE;
+
+	}
+	fwts_log_info(fwts_settings->fw, "Glib loop not ready\n");
+	return TRUE;
+}
+
+/*
+ *  logind_signal_subscribe()
+ *  subscribe to a signal coming from Logind
+ */
+static guint logind_signal_subscribe(
+	GDBusConnection *connection,
+	const gchar *logind_signal,
+	GDBusSignalCallback callback,
+	gpointer user_data)
+{
+	return g_dbus_connection_signal_subscribe (connection,
+		"org.freedesktop.login1", /* sender */
+		"org.freedesktop.login1.Manager",
+		logind_signal,
+		"/org/freedesktop/login1",
+		NULL, /* arg0 */
+		G_DBUS_SIGNAL_FLAGS_NONE,
+		callback,
+		user_data,
+		NULL);
+}
+
+/*
+ *  logind_signal_unsubscribe()
+ *  unsubscribe from a signal coming from Logind
+ */
+static void logind_signal_unsubscribe(GDBusConnection *connection, guint subscription_id)
+{
+	g_dbus_connection_signal_unsubscribe(connection, subscription_id);
+}
+
+/*
+ *  logind_on_signal()
+ *  callback to handle suspend and resume events
+ */
+static void logind_on_signal(
+	GDBusConnection *connection,
+	const gchar *sender_name,
+	const gchar *object_path,
+	const gchar *interface_name,
+	const gchar *signal_name,
+	GVariant *parameters,
+	gpointer user_data)
+{
+	gboolean status, is_s3;
+	char buffer[50];
+	fwts_pm_method_vars *fwts_settings = (fwts_pm_method_vars *)user_data;
+
+	/* Prevent -Werror=unused-parameter from complaining */
+	FWTS_UNUSED(connection);
+	FWTS_UNUSED(sender_name);
+	FWTS_UNUSED(object_path);
+	FWTS_UNUSED(interface_name);
+	FWTS_UNUSED(signal_name);
+
+	is_s3 = (strcmp(fwts_settings->action, PM_SUSPEND_LOGIND) == 0 ||
+		strcmp(fwts_settings->action, PM_SUSPEND_HYBRID_LOGIND) == 0);
+
+	if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE ("(b)"))) {
+		fwts_log_error(fwts_settings->fw, "Suspend type %s\n",
+			g_variant_get_type_string(parameters));
+		return;
+	}
+	else {
+		g_variant_get(parameters, "(b)", &status);
+		fwts_log_info(fwts_settings->fw,
+			"Suspend status: %s\n",
+			status ? "true" : "false");
+
+		if (status) {
+			time(&(fwts_settings->t_start));
+			snprintf(buffer, sizeof(buffer), "Starting fwts %s\n", is_s3 ? "suspend" : "hibernate");
+			(void)fwts_klog_write(fwts_settings->fw, buffer);
+			snprintf(buffer, sizeof(buffer), "%s\n", fwts_settings->action);
+			(void)fwts_klog_write(fwts_settings->fw, buffer);
+		}
+		else {
+			time(&(fwts_settings->t_end));
+			(void)fwts_klog_write(fwts_settings->fw, FWTS_RESUME "\n");
+			(void)fwts_klog_write(fwts_settings->fw, "Finished fwts resume\n");
+			/*
+			 * Let's give the system some time to get back from S3
+			 * or Logind will refuse to suspend and shoot both events
+			 * without doing anything
+			 */
+			if (fwts_settings->min_delay < 3) {
+				fwts_log_info(fwts_settings->fw,
+					"Skipping the minimum delay (%d) and using a 3 seconds delay instead\n",
+					fwts_settings->min_delay);
+				sleep(3);
+			}
+			g_main_loop_quit(fwts_settings->gmainloop);
+		}
+	}
+}
+
+/*
+ *  logind_can_do_action()
+ *  test supported Logind actions that reply with a string
+ */
+static bool logind_can_do_action(fwts_pm_method_vars *fwts_settings, const char* action)
+{
+	GVariant *reply;
+	GError *error = NULL;
+	bool status = false;
+	gchar *response;
+
+	if (fwts_logind_init_proxy(fwts_settings) != 0)
+		return false;
+
+	reply = g_dbus_proxy_call_sync(fwts_settings->logind_proxy,
+		action,
+		NULL,
+		G_DBUS_CALL_FLAGS_NONE,
+		-1,
+		NULL,
+		&error);
+
+	if (reply != NULL) {
+		if (!g_variant_is_of_type(reply, G_VARIANT_TYPE ("(s)"))) {
+			fwts_log_error(fwts_settings->fw,
+				"Unexpected response to %s action: %s\n",
+				action,
+				g_variant_get_type_string (reply));
+
+			g_variant_unref(reply);
+			return status;
+		}
+
+		g_variant_get(reply, "(&s)", &response);
+		fwts_log_info(fwts_settings->fw, "Response to %s is %s\n",
+			action, response);
+
+		if (strcmp(response, "challenge") == 0) {
+			fwts_log_error(fwts_settings->fw,
+				"%s action available only after authorisation\n",
+				action);
+		} else if (strcmp(response, "yes") == 0) {
+			fwts_log_info(fwts_settings->fw,
+				"User allowed to execute the %s action\n",
+				action);
+			status = true;
+		} else if (strcmp(response, "no") == 0) {
+			fwts_log_error(fwts_settings->fw,
+				"User not allowed to execute the %s action\n",
+				action);
+		} else if (strcmp(response, "na") == 0) {
+			fwts_log_error(fwts_settings->fw,
+				"Hardware doesn't support %s action\n",
+				action);
+		}
+
+		g_variant_unref(reply);
+	}
+	else {
+		fwts_log_error(fwts_settings->fw,
+			"Invalid response from Logind on %s action\n",
+			action);
+		g_error_free(error);
+	}
+
+	return status;
+}
+
+/*
+ *  fwts_logind_init_proxy()
+ *  initialise the Dbus proxy for Logind Manager
+ */
+int fwts_logind_init_proxy(fwts_pm_method_vars *fwts_settings)
+{
+	int status = 0;
+
+	if (fwts_settings->logind_connection == NULL)
+		fwts_settings->logind_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
+
+	if (fwts_settings->logind_connection == NULL) {
+		status = 1;
+		fwts_log_error(fwts_settings->fw, "Cannot establish a connection to Dbus\n");
+		goto out;
+	}
+
+	if (fwts_settings->logind_proxy == NULL) {
+		fwts_settings->logind_proxy = g_dbus_proxy_new_sync(fwts_settings->logind_connection,
+			G_DBUS_PROXY_FLAGS_NONE,
+			NULL, "org.freedesktop.login1",
+			"/org/freedesktop/login1",
+			"org.freedesktop.login1.Manager",
+			NULL, NULL);
+	}
+
+	if (fwts_settings->logind_proxy == NULL) {
+		status = 1;
+		fwts_log_error(fwts_settings->fw,
+			"Cannot establish a connection to login1.Manager\n");
+		goto out;
+	}
+
+out:
+	return status;
+}
+
+/*
+ *  fwts_logind_wait_for_resume_from_action()
+ *  start Glib mainloop and listen to suspend/resume events coming from Logind
+ *  exit the loop and return the duration after an event.
+ *
+ *  "action" is one of the following actions supported by Logind:
+ *    "Suspend", "Hibernate", "HybridSleep"
+ *  "minimum_delay" is the minimum delay to use before the function returns
+ *    the function after resume
+ */
+int fwts_logind_wait_for_resume_from_action(
+	fwts_pm_method_vars *fwts_settings,
+	const char *action,
+	int minimum_delay)
+{
+	guint subscription_id = 0;
+	int duration = 0;
+
+	/* Check that the action is supported */
+	if (!(strcmp(action, PM_SUSPEND_LOGIND) == 0 ||
+		strcmp(action, PM_SUSPEND_HYBRID_LOGIND) == 0 ||
+		strcmp(action, PM_HIBERNATE_LOGIND) == 0)) {
+		fwts_log_error(fwts_settings->fw, "Unknown logind action: %s\n", action);
+		return 0;
+	}
+
+	/* Initialise the proxy */
+	if (fwts_logind_init_proxy(fwts_settings) != 0) {
+		fwts_log_error(fwts_settings->fw, "Failed to initialise logind proxy\n");
+		return 0;
+	}
+
+	/* Set the action to perform */
+	fwts_settings->action = strdup(action);
+	if (!fwts_settings->action) {
+		fwts_log_error(fwts_settings->fw, "Failed to initialise logind action\n");
+		return 0;
+	}
+
+	/* Set the minimum delay (this is needed for both S3 and S4) */
+	fwts_settings->min_delay = minimum_delay;
+
+	/* Subscribe to the signal that Logind sends on resume */
+	subscription_id = logind_signal_subscribe(fwts_settings->logind_connection,
+				"PrepareForSleep",
+				logind_on_signal,
+				fwts_settings);
+
+	/* Start the main loop */
+	fwts_settings->gmainloop = g_main_loop_new(NULL, FALSE);
+	if (fwts_settings->gmainloop) {
+		g_timeout_add(0.1, logind_do, fwts_settings);
+
+		g_main_loop_run(fwts_settings->gmainloop);
+		duration = (int)(fwts_settings->t_end - fwts_settings->t_start);
+
+		/* Optional, as it will be freed together with the struct */
+		g_main_loop_unref(fwts_settings->gmainloop);
+		fwts_settings->gmainloop = NULL;
+	}
+	else {
+		fwts_log_error(fwts_settings->fw, "Failed to start glib mainloop\n");
+	}
+
+	/* Unsubscribe from the signal */
+	logind_signal_unsubscribe(fwts_settings->logind_connection,
+		subscription_id);
+
+	return duration;
+}
+
+/*
+ *  fwts_logind_can_suspend()
+ *  return a boolean that states whether suspend is a supported action or not
+ */
+bool fwts_logind_can_suspend(fwts_pm_method_vars *fwts_settings)
+{
+	return logind_can_do_action(fwts_settings, "CanSuspend");
+}
+
+/*
+ *  fwts_logind_can_hybrid_suspend()
+ *  return a boolean that states whether hybrid suspend is a supported action or not
+ */
+bool fwts_logind_can_hybrid_suspend(fwts_pm_method_vars *fwts_settings)
+{
+	return logind_can_do_action(fwts_settings, "CanHybridSleep");
+}
+
+/*
+ *  fwts_logind_can_hibernate()
+ *  return a boolean that states whether hibernate is a supported action or not
+ */
+bool fwts_logind_can_hibernate(fwts_pm_method_vars *fwts_settings)
+{
+	return logind_can_do_action(fwts_settings, "CanHibernate");
+}
+
+/*
+ *  fwts_sysfs_can_suspend()
+ *  return a boolean that states whether suspend is a supported action or not
+ */
+bool fwts_sysfs_can_suspend(const fwts_pm_method_vars *fwts_settings)
+{
+	return fwts_file_first_line_contains_string(fwts_settings->fw,
+		"/sys/power/state",
+		"mem");
+}
+
+/*
+ *  fwts_sysfs_can_hybrid_suspend()
+ *  return a boolean that states whether hybrid suspend is a supported action or not
+ */
+bool fwts_sysfs_can_hybrid_suspend(const fwts_pm_method_vars *fwts_settings)
+{
+	bool status;
+
+	status = fwts_file_first_line_contains_string(fwts_settings->fw,
+		"/sys/power/state",
+		"disk");
+
+	if (!status)
+		return FALSE;
+
+	return fwts_file_first_line_contains_string(fwts_settings->fw,
+		"/sys/power/disk",
+		"suspend");
+}
+
+/*
+ *  fwts_sysfs_can_hibernate()
+ *  return a boolean that states whether hibernate is a supported action or not
+ */
+bool fwts_sysfs_can_hibernate(const fwts_pm_method_vars *fwts_settings)
+{
+	return fwts_file_first_line_contains_string(fwts_settings->fw,
+		"/sys/power/state",
+		"disk");
+}
+
+/*
+ *  fwts_sysfs_do_suspend()
+ *  enter either S3 or hybrid S3
+ *  return the exit status
+ */
+int fwts_sysfs_do_suspend(const fwts_pm_method_vars *fwts_settings, bool s3_hybrid)
+{
+	int status;
+
+	if (s3_hybrid) {
+		status = fwts_write_string_file(fwts_settings->fw,
+		"/sys/power/disk",
+		"suspend");
+
+		if (status != FWTS_OK)
+			return status;
+
+		status = fwts_write_string_file(fwts_settings->fw,
+			"/sys/power/state",
+			"disk");
+	}
+	else {
+		status = fwts_write_string_file(fwts_settings->fw,
+			"/sys/power/state",
+			"mem");
+	}
+
+	return status;
+}
+
+/*
+ *  fwts_sysfs_do_hibernate()
+ *  enter S4
+ *  return the exit status
+ */
+int fwts_sysfs_do_hibernate(const fwts_pm_method_vars *fwts_settings)
+{
+	return fwts_write_string_file(fwts_settings->fw,
+		"/sys/power/state",
+		"disk");
+}
+
-- 
1.9.1




More information about the fwts-devel mailing list