summaryrefslogtreecommitdiff
path: root/battstat/power-management.c
diff options
context:
space:
mode:
Diffstat (limited to 'battstat/power-management.c')
-rw-r--r--battstat/power-management.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/battstat/power-management.c b/battstat/power-management.c
new file mode 100644
index 00000000..53ae081a
--- /dev/null
+++ b/battstat/power-management.c
@@ -0,0 +1,535 @@
+/* battstat A MATE battery meter for laptops.
+ * Copyright (C) 2000 by Jörgen Pehrson <[email protected]>
+ * Copyright (C) 2002-2005 Free Software Foundation
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+
+#ifdef HAVE_ERR_H
+#include <err.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "battstat.h"
+#include "battstat-hal.h"
+
+#define ERR_ACPID _("Can't access ACPI events in /var/run/acpid.socket! " \
+ "Make sure the ACPI subsystem is working and " \
+ "the acpid daemon is running.")
+
+#define ERR_OPEN_APMDEV _("Can't open the APM device!\n\n" \
+ "Make sure you have read permission to the\n" \
+ "APM device.")
+
+#define ERR_APM_E _("The APM Management subsystem seems to be disabled.\n" \
+ "Try executing \"apm -e 1\" (FreeBSD) and see if \n" \
+ "that helps.\n")
+
+#define ERR_NO_SUPPORT _("Your platform is not supported! The battery\n" \
+ "monitor applet will not work on your system.\n")
+
+#define ERR_FREEBSD_ACPI _("There was an error reading information from " \
+ "the ACPI subsystem. Check to make sure the " \
+ "ACPI subsystem is properly loaded.")
+
+static const char *apm_readinfo (BatteryStatus *status);
+static int pm_initialised;
+#ifdef HAVE_HAL
+static int using_hal;
+#endif
+
+/*
+ * What follows is a series of platform-specific apm_readinfo functions
+ * that take care of the quirks of getting battery information for different
+ * platforms. Each function takes a BatteryStatus pointer and fills it
+ * then returns a const char *.
+ *
+ * In the case of success, NULL is returned. In case of failure, a
+ * localised error message is returned to give the user hints about what
+ * the problem might be. This error message is not to be freed.
+ */
+
+
+/* Uncomment the following to enable a 'testing' backend. When you add the
+ applet to the panel a window will appear that allows you to manually
+ change the battery status values for testing purposes.
+
+ NB: Be sure to unset this before committing!!
+ */
+
+/* #define BATTSTAT_TESTING_BACKEND */
+#ifdef BATTSTAT_TESTING_BACKEND
+
+#include <gtk/gtk.h>
+
+BatteryStatus test_status;
+
+static void
+test_update_boolean( GtkToggleButton *button, gboolean *value )
+{
+ *value = gtk_toggle_button_get_active( button );
+}
+
+static void
+test_update_integer( GtkSpinButton *spin, gint *value )
+{
+ *value = gtk_spin_button_get_value_as_int( spin );
+}
+
+static void
+initialise_test( void )
+{
+ GtkWidget *w;
+ GtkBox *box;
+
+ test_status.percent = 50;
+ test_status.minutes = 180;
+ test_status.present = TRUE;
+ test_status.on_ac_power = FALSE;
+ test_status.charging = FALSE;
+
+ box = GTK_BOX( gtk_vbox_new( 5, FALSE ) );
+
+ gtk_box_pack_start( box, gtk_label_new( "percent" ), TRUE, TRUE, 0);
+ w = gtk_spin_button_new_with_range( -1.0, 100.0, 1 );
+ gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), 50.0 );
+ g_signal_connect( G_OBJECT( w ), "value-changed",
+ G_CALLBACK( test_update_integer ), &test_status.percent );
+ gtk_box_pack_start( box, w, TRUE, TRUE, 0 );
+
+ gtk_box_pack_start( box, gtk_label_new( "minutes" ), TRUE, TRUE, 0);
+ w = gtk_spin_button_new_with_range( -1.0, 1000.0, 1 );
+ gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), 180.0 );
+ g_signal_connect( G_OBJECT( w ), "value-changed",
+ G_CALLBACK( test_update_integer ), &test_status.minutes );
+ gtk_box_pack_start( box, w, TRUE, TRUE, 0);
+
+
+ w = gtk_toggle_button_new_with_label( "on_ac_power" );
+ g_signal_connect( G_OBJECT( w ), "toggled",
+ G_CALLBACK( test_update_boolean ),
+ &test_status.on_ac_power );
+ gtk_box_pack_start( box, w, TRUE, TRUE, 0);
+
+ w = gtk_toggle_button_new_with_label( "charging" );
+ g_signal_connect( G_OBJECT( w ), "toggled",
+ G_CALLBACK( test_update_boolean ), &test_status.charging );
+ gtk_box_pack_start( box, w, TRUE, TRUE, 0);
+
+ w = gtk_toggle_button_new_with_label( "present" );
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), TRUE );
+ g_signal_connect( G_OBJECT( w ), "toggled",
+ G_CALLBACK( test_update_boolean ), &test_status.present );
+ gtk_box_pack_start( box, w, TRUE, TRUE, 0);
+
+ w = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_container_add( GTK_CONTAINER( w ), GTK_WIDGET( box ) );
+ gtk_widget_show_all( w );
+}
+
+static const char *
+apm_readinfo (BatteryStatus *status)
+{
+ static int test_initialised;
+
+ if( !test_initialised )
+ initialise_test();
+
+ test_initialised = 1;
+ *status = test_status;
+
+ return NULL;
+}
+
+#undef __linux__
+#undef HAVE_HAL
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+
+#include <machine/apm_bios.h>
+#include "acpi-freebsd.h"
+
+static struct acpi_info acpiinfo;
+static gboolean using_acpi;
+static int acpi_count;
+static struct apm_info apminfo;
+
+#define APMDEVICE "/dev/apm"
+
+static const char *
+apm_readinfo (BatteryStatus *status)
+{
+ int fd;
+
+ if (DEBUG) g_print("apm_readinfo() (FreeBSD)\n");
+
+ if (using_acpi) {
+ if (acpi_count <= 0) {
+ acpi_count = 30;
+ acpi_process_event(&acpiinfo);
+ if (acpi_freebsd_read(&apminfo, &acpiinfo) == FALSE)
+ return ERR_FREEBSD_ACPI;
+ }
+ acpi_count--;
+ }
+ else
+ {
+ /* This is how I read the information from the APM subsystem under
+ FreeBSD. Each time this functions is called (once every second)
+ the APM device is opened, read from and then closed.
+ */
+ fd = open(APMDEVICE, O_RDONLY);
+ if (fd == -1) {
+ return ERR_OPEN_APMDEV;
+ }
+
+ if (ioctl(fd, APMIO_GETINFO, &apminfo) == -1)
+ err(1, "ioctl(APMIO_GETINFO)");
+
+ close(fd);
+
+ if(apminfo.ai_status == 0)
+ return ERR_APM_E;
+ }
+
+ status->present = TRUE;
+ status->on_ac_power = apminfo.ai_acline ? 1 : 0;
+ status->percent = apminfo.ai_batt_life;
+ status->charging = (apminfo.ai_batt_stat) ? TRUE : FALSE;
+ if (using_acpi)
+ status->minutes = apminfo.ai_batt_time;
+ else
+ status->minutes = (int) (apminfo.ai_batt_time/60.0);
+
+ return NULL;
+}
+
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+
+#include <sys/param.h>
+#include <machine/apmvar.h>
+
+#define APMDEVICE "/dev/apm"
+
+static const char *
+apm_readinfo (BatteryStatus *status)
+{
+ /* Code for OpenBSD by Joe Ammond <[email protected]>. Using the same
+ procedure as for FreeBSD.
+ */
+ struct apm_info apminfo;
+ int fd;
+
+#if defined(__NetBSD__)
+ if (DEBUG) g_print("apm_readinfo() (NetBSD)\n");
+#else /* __OpenBSD__ */
+ if (DEBUG) g_print("apm_readinfo() (OpenBSD)\n");
+#endif
+
+ fd = open(APMDEVICE, O_RDONLY);
+ if (fd == -1)
+ {
+ pm_initialised = 0;
+ return ERR_OPEN_APMDEV;
+ }
+ if (ioctl(fd, APM_IOC_GETPOWER, &apminfo) == -1)
+ err(1, "ioctl(APM_IOC_GETPOWER)");
+ close(fd);
+
+ status->present = TRUE;
+ status->on_ac_power = apminfo.ac_state ? 1 : 0;
+ status->percent = apminfo.battery_life;
+ status->charging = (apminfo.battery_state == 3) ? TRUE : FALSE;
+ status->minutes = apminfo.minutes_left;
+
+ return NULL;
+}
+
+#elif __linux__
+
+#include <apm.h>
+#include "acpi-linux.h"
+
+static struct acpi_info acpiinfo;
+static gboolean using_acpi;
+static int acpi_count;
+static int acpiwatch;
+static struct apm_info apminfo;
+
+/* Declared in acpi-linux.c */
+gboolean acpi_linux_read(struct apm_info *apminfo, struct acpi_info *acpiinfo);
+
+static gboolean acpi_callback (GIOChannel * chan, GIOCondition cond, gpointer data)
+{
+ if (cond & (G_IO_ERR | G_IO_HUP)) {
+ acpi_linux_cleanup(&acpiinfo);
+ apminfo.battery_percentage = -1;
+ return FALSE;
+ }
+
+ if (acpi_process_event(&acpiinfo)) {
+ acpi_linux_read(&apminfo, &acpiinfo);
+ }
+ return TRUE;
+}
+
+static const char *
+apm_readinfo (BatteryStatus *status)
+{
+ /* Code for Linux by Thomas Hood <[email protected]>. apm_read() will
+ read from /proc/... instead and we do not need to open the device
+ ourselves.
+ */
+ if (DEBUG) g_print("apm_readinfo() (Linux)\n");
+
+ /* ACPI support added by Lennart Poettering <[email protected]> 10/27/2001
+ * Updated by David Moore <[email protected]> 5/29/2003 to poll less and
+ * use ACPI events. */
+ if (using_acpi && acpiinfo.event_fd >= 0) {
+ if (acpi_count <= 0) {
+ /* Only call this one out of 30 calls to apm_readinfo() (every 30 seconds)
+ * since reading the ACPI system takes CPU cycles. */
+ acpi_count=30;
+ acpi_linux_read(&apminfo, &acpiinfo);
+ }
+ acpi_count--;
+ }
+ /* If we lost the file descriptor with ACPI events, try to get it back. */
+ else if (using_acpi) {
+ if (acpi_linux_init(&acpiinfo)) {
+ acpiwatch = g_io_add_watch (acpiinfo.channel,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ acpi_callback, NULL);
+ acpi_linux_read(&apminfo, &acpiinfo);
+ }
+ }
+ else
+ apm_read(&apminfo);
+
+ status->present = TRUE;
+ status->on_ac_power = apminfo.ac_line_status ? 1 : 0;
+ status->percent = (guint) apminfo.battery_percentage;
+ status->charging = (apminfo.battery_flags & 0x8) ? TRUE : FALSE;
+ status->minutes = apminfo.battery_time;
+
+ return NULL;
+}
+
+#else
+
+static const char *
+apm_readinfo (BatteryStatus *status)
+{
+ status->present = FALSE;
+ status->on_ac_power = 1;
+ status->percent = 100;
+ status->charging = FALSE;
+ status->minutes = 0;
+
+ return ERR_NO_SUPPORT;
+}
+
+#endif
+
+/*
+ * End platform-specific code.
+ */
+
+/*
+ * power_management_getinfo
+ *
+ * Main interface to the power management code. Fills 'status' for you so
+ * you don't have to worry about platform-specific details.
+ *
+ * In the case of success, NULL is returned. In case of failure, a
+ * localised error message is returned to give the user hints about what
+ * the problem might be. This error message is not to be freed.
+ */
+const char *
+power_management_getinfo( BatteryStatus *status )
+{
+ const char *retval;
+
+ if( !pm_initialised )
+ {
+ status->on_ac_power = TRUE;
+ status->minutes = -1;
+ status->percent = 0;
+ status->charging = FALSE;
+ status->present = FALSE;
+
+ return NULL;
+ }
+
+#ifdef HAVE_HAL
+ if( using_hal )
+ {
+ battstat_hal_get_battery_info( status );
+ return NULL;
+ }
+#endif
+
+ retval = apm_readinfo( status );
+
+ if(status->percent == -1) {
+ status->percent = 0;
+ status->present = FALSE;
+ }
+
+ if(status->percent > 100)
+ status->percent = 100;
+
+ if(status->percent == 100)
+ status->charging = FALSE;
+
+ if(!status->on_ac_power)
+ status->charging = FALSE;
+
+ return retval;
+}
+
+/*
+ * power_management_initialise
+ *
+ * Initialise the power management code. Call this before you call anything
+ * else.
+ *
+ * In the case of success, NULL is returned. In case of failure, a
+ * localised error message is returned to give the user hints about what
+ * the problem might be. This error message is not to be freed.
+ */
+const char *
+power_management_initialise (int no_hal, void (*callback) (void))
+{
+#ifdef __linux__
+ struct stat statbuf;
+#endif
+#ifdef HAVE_HAL
+ char *err;
+
+ if( no_hal )
+ err = g_strdup( ":(" );
+ else
+ err = battstat_hal_initialise (callback);
+
+
+ if( err == NULL ) /* HAL is up */
+ {
+ pm_initialised = 1;
+ using_hal = TRUE;
+ return NULL;
+ }
+ else
+ /* fallback to legacy methods */
+ g_free( err );
+#endif
+
+#ifdef __linux__
+
+ if (acpi_linux_init(&acpiinfo)) {
+ using_acpi = TRUE;
+ acpi_count = 0;
+ }
+ else
+ using_acpi = FALSE;
+
+ /* If neither ACPI nor APM could be read, but ACPI does seem to be
+ * installed, warn the user how to get ACPI working. */
+ if (!using_acpi && (apm_exists() == 1) &&
+ (stat("/proc/acpi", &statbuf) == 0)) {
+ using_acpi = TRUE;
+ acpi_count = 0;
+ return ERR_ACPID;
+ }
+
+ /* Watch for ACPI events and handle them immediately with acpi_callback(). */
+ if (using_acpi && acpiinfo.event_fd >= 0) {
+ acpiwatch = g_io_add_watch (acpiinfo.channel,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ acpi_callback, NULL);
+ }
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (acpi_freebsd_init(&acpiinfo)) {
+ using_acpi = TRUE;
+ acpi_count = 0;
+ }
+ else
+ using_acpi = FALSE;
+#endif
+ pm_initialised = 1;
+
+ return NULL;
+}
+
+/*
+ * power_management_cleanup
+ *
+ * Perform any cleanup that might be required.
+ */
+void
+power_management_cleanup( void )
+{
+#ifdef HAVE_HAL
+ if( using_hal )
+ {
+ battstat_hal_cleanup();
+ pm_initialised = 1;
+ return;
+ }
+#endif
+
+#ifdef __linux__
+ if (using_acpi)
+ {
+ if (acpiwatch != 0)
+ g_source_remove(acpiwatch);
+ acpiwatch = 0;
+ acpi_linux_cleanup(&acpiinfo);
+ }
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (using_acpi) {
+ acpi_freebsd_cleanup(&acpiinfo);
+ }
+#endif
+
+ pm_initialised = 0;
+}
+
+int
+power_management_using_hal( void )
+{
+#ifdef HAVE_HAL
+ return using_hal;
+#else
+ return 0;
+#endif
+}