Hi,

CPU frequency scaling is the last remaining important power management
technology that should be controllable from the desktop IMO, but that's
not possible yet. Or there's at least no common interface/ software
everybody agrees on.

Richard already proposed something similar in the past. His idea was to
control the different frequencies a CPU supports from higher level
applications on the desktop. His proposal was rejected, though.

So I'm doing a second apprach and I've got a different way of solving the
problem. It's implemented as a HAL addon which summarizes all cpufreq
capable CPUs into six DBus methods on the
org.freedesktop.Hal.Device.SystemPowerManagement interface.

Addon-cpufreq supports kernel governors and also implements a userspace
controlling mechanism. That makes it the "all you need for CPUFreq"
application. Furthermore, to not make things unnecessarily complicated for
desktop applications, it is supposed to abstract all the different
settings you can make for the different governors. One thing which isn't
available yet, is the possibility to control the performance of
dynamically scaled CPUs. There's a method to set the performance (from 1
to 100) which can be easily used from other applications with showing a
nice progress bar or the like. That makes it unique in contrast to former
solutions.

The biggest advantage is that you get CPUFreq out of the box on every
system supporting HAL without the need to install another daemon. Another
one is that desktop applications like gnome-power-manager or kpowersave
can use one common interface after all.

It claims the following DBus interfaces on the
/org/freedesktop/Hal/devices/computer device:


Set a specific CPU frequency governor
-------------------------------------
  Interface: org.freedesktop.Hal.Device.SystemPowerManagement
  Member   : "SetCPUFreqGovernor"
  Type     : Method call with return

  Param:
    string : The governor to be set. This can be any arbitary string. Also
             governors which are not known can be set

  Return type:
    int    : 0 on success, 1 on error

Set the performance independently from the current governor
-----------------------------------------------------------
  Interface: org.freedesktop.Hal.Device.SystemPowerManagement
  Member   : "SetCPUFreqPerformance"
  Type     : Method call with return

  Param:
    int    : Value between 1 and 100. The higher the value the more
             performance you get. 50 is default and should be sufficient
             in most cases

  Return type:
    int    : 0 on success, 1 on error or if performance settings are not
             supported for the current governor

Set if niced processes should be consider when calculating CPU load
--------------------------------------------------------------------
  Interface: org.freedesktop.Hal.Device.SystemPowerManagement
  Member   : "SetCPUFreqConsiderNice"
  Type     : Method call with return

  Param:
    boolean: True if niced processes should be considered, false otherwise

  Return type:
    int    : 0 on success, 1 on error or if this setting is not supported
             with the current governor

Get the current active governor
-------------------------------
  Interface: org.freedesktop.Hal.Device.SystemPowerManagement
  Member   : "GetCPUFreqGovernor"
  Type     : Method call with return

  Return type:
    string : The current active governor

Get the current performance setting
-----------------------------------
  Interface: org.freedesktop.Hal.Device.SystemPowerManagement
  Member   : "GetCPUFreqPerformance"
  Type     : Method call with return

  Return type:
    int    : The current performance setting from 1 to 100

Get the current consider nice setting
-------------------------------------
  Interface: org.freedesktop.Hal.Device.SystemPowerManagement
  Member   : "GetCPUFreqConsiderNice"
  Type     : Method call with return

  Return type:
    boolean: True if niced processes are considered, false otherwise



The addon basically consists of five parts:

  1. One interface which contains all methods which are common for all
     governors (see DBus interfaces above)

  2. Some helper functions

  3. The userspace governor implementation with the CPULoad calculation
     mechanism

  4. The ondemand governor implementation

  5. DBus/HAL integration

Current addons only consist of one source file. However, I don't think
that makes sense for addon-cpufreq. If this would be a independent
project, I would have separated these parts into more different files. But
for now, I've just put the userspace part into two files (header, source)
and the remaining parts into another two files (header, source). It would
of course be possible to only have one source file and thus get rid of the
header files if this is desired.

I've appended a somewhat patch to accomplish all this. Please comment...

If you guys don't agree that this could be added to HAL as an addon, maybe
the above mentioned interfaces can be implemented by installing some
scripts like hal-system-power-cpufreq-set-governor. Then I'm trying to get
org.freedesktop.CPUFreq for a separate daemon and can call the DBus
methods inside them. All other people can then of course do whatever they
like in those scripts.


Regards,
        Holger
diff -uNr -x CVS hal/fdi/policy/10osvendor/10-power-mgmt-policy.fdi hal.new/fdi/policy/10osvendor/10-power-mgmt-policy.fdi
--- hal/fdi/policy/10osvendor/10-power-mgmt-policy.fdi	2006-06-07 01:56:09.000000000 +0200
+++ hal.new/fdi/policy/10osvendor/10-power-mgmt-policy.fdi	2006-07-05 16:06:23.000000000 +0200
@@ -52,5 +52,12 @@
       <append key="org.freedesktop.Hal.Device.SystemPowerManagement.method_execpaths" type="strlist">hal-system-power-set-power-save</append>
     </match>
   </device>
+
+  <device>
+   <match key="info.udi" string="/org/freedesktop/Hal/devices/computer">
+      <append key="info.addons" type="strlist">hald-addon-cpufreq</append>
+    </match>
+  </device>
+
       
 </deviceinfo>
diff -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq.c hal.new/hald/linux2/addons/addon-cpufreq.c
--- hal/hald/linux2/addons/addon-cpufreq.c	1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq.c	2006-07-05 16:03:12.000000000 +0200
@@ -0,0 +1,900 @@
+/***************************************************************************
+ *                                                                         *
+ *                            addon-cpufreq.c                              *
+ *                                                                         *
+ *              Copyright (C) 2006 SUSE Linux Products GmbH                *
+ *                                                                         *
+ *               Author(s): Holger Macht <[EMAIL PROTECTED]>                  *
+ *                                                                         *
+ * 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 <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"
+
+#define MAX_LINE_SIZE		255
+#define PROC_CPUINFO_FILE	"/proc/cpuinfo"
+#define DBUS_INTERFACE "org.freedesktop.Hal.Device.SystemPowerManagement"
+
+
+const char SYSFS_GOVERNOR_FILE[] =
+     "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor";
+
+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";
+
+/** list holding all cpufreq objects (userspace, ondemand, etc.) */
+static GSList *cpufreq_objs = NULL;
+
+/******************** helper functions **********************/
+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;
+}
+
+/** @brief returns the number of CPUs
+ *
+ * @return number of CPUs or < 0 on error
+ * @retval > 0 number of CPUs
+ * @retval -1 general error
+ * @retval -2 no CPUs found
+ */
+int get_cpu_count(void)
+{
+	FILE	*fp;
+	char	line[256];
+	int	count = 0;
+
+	fp = fopen(PROC_CPUINFO_FILE, "r");
+	if (fp == NULL) {
+		ERROR("Could not open %s: %s", PROC_CPUINFO_FILE, strerror(errno));
+		return -1;
+	}
+
+	/* search how many lines start with processor in /proc/cpuinfo */
+	while (fgets(line, MAX_LINE_SIZE, fp) != NULL) {
+		if (!memcmp(line, "processor", 9))
+			count++;
+	}
+	fclose(fp);
+
+	if (count > 0)
+		return count;
+	else {
+		ERROR("No processor found");
+		return -2;
+	}
+}
+
+/** reads one line from filename, splits its integers by delim and stores
+ * all items in the given list */
+gboolean read_line_split(GString *filename, gchar delim, GSList **list)
+{
+	gchar	line[MAX_LINE_SIZE];
+	gchar	**l;
+	int	i;
+
+        if(!read_line(filename->str, line, MAX_LINE_SIZE)){
+		ERROR("Could not read splitted line from %s", filename->str);
+		return FALSE;
+	}
+
+	/* strip trailing '\n' */
+	line[strlen(line)-1] = '\0';
+	
+	l = g_strsplit(line, &delim, MAX_LINE_SIZE);
+
+	for (i = 0; l[i] != NULL; i++) {
+		int value = atoi(l[i]);
+
+		if (g_strcasecmp(l[i], "") != 0) {
+			*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_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 *********************/
+static gboolean set_performance(int performance)
+{
+	float steps;
+	float up_threshold;
+
+	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;
+	}
+
+	GSList *it = NULL;
+	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;
+}
+
+static int get_performance(void)
+{
+	struct cpufreq_obj *obj;
+	float		   steps;
+	float		   performance;
+	int		   up_threshold;
+
+	if (cpufreq_objs == NULL)
+		return -1;
+	obj = cpufreq_objs->data;
+
+	up_threshold = obj->get_performance();
+
+	if (up_threshold < UP_THRESHOLD_BASE) {
+		steps = UP_THRESHOLD_BASE - UP_THRESHOLD_MIN + 1;
+		performance = (((UP_THRESHOLD_BASE) - up_threshold) /
+			       (steps / 51.0)) + 50.0;
+	} else if (up_threshold >= UP_THRESHOLD_BASE) {
+		steps = UP_THRESHOLD_MAX - UP_THRESHOLD_BASE;
+		performance = ((UP_THRESHOLD_MAX + 1) - up_threshold) /
+			(steps / 49.0);
+	}
+
+	return (int)performance;
+}
+
+static gboolean set_consider_nice(gboolean consider)
+{
+	GSList *it = NULL;
+	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;
+}
+
+static gboolean get_consider_nice(void)
+{
+	struct cpufreq_obj *obj;
+
+	if (cpufreq_objs == NULL)
+		return -1;
+	obj = cpufreq_objs->data;
+	return obj->get_consider_nice();
+}
+
+static gboolean set_governors(const char *governor)
+{
+	GSList	*cpus		= NULL;
+	GSList  *it		= NULL;
+	int	num_cpus;
+	static int g_source_id  = -1;
+
+	/** 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 = get_cpu_count();
+	if (num_cpus < 0) {
+		ERROR("No CPUs found in system");
+		return FALSE;
+	}
+
+	if (!get_cpu_dependencies(&cpus, num_cpus)) {
+		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
+				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
+				return FALSE;
+		}
+	} else {
+		for (it = cpus; it != NULL; it = g_slist_next(it)) {
+			if (!write_governor((char*)governor,
+					    (int)((GSList*)it->data)->data)) {
+				ERROR("Could not set %s governor.", governor);
+				return FALSE;
+			}
+		}
+	}
+	
+	set_performance(DEFAULT_PERFORMANCE);
+
+	return TRUE;
+}
+
+static gboolean get_governors(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)) {
+		ERROR("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 *********************/
+static gboolean dbus_send_reply_int(DBusConnection *connection, DBusMessage *message,
+				    int result)
+{
+	DBusMessage *reply;
+
+	if ((reply = dbus_message_new_method_return(message)) == NULL) {
+		ERROR("Could not allocate memory for the DBus reply");
+		return FALSE;
+	}
+
+	dbus_message_append_args(reply, DBUS_TYPE_INT32, &result, 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;
+}
+
+static gboolean dbus_send_reply_string(DBusConnection *connection, DBusMessage *message,
+				       char *result)
+{
+	DBusMessage *reply;
+
+	if ((reply = dbus_message_new_method_return(message)) == NULL) {
+		ERROR("Could not allocate memory for the DBus reply");
+		return FALSE;
+	}
+
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, &result, 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;
+}
+
+static gboolean dbus_get_argument_int(DBusMessage *message, DBusError *dbus_error,
+				      int *arg)
+{
+	dbus_message_get_args(message, dbus_error, DBUS_TYPE_INT32, arg,
+			      DBUS_TYPE_INVALID);
+	if (dbus_error_is_set(dbus_error)) {
+		ERROR("Could not get argument of DBus message: %s",
+		      dbus_error->message);
+		dbus_error_free(dbus_error);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean dbus_get_argument_bool(DBusMessage *message, DBusError *dbus_error,
+				       gboolean *arg)
+{
+	dbus_message_get_args(message, dbus_error, DBUS_TYPE_BOOLEAN, arg, 
+			      DBUS_TYPE_INVALID);
+	if (dbus_error_is_set(dbus_error)) {
+		ERROR("Could not get argument of DBus message: %s",
+		      dbus_error->message);
+		dbus_error_free(dbus_error);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean dbus_get_argument_string(DBusMessage *message, DBusError *dbus_error,
+					 char **arg)
+{
+	dbus_message_get_args(message, dbus_error, DBUS_TYPE_STRING, arg,
+			      DBUS_TYPE_INVALID);
+	if (dbus_error_is_set(dbus_error)) {
+		ERROR("Could not get argument of DBus message: %s",
+		      dbus_error->message);
+		dbus_error_free(dbus_error);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+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);
+	if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+					"SetCPUFreqGovernor")) {
+		char *arg;
+
+		if (!dbus_get_argument_string(message, &dbus_error, &arg))
+			return DBUS_HANDLER_RESULT_HANDLED;
+
+ 		dbg("Received argument: %s", arg);
+
+		int err_id_u16 = !set_governors(arg);
+		dbg("Sent reply %d\n", err_id_u16);
+		dbus_send_reply_int(connection, message, err_id_u16);
+
+	} else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+					       "SetCPUFreqPerformance")) {
+		int arg;
+
+		if (!dbus_get_argument_int(message, &dbus_error, &arg))
+			return DBUS_HANDLER_RESULT_HANDLED;
+
+ 		dbg("Received argument: %d", arg);
+
+		int err_id_u16 = !set_performance(arg);
+		dbus_send_reply_int(connection, message, err_id_u16);
+
+	} else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+					       "SetCPUFreqConsiderNice")) {
+		gboolean arg;
+
+		if (!dbus_get_argument_bool(message, &dbus_error, &arg))
+			return DBUS_HANDLER_RESULT_HANDLED;
+
+ 		dbg("Received argument: %d", arg);
+
+		int err_id_u16 = !set_consider_nice(arg);
+		dbus_send_reply_int(connection, message, err_id_u16);
+
+	} else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+					       "GetCPUFreqGovernor")) {
+		char governor[MAX_LINE_SIZE + 1];
+
+		if (!get_governors(governor)) 
+			return DBUS_HANDLER_RESULT_HANDLED;
+
+		dbus_send_reply_string(connection, message, governor);
+
+	} else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+					       "GetCPUFreqPerformance")) {
+		dbus_send_reply_int(connection, message, get_performance());
+
+	} else if (dbus_message_is_method_call(message, DBUS_INTERFACE,
+					       "GetCPUFreqConsiderNice")) {
+		dbus_send_reply_int(connection, message, get_consider_nice());
+	} 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);
+	}
+
+	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>",
+		&dbus_error)) {
+
+		ERROR("Cannot claim interface");
+		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", "");
+	}
+	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_bus_add_match(dbus_connection,
+			   "type='method_call',"
+			   "interface='" DBUS_INTERFACE "',", 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 (dbus_init())
+		ERROR("Could not establish DBus connection");
+
+	if ((getenv("HALD_VERBOSE")) != NULL)
+		is_verbose = TRUE;
+
+	gmain = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(gmain);
+
+	return 0;
+}
diff -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq.h hal.new/hald/linux2/addons/addon-cpufreq.h
--- hal/hald/linux2/addons/addon-cpufreq.h	1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq.h	2006-07-05 16:03:15.000000000 +0200
@@ -0,0 +1,77 @@
+/***************************************************************************
+ *                                                                         *
+ *                            addon-cpufreq.h                              *
+ *                                                                         *
+ *              Copyright (C) 2006 SUSE Linux Products GmbH                *
+ *                                                                         *
+ *               Author(s): Holger Macht <[EMAIL PROTECTED]>                  *
+ *                                                                         *
+ * 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_split		(GString *filename,
+					 gchar delim,
+					 GSList **list);
+
+int		get_cpu_count		(void);
+
+gboolean	cpu_online		(int cpu_id);
+
+gboolean	write_governor		(char *new_governor,
+					 int cpu_id);
+
+gboolean	dbus_init		(void);
+
+#endif /* ADDON_CPUFREQ_H */
diff -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq-userspace.c hal.new/hald/linux2/addons/addon-cpufreq-userspace.c
--- hal/hald/linux2/addons/addon-cpufreq-userspace.c	1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq-userspace.c	2006-07-05 16:03:21.000000000 +0200
@@ -0,0 +1,528 @@
+/***************************************************************************
+ *                                                                         *
+ *                      addon-cpufreq-userspace.c                          *
+ *                                                                         *
+ *              Copyright (C) 2006 SUSE Linux Products GmbH                *
+ *                                                                         *
+ *              Author(s): Holger Macht <[EMAIL PROTECTED]>                   *
+ *                         Speed adjustments based on code by              *
+ *                           Thomas Renninger <[EMAIL PROTECTED]>              *
+ *                                                                         *
+ * 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 = get_cpu_count();
+	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_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 -uNr -x CVS hal/hald/linux2/addons/addon-cpufreq-userspace.h hal.new/hald/linux2/addons/addon-cpufreq-userspace.h
--- hal/hald/linux2/addons/addon-cpufreq-userspace.h	1970-01-01 01:00:00.000000000 +0100
+++ hal.new/hald/linux2/addons/addon-cpufreq-userspace.h	2006-07-05 16:03:25.000000000 +0200
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *                                                                         *
+ *                      addon-cpufreq-userspace.h                          *
+ *                                                                         *
+ *              Copyright (C) 2006 SUSE Linux Products GmbH                *
+ *                                                                         *
+ *               Author(s): Holger Macht <[EMAIL PROTECTED]>                  *
+ *                                                                         *
+ * 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 -uNr -x CVS hal/hald/linux2/addons/Makefile.am hal.new/hald/linux2/addons/Makefile.am
--- hal/hald/linux2/addons/Makefile.am	2006-06-09 04:19:32.000000000 +0200
+++ hal.new/hald/linux2/addons/Makefile.am	2006-07-05 16:03:05.000000000 +0200
@@ -15,7 +15,8 @@
 	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 @@
 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 @PACKAGE_LIBS@ 
+
 hald_addon_hid_ups_SOURCES = addon-hid-ups.c
 hald_addon_hid_ups_LDADD = $(top_builddir)/libhal/libhal.la
 
_______________________________________________
powersave-devel mailing list
[email protected]
http://forge.novell.com/mailman/listinfo/powersave-devel

Reply via email to