From patchwork Thu Jul 31 10:47:44 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Milone X-Patchwork-Id: 375209 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) by ozlabs.org (Postfix) with ESMTP id 8CD9714009B; Thu, 31 Jul 2014 20:48:15 +1000 (EST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1XCnuA-0002qq-Fr; Thu, 31 Jul 2014 10:48:14 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1XCntu-0002o8-Rh for fwts-devel@lists.ubuntu.com; Thu, 31 Jul 2014 10:47:58 +0000 Received: from host25-93-dynamic.7-87-r.retail.telecomitalia.it ([87.7.93.25] helo=localhost.localdomain) by youngberry.canonical.com with esmtpsa (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1XCntu-00068k-3D for fwts-devel@lists.ubuntu.com; Thu, 31 Jul 2014 10:47:58 +0000 From: Alberto Milone To: fwts-devel@lists.ubuntu.com Subject: [PATCH 2/7] Store all the pm_method related functions in fwts_pm_method.c Date: Thu, 31 Jul 2014 12:47:44 +0200 Message-Id: <1406803669-12439-3-git-send-email-alberto.milone@canonical.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1406803669-12439-1-git-send-email-alberto.milone@canonical.com> References: <1406803669-12439-1-git-send-email-alberto.milone@canonical.com> X-BeenThere: fwts-devel@lists.ubuntu.com X-Mailman-Version: 2.1.14 Precedence: list List-Id: Firmware Test Suite Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: fwts-devel-bounces@lists.ubuntu.com Sender: fwts-devel-bounces@lists.ubuntu.com 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 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 +#include +#include +#include + +#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 +#include + +#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"); +}