diff mbox

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

Message ID 1406803669-12439-3-git-send-email-alberto.milone@canonical.com
State Superseded
Headers show

Commit Message

Alberto Milone July 31, 2014, 10:47 a.m. UTC
This allows sharing Logind functions between s3.c and s4.c
---
 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     | 446 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 531 insertions(+), 14 deletions(-)
 create mode 100644 src/lib/src/fwts_pm_method.c

\ No newline at end of file

Comments

Keng-Yu Lin Aug. 1, 2014, 9:50 a.m. UTC | #1
Hi Alberto,
  This series of patches has no Signed-Of-By field. Probably you will
like to add it.

  This patch has unnecessary message "\ No newline at end of file" in the end.

  Otherwise they looks good to me.


On Thu, Jul 31, 2014 at 6:47 PM, Alberto Milone
<alberto.milone@canonical.com> wrote:
> This allows sharing Logind functions between s3.c and s4.c
> ---
>  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     | 446 +++++++++++++++++++++++++++++++++++++++
>  6 files changed, 531 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..2120669
> --- /dev/null
> +++ b/src/lib/src/fwts_pm_method.c
> @@ -0,0 +1,446 @@
> +/*
> + * 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");
> +}
> \ No newline at end of file
> --
> 1.9.1
>
>
> --
> fwts-devel mailing list
> fwts-devel@lists.ubuntu.com
> Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/fwts-devel
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..2120669
--- /dev/null
+++ b/src/lib/src/fwts_pm_method.c
@@ -0,0 +1,446 @@ 
+/*
+ * 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");
+}