Mercurial > illumos > illumos-gate
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
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;