diff mbox

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

Message ID 1406888440-5692-3-git-send-email-alberto.milone@canonical.com
State Accepted
Headers show

Commit Message

Alberto Milone Aug. 1, 2014, 10:20 a.m. UTC
This allows sharing Logind functions between s3.c and s4.c

Signed-off-by: Alberto Milone <alberto.milone@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

Comments

Alex Hung Aug. 4, 2014, 10:01 a.m. UTC | #1
On 08/01/2014 06:20 PM, Alberto Milone wrote:
> This allows sharing Logind functions between s3.c and s4.c
>
> Signed-off-by: Alberto Milone <alberto.milone@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");
> +}
> +
>

Acked-by: Alex Hung <alex.hung@canonical.com>
Keng-Yu Lin Aug. 4, 2014, 2:15 p.m. UTC | #2
On Mon, Aug 4, 2014 at 6:01 PM, Alex Hung <alex.hung@canonical.com> wrote:
> On 08/01/2014 06:20 PM, Alberto Milone wrote:
>>
>> This allows sharing Logind functions between s3.c and s4.c
>>
>> Signed-off-by: Alberto Milone <alberto.milone@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");
>> +}
>> +
>>
>
> Acked-by: Alex Hung <alex.hung@canonical.com>
>

Acked-by: Keng-Yu Lin <kengyu@canonical.com>
diff mbox

Patch

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");
+}
+