[patch] pmu patch

Sjoerd Simons sjoerd at luon.net
Tue Feb 1 10:52:46 PST 2005


Hi,

  Attached path enabled hal to read the various sensors and battery information
  on apple machines. 

  On the todo list is to convert it to using the hal utility functions instead
  of the nice glib ones where possible. Unfortunately the current one needs some
  adaption to work nicely for the pmu implementation..
  
  The biggest issue is that you i really need to get all the pmu battery
  information in one read to ensure sane information (i guess acpi does need
  this too, but it doesn't right now).

  Other issues, battery.charge_level.maximum_specified is mandatory (i can't
  get that info with pmu) but battery.charge_level.maximum_real is available
  (which i can get).. battery.charge_level.unit can be set to a physical unit,
  but isn't it better to agree it's always a certain uni (always mWh for
  example and let hal do the conversion instead of requiring every app to do
  it)

  There is no key for remaining time, the kernel knows how to calculate this 
  for pmu and apm (dunno about acpi).. Would be usefull to create a key for
  that. Also the pmu delivers voltage and current information, which i can't
  place in a standard specified key..

  The code also reads the sensors avaible from the thermal control units modern
  apple laptops and powermacs have (maybe others too, dunno).. I've placed it
  in pmu file too, although it isn't really a pmu thing.. 
  
  Biggest problem i'm seeing here with there is no key indicated for the value
  (i'm using system.sensor.value in my code currently). And there is no
  indication in which units these are. Imho it's best to choose standard units
  and let hal do the conversion when needed, just so higher leven applications
  don't have to do this..

  So after all this now for the fun part, a screenshot: 
    http://luon.net/~sjoerd/hal/hal_cpu_temperature.png


  The keyboard light sensor and lid button stuff for which i need callouts will
  hopefully come later this week. 

  Sjoerd
-- 
Expansion means complexity; and complexity decay.
-------------- next part --------------
? indent.pro
Index: hald/linux2/Makefile.am
===================================================================
RCS file: /cvs/hal/hal/hald/linux2/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- hald/linux2/Makefile.am	1 Feb 2005 05:17:55 -0000	1.3
+++ hald/linux2/Makefile.am	1 Feb 2005 18:14:50 -0000
@@ -21,6 +21,7 @@
 	blockdev.h		blockdev.c			\
 	util.h			util.c				\
 	acpi.h			acpi.c				\
+	pmu.h			pmu.c				\
 	ids.h			ids.c				\
 	pcmcia_utils.h		pcmcia_utils.c
 
Index: hald/linux2/osspec.c
===================================================================
RCS file: /cvs/hal/hal/hald/linux2/osspec.c,v
retrieving revision 1.4
diff -u -r1.4 osspec.c
--- hald/linux2/osspec.c	1 Feb 2005 05:17:55 -0000	1.4
+++ hald/linux2/osspec.c	1 Feb 2005 18:14:51 -0000
@@ -80,6 +80,7 @@
 #include "ids.h"
 
 #include "acpi.h"
+#include "pmu.h"
 
 char hal_sysfs_path [HAL_PATH_MAX];
 char hal_proc_path [HAL_PATH_MAX];
@@ -476,6 +477,9 @@
 	/* ACPI */
 	acpi_probe ();
 
+  /* PMU */
+  pmu_probe ();
+
 	/*osspec_probe_done ();*/
 }
 
Index: hald/linux2/pmu.c
===================================================================
RCS file: hald/linux2/pmu.c
diff -N hald/linux2/pmu.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ hald/linux2/pmu.c	1 Feb 2005 18:14:51 -0000
@@ -0,0 +1,411 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * Copyright (C) 2005 Sjoerd Simons <sjoerd at luon.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+
+#include "../callout.h"
+#include "../logger.h"
+#include "../hald_dbus.h"
+#include "../hald.h"
+
+#include "util.h"
+
+#include "pmu.h"
+
+#define COMPUTER_CHILD(d) hal_device_property_set_string (d, "info.parent", "/org/freedesktop/Hal/devices/computer");
+
+typedef enum {
+	HAL_SENSOR_LIGHT,
+	HAL_SENSOR_FAN,
+	HAL_SENSOR_TEMPERATURE,
+	HAL_SENSOR_VOLTAGE,
+	HAL_SENSOR_CURRENT,
+} sensor_type;
+
+static gchar *sensor_type_names[] = {
+	"light",
+	"fanspeed",
+	"temperature",
+	"voltage",
+	"current",
+};
+
+static HalDevice *
+hal_add_sensor (sensor_type type, gchar * location, int value) {
+	HalDevice *d;
+	gchar *s;
+
+	d = hal_device_new ();
+
+	s = g_strdup_printf ("/org/freedesktop/Hal/devices/%s_%s", location, sensor_type_names[type]);
+	hal_device_set_udi (d, s);
+	hal_device_property_set_string (d, "info.udi", s);
+	g_free (s);
+
+	s = g_strdup_printf ("%s %s sensor", location, sensor_type_names[type]);
+	hal_device_property_set_string (d, "info.product", s);
+	g_free (s);
+
+	s = g_strdup_printf ("%s sensor", sensor_type_names[type]);
+	hal_device_property_set_string (d, "info.category", s);
+	hal_device_add_capability (d, "system.sensor");
+	COMPUTER_CHILD (d);
+
+	if (location) {
+		hal_device_property_set_string (d, "system.sensor.location", location);
+	} else {
+		hal_device_property_set_string (d, "system.sensor.location", "unknown");
+	}
+	hal_device_property_set_string (d, "system.sensor.type", sensor_type_names[type]);
+	hal_device_property_set_int (d, "system.sensor.value", value);
+
+	return d;
+}
+
+static gboolean
+pmu_sensor_update_value (HalDevice * d) {
+	const char *path;
+	gchar *contents;
+	GError *error = NULL;
+	gint value;
+
+	path = hal_device_property_get_string (d, "linux.sysfs_path");
+	if (path == NULL) {
+		HAL_WARNING (("Missing sysfs path on %s", hal_device_get_udi (d)));
+		return FALSE;
+	}
+
+	if (!g_file_get_contents (path, &contents, NULL, &error)) {
+		HAL_WARNING (("Couldn't get contents of %s: %s", path, error->message));
+		g_error_free (error);
+		error = NULL;
+		return FALSE;
+	}
+	g_strstrip (contents);
+	value = atoi (contents);
+
+	hal_device_property_set_int (d, "system.sensor.value", value);
+	return TRUE;
+}
+
+static gboolean
+pmu_update_sensor_cb (gchar * data) {
+	HalDevice *d = NULL;
+
+	d = hal_device_store_find (hald_get_gdl (), data);
+	if (d == NULL) {
+		goto failed;
+	}
+	if (!pmu_sensor_update_value (d)) {
+		goto failed;
+	}
+	return TRUE;
+
+      failed:
+	HAL_WARNING (("Reading data from sensor %s failed!", data));
+	if (d) {
+		hal_device_store_remove (hald_get_gdl (), d);
+	}
+	g_free (data);
+	return FALSE;
+}
+
+static void
+probe_pmu_sensors (void) {
+#define SENSORDIR "/sys/devices/temperatures"
+	GDir *dir;
+	GError *error = NULL;
+	const gchar *name;
+	HalDevice *d;
+
+	dir = g_dir_open (SENSORDIR, 0, &error);
+	if (dir == NULL) {
+		g_warning ("Opening sensors dir failed: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+	while ((name = g_dir_read_name (dir)) != NULL) {
+		gchar *path;
+
+		path = g_build_filename (SENSORDIR, name, NULL);
+		/* special case to ignore the specified_fan_speed file */
+		if (strcmp (name, "specified_fan_speed") && g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
+			int i = 0;
+			struct sensor_type {
+				char *suffix;
+				sensor_type type;
+			} types[] = {
+				{
+				"_fan_speed", HAL_SENSOR_FAN}, {
+				"_fan_rpm", HAL_SENSOR_FAN}, {
+				"_temperature", HAL_SENSOR_TEMPERATURE}, {
+				"_voltage", HAL_SENSOR_VOLTAGE}, {
+				"_current", HAL_SENSOR_CURRENT}, {
+				NULL}
+			};
+
+			while (types[i].suffix != NULL) {
+				if (g_str_has_suffix (name, types[i].suffix)) {
+					gchar *location;
+
+					location = g_strdup (name);
+					location[strlen (location) - strlen (types[i].suffix)] = '\0';
+					d = hal_add_sensor (types[i].type, location, 0);
+					g_free (location);
+
+					hal_device_property_set_string (d, "linux.sysfs_path", path);
+
+					if (pmu_sensor_update_value (d)) {
+						hal_device_store_add (hald_get_gdl (), d);
+						g_timeout_add (2000, (GSourceFunc) pmu_update_sensor_cb,
+							       g_strdup (hal_device_get_udi (d)));
+					} else {
+						g_object_unref (d);
+					}
+
+					break;
+				}
+				i++;
+			}
+		}
+		g_free (path);
+	}
+}
+
+static void
+hal_add_ac_adapter (int number, gchar * procfspath, gboolean on_ac) {
+	HalDevice *d;
+	gchar *s;
+
+	d = hal_device_new ();
+
+	s = g_strdup_printf ("/org/freedesktop/Hal/devices/ac_adapter_%d", number);
+	hal_device_set_udi (d, s);
+	hal_device_property_set_string (d, "info.udi", s);
+	g_free (s);
+	COMPUTER_CHILD (d);
+
+	hal_device_add_capability (d, "system.ac_adapter");
+	hal_device_property_set_string (d, "info.product", "AC Adapter");
+	hal_device_property_set_bool (d, "system.adaptor.present", on_ac);
+	if (procfspath) {
+		hal_device_property_set_string (d, "linux.procfs_path", procfspath);
+	}
+	hal_device_store_add (hald_get_gdl (), d);
+}
+
+static gboolean
+pmu_battery_update_info (HalDevice * d) {
+/* defines from the kernel PMU driver */
+#define PMU_BATT_PRESENT  0x00000001
+#define PMU_BATT_CHARGING 0x00000002
+#define PMU_BATT_TYPE_MASK  0x000000f0
+#define PMU_BATT_TYPE_SMART 0x00000010	/* Smart battery */
+#define PMU_BATT_TYPE_HOOPER  0x00000020	/* 3400/3500 */
+#define PMU_BATT_TYPE_COMET 0x00000030	/* 2400 */
+	const gchar *path;
+	gint flags = 0;
+	gint charge = 0;
+	gint max_charge = 0;
+	gint current = 0;
+	gint voltage = 0;
+	gint time_remaining = 0;
+	GIOChannel *io;
+	GString *line;
+	gchar **kv;
+
+	path = hal_device_property_get_string (d, "linux.procfs_path");
+	if (path == NULL) {
+		HAL_WARNING (("Missing procfs path on %s", hal_device_get_udi (d)));
+		return FALSE;
+	}
+
+	io = g_io_channel_new_file (path, "r", NULL);
+	if (!io) {
+		return FALSE;
+	}
+	line = g_string_sized_new (100);
+	while (g_io_channel_read_line_string (io, line, NULL, NULL) == G_IO_STATUS_NORMAL) {
+		kv = g_strsplit (line->str, ":", 2);
+		if (kv[1] == NULL) {
+			g_strfreev (kv);
+			continue;
+		}
+		g_strstrip (kv[0]);
+		g_strstrip (kv[1]);
+		if (!g_ascii_strcasecmp ("flags", kv[0])) {
+			flags = strtol (kv[1], NULL, 16);
+		} else if (!g_ascii_strcasecmp ("charge", kv[0])) {
+			charge = strtol (kv[1], NULL, 10);
+		} else if (!g_ascii_strcasecmp ("max_charge", kv[0])) {
+			max_charge = strtol (kv[1], NULL, 10);
+		} else if (!g_ascii_strcasecmp ("current", kv[0])) {
+			current = strtol (kv[1], NULL, 10);
+		} else if (!g_ascii_strcasecmp ("voltage", kv[0])) {
+			voltage = strtol (kv[1], NULL, 10);
+		} else if (!g_ascii_strcasecmp ("time rem.", kv[0])) {
+			time_remaining = strtol (kv[1], NULL, 10);
+		}
+		g_strfreev (kv);
+	}
+	g_string_free (line, TRUE);
+
+	device_property_atomic_update_begin ();
+	hal_device_property_set_bool (d, "battery.present", flags & PMU_BATT_PRESENT);
+	if (flags & PMU_BATT_PRESENT) {
+		hal_device_property_set_bool (d, "battery.is_rechargeable", TRUE);
+		hal_device_property_set_bool (d, "battery.rechargeable.is_charging", flags & PMU_BATT_CHARGING);
+		hal_device_property_set_bool (d, "battery.rechargeable.is_discharging", current < 0);
+
+		hal_device_property_set_string (d, "battery.charge_level.unit", "unknown");
+		hal_device_property_set_int (d, "battery.charge_level.current", charge);
+		hal_device_property_set_int (d, "battery.charge_level.maximum_real", max_charge);
+	} else {
+		hal_device_property_remove (d, "battery.is_rechargeable");
+		hal_device_property_remove (d, "battery.rechargeable.is_charging");
+		hal_device_property_remove (d, "battery.rechargeable.is_discharging");
+
+		hal_device_property_remove (d, "battery.charge_level.unit");
+		hal_device_property_remove (d, "battery.charge_level.current");
+		hal_device_property_remove (d, "battery.charge_level.maximum_real");
+	}
+	device_property_atomic_update_end ();
+	return TRUE;
+}
+
+static gboolean
+pmu_update_battery_cb (gchar * data) {
+	HalDevice *d = NULL;
+
+	d = hal_device_store_find (hald_get_gdl (), data);
+	if (d == NULL) {
+		goto failed;
+	}
+	if (!pmu_battery_update_info (d)) {
+		goto failed;
+	}
+	return TRUE;
+
+      failed:
+	HAL_WARNING (("Reading info from battery %s failed!", data));
+	if (d) {
+		hal_device_store_remove (hald_get_gdl (), d);
+	}
+	g_free (data);
+	return FALSE;
+}
+
+static void
+add_pmu_battery (int i) {
+	HalDevice *d;
+	gchar *s;
+
+	d = hal_device_new ();
+
+	s = g_strdup_printf ("/org/freedesktop/Hal/devices/pmu_battery_%d", i);
+	hal_device_set_udi (d, s);
+	hal_device_property_set_string (d, "info.udi", s);
+	g_free (s);
+
+	hal_device_property_set_string (d, "info.product", "Battery bay");
+	/* Assuming all pmu batteries are primary! */
+	hal_device_property_set_string (d, "battery.type", "primary");
+
+	s = g_strdup_printf ("/proc/pmu/battery_%d", i);
+	hal_device_property_set_string (d, "linux.procfs_path", s);
+	g_free (s);
+
+	COMPUTER_CHILD (d);
+	if (pmu_battery_update_info (d)) {
+		hal_device_store_add (hald_get_gdl (), d);
+		g_timeout_add (2000, (GSourceFunc) pmu_update_battery_cb, g_strdup (hal_device_get_udi (d)));
+	} else {
+		g_object_unref (d);
+	}
+}
+
+static void
+probe_pmu_procinfo (void) {
+	GIOChannel *io;
+	GError *error = NULL;
+
+	io = g_io_channel_new_file ("/proc/pmu/info", "r", &error);
+	if (!io) {
+		g_warning ("Failed to open the pmu: %s", error->message);
+		g_error_free (error);
+	} else {
+		GString *line;
+		gchar **kv;
+		gboolean on_ac = FALSE;
+		gint nr_bat = 0;
+		gint i;
+		HalDevice *c;
+
+		c = hal_device_store_find (hald_get_gdl (), "/org/freedesktop/Hal/devices/computer");
+
+		if (c == NULL) {
+			HAL_ERROR (("No computer object ?"));
+			return;
+		}
+		hal_device_property_set_string (c, "linux.powersystem", "pmu");
+
+		line = g_string_sized_new (100);
+		while (g_io_channel_read_line_string (io, line, NULL, NULL)
+		       == G_IO_STATUS_NORMAL) {
+			kv = g_strsplit (line->str, ":", 2);
+			g_strstrip (kv[0]);
+			g_strstrip (kv[1]);
+
+			if (!g_ascii_strcasecmp ("PMU driver version", kv[0])) {
+				hal_device_property_set_string (c, "linux.pmu.driver_version", kv[1]);
+			} else if (!g_ascii_strcasecmp ("PMU firmware version", kv[0])) {
+				hal_device_property_set_string (c, "linux.pmu.firmware_version", kv[1]);
+			} else if (!g_ascii_strcasecmp ("PMU firmware version", kv[0])) {
+				hal_device_property_set_string (c, "linux.pmu.firmware_version", kv[1]);
+			} else if (!g_ascii_strcasecmp ("AC Power", kv[0])) {
+				on_ac = (*kv[1] == '1');
+			} else if (!g_ascii_strcasecmp ("AC Power", kv[0])) {
+				on_ac = (*kv[1] == '1');
+			} else if (!g_ascii_strcasecmp ("Battery count", kv[0])) {
+				nr_bat = atoi (kv[1]);
+			}
+			g_strfreev (kv);
+		}
+		g_string_free (line, TRUE);
+		if (nr_bat > 0) {
+			hal_add_ac_adapter (0, NULL, on_ac);
+			for (i = 0; i < nr_bat; i++) {
+				add_pmu_battery (i);
+			}
+		}
+	}
+}
+
+
+void
+pmu_probe (void) {
+	probe_pmu_procinfo ();
+	probe_pmu_sensors ();
+}
Index: hald/linux2/pmu.h
===================================================================
RCS file: hald/linux2/pmu.h
diff -N hald/linux2/pmu.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ hald/linux2/pmu.h	1 Feb 2005 18:14:51 -0000
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * Copyright (C) 2005 Sjoerd Simons <sjoerd at luon.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ **************************************************************************/
+
+#ifndef PMU_H
+#define PMU_H
+
+void pmu_probe (void);
+
+#endif /* PMU_H */
-------------- next part --------------
_______________________________________________
hal mailing list
hal at lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/hal


More information about the Hal mailing list