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

Alex Hung alex.hung at canonical.com
Mon Aug 4 10:01:58 UTC 2014


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

Acked-by: Alex Hung <alex.hung at canonical.com>



More information about the fwts-devel mailing list