summaryrefslogtreecommitdiff
path: root/cpufreq/src/cpufreq-selector/cpufreq-selector-sysfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpufreq/src/cpufreq-selector/cpufreq-selector-sysfs.c')
-rw-r--r--cpufreq/src/cpufreq-selector/cpufreq-selector-sysfs.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/cpufreq/src/cpufreq-selector/cpufreq-selector-sysfs.c b/cpufreq/src/cpufreq-selector/cpufreq-selector-sysfs.c
new file mode 100644
index 00000000..ae790caf
--- /dev/null
+++ b/cpufreq/src/cpufreq-selector/cpufreq-selector-sysfs.c
@@ -0,0 +1,430 @@
+/*
+ * MATE CPUFreq Applet
+ * Copyright (C) 2004 Carlos Garcia Campos <[email protected]>
+ *
+ * This library 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 library 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 library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors : Carlos Garc�a Campos <[email protected]>
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "cpufreq-selector-sysfs.h"
+
+#define CPUFREQ_SELECTOR_SYSFS_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), CPUFREQ_TYPE_SELECTOR_SYSFS, CPUFreqSelectorSysfsPrivate))
+
+struct _CPUFreqSelectorSysfsPrivate {
+ GList *available_freqs;
+ GList *available_govs;
+};
+
+static void cpufreq_selector_sysfs_init (CPUFreqSelectorSysfs *selector);
+static void cpufreq_selector_sysfs_class_init (CPUFreqSelectorSysfsClass *klass);
+static void cpufreq_selector_sysfs_finalize (GObject *object);
+
+static gboolean cpufreq_selector_sysfs_set_frequency (CPUFreqSelector *selector,
+ guint frequency,
+ GError **error);
+static gboolean cpufreq_selector_sysfs_set_governor (CPUFreqSelector *selector,
+ const gchar *governor,
+ GError **error);
+
+#define CPUFREQ_SYSFS_BASE_PATH "/sys/devices/system/cpu/cpu%u/cpufreq/%s"
+
+G_DEFINE_TYPE (CPUFreqSelectorSysfs, cpufreq_selector_sysfs, CPUFREQ_TYPE_SELECTOR)
+
+static void
+cpufreq_selector_sysfs_init (CPUFreqSelectorSysfs *selector)
+{
+ selector->priv = CPUFREQ_SELECTOR_SYSFS_GET_PRIVATE (selector);
+
+ selector->priv->available_freqs = NULL;
+ selector->priv->available_govs = NULL;
+}
+
+static void
+cpufreq_selector_sysfs_class_init (CPUFreqSelectorSysfsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ CPUFreqSelectorClass *selector_class = CPUFREQ_SELECTOR_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (CPUFreqSelectorSysfsPrivate));
+
+ selector_class->set_frequency = cpufreq_selector_sysfs_set_frequency;
+ selector_class->set_governor = cpufreq_selector_sysfs_set_governor;
+
+ object_class->finalize = cpufreq_selector_sysfs_finalize;
+}
+
+static void
+cpufreq_selector_sysfs_finalize (GObject *object)
+{
+ CPUFreqSelectorSysfs *selector = CPUFREQ_SELECTOR_SYSFS (object);
+
+ if (selector->priv->available_freqs) {
+ g_list_foreach (selector->priv->available_freqs,
+ (GFunc) g_free,
+ NULL);
+ g_list_free (selector->priv->available_freqs);
+ selector->priv->available_freqs = NULL;
+ }
+
+ if (selector->priv->available_govs) {
+ g_list_foreach (selector->priv->available_govs,
+ (GFunc) g_free,
+ NULL);
+ g_list_free (selector->priv->available_govs);
+ selector->priv->available_govs = NULL;
+ }
+
+ G_OBJECT_CLASS (cpufreq_selector_sysfs_parent_class)->finalize (object);
+}
+
+CPUFreqSelector *
+cpufreq_selector_sysfs_new (guint cpu)
+{
+ CPUFreqSelector *selector;
+
+ selector = CPUFREQ_SELECTOR (g_object_new (CPUFREQ_TYPE_SELECTOR_SYSFS,
+ "cpu", cpu,
+ NULL));
+
+ return selector;
+}
+
+static gchar *
+cpufreq_sysfs_read (const gchar *path,
+ GError **error)
+{
+ gchar *buffer = NULL;
+
+ if (!g_file_get_contents (path, &buffer, NULL, error)) {
+ return NULL;
+ }
+
+ return g_strchomp (buffer);
+}
+
+static gboolean
+cpufreq_sysfs_write (const gchar *path,
+ const gchar *setting,
+ GError **error)
+{
+ FILE *fd;
+
+ fd = g_fopen (path, "w");
+
+ if (!fd) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "Failed to open '%s' for writing: "
+ "g_fopen() failed: %s",
+ path,
+ g_strerror (errno));
+
+ return FALSE;
+ }
+
+ if (g_fprintf (fd, "%s", setting) < 0) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "Failed to write '%s': "
+ "g_fprintf() failed: %s",
+ path,
+ g_strerror (errno));
+
+ fclose (fd);
+
+ return FALSE;
+ }
+
+ fclose (fd);
+
+ return TRUE;
+}
+
+static gint
+compare (gconstpointer a, gconstpointer b)
+{
+ gint aa, bb;
+
+ aa = atoi ((gchar *) a);
+ bb = atoi ((gchar *) b);
+
+ if (aa == bb)
+ return 0;
+ else if (aa > bb)
+ return -1;
+ else
+ return 1;
+}
+
+static GList *
+cpufreq_selector_sysfs_get_freqs (CPUFreqSelectorSysfs *selector)
+{
+ gchar *buffer;
+ GList *list = NULL;
+ gchar **frequencies = NULL;
+ gint i;
+ gchar *path;
+ guint cpu;
+ GError *error = NULL;
+
+ g_object_get (G_OBJECT (selector),
+ "cpu", &cpu,
+ NULL);
+
+ path = g_strdup_printf (CPUFREQ_SYSFS_BASE_PATH, cpu,
+ "scaling_available_frequencies");
+
+ buffer = cpufreq_sysfs_read (path, &error);
+ if (!buffer) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+
+ g_free (path);
+
+ return NULL;
+ }
+
+ g_free (path);
+
+ frequencies = g_strsplit (buffer, " ", -1);
+
+ i = 0;
+ while (frequencies[i]) {
+ if (!g_list_find_custom (list, frequencies[i], compare))
+ list = g_list_prepend (list, g_strdup (frequencies[i]));
+ i++;
+ }
+
+ g_strfreev (frequencies);
+ g_free (buffer);
+
+ return g_list_sort (list, compare);
+}
+
+static const gchar *
+cpufreq_selector_sysfs_get_valid_frequency (CPUFreqSelectorSysfs *selector,
+ guint frequency)
+{
+ GList *list = NULL;
+ GList *l;
+ gint dist = G_MAXINT;
+ const gchar *retval = NULL;
+
+ if (!selector->priv->available_freqs) {
+ list = cpufreq_selector_sysfs_get_freqs (selector);
+ selector->priv->available_freqs = list;
+ } else {
+ list = selector->priv->available_freqs;
+ }
+
+ if (!list)
+ return NULL;
+
+ for (l = list; l && l->data; l = g_list_next (l)) {
+ const gchar *freq;
+ guint f;
+ guint current_dist;
+
+ freq = (const gchar *) l->data;
+ f = atoi (freq);
+
+ if (f == frequency)
+ return freq;
+
+ current_dist = abs (frequency - f);
+ if (current_dist < dist) {
+ dist = current_dist;
+ retval = freq;
+ }
+ }
+
+ return retval;
+}
+
+static gboolean
+cpufreq_selector_sysfs_set_frequency (CPUFreqSelector *selector,
+ guint frequency,
+ GError **error)
+{
+ gchar *governor;
+ gchar *path;
+ const gchar *frequency_text;
+ guint cpu;
+
+ g_object_get (G_OBJECT (selector),
+ "cpu", &cpu,
+ NULL);
+
+ path = g_strdup_printf (CPUFREQ_SYSFS_BASE_PATH, cpu,
+ "scaling_governor");
+
+ governor = cpufreq_sysfs_read (path, error);
+ g_free (path);
+
+ if (!governor)
+ return FALSE;
+
+ if (g_ascii_strcasecmp (governor, "userspace") != 0) {
+ if (!cpufreq_selector_sysfs_set_governor (selector,
+ "userspace",
+ error)) {
+ g_free (governor);
+
+ return FALSE;
+ }
+ }
+
+ g_free (governor);
+
+ frequency_text =
+ cpufreq_selector_sysfs_get_valid_frequency (CPUFREQ_SELECTOR_SYSFS (selector),
+ frequency);
+ if (!frequency_text) {
+ g_set_error (error,
+ CPUFREQ_SELECTOR_ERROR,
+ SELECTOR_ERROR_SET_FREQUENCY,
+ "Cannot set frequency '%d'",
+ frequency);
+
+ return FALSE;
+ }
+
+ path = g_strdup_printf (CPUFREQ_SYSFS_BASE_PATH, cpu,
+ "scaling_setspeed");
+ if (!cpufreq_sysfs_write (path, frequency_text, error)) {
+ g_free (path);
+
+ return FALSE;
+ }
+
+ g_free (path);
+
+ return TRUE;
+}
+
+static GList *
+cpufreq_selector_sysfs_get_govs (CPUFreqSelectorSysfs *selector)
+{
+ gchar *buffer;
+ GList *list = NULL;
+ gchar **governors = NULL;
+ gint i;
+ gchar *path;
+ guint cpu;
+ GError *error = NULL;
+
+ g_object_get (G_OBJECT (selector),
+ "cpu", &cpu,
+ NULL);
+
+ path = g_strdup_printf (CPUFREQ_SYSFS_BASE_PATH, cpu,
+ "scaling_available_governors");
+
+ buffer = cpufreq_sysfs_read (path, &error);
+ if (!buffer) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+
+ g_free (path);
+
+ return NULL;
+ }
+
+ g_free (path);
+
+ governors = g_strsplit (buffer, " ", -1);
+
+ i = 0;
+ while (governors[i]) {
+ list = g_list_prepend (list, g_strdup (governors[i]));
+ i++;
+ }
+
+ g_strfreev (governors);
+ g_free (buffer);
+
+ return list;
+}
+
+static gboolean
+cpufreq_selector_sysfs_validate_governor (CPUFreqSelectorSysfs *selector,
+ const gchar *governor)
+{
+ GList *list = NULL;
+
+ if (!selector->priv->available_govs) {
+ list = cpufreq_selector_sysfs_get_govs (selector);
+ selector->priv->available_govs = list;
+ } else {
+ list = selector->priv->available_govs;
+ }
+
+ if (!list)
+ return FALSE;
+
+ list = g_list_find_custom (selector->priv->available_govs,
+ governor,
+ (GCompareFunc) g_ascii_strcasecmp);
+
+ return (list != NULL);
+}
+
+static gboolean
+cpufreq_selector_sysfs_set_governor (CPUFreqSelector *selector,
+ const gchar *governor,
+ GError **error)
+{
+ CPUFreqSelectorSysfs *selector_sysfs;
+ gchar *path;
+ guint cpu;
+
+ selector_sysfs = CPUFREQ_SELECTOR_SYSFS (selector);
+
+ if (!cpufreq_selector_sysfs_validate_governor (selector_sysfs, governor)) {
+ g_set_error (error,
+ CPUFREQ_SELECTOR_ERROR,
+ SELECTOR_ERROR_INVALID_GOVERNOR,
+ "Invalid governor '%s'",
+ governor);
+
+ return FALSE;
+ }
+
+ g_object_get (G_OBJECT (selector),
+ "cpu", &cpu,
+ NULL);
+
+ path = g_strdup_printf (CPUFREQ_SYSFS_BASE_PATH, cpu,
+ "scaling_governor");
+
+ if (!cpufreq_sysfs_write (path, governor, error)) {
+ g_free (path);
+
+ return FALSE;
+ }
+
+ g_free (path);
+
+ return TRUE;
+}