summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHolger Macht <hmacht@suse.de>2006-08-23 21:37:45 -0400
committerDavid Zeuthen <davidz@redhat.com>2006-08-23 21:37:45 -0400
commit18f52da6185f14b8b2d806cf17034bbda7593eee (patch)
treea6289642e3dff658e9318f68c5cdd8c265735f3e
parentbe78af22b72006a2209fb09f077a7421b5c637ab (diff)
add cpu frequency scaling support to hal
The following patches add CPU frequency capabilities to HAL via an addon. This was already discussed in another thread [1]. Addon-cpufreq supports all kernel governors and also implements a userspace controlling mechanism. Furthermore, it is supposed to abstract all the different settings you can make for the different governors. It is unique because it gives you a fine grained control over dynamic scaling mechanisms via a DBus interface higher level applications like gnome-power-manager or kpowersave can make use of. [1] http://lists.freedesktop.org/archives/hal/2006-July/005545.html --- Patch adding the cpufreq addon itself. This version implements the following new things: - specific DBus errors on failure (exceptions) - PolicyKit integration - add DBus method to get a list of all available governors Signed-off-by: Holger Macht <hmacht@suse.de> --- Patch adding the privilege descriptor for the hal-power-cpufreq privilege. Signed-off-by: Holger Macht <hmacht@suse.de> --- Patch adding the documentation for all CPUFreq methods on the org.freedesktop.Hal.Device.SystemPowerManagement interface to the Hal specification. Signed-off-by: Holger Macht <hmacht@suse.de> ---
-rw-r--r--doc/api/tmpl/hal-unused.sgml24
-rw-r--r--doc/api/tmpl/logger.sgml16
-rw-r--r--doc/api/tmpl/util.sgml26
-rw-r--r--doc/spec/hal-spec-properties.xml212
-rw-r--r--fdi/policy/10osvendor/10-power-mgmt-policy.fdi2
-rw-r--r--hald/linux/addons/Makefile.am7
-rw-r--r--hald/linux/addons/addon-cpufreq-userspace.c525
-rw-r--r--hald/linux/addons/addon-cpufreq-userspace.h61
-rw-r--r--hald/linux/addons/addon-cpufreq.c1214
-rw-r--r--hald/linux/addons/addon-cpufreq.h72
-rw-r--r--privileges/Makefile.am3
-rw-r--r--privileges/hal-power-cpufreq.privilege12
12 files changed, 2146 insertions, 28 deletions
diff --git a/doc/api/tmpl/hal-unused.sgml b/doc/api/tmpl/hal-unused.sgml
index e4bf5ce5..0f22ab22 100644
--- a/doc/api/tmpl/hal-unused.sgml
+++ b/doc/api/tmpl/hal-unused.sgml
@@ -286,6 +286,30 @@ logging
main
+<!-- ##### SECTION ./tmpl/shared.sgml:Long_Description ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SECTION ./tmpl/shared.sgml:See_Also ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SECTION ./tmpl/shared.sgml:Short_Description ##### -->
+
+
+
+<!-- ##### SECTION ./tmpl/shared.sgml:Stability_Level ##### -->
+
+
+
+<!-- ##### SECTION ./tmpl/shared.sgml:Title ##### -->
+shared
+
+
<!-- ##### SECTION ./tmpl/sysfs.sgml:Long_Description ##### -->
<para>
diff --git a/doc/api/tmpl/logger.sgml b/doc/api/tmpl/logger.sgml
index 2878b232..ee46b8c6 100644
--- a/doc/api/tmpl/logger.sgml
+++ b/doc/api/tmpl/logger.sgml
@@ -37,6 +37,15 @@ logger
@Varargs:
+<!-- ##### FUNCTION logger_forward_debug ##### -->
+<para>
+
+</para>
+
+@format:
+@Varargs:
+
+
<!-- ##### FUNCTION logger_enable ##### -->
<para>
@@ -65,6 +74,13 @@ logger
+<!-- ##### FUNCTION setup_logger ##### -->
+<para>
+
+</para>
+
+
+
<!-- ##### MACRO HAL_TRACE ##### -->
<para>
diff --git a/doc/api/tmpl/util.sgml b/doc/api/tmpl/util.sgml
index d2e23210..a61ab350 100644
--- a/doc/api/tmpl/util.sgml
+++ b/doc/api/tmpl/util.sgml
@@ -38,32 +38,6 @@ util
-<!-- ##### FUNCTION util_compute_time_remaining ##### -->
-<para>
-
-</para>
-
-@id:
-@chargeRate:
-@chargeLevel:
-@chargeLastFull:
-@isDischarging:
-@isCharging:
-@guessChargeRate:
-@Returns:
-
-
-<!-- ##### FUNCTION util_compute_percentage_charge ##### -->
-<para>
-
-</para>
-
-@id:
-@chargeLevel:
-@chargeLastFull:
-@Returns:
-
-
<!-- ##### FUNCTION hal_util_remove_trailing_slash ##### -->
<para>
diff --git a/doc/spec/hal-spec-properties.xml b/doc/spec/hal-spec-properties.xml
index 7c1eddda..b81aa9e2 100644
--- a/doc/spec/hal-spec-properties.xml
+++ b/doc/spec/hal-spec-properties.xml
@@ -5491,6 +5491,218 @@
</tbody>
</tgroup>
</informaltable>
+ <para>
+ The following methods exist on the interface
+ <literal>org.freedesktop.Hal.Device.CPUFreq</literal>.
+ </para>
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Method (parameter types)</entry>
+ <entry>Parameters</entry>
+ <entry>Mandatory</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>SetCPUFreqGovernor</literal> (string)
+ </entry>
+ <entry>
+ The name of the governor to set. Get a list of available governors
+ with the GetCPUFreqAvailableGovernors method.
+ </entry>
+ <entry>No</entry>
+ <entry>
+ Sets a CPU frequency scaling governor for all CPUFreq
+ interfaces the kernel provides. If the userspace governor
+ is set, this interface also contains a proper scaling
+ mechanism. The default performance is set to 50.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>SetCPUFreqPerformance</literal> (integer)
+ </entry>
+ <entry>
+ The performance between 1 and 100 to set in dynamic scaling modes.
+ </entry>
+ <entry>No</entry>
+ <entry>
+ Sets the performance of the dynamic scaling mechanism. This method
+ summarizes and abstracts all the different settings which can be taken
+ for dynamic frequency adjustments, like at which load to switch up
+ frequency or how many steps the mechanism should traverse until
+ reaching the maximum frequency. The higher the value, the more
+ performance you get. Respectively, the higher the value, the sooner
+ and the more often the frequency is switched up.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>SetCPUFreqConsiderNice</literal> (boolean)
+ </entry>
+ <entry>
+ Whether or not niced processes should be considered on CPU
+ load calculation.
+ </entry>
+ <entry>No</entry>
+ <entry>
+ Whether or not niced processes should be considered on CPU
+ load calculation. If niced processes are considered, they can cause a
+ frequency increment although their absolute load percentage wouldn't
+ trigger the scaling mechanism to switch up the frequency. The default
+ setting is 'false'.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>GetCPUFreqGovernor</literal> (void)
+ </entry>
+ <entry>
+ </entry>
+ <entry>No</entry>
+ <entry>
+ Get the current active governor for all CPU frequency interfaces (string).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>GetCPUFreqPerformance</literal> (void)
+ </entry>
+ <entry>
+ </entry>
+ <entry>No</entry>
+ <entry>
+ Get the current active performance setting if a dynamic scaling
+ mechanism is in use (integer between 1 and 100).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>GetCPUFreqConsiderNice</literal> (void)
+ </entry>
+ <entry>
+ </entry>
+ <entry>No</entry>
+ <entry>
+ Returns whether niced processed are considered during CPU load
+ calculation or not (returns boolean).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>GetCPUFreqAvailableGovernors</literal> (void)
+ </entry>
+ <entry>
+ </entry>
+ <entry>No</entry>
+ <entry>
+ Returns a list of strings of all available governors which
+ could be set with the SetCPUFreqGovernor method.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <para>
+ The following errors maybe raised on the interface
+ <literal>org.freedesktop.Hal.Device.CPUFreq</literal>.
+ </para>
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Error</entry>
+ <entry>Description</entry>
+ <entry>Detail field</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>GeneralError</literal>
+ </entry>
+ <entry>
+ A general error occured.
+ </entry>
+ <entry>
+ The exact error.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>UnknownMethod</literal>
+ </entry>
+ <entry>
+ The executed method doesn't exist.
+ </entry>
+ <entry>
+ The method which was tried to be executed.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>UnknownGovernor</literal>
+ </entry>
+ <entry>
+ The governor which was tried to be set doesn't exist.
+ </entry>
+ <entry>
+ The governor which was tried be to set.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>InvalidMessage</literal>
+ </entry>
+ <entry>
+ The message that was sent to the interface is invalid.
+ For instance, a parameter is missing.
+ </entry>
+ <entry>
+ A DBus error message.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>PermissionDenied</literal>
+ </entry>
+ <entry>
+ The caller doesn't have the privilege to execute this
+ method.
+ </entry>
+ <entry>
+ The privilege the caller needs to execute the method.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>NoSuitableGovernor</literal>
+ </entry>
+ <entry>
+ The method executed doesn't exist for the current active governor.
+ </entry>
+ <entry>
+ The method which was tried to be executed.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>GovernorInitFailed</literal>
+ </entry>
+ <entry>
+ The initialization of the governor failed.
+ </entry>
+ <entry>
+ The reason for the failure.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
</sect2>
<sect2 id="device-properties-tape">
<title>
diff --git a/fdi/policy/10osvendor/10-power-mgmt-policy.fdi b/fdi/policy/10osvendor/10-power-mgmt-policy.fdi
index 2c2705ab..c0b48451 100644
--- a/fdi/policy/10osvendor/10-power-mgmt-policy.fdi
+++ b/fdi/policy/10osvendor/10-power-mgmt-policy.fdi
@@ -26,6 +26,8 @@
<match key="info.udi" string="/org/freedesktop/Hal/devices/computer">
<append key="info.interfaces" type="strlist">org.freedesktop.Hal.Device.SystemPowerManagement</append>
+ <append key="info.addons" type="strlist">hald-addon-cpufreq</append>
+
<append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_names" type="strlist">Suspend</append>
<append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_signatures" type="strlist">i</append>
<append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_argnames" type="strlist">num_seconds_to_sleep</append>
diff --git a/hald/linux/addons/Makefile.am b/hald/linux/addons/Makefile.am
index 48226afc..63e2d603 100644
--- a/hald/linux/addons/Makefile.am
+++ b/hald/linux/addons/Makefile.am
@@ -15,7 +15,8 @@ libexec_PROGRAMS = \
hald-addon-hid-ups \
hald-addon-keyboard \
hald-addon-pmu \
- hald-addon-storage
+ hald-addon-storage \
+ hald-addon-cpufreq
if HAVE_LIBPCI
libexec_PROGRAMS += hald-addon-macbookpro-backlight
@@ -25,6 +26,10 @@ libexec_PROGRAMS += hald-addon-usb-csr
endif
endif
+hald_addon_cpufreq_SOURCES = addon-cpufreq.c addon-cpufreq.h addon-cpufreq-userspace.h \
+ addon-cpufreq-userspace.c ../../logger.c
+hald_addon_cpufreq_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@ @POLKIT_LIBS@
+
hald_addon_acpi_SOURCES = addon-acpi.c ../../logger.c ../../util_helper.c
hald_addon_acpi_LDADD = $(top_builddir)/libhal/libhal.la
diff --git a/hald/linux/addons/addon-cpufreq-userspace.c b/hald/linux/addons/addon-cpufreq-userspace.c
new file mode 100644
index 00000000..953d46af
--- /dev/null
+++ b/hald/linux/addons/addon-cpufreq-userspace.c
@@ -0,0 +1,525 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq-userspace.c *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <hmacht@suse.de> *
+ * Speed adjustments based on code by *
+ * Thomas Renninger <trenn@suse.de> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "addon-cpufreq.h"
+#include "addon-cpufreq-userspace.h"
+#include "../../logger.h"
+
+/** at which load difference (in percent) we should immediately switch to
+ * the maximum possible frequency */
+#define JUMP_CPUFREQ_LIMIT_MIN 20
+/** the load difference at which we jump up to the maximum freq
+ * immediately is calculated by the UP_THRESHOLD multiplied with this
+ * relation value */
+#define THRESHOLD_JUMP_LIMIT_RELATION 0.625
+/** how many frequency steps we should consider */
+#define HYSTERESIS 5
+#define DEFAULT_CONSIDER_NICE FALSE
+#define PROC_STAT_FILE "/proc/stat"
+
+const char SYSFS_SCALING_SETSPEED_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_setspeed";
+
+const char SYSFS_SCALING_AVAILABLE_FREQS_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_available_frequencies";
+
+/** shortcut for g_array_index */
+#define g_a_i(a,i) g_array_index(a, unsigned, i)
+
+struct userspace_config {
+ int up_threshold;
+ int cpu_high_limit;
+ int consider_nice;
+ int performance;
+};
+
+static struct userspace_config config = { UP_THRESHOLD_MAX,
+ JUMP_CPUFREQ_LIMIT_MIN,
+ DEFAULT_CONSIDER_NICE,
+ DEFAULT_PERFORMANCE };
+
+/********************* CPU load calculation *********************/
+struct cpuload_data {
+ int num_cpus;
+ int *load;
+ unsigned long *last_total_time;
+ unsigned long *last_working_time;
+};
+static struct cpuload_data cpuload = { -1,
+ NULL,
+ NULL,
+ NULL };
+
+/** frees data needed for CPU load calculation */
+void free_cpu_load_data(void)
+{
+ if (cpuload.num_cpus != -1) {
+ free(cpuload.last_working_time);
+ free(cpuload.last_total_time);
+ free(cpuload.load);
+ cpuload.num_cpus = -1;
+ cpuload.load = NULL;
+ cpuload.last_total_time = NULL;
+ cpuload.last_working_time = NULL;
+ }
+}
+
+/** calculates current cpu load and stores it in cpuload_data object */
+static int calc_cpu_load(const int consider_nice)
+{
+ unsigned long total_elapsed, working_elapsed;
+ char what[32];
+ unsigned long user_time, nice_time, system_time, idle_time;
+ unsigned long total_time, iowait_time;
+ unsigned scan_ret;
+ char line[256];
+ char cpu_string[7];
+ FILE *fp;
+ int new_num_cpus;
+
+ new_num_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (new_num_cpus == -1 || new_num_cpus != cpuload.num_cpus) {
+ free_cpu_load_data();
+ cpuload.num_cpus = new_num_cpus;
+ if (cpuload.num_cpus <= 0) {
+ errno = ENODEV;
+ return -20;
+ }
+
+ cpuload.last_total_time = (unsigned long *)calloc(cpuload.num_cpus + 1,
+ sizeof(unsigned long));
+ cpuload.last_working_time = (unsigned long *)calloc(cpuload.num_cpus + 1,
+ sizeof(unsigned long));
+ cpuload.load = (int *)calloc(cpuload.num_cpus + 1, sizeof(int));
+ }
+
+ if ((fp = fopen(PROC_STAT_FILE, "r")) == NULL) {
+ HAL_DEBUG(("Could not open %s: %s", PROC_STAT_FILE, strerror(errno)));
+ return -1;
+ }
+
+ /* start with the first line, "overall" cpu load */
+ /* if cpuload.num_cpus == 1, we do not need to evaluate "overall" and "per-cpu" load */
+ sprintf(cpu_string, "cpu ");
+ int i;
+ for (i = 0; i <= cpuload.num_cpus - (cpuload.num_cpus == 1); i++) {
+
+ if (fgets(line,255,fp) == NULL) {
+ HAL_WARNING(("%s too short (%s)", PROC_STAT_FILE, cpu_string));
+ fclose(fp);
+ return -1;
+ }
+ if (memcmp(line, cpu_string, strlen(cpu_string))) {
+ HAL_WARNING(("no '%s' string in %s line %d",
+ cpu_string, PROC_STAT_FILE, i));
+ fclose(fp);
+ return -1;
+ }
+ /* initialized, since it is simply not there in 2.4 */
+ iowait_time = 0;
+ scan_ret = sscanf(line, "%s %lu %lu %lu %lu %lu", what, &user_time, &nice_time,
+ &system_time, &idle_time, &iowait_time);
+ if (scan_ret < 5) {
+ HAL_WARNING(("only %d values in %s. Please report.",
+ scan_ret, PROC_STAT_FILE));
+ fclose(fp);
+ return -1;
+ }
+
+ unsigned long working_time;
+ if (consider_nice) {
+ working_time = user_time + system_time + nice_time;
+ idle_time += iowait_time;
+ } else {
+ working_time = user_time + system_time;
+ idle_time += (nice_time + iowait_time);
+ }
+ total_time = working_time + idle_time;
+ total_elapsed = total_time - cpuload.last_total_time[i];
+ working_elapsed = working_time - cpuload.last_working_time[i];
+ cpuload.last_working_time[i] = working_time;
+ cpuload.last_total_time[i] = total_time;
+
+ if (!total_elapsed) {
+ /* not once per CPU, only once per check. */
+ if (!i)
+ HAL_DEBUG(("%s not updated yet, poll slower.", PROC_STAT_FILE));
+ } else
+ cpuload.load[i] = working_elapsed * 100 / total_elapsed;
+
+ sprintf(cpu_string, "cpu%d ", i);
+ }
+ /* shortcut for UP systems */
+ if (cpuload.num_cpus == 1)
+ cpuload.load[1] = cpuload.load[0];
+
+ fclose(fp);
+
+ return 0;
+}
+
+/** returns current cpuload which has been caluclated before */
+static int get_cpu_load(const int cpu_id)
+{
+ if (cpu_id < -1) {
+ errno = EINVAL;
+ return -10;
+ }
+
+ if (cpuload.load == NULL) {
+ HAL_WARNING(("cpuload.load uninitialized"));
+ errno = EFAULT;
+ return -40;
+ }
+
+ if (cpu_id >= cpuload.num_cpus) {
+ errno = ENODEV;
+ return -30;
+ }
+
+ return cpuload.load[cpu_id + 1];
+}
+/********************* CPU load end *********************/
+
+/********************* userspace interface *********************/
+static gboolean write_speed(unsigned kHz, int cpu_id)
+{
+ char *speed_file = NULL;
+
+ if (!cpu_online(cpu_id))
+ return FALSE;
+
+ speed_file = g_strdup_printf(SYSFS_SCALING_SETSPEED_FILE, cpu_id);
+ if(!write_line(speed_file, "%u", kHz)){
+ HAL_WARNING(("Could not set speed to: %u kHz; %s", kHz, strerror(errno)));
+ g_free(speed_file);
+ return FALSE;
+ }
+ g_free(speed_file);
+ HAL_DEBUG(("Speed set to: %uKHz for CPU %d", kHz, cpu_id));
+
+ return TRUE;
+}
+
+static void reinit_speed(struct userspace_interface *iface, int current_speed)
+{
+ if (!cpu_online(iface->base_cpu))
+ return;
+
+ write_speed(g_a_i(iface->speeds_kHz, current_speed), iface->base_cpu);
+ HAL_DEBUG(("forced speed to %d kHz", g_a_i(iface->speeds_kHz, current_speed)));
+}
+
+/** @brief set a speed with traversing all intermediary speeds */
+static int set_speed(struct userspace_interface *iface, int target_speed)
+{
+ int delta;
+ int current_speed = iface->current_speed;
+
+ if (current_speed == target_speed)
+ return -1;
+
+ if (current_speed > target_speed)
+ delta = -1;
+ else
+ delta = 1;
+
+ do {
+ current_speed += delta;
+ write_speed(g_a_i(iface->speeds_kHz, current_speed), iface->base_cpu);
+ } while (current_speed != target_speed);
+
+ return current_speed;
+}
+
+/** @brief set speed to the next higher supported value
+ *
+ * @return integer with result of increase speed
+ * @retval 0 if maximum is already reached
+ * @retval 1 if new speed could be set
+ * @retval -1 if mode is not userspace
+ */
+static int increase_speed(struct userspace_interface *iface)
+{
+ int new_speed = iface->current_speed;
+ int current_speed = iface->current_speed;
+
+ if (current_speed != 0)
+ new_speed--;
+ else
+ return current_speed;
+ if (current_speed != new_speed) {
+ HAL_DEBUG(("current: %u new: %u", g_a_i(iface->speeds_kHz, current_speed),
+ g_a_i(iface->speeds_kHz, new_speed)));
+ set_speed(iface, new_speed);
+ }
+ return new_speed;
+}
+
+/** @brief set speed to the next lower supported value
+ *
+ * @return integer with result of increase speed
+ * @retval 0 if maximum is already reached
+ * @retval 1 if new speed could be set
+ * @retval -1 if mode is not userspace
+ */
+static int decrease_speed(struct userspace_interface *iface)
+{
+ int new_speed = iface->current_speed;
+ int current_speed = iface->current_speed;
+
+
+ if (g_a_i(iface->speeds_kHz, new_speed + 1) != 0)
+ new_speed++;
+ else
+ return current_speed;
+ if (current_speed != new_speed) {
+ HAL_DEBUG(("current: %u new: %u", g_a_i(iface->speeds_kHz, current_speed),
+ g_a_i(iface->speeds_kHz, new_speed)));
+ set_speed(iface, new_speed);
+ }
+ return new_speed;
+}
+
+/** increases and decreases speeds */
+static gboolean adjust_speed(struct userspace_interface *iface)
+{
+ GSList *cpus = (GSList*)iface->cpus;
+ GSList *it = NULL;
+ int ret = 0;
+ int cpu_load = 0;
+
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ HAL_DEBUG(("checking cpu %d: cpu_core: %d",
+ GPOINTER_TO_INT(it->data), GPOINTER_TO_INT(it->data)));
+ if (get_cpu_load(GPOINTER_TO_INT(it->data)) > cpu_load)
+ cpu_load = get_cpu_load(GPOINTER_TO_INT(it->data));
+ }
+
+ HAL_DEBUG(("cpu_max: %d cpu_high_limit: %d consider_nice: %d",
+ config.up_threshold, config.cpu_high_limit,
+ config.consider_nice));
+ HAL_DEBUG(("Current: %u; current speed: %u MHz",
+ iface->current_speed, g_a_i(iface->speeds_kHz, iface->current_speed)));
+ HAL_DEBUG(("CPU load: %d, Previous CPU load %d, cpu_load diff: %d, last_step: %d, demotion: %u",
+ cpu_load, iface->prev_cpu_load, cpu_load - iface->prev_cpu_load, iface->last_step,
+ g_a_i(iface->demotion, iface->current_speed)));
+
+ /* directly increase speed to maximum if cpu load jumped */
+ if (config.cpu_high_limit &&
+ (cpu_load - iface->prev_cpu_load) > config.cpu_high_limit) {
+ if (iface->current_speed != 0) {
+ set_speed(iface, 0);
+ iface->current_speed = 0;
+ HAL_DEBUG(("jumped to max (%d kHz)",
+ g_a_i(iface->speeds_kHz, iface->current_speed)));
+ ret = 1;
+ }
+ } else if (cpu_load > config.up_threshold && iface->current_speed > 0) {
+ iface->current_speed = increase_speed(iface);
+ HAL_DEBUG(("increased to %d kHz", g_a_i(iface->speeds_kHz, iface->current_speed)));
+ ret = 1;
+ } else if (cpu_load < (int)g_a_i(iface->demotion, iface->current_speed) &&
+ iface->current_speed < iface->last_step) {
+ iface->current_speed = decrease_speed(iface);
+ HAL_DEBUG(("decreased to %d kHz", g_a_i(iface->speeds_kHz, iface->current_speed)));
+ ret = -1;
+ } else {
+ ret = 0;
+ HAL_DEBUG(("Speed not changed"));
+ }
+
+ iface->prev_cpu_load = cpu_load;
+ return TRUE;
+}
+
+/** @brief create the hysteresis array */
+static void create_hysteresis_array(struct userspace_interface *iface)
+{
+ g_array_free(iface->demotion, TRUE);
+ iface->demotion = g_array_new(TRUE, TRUE, sizeof(unsigned));
+
+ int i;
+ if (iface->last_step > 0) {
+ for (i = 0; i < iface->last_step; i++) {
+ int demotion = (config.up_threshold - HYSTERESIS) *
+ g_a_i(iface->speeds_kHz, i + 1) /
+ g_a_i(iface->speeds_kHz, i);
+ g_array_append_val(iface->demotion, demotion);
+ HAL_DEBUG(("Speed: %2u, kHz: %9u, demotion: %3u %%", i,
+ g_a_i(iface->speeds_kHz, i), g_a_i(iface->demotion, i)));
+ }
+ }
+}
+
+static gboolean read_frequencies(struct userspace_interface *iface)
+{
+ int num_speeds = 0;
+ GSList *it = NULL;
+ GSList *available_freqs = NULL;
+ char *available_frequencies_file = NULL;
+
+ if (!cpu_online(iface->base_cpu))
+ return FALSE;
+
+ available_frequencies_file = g_strdup_printf(SYSFS_SCALING_AVAILABLE_FREQS_FILE,
+ iface->base_cpu);
+ if (!read_line_int_split(available_frequencies_file, " ", &available_freqs)) {
+ g_free(available_frequencies_file);
+ return FALSE;
+ }
+ g_free(available_frequencies_file);
+
+ if (available_freqs == NULL) {
+ iface->last_step = 0;
+ return FALSE;
+ }
+
+ for (num_speeds = 0, it = available_freqs; it != NULL;
+ num_speeds++, it = g_slist_next(it)) {
+
+ unsigned index = GPOINTER_TO_UINT(it->data);
+ g_array_append_val(iface->speeds_kHz, index);
+ }
+ g_slist_free(available_freqs);
+
+ iface->last_step = num_speeds - 1;
+ HAL_DEBUG(("Number of speeds: %d, last_step: %d", num_speeds, iface->last_step));
+
+ reinit_speed(iface, 0);
+
+ HAL_DEBUG(("Available speeds:"));
+ for (num_speeds = 0; g_a_i(iface->speeds_kHz, num_speeds); num_speeds++) {
+ HAL_DEBUG((" %2u: %9uKHz", num_speeds, g_a_i(iface->speeds_kHz, num_speeds)));
+ }
+
+ return TRUE;
+}
+
+/** calculates current cpu load and traverses all existing interfaces */
+gboolean userspace_adjust_speeds(GSList *cpufreq_objs)
+{
+ GSList *it = NULL;
+
+ HAL_DEBUG(("Adjusting speeds..."));
+
+ if ((calc_cpu_load(DEFAULT_CONSIDER_NICE) < 0)) {
+ HAL_DEBUG(("calc_cpu_load failed. Cannot adjust speeds"));
+ return TRUE;
+ }
+
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ adjust_speed(obj->iface);
+ }
+
+ return TRUE;
+}
+
+/** inits one userspace interface with the given cores list. iface has to
+ * be allocated before passing it to that fucntion */
+gboolean userspace_init(struct userspace_interface *iface, GSList *cpus)
+{
+ if (iface == NULL)
+ return FALSE;
+
+ iface->demotion = g_array_new(TRUE, TRUE, sizeof(unsigned));
+ iface->speeds_kHz = g_array_new(TRUE, TRUE, sizeof(unsigned));
+ iface->last_step = -1;
+ iface->current_speed = 0;
+ iface->cpus = cpus;
+ iface->prev_cpu_load = 50;
+ iface->base_cpu = GPOINTER_TO_INT(cpus->data);
+
+ if (!write_governor(USERSPACE_STRING, GPOINTER_TO_INT(cpus->data))) {
+ HAL_WARNING(("Could not set userspace governor."));
+ return FALSE;
+ }
+
+ if (!read_frequencies(iface)) {
+ HAL_WARNING(("Could not read available frequencies"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** frees the userspace data */
+void userspace_free(void *data)
+{
+ struct userspace_interface *iface = data;
+ free_cpu_load_data();
+ g_array_free(iface->speeds_kHz, TRUE);
+ g_array_free(iface->demotion, TRUE);
+}
+
+/** sets the performance of the userspace governor. num has to be between
+ * 1 and 100 */
+gboolean userspace_set_performance(void *data, int up_threshold)
+{
+ struct userspace_interface *iface = data;
+
+ config.up_threshold = up_threshold;
+
+ config.cpu_high_limit = (int)(up_threshold * THRESHOLD_JUMP_LIMIT_RELATION);
+ if (config.cpu_high_limit < JUMP_CPUFREQ_LIMIT_MIN)
+ config.cpu_high_limit = JUMP_CPUFREQ_LIMIT_MIN;
+
+ HAL_DEBUG(("cpu_max set to %d, cpu_high_limit set to %d",
+ config.up_threshold, config.cpu_high_limit));
+
+ create_hysteresis_array(iface);
+
+ return TRUE;
+}
+
+/** return the current performance setting */
+int userspace_get_performance(void)
+{
+ return config.up_threshold;
+}
+
+/** sets whether niced processes should be considered when calculating CPU
+ * load */
+gboolean userspace_set_consider_nice(void *data, gboolean consider)
+{
+ HAL_DEBUG(("consider nice set to %d for userspace", consider));
+ config.consider_nice = consider;
+ return TRUE;
+}
+
+/** return the current consider nice setting */
+gboolean userspace_get_consider_nice(void)
+{
+ return config.consider_nice;
+}
+/********************* userspace end *********************/
diff --git a/hald/linux/addons/addon-cpufreq-userspace.h b/hald/linux/addons/addon-cpufreq-userspace.h
new file mode 100644
index 00000000..4424de20
--- /dev/null
+++ b/hald/linux/addons/addon-cpufreq-userspace.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq-userspace.h *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <hmacht@suse.de> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#ifndef ADDON_CPUFREQ_USERSPACE_H
+#define ADDON_CPUFREQ_USERSPACE_H
+
+#define USERSPACE_STRING "userspace"
+#define USERSPACE_POLL_INTERVAL 333
+
+struct userspace_interface {
+ int base_cpu;
+ int last_step;
+ int current_speed;
+ int g_source_id;
+ int prev_cpu_load;
+ GSList *cpus;
+ GArray *speeds_kHz;
+ GArray *demotion;
+};
+
+gboolean userspace_adjust_speeds (GSList *cpufreq_objs);
+
+gboolean userspace_init (struct userspace_interface *iface,
+ GSList *cpus);
+
+gboolean userspace_set_performance (void *data,
+ int performance);
+
+int userspace_get_performance (void);
+
+gboolean userspace_set_consider_nice (void *data,
+ gboolean consider);
+
+gboolean userspace_get_consider_nice (void);
+
+void userspace_free (void *data);
+
+void free_cpu_load_data (void);
+
+#endif /* ADDON_CPUFREQ_USERSPACE_H */
diff --git a/hald/linux/addons/addon-cpufreq.c b/hald/linux/addons/addon-cpufreq.c
new file mode 100644
index 00000000..a74fecb3
--- /dev/null
+++ b/hald/linux/addons/addon-cpufreq.c
@@ -0,0 +1,1214 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq.c *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <hmacht@suse.de> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <getopt.h>
+#include <glib/gprintf.h>
+
+#include "addon-cpufreq.h"
+#include "addon-cpufreq-userspace.h"
+#include "libhal/libhal.h"
+#include "../../logger.h"
+
+#ifdef HAVE_POLKIT
+#include <libpolkit/libpolkit.h>
+#endif
+
+#define MAX_LINE_SIZE 255
+#define CPUFREQ_POLKIT_PRIVILEGE "hal-power-cpufreq"
+#define DBUS_INTERFACE "org.freedesktop.Hal.Device.CPUFreq"
+
+#define CPUFREQ_ERROR_GENERAL "GeneralError"
+#define CPUFREQ_ERROR_UNKNOWN_METHOD "UnknownMethod"
+#define CPUFREQ_ERROR_UNKNOWN_GOVERNOR "UnknownGovernor"
+#define CPUFREQ_ERROR_INVALID_MESSAGE "InvalidMessage"
+#define CPUFREQ_ERROR_PERMISSION_DENIED "PermissionDenied"
+#define CPUFREQ_ERROR_NO_SUITABLE_GOVERNOR "NoSuitableGovernor"
+#define CPUFREQ_ERROR_GOVERNOR_INIT_FAILED "GovernorInitFailed"
+
+const char SYSFS_GOVERNOR_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor";
+
+const char SYSFS_AVAILABLE_GOVERNORS_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_available_governors";
+
+const char ONDEMAND_UP_THRESHOLD_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/ondemand/up_threshold";
+
+const char SYSFS_AFFECTED_CPUS_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/affected_cpus";
+
+const char SYSFS_CPU_ONLINE_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/online";
+
+const char ONDEMAND_IGNORE_NICE_LOAD_FILE[] =
+ "/sys/devices/system/cpu/cpu%u/cpufreq/ondemand/ignore_nice_load";
+
+static gboolean dbus_raise_error(DBusConnection *connection, DBusMessage *message,
+ const char *error_name, char *format, ...);
+
+static gboolean dbus_raise_no_suitable_governor(DBusConnection *connection,
+ DBusMessage *message,
+ char *method);
+
+static gboolean dbus_raise_governor_init_failed(DBusConnection *connection,
+ DBusMessage *message,
+ char *governor);
+
+/** list holding all cpufreq objects (userspace, ondemand, etc.) */
+static GSList *cpufreq_objs = NULL;
+
+/******************** helper functions **********************/
+
+/** reads one integer from filename and stores it in val */
+static gboolean read_line_int(const char *filename, int *val)
+{
+ char line[MAX_LINE_SIZE + 1];
+
+ if (!read_line(filename, line, MAX_LINE_SIZE)) {
+ HAL_WARNING(("Could not read from %s", filename));
+ return FALSE;
+ }
+
+ /* strip trailing '\n' */
+ line[strlen(line) - 1] = '\0';
+ *val = atoi(line);
+
+ return TRUE;
+}
+
+/** reads one line from filename with the given length */
+gboolean read_line(const char *filename, char *line, unsigned len)
+{
+ FILE *fp = fopen(filename, "r");
+ if (!fp) {
+ HAL_WARNING(("Could not open '%s': %s", filename, strerror(errno)));
+ return FALSE;
+ }
+ if ((!fgets(line, len, fp))) {
+ HAL_WARNING(("Could not read from '%s': %s", filename, strerror(errno)));
+ fclose(fp);
+ return FALSE;
+ }
+ fclose(fp);
+ return TRUE;
+}
+
+/** writes one line with the given format to filename */
+gboolean write_line(const char *filename, const char *fmt, ...)
+{
+ va_list ap;
+ FILE *fp;
+
+ fp = fopen(filename, "w+");
+ if (!fp) {
+ HAL_WARNING(("Could not open file for writing: %s; %s", filename,
+ strerror(errno)));
+ return FALSE;
+ }
+
+ va_start(ap, fmt);
+
+ if (vfprintf(fp, fmt, ap) < 0) {
+ HAL_WARNING(("Could not write to file: %s", filename));
+ fclose(fp);
+ return FALSE;
+ }
+
+ va_end(ap);
+ fclose(fp);
+ return TRUE;
+}
+
+/** reads one line from filename, splits it by delim and returns a two
+ * dimension array of strings or NULL on error */
+static gchar **read_line_str_split(char *filename, gchar *delim)
+{
+ gchar line[MAX_LINE_SIZE];
+ int i;
+ gchar **l;
+
+ if(!read_line(filename, line, MAX_LINE_SIZE)) {
+ printf("returning NULL from str split\n");
+ return NULL;
+ }
+
+ /* strip trailing '\n' */
+ line[strlen(line)-1] = '\0';
+
+ l = g_strsplit(line, delim, MAX_LINE_SIZE);
+
+ if (l[0] == NULL)
+ return NULL;
+
+ for (i = 0; l[i] != NULL; i++) {
+ if (g_strcasecmp(l[i], "") == 0) {
+ free(l[i]);
+ l[i] = NULL;
+ }
+ }
+ return l;
+}
+
+/** reads one line from filename, splits its integers by delim and stores
+ * all items in the given list */
+gboolean read_line_int_split(char *filename, gchar *delim, GSList **list)
+{
+ gchar **l;
+ int i;
+
+ l = read_line_str_split(filename, delim);
+
+ for (i = 0; l[i] != NULL; i++) {
+ int value = atoi(l[i]);
+ *list = g_slist_append(*list, GINT_TO_POINTER(value));
+ }
+ g_strfreev(l);
+ return TRUE;
+}
+
+/** gets a two dimensional list of integers and sorts out duplicates */
+static void cpu_list_unique(gpointer data, gpointer whole_list)
+{
+ GSList **list = (GSList**)whole_list;
+ GSList *current = (GSList*)data;
+ GSList *it = NULL;
+
+ for (it = *list; it != NULL; it = g_slist_next(it)) {
+ gboolean equal = TRUE;
+ if (current == it->data)
+ continue;
+
+ GSList *list_it = NULL;
+ GSList *current_it = NULL;
+ for (list_it = it->data, current_it = current;
+ list_it != NULL && current_it != NULL;
+ list_it = g_slist_next(list_it), current_it = g_slist_next(current_it)) {
+
+ HAL_DEBUG(("comparing %d with %d", GPOINTER_TO_INT(current_it->data),
+ GPOINTER_TO_INT(list_it->data)));
+ if (GPOINTER_TO_INT(current_it->data) != GPOINTER_TO_INT(list_it->data))
+ equal = FALSE;
+ }
+
+ HAL_DEBUG(("equal? %s, %d", equal ? "yes" : "no", equal));
+ if (equal) {
+ HAL_DEBUG(("remove: %d", g_slist_length(*list)));
+ *list = g_slist_remove(*list, current);
+ HAL_DEBUG(("remove_2: %d", g_slist_length(*list)));
+ return;
+ }
+ }
+}
+
+/** @brief gets the CPUs and their dependencies */
+static gboolean get_cpu_dependencies(GSList **cpu_list, int num_cpus)
+{
+ int i;
+
+ for (i = 0; i < num_cpus; i++) {
+ GSList *int_cpus = NULL;
+ GSList *affected_cpus = NULL;
+ GSList *it = NULL;
+ char *affected_cpus_file = NULL;
+
+ affected_cpus_file = g_strdup_printf(SYSFS_AFFECTED_CPUS_FILE, i);
+
+ if (!read_line_int_split(affected_cpus_file, " ", &affected_cpus)) {
+ g_free(affected_cpus_file);
+ return FALSE;
+ }
+ g_free(affected_cpus_file);
+
+ if (affected_cpus == NULL)
+ return FALSE;
+
+ for (it = affected_cpus; it != NULL; it = g_slist_next(it)) {
+ int_cpus = g_slist_append(int_cpus,
+ GINT_TO_POINTER(affected_cpus->data));
+ }
+ g_slist_free(affected_cpus);
+
+ if (!g_slist_length(int_cpus)) {
+ HAL_WARNING(("failed to get affected_cpus for cpu %d", i));
+ continue;
+ }
+
+ *cpu_list = g_slist_append(*cpu_list, int_cpus);
+ }
+
+ HAL_DEBUG(("Number of CPUs before uniquing cpu_list: %d", g_slist_length(*cpu_list)));
+ g_slist_foreach(*cpu_list, (GFunc)cpu_list_unique, cpu_list);
+ HAL_DEBUG(("Number of CPUs after uniquing cpu_list: %d", g_slist_length(*cpu_list)));
+
+ if (g_slist_length(*cpu_list) == 0)
+ return FALSE;
+ return TRUE;
+}
+
+/** check if given CPU starting from 0 is online */
+gboolean cpu_online(int cpu_id)
+{
+ gboolean online;
+ char online_str[2];
+ char *online_file;
+
+ online_file = g_strdup_printf(SYSFS_CPU_ONLINE_FILE, cpu_id);
+
+ if (access(online_file, F_OK) < 0) {
+ online = TRUE;
+ goto Out;
+ }
+
+ if (!read_line(online_file, online_str, 2)) {
+ HAL_WARNING(("Unable to open file: %s", online_file));
+ online = FALSE;
+ goto Out;
+ }
+
+ online = atoi(online_str);
+
+ if (!online)
+ online = FALSE;
+Out:
+ g_free(online_file);
+ return online;
+}
+
+/** writes the new_governor string into the sysfs interface */
+gboolean write_governor(char *new_governor, int cpu_id)
+{
+ gboolean ret = TRUE;
+ char *governor_file = NULL;
+ char governor[MAX_LINE_SIZE + 1];
+
+ if (!cpu_online(cpu_id))
+ goto Out;
+
+ governor_file = g_strdup_printf(SYSFS_GOVERNOR_FILE, cpu_id);
+ HAL_DEBUG(("Trying ot write governor %s", new_governor));
+
+ if (!write_line(governor_file, "%s", new_governor)) {
+ ret = FALSE;
+ goto Out;
+ }
+
+ /* check if governor has been set */
+ read_line(governor_file, governor, MAX_LINE_SIZE);
+ if (strstr(governor, new_governor))
+ ret = TRUE;
+ else
+ ret = FALSE;
+Out:
+ g_free(governor_file);
+ return ret;
+}
+/******************** helper functions end ********************/
+
+/********************* ondemand interface *********************/
+#define ONDEMAND_STRING "ondemand"
+
+struct ondemand_interface {
+ int base_cpu;
+};
+
+static gboolean ondemand_set_performance(void *data, int performance)
+{
+ struct ondemand_interface *iface = data;
+ char *up_threshold_file = NULL;
+
+ up_threshold_file = g_strdup_printf(ONDEMAND_UP_THRESHOLD_FILE,
+ iface->base_cpu);
+
+ if(!write_line(up_threshold_file, "%u", performance)){
+ HAL_WARNING(("Could not set up_threshold to %u kHz; %s", performance,
+ strerror(errno)));
+ g_free(up_threshold_file);
+ return FALSE;
+ }
+ g_free(up_threshold_file);
+ HAL_DEBUG(("Up threshold set to %d for ondemand", performance));
+
+ return TRUE;
+}
+
+static int ondemand_get_performance(void)
+{
+ char *governor_file;
+ int performance = -1;
+
+ governor_file = g_strdup_printf(ONDEMAND_UP_THRESHOLD_FILE, 0);
+
+ if (!read_line_int(governor_file, &performance)) {
+ HAL_WARNING(("Could not read up_threshold"));
+ g_free(governor_file);
+ return -1;
+ }
+ g_free(governor_file);
+
+ return performance;
+}
+
+static gboolean ondemand_set_consider_nice(void *data, gboolean consider)
+{
+ struct ondemand_interface *iface = data;
+ char *consider_file;
+
+ consider_file = g_strdup_printf(ONDEMAND_IGNORE_NICE_LOAD_FILE, iface->base_cpu);
+
+ if(!write_line(consider_file, "%u", consider)){
+ HAL_WARNING(("Could not set ignore_nice_load to: %u kHz; %s", consider,
+ strerror(errno)));
+ g_free(consider_file);
+ return FALSE;
+ }
+ g_free(consider_file);
+ HAL_DEBUG(("Set consider nice to %d for ondemand", consider));
+
+ return TRUE;
+}
+
+static gboolean ondemand_get_consider_nice(void)
+{
+ char *governor_file;
+ gboolean consider = -1;
+
+ /* only read the setting of cpu0 */
+ governor_file = g_strdup_printf(ONDEMAND_IGNORE_NICE_LOAD_FILE, 0);
+
+ if (!read_line_int(governor_file, &consider)) {
+ HAL_WARNING(("Could not read ignore_nice_load file"));
+ g_free(governor_file);
+ return -1;
+ }
+ g_free(governor_file);
+
+ return consider;
+}
+
+static gboolean ondemand_init(struct ondemand_interface *iface, GSList *cores)
+{
+ if (iface == NULL)
+ return FALSE;
+
+ if (!write_governor(ONDEMAND_STRING, GPOINTER_TO_INT(cores->data))) {
+ HAL_WARNING(("Could not set ondemand governor."));
+ return FALSE;
+ }
+
+ iface->base_cpu = GPOINTER_TO_INT(cores->data);
+
+ return TRUE;
+}
+
+static void ondemand_free(void *data)
+{
+ return;
+}
+
+/********************* ondemand end *********************/
+
+/********************* main interface *********************/
+
+/** sets the performance for all cpufreq objects
+ *
+ * @raises NoSuitableGoveror
+ */
+static gboolean set_performance(DBusConnection *connection, DBusMessage *message,
+ int performance)
+{
+ float steps;
+ float up_threshold;
+ GSList *it = NULL;
+
+ if (cpufreq_objs == NULL) {
+ dbus_raise_no_suitable_governor(connection, message,
+ "CPUFreqSetPerformance");
+ return FALSE;
+ }
+
+ if (performance < 1)
+ performance = 1;
+ if (performance > 100)
+ performance = 100;
+
+ if (performance >= 50) {
+ steps = UP_THRESHOLD_BASE - UP_THRESHOLD_MIN + 1;
+ up_threshold = (UP_THRESHOLD_BASE) - (((float)performance - 50.0) *
+ (steps / 51.0));
+ performance = (int)up_threshold;
+ } else if (performance < 50) {
+ steps = UP_THRESHOLD_MAX - UP_THRESHOLD_BASE;
+ up_threshold = (UP_THRESHOLD_MAX + 1) - ((float)performance *
+ (steps / 49.0));
+ performance = (int)up_threshold;
+ }
+
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ obj->set_performance(obj->iface, performance);
+ }
+ return TRUE;
+}
+
+/** sets the performance for all cpufreq objects
+ *
+ * @raises (NoSuitableGoveror|GeneralError)
+ */
+static gboolean get_performance(DBusConnection *connection, DBusMessage *message,
+ int *performance)
+{
+ struct cpufreq_obj *obj;
+ float steps;
+ float perf;
+ int up_threshold;
+
+ if (cpufreq_objs == NULL) {
+ dbus_raise_no_suitable_governor(connection, message,
+ "CPUFreqGetPerformance");
+ return FALSE;
+ }
+
+ obj = cpufreq_objs->data;
+
+ up_threshold = obj->get_performance();
+ if (up_threshold < 0) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "Could not read up_threshold");
+ return FALSE;
+ }
+
+ if (up_threshold < UP_THRESHOLD_BASE) {
+ steps = UP_THRESHOLD_BASE - UP_THRESHOLD_MIN + 1;
+ perf = (((UP_THRESHOLD_BASE) - up_threshold) /
+ (steps / 51.0)) + 50.0;
+ } else if (up_threshold >= UP_THRESHOLD_BASE) {
+ steps = UP_THRESHOLD_MAX - UP_THRESHOLD_BASE;
+ perf = ((UP_THRESHOLD_MAX + 1) - up_threshold) /
+ (steps / 49.0);
+ }
+
+ *performance = (int)perf;
+
+ return TRUE;
+}
+
+/** sets the performance for all cpufreq objects
+ *
+ * @raises NoSuitableGoveror
+ */
+static gboolean set_consider_nice(DBusConnection *connection, DBusMessage *message,
+ gboolean consider)
+{
+ GSList *it = NULL;
+
+ if (cpufreq_objs == NULL) {
+ dbus_raise_no_suitable_governor(connection, message,
+ "CPUFreqSetConsiderNice");
+ return FALSE;
+ }
+
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj= it->data;
+ obj->set_consider_nice(obj->iface, consider);
+ }
+ return TRUE;
+}
+
+/** sets the performance for all cpufreq objects
+ *
+ * @raises NoSuitableGoveror
+ */
+static gboolean get_consider_nice(DBusConnection *connection, DBusMessage *message,
+ int *consider)
+{
+ struct cpufreq_obj *obj;
+
+ if (cpufreq_objs == NULL) {
+ dbus_raise_no_suitable_governor(connection, message,
+ "CPUFreqGetConsiderNice");
+ return FALSE;
+ }
+ obj = cpufreq_objs->data;
+
+ *consider = obj->get_consider_nice();
+
+ return TRUE;
+}
+
+/** stores a list of all available governors in the given list.
+ *
+ * @raises GeneralError
+ */
+static gboolean get_available_governors(DBusConnection *connection, DBusMessage *message,
+ gchar ***governors)
+{
+ char *agovs_file;
+
+ agovs_file = g_strdup_printf(SYSFS_AVAILABLE_GOVERNORS_FILE, 0);
+ *governors = read_line_str_split(agovs_file, " ");
+ g_free(agovs_file);
+
+ if (*governors == NULL) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "No CPUFreq governors");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** sets a governor for all cpufreq objects
+ *
+ * @raises (GeneralError|UnknownGovernor|GovernorInitFailed)
+ */
+static gboolean set_governors(DBusConnection *connection, DBusMessage *message,
+ const char *governor)
+{
+ GSList *cpus = NULL;
+ GSList *it = NULL;
+ static int g_source_id = -1;
+ gboolean have_governor = FALSE;
+ int i;
+ int num_cpus;
+ gchar **available_governors;
+
+ if (!get_available_governors(connection, message, &available_governors))
+ return FALSE;
+
+ for (i = 0; available_governors[i] != NULL; i++) {
+ if (strcmp(available_governors[i], governor) == 0) {
+ have_governor = TRUE;
+ break;
+ }
+ }
+ g_strfreev(available_governors);
+
+ if (!have_governor) {
+ dbus_raise_error(connection, message,
+ CPUFREQ_ERROR_UNKNOWN_GOVERNOR,
+ "No governor '%s' available", governor);
+ return FALSE;
+ }
+
+ /** clear all previous cpufreq_objs */
+ if (g_slist_length(cpufreq_objs) > 0) {
+ GSList *it = NULL;
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ obj->free(obj->iface);
+ free(obj->iface);
+ free(obj);
+ }
+ g_slist_free(cpufreq_objs);
+ cpufreq_objs = NULL;
+ g_source_remove(g_source_id);
+ g_source_id = -1;
+ }
+
+ num_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (num_cpus < 0) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "No CPUs found in system");
+ HAL_WARNING(("No CPUs found in system"));
+ return FALSE;
+ }
+
+ if (!get_cpu_dependencies(&cpus, num_cpus)) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "Could not figure out cpu core dependencies");
+ HAL_WARNING(("Could not figure out cpu core dependencies"));
+ return FALSE;
+ }
+
+ if (!strcmp(governor, USERSPACE_STRING)) {
+ struct cpufreq_obj *cpufreq_obj;
+ struct userspace_interface *iface;
+
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ cpufreq_obj = malloc(sizeof(struct cpufreq_obj));
+ iface = malloc(sizeof(struct userspace_interface));
+
+ if (userspace_init(iface, it->data)) {
+ cpufreq_obj->iface = iface;
+ cpufreq_obj->set_performance = userspace_set_performance;
+ cpufreq_obj->get_performance = userspace_get_performance;
+ cpufreq_obj->set_consider_nice = userspace_set_consider_nice;
+ cpufreq_obj->get_consider_nice = userspace_get_consider_nice;
+ cpufreq_obj->free = userspace_free;
+ cpufreq_objs = g_slist_append(cpufreq_objs, cpufreq_obj);
+ HAL_DEBUG(("added userspace interface"));
+ } else {
+ dbus_raise_governor_init_failed(connection, message,
+ (char*)governor);
+ return FALSE;
+ }
+ }
+ g_source_id = g_timeout_add(USERSPACE_POLL_INTERVAL,
+ (GSourceFunc)userspace_adjust_speeds,
+ cpufreq_objs);
+
+ } else if (!strcmp(governor, ONDEMAND_STRING)) {
+ struct cpufreq_obj *cpufreq_obj;
+ struct ondemand_interface *iface;
+
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ cpufreq_obj = malloc(sizeof(struct cpufreq_obj));
+ iface = malloc(sizeof(struct ondemand_interface));
+
+ if (ondemand_init(iface, it->data)) {
+ cpufreq_obj->iface = iface;
+ cpufreq_obj->set_performance = ondemand_set_performance;
+ cpufreq_obj->get_performance = ondemand_get_performance;
+ cpufreq_obj->set_consider_nice = ondemand_set_consider_nice;
+ cpufreq_obj->get_consider_nice = ondemand_get_consider_nice;
+ cpufreq_obj->free = ondemand_free;
+ cpufreq_objs = g_slist_append(cpufreq_objs, cpufreq_obj);
+ HAL_DEBUG(("added ondemand interface"));
+ } else {
+ dbus_raise_governor_init_failed(connection, message,
+ (char*)governor);
+ return FALSE;
+ }
+ }
+ } else {
+ for (it = cpus; it != NULL; it = g_slist_next(it)) {
+ if (!write_governor((char*)governor,
+ GPOINTER_TO_INT(((GSList*)it->data)->data))) {
+ dbus_raise_governor_init_failed(connection, message,
+ (char*)governor);
+ HAL_WARNING(("Could not set %s governor.", governor));
+ return FALSE;
+ }
+ }
+ }
+
+ set_performance(NULL, NULL, DEFAULT_PERFORMANCE);
+
+ return TRUE;
+}
+
+/** gets the current governor which is set for all cpufreq objects
+ *
+ * @raises GeneralError
+ */
+static gboolean get_governors(DBusConnection *connection, DBusMessage *message,
+ char *governor)
+{
+ char *governor_file;
+ int cpu_id = 0;
+
+ governor_file = g_strdup_printf(SYSFS_GOVERNOR_FILE, cpu_id);
+
+ if (!read_line(governor_file, governor, MAX_LINE_SIZE)) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "Could not read current governor");
+ g_free(governor_file);
+ return FALSE;
+ }
+ g_free(governor_file);
+
+ /* strip trailing '\n' */
+ governor[strlen(governor)-1] = '\0';
+
+ return TRUE;
+}
+/********************* main interface end *********************/
+
+/********************* DBus stuff *********************/
+
+/** raises the NoSuitableGovernor error with the given method in the
+ * detail field */
+static gboolean dbus_raise_no_suitable_governor(DBusConnection *connection,
+ DBusMessage *message,
+ char *method)
+{
+ return dbus_raise_error(connection, message,
+ CPUFREQ_ERROR_NO_SUITABLE_GOVERNOR,
+ "No '%s' setting for current governor",
+ method);
+}
+
+/** raises the GovernorInitFailed error with the given governor in the
+ * detail field */
+static gboolean dbus_raise_governor_init_failed(DBusConnection *connection,
+ DBusMessage *message,
+ char *governor)
+{
+ return dbus_raise_error(connection, message,
+ CPUFREQ_ERROR_GOVERNOR_INIT_FAILED,
+ "Initialization of %s interface failed",
+ governor);
+}
+
+/** raises the given error_name with the format in the detail field */
+static gboolean dbus_raise_error(DBusConnection *connection, DBusMessage *message,
+ const char *error_name, char *format, ...)
+{
+ char buf[2 * MAX_LINE_SIZE];
+ DBusMessage *reply;
+ va_list args;
+ char *error = NULL;
+
+ if (connection == NULL || message == NULL)
+ return FALSE;
+
+ va_start(args, format);
+ vsnprintf(buf, sizeof buf, format, args);
+ va_end(args);
+
+ error = g_strdup_printf("%s.%s", DBUS_INTERFACE, error_name);
+ reply = dbus_message_new_error(message, error, buf);
+ g_free(error);
+ if (reply == NULL) {
+ HAL_WARNING(("No memory"));
+ return FALSE;
+ }
+
+ if (!dbus_connection_send(connection, reply, NULL)) {
+ HAL_WARNING(("No memory"));
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+ dbus_message_unref(reply);
+
+ return TRUE;
+}
+
+#ifdef HAVE_POLKIT
+/** checks if caller of message possesses the CPUFREQ_POLKIT_PRIVILGE */
+static gboolean dbus_is_privileged(DBusConnection *connection, DBusMessage *message,
+ DBusError *error)
+{
+ LibPolKitContext *polctx = NULL;
+ char *caller_unix_user_str;
+ const char *caller_dbus_name;
+ unsigned long caller_unix_user;
+ DBusConnection *connection_new;
+ gboolean out_is_allowed;
+ gboolean out_is_temporary;
+ LibPolKitResult res;
+
+ connection_new = dbus_bus_get(DBUS_BUS_SYSTEM, error);
+ if (dbus_error_is_set(error)) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "Cannot get connection to system bus");
+ return FALSE;
+ }
+
+ polctx = libpolkit_new_context(connection_new);
+ if (polctx == NULL) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "Cannot get PolicyKit context");
+ return FALSE;
+ }
+
+ caller_dbus_name = dbus_message_get_sender(message);
+ caller_unix_user = dbus_bus_get_unix_user(connection_new, caller_dbus_name, error);
+ HAL_DEBUG(("Connection name of caller: %s", caller_dbus_name));
+ HAL_DEBUG(("Unix user id of caller: %ld", caller_unix_user));
+ if (dbus_error_is_set(error)) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "Cannot get unix user of caller");
+ dbus_error_free(error);
+ goto Error;
+ }
+
+ caller_unix_user_str = g_strdup_printf("%ld", caller_unix_user);
+ res = libpolkit_is_uid_allowed_for_privilege(polctx,
+ caller_dbus_name,
+ caller_unix_user_str,
+ CPUFREQ_POLKIT_PRIVILEGE,
+ getenv("UDI"),
+ &out_is_allowed,
+ &out_is_temporary,
+ NULL);
+ g_free(caller_unix_user_str);
+
+ if (res != LIBPOLKIT_RESULT_OK) {
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+ "Cannot lookup privilege: %d", res);
+ goto Error;
+ }
+
+ if (!out_is_allowed) {
+ HAL_DEBUG(("caller don't possess privilege"));
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_PERMISSION_DENIED,
+ "%s refused uid %d", CPUFREQ_POLKIT_PRIVILEGE, caller_unix_user);
+ goto Error;
+ }
+
+ HAL_DEBUG(("Caller is privileged"));
+ return out_is_allowed;
+
+Error:
+ libpolkit_free_context(polctx);
+ return FALSE;
+}
+#endif
+
+/** sends a reply to message with the given data and its dbus_type */
+static gboolean dbus_send_reply(DBusConnection *connection, DBusMessage *message,
+ int dbus_type, void *data)
+{
+ DBusMessage *reply;
+
+ if ((reply = dbus_message_new_method_return(message)) == NULL) {
+ HAL_WARNING(("Could not allocate memory for the DBus reply"));
+ return FALSE;
+ }
+
+ if (data != NULL)
+ dbus_message_append_args(reply, dbus_type, data, DBUS_TYPE_INVALID);
+
+ if (!dbus_connection_send(connection, reply, NULL)) {
+ HAL_WARNING(("Could not sent reply"));
+ return FALSE;
+ }
+ dbus_connection_flush(connection);
+ dbus_message_unref(reply);
+
+ return TRUE;
+}
+
+/** sends a reply to message appending a list of strings */
+static gboolean dbus_send_reply_strlist(DBusConnection *connection, DBusMessage *message,
+ gchar **list)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter iter_array;
+ int i;
+
+ if ((reply = dbus_message_new_method_return(message)) == NULL) {
+ HAL_WARNING(("Could not allocate memory for the DBus reply"));
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &iter_array);
+
+ for (i = 0; list[i] != NULL; i++)
+ dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &list[i]);
+
+ dbus_message_iter_close_container (&iter, &iter_array);
+
+ if (!dbus_connection_send(connection, reply, NULL)) {
+ HAL_WARNING(("Could not sent reply"));
+ return FALSE;
+ }
+
+ dbus_connection_flush(connection);
+ dbus_message_unref(reply);
+
+ return TRUE;
+}
+
+/** gets one argument from message with the given dbus_type and stores it
+ * in arg
+ *
+ * @raises InvalidMessage
+ */
+static gboolean dbus_get_argument(DBusConnection *connection, DBusMessage *message,
+ DBusError *dbus_error, int dbus_type, void *arg)
+{
+ dbus_message_get_args(message, dbus_error, dbus_type, arg,
+ DBUS_TYPE_INVALID);
+ if (dbus_error_is_set(dbus_error)) {
+ HAL_WARNING(("Could not get argument of DBus message: %s",
+ dbus_error->message));
+
+ dbus_raise_error(connection, message,
+ CPUFREQ_ERROR_INVALID_MESSAGE,
+ "%s", dbus_error->message);
+ dbus_error_free(dbus_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/** dbus filter function
+ *
+ * @raises UnknownMethod
+ */
+static DBusHandlerResult dbus_filter_function(DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ DBusError dbus_error;
+ const char *member = dbus_message_get_member(message);
+ const char *path = dbus_message_get_path(message);
+
+ HAL_DEBUG(("Received DBus message with member %s", member));
+ HAL_DEBUG(("Received DBus message with path %s", path));
+
+ dbus_error_init(&dbus_error);
+
+#ifdef HAVE_POLKIT
+ if (!dbus_is_privileged(connection, message, &dbus_error))
+ return DBUS_HANDLER_RESULT_HANDLED;
+#endif
+
+ if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "SetCPUFreqGovernor")) {
+ char *arg;
+
+ if (!dbus_get_argument(connection, message, &dbus_error,
+ DBUS_TYPE_STRING, &arg)) {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ HAL_DEBUG(("Received argument: %s", arg));
+
+ if (set_governors(connection, message, arg))
+ dbus_send_reply(connection, message, DBUS_TYPE_INVALID, NULL);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "SetCPUFreqPerformance")) {
+ int arg;
+
+ if (!dbus_get_argument(connection, message, &dbus_error,
+ DBUS_TYPE_INT32, &arg)) {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ HAL_DEBUG(("Received argument: %d", arg));
+
+ if (set_performance(connection, message, arg))
+ dbus_send_reply(connection, message, DBUS_TYPE_INVALID, NULL);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "SetCPUFreqConsiderNice")) {
+ gboolean arg;
+
+ if (!dbus_get_argument(connection, message, &dbus_error,
+ DBUS_TYPE_BOOLEAN, &arg)) {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ HAL_DEBUG(("Received argument: %d", arg));
+
+ if (set_consider_nice(connection, message, arg))
+ dbus_send_reply(connection, message, DBUS_TYPE_INVALID, NULL);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "GetCPUFreqGovernor")) {
+ char governor[MAX_LINE_SIZE + 1];
+ char *gov = governor;
+
+ if (get_governors(connection, message, governor))
+ dbus_send_reply(connection, message, DBUS_TYPE_STRING, &gov);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "GetCPUFreqPerformance")) {
+ int performance = -1;
+
+ if (get_performance(connection, message, &performance))
+ dbus_send_reply(connection, message, DBUS_TYPE_INT32, &performance);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "GetCPUFreqConsiderNice")) {
+ int consider = -1;
+
+ if (get_consider_nice(connection, message, &consider))
+ dbus_send_reply(connection, message, DBUS_TYPE_BOOLEAN, &consider);
+
+ } else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+ "GetCPUFreqAvailableGovernors")) {
+ gchar **governors = NULL;
+
+ if (get_available_governors(connection, message, &governors))
+ dbus_send_reply_strlist(connection, message, governors);
+ g_strfreev(governors);
+
+ } else if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
+ "Disconnected")) {
+ HAL_DEBUG(("DBus daemon disconnected. Trying to reconnect..."));
+ dbus_connection_close(connection);
+ dbus_connection_unref(connection);
+ g_timeout_add(5000, (GSourceFunc)dbus_init, NULL);
+
+ } else
+ dbus_raise_error(connection, message, CPUFREQ_ERROR_UNKNOWN_METHOD,
+ "No such method '%s'", member);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void dbus_add_method(LibHalContext *halctx, const char *method,
+ const char *signature)
+{
+ libhal_device_property_strlist_append(halctx,
+ "/org/freedesktop/Hal/devices/computer",
+ "org.freedesktop.Hal.Device.CPUFreq.method_names",
+ method,
+ NULL);
+ libhal_device_property_strlist_append(halctx,
+ "/org/freedesktop/Hal/devices/computer",
+ "org.freedesktop.Hal.Device.CPUFreq.method_signatures",
+ signature,
+ NULL);
+}
+
+static gboolean is_supported(void)
+{
+ char *governor_file = NULL;
+
+ governor_file = g_strdup_printf(SYSFS_GOVERNOR_FILE, 0);
+ if (access(governor_file, F_OK) != 0) {
+ g_free(governor_file);
+ return FALSE;
+ }
+ g_free(governor_file);
+ return TRUE;
+}
+
+/** returns FALSE on success because it's used as a callback */
+gboolean dbus_init(void)
+{
+ DBusError dbus_error;
+ DBusConnection *dbus_connection;
+ char *udi = getenv("UDI");
+ LibHalContext *halctx = NULL;
+
+ dbus_error_init(&dbus_error);
+
+ if ((halctx = libhal_ctx_init_direct(&dbus_error)) == NULL) {
+ HAL_WARNING(("Cannot connect to hald"));
+ goto Error;
+ }
+
+ if ((dbus_connection = libhal_ctx_get_dbus_connection(halctx)) == NULL) {
+ HAL_WARNING(("Cannot get DBus connection"));
+ goto Error;
+ }
+
+ if (!libhal_device_claim_interface(halctx, udi,
+ "org.freedesktop.Hal.Device.CPUFreq",
+ " <method name=\"SetCPUFreqGovernor\">"
+ " <arg name=\"governor_string\" direction=\"in\" type=\"s\"/>"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"SetCPUFreqPerformance\">"
+ " <arg name=\"value\" direction=\"in\" type=\"i\"/>"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"SetCPUFreqConsiderNice\">"
+ " <arg name=\"value\" direction=\"in\" type=\"b\"/>"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"GetCPUFreqGovernor\">"
+ " <arg name=\"return_code\" direction=\"out\" type=\"s\"/>"
+ " </method>"
+ " <method name=\"GetCPUFreqPerformance\">"
+ " <arg name=\"return_code\" direction=\"out\" type=\"i\"/>"
+ " </method>"
+ " <method name=\"GetCPUFreqConsiderNice\">"
+ " <arg name=\"return_code\" direction=\"out\" type=\"b\"/>"
+ " </method>"
+ " <method name=\"GetCPUFreqAvailableGovernors\">"
+ " <arg name=\"return_code\" direction=\"out\" type=\"as\"/>"
+ " </method>",
+ &dbus_error)) {
+
+ HAL_WARNING(("Cannot claim interface: %s", dbus_error.message));
+ fprintf(stderr, "direct Cannot claim interface: %s", dbus_error.message);
+ goto Error;
+ }
+
+ libhal_device_add_capability(halctx,
+ "/org/freedesktop/Hal/devices/computer",
+ "cpufreq_control",
+ NULL);
+
+ dbus_add_method(halctx, "SetCPUFreqGovernor", "s");
+ dbus_add_method(halctx, "SetCPUFreqPerformance", "i");
+ dbus_add_method(halctx, "SetCPUFreqConsiderNice", "b");
+ dbus_add_method(halctx, "GetCPUFreqGovernor", "");
+ dbus_add_method(halctx, "GetCPUFreqPerformance", "");
+ dbus_add_method(halctx, "GetCPUFreqConsiderNice", "");
+ dbus_add_method(halctx, "GetCPUFreqAvailableGovernors", "");
+
+
+ dbus_connection_setup_with_g_main(dbus_connection, NULL);
+ dbus_connection_add_filter(dbus_connection, dbus_filter_function, NULL, NULL);
+ dbus_connection_set_exit_on_disconnect(dbus_connection, 0);
+ return FALSE;
+
+Error:
+ dbus_error_free(&dbus_error);
+ return TRUE;
+}
+/********************* DBus end *********************/
+
+static void exit_handler(int i)
+{
+ GSList *it = NULL;
+
+ for (it = cpufreq_objs; it != NULL; it = g_slist_next(it)) {
+ struct cpufreq_obj *obj = it->data;
+ obj->free(obj->iface);
+ free(obj->iface);
+ free(obj);
+ }
+ g_slist_free(cpufreq_objs);
+
+ HAL_DEBUG(("exit"));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction signal_action;
+ GMainLoop *gmain;
+
+ memset(&signal_action, 0, sizeof(signal_action));
+ sigaddset(&signal_action.sa_mask, SIGTERM);
+ signal_action.sa_flags = SA_RESTART || SA_NOCLDSTOP;
+ signal_action.sa_handler = exit_handler;
+ sigaction(SIGINT, &signal_action, 0);
+ sigaction(SIGQUIT, &signal_action, 0);
+ sigaction(SIGTERM, &signal_action, 0);
+
+ if (!is_supported()) {
+ HAL_WARNING(("CPUFreq not supported. Exiting..."));
+ exit(EXIT_FAILURE);
+ }
+
+ if (dbus_init())
+ exit(EXIT_FAILURE);
+
+ gmain = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(gmain);
+
+ return 0;
+}
diff --git a/hald/linux/addons/addon-cpufreq.h b/hald/linux/addons/addon-cpufreq.h
new file mode 100644
index 00000000..d8122190
--- /dev/null
+++ b/hald/linux/addons/addon-cpufreq.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * *
+ * addon-cpufreq.h *
+ * *
+ * Copyright (C) 2006 SUSE Linux Products GmbH *
+ * *
+ * Author(s): Holger Macht <hmacht@suse.de> *
+ * *
+ * 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 you *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#ifndef ADDON_CPUFREQ_H
+#define ADDON_CPUFREQ_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/** UP_THRESHOLD defines at which CPU load (in percent) we switch up */
+#define UP_THRESHOLD_MAX 99
+#define UP_THRESHOLD_MIN 11
+/** this is the kernel default up_threshold */
+#define UP_THRESHOLD_BASE 80
+#define DEFAULT_PERFORMANCE 50
+
+struct cpufreq_obj {
+ void *iface;
+ gboolean (*set_performance) (void *data, int);
+ gboolean (*set_consider_nice) (void *data, gboolean);
+ int (*get_performance) (void);
+ gboolean (*get_consider_nice) (void);
+ void (*free) (void *data);
+};
+
+gboolean write_line (const char *filename,
+ const char *fmt, ...);
+
+gboolean read_line (const char *filename,
+ char *line,
+ unsigned len);
+
+gboolean read_line_int_split (char *filename,
+ gchar *delim,
+ GSList **list);
+
+gboolean cpu_online (int cpu_id);
+
+gboolean write_governor (char *new_governor,
+ int cpu_id);
+
+gboolean dbus_init (void);
+
+#endif /* ADDON_CPUFREQ_H */
diff --git a/privileges/Makefile.am b/privileges/Makefile.am
index f32ab1c9..d300a603 100644
--- a/privileges/Makefile.am
+++ b/privileges/Makefile.am
@@ -9,7 +9,8 @@ dist_polkit_privilege_DATA = \
hal-power-suspend.privilege \
hal-power-hibernate.privilege \
hal-power-poweroff.privilege \
- hal-power-reboot.privilege
+ hal-power-reboot.privilege \
+ hal-power-cpufreq.privilege
clean-local :
rm -f *~
diff --git a/privileges/hal-power-cpufreq.privilege b/privileges/hal-power-cpufreq.privilege
new file mode 100644
index 00000000..cee08b46
--- /dev/null
+++ b/privileges/hal-power-cpufreq.privilege
@@ -0,0 +1,12 @@
+
+# This privilege specifies who is allowed to control CPUFreq
+# via the org.freedesktop.Hal.Device.CPUFreq interface
+
+[Privilege]
+RequiredPrivileges=desktop-console
+SufficientPrivileges=
+Allow=uid:__all__
+Deny=
+CanObtain=True
+CanGrant=True
+ObtainRequireRoot=False