changeset 4035:4874e0752050

PSARC/2006/601 Battery Project 6527351 PSARC/2006/601 Battery Project: Add HAL ACPI support 6527350 PSARC/2006/601 Battery Project: create battery driver
author phitran
date Sat, 14 Apr 2007 18:08:01 -0700
parents b20b176bd1e8
children 9db83fcc6245
files usr/src/cmd/hal/addons/Makefile usr/src/cmd/hal/addons/acpi/Makefile usr/src/cmd/hal/addons/acpi/addon-acpi.c usr/src/cmd/hal/hald/Makefile usr/src/cmd/hal/hald/solaris/Makefile usr/src/cmd/hal/hald/solaris/devinfo.c usr/src/cmd/hal/hald/solaris/devinfo_acpi.c usr/src/cmd/hal/hald/solaris/devinfo_acpi.h usr/src/cmd/hal/hald/solaris/devinfo_misc.c usr/src/cmd/hal/hald/solaris/sysevent.c usr/src/cmd/hal/probing/Makefile usr/src/cmd/hal/probing/battery/Makefile usr/src/cmd/hal/probing/battery/probe-battery.c usr/src/cmd/hal/utils/battery.c usr/src/cmd/hal/utils/battery.h usr/src/pkgdefs/SUNWckr/prototype_i386 usr/src/pkgdefs/SUNWhal/prototype_com usr/src/pkgdefs/SUNWhea/prototype_com usr/src/pkgdefs/common_files/i.minorperm_i386 usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/battery.h usr/src/uts/common/sys/sysevent/acpiev.h usr/src/uts/common/sys/sysevent/eventdefs.h usr/src/uts/i86pc/Makefile.files usr/src/uts/i86pc/Makefile.i86pc.shared usr/src/uts/i86pc/Makefile.rules usr/src/uts/i86pc/battery/Makefile usr/src/uts/i86pc/io/battery/battery.c usr/src/uts/i86pc/io/battery/battery.conf usr/src/uts/intel/os/minor_perm usr/src/uts/intel/os/name_to_major
diffstat 31 files changed, 3638 insertions(+), 65 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/hal/addons/Makefile	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/cmd/hal/addons/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -19,13 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
 #
 
-SUBDIRS = storage
+SUBDIRS = storage acpi
 
 all :=		TARGET= all
 install :=	TARGET= install
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/addons/acpi/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,74 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+PROG =		hald-addon-acpi
+OBJS =		addon-acpi.o logger.o util_helper.o battery.o util_pm.o
+SRCS =		addon-acpi.c ../../hald/logger.c ../../hald/util_helper.c
+
+include ../../../Makefile.cmd
+include ../../Makefile.hal
+
+ROOTCMDDIR =	$(ROOTLIB_HAL)
+
+LDLIBS +=	-lc -ldbus-1 -lhal -lglib-2.0
+
+CPPFLAGS +=	$(HAL_DBUS_CPPFLAGS) $(HAL_GLIB_CPPFLAGS) $(HAL_CONFIG_CPPFLAGS)
+CPPFLAGS +=	-I$(ROOT)/usr/include/hal -I../../hald
+C99MODE =	$(C99_ENABLE)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+battery.o: ../../utils/battery.c
+	$(COMPILE.c) -o $@ ../../utils/battery.c
+	$(POST_PROCESS_O)
+
+logger.o: ../../hald/logger.c
+	$(COMPILE.c) -o $@ ../../hald/logger.c
+	$(POST_PROCESS_O)
+
+util_helper.o: ../../hald/util_helper.c
+	$(COMPILE.c) -o $@ ../../hald/util_helper.c
+	$(POST_PROCESS_O)
+
+util_pm.o: ../../hald/util_pm.c
+	$(COMPILE.c) -o $@ ../../hald/util_pm.c
+	$(POST_PROCESS_O)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+install: all $(ROOTCMD)
+
+clean:
+	$(RM) $(OBJS) $(PROG)
+
+FRC:
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/addons/acpi/addon-acpi.c	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,56 @@
+/***************************************************************************
+ *
+ * addon-acpi.c : Poll battery and AC adapter devices and update
+ *                   properties
+ *
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/dkio.h>
+#include <sys/stat.h>
+#include <glib.h>
+
+#include <libhal.h>
+#include "../../hald/logger.h"
+#include "../../hald/util_helper.h"
+#include "../../utils/battery.h"
+
+int
+main(int argc, char **argv)
+{
+	LibHalContext *ctx = NULL;
+	DBusError error;
+
+	GMainLoop *loop = g_main_loop_new(NULL, FALSE);
+
+	drop_privileges(0);
+	setup_logger();
+
+	dbus_error_init(&error);
+	if ((ctx = libhal_ctx_init_direct(&error)) == NULL) {
+		printf("main(): init_direct failed\n");
+		return (0);
+	}
+	dbus_error_init(&error);
+	if (!libhal_device_addon_is_ready(ctx, getenv("UDI"), &error)) {
+		return (0);
+	}
+
+	g_timeout_add(BATTERY_POLL_TIMER, update_devices, ctx);
+
+	g_main_loop_run(loop);
+}
--- a/usr/src/cmd/hal/hald/Makefile	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/cmd/hal/hald/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -32,7 +32,7 @@
 		hald_dbus.o hald_runner.o ids.o logger.o property.o util.o \
 		util_helper.o util_pm.o
 OBJS_SOL =	devinfo.o devinfo_ieee1394.o devinfo_misc.o devinfo_pci.o devinfo_storage.o \
-		devinfo_usb.o hotplug.o osspec.o sysevent.o
+		devinfo_usb.o hotplug.o osspec.o sysevent.o devinfo_acpi.o
 OBJS_ALL =	$(OBJS) $(OBJS_SOL:%=solaris/%)
 SRCS =		$(OBJS:%.o=%.c)
 
--- a/usr/src/cmd/hal/hald/solaris/Makefile	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/cmd/hal/hald/solaris/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -19,14 +19,15 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
 #
 
 OBJS =		devinfo.o devinfo_ieee1394.o devinfo_misc.o devinfo_pci.o \
-		devinfo_storage.o devinfo_usb.o hotplug.o osspec.o sysevent.o
+		devinfo_storage.o devinfo_usb.o hotplug.o osspec.o sysevent.o \
+		devinfo_acpi.o
 
 SRCS =		$(OBJS:%.o=%.c)
 
--- a/usr/src/cmd/hal/hald/solaris/devinfo.c	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/cmd/hal/hald/solaris/devinfo.c	Sat Apr 14 18:08:01 2007 -0700
@@ -34,6 +34,7 @@
 #include "devinfo_ieee1394.h"
 #include "devinfo_usb.h"
 #include "devinfo_misc.h"
+#include "devinfo_acpi.h"
 
 void devinfo_add_subtree(HalDevice *parent, di_node_t node, gboolean is_root);
 HalDevice *devinfo_add_node(HalDevice *parent, di_node_t node);
@@ -138,6 +139,8 @@
 	&devinfo_ieee1394_handler,
 	&devinfo_pci_handler,
 	&devinfo_lofi_handler,
+	&devinfo_acpi_handler,
+	&devinfo_battery_handler,
 	&devinfo_default_handler,
 	NULL
 };
@@ -375,7 +378,7 @@
 {
 	if (hal_device_has_capability (d, "block")) {
 		return (devinfo_storage_device_rescan (d));
-	} else {
+        } else { 
 		return (FALSE);
 	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/hald/solaris/devinfo_acpi.c	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,178 @@
+/***************************************************************************
+ *
+ * devinfo_acpi : acpi devices
+ *
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <libdevinfo.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "../osspec.h"
+#include "../logger.h"
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../device_info.h"
+#include "../util.h"
+#include "../hald_runner.h"
+#include "devinfo_acpi.h"
+
+#define		DEVINFO_PROBE_BATTERY_TIMEOUT	30000
+
+static HalDevice *devinfo_acpi_add(HalDevice *, di_node_t, char *, char *);
+static HalDevice *devinfo_battery_add(HalDevice *, di_node_t, char *, char *);
+
+DevinfoDevHandler devinfo_acpi_handler = {
+	devinfo_acpi_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+DevinfoDevHandler devinfo_battery_handler = {
+	devinfo_battery_add,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	devinfo_battery_get_prober
+};
+
+static HalDevice *
+devinfo_acpi_add(HalDevice *parent, di_node_t node, char *devfs_path,
+    char *device_type)
+{
+	HalDevice *d, *computer;
+
+	if (strcmp(devfs_path, "/acpi") != 0) {
+		return (NULL);
+	}
+
+	d = hal_device_new();
+
+	if ((computer = hal_device_store_find(hald_get_gdl(),
+	    "/org/freedesktop/Hal/devices/computer")) ||
+	    (computer = hal_device_store_find(hald_get_tdl(),
+	    "/org/freedesktop/Hal/devices/computer"))) {
+		hal_device_property_set_string(computer,
+		    "power_management.type", "acpi");
+	}
+	devinfo_set_default_properties(d, parent, node, devfs_path);
+	devinfo_add_enqueue(d, devfs_path, &devinfo_acpi_handler);
+
+	return (d);
+}
+
+static HalDevice *
+devinfo_battery_add(HalDevice *parent, di_node_t node, char *devfs_path,
+    char *device_type)
+{
+	HalDevice *d, *computer;
+	char	*driver_name;
+	di_devlink_handle_t devlink_hdl;
+	int	major;
+	di_minor_t minor;
+	dev_t   dev;
+	char    *minor_path = NULL;
+	char    *devpath;
+
+	driver_name = di_driver_name(node);
+	if ((driver_name == NULL) || (strcmp(driver_name, "battery") != 0)) {
+		return (NULL);
+	}
+
+	d = hal_device_new();
+
+	if ((computer = hal_device_store_find(hald_get_gdl(),
+	    "/org/freedesktop/Hal/devices/computer")) ||
+	    (computer = hal_device_store_find(hald_get_tdl(),
+	    "/org/freedesktop/Hal/devices/computer"))) {
+		hal_device_property_set_string(computer,
+		    "system.formfactor", "laptop");
+	}
+	devinfo_set_default_properties(d, parent, node, devfs_path);
+	devinfo_add_enqueue(d, devfs_path, &devinfo_battery_handler);
+
+	major = di_driver_major(node);
+	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
+		return (d);
+	}
+	minor = DI_MINOR_NIL;
+	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
+		dev = di_minor_devt(minor);
+		if ((major != major(dev)) ||
+		    (di_minor_type(minor) != DDM_MINOR) ||
+		    (di_minor_spectype(minor) != S_IFCHR) ||
+		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
+			continue;
+		}
+
+		if (hal_device_store_match_key_value_string(hald_get_gdl(),
+		    "solaris.devfs_path", minor_path) == NULL) {
+			devinfo_battery_add_minor(d, node, minor_path, dev);
+		}
+
+		di_devfs_path_free(minor_path);
+	}
+	di_devlink_fini(&devlink_hdl);
+
+	return (d);
+}
+
+void
+devinfo_battery_add_minor(HalDevice *parent, di_node_t node, char *minor_path,
+    dev_t dev)
+{
+	HalDevice *d;
+
+	d = hal_device_new();
+	devinfo_set_default_properties(d, parent, node, minor_path);
+	devinfo_add_enqueue(d, minor_path, &devinfo_battery_handler);
+}
+
+void
+devinfo_battery_device_rescan(char *parent_devfs_path, gchar *udi)
+{
+	HalDevice *d = NULL;
+
+	d = hal_device_store_find(hald_get_gdl(), udi);
+	if (d == NULL) {
+		HAL_INFO(("device not found %s", udi));
+		return;
+	}
+
+	hald_runner_run(d, "hald-probe-battery", NULL,
+	    DEVINFO_PROBE_BATTERY_TIMEOUT, devinfo_battery_rescan_probing_done,
+	    NULL, NULL);
+}
+
+static void
+devinfo_battery_rescan_probing_done(HalDevice *d, guint32 exit_type,
+    gint return_code, char **error, gpointer userdata1, gpointer userdata2)
+{
+	/* hald_runner_run() requires this function since cannot pass NULL */
+}
+
+const gchar *
+devinfo_battery_get_prober(HalDevice *d, int *timeout)
+{
+	*timeout = DEVINFO_PROBE_BATTERY_TIMEOUT;    /* 30 second timeout */
+	return ("hald-probe-battery");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/hald/solaris/devinfo_acpi.h	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,43 @@
+/***************************************************************************
+ *
+ * devinfo_acpi.h : definitions for acpi devices
+ *
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifndef DEVINFO_ACPI_H
+#define	DEVINFO_ACPI_H
+
+#include "devinfo.h"
+
+extern DevinfoDevHandler devinfo_acpi_handler;
+extern DevinfoDevHandler devinfo_battery_handler;
+
+#define	MINOR_SHIFT			8
+#define	MINOR2TYPE(minor)		((minor) >> MINOR_SHIFT)
+
+/* Battery device types */
+enum batt_type {
+	BATT_TYPE_UNKNOWN = -1,
+	BATT_TYPE_CBAT,
+	BATT_TYPE_AC,
+	BATT_TYPE_SBAT
+};
+
+HalDevice *devinfo_battery_add_major(HalDevice *parent, di_node_t node,
+    char *devfs_path, char *device_type, gboolean rescan, HalDevice *battery_d);
+void devinfo_battery_add_minor(HalDevice *parent, di_node_t node,
+    char *minor_path, dev_t dev);
+void devinfo_battery_remove_minor(char *parent_devfs_path, gchar *udi);
+void devinfo_battery_device_rescan(char *parent_devfs_path, gchar *udi);
+static void devinfo_battery_rescan_probing_done(HalDevice *d, guint32 exit_type,
+    gint return_code, char **error, gpointer userdata1, gpointer userdata2);
+const gchar *devinfo_battery_get_prober(HalDevice *d, int *timeout);
+
+#endif /* DEVINFO_ACPI_H */
--- a/usr/src/cmd/hal/hald/solaris/devinfo_misc.c	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/cmd/hal/hald/solaris/devinfo_misc.c	Sat Apr 14 18:08:01 2007 -0700
@@ -2,7 +2,7 @@
  *
  * devinfo_misc : misc devices
  *
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Licensed under the Academic Free License version 2.1
@@ -81,6 +81,12 @@
 		hal_device_property_set_string (d, "system.kernel.machine", un.machine);
 	}
 
+	/*
+	 * Let computer be in TDL while synthesizing all other events
+	 * because some may write to the object
+	 */
+        hal_device_store_add (hald_get_tdl (), d);
+
 	devinfo_add_enqueue (d, devfs_path, &devinfo_computer_handler);
 
 	/* all devinfo devices belong to the 'local' branch */
--- a/usr/src/cmd/hal/hald/solaris/sysevent.c	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/cmd/hal/hald/solaris/sysevent.c	Sat Apr 14 18:08:01 2007 -0700
@@ -26,6 +26,7 @@
 #include <libdevinfo.h>
 #include <libsysevent.h>
 #include <sys/sysevent/dev.h>
+#include <sys/sysevent/acpiev.h>
 #include <glib.h>
 
 #include "../osspec.h"
@@ -38,6 +39,7 @@
 #include "hotplug.h"
 #include "devinfo.h"
 #include "devinfo_storage.h"
+#include "devinfo_acpi.h"
 #include "sysevent.h"
 
 #ifndef ESC_LOFI
@@ -107,6 +109,15 @@
 		return (FALSE);
 	}
 
+	subcl[0] = ESC_ACPIEV_ADD;
+        subcl[1] = ESC_ACPIEV_REMOVE;
+        subcl[2] = ESC_ACPIEV_STATE_CHANGE;
+	if (sysevent_subscribe_event(shp, EC_ACPIEV, subcl, 3) != 0) {
+                HAL_INFO(("subscribe(dev_add) failed %d", errno));
+                sysevent_unbind_handle(shp);
+                return (FALSE);
+        }
+
 	return (B_TRUE);
 }
 
@@ -125,6 +136,9 @@
 	nvlist_t	*attr_list;
 	char		*phys_path;
 	char		*dev_name;
+	char		*dev_hid;
+	char		*dev_uid;
+	uint_t		dev_index;
 	char		s[1024];
 	ssize_t		nwritten;
 
@@ -137,14 +151,37 @@
 	if (sysevent_get_attr_list(ev, &attr_list) != 0)
 		return;
 
-	if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path) != 0)
+	if (strcmp(class, EC_ACPIEV) == 0) {		
+		if (nvlist_lookup_string(attr_list, ACPIEV_DEV_PHYS_PATH,
+		    &phys_path) != 0) {
+			goto out;
+		}
+	} else if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path)
+	    != 0) {
 		goto out;
+	}
 
-	if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0)
-		dev_name = "";
+	if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0) {
+		if (strcmp(class, EC_ACPIEV) == 0) {
+			dev_name = "noname";
+		} else {
+			dev_name = "";
+		}
+	}
 
-	snprintf(s, sizeof (s), "%s %s %s %s\n",
-		class, subclass, phys_path, dev_name);
+	if (nvlist_lookup_string(attr_list, ACPIEV_DEV_HID, &dev_hid) != 0) {
+                dev_hid = "";
+	}
+        if (nvlist_lookup_string(attr_list, ACPIEV_DEV_UID, &dev_uid) != 0) {
+                dev_uid = "";
+	}
+        if (nvlist_lookup_uint32(attr_list, ACPIEV_DEV_INDEX, &dev_index)
+	    != 0) {
+                dev_index = 0;
+	}
+
+	snprintf(s, sizeof (s), "%s %s %s %s %s %s %d\n",
+	    class, subclass, phys_path, dev_name, dev_hid, dev_uid, dev_index);
 	nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1);
 
 	HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten));
@@ -166,6 +203,10 @@
 	gchar subclass[1024];
 	gchar phys_path[1024];
 	gchar dev_name[1024];
+        gchar dev_uid[1024];
+        gchar dev_hid[1024];
+        gchar udi[1024];
+	uint_t dev_index;
 
 	HAL_INFO (("sysevent_iochannel_data"));
 
@@ -175,8 +216,10 @@
 			break;
 		}
 
-		class[0] = subclass[0] = phys_path[0] = dev_name[0] = '\0';
-		matches = sscanf(s, "%s %s %s %s", class, subclass, phys_path, dev_name);
+		class[0] = subclass[0] = phys_path[0] = dev_name[0] =
+		    dev_hid[0] = dev_uid[0] = '\0';
+		matches = sscanf(s, "%s %s %s %s %s %s %d", class, subclass,
+		    phys_path, dev_name, dev_hid, dev_uid, &dev_index);
 		g_free (s);
 		s = NULL;
 		if (matches < 3) {
@@ -200,7 +243,21 @@
 			}
 		} else if (strcmp(class, EC_DEV_BRANCH) == 0) {
 			sysevent_dev_branch(phys_path);
-		}
+		} else if (strcmp(class, EC_ACPIEV) == 0) {
+			if (strcmp(dev_hid, "PNP0C0A") == 0) {
+				snprintf(udi, sizeof(udi),
+				    "/org/freedesktop/Hal/devices/pseudo/"
+				    "battery_0_battery%d_0", dev_index);
+			} else if (strcmp(dev_hid, "ACPI0003") == 0) {
+				snprintf(udi, sizeof(udi),
+				    "/org/freedesktop/Hal/devices/pseudo/"
+				    "battery_0_ac%d_0", dev_index);
+                        } else {
+				HAL_INFO(("dev_hid %s unknown", dev_hid));
+				continue;
+			}
+			devinfo_battery_device_rescan(phys_path, udi);
+                }
 	}
 
 	if (err) {
--- a/usr/src/cmd/hal/probing/Makefile	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/cmd/hal/probing/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -25,7 +25,7 @@
 # ident	"%Z%%M%	%I%	%E% SMI"
 #
 
-SUBDIRS = storage volume printer
+SUBDIRS = storage volume printer battery
 
 all :=		TARGET= all
 install :=	TARGET= install
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/probing/battery/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,70 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+PROG =		hald-probe-battery
+OBJS =		probe-battery.o logger.o battery.o util_pm.o
+SRCS =		probe-battery.c
+
+include ../../../Makefile.cmd
+include ../../Makefile.hal
+
+ROOTCMDDIR =	$(ROOTLIB_HAL)
+
+LDLIBS +=	-lc -ldbus-1 -lhal -lglib-2.0
+
+CPPFLAGS +=	$(HAL_DBUS_CPPFLAGS) $(HAL_CONFIG_CPPFLAGS) $(HAL_GLIB_CPPFLAGS)
+CPPFLAGS +=	-I$(ROOT)/usr/include/hal -I../../utils -I../../hald
+C99MODE =	$(C99_ENABLE)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+logger.o: ../../hald/logger.c
+	$(COMPILE.c) -o $@ ../../hald/logger.c
+	$(POST_PROCESS_O)
+
+battery.o: ../../utils/battery.c
+	$(COMPILE.c) -o $@ ../../utils/battery.c
+	$(POST_PROCESS_O)
+
+util_pm.o: ../../hald/util_pm.c
+	$(COMPILE.c) -o $@ ../../hald/util_pm.c
+	$(POST_PROCESS_O)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+install: all $(ROOTCMD)
+
+clean:
+	$(RM) $(OBJS) $(PROG)
+
+FRC:
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/probing/battery/probe-battery.c	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,85 @@
+/***************************************************************************
+ *
+ * probe-battery.c : Probe for battery device information
+ *
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include <libhal.h>
+#include <logger.h>
+#include "../utils/battery.h"
+
+int
+main(int argc, char *argv[])
+{
+	int ret = 1;
+	int fd = -1;
+	char *udi;
+	char device_file[HAL_PATH_MAX] = "/devices";
+	char *devfs_path;
+	LibHalContext *ctx = NULL;
+	DBusError error;
+
+	if ((udi = getenv("UDI")) == NULL)
+		goto out;
+	if ((devfs_path = getenv("HAL_PROP_SOLARIS_DEVFS_PATH")) == NULL)
+		goto out;
+	strlcat(device_file, devfs_path, HAL_PATH_MAX);
+
+	setup_logger();
+
+	dbus_error_init(&error);
+	if ((ctx = libhal_ctx_init_direct(&error)) == NULL)
+		goto out;
+
+	HAL_DEBUG(("Doing probe-battery for %s (udi=%s)",
+	    device_file, udi));
+
+	if ((fd = open(device_file, O_RDONLY | O_NONBLOCK)) < 0) {
+		HAL_DEBUG(("Cannot open %s: %s", device_file, strerror(errno)));
+		goto out;
+	}
+	if (strstr(udi, "ac")) {
+		ac_adapter_update(ctx, udi, fd);
+	} else {
+		battery_update(ctx, udi, fd);
+	}
+
+	ret = 0;
+
+out:
+	if (fd >= 0) {
+		close(fd);
+	}
+
+	if (ctx != NULL) {
+		libhal_ctx_shutdown(ctx, &error);
+		libhal_ctx_free(ctx);
+		if (dbus_error_is_set(&error)) {
+			dbus_error_free(&error);
+		}
+	}
+
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/utils/battery.c	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,637 @@
+/***************************************************************************
+ *
+ * battery.c : Main routines for setting battery and AC adapter properties
+ *
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <kstat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/battery.h>
+
+#include <libhal.h>
+#include "../hald/device_info.h"
+#include "../hald/hald_dbus.h"
+#include "../hald/logger.h"
+#include "../hald/util_pm.h"
+#include "battery.h"
+
+
+static void
+my_dbus_error_free(DBusError *error)
+{
+	if (dbus_error_is_set(error)) {
+		dbus_error_free(error);
+	}
+}
+
+static void
+ac_adapter_present(LibHalContext *ctx, const char *udi, int fd)
+{
+	int pow;
+	LibHalChangeSet *cs;
+	DBusError error;
+
+	HAL_DEBUG(("ac_adapter_present() enter"));
+	if (ioctl(fd, BATT_IOC_POWER_STATUS, &pow) < 0) {
+		return;
+	}
+	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
+		return;
+	}
+	if (pow > 0) {
+		libhal_changeset_set_property_bool(cs, "ac_adapter.present",
+		    TRUE);
+	} else {
+		libhal_changeset_set_property_bool(cs, "ac_adapter.present",
+		    FALSE);
+	}
+
+	dbus_error_init(&error);
+	libhal_device_commit_changeset(ctx, cs, &error);
+	libhal_device_free_changeset(cs);
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("ac_adapter_present() exit"));
+}
+
+static void
+battery_remove(LibHalContext *ctx, const char *udi)
+{
+	DBusError error;
+
+	HAL_DEBUG(("battery_remove() enter"));
+	dbus_error_init(&error);
+	libhal_device_remove_property(ctx, udi, "battery.remaining_time",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.charge_level.percentage", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.charge_level.rate",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.charge_level.last_full", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.charge_level.current", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.voltage.present",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.rate",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.current",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.rechargeable.is_discharging", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.rechargeable.is_charging", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.is_rechargeable",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.charge_level.unit",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.charge_level.granularity_2", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.charge_level.granularity_1", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.charge_level.low",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.charge_level.warning",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.charge_level.design",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.voltage.design",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.reporting.granularity_2", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi,
+	    "battery.reporting.granularity_1", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.low",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.warning",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.design",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.last_full",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.unit",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.technology", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.reporting.technology",
+	    &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.serial", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.model", &error);
+	my_dbus_error_free(&error);
+	libhal_device_remove_property(ctx, udi, "battery.vendor", &error);
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("battery_remove() exit"));
+}
+
+static void
+battery_last_full(LibHalChangeSet *cs, int fd)
+{
+	acpi_bif_t bif;
+
+	bzero(&bif, sizeof (bif));
+	if (ioctl(fd, BATT_IOC_INFO, &bif) < 0) {
+		return;
+	}
+	libhal_changeset_set_property_int(cs, "battery.reporting_last_full",
+	    bif.bif_last_cap);
+}
+
+static void
+battery_dynamic_update(LibHalContext *ctx, const char *udi, int fd)
+{
+	int reporting_rate;
+	int reporting_current;
+	int reporting_lastfull;
+	int design_voltage;
+	int present_voltage;
+	char *reporting_unit;
+	int remaining_time;
+	int remaining_percentage;
+	gboolean charging;
+	gboolean discharging;
+	acpi_bst_t bst;
+	LibHalChangeSet *cs;
+	DBusError error;
+	static int counter = 0;
+
+	HAL_DEBUG(("battery_dynamic_update() enter"));
+	bzero(&bst, sizeof (bst));
+	if (ioctl(fd, BATT_IOC_STATUS, &bst) < 0) {
+		return;
+	}
+
+	charging = bst.bst_state & BATT_BST_CHARGING ? TRUE : FALSE;
+	discharging = bst.bst_state & BATT_BST_DISCHARGING ? TRUE : FALSE;
+	/* No need to continue if battery is essentially idle. */
+	if (counter && !charging && !discharging) {
+		return;
+	}
+	dbus_error_init(&error);
+	libhal_device_set_property_bool(ctx, udi, "battery.is_rechargeable",
+	    TRUE, &error);
+	my_dbus_error_free(&error);
+	if (libhal_device_property_exists(ctx, udi,
+	    "battery.charge_level.percentage", &error)) {
+		remaining_percentage = libhal_device_get_property_int(ctx, udi,
+		    "battery.charge_level.percentage", &error);
+		if ((remaining_percentage == 100) && charging) {
+			charging = FALSE;
+		}
+	}
+	libhal_device_set_property_bool(ctx, udi,
+	    "battery.rechargeable.is_charging", charging, &error);
+	my_dbus_error_free(&error);
+	libhal_device_set_property_bool(ctx, udi,
+	    "battery.rechargeable.is_discharging", discharging, &error);
+	my_dbus_error_free(&error);
+	reporting_current = bst.bst_rem_cap;
+	libhal_device_set_property_int(ctx, udi, "battery.reporting.current",
+	    bst.bst_rem_cap, &error);
+	my_dbus_error_free(&error);
+	reporting_rate = bst.bst_rate;
+	libhal_device_set_property_int(ctx, udi, "battery.reporting.rate",
+	    bst.bst_rate, &error);
+	my_dbus_error_free(&error);
+	present_voltage = bst.bst_voltage;
+	libhal_device_set_property_int(ctx, udi, "battery.voltage.present",
+	    bst.bst_voltage, &error);
+	/* get all the data we know */
+	my_dbus_error_free(&error);
+	reporting_unit = libhal_device_get_property_string(ctx, udi,
+	    "battery.reporting.unit", &error);
+	my_dbus_error_free(&error);
+	reporting_lastfull = libhal_device_get_property_int(ctx, udi,
+	    "battery.reporting.last_full", &error);
+
+	/*
+	 * Convert mAh to mWh since util_compute_time_remaining() works
+	 * for mWh.
+	 */
+	if (reporting_unit && strcmp(reporting_unit, "mAh") == 0) {
+		my_dbus_error_free(&error);
+		design_voltage = libhal_device_get_property_int(ctx, udi,
+		    "battery.voltage.design", &error);
+		/*
+		 * If the present_voltage is inaccurate, set it to the
+		 * design_voltage.
+		 */
+		if (((present_voltage * 10) < design_voltage) ||
+		    (present_voltage <= 0) ||
+		    (present_voltage > design_voltage)) {
+			present_voltage = design_voltage;
+		}
+		reporting_rate = (reporting_rate * present_voltage) / 1000;
+		reporting_lastfull = (reporting_lastfull * present_voltage) /
+		    1000;
+		reporting_current = (reporting_current * present_voltage) /
+		    1000;
+	}
+
+	/* Make sure the current charge does not exceed the full charge */
+	if (reporting_current > reporting_lastfull) {
+		reporting_current = reporting_lastfull;
+	}
+	if (!charging && !discharging) {
+		counter++;
+		reporting_rate = 0;
+	}
+
+	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
+		HAL_DEBUG(("Cannot allocate changeset"));
+		libhal_free_string(reporting_unit);
+		my_dbus_error_free(&error);
+		return;
+	}
+
+	libhal_changeset_set_property_int(cs, "battery.charge_level.rate",
+	    reporting_rate);
+	libhal_changeset_set_property_int(cs,
+	    "battery.charge_level.last_full", reporting_lastfull);
+	libhal_changeset_set_property_int(cs,
+	    "battery.charge_level.current", reporting_current);
+
+	remaining_percentage = util_compute_percentage_charge(udi,
+	    reporting_current, reporting_lastfull);
+	remaining_time = util_compute_time_remaining(udi, reporting_rate,
+	    reporting_current, reporting_lastfull, discharging, charging, 0);
+	/*
+	 * Some batteries give bad remaining_time estimates relative to
+	 * the charge level.
+	 */
+	if (charging && ((remaining_time < 30) || ((remaining_time < 300) &&
+	    (remaining_percentage < 95)) || (remaining_percentage > 97))) {
+		remaining_time = util_compute_time_remaining(udi,
+		    reporting_rate, reporting_current, reporting_lastfull,
+		    discharging, charging, 1);
+	}
+
+	if (remaining_percentage > 0) {
+		libhal_changeset_set_property_int(cs,
+		    "battery.charge_level.percentage", remaining_percentage);
+	} else {
+		my_dbus_error_free(&error);
+		libhal_device_remove_property(ctx, udi,
+		    "battery.charge_level.percentage", &error);
+	}
+	if ((remaining_percentage == 100) && charging) {
+		battery_last_full(cs, fd);
+	}
+	/*
+	 * remaining_percentage is more accurate so we handle cases
+	 * where the remaining_time cannot be correct.
+	 */
+	if ((!charging && !discharging) || ((remaining_percentage == 100) &&
+	    !discharging)) {
+		remaining_time = 0;
+	}
+	if (remaining_time < 0) {
+		my_dbus_error_free(&error);
+		libhal_device_remove_property(ctx, udi,
+		    "battery.remaining_time", &error);
+	} else if (remaining_time >= 0) {
+		libhal_changeset_set_property_int(cs,
+		    "battery.remaining_time", remaining_time);
+	}
+
+	my_dbus_error_free(&error);
+	libhal_device_commit_changeset(ctx, cs, &error);
+	libhal_device_free_changeset(cs);
+	libhal_free_string(reporting_unit);
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("battery_dynamic_update() exit"));
+}
+
+static gboolean
+battery_static_update(LibHalContext *ctx, const char *udi, int fd)
+{
+	const char *technology;
+	int reporting_design;
+	int reporting_warning;
+	int reporting_low;
+	int reporting_gran1;
+	int reporting_gran2;
+	int voltage_design;
+	char reporting_unit[10];
+	acpi_bif_t bif;
+	LibHalChangeSet *cs;
+	DBusError error;
+
+	HAL_DEBUG(("battery_static_update() enter"));
+	bzero(&bif, sizeof (bif));
+	if (ioctl(fd, BATT_IOC_INFO, &bif) < 0) {
+		return (FALSE);
+	}
+	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
+		HAL_DEBUG(("Cannot allocate changeset"));
+		return (FALSE);
+	}
+
+	libhal_changeset_set_property_string(cs, "battery.vendor",
+	    bif.bif_oem_info);
+	technology = bif.bif_type;
+	if (technology != NULL) {
+		libhal_changeset_set_property_string(cs,
+		    "battery.reporting.technology", technology);
+		libhal_changeset_set_property_string(cs, "battery.technology",
+		    util_get_battery_technology(technology));
+	}
+	libhal_changeset_set_property_string(cs, "battery.serial",
+	    bif.bif_serial);
+	libhal_changeset_set_property_string(cs, "battery.model",
+	    bif.bif_model);
+
+	if (bif.bif_unit) {
+		libhal_changeset_set_property_string(cs,
+		    "battery.reporting.unit", "mAh");
+		strlcpy(reporting_unit, "mAh", sizeof (reporting_unit));
+	} else {
+		libhal_changeset_set_property_string(cs,
+		    "battery.reporting.unit", "mWh");
+		strlcpy(reporting_unit, "mWh", sizeof (reporting_unit));
+	}
+	libhal_changeset_set_property_int(cs, "battery.reporting.last_full",
+	    bif.bif_last_cap);
+	libhal_changeset_set_property_int(cs, "battery.reporting.design",
+	    bif.bif_design_cap);
+	reporting_design = bif.bif_design_cap;
+	libhal_changeset_set_property_int(cs, "battery.reporting.warning",
+	    bif.bif_warn_cap);
+	reporting_warning = bif.bif_warn_cap;
+	libhal_changeset_set_property_int(cs, "battery.reporting.low",
+	    bif.bif_low_cap);
+	reporting_low = bif.bif_low_cap;
+	libhal_changeset_set_property_int(cs,
+	    "battery.reporting.granularity_1", bif.bif_gran1_cap);
+	reporting_gran1 = bif.bif_gran1_cap;
+	libhal_changeset_set_property_int(cs,
+	    "battery.reporting.granularity_2", bif.bif_gran2_cap);
+	reporting_gran2 = bif.bif_gran2_cap;
+	libhal_changeset_set_property_int(cs, "battery.voltage.design",
+	    bif.bif_voltage);
+	voltage_design = bif.bif_voltage;
+
+	if (reporting_unit && strcmp(reporting_unit, "mAh") == 0) {
+		/* convert to mWh */
+		libhal_changeset_set_property_string(cs,
+		    "battery.charge_level.unit", "mWh");
+		libhal_changeset_set_property_int(cs,
+		    "battery.charge_level.design",
+		    (reporting_design * voltage_design) / 1000);
+		libhal_changeset_set_property_int(cs,
+		    "battery.charge_level.warning",
+		    (reporting_warning * voltage_design) / 1000);
+		libhal_changeset_set_property_int(cs,
+		    "battery.charge_level.low",
+		    (reporting_low * voltage_design) / 1000);
+		libhal_changeset_set_property_int(cs,
+		    "battery.charge_level.granularity_1",
+		    (reporting_gran1 * voltage_design) / 1000);
+		libhal_changeset_set_property_int(cs,
+		    "battery.charge_level.granularity_2",
+		    (reporting_gran2 * voltage_design) / 1000);
+	} else {
+		if (reporting_unit && strcmp(reporting_unit, "mWh") == 0) {
+			libhal_changeset_set_property_string(cs,
+			    "battery.charge_level.unit", "mWh");
+		}
+		libhal_changeset_set_property_int(cs,
+                    "battery.charge_level.design", reporting_design);
+                libhal_changeset_set_property_int(cs,
+                    "battery.charge_level.warning", reporting_warning);
+                libhal_changeset_set_property_int(cs,
+                    "battery.charge_level.low", reporting_low);
+                libhal_changeset_set_property_int(cs,
+                    "battery.charge_level.granularity_1", reporting_gran1);
+                libhal_changeset_set_property_int(cs,
+                    "battery.charge_level.granularity_2", reporting_gran2);
+	}
+		
+
+	dbus_error_init(&error);
+	libhal_device_commit_changeset(ctx, cs, &error);
+	libhal_device_free_changeset(cs);
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("battery_static_update() exit"));
+	return (TRUE);
+}
+
+gboolean
+battery_update(LibHalContext *ctx, const char *udi, int fd)
+{
+	acpi_bst_t bst;
+	DBusError error;
+
+	HAL_DEBUG(("battery_update() enter"));
+	dbus_error_init(&error);
+	libhal_device_set_property_string(ctx, udi, "info.product",
+	    "Battery Bay", &error);
+	my_dbus_error_free(&error);
+	libhal_device_set_property_string(ctx, udi, "info.category", "battery",
+	    &error);
+
+	bzero(&bst, sizeof (bst));
+	if (ioctl(fd, BATT_IOC_STATUS, &bst) < 0) {
+		if (errno == ENXIO) {
+			my_dbus_error_free(&error);
+			libhal_device_set_property_bool(ctx, udi,
+			    "battery.present", FALSE, &error);
+		} else {
+			my_dbus_error_free(&error);
+			return (FALSE);
+		}
+	} else {
+		my_dbus_error_free(&error);
+		libhal_device_set_property_bool(ctx, udi, "battery.present",
+		    TRUE, &error);
+	}
+
+	my_dbus_error_free(&error);
+	if (!libhal_device_get_property_bool(ctx, udi, "battery.present",
+	    &error)) {
+		HAL_DEBUG(("battery_update(): battery is NOT present"));
+		battery_remove(ctx, udi);
+	} else {
+		HAL_DEBUG(("battery_update(): battery is present"));
+		my_dbus_error_free(&error);
+		libhal_device_set_property_string(ctx, udi, "battery.type",
+		    "primary", &error);
+		my_dbus_error_free(&error);
+		libhal_device_add_capability(ctx, udi, "battery", &error);
+		my_dbus_error_free(&error);
+		if (libhal_device_get_property_type(ctx, udi, "battery.vendor",
+		    &error) == LIBHAL_PROPERTY_TYPE_INVALID) {
+			battery_static_update(ctx, udi, fd);
+		}
+		battery_dynamic_update(ctx, udi, fd);
+	}
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("battery_update() exit"));
+	return (TRUE);
+}
+
+static gboolean
+battery_update_all(LibHalContext *ctx)
+{
+	int i;
+	int num_devices;
+	char **battery_devices;
+	int fd;
+	DBusError error;
+
+	HAL_DEBUG(("battery_update_all() enter"));
+
+	dbus_error_init(&error);
+	if ((battery_devices = libhal_manager_find_device_string_match
+	    (ctx, "info.category", "battery", &num_devices, &error)) !=
+	    NULL) {
+		for (i = 0; i < num_devices; i++) {
+			my_dbus_error_free(&error);
+			if (libhal_device_get_property_bool(ctx,
+			    battery_devices[i], "battery.present", &error)) {
+				if ((fd = open_device(ctx,
+				    battery_devices[i])) == -1) {
+					continue;
+				}
+				battery_dynamic_update(ctx, battery_devices[i],
+				    fd);
+				close(fd);
+			}
+		}
+		libhal_free_string_array(battery_devices);
+	}
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("battery_update_all() exit"));
+	return (TRUE);
+}
+
+gboolean
+ac_adapter_update(LibHalContext *ctx, const char *udi, int fd)
+{
+	LibHalChangeSet *cs;
+	DBusError error;
+
+	HAL_DEBUG(("ac_adapter_update() enter"));
+	dbus_error_init(&error);
+	if (!libhal_device_query_capability(ctx, udi, "ac_adapter", &error)) {
+		my_dbus_error_free(&error);
+		libhal_device_add_capability(ctx, udi, "ac_adapter", &error);
+		if ((cs = libhal_device_new_changeset(udi)) == NULL) {
+			my_dbus_error_free(&error);
+			return (FALSE);
+		}
+		libhal_changeset_set_property_string(cs, "info.product",
+		    "AC Adapter");
+		libhal_changeset_set_property_string(cs, "info.category",
+		    "ac_adapter");
+		my_dbus_error_free(&error);
+		libhal_device_commit_changeset(ctx, cs, &error);
+		libhal_device_free_changeset(cs);
+	}
+	ac_adapter_present(ctx, udi, fd);
+	battery_update_all(ctx);
+
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("ac_adapter_update() exit"));
+	return (TRUE);
+}
+
+static gboolean
+ac_adapter_update_all(LibHalContext *ctx)
+{
+	int i;
+	int num_devices;
+	char **ac_adapter_devices;
+	int fd;
+	DBusError error;
+
+	HAL_DEBUG(("ac_adapter_update_all() enter"));
+	dbus_error_init(&error);
+	if ((ac_adapter_devices = libhal_manager_find_device_string_match(
+	    ctx, "info.category", "ac_adapter", &num_devices, &error)) !=
+	    NULL) {
+		for (i = 0; i < num_devices; i++) {
+			if ((fd = open_device(ctx, ac_adapter_devices[i]))
+			    == -1) {
+				continue;
+			}
+			ac_adapter_present(ctx, ac_adapter_devices[i], fd);
+			close(fd);
+		}
+		libhal_free_string_array(ac_adapter_devices);
+	}
+	my_dbus_error_free(&error);
+	HAL_DEBUG(("ac_adapter_update_all() exit"));
+	return (TRUE);
+}
+
+gboolean
+update_devices(gpointer data)
+{
+	LibHalContext *ctx = (LibHalContext *)data;
+
+	HAL_DEBUG(("update_devices() enter"));
+	ac_adapter_update_all(ctx);
+	battery_update_all(ctx);
+	HAL_DEBUG(("update_devices() exit"));
+	return (TRUE);
+}
+
+int
+open_device(LibHalContext *ctx, char *udi)
+{
+	char path[HAL_PATH_MAX] = "/devices";
+	char *devfs_path;
+	DBusError error;
+
+	dbus_error_init(&error);
+	devfs_path = libhal_device_get_property_string(ctx, udi,
+	    "solaris.devfs_path", &error);
+	my_dbus_error_free(&error);
+	if (devfs_path == NULL) {
+		return (-1);
+	}
+	strlcat(path, devfs_path, HAL_PATH_MAX);
+	libhal_free_string(devfs_path);
+	return (open(path, O_RDONLY | O_NONBLOCK));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/hal/utils/battery.h	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,26 @@
+/***************************************************************************
+ *
+ * battery.h 
+ *
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ **************************************************************************/
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifndef BATTERY_H
+#define	BATTERY_H
+
+#include "../hald/util.h"
+
+#define	BATTERY_POLL_TIMER		30000
+
+gboolean battery_update(LibHalContext *ctx, const char *udi, int fd);
+gboolean ac_adapter_update(LibHalContext *ctx, const char *udi, int fd);
+gboolean update_devices(gpointer data);
+int open_device(LibHalContext *ctx, char *udi);
+
+#endif /* BATTERY_H */
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386	Sat Apr 14 18:08:01 2007 -0700
@@ -61,6 +61,8 @@
 f none kernel/devname/sdev_nsconfig_mod 755 root sys
 f none kernel/drv/aggr 755 root sys
 f none kernel/drv/arp 755 root sys
+f none kernel/drv/battery 755 root sys
+f none kernel/drv/battery.conf 644 root sys
 f none kernel/drv/bl 755 root sys
 f none kernel/drv/bmc 755 root sys
 f none kernel/drv/bmc.conf 644 root sys
@@ -260,6 +262,7 @@
 d none kernel/drv/amd64 755 root sys
 f none kernel/drv/amd64/aggr 755 root sys
 f none kernel/drv/amd64/arp 755 root sys
+f none kernel/drv/amd64/battery 755 root sys
 f none kernel/drv/amd64/bl 755 root sys
 f none kernel/drv/amd64/bmc 755 root sys
 f none kernel/drv/amd64/bscbus 755 root sys
--- a/usr/src/pkgdefs/SUNWhal/prototype_com	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/pkgdefs/SUNWhal/prototype_com	Sat Apr 14 18:08:01 2007 -0700
@@ -47,7 +47,9 @@
 d none usr/lib 755 root bin
 d none usr/lib/hal 755 root bin
 f none usr/lib/hal/hald 555 root bin
+f none usr/lib/hal/hald-addon-acpi 555 root bin
 f none usr/lib/hal/hald-addon-storage 555 root bin
+f none usr/lib/hal/hald-probe-battery 555 root bin
 f none usr/lib/hal/hald-probe-printer 555 root bin
 f none usr/lib/hal/hald-probe-storage 555 root bin
 f none usr/lib/hal/hald-probe-volume 555 root bin
--- a/usr/src/pkgdefs/SUNWhea/prototype_com	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com	Sat Apr 14 18:08:01 2007 -0700
@@ -1113,6 +1113,7 @@
 f none usr/include/sys/sysconfig.h 644 root bin
 f none usr/include/sys/sysconfig_impl.h 644 root bin
 d none usr/include/sys/sysevent 755 root bin
+f none usr/include/sys/sysevent/acpiev.h 644 root bin
 f none usr/include/sys/sysevent/ap_driver.h 644 root bin
 f none usr/include/sys/sysevent/domain.h 644 root bin
 f none usr/include/sys/sysevent/dr.h 644 root bin
--- a/usr/src/pkgdefs/common_files/i.minorperm_i386	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/pkgdefs/common_files/i.minorperm_i386	Sat Apr 14 18:08:01 2007 -0700
@@ -273,6 +273,7 @@
 physmem:*
 asy:*
 asy:*,cu
+battery:*
 EOF
 }
 
--- a/usr/src/uts/common/sys/Makefile	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/common/sys/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -821,6 +821,7 @@
 	sata_cfgadm.h
 
 SYSEVENTHDRS=		\
+	acpiev.h	\
 	ap_driver.h     \
 	dev.h		\
 	domain.h        \
--- a/usr/src/uts/common/sys/battery.h	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/common/sys/battery.h	Sat Apr 14 18:08:01 2007 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -19,12 +18,14 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright (c) 1994, by Sun Microsystems, Inc.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
  */
 
-#ifndef	_SYS_BATTERY_H
-#define	_SYS_BATTERY_H
+#ifndef _BATTERY_H
+#define	_BATTERY_H
 
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
 
@@ -32,56 +33,178 @@
 extern "C" {
 #endif
 
-/*
- * battery.h:	Declarations for the common battery interface.
- *	It is expected that any module supporting /dev/battery
- *	will support the following ioctls. When the BATT_STATUS
- *	ioctl is used, a module may return -1 for any fields which
- *	are not known.
- */
+#include <sys/param.h>
+
+#define	_BATT_DRV		(('B' << 24) + ('A' << 16) + ('T' << 8))
+
+#define	BATT_IOC_BAY		(_BATT_DRV | 0)
+#define	BATT_IOC_INFO		(_BATT_DRV | 1)
+#define	BATT_IOC_STATUS		(_BATT_DRV | 2)
+#define	BATT_IOC_AC_COUNT	(_BATT_DRV | 3)
+#define	BATT_IOC_POWER_STATUS	(_BATT_DRV | 4)
+#define	BATT_IOC_SET_WARNING	(_BATT_DRV | 5)
+#define	BATT_IOC_GET_WARNING	(_BATT_DRV | 6)
+
+#define	BATT_BST_CHARGING	2
+#define	BATT_BST_DISCHARGING	1
+
+typedef struct batt_bay {
+	/* Total number of bays in the system */
+	int bay_number;
+
+	/*
+	 * Bitmap for each bay and its battery.
+	 * battery_map bit i:
+	 *    1 -- battery inserted to bay i
+	 *    0 -- bay i empty
+	 */
+	uint64_t battery_map;
+} batt_bay_t;
+
+typedef	struct acpi_bif {
+	uint32_t	bif_unit;
+
+	/*
+	 * 0x00000000 - 0x7fffffff
+	 * 0xffffffff - Unknown design capacity in [mWh] or [mAh]
+	 */
+	uint32_t	bif_design_cap;
 
-#define	BATT_IDSTR_LEN	40
+	/*
+	 * 0x00000000 - 0x7fffffff
+	 * 0xffffffff - Unknown last full charge capacity in [mWh] or [mAh]
+	 */
+	uint32_t	bif_last_cap;
+
+	uint32_t	bif_tech;
+
+	/*
+	 * 0x00000000 - 0x7fffffff
+	 * 0xffffffff - Unknown design voltage in [mV]
+	 */
+	uint32_t	bif_voltage;
+
+	/*
+	 * 0x00000000 - 0x7fffffff in [mWh] or [mAh]
+	 */
+	uint32_t	bif_warn_cap;
 
-/*
- * Generic ioctls
- */
-typedef enum {
-	BATT_STATUS = 0,	/* Module will return a battery_t structure */
-	BATT_ESCAPE		/* Module specific */
-} batt_ioctl_t;
+	/*
+	 * 0x00000000 - 0x7fffffff in [mWh] or [mAh]
+	 */
+	uint32_t	bif_low_cap;
+
+	uint32_t	bif_gran1_cap;
+	uint32_t	bif_gran2_cap;
+	char		bif_model[MAXNAMELEN];
+	char		bif_serial[MAXNAMELEN];
+	char		bif_type[MAXNAMELEN];
+	char		bif_oem_info[MAXNAMELEN];
+} acpi_bif_t;
+
+typedef	struct acpi_bst {
+	uint32_t	bst_state;
+
+	/*
+	 * 0x00000000 - 0x7fffffff in [mW] or [mA]
+	 * 0xffffffff - Unknown rate
+	 */
+	uint32_t	bst_rate;
+
+	/*
+	 * 0x00000000 - 0x7fffffff in [mWh] or [mAh]
+	 * 0xffffffff - Unknown capacity
+	 */
+	uint32_t	bst_rem_cap;
 
-/*
- * Response fields
- */
-typedef enum {
-	NOT_PRESENT = 0,
-	EMPTY,			/* Battery has (effectively) no capacity */
-	LOW_CAPACITY,		/* Battery has less than 25% capacity */
-	MED_CAPACITY,		/* Battery has less than 50% capacity */
-	HIGH_CAPACITY,		/* Battery has less than 75% capacity */
-	FULL_CAPACITY,		/* Battery has more than 75% capacity */
-	EOL			/* Battery is dead */
-} batt_status_t;
+	/*
+	 * 0x00000000 - 0x7fffffff in [mV]
+	 * 0xffffffff - Unknown voltage
+	 */
+	uint32_t	bst_voltage;
+} acpi_bst_t;
+
+/* Battery warnning levels in percentage */
+typedef struct batt_warn {
+	uint32_t	bw_enabled;	/* Enabled */
+	uint32_t	bw_charge_warn;	/* charge warn threshold */
+	uint32_t	bw_charge_low;	/* charge low threshold */
+} batt_warn_t;
+
+#define	BATT_DRV_NAME		"battery"
+#define	BATT_POWER_KSTAT_NAME	"power"
+#define	BATT_BTWARN_KSTAT_NAME	"battery warning"
+#define	BATT_BIF_KSTAT_NAME	"battery BIF"
+#define	BATT_BST_KSTAT_NAME	"battery BST"
+
+#define	AC			"AC"
+#define	BATTERY			"battery"
+#define	SYSTEM_POWER		"system power"
+#define	SUPPORTED_BATTERY_COUNT	"supported_battery_count"
+
+#define	BW_ENABLED		"enabled"
+#define	BW_POWEROFF_THRESHOLD	"warn capacity threshold"
+#define	BW_SHUTDOWN_THRESHOLD	"low capacity threshold"
+
+#define	BIF_UNIT		"bif_unit"
+#define	BIF_DESIGN_CAP		"bif_design_cap"
+#define	BIF_LAST_CAP		"bif_last_cap"
+#define	BIF_TECH		"bif_tech"
+#define	BIF_VOLTAGE		"bif_voltage"
+#define	BIF_WARN_CAP		"bif_warn_cap"
+#define	BIF_LOW_CAP		"bif_low_cap"
+#define	BIF_GRAN1_CAP		"bif_gran1_cap"
+#define	BIF_GRAN2_CAP		"bif_gran2_cap"
+#define	BIF_MODEL		"bif_model"
+#define	BIF_SERIAL		"bif_serial"
+#define	BIF_TYPE		"bif_type"
+#define	BIF_OEM_INFO		"bif_oem_info"
 
-typedef enum {
-	DISCHARGE = 0,		/* Battery is discharging (i.e. in use) */
-	FULL_CHARGE,		/* Battery is charging at its fastest rate */
-	TRICKLE_CHARGE		/* Battery is charging at a slower rate */
-} batt_charge_t;
+#define	BST_STATE		"bst_state"
+#define	BST_RATE		"bst_rate"
+#define	BST_REM_CAP		"bst_rem_cap"
+#define	BST_VOLTAGE		"bst_voltage"
+
+#define	PSR_AC_PRESENT		"psr_ac_present"
+
+typedef struct batt_power_kstat_s {
+	struct kstat_named	batt_power;
+	struct kstat_named	batt_supported_battery_count;
+} batt_power_kstat_t;
+
+typedef struct batt_warn_kstat_s {
+	struct kstat_named	batt_bw_enabled;
+	struct kstat_named	batt_bw_charge_warn;
+	struct kstat_named	batt_bw_charge_low;
+} batt_warn_kstat_t;
 
-typedef struct {
-	char		id_string[BATT_IDSTR_LEN];
-	int		total;		/* Total capacity (mWhrs) */
-	char		capacity;	/* Current capacity (percentage) */
-	int		discharge_rate;	/* Current discharge rate (mW) */
-	int		discharge_time;	/* Discharge time at current rate (s) */
-	batt_status_t	status;		/* General battery status */
-	batt_charge_t	charge;		/* Current charging condition */
-} battery_t;
+/* BIF kstat */
+typedef struct batt_bif_kstat_s {
+	struct kstat_named	batt_bif_unit;
+	struct kstat_named	batt_bif_design_cap;
+	struct kstat_named	batt_bif_last_cap;
+	struct kstat_named	batt_bif_tech;
+	struct kstat_named	batt_bif_voltage;
+	struct kstat_named	batt_bif_warn_cap;
+	struct kstat_named	batt_bif_low_cap;
+	struct kstat_named	batt_bif_gran1_cap;
+	struct kstat_named	batt_bif_gran2_cap;
+	struct kstat_named	batt_bif_model;
+	struct kstat_named	batt_bif_serial;
+	struct kstat_named	batt_bif_type;
+	struct kstat_named	batt_bif_oem_info;
+} batt_bif_kstat_t;
 
+/* BST kstat */
+typedef struct batt_bst_kstat_s {
+	struct kstat_named	batt_bst_state;
+	struct kstat_named	batt_bst_rate;
+	struct kstat_named	batt_bst_rem_cap;
+	struct kstat_named	batt_bst_voltage;
+} batt_bst_kstat_t;
 
 #ifdef	__cplusplus
 }
 #endif
 
-#endif	/* _SYS_BATTERY_H */
+#endif /* _BATTERY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/sysevent/acpiev.h	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SYSEVENT_ACPIEV_H
+#define	_SYS_SYSEVENT_ACPIEV_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Event type schema for EC_ACPIEV:
+ *	Event Class	- EC_ACPIEV
+ *	Event Sub-Class	- ESC_ACPIEV_ADD |
+ *                        ESC_ACPIEV_REMOVE |
+ *                        ESC_ACPIEV_WARN |
+ *                        ESC_ACPIEV_LOW |
+ *                        ESC_ACPIEV_STATE_CHANGE
+ *	Event Publisher	- SUNW:kern:[environmental monitor name]
+ *	Attribute Name	- ACPIEV_VERSION
+ *	Attribute Type	- SE_DATA_TYPE_UINT32
+ *	Attribute Value	- [version of the schema]
+ *	Attribute Name	- ACPIEV_DEV_HID
+ *	Attribute Type  - SE_DATA_TYPE_STRING
+ *	Attribute Value	- [Label identifying the ACPI hardware]
+ *	Attribute Name	- ACPIEV_DEV_UID
+ *	Attribute Type  - SE_DATA_TYPE_STRING
+ *	Attribute Value	- [Both the _HID and _UID values can be of either type
+ *	                   STRING or NUMBER in the ACPI tables. In order to
+ *	                   provide a consistent data type in the external
+ *	                   interface, these values are always returned as NULL
+ *	                   terminated strings, regardless of the original data
+ *	                   type in the source ACPI table.]
+ *	Attribute Name 	- ACPIEV_DEV_INDEX
+ *	Attribute Type	- SE_DATA_TYPE_UINT32
+ *	Attribute Value	- [Device index]
+ *
+ * ESC_ACPIEV_WARN, ESC_ACPIEV_LOW only field:
+ *	Attribute Name	- ACPIEV_CHARGE_LEVEL
+ *	Attribute Type  - SE_DATA_TYPE_UINT32
+ *	Attribute Value	- [charge level]
+ */
+
+#define	ACPIEV_VERSION		"acpiev_version" /* Version of the schema */
+#define	ACPIEV_DEV_PHYS_PATH	"acpiev_dev_phys_path" /* Physical Path */
+#define	ACPIEV_DEV_HID		"acpiev_dev_hid" /* ACPI device Hardware Id */
+#define	ACPIEV_DEV_UID		"acpiev_dev_uid" /* ACPI device Unique Id */
+#define	ACPIEV_DEV_INDEX	"acpiev_dev_index" /* Device index */
+#define	ACPIEV_CHARGE_LEVEL	"acpiev_charge_level" /* Event related state */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _SYS_SYSEVENT_ACPIEV_H */
--- a/usr/src/uts/common/sys/sysevent/eventdefs.h	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/common/sys/sysevent/eventdefs.h	Sat Apr 14 18:08:01 2007 -0700
@@ -204,6 +204,16 @@
 /* Service processor subclass definitions */
 #define	ESC_PLATFORM_SP_RESET	"ESC_platform_sp_reset"
 
+/*
+ * EC_ACPIEV subclass definitions
+ */
+#define	EC_ACPIEV			"EC_acpiev"
+#define	ESC_ACPIEV_ADD			"ESC_acpiev_add"
+#define	ESC_ACPIEV_REMOVE		"ESC_acpiev_remove"
+#define	ESC_ACPIEV_WARN			"ESC_acpiev_warn"
+#define	ESC_ACPIEV_LOW			"ESC_acpiev_low"
+#define	ESC_ACPIEV_STATE_CHANGE		"ESC_acpiev_state_change"
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/i86pc/Makefile.files	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/i86pc/Makefile.files	Sat Apr 14 18:08:01 2007 -0700
@@ -163,6 +163,7 @@
 PCINEXUS_OBJS += pci.o pci_common.o pci_kstats.o pci_tools.o
 PCPLUSMP_OBJS += apic.o psm_common.o apic_introp.o mp_platform_common.o
 
+BATTERY_OBJS	+= battery.o
 include $(SRC)/common/mc/mc-amd/Makefile.mcamd
 MCAMD_OBJS	+= \
 	$(MCAMD_CMN_OBJS) \
--- a/usr/src/uts/i86pc/Makefile.i86pc.shared	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/i86pc/Makefile.i86pc.shared	Sat Apr 14 18:08:01 2007 -0700
@@ -251,6 +251,7 @@
 DRV_KMODS	+= xsvc
 DRV_KMODS	+= mc-amd
 DRV_KMODS	+= tzmon
+DRV_KMODS	+= battery
 
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= memtest
 
--- a/usr/src/uts/i86pc/Makefile.rules	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/i86pc/Makefile.rules	Sat Apr 14 18:08:01 2007 -0700
@@ -63,6 +63,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:                $(UTSBASE)/i86pc/io/battery/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/i86pc/io/mc/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -229,6 +233,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/i86pc/io/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:              $(UTSBASE)/i86pc/io/battery/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/i86pc/io/mc/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/i86pc/battery/Makefile	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,104 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#	This makefile drives the production of the battery
+#	Battery Monitor driver kernel module.
+#
+#	i86pc architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= battery
+OBJECTS		= $(BATTERY_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(BATTERY_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/i86pc/io/battery
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(CONFMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+DEBUG_FLGS      =
+$(NOT_RELEASE_BUILD)DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+CPPFLAGS	+= -DSUNDDI
+
+LDFLAGS		+= -dy -N misc/acpica
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
+
+#
+#	Overrides
+#
+C99LMODE=	-Xc99=%all
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/i86pc/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/i86pc/io/battery/battery.c	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,1972 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Solaris x86 ACPI Battery Monitor
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stat.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/acpiev.h>
+#include <sys/reboot.h>
+#include <sys/acpi/acpi.h>
+#include <sys/note.h>
+#include <sys/battery.h>
+
+
+#define	BATT_MOD_STRING			"ACPI battery driver %I%"
+
+#define	MINOR_SHIFT			8
+#define	IDX_MASK			((1 << MINOR_SHIFT) - 1)
+#define	MINOR_BATT(idx)			(BATT_TYPE_CBAT << MINOR_SHIFT | (idx))
+#define	MINOR_AC(idx)			(BATT_TYPE_AC << MINOR_SHIFT | (idx))
+#define	MINOR2IDX(minor)		((minor) & IDX_MASK)
+#define	MINOR2TYPE(minor)		((minor) >> MINOR_SHIFT)
+
+#define	BATT_OK				(0)
+#define	BATT_ERR			(1)
+
+#define	BATT_MAX_BAT_NUM		8
+#define	BATT_MAX_AC_NUM			10
+
+#define	BST_FLAG_DISCHARGING		(0x1)
+#define	BST_FLAG_CHARGING		(0x2)
+#define	BST_FLAG_CRITICAL		(0x4)
+
+/* Set if the battery is present */
+#define	STA_FLAG_BATT_PRESENT		(0x10)
+
+#define	ACPI_DEVNAME_CBAT		"PNP0C0A"
+#define	ACPI_DEVNAME_SBAT		"ACPI0002"
+#define	ACPI_DEVNAME_AC			"ACPI0003"
+
+#define	BATT_EVENTS			(POLLIN | POLLRDNORM)
+
+#ifdef DEBUG
+
+#define	BATT_PRINT_BUFFER_SIZE		512
+static char batt_prt_buf[BATT_PRINT_BUFFER_SIZE];
+static kmutex_t batt_prt_mutex;
+
+static int batt_debug = 0;
+#define	BATT_DBG(lev, devp, ...) \
+	do { \
+		if (batt_debug) batt_printf((devp), (lev), __VA_ARGS__); \
+_NOTE(CONSTCOND) } while (0)
+#define	BATT_PRT_NOTIFY(hdl, val) \
+	do { \
+		if (batt_debug) batt_prt_notify((hdl), (val)); \
+_NOTE(CONSTCOND) } while (0)
+
+#else
+
+#define	BATT_DBG(lev, devp, ...)
+#define	BATT_PRT_NOTIFY(hdl, val)
+
+#endif /* DEBUG */
+
+/* ACPI notify types */
+enum batt_notify {
+	BATT_NTF_UNKNOWN = -1,	/* No notifications seen, ever. */
+	BATT_NTF_CHANGED,
+	BATT_NTF_OK
+};
+
+/* Battery device types */
+enum batt_type {
+	BATT_TYPE_UNKNOWN = -1,
+	BATT_TYPE_CBAT,
+	BATT_TYPE_AC,
+	BATT_TYPE_SBAT
+};
+
+struct batt_acpi_dev {
+	ACPI_HANDLE hdl;
+	char hid[9];	/* ACPI HardwareId */
+	char uid[9];	/* ACPI UniqueId */
+	int valid;	/* the device state is valid */
+
+	/*
+	 * Unlike most other devices, when a battery is inserted or
+	 * removed from the system, the device itself(the battery bay)
+	 * is still considered to be present in the system.
+	 *
+	 * Value:
+	 *    0 -- On-line
+	 *    1 -- Off-line
+	 *   -1 -- Unknown
+	 */
+	int present;
+	enum batt_type type;
+	int index;	/* device index */
+};
+
+static int batt_dev_present(struct batt_acpi_dev *);
+#define	batt_ac_present(a)	(((a)->dev.type == BATT_TYPE_AC) ? \
+				batt_dev_present(&(a)->dev) : -1)
+#define	batt_cbat_present(a)	(((a)->dev.type == BATT_TYPE_CBAT) ? \
+				batt_dev_present(&(a)->dev) : -1)
+
+static dev_info_t *batt_dip = NULL;
+static kmutex_t batt_mutex;
+static struct pollhead batt_pollhead;
+
+/* Control Method Battery state */
+struct batt_cbat_state {
+	struct batt_acpi_dev dev;
+/* Caches of _BST and _BIF */
+	enum batt_notify bat_bifok;
+	acpi_bif_t bif_cache;
+	enum batt_notify bat_bstok;
+	acpi_bst_t bst_cache;
+
+	uint32_t charge_warn;
+	uint32_t charge_low;
+
+	kstat_t *bat_bif_ksp;
+	kstat_t *bat_bst_ksp;
+} batt_cbat[BATT_MAX_BAT_NUM];
+static int nbat;
+
+/*
+ * Synthesis battery state
+ * When there are multiple batteries present, the battery subsystem
+ * is not required to perform any synthesis of a composite battery
+ * from the data of the separate batteries. In cases where the
+ * battery subsystem does not synthesize a composite battery from
+ * the separate battery's data, the OS must provide that synthesis.
+ */
+static uint32_t batt_syn_rem_cap;
+static uint32_t batt_syn_last_cap;
+static uint32_t batt_syn_oem_warn_cap;
+static uint32_t batt_syn_oem_low_cap;
+
+static int batt_warn_enabled;
+static uint32_t batt_syn_warn_per;
+static uint32_t batt_syn_low_per;
+static uint32_t batt_syn_warn_cap;
+static uint32_t batt_syn_low_cap;
+/* Tracking boundery passing of _BST charge levels */
+static uint32_t batt_syn_last_level;
+
+/* AC state */
+static struct batt_ac_state {
+	struct batt_acpi_dev dev;
+} batt_ac[BATT_MAX_AC_NUM];
+static int nac;
+
+/*
+ * Current power source device
+ * Note: assume only one device can be the power source device.
+ */
+static int batt_psr_type = BATT_TYPE_UNKNOWN;
+static struct batt_acpi_dev *batt_psr_devp = NULL;
+
+/* Smart Battery state */
+static struct batt_sbat_state {
+	struct batt_acpi_dev dev;
+} batt_sbat;
+
+struct obj_desc {
+	char *name;
+	int offset;
+	int size;
+	int type;
+};
+
+/* Object copy definitions */
+#define	OFFSETOF(s, m)		((size_t)(&(((s *)0)->m)))
+#define	SIZEOF(s, m)		(sizeof (((s *)0)->m))
+#define	FIELD(n, s, m, t) \
+	{ n, OFFSETOF(s, m), SIZEOF(s, m), t }
+#define	FIELD_NULL		{ NULL, -1, 0, ACPI_TYPE_ANY }
+
+static struct obj_desc bif_desc[] = {
+	FIELD("bif_unit",	acpi_bif_t, bif_unit,	ACPI_TYPE_INTEGER),
+	FIELD("bif_design_cap", acpi_bif_t, bif_design_cap, ACPI_TYPE_INTEGER),
+	FIELD("bif_last_cap",	acpi_bif_t, bif_last_cap,   ACPI_TYPE_INTEGER),
+	FIELD("bif_tech",	acpi_bif_t, bif_tech,	ACPI_TYPE_INTEGER),
+	FIELD("bif_voltage",	acpi_bif_t, bif_voltage, ACPI_TYPE_INTEGER),
+	FIELD("bif_warn_cap",	acpi_bif_t, bif_warn_cap, ACPI_TYPE_INTEGER),
+	FIELD("bif_low_cap",	acpi_bif_t, bif_low_cap,  ACPI_TYPE_INTEGER),
+	FIELD("bif_gran1_cap",	acpi_bif_t, bif_gran1_cap, ACPI_TYPE_INTEGER),
+	FIELD("bif_gran2_cap",	acpi_bif_t, bif_gran2_cap, ACPI_TYPE_INTEGER),
+	FIELD("bif_model",	acpi_bif_t, bif_model,	ACPI_TYPE_STRING),
+	FIELD("bif_serial",	acpi_bif_t, bif_serial,	ACPI_TYPE_STRING),
+	FIELD("bif_type",	acpi_bif_t, bif_type,	ACPI_TYPE_STRING),
+	FIELD("bif_oem_info",	acpi_bif_t, bif_oem_info, ACPI_TYPE_STRING),
+	FIELD_NULL
+};
+
+static struct obj_desc bst_desc[] = {
+	FIELD("bst_state",   acpi_bst_t, bst_state,	ACPI_TYPE_INTEGER),
+	FIELD("bst_rate",    acpi_bst_t, bst_rate,	ACPI_TYPE_INTEGER),
+	FIELD("bst_rem_cap", acpi_bst_t, bst_rem_cap,	ACPI_TYPE_INTEGER),
+	FIELD("bst_voltage", acpi_bst_t, bst_voltage,	ACPI_TYPE_INTEGER),
+	FIELD_NULL
+};
+
+/* kstat definitions */
+static kstat_t *batt_power_ksp;
+static kstat_t *batt_warn_ksp;
+
+batt_power_kstat_t batt_power_kstat = {
+	{ SYSTEM_POWER,			KSTAT_DATA_STRING },
+	{ SUPPORTED_BATTERY_COUNT,	KSTAT_DATA_UINT32 },
+};
+
+batt_warn_kstat_t batt_warn_kstat = {
+	{ BW_ENABLED,			KSTAT_DATA_UINT32 },
+	{ BW_POWEROFF_THRESHOLD,	KSTAT_DATA_UINT32 },
+	{ BW_SHUTDOWN_THRESHOLD,	KSTAT_DATA_UINT32 },
+};
+
+/* BIF */
+batt_bif_kstat_t batt_bif_kstat = {
+	{ BIF_UNIT,		KSTAT_DATA_UINT32 },
+	{ BIF_DESIGN_CAP,	KSTAT_DATA_UINT32 },
+	{ BIF_LAST_CAP,		KSTAT_DATA_UINT32 },
+	{ BIF_TECH,		KSTAT_DATA_UINT32 },
+	{ BIF_VOLTAGE,		KSTAT_DATA_UINT32 },
+	{ BIF_WARN_CAP,		KSTAT_DATA_UINT32 },
+	{ BIF_LOW_CAP,		KSTAT_DATA_UINT32 },
+	{ BIF_GRAN1_CAP,	KSTAT_DATA_UINT32 },
+	{ BIF_GRAN2_CAP,	KSTAT_DATA_UINT32 },
+	{ BIF_MODEL,		KSTAT_DATA_STRING },
+	{ BIF_SERIAL,		KSTAT_DATA_STRING },
+	{ BIF_TYPE,		KSTAT_DATA_STRING },
+	{ BIF_OEM_INFO,		KSTAT_DATA_STRING },
+};
+
+/* BST */
+batt_bst_kstat_t batt_bst_kstat = {
+	{ BST_STATE,		KSTAT_DATA_UINT32 },
+	{ BST_RATE,		KSTAT_DATA_UINT32 },
+	{ BST_REM_CAP,		KSTAT_DATA_UINT32 },
+	{ BST_VOLTAGE,		KSTAT_DATA_UINT32 },
+};
+
+static int batt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
+static int batt_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
+static int batt_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
+    void **resultp);
+static int batt_open(dev_t *devp, int flag, int otyp, cred_t *crp);
+static int batt_close(dev_t dev, int flag, int otyp, cred_t *crp);
+static int batt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr,
+    int *rval);
+static int batt_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
+    struct pollhead **phpp);
+static int batt_ac_ioctl(int index, int cmd, intptr_t arg, int mode,
+    cred_t *cr, int *rval);
+static int batt_cbat_ioctl(int index, int cmd, intptr_t arg, int mode,
+    cred_t *cr, int *rval);
+#ifdef DEBUG
+static void batt_printf(struct batt_acpi_dev *devp, uint_t lev,
+    const char *fmt, ...);
+#endif
+static int batt_get_bif(acpi_bif_t *bifp, struct batt_cbat_state *bp);
+static int batt_get_bst(acpi_bst_t *bstp, struct batt_cbat_state *bp);
+static int batt_set_warn(batt_warn_t *bwp);
+static struct batt_cbat_state *batt_idx2cbat(int idx);
+static struct batt_ac_state *batt_idx2ac(int idx);
+static int batt_acpi_init(void);
+static void batt_acpi_fini(void);
+static int batt_kstat_init(void);
+static void batt_kstat_fini(void);
+
+static struct cb_ops batt_cb_ops = {
+	batt_open,		/* open */
+	batt_close,		/* close */
+	nodev,			/* strategy */
+	nodev,			/* print */
+	nodev,			/* dump */
+	nodev,			/* read */
+	nodev,			/* write */
+	batt_ioctl,		/* ioctl */
+	nodev,			/* devmap */
+	nodev,			/* mmap */
+	nodev,			/* segmap */
+	batt_chpoll,		/* chpoll */
+	ddi_prop_op,		/* prop_op */
+	NULL,			/* streamtab */
+	D_NEW | D_MP,
+	CB_REV,
+	nodev,
+	nodev
+};
+
+static struct dev_ops batt_dev_ops = {
+	DEVO_REV,
+	0,			/* refcnt */
+	batt_getinfo,		/* getinfo */
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	batt_attach,		/* attach */
+	batt_detach,		/* detach */
+	nodev,			/* reset */
+	&batt_cb_ops,
+	NULL,			/* no bus operations */
+	NULL			/* power */
+};
+
+static struct modldrv modldrv1 = {
+	&mod_driverops,
+	BATT_MOD_STRING,
+	&batt_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	(void *)&modldrv1,
+	NULL,
+};
+
+int
+_init(void)
+{
+	int ret;
+
+	mutex_init(&batt_mutex, NULL, MUTEX_DRIVER, NULL);
+#ifdef DEBUG
+	mutex_init(&batt_prt_mutex, NULL, MUTEX_DRIVER, NULL);
+#endif
+
+	if ((ret = mod_install(&modlinkage)) != 0) {
+		mutex_destroy(&batt_mutex);
+#ifdef DEBUG
+		mutex_destroy(&batt_prt_mutex);
+#endif
+	}
+	return (ret);
+}
+
+int
+_fini(void)
+{
+	int ret;
+
+	if ((ret = mod_remove(&modlinkage)) == 0) {
+#ifdef DEBUG
+		mutex_destroy(&batt_prt_mutex);
+#endif
+		mutex_destroy(&batt_mutex);
+	}
+
+	return (ret);
+}
+
+int
+_info(struct modinfo *mp)
+{
+	return (mod_info(&modlinkage, mp));
+}
+
+static int
+batt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+	char name[20];
+	int i;
+	struct batt_cbat_state *bp;
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		/* Limit to one instance of driver */
+		if (batt_dip) {
+			return (DDI_FAILURE);
+		}
+		break;
+	case DDI_RESUME:
+	case DDI_PM_RESUME:
+		return (DDI_SUCCESS);
+	default:
+		return (DDI_FAILURE);
+	}
+
+	batt_dip = devi;
+
+	/* Init ACPI related stuff */
+	if (batt_acpi_init() != BATT_OK) {
+		goto error;
+	}
+
+	/* Init kstat related stuff */
+	if (batt_kstat_init() != BATT_OK) {
+		goto error;
+	}
+
+	/* Create minor node for each battery and ac */
+	for (bp = &batt_cbat[0]; bp < &batt_cbat[BATT_MAX_BAT_NUM]; bp++) {
+		if (bp->dev.valid) {
+			(void) snprintf(name, sizeof (name), "battery%d",
+			    bp->dev.index);
+			if (ddi_create_minor_node(devi, name, S_IFCHR,
+			    MINOR_BATT(bp->dev.index), DDI_PSEUDO, 0) ==
+			    DDI_FAILURE) {
+				BATT_DBG(CE_WARN, NULL,
+				    "%s: minor node create failed", name);
+				goto error;
+			}
+		}
+	}
+	for (i = 0; i < nac; i++) {
+		(void) snprintf(name, sizeof (name), "ac%d", i);
+		if (ddi_create_minor_node(devi, name, S_IFCHR,
+		    MINOR_AC(i), DDI_PSEUDO, 0) == DDI_FAILURE) {
+			BATT_DBG(CE_WARN, NULL,
+			    "%s: minor node create failed", name);
+			goto error;
+		}
+	}
+
+	return (DDI_SUCCESS);
+
+error:
+	ddi_remove_minor_node(devi, NULL);
+	batt_kstat_fini();
+	batt_acpi_fini();
+	batt_dip = NULL;
+	return (DDI_FAILURE);
+}
+
+static int
+batt_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+	if (cmd != DDI_DETACH) {
+		return (DDI_FAILURE);
+	}
+
+	ddi_remove_minor_node(devi, NULL);
+
+	batt_kstat_fini();
+	batt_acpi_fini();
+	return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+batt_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
+{
+	switch (cmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		*resultp = batt_dip;
+		return (DDI_SUCCESS);
+	case DDI_INFO_DEVT2INSTANCE:
+		*resultp = (void*) 0;
+		return (DDI_SUCCESS);
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+/*ARGSUSED*/
+static int
+batt_open(dev_t *devp, int flag, int otyp, cred_t *crp)
+{
+	if (batt_dip == NULL) {
+		return (ENXIO);
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+batt_close(dev_t dev, int flag, int otyp, cred_t *crp)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+batt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
+{
+	int minor;
+	int type, index;
+	int res = 0;
+
+	minor = getminor(dev);
+	type = MINOR2TYPE(minor);
+	index = MINOR2IDX(minor);
+
+	mutex_enter(&batt_mutex);
+
+	if (type == BATT_TYPE_CBAT) {
+		res = batt_cbat_ioctl(index, cmd, arg, mode, cr, rval);
+	} else if (type == BATT_TYPE_AC) {
+		res = batt_ac_ioctl(index, cmd, arg, mode, cr, rval);
+	} else {
+		res = EINVAL;
+	}
+
+	mutex_exit(&batt_mutex);
+	return (res);
+}
+
+/*ARGSUSED*/
+static int
+batt_cbat_ioctl(int index, int cmd, intptr_t arg, int mode, cred_t *cr,
+    int *rval)
+{
+	int res = 0;
+	acpi_bif_t bif;
+	acpi_bst_t bst;
+	batt_warn_t bwarn;
+	struct batt_cbat_state *bp;
+
+	ASSERT(mutex_owned(&batt_mutex));
+
+	bp = batt_idx2cbat(index);
+	if (!bp || bp->dev.valid != 1) {
+		return (ENXIO);
+	}
+
+	switch (cmd) {
+	/*
+	 * Return _BIF(Battery Information) of battery[index],
+	 * if battery plugged.
+	 */
+	case BATT_IOC_INFO:
+		if (bp->dev.present == 0) {
+			res = ENXIO;
+			break;
+		}
+
+		(void) memset(&bif, 0, sizeof (bif));
+		bp->bat_bifok = BATT_NTF_UNKNOWN;
+		res = batt_get_bif(&bif, bp);
+		if (res != BATT_OK) {
+			break;
+		}
+		if (copyout(&bif, (void *)arg, sizeof (bif))) {
+			res = EFAULT;
+		}
+		break;
+
+	/*
+	 * Return _BST(Battery Status) of battery[index],
+	 * if battery plugged.
+	 */
+	case BATT_IOC_STATUS:
+		if (bp->dev.present == 0) {
+			res = ENXIO;
+			break;
+		}
+
+		(void) memset(&bst, 0, sizeof (bst));
+		bp->bat_bstok = BATT_NTF_UNKNOWN;
+		res = batt_get_bst(&bst, bp);
+		if (res != BATT_OK) {
+			break;
+		}
+		if (copyout(&bst, (void *)arg, sizeof (bst))) {
+			res = EFAULT;
+		}
+		break;
+
+	/* Return the state of the battery bays in the system */
+	case BATT_IOC_BAY:
+		{
+			batt_bay_t bay;
+
+			bay.bay_number = nbat;
+			bay.battery_map = 0;
+			for (bp = &batt_cbat[0];
+			    bp < &batt_cbat[BATT_MAX_BAT_NUM]; bp++) {
+				if (bp->dev.valid) {
+					if (bp->dev.present) {
+						bay.battery_map |=
+						    (1 << bp->dev.index);
+					}
+				}
+			}
+			if (copyout(&bay, (void *)arg, sizeof (bay))) {
+				res = EFAULT;
+				break;
+			}
+		}
+		break;
+
+	/*
+	 * Return the current power source device if available:
+	 * 0 -- battery supplying power
+	 * 1 -- AC supplying power
+	 */
+	case BATT_IOC_POWER_STATUS:
+		{
+			int val;
+
+			/* State not available */
+			if (batt_psr_type == BATT_TYPE_UNKNOWN) {
+				res = ENXIO;
+				break;
+			}
+			val = (batt_psr_type == BATT_TYPE_AC) ? 1 : 0;
+			if (copyout(&val, (void *)arg, sizeof (val))) {
+				res = EFAULT;
+				break;
+			}
+		}
+		break;
+
+	/* Get charge-warn and charge-low levels for the whole system */
+	case BATT_IOC_GET_WARNING:
+		bwarn.bw_enabled = batt_warn_enabled;
+		bwarn.bw_charge_warn = batt_syn_warn_per;
+		bwarn.bw_charge_low = batt_syn_low_per;
+		if (copyout(&bwarn, (void *)arg, sizeof (&bwarn))) {
+			res = EFAULT;
+		}
+		break;
+
+	/* Set charge-warn and charge-low levels for the whole system */
+	case BATT_IOC_SET_WARNING:
+		if (drv_priv(cr)) {
+			res = EPERM;
+			break;
+		}
+		if (copyin((void *)arg, &bwarn, sizeof (&bwarn))) {
+			res = EFAULT;
+			break;
+		}
+		res = batt_set_warn(&bwarn);
+		break;
+
+	default:
+		res = EINVAL;
+		break;
+	}
+
+	return (res);
+}
+
+/*ARGSUSED*/
+static int
+batt_ac_ioctl(int index, int cmd, intptr_t arg, int mode, cred_t *cr,
+    int *rval)
+{
+	int res = 0;
+	int ac_state;
+	struct batt_ac_state *acp;
+
+	ASSERT(mutex_owned(&batt_mutex));
+
+	acp = batt_idx2ac(index);
+	if (!acp || acp->dev.valid != 1) {
+		return (ENXIO);
+	}
+
+	switch (cmd) {
+	/* Return the number of AC adapters in the system */
+	case BATT_IOC_AC_COUNT:
+		if (copyout(&nac, (void *)arg, sizeof (nac))) {
+			res = EFAULT;
+		}
+		break;
+
+	/*
+	 * Return the state of AC[index] if available:
+	 * 0 -- Off-line
+	 * 1 -- On-line
+	 */
+	case BATT_IOC_POWER_STATUS:
+		if (!acp || acp->dev.valid != 1) {
+			res = ENXIO;
+			break;
+		}
+		/* State not available */
+		if ((ac_state = batt_ac_present(acp)) == -1) {
+			res = ENXIO;
+			break;
+		}
+		if (copyout(&ac_state, (void *)arg, sizeof (ac_state))) {
+			res = EFAULT;
+		}
+		break;
+
+	default:
+		res = EINVAL;
+		break;
+	}
+
+	return (res);
+}
+
+/*ARGSUSED*/
+static int
+batt_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
+	struct pollhead **phpp)
+{
+	if (!anyyet) {
+		*phpp = &batt_pollhead;
+	}
+	*reventsp = 0;
+	return (0);
+}
+
+#ifdef DEBUG
+static void
+batt_printf(struct batt_acpi_dev *devp, uint_t lev, const char *fmt, ...)
+{
+	va_list args;
+
+	mutex_enter(&batt_prt_mutex);
+
+	va_start(args, fmt);
+	(void) vsprintf(batt_prt_buf, fmt, args);
+	va_end(args);
+
+	if (devp) {
+		cmn_err(lev, "%s.%s: %s", devp->hid, devp->uid, batt_prt_buf);
+	} else {
+		cmn_err(lev, "%s", batt_prt_buf);
+	}
+	mutex_exit(&batt_prt_mutex);
+}
+
+static void
+batt_prt_notify(ACPI_HANDLE hdl, UINT32 val)
+{
+	ACPI_BUFFER buf;
+	char str[1024];
+
+	buf.Length = sizeof (str);
+	buf.Pointer = str;
+	AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf);
+	cmn_err(CE_NOTE, "AcpiNotify(%s, 0x%02x)", str, val);
+}
+#endif /* DEBUG */
+
+static void
+batt_gen_sysevent(struct batt_acpi_dev *devp, char *ev, uint32_t val)
+{
+	nvlist_t *attr_list = NULL;
+	int err;
+	char pathname[MAXPATHLEN];
+
+	/* Allocate and build sysevent attribute list */
+	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, DDI_NOSLEEP);
+	if (err != 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "cannot allocate memory for sysevent attributes\n");
+		return;
+	}
+
+	/* Add attributes */
+	err = nvlist_add_string(attr_list, ACPIEV_DEV_HID, devp->hid);
+	if (err != 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "Failed to add attr [%s] for %s/%s event",
+		    ACPIEV_DEV_HID, EC_ACPIEV, ev);
+		nvlist_free(attr_list);
+		return;
+	}
+
+	err = nvlist_add_string(attr_list, ACPIEV_DEV_UID, devp->uid);
+	if (err != 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "Failed to add attr [%s] for %s/%s event",
+		    ACPIEV_DEV_UID, EC_ACPIEV, ev);
+		nvlist_free(attr_list);
+		return;
+	}
+
+	err = nvlist_add_uint32(attr_list, ACPIEV_DEV_INDEX, devp->index);
+	if (err != 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "Failed to add attr [%s] for %s/%s event",
+		    ACPIEV_DEV_INDEX, EC_ACPIEV, ev);
+		nvlist_free(attr_list);
+		return;
+	}
+
+	(void) ddi_pathname(batt_dip, pathname);
+	err = nvlist_add_string(attr_list, ACPIEV_DEV_PHYS_PATH, pathname);
+	if (err != 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "Failed to add attr [%s] for %s/%s event",
+		    ACPIEV_DEV_PHYS_PATH, EC_ACPIEV, ev);
+		nvlist_free(attr_list);
+		return;
+	}
+
+	if (strcmp(ev, ESC_ACPIEV_WARN) && strcmp(ev, ESC_ACPIEV_LOW)) {
+		goto finish;
+	}
+
+	err = nvlist_add_uint32(attr_list, ACPIEV_CHARGE_LEVEL, val);
+	if (err != 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "Failed to add attr [%s] for %s/%s event",
+		    ACPIEV_CHARGE_LEVEL, EC_ACPIEV, ev);
+		nvlist_free(attr_list);
+		return;
+	}
+
+finish:
+	BATT_DBG(CE_NOTE, NULL, "SysEv(%s, %s.%s, %d)",
+	    ev, devp->hid, devp->uid, val);
+	/* Generate/log sysevent */
+	err = ddi_log_sysevent(batt_dip, DDI_VENDOR_SUNW, EC_ACPIEV,
+	    ev, attr_list, NULL, DDI_NOSLEEP);
+#ifdef DEBUG
+	if (err != DDI_SUCCESS) {
+		BATT_DBG(CE_WARN, NULL,
+		    "cannot log sysevent, err code %x\n", err);
+	}
+#endif
+
+	nvlist_free(attr_list);
+}
+
+static int
+batt_obj_copy(ACPI_OBJECT *op, char *bp, struct obj_desc *dp)
+{
+	ACPI_OBJECT *ep;
+	char *fp;
+
+	ep = &op->Package.Elements[0];
+	for (; dp->offset != -1; dp++) {
+		fp = bp + dp->offset;
+		if (dp->type == ACPI_TYPE_INTEGER &&
+			ep->Type == dp->type) {
+#ifdef DEBUG
+			if (dp->size <= 4) {
+				BATT_DBG(CE_NOTE, NULL, "\t%s: %u", dp->name,
+				    (uint32_t)ep->Integer.Value);
+			} else {
+#ifdef _LP64
+				BATT_DBG(CE_NOTE, NULL, "\t%s: %lu",
+				    dp->name, (uint64_t)ep->Integer.Value);
+			}
+#else
+				BATT_DBG(CE_NOTE, NULL, "\t%s: %llu",
+				    dp->name, (uint64_t)ep->Integer.Value);
+			}
+#endif /* _LP64 */
+#endif /* DEBUG */
+			*(uint32_t *)fp = ep->Integer.Value;
+		} else if (dp->type == ACPI_TYPE_STRING &&
+		    ep->Type == dp->type) {
+			BATT_DBG(CE_NOTE, NULL, "\t%s: \"%s\"",
+			    dp->name, ep->String.Pointer);
+			(void) strncpy(fp, ep->String.Pointer, dp->size);
+		} else if (dp->type == ACPI_TYPE_STRING &&
+		    ep->Type == ACPI_TYPE_BUFFER) {
+#ifdef DEBUG
+			int len;
+			char buf[MAXNAMELEN + 1];
+
+			len = (MAXNAMELEN < ep->Buffer.Length) ?
+			    MAXNAMELEN : ep->Buffer.Length;
+			bcopy(ep->Buffer.Pointer, buf, len);
+			buf[len] = 0;
+			BATT_DBG(CE_NOTE, NULL, "\t%s: [%d] \"%s\"",
+			    dp->name, len, buf);
+#endif
+
+			ASSERT(MAXNAMELEN >= ep->Buffer.Length);
+			bcopy(ep->Buffer.Pointer, fp, ep->Buffer.Length);
+		} else {
+			BATT_DBG(CE_WARN, NULL,
+			    "Bad field at offset %d: type %d",
+			    dp->offset, ep->Type);
+			if (dp->type != ACPI_TYPE_STRING) {
+				return (BATT_ERR);
+			}
+		}
+		ep++;
+	}
+
+	return (BATT_OK);
+}
+
+static int
+batt_eval_int(ACPI_HANDLE hdl, ACPI_STRING name, ACPI_OBJECT_LIST *parms,
+    int *rval)
+{
+	ACPI_BUFFER buf;
+	ACPI_OBJECT obj;
+
+	buf.Length = sizeof (obj);
+	buf.Pointer = &obj;
+
+	if (ACPI_FAILURE(AcpiEvaluateObjectTyped(hdl, name, parms, &buf,
+	    ACPI_TYPE_INTEGER))) {
+		return (BATT_ERR);
+	}
+
+	*rval = (int)obj.Integer.Value;
+	return (BATT_OK);
+}
+
+/*
+ * Returns the current power source devices. Used for the AC adapter and is
+ * located under the AC adapter object in name space. Used to determine if
+ * system is running off the AC adapter. This will report that the system is
+ * not running on the AC adapter if any of the batteries in the system is
+ * being forced to discharge through _BMC.
+ *
+ * Return value:
+ *	 0 -- Off-line, ie. battery supplying system power
+ *	 1 -- On-line, ie. AC supplying system power
+ *	-1 -- Unknown, some error ocurred.
+ * Note: It will also update the driver ac state.
+ */
+static int
+batt_get_psr(struct batt_ac_state *acp)
+{
+	struct batt_acpi_dev *devp = &acp->dev;
+	int ac;
+
+	if (!devp->valid) {
+		BATT_DBG(CE_WARN, NULL, "device not valid");
+		return (-1);
+	}
+
+	if (ACPI_FAILURE(batt_eval_int(devp->hdl, "_PSR", NULL, &ac))) {
+		BATT_DBG(CE_WARN, NULL, "AcpiEval _PSR failed");
+		devp->present = -1;
+	} else {
+		BATT_DBG(CE_NOTE, devp, "_PSR = %d", ac);
+		devp->present = ac;
+	}
+
+	return (ac);
+}
+
+/*
+ * For most systems, the _STA for this device will always
+ * return a value with bits 0-3 set and will toggle bit 4
+ * to indicate the actual presence of a battery.
+ *
+ * Return value:
+ *	 0 -- battery not present
+ *	 1 -- battery present
+ *	-1 -- Unknown, some error ocurred.
+ * Note: It will also update the driver cbat state.
+ */
+static int
+batt_get_sta(struct batt_cbat_state *bp)
+{
+	struct batt_acpi_dev *devp = &bp->dev;
+	int val;
+
+	if (!devp->valid) {
+		BATT_DBG(CE_WARN, NULL, "device not valid");
+		return (-1);
+	}
+
+	if (batt_eval_int(devp->hdl, "_STA", NULL, &val) == BATT_ERR) {
+		BATT_DBG(CE_WARN, NULL, "AcpiEval _STA failed");
+		devp->present = -1;
+	} else {
+		BATT_DBG(CE_NOTE, devp, "_STA = 0x%x", val);
+		devp->present = ((val & STA_FLAG_BATT_PRESENT) != 0);
+	}
+
+	return (val);
+}
+
+static int
+batt_get_bif(acpi_bif_t *bifp, struct batt_cbat_state *bp)
+{
+	/* BIF is only available when battery plugged */
+	ASSERT(bp->dev.present != 0);
+
+	/* Update internal BIF cache */
+	if (bp->bat_bifok != BATT_NTF_OK) {
+		ACPI_BUFFER buf;
+		ACPI_OBJECT *objp;
+
+		buf.Length = ACPI_ALLOCATE_BUFFER;
+		if (ACPI_FAILURE(AcpiEvaluateObjectTyped(bp->dev.hdl, "_BIF",
+		    NULL, &buf, ACPI_TYPE_PACKAGE))) {
+			BATT_DBG(CE_WARN, NULL, "AcpiEval _BIF failed");
+			return (BATT_ERR);
+		}
+
+		objp = buf.Pointer;
+		BATT_DBG(CE_NOTE, &bp->dev, "get _BIF");
+		if (batt_obj_copy(objp, (char *)&bp->bif_cache, bif_desc) ==
+		    BATT_ERR) {
+			AcpiOsFree(objp);
+			return (BATT_ERR);
+		}
+		AcpiOsFree(objp);
+
+		bp->bat_bifok = BATT_NTF_OK;
+	}
+
+	/* Copy BIF back to user */
+	if (bifp) {
+		*bifp = bp->bif_cache;
+	}
+	return (BATT_OK);
+}
+
+static int
+batt_get_bst(acpi_bst_t *bstp, struct batt_cbat_state *bp)
+{
+	/* BST is only available when battery plugged */
+	ASSERT(bp->dev.present != 0);
+
+	/* Update internal BST cache */
+	if (bp->bat_bstok != BATT_NTF_OK) {
+		ACPI_BUFFER buf;
+		ACPI_OBJECT *objp;
+
+		buf.Length = ACPI_ALLOCATE_BUFFER;
+		if (ACPI_FAILURE(AcpiEvaluateObjectTyped(bp->dev.hdl, "_BST",
+		    NULL, &buf, ACPI_TYPE_PACKAGE))) {
+			BATT_DBG(CE_WARN, NULL, "AcpiEval _BST failed");
+			return (BATT_ERR);
+		}
+
+		objp = buf.Pointer;
+		BATT_DBG(CE_NOTE, &bp->dev, "get _BST");
+		if (batt_obj_copy(objp, (char *)&bp->bst_cache, bst_desc) ==
+		    BATT_ERR) {
+			AcpiOsFree(objp);
+			return (BATT_ERR);
+		}
+		AcpiOsFree(objp);
+
+		if (bp->bst_cache.bst_rate == 0) {
+			bp->bst_cache.bst_state &= ~(BATT_BST_CHARGING |
+			    BATT_BST_DISCHARGING);
+		}
+		bp->bat_bstok = BATT_NTF_OK;
+	}
+
+	/* Copy BST back to user */
+	if (bstp) {
+		*bstp = bp->bst_cache;
+	}
+	return (BATT_OK);
+}
+
+static int
+batt_update_bif(struct batt_cbat_state *bp)
+{
+	bp->bat_bifok = BATT_NTF_UNKNOWN;
+	return (batt_get_bif(NULL, bp));
+}
+
+static int
+batt_update_bst(struct batt_cbat_state *bp)
+{
+	bp->bat_bstok = BATT_NTF_UNKNOWN;
+	return (batt_get_bst(NULL, bp));
+}
+
+/*
+ * Return value:
+ *	 1 -- device On-line
+ *	 0 -- device Off-line
+ *	-1 -- Unknown, some error ocurred.
+ */
+static int
+batt_dev_present(struct batt_acpi_dev *devp)
+{
+	if (!devp->valid) {
+		BATT_DBG(CE_WARN, NULL, "device not valid");
+		return (-1);
+	}
+
+	ASSERT(devp->type != BATT_TYPE_UNKNOWN);
+
+	/* Update the device state */
+	if (devp->present == -1) {
+		if (devp->type == BATT_TYPE_AC) {
+			(void) batt_get_psr((struct batt_ac_state *)devp);
+		} else if (devp->type == BATT_TYPE_CBAT) {
+			(void) batt_get_sta((struct batt_cbat_state *)devp);
+		}
+	}
+
+	return (devp->present);
+}
+
+/*
+ * Check if the device p existance state has changed.
+ * Return value:
+ *	 1 -- changed
+ *	 0 -- no change
+ *	-1 -- unknown
+ */
+static int
+batt_update_present(struct batt_acpi_dev *p)
+{
+	int old_present = p->present;
+	int new_present;
+
+	ASSERT(p && p->valid);
+
+	p->present = -1;
+	new_present = batt_dev_present(p);
+	if (new_present == -1) {
+		return (-1);
+	}
+	if (new_present != old_present) {
+		return (1);
+	}
+	return (0);
+}
+
+static void
+batt_set_psr(struct batt_acpi_dev *p)
+{
+	batt_psr_devp = p;
+	if (p != NULL) {
+		BATT_DBG(CE_NOTE, p, "psr = .");
+		batt_psr_type = p->type;
+	} else {
+		BATT_DBG(CE_NOTE, p, "psr = ?");
+		batt_psr_type = BATT_TYPE_UNKNOWN;
+	}
+}
+
+/*
+ * OSPM can determine independent warning and low battery
+ * capacity values based on the OEM-designed levels, but
+ * cannot set these values lower than the OEM-designed values.
+ */
+static int
+batt_set_warn(batt_warn_t *bwp)
+{
+	uint32_t warn, low;
+
+	warn = batt_syn_last_cap * bwp->bw_charge_warn / 100;
+	low = batt_syn_last_cap * bwp->bw_charge_low / 100;
+
+	/* Update internal state */
+	if (bwp->bw_enabled) {
+		if (low >= warn || warn < batt_syn_oem_warn_cap ||
+		    low < batt_syn_oem_low_cap) {
+			BATT_DBG(CE_WARN, NULL, "charge level error");
+			return (EINVAL);
+		}
+
+		BATT_DBG(CE_NOTE, NULL, "set warn: warn=%d low=%d", warn, low);
+
+		batt_syn_warn_per = bwp->bw_charge_warn;
+		batt_syn_low_per = bwp->bw_charge_low;
+		batt_syn_warn_cap = warn;
+		batt_syn_low_cap = low;
+		batt_warn_enabled = 1;
+	} else {
+		batt_warn_enabled = 0;
+	}
+
+	return (0);
+}
+
+/*
+ * Update information for the synthesis battery
+ *
+ * Note: Sometimes the value to be returned from _BST or _BIF will be
+ * temporarily unknown. In this case, the method may return the value
+ * 0xFFFFFFFF as a placeholder. When the value becomes known, the
+ * appropriate notification (0x80 for _BST or 0x81 for BIF) should be
+ * issued, in like manner to any other change in the data returned by
+ * these methods. This will cause OSPM to re-evaluate the method obtaining
+ * the correct data value.
+ */
+static void
+batt_update_cap(int bif_changed)
+{
+	struct batt_cbat_state *bp;
+
+	if (bif_changed != 0) {
+		batt_syn_oem_warn_cap = 0xffffffff;
+		batt_syn_oem_low_cap = 0xffffffff;
+		batt_syn_last_cap = 0xffffffff;
+	}
+	batt_syn_last_level = batt_syn_rem_cap;
+	batt_syn_rem_cap = 0xffffffff; /* initially unknown */
+
+	for (bp = &batt_cbat[0]; bp < &batt_cbat[BATT_MAX_BAT_NUM]; bp++) {
+		if (bp->dev.valid) {
+			/* Escape the empty bays */
+			if (batt_cbat_present(bp) <= 0) {
+				continue;
+			}
+
+			if (bif_changed != 0 && bp->bat_bifok == BATT_NTF_OK) {
+				acpi_bif_t *bif;
+
+				bif = &bp->bif_cache;
+
+				if (batt_syn_last_cap == 0xffffffff) {
+					batt_syn_last_cap = 0;
+				}
+				batt_syn_last_cap += bif->bif_last_cap;
+
+				if (bif->bif_warn_cap == 0xffffffff ||
+				    bif->bif_low_cap == 0xffffffff) {
+					BATT_DBG(CE_WARN, &bp->dev, "BIF value "
+					    "invalid, warn_cap=0x%x "
+					    "low_cap=0x%x", bif->bif_warn_cap,
+					    bif->bif_low_cap);
+					continue;
+				}
+				if (batt_syn_oem_warn_cap == 0xffffffff) {
+					batt_syn_oem_warn_cap = 0;
+				}
+				if (batt_syn_oem_low_cap == 0xffffffff) {
+					batt_syn_oem_low_cap = 0;
+				}
+
+				/*
+				 * Use the highest level as the synthesis
+				 * level.
+				 */
+				if (bif->bif_warn_cap > batt_syn_oem_warn_cap) {
+					batt_syn_oem_low_cap = bif->bif_low_cap;
+					batt_syn_oem_warn_cap =
+					    bif->bif_warn_cap;
+				}
+			}
+#ifdef DEBUG
+			else if (bif_changed) {
+				BATT_DBG(CE_NOTE, &bp->dev, "BIF not ready");
+			}
+#endif
+
+			if (bp->bat_bstok == BATT_NTF_OK) {
+				acpi_bst_t *bst;
+
+				bst = &bp->bst_cache;
+
+				/*
+				 * Batteries that are rechargeable and are in
+				 * the discharging state are required to return
+				 * a valid Battery Present Rate value.
+				 * 0xFFFFFFFF - Unknown rate/capacity
+				 */
+				if (bst->bst_rem_cap == 0xffffffff) {
+					BATT_DBG(CE_WARN, &bp->dev,
+					    "BST value invalid, "
+					    "rate=0x%x cap=0x%x",
+					    bst->bst_rate, bst->bst_rem_cap);
+					continue;
+				}
+
+				if (batt_syn_rem_cap == 0xffffffff) {
+					batt_syn_rem_cap = 0;
+				}
+				batt_syn_rem_cap += bst->bst_rem_cap;
+				/* Check for overflow */
+				ASSERT(batt_syn_rem_cap >= bst->bst_rem_cap);
+			}
+#ifdef DEBUG
+			else {
+				BATT_DBG(CE_NOTE, &bp->dev, "BST not ready");
+			}
+#endif
+		}
+	}
+
+	BATT_DBG(CE_NOTE, NULL, "syn_cap: %d syn_oem_warn: %d syn_oem_low: %d",
+	    batt_syn_rem_cap, batt_syn_oem_warn_cap, batt_syn_oem_low_cap);
+}
+
+static struct batt_cbat_state *
+batt_idx2cbat(int idx)
+{
+	if (idx >= BATT_MAX_BAT_NUM) {
+		return (NULL);
+	}
+	return (&batt_cbat[idx]);
+}
+
+static struct batt_ac_state *
+batt_idx2ac(int idx)
+{
+	if (idx >= BATT_MAX_AC_NUM) {
+		return (NULL);
+	}
+	return (&batt_ac[idx]);
+}
+
+/*ARGSUSED*/
+static void
+batt_cbat_notify(ACPI_HANDLE hdl, UINT32 val, void *ctx)
+{
+	struct batt_cbat_state *bp = ctx;
+	struct batt_acpi_dev *devp = &bp->dev;
+	int bif_changed;
+	uint32_t eval;
+	char *ev;
+	acpi_bst_t *bst;
+
+	mutex_enter(&batt_mutex);
+	BATT_PRT_NOTIFY(hdl, val);
+
+	switch (val) {
+	/*
+	 * BST has changed
+	 * Whenever the Battery State value changes, the
+	 * system will generate an SCI to notify the OS.
+	 *
+	 * Note: trip point is not used to implement the
+	 * warning levels.
+	 */
+	case 0x80:
+		/*
+		 * We always get 0x80 and 0x81 at battery plug/unplug,
+		 * but 0x80 may come first. In case that situation, we have
+		 * to update battery present state here too to update bst
+		 * correctly.
+		 */
+		bif_changed = batt_update_present(devp);
+
+		/* Omit events sent by empty battery slot */
+		if (devp->present == 0) {
+			break;
+		}
+
+		if (batt_update_bst(bp) != BATT_OK) {
+			break;
+		}
+		batt_update_cap(bif_changed);
+
+		bst = &bp->bst_cache;
+		eval = bst->bst_rem_cap;
+
+		/*
+		 * Keep tracking the current power source device
+		 *
+		 * Note: Even no battery plugged, some system
+		 * send out 0x80 ACPI event. So make sure the battery
+		 * is present first.
+		 */
+		if (devp->present == 0) {
+			if (batt_psr_devp == devp) {
+				batt_set_psr(NULL);
+			}
+			break;
+		}
+		if (bst->bst_state & BST_FLAG_DISCHARGING) {
+			batt_set_psr(devp);
+		}
+		/*
+		 * The Critical battery state indicates that all
+		 * available batteries are discharged and do not
+		 * appear to be able to supply power to run the
+		 * system any longer. When this occurs, the OS
+		 * should attempt to perform an emergency shutdown.
+		 * Right now we do not shutdown.  This would
+		 * need some discussion first since it could be
+		 * controversial.
+		 */
+#ifdef DEBUG
+		if (bst->bst_state & BST_FLAG_CRITICAL) {
+			BATT_DBG(CE_WARN, devp, "BST_FLAG_CRITICAL set");
+
+			/*
+			 * BST_FLAG_CRITICAL may set even with AC,
+			 * plugged, when plug/unplug battery. Check
+			 * to avoid erroneous shutdown.
+			 */
+			if (batt_psr_devp == devp &&
+			    bst->bst_rem_cap != 0xffffffff) {
+				BATT_DBG(CE_WARN, NULL,
+				    "Battery in critical state");
+			}
+		} else
+#endif
+		if (batt_warn_enabled &&
+		    (bst->bst_state & BST_FLAG_DISCHARGING)) {
+			/*
+			 * This value is an estimation of the amount of
+			 * energy or battery capacity required by the
+			 * system to transition to any supported sleeping
+			 * state. When the OS detects that the total
+			 * available battery capacity is less than this
+			 * value, it will transition the system to a user
+			 * defined system state (S1-S5).
+			 */
+			if (batt_syn_last_level > batt_syn_low_cap &&
+			    batt_syn_rem_cap <= batt_syn_low_cap) {
+				batt_gen_sysevent(devp, ESC_ACPIEV_LOW, eval);
+			/*
+			 * When the total available energy (mWh) or capacity
+			 * (mAh) in the batteries falls below this level,
+			 * the OS will notify the user through the UI.
+			 */
+			} else if (batt_syn_last_level > batt_syn_warn_cap &&
+			    batt_syn_rem_cap <= batt_syn_warn_cap) {
+				batt_gen_sysevent(devp, ESC_ACPIEV_WARN, eval);
+			}
+		}
+
+		batt_gen_sysevent(devp, ESC_ACPIEV_STATE_CHANGE, 0);
+		pollwakeup(&batt_pollhead, BATT_EVENTS);
+		break;
+
+	/* BIF has changed */
+	case 0x81:
+		/*
+		 * Note: Do not eliminate multiple ADD/REMOVE here,
+		 * because they may corresponding to different batterys.
+		 */
+		(void) batt_update_present(devp);
+		if (devp->present == 1) {
+			if (batt_update_bif(bp) != BATT_OK) {
+				break;
+			}
+		} else {
+			bp->bat_bifok = BATT_NTF_UNKNOWN;
+			bp->bat_bstok = BATT_NTF_UNKNOWN;
+		}
+
+		batt_update_cap(1);
+
+		eval = devp->present;
+		ev = eval ? ESC_ACPIEV_ADD : ESC_ACPIEV_REMOVE;
+		batt_gen_sysevent(devp, ev, 0);
+		pollwakeup(&batt_pollhead, BATT_EVENTS);
+		break;
+
+	case 0x82:
+	default:
+		break;
+	}
+
+	mutex_exit(&batt_mutex);
+}
+
+/*ARGSUSED*/
+static void
+batt_ac_notify(ACPI_HANDLE hdl, UINT32 val, void *ctx)
+{
+	struct batt_ac_state *acp = ctx;
+	struct batt_acpi_dev *devp = &acp->dev;
+	int old_present;
+	char *ev;
+	int eval;
+
+	mutex_enter(&batt_mutex);
+	BATT_PRT_NOTIFY(hdl, val);
+
+	if (val != 0x80) {
+		return;
+	}
+	/*
+	 * Note: if unplug and then quickly plug back, two ADD
+	 * events will be generated.
+	 */
+	old_present = devp->present;
+	eval = batt_get_psr(acp);
+
+	/* Eliminate redudant events */
+	if (eval != -1 && eval != old_present) {
+		/* Keep tracking the current power source device */
+		if (eval == 1) {
+			ev = ESC_ACPIEV_ADD;
+			batt_set_psr(devp);
+		} else {
+			ev = ESC_ACPIEV_REMOVE;
+			/* If AC was supplying the power, it's not now */
+			if (batt_psr_devp == devp) {
+				batt_set_psr(NULL);
+			}
+		}
+
+		batt_gen_sysevent(devp, ev, 0);
+		pollwakeup(&batt_pollhead, BATT_EVENTS);
+	}
+
+	mutex_exit(&batt_mutex);
+}
+
+static int
+batt_obj_init(struct batt_acpi_dev *p)
+{
+	ACPI_DEVICE_INFO *info;
+	ACPI_HANDLE hdl;
+	ACPI_BUFFER buf;
+	ACPI_NOTIFY_HANDLER ntf_handler = NULL;
+	ACPI_STATUS ret;
+
+	ASSERT(p != NULL && p->hdl != NULL);
+
+	hdl = p->hdl;
+
+	/* Info size is variable depending on existance of _CID */
+	buf.Length = ACPI_ALLOCATE_BUFFER;
+	ret = AcpiGetObjectInfo(hdl, &buf);
+	if (ACPI_FAILURE(ret)) {
+		BATT_DBG(CE_WARN, NULL,
+		    "AcpiGetObjectInfo() fail: %d", (int32_t)ret);
+		return (BATT_ERR);
+	}
+
+	info = buf.Pointer;
+
+	if ((info->Valid & ACPI_VALID_HID) == 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "AcpiGetObjectInfo(): _HID not available");
+		AcpiOsFree(info);
+		return (BATT_ERR);
+	}
+	(void) strncpy(p->hid, info->HardwareId.Value, 9);
+
+	/*
+	 * This object is optional, but is required when the device
+	 * has no other way to report a persistent unique device ID.
+	 */
+	if ((info->Valid & ACPI_VALID_UID) == 0) {
+		BATT_DBG(CE_WARN, NULL,
+		    "AcpiGetObjectInfo(): _UID not available");
+		/* Use 0 as the default _UID */
+		(void) strncpy(p->uid, "0", 9);
+	} else {
+		(void) strncpy(p->uid, info->UniqueId.Value, 9);
+	}
+
+	p->valid = 1;
+	p->type = BATT_TYPE_UNKNOWN;
+
+	if (strcmp(p->hid, ACPI_DEVNAME_CBAT) == 0) {
+		struct batt_cbat_state *bp = (struct batt_cbat_state *)p;
+
+		p->type = BATT_TYPE_CBAT;
+		p->index = nbat - 1;
+		bp->bat_bifok = BATT_NTF_UNKNOWN;
+		bp->bat_bstok = BATT_NTF_UNKNOWN;
+
+		/* Update device present state */
+		(void) batt_update_present(p);
+		if (p->present) {
+			(void) batt_update_bif(bp);
+			(void) batt_update_bst(bp);
+
+			/* Init the current power source */
+			if (bp->bst_cache.bst_state & BST_FLAG_DISCHARGING) {
+				batt_set_psr(p);
+			}
+		}
+		ntf_handler = batt_cbat_notify;
+		BATT_DBG(CE_NOTE, p, "battery %s",
+		    (p->present ? "present" : "absent"));
+	} else if (strcmp(p->hid, ACPI_DEVNAME_AC) == 0) {
+		p->type = BATT_TYPE_AC;
+		p->index = nac - 1;
+
+		/* Update device present state */
+		(void) batt_update_present(p);
+		if (p->present) {
+			/* Init the current power source */
+			batt_set_psr(p);
+		}
+		ntf_handler = batt_ac_notify;
+		BATT_DBG(CE_NOTE, p, "AC %s",
+		    (p->present ? "on-line" : "off-line"));
+	} else if (strcmp(p->hid, ACPI_DEVNAME_SBAT) == 0) {
+		p->type = BATT_TYPE_SBAT;
+		BATT_DBG(CE_NOTE, p, "added");
+	} else {
+		BATT_DBG(CE_NOTE, p, "unknown device");
+		p->valid = 0;
+	}
+
+	/* Register ACPI battery related events */
+	if (ntf_handler != NULL) {
+		if (ACPI_FAILURE(AcpiInstallNotifyHandler(hdl,
+		    ACPI_ALL_NOTIFY, ntf_handler, p))) {
+			BATT_DBG(CE_NOTE, NULL,
+			    "Notify handler for %s.%s install failed",
+			    p->hid, p->uid);
+			return (BATT_ERR);
+		}
+	}
+
+out:
+	AcpiOsFree(info);
+	return (BATT_OK);
+}
+
+/*ARGSUSED*/
+static ACPI_STATUS
+batt_find_cb(ACPI_HANDLE ObjHandle, UINT32 NestingLevel, void *Context,
+    void **ReturnValue)
+{
+	struct batt_acpi_dev *devp = (struct batt_acpi_dev *)Context;
+
+	if (devp == &batt_cbat[0].dev) {
+		struct batt_cbat_state *bp;
+
+		if (nbat == BATT_MAX_BAT_NUM) {
+			BATT_DBG(CE_WARN, NULL,
+			    "Need to support more batteries: "
+			    "BATTERY_MAX = %d", BATT_MAX_BAT_NUM);
+			return (AE_LIMIT);
+		}
+		bp = &batt_cbat[nbat++];
+		devp = (struct batt_acpi_dev *)bp;
+	} else if (devp == &batt_ac[0].dev) {
+		struct batt_ac_state *ap;
+
+		if (nac == BATT_MAX_AC_NUM) {
+			BATT_DBG(CE_WARN, NULL, "Need to support more ACs: "
+			    "AC_MAX = %d", BATT_MAX_AC_NUM);
+			return (AE_LIMIT);
+		}
+		ap = &batt_ac[nac++];
+		devp = (struct batt_acpi_dev *)ap;
+	}
+
+	devp->hdl = ObjHandle;
+	*ReturnValue = NULL;
+
+	/* Try to get as many working objs as possible */
+	(void) batt_obj_init(devp);
+	return (0);
+}
+
+static int
+batt_acpi_init()
+{
+	int *retp;
+
+	/* Check to see if ACPI CA services are available */
+	if (AcpiSubsystemStatus() != AE_OK) {
+		BATT_DBG(CE_WARN, NULL, "ACPI CA not ready");
+		return (BATT_ERR);
+	}
+
+	/* Init Control Method Batterys */
+	if (ACPI_FAILURE(AcpiGetDevices(ACPI_DEVNAME_CBAT, batt_find_cb,
+	    batt_cbat, (void *)&retp))) {
+		return (BATT_ERR);
+	}
+
+	/* Init AC */
+	if (ACPI_FAILURE(AcpiGetDevices(ACPI_DEVNAME_AC, batt_find_cb, batt_ac,
+	    (void *)&retp))) {
+		return (BATT_ERR);
+	}
+
+	/* Init Smart Battery */
+	if (ACPI_FAILURE(AcpiGetDevices(ACPI_DEVNAME_SBAT, batt_find_cb,
+	    &batt_sbat, (void *)&retp))) {
+		return (BATT_ERR);
+	}
+
+	batt_update_cap(1);
+
+	return (BATT_OK);
+}
+
+static void
+batt_acpi_fini(void)
+{
+	int i;
+	struct batt_cbat_state *bp;
+
+	for (bp = &batt_cbat[0]; bp < &batt_cbat[BATT_MAX_BAT_NUM]; bp++) {
+		if (bp->dev.valid) {
+			AcpiRemoveNotifyHandler(bp->dev.hdl, ACPI_DEVICE_NOTIFY,
+			    batt_cbat_notify);
+		}
+	}
+	for (i = 0; i < nac; i++) {
+		AcpiRemoveNotifyHandler(batt_ac[i].dev.hdl, ACPI_DEVICE_NOTIFY,
+		    batt_ac_notify);
+	}
+}
+
+/*ARGSUSED*/
+static int
+batt_kstat_power_update(kstat_t *ksp, int flag)
+{
+	if (flag == KSTAT_WRITE) {
+		return (EACCES);
+	}
+
+	mutex_enter(&batt_mutex);
+	if (batt_psr_type == BATT_TYPE_UNKNOWN) {
+		mutex_exit(&batt_mutex);
+		return (EIO);
+	}
+	kstat_named_setstr(&batt_power_kstat.batt_power,
+	    batt_psr_type == BATT_TYPE_AC ? AC : BATTERY);
+	batt_power_kstat.batt_supported_battery_count.value.ui32 =
+	    (uint32_t)nbat;
+	mutex_exit(&batt_mutex);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+batt_kstat_warn_update(kstat_t *ksp, int flag)
+{
+	if (flag == KSTAT_WRITE) {
+		int ret = 0;
+		batt_warn_t bw;
+		batt_warn_kstat_t kbw;
+
+		kbw = *(batt_warn_kstat_t *)batt_warn_ksp->ks_data;
+
+		mutex_enter(&batt_mutex);
+		bw.bw_enabled  = kbw.batt_bw_enabled.value.ui32;
+		bw.bw_charge_warn = kbw.batt_bw_charge_warn.value.ui32;
+		bw.bw_charge_low = kbw.batt_bw_charge_low.value.ui32;
+		ret = batt_set_warn(&bw);
+		mutex_exit(&batt_mutex);
+
+		return (ret);
+	} else {
+		batt_warn_kstat_t *wp = &batt_warn_kstat;
+
+		mutex_enter(&batt_mutex);
+		wp->batt_bw_enabled.value.ui32 = batt_warn_enabled;
+		wp->batt_bw_charge_warn.value.ui32 = batt_syn_warn_per;
+		wp->batt_bw_charge_low.value.ui32 = batt_syn_low_per;
+		mutex_exit(&batt_mutex);
+
+		return (0);
+	}
+}
+
+static int
+batt_kstat_bif_update(kstat_t *ksp, int flag)
+{
+	struct batt_cbat_state *bp;
+	acpi_bif_t bif;
+	batt_bif_kstat_t *kp;
+
+	if (flag == KSTAT_WRITE) {
+		return (EACCES);
+	}
+
+	bp = (struct batt_cbat_state *)ksp->ks_private;
+	mutex_enter(&batt_mutex);
+
+	if (batt_cbat_present(bp) <= 0) {
+		mutex_exit(&batt_mutex);
+		return (ENXIO);
+	}
+
+	bzero(&bif, sizeof (bif));
+	bp->bat_bifok = BATT_NTF_UNKNOWN;
+	if (batt_get_bif(&bif, bp) != BATT_OK) {
+		mutex_exit(&batt_mutex);
+		return (ENXIO);
+	}
+
+	kp = &batt_bif_kstat;
+
+	/* Update BIF */
+	kp->batt_bif_unit.value.ui32 = bif.bif_unit;
+	kp->batt_bif_design_cap.value.ui32 = bif.bif_design_cap;
+	kp->batt_bif_last_cap.value.ui32 = bif.bif_last_cap;
+	kp->batt_bif_tech.value.ui32 = bif.bif_tech;
+	kp->batt_bif_voltage.value.ui32 = bif.bif_voltage;
+	kp->batt_bif_warn_cap.value.ui32 = bif.bif_warn_cap;
+	kp->batt_bif_low_cap.value.ui32 = bif.bif_low_cap;
+	kp->batt_bif_gran1_cap.value.ui32 = bif.bif_gran1_cap;
+	kp->batt_bif_gran2_cap.value.ui32 = bif.bif_gran2_cap;
+
+	kstat_named_setstr(&kp->batt_bif_model, bif.bif_model);
+	kstat_named_setstr(&kp->batt_bif_serial, bif.bif_serial);
+	kstat_named_setstr(&kp->batt_bif_type, bif.bif_type);
+	kstat_named_setstr(&kp->batt_bif_oem_info, bif.bif_oem_info);
+
+	mutex_exit(&batt_mutex);
+	return (0);
+}
+
+static int
+batt_kstat_bst_update(kstat_t *ksp, int flag)
+{
+	struct batt_cbat_state *bp;
+	acpi_bst_t bst;
+	batt_bst_kstat_t *kp;
+
+	if (flag == KSTAT_WRITE) {
+		return (EACCES);
+	}
+
+	bp = (struct batt_cbat_state *)ksp->ks_private;
+	mutex_enter(&batt_mutex);
+
+	if (batt_cbat_present(bp) <= 0) {
+		mutex_exit(&batt_mutex);
+		return (ENXIO);
+	}
+
+	bzero(&bst, sizeof (bst));
+	bp->bat_bstok = BATT_NTF_UNKNOWN;
+	if (batt_get_bst(&bst, bp) != BATT_OK) {
+		mutex_exit(&batt_mutex);
+		return (ENXIO);
+	}
+
+	kp = &batt_bst_kstat;
+
+	/* Update BST */
+	kp->batt_bst_state.value.ui32 = bst.bst_state;
+	kp->batt_bst_rate.value.ui32 = bst.bst_rate;
+	kp->batt_bst_rem_cap.value.ui32 = bst.bst_rem_cap;
+	kp->batt_bst_voltage.value.ui32 = bst.bst_voltage;
+
+	mutex_exit(&batt_mutex);
+	return (0);
+}
+
+static int
+batt_kstat_init(void)
+{
+	char name[KSTAT_STRLEN];
+	struct batt_cbat_state *bp;
+
+	/*
+	 * Allocate, initialize and install powerstatus and
+	 * supported_battery_count kstat.
+	 */
+	batt_power_ksp = kstat_create(BATT_DRV_NAME, 0,
+	    BATT_POWER_KSTAT_NAME, "misc",
+	    KSTAT_TYPE_NAMED,
+	    sizeof (batt_power_kstat) / sizeof (kstat_named_t),
+	    KSTAT_FLAG_VIRTUAL);
+	if (batt_power_ksp == NULL) {
+		BATT_DBG(CE_WARN, NULL,
+		    "kstat_create(%s) fail", BATT_POWER_KSTAT_NAME);
+		return (BATT_ERR);
+	}
+
+	batt_power_ksp->ks_data = &batt_power_kstat;
+	batt_power_ksp->ks_update = batt_kstat_power_update;
+	batt_power_ksp->ks_data_size += MAXNAMELEN;
+	kstat_install(batt_power_ksp);
+
+	/*
+	 * Allocate, initialize and install battery_capacity_warning kstat.
+	 */
+	batt_warn_ksp = kstat_create(BATT_DRV_NAME, 0,
+	    BATT_BTWARN_KSTAT_NAME, "misc",
+	    KSTAT_TYPE_NAMED,
+	    sizeof (batt_warn_kstat) / sizeof (kstat_named_t),
+	    KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE);
+	if (batt_warn_ksp == NULL) {
+		BATT_DBG(CE_WARN, NULL,
+		    "kstat_create(%s) fail", BATT_BTWARN_KSTAT_NAME);
+		return (BATT_ERR);
+	}
+
+	batt_warn_ksp->ks_data = &batt_warn_kstat;
+	batt_warn_ksp->ks_update = batt_kstat_warn_update;
+	kstat_install(batt_warn_ksp);
+
+	/*
+	 * Allocate, initialize and install BIF and BST kstat
+	 * for each battery.
+	 */
+	for (bp = &batt_cbat[0]; bp < &batt_cbat[BATT_MAX_BAT_NUM]; bp++) {
+		if (bp->dev.valid) {
+			kstat_t *ksp;
+
+			/* BIF kstat */
+			(void) snprintf(name, KSTAT_STRLEN-1, "%s%d",
+			    BATT_BIF_KSTAT_NAME, bp->dev.index);
+			ksp = kstat_create(BATT_DRV_NAME, 0,
+			    name, "misc", KSTAT_TYPE_NAMED,
+			    sizeof (batt_bif_kstat) / sizeof (kstat_named_t),
+			    KSTAT_FLAG_VIRTUAL);
+			if (ksp == NULL) {
+				BATT_DBG(CE_WARN, NULL, "kstat_create(%s) fail",
+				    name);
+				return (BATT_ERR);
+			}
+			BATT_DBG(CE_NOTE, NULL, "kstat_create(%s) ok", name);
+
+			bp->bat_bif_ksp = ksp;
+			ksp->ks_data = &batt_bif_kstat;
+			ksp->ks_update = batt_kstat_bif_update;
+			ksp->ks_data_size += MAXNAMELEN * 4;
+			ksp->ks_private = bp;
+
+			kstat_install(ksp);
+
+			/* BST kstat */
+			(void) snprintf(name, KSTAT_STRLEN-1, "%s%d",
+			    BATT_BST_KSTAT_NAME, bp->dev.index);
+			ksp = kstat_create(BATT_DRV_NAME, 0, name, "misc",
+			    KSTAT_TYPE_NAMED,
+			    sizeof (batt_bst_kstat) / sizeof (kstat_named_t),
+			    KSTAT_FLAG_VIRTUAL);
+			if (ksp == NULL) {
+				BATT_DBG(CE_WARN, NULL,
+				    "kstat_create(%s) fail", name);
+				return (BATT_ERR);
+			}
+			BATT_DBG(CE_NOTE, NULL, "kstat_create(%s) ok", name);
+
+			bp->bat_bst_ksp = ksp;
+			ksp->ks_data = &batt_bst_kstat;
+			ksp->ks_update = batt_kstat_bst_update;
+			ksp->ks_data_size += MAXNAMELEN * 4;
+			ksp->ks_private = bp;
+
+			kstat_install(ksp);
+		}
+	}
+
+	return (BATT_OK);
+}
+
+static void
+batt_kstat_fini()
+{
+	struct batt_cbat_state *bp;
+
+	if (batt_power_ksp != NULL) {
+		kstat_delete(batt_power_ksp);
+	}
+	if (batt_warn_ksp != NULL) {
+		kstat_delete(batt_warn_ksp);
+	}
+	for (bp = &batt_cbat[0]; bp < &batt_cbat[BATT_MAX_BAT_NUM]; bp++) {
+		if (bp->dev.valid) {
+			if (bp->bat_bif_ksp != NULL) {
+				kstat_delete(bp->bat_bif_ksp);
+			}
+			if (bp->bat_bst_ksp != NULL) {
+				kstat_delete(bp->bat_bst_ksp);
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/i86pc/io/battery/battery.conf	Sat Apr 14 18:08:01 2007 -0700
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+# ident	"%Z%%M%	%I%	%E% SMI"
+
+name="battery" parent="pseudo" instance=0;
+
+ddi-forceattach=1;
--- a/usr/src/uts/intel/os/minor_perm	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/intel/os/minor_perm	Sat Apr 14 18:08:01 2007 -0700
@@ -141,3 +141,4 @@
 ath:* 0666 root sys
 physmem:* 0600 root sys
 sdp:sdp 0666 root sys
+battery:* 0666 root sys
--- a/usr/src/uts/intel/os/name_to_major	Sat Apr 14 11:11:14 2007 -0700
+++ b/usr/src/uts/intel/os/name_to_major	Sat Apr 14 18:08:01 2007 -0700
@@ -124,3 +124,4 @@
 lx_systrace 241
 lx_audio 242
 physmem 243
+battery 244