[PATCH 1/3] CPU frequency scaling addon

Holger Macht hmacht at suse.de
Fri Aug 11 10:45:15 PDT 2006


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 at suse.de>
---

diff --git a/fdi/policy/10osvendor/10-power-mgmt-policy.fdi b/fdi/policy/10osvendor/10-power-mgmt-policy.fdi
index 3c292fd..27f837f 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/linux2/addons/Makefile.am b/hald/linux2/addons/Makefile.am
index 33cdd9c..1923093 100644
--- a/hald/linux2/addons/Makefile.am
+++ b/hald/linux2/addons/Makefile.am
@@ -15,7 +15,8 @@ libexec_PROGRAMS  = \
 	hald-addon-acpi-buttons-toshiba \
 	hald-addon-storage \
 	hald-addon-keyboard \
-	hald-addon-pmu
+	hald-addon-pmu \
+	hald-addon-cpufreq
 
 if HAVE_LIBUSB
 libexec_PROGRAMS += hald-addon-usb-csr
@@ -25,6 +26,10 @@ libexec_PROGRAMS += hald-addon-macbookpr
 endif
 endif
 
+hald_addon_cpufreq_SOURCES = addon-cpufreq.c addon-cpufreq.h addon-cpufreq-userspace.h \
+			       addon-cpufreq-userspace.c
+hald_addon_cpufreq_LDADD = $(top_builddir)/libhal/libhal.la @GLIB_LIBS@ @POLKIT_LIBS@
+
 hald_addon_hid_ups_SOURCES = addon-hid-ups.c
 hald_addon_hid_ups_LDADD = $(top_builddir)/libhal/libhal.la
 
diff --git a/hald/linux2/addons/addon-acpi-buttons-toshiba.c b/hald/linux2/addons/addon-acpi-buttons-toshiba.c
diff --git a/hald/linux2/addons/addon-acpi.c b/hald/linux2/addons/addon-acpi.c
diff --git a/hald/linux2/addons/addon-cpufreq-userspace.c b/hald/linux2/addons/addon-cpufreq-userspace.c
new file mode 100644
index 0000000..db458bf
--- /dev/null
+++ b/hald/linux2/addons/addon-cpufreq-userspace.c
@@ -0,0 +1,528 @@
+/***************************************************************************
+ *                                                                         *
+ *                      addon-cpufreq-userspace.c                          *
+ *                                                                         *
+ *              Copyright (C) 2006 SUSE Linux Products GmbH                *
+ *                                                                         *
+ *              Author(s): Holger Macht <hmacht at suse.de>                   *
+ *                         Speed adjustments based on code by              *
+ *                           Thomas Renninger <trenn at 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 "addon-cpufreq.h"
+#include "addon-cpufreq-userspace.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()
+{
+	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) {
+		dbg("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) {
+			ERROR("%s too short (%s)", PROC_STAT_FILE, cpu_string);
+			fclose(fp);
+			return -1;
+		}
+		if (memcmp(line, cpu_string, strlen(cpu_string))) {
+			ERROR("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) {
+			ERROR("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)
+				dbg("%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) {
+		ERROR("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)
+{
+	GString  *speed_file = g_string_new("");
+	gboolean ret = TRUE;
+
+	if (!cpu_online(cpu_id)) {
+		ret = FALSE;
+		goto Out;
+	}
+
+	g_string_printf(speed_file, SYSFS_SCALING_SETSPEED_FILE, cpu_id); 
+
+        if(!write_line(speed_file->str, "%u", kHz)){
+                ERROR("Could not set speed to: %u kHz; %s", kHz, strerror(errno));
+                ret = FALSE;
+		goto Out;
+        }
+
+	dbg("Speed set to: %uKHz  for CPU %d", kHz, cpu_id);
+Out:
+	g_string_free(speed_file, TRUE);
+	return ret;
+}
+
+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);
+	dbg("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) {
+		dbg("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) {
+		dbg("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)) {
+		dbg("checking cpu %d: cpu_core: %d", (int)it->data, (int)it->data);
+		if (get_cpu_load((int)it->data) > cpu_load)
+			 cpu_load = get_cpu_load((int)it->data);
+	}
+
+	dbg("cpu_max: %d cpu_high_limit: %d consider_nice: %d",
+	      config.up_threshold, config.cpu_high_limit,
+	      config.consider_nice);
+	dbg("Current: %u; current speed: %u MHz", 
+	      iface->current_speed, g_a_i(iface->speeds_kHz, iface->current_speed));
+	dbg("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;
+			dbg("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);
+		dbg("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);
+		dbg("decreased to %d kHz", g_a_i(iface->speeds_kHz, iface->current_speed));
+		ret = -1;
+	} else {
+		ret = 0;
+		dbg("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);
+			dbg("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;
+	GString *available_frequencies_file	= g_string_new("");
+	
+	if (!cpu_online(iface->base_cpu))
+		return FALSE;
+
+	g_string_printf(available_frequencies_file,
+			 SYSFS_SCALING_AVAILABLE_FREQS_FILE,
+			 iface->base_cpu); 
+	if (!read_line_int_split(available_frequencies_file, " ", &available_freqs))
+		return FALSE;
+
+	g_string_free(available_frequencies_file, TRUE);
+	
+	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 = (unsigned)it->data;
+		g_array_append_val(iface->speeds_kHz, index);
+	}
+	g_slist_free(available_freqs);
+	
+	iface->last_step = num_speeds - 1;
+	dbg("Number of speeds: %d, last_step: %d", num_speeds, iface->last_step);
+	
+	reinit_speed(iface, 0);
+	
+	dbg("Available speeds:");
+	for (num_speeds = 0; g_a_i(iface->speeds_kHz, num_speeds); num_speeds++) {
+		dbg(" %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;
+
+	dbg("Adjusting speeds...");
+
+	if ((calc_cpu_load(DEFAULT_CONSIDER_NICE) < 0)) {
+		dbg("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		= (int)cpus->data;
+	
+	if ((getenv("HALD_VERBOSE")) != NULL)
+		is_verbose = TRUE;
+
+	if (!write_governor(USERSPACE_STRING, (int)cpus->data)) {
+		ERROR("Could not set userspace governor.");
+		return FALSE;
+	}
+
+	if (!read_frequencies(iface)) {
+		ERROR("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;
+
+	dbg("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)
+{
+	dbg("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/linux2/addons/addon-cpufreq-userspace.h b/hald/linux2/addons/addon-cpufreq-userspace.h
new file mode 100644
index 0000000..e5674d5
--- /dev/null
+++ b/hald/linux2/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 at 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/linux2/addons/addon-cpufreq.c b/hald/linux2/addons/addon-cpufreq.c
new file mode 100644
index 0000000..c5b13d5
--- /dev/null
+++ b/hald/linux2/addons/addon-cpufreq.c
@@ -0,0 +1,1185 @@
+/***************************************************************************
+ *                                                                         *
+ *                            addon-cpufreq.c                              *
+ *                                                                         *
+ *              Copyright (C) 2006 SUSE Linux Products GmbH                *
+ *                                                                         *
+ *               Author(s): Holger Macht <hmacht at 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 "addon-cpufreq.h"
+#include "addon-cpufreq-userspace.h"
+#include "libhal/libhal.h"
+
+#ifdef HAVE_POLKIT
+#include <libpolkit/libpolkit.h>
+#endif
+
+#define MAX_LINE_SIZE				255
+#define PROC_CPUINFO_FILE			"/proc/cpuinfo"
+#define CPUFREQ_POLKIT_PRIVILEGE		"hal-power-cpufreq"
+#define DBUS_INTERFACE				"org.freedesktop.Hal.Device.SystemPowerManagement"
+
+#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)) {
+		ERROR("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) {
+		ERROR("Could not open '%s': %s", filename, strerror(errno));
+		return FALSE;
+	}
+	if ((!fgets(line, len, fp))) {
+		ERROR("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) {
+		ERROR("Could not open file for writing: %s; %s", filename,
+		      strerror(errno));
+		return FALSE;
+	}
+
+	va_start(ap, fmt);
+
+	if (vfprintf(fp, fmt, ap) < 0) {
+		ERROR("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(GString *filename, gchar *delim)
+{
+	gchar	line[MAX_LINE_SIZE];
+	int	i;
+	gchar	**l;
+
+        if(!read_line(filename->str, 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(GString *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)) {
+
+			dbg("comparing %d with %d", (int)current_it->data, (int)list_it->data);
+			if ((int)current_it->data != (int)list_it->data)
+				equal = FALSE;
+		}
+
+		dbg("equal? %s, %d", equal ? "yes" : "no", equal);		
+		if (equal) {
+			dbg("remove: %d", g_slist_length(*list));
+			*list = g_slist_remove(*list, current);
+			dbg("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;
+		GString	*affected_cpus_file	= g_string_new("");
+
+		g_string_printf(affected_cpus_file, SYSFS_AFFECTED_CPUS_FILE, i); 
+
+		if (!read_line_int_split(affected_cpus_file, " ", &affected_cpus))
+			return FALSE;
+		g_string_free(affected_cpus_file, TRUE);
+
+		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)) {
+			ERROR("failed to get affected_cpus for cpu %d", i);
+			continue;
+		}
+
+		*cpu_list = g_slist_append(*cpu_list, int_cpus);
+	}
+
+	dbg("Number of CPUs before uniquing cpu_list: %d", g_slist_length(*cpu_list));
+	g_slist_foreach(*cpu_list, (GFunc)cpu_list_unique, cpu_list);
+	dbg("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];
+
+	GString *online_file = g_string_new("");
+	g_string_printf(online_file, SYSFS_CPU_ONLINE_FILE, cpu_id); 
+
+	if (access(online_file->str, F_OK) < 0) {
+		online = TRUE;
+		goto Out;
+	}
+
+	if (!read_line(online_file->str, online_str, 2)) {
+		ERROR("Unable to open file: %s", online_file->str);
+		online = FALSE;
+		goto Out;
+	}
+	
+	online = atoi(online_str);
+
+	if (!online)
+		online = FALSE;
+Out:
+	g_string_free(online_file, TRUE);
+	return online;
+}
+
+/** writes the new_governor string into the sysfs interface */ 
+gboolean write_governor(char *new_governor, int cpu_id)
+{
+	gboolean	ret		= TRUE;
+	GString		*governor_file	= g_string_new("");
+	char		governor[MAX_LINE_SIZE + 1];
+
+	if (!cpu_online(cpu_id))
+		goto Out;
+
+	g_string_printf(governor_file, SYSFS_GOVERNOR_FILE, cpu_id); 
+	dbg("Trying ot write governor %s", new_governor);
+
+	if (!write_line(governor_file->str, "%s", new_governor)) {
+		ret = FALSE;
+		goto Out;
+	}
+	
+	/* check if governor has been set */
+	usleep(1000);
+	read_line(governor_file->str, governor, MAX_LINE_SIZE);
+	if (strstr(governor, new_governor))
+		ret = TRUE;
+	else
+		ret = FALSE;
+Out:
+	g_string_free(governor_file, TRUE);
+	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;
+	GString				*up_threshold_file = g_string_new("");
+
+	g_string_printf(up_threshold_file, ONDEMAND_UP_THRESHOLD_FILE, iface->base_cpu); 
+
+        if(!write_line(up_threshold_file->str, "%u", performance)){
+                ERROR("Could not set up_threshold to %u kHz; %s", performance,
+		      strerror(errno));
+                return FALSE;
+        }
+	dbg("Up threshold set to %d for ondemand", performance);
+	g_string_free(up_threshold_file, TRUE);
+
+	return TRUE;
+}
+
+static int ondemand_get_performance(void)
+{
+	GString	*governor_file	= g_string_new("");
+	int	performance	= -1;
+
+	g_string_printf(governor_file, ONDEMAND_UP_THRESHOLD_FILE, 0); 
+
+	if (!read_line_int(governor_file->str, &performance)) {
+		ERROR("Could not read up_threshold");
+		return -1;
+	}
+	g_string_free(governor_file, TRUE);
+
+	return performance;
+}
+
+static gboolean ondemand_set_consider_nice(void *data, gboolean consider)
+{
+	struct ondemand_interface	*iface	       = data;
+	GString				*consider_file = g_string_new("");
+
+	g_string_printf(consider_file, ONDEMAND_IGNORE_NICE_LOAD_FILE, iface->base_cpu); 
+
+        if(!write_line(consider_file->str, "%u", consider)){
+                ERROR("Could not set ignore_nice_load to: %u kHz; %s", consider,
+		      strerror(errno));
+                return FALSE;
+        }
+	dbg("Set consider nice to %d for ondemand", consider);
+	g_string_free(consider_file, TRUE);
+
+	return TRUE;
+}
+
+static gboolean ondemand_get_consider_nice(void)
+{
+	GString		*governor_file	= g_string_new("");
+	gboolean	consider	= -1;
+
+	/* only read the setting of cpu0 */
+	g_string_printf(governor_file, ONDEMAND_IGNORE_NICE_LOAD_FILE, 0); 
+
+	if (!read_line_int(governor_file->str, &consider)) {
+		ERROR("Could not read ignore_nice_load file");
+		return -1;
+	}
+	g_string_free(governor_file, TRUE);
+
+	return consider;
+}
+
+static gboolean ondemand_init(struct ondemand_interface *iface, GSList *cores)
+{
+	if (iface == NULL)
+		return FALSE;
+
+	if (!write_governor(ONDEMAND_STRING, (int)cores->data)) {
+		ERROR("Could not set ondemand governor.");
+		return FALSE;
+	}
+
+	iface->base_cpu = (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)
+{
+	GString	*agovs_file = g_string_new("");
+
+	g_string_printf(agovs_file, SYSFS_AVAILABLE_GOVERNORS_FILE, 0); 
+	*governors = read_line_str_split(agovs_file, " ");
+	g_string_free(agovs_file, TRUE);
+
+	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");
+		ERROR("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");
+		ERROR("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);
+				dbg("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);
+				dbg("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,
+					    (int)((GSList*)it->data)->data)) {
+				dbus_raise_governor_init_failed(connection, message,
+								(char*)governor);
+				ERROR("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)
+{
+	GString	*governor_file	= g_string_new("");
+	int	cpu_id		= 0;
+
+	g_string_printf(governor_file, SYSFS_GOVERNOR_FILE, cpu_id); 
+
+	if (!read_line(governor_file->str, governor, MAX_LINE_SIZE)) {
+		dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+				 "Could not read current governor");
+		return FALSE;
+	}
+	g_string_free(governor_file, TRUE);
+
+	/* 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;
+	GString		*error			= g_string_new("");
+
+	if (connection == NULL || message == NULL)
+		return FALSE;
+
+	va_start(args, format);
+	vsnprintf(buf, sizeof buf, format, args);
+	va_end(args);
+
+	g_string_printf(error, "%s.%s", DBUS_INTERFACE, error_name);
+	reply = dbus_message_new_error(message, error->str, buf);
+	g_string_free(error, TRUE);
+	if (reply == NULL) {
+		ERROR("No memory");
+		return FALSE;
+	}
+
+	if (!dbus_connection_send(connection, reply, NULL)) {
+		ERROR("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;
+	GString			*caller_unix_user_str	= g_string_new("");
+	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);
+	dbg("Connection name of caller: %s", caller_dbus_name);
+	dbg("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;
+	}
+
+	g_string_printf(caller_unix_user_str, "%ld", caller_unix_user);
+	res = libpolkit_is_uid_allowed_for_privilege(polctx,
+						     caller_dbus_name,
+						     caller_unix_user_str->str,
+						     CPUFREQ_POLKIT_PRIVILEGE,
+						     getenv("UDI"),
+						     &out_is_allowed,
+						     &out_is_temporary,
+						     NULL);
+	g_string_free(caller_unix_user_str, TRUE);
+
+	if (res != LIBPOLKIT_RESULT_OK) {
+		dbus_raise_error(connection, message, CPUFREQ_ERROR_GENERAL,
+				 "Cannot lookup privilege: %d", res);
+		goto Error;
+	}
+	
+	if (!out_is_allowed) {
+		dbg("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;
+	}
+
+	dbg("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) {
+		ERROR("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)) {
+		ERROR("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;
+	int	    i;
+
+	if ((reply = dbus_message_new_method_return(message)) == NULL) {
+		ERROR("Could not allocate memory for the DBus reply");
+		return FALSE;
+	}
+
+	for (i = 0; list[i] != NULL; i++)
+		dbus_message_append_args(reply, DBUS_TYPE_STRING, &list[i], DBUS_TYPE_INVALID);			
+
+	if (!dbus_connection_send(connection, reply, NULL)) {
+		ERROR("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)) {
+		ERROR("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);
+
+	dbg("Received DBus message with member %s", member);
+	dbg("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;
+		}
+ 		dbg("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;
+		}
+ 		dbg("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;
+		}
+ 		dbg("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")) {
+		dbg("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.SystemPowerManagement.method_names",
+		      method,
+		      NULL);
+	libhal_device_property_strlist_append(halctx,
+		      "/org/freedesktop/Hal/devices/computer",
+		      "org.freedesktop.Hal.Device.SystemPowerManagement.method_signatures",
+		      signature,
+		      NULL);
+}
+
+/** 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;
+	GString		*governor_file	= g_string_new("");
+
+	dbus_error_init(&dbus_error);
+
+	if ((halctx = libhal_ctx_init_direct(&dbus_error)) == NULL) {
+		ERROR("Cannot connect to hald");
+		goto Error;
+	}
+
+	if ((dbus_connection = libhal_ctx_get_dbus_connection(halctx)) == NULL) {
+		ERROR("Cannot get DBus connection");
+		goto Error;
+	}
+
+	if (!libhal_device_claim_interface(halctx, udi,
+		"org.freedesktop.Hal.Device.SystemPowerManagement", 
+		"    <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=\"strlist\"/>"
+		"    </method>",
+		&dbus_error)) {
+
+		ERROR("Cannot claim interface: %s", dbus_error.message);
+		fprintf(stderr, "direct Cannot claim interface: %s", dbus_error.message);
+		goto Error;
+	}
+
+	g_string_printf(governor_file, SYSFS_GOVERNOR_FILE, 0); 
+	if (access(governor_file->str, F_OK) == 0) {
+		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", "");
+	}
+	g_string_free(governor_file, TRUE);
+
+	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);
+
+	dbg("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 ((getenv("HALD_VERBOSE")) != NULL)
+		is_verbose = TRUE;
+
+	if (dbus_init())
+		ERROR("Could not establish DBus connection");
+
+	gmain = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(gmain);
+
+	_set_debug ();
+	drop_privileges(0);
+	char *ar[] = { };
+	hal_set_proc_title_init (0, ar);
+	hal_set_proc_title ("");
+
+	return 0;
+}
diff --git a/hald/linux2/addons/addon-cpufreq.h b/hald/linux2/addons/addon-cpufreq.h
new file mode 100644
index 0000000..43a6f86
--- /dev/null
+++ b/hald/linux2/addons/addon-cpufreq.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ *                                                                         *
+ *                            addon-cpufreq.h                              *
+ *                                                                         *
+ *              Copyright (C) 2006 SUSE Linux Products GmbH                *
+ *                                                                         *
+ *               Author(s): Holger Macht <hmacht at 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>
+#include "../probing/shared.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
+
+#define ERROR(string, args...) dbg(string, ## args)
+
+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	(GString *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/hald/linux2/addons/addon-hid-ups.c b/hald/linux2/addons/addon-hid-ups.c
diff --git a/hald/linux2/addons/addon-keyboard.c b/hald/linux2/addons/addon-keyboard.c
diff --git a/hald/linux2/addons/addon-macbookpro-backlight.c b/hald/linux2/addons/addon-macbookpro-backlight.c
diff --git a/hald/linux2/addons/addon-pmu.c b/hald/linux2/addons/addon-pmu.c
diff --git a/hald/linux2/addons/addon-storage.c b/hald/linux2/addons/addon-storage.c
diff --git a/hald/linux2/addons/addon-usb-csr.c b/hald/linux2/addons/addon-usb-csr.c


More information about the hal mailing list