Module Name:    src
Committed By:   jruoho
Date:           Sun Jun 12 07:25:44 UTC 2011

Modified Files:
        src/share/man/man4: aibs.4
        src/sys/dev/acpi: files.acpi
        src/sys/modules/aibs: Makefile
Added Files:
        src/sys/dev/acpi: aibs_acpi.c
Removed Files:
        src/sys/dev/acpi: atk0110.c

Log Message:
Rewrite the aibs(4) driver in order to support new models. Draws from the
revision 1.6 (claudio@) of the OpenBSD's equivalent driver. Tested by mrg@.


To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/share/man/man4/aibs.4
cvs rdiff -u -r0 -r1.1 src/sys/dev/acpi/aibs_acpi.c
cvs rdiff -u -r1.16 -r0 src/sys/dev/acpi/atk0110.c
cvs rdiff -u -r1.88 -r1.89 src/sys/dev/acpi/files.acpi
cvs rdiff -u -r1.3 -r1.4 src/sys/modules/aibs/Makefile

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/share/man/man4/aibs.4
diff -u src/share/man/man4/aibs.4:1.6 src/share/man/man4/aibs.4:1.7
--- src/share/man/man4/aibs.4:1.6	Sat Mar  6 19:53:33 2010
+++ src/share/man/man4/aibs.4	Sun Jun 12 07:25:43 2011
@@ -1,4 +1,4 @@
-.\"	$NetBSD: aibs.4,v 1.6 2010/03/06 19:53:33 cnst Exp $
+.\"	$NetBSD: aibs.4,v 1.7 2011/06/12 07:25:43 jruoho Exp $
 .\"	$OpenBSD: aibs.4,v 1.4 2009/07/30 06:30:45 jmc Exp $
 .\"
 .\" Copyright (c) 2009 Constantine A. Murenin <cnst+net...@bugmail.mojo.ru>
@@ -15,24 +15,21 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd February 8, 2010
+.Dd June 12, 2011
 .Dt AIBS 4
 .Os
 .Sh NAME
 .Nm aibs
-.Nd ASUSTeK AI Booster ACPI ATK0110 voltage, temperature, and fan sensor
+.Nd ASUSTeK AI Booster voltage, temperature, and fan sensor
 .Sh SYNOPSIS
 .Cd "aibs* at acpi?"
 .Sh DESCRIPTION
 The
 .Nm
-driver provides support for the voltage, temperature and fan sensors
-available through the
-.Tn ATK0110
-.Tn ASOC
+driver provides support for voltage, temperature, and fan sensors
+available as an
 .Tn ACPI
-device
-on
+device on
 .Tn ASUSTeK
 motherboards.
 The number of sensors of each type,
@@ -53,43 +50,43 @@
 .Bl -bullet
 .It
 Voltage sensors can have a state of
-.Dv valid ,
-.Dv critunder ,
+.Sq valid ,
+.Sq critunder ,
 or
-.Dv critover ;
+.Sq critover ;
 temperature sensors can have a state of
-.Dv valid ,
-.Dv warnover ,
-.Dv critover ,
+.Sq valid ,
+.Sq warnover ,
+.Sq critover ,
 or
-.Dv invalid ;
+.Sq invalid ;
 and fan sensors can have a state of
-.Dv valid ,
-.Dv warnunder ,
+.Sq valid ,
+.Sq warnunder ,
 or
-.Dv warnover .
+.Sq warnover .
 .It
 Temperature sensors that have a reading of 0
 are marked
-.Dv invalid ,
+.Sq invalid ,
 whereas all other sensors are always assumed valid.
 .It
-Voltage sensors have a lower and an upper limit
-.Dv ( critunder
+Voltage sensors have a lower and an upper limit,
+.Sq critunder
 and
-.Dv critover ) ,
-temperature sensors have two upper limits
-.Dv ( warnover
+.Sq critover ,
+temperature sensors have two upper limits,
+.Sq warnover
 and
-.Dv critover ) ,
+.Sq critover ,
 whereas fan sensors may either have only the lower limit
-.Dv ( warnunder ) ,
-or, depending on the
-.Tn DSDT ,
-one lower and one upper limit
-.Dv ( warnunder
+.Sq warnunder ,
+or, depending on the vendor's
+.Tn ACPI
+implementation, one lower and one upper limit,
+.Sq warnunder
 and
-.Dv warnover ) .
+.Sq warnover .
 .El
 .Pp
 Sensor values and limits are made available through the
@@ -189,6 +186,7 @@
 the hardware monitoring chip through
 .Tn ACPI .
 .Sh SEE ALSO
+.Xr acpi 4 ,
 .Xr envsys 4 ,
 .Xr envstat 8
 .Sh HISTORY
@@ -199,8 +197,7 @@
 DragonFly 2.4.1
 and
 .Nx 6.0 .
-.Pp
-An earlier version of the driver,
+An earlier version of the driver, named
 .Nm aiboost ,
 first appeared in
 .Fx 7.0
@@ -212,16 +209,19 @@
 .Nm
 driver was written for
 .Ox ,
-DragonFly
-and
+DragonFly BSD, and
 .Nx
 by
 .An Constantine A. Murenin Aq http://cnst.su/ ,
 Raouf Boutaba Research Group,
 David R. Cheriton School of Computer Science,
 University of Waterloo.
-.Pp
-An earlier version of the driver, named
+.An Jukka Ruohonen
+.Aq jruoho...@iki.fi
+later reworked and adjusted the driver to support new
+.Tn ASUSTeK
+motherboards.
+The earlier version of the driver,
 .Nm aiboost ,
 was written for
 .Fx

Index: src/sys/dev/acpi/files.acpi
diff -u src/sys/dev/acpi/files.acpi:1.88 src/sys/dev/acpi/files.acpi:1.89
--- src/sys/dev/acpi/files.acpi:1.88	Sun Feb 27 17:10:33 2011
+++ src/sys/dev/acpi/files.acpi	Sun Jun 12 07:25:43 2011
@@ -1,4 +1,4 @@
-#	$NetBSD: files.acpi,v 1.88 2011/02/27 17:10:33 jruoho Exp $
+#	$NetBSD: files.acpi,v 1.89 2011/06/12 07:25:43 jruoho Exp $
 
 include "dev/acpi/acpica/files.acpica"
 
@@ -180,10 +180,10 @@
 attach	wb at acpinodebus with wb_acpi
 file	dev/acpi/wb_acpi.c		wb_acpi
 
-# ASUSTeK AI Booster ATK0110
+# ASUSTeK AI Booster
 device	aibs: sysmon_envsys
 attach	aibs at acpinodebus
-file	dev/acpi/atk0110.c		aibs
+file	dev/acpi/aibs_acpi.c		aibs
 
 # ACPI SMBus controller
 device	acpismbus: i2cbus

Index: src/sys/modules/aibs/Makefile
diff -u src/sys/modules/aibs/Makefile:1.3 src/sys/modules/aibs/Makefile:1.4
--- src/sys/modules/aibs/Makefile:1.3	Wed Feb 16 08:08:14 2011
+++ src/sys/modules/aibs/Makefile	Sun Jun 12 07:25:43 2011
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.3 2011/02/16 08:08:14 jruoho Exp $
+#	$NetBSD: Makefile,v 1.4 2011/06/12 07:25:43 jruoho Exp $
 
 .include "../Makefile.inc"
 
@@ -6,7 +6,7 @@
 
 KMOD=	aibs
 IOCONF=	aibs.ioconf
-SRCS=	atk0110.c
+SRCS=	aibs_acpi.c
 
 WARNS=	4
 

Added files:

Index: src/sys/dev/acpi/aibs_acpi.c
diff -u /dev/null src/sys/dev/acpi/aibs_acpi.c:1.1
--- /dev/null	Sun Jun 12 07:25:44 2011
+++ src/sys/dev/acpi/aibs_acpi.c	Sun Jun 12 07:25:43 2011
@@ -0,0 +1,742 @@
+/* $NetBSD: aibs_acpi.c,v 1.1 2011/06/12 07:25:43 jruoho Exp $ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jukka Ruohonen.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*	$OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $	*/
+/*
+ * Copyright (c) 2009 Constantine A. Murenin <cnst+net...@bugmail.mojo.ru>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: aibs_acpi.c,v 1.1 2011/06/12 07:25:43 jruoho Exp $");
+
+#include <sys/param.h>
+#include <sys/kmem.h>
+#include <sys/module.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+
+/*
+ * ASUSTeK AI Booster (ACPI ASOC ATK0110).
+ *
+ * This code was originally written for OpenBSD after the techniques
+ * described in the Linux's asus_atk0110.c and FreeBSD's acpi_aiboost.c
+ * were verified to be accurate on the actual hardware kindly provided by
+ * Sam Fourman Jr.  It was subsequently ported from OpenBSD to DragonFly BSD,
+ * and then to the NetBSD's sysmon_envsys(9) framework.
+ *
+ *				  -- Constantine A. Murenin <http://cnst.su/>
+ */
+
+#define _COMPONENT		 ACPI_RESOURCE_COMPONENT
+ACPI_MODULE_NAME		 ("acpi_aibs")
+
+#define AIBS_MUX_HWMON		 0x00000006
+#define AIBS_MUX_MGMT		 0x00000011
+
+#define AIBS_TYPE(x)		 (((x) >> 16) & 0xff)
+#define AIBS_TYPE_VOLT		 2
+#define AIBS_TYPE_TEMP		 3
+#define AIBS_TYPE_FAN		 4
+
+struct aibs_sensor {
+	envsys_data_t			 as_sensor;
+	uint64_t			 as_type;
+	uint64_t			 as_liml;
+	uint64_t			 as_limh;
+
+	SIMPLEQ_ENTRY(aibs_sensor)	 as_list;
+};
+
+struct aibs_softc {
+	device_t			 sc_dev;
+	struct acpi_devnode		*sc_node;
+	struct sysmon_envsys		*sc_sme;
+	bool				 sc_model;	/* new model = true */
+
+	SIMPLEQ_HEAD(, aibs_sensor)	 as_head;
+};
+
+static int	aibs_match(device_t, cfdata_t, void *);
+static void	aibs_attach(device_t, device_t, void *);
+static int	aibs_detach(device_t, int);
+
+static void	aibs_init(device_t);
+static void	aibs_init_new(device_t);
+static void	aibs_init_old(device_t, int);
+
+static void	aibs_sensor_add(device_t, ACPI_OBJECT *);
+static bool	aibs_sensor_value(device_t, struct aibs_sensor *, uint64_t *);
+static void	aibs_sensor_refresh(struct sysmon_envsys *, envsys_data_t *);
+static void	aibs_sensor_limits(struct sysmon_envsys *, envsys_data_t *,
+				   sysmon_envsys_lim_t *, uint32_t *);
+
+CFATTACH_DECL_NEW(aibs, sizeof(struct aibs_softc),
+    aibs_match, aibs_attach, aibs_detach, NULL);
+
+static const char* const aibs_hid[] = {
+	"ATK0110",
+	NULL
+};
+
+static int
+aibs_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct acpi_attach_args *aa = aux;
+
+	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
+		return 0;
+
+	return acpi_match_hid(aa->aa_node->ad_devinfo, aibs_hid);
+}
+
+static void
+aibs_attach(device_t parent, device_t self, void *aux)
+{
+	struct aibs_softc *sc = device_private(self);
+	struct acpi_attach_args *aa = aux;
+
+	sc->sc_dev = self;
+	sc->sc_node = aa->aa_node;
+
+	aprint_naive("\n");
+	aprint_normal(": ASUSTeK AI Booster\n");
+
+	sc->sc_sme = sysmon_envsys_create();
+
+	sc->sc_sme->sme_cookie = sc;
+	sc->sc_sme->sme_name = device_xname(self);
+	sc->sc_sme->sme_refresh = aibs_sensor_refresh;
+	sc->sc_sme->sme_get_limits = aibs_sensor_limits;
+
+	aibs_init(self);
+	SIMPLEQ_INIT(&sc->as_head);
+
+	if (sc->sc_model != false)
+		aibs_init_new(self);
+	else {
+		aibs_init_old(self, AIBS_TYPE_FAN);
+		aibs_init_old(self, AIBS_TYPE_TEMP);
+		aibs_init_old(self, AIBS_TYPE_VOLT);
+	}
+
+	(void)pmf_device_register(self, NULL, NULL);
+
+	if (sc->sc_sme->sme_nsensors == 0) {
+		aprint_error_dev(self, "no sensors found\n");
+		sysmon_envsys_destroy(sc->sc_sme);
+		sc->sc_sme = NULL;
+		return;
+	}
+
+	if (sysmon_envsys_register(sc->sc_sme) != 0)
+		aprint_error_dev(self, "failed to register with sysmon\n");
+}
+
+static int
+aibs_detach(device_t self, int flags)
+{
+	struct aibs_softc *sc = device_private(self);
+	struct aibs_sensor *as;
+
+	pmf_device_deregister(self);
+
+	if (sc->sc_sme != NULL)
+		sysmon_envsys_unregister(sc->sc_sme);
+
+	while (SIMPLEQ_FIRST(&sc->as_head) != NULL) {
+		as = SIMPLEQ_FIRST(&sc->as_head);
+		SIMPLEQ_REMOVE_HEAD(&sc->as_head, as_list);
+		kmem_free(as, sizeof(*as));
+	}
+
+	return 0;
+}
+
+static void
+aibs_init(device_t self)
+{
+	struct aibs_softc *sc = device_private(self);
+	ACPI_HANDLE tmp;
+	ACPI_STATUS rv;
+
+	/*
+	 * Old model uses the tuple { TSIF, VSIF, FSIF } to
+	 * enumerate the sensors and { RTMP, RVLT, RFAN }
+	 * to obtain the values. New mode uses GGRP for the
+	 * enumeration and { GITM, SITM } as accessors.
+	 */
+	rv = AcpiGetHandle(sc->sc_node->ad_handle, "GGRP", &tmp);
+
+	if (ACPI_FAILURE(rv)) {
+		sc->sc_model = false;
+		return;
+	}
+
+	rv = AcpiGetHandle(sc->sc_node->ad_handle, "GITM", &tmp);
+
+	if (ACPI_FAILURE(rv)) {
+		sc->sc_model = false;
+		return;
+	}
+
+	rv = AcpiGetHandle(sc->sc_node->ad_handle, "SITM", &tmp);
+
+	if (ACPI_FAILURE(rv)) {
+		sc->sc_model = false;
+		return;
+	}
+
+	sc->sc_model = true;
+}
+
+static void
+aibs_init_new(device_t self)
+{
+	struct aibs_softc *sc = device_private(self);
+	ACPI_OBJECT_LIST arg;
+	ACPI_OBJECT id, *obj;
+	ACPI_BUFFER buf;
+	ACPI_STATUS rv;
+	uint32_t i, n;
+
+	arg.Count = 1;
+	arg.Pointer = &id;
+
+	id.Type = ACPI_TYPE_INTEGER;
+	id.Integer.Value = AIBS_MUX_HWMON;
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GGRP", &arg, &buf);
+
+	if (ACPI_FAILURE(rv))
+		goto out;
+
+	obj = buf.Pointer;
+
+	if (obj->Type != ACPI_TYPE_PACKAGE) {
+		rv = AE_TYPE;
+		goto out;
+	}
+
+	if (obj->Package.Count > UINT32_MAX) {
+		rv = AE_AML_NUMERIC_OVERFLOW;
+		goto out;
+	}
+
+	n = obj->Package.Count;
+
+	if (n == 0) {
+		rv = AE_NOT_EXIST;
+		goto out;
+	}
+
+	for (i = 0; i < n; i++)
+		aibs_sensor_add(self, &obj->Package.Elements[i]);
+
+out:
+	if (buf.Pointer != NULL)
+		ACPI_FREE(buf.Pointer);
+
+	if (ACPI_FAILURE(rv)) {
+
+		aprint_error_dev(self, "failed to evaluate "
+		    "GGRP: %s\n", AcpiFormatException(rv));
+	}
+}
+
+static void
+aibs_init_old(device_t self, int type)
+{
+	struct aibs_softc *sc = device_private(self);
+	char path[] = "?SIF";
+	ACPI_OBJECT *elm, *obj;
+	ACPI_BUFFER buf;
+	ACPI_STATUS rv;
+	uint32_t i, n;
+
+	switch (type) {
+
+	case AIBS_TYPE_FAN:
+		path[0] = 'F';
+		break;
+
+	case AIBS_TYPE_TEMP:
+		path[0] = 'T';
+		break;
+
+	case AIBS_TYPE_VOLT:
+		path[0] = 'V';
+		break;
+
+	default:
+		return;
+	}
+
+	rv = acpi_eval_struct(sc->sc_node->ad_handle, path, &buf);
+
+	if (ACPI_FAILURE(rv))
+		goto out;
+
+	obj = buf.Pointer;
+
+	if (obj->Type != ACPI_TYPE_PACKAGE) {
+		rv = AE_TYPE;
+		goto out;
+	}
+
+	elm = obj->Package.Elements;
+
+	if (elm[0].Type != ACPI_TYPE_INTEGER) {
+		rv = AE_TYPE;
+		goto out;
+	}
+
+	if (elm[0].Integer.Value > UINT32_MAX) {
+		rv = AE_AML_NUMERIC_OVERFLOW;
+		goto out;
+	}
+
+	n = elm[0].Integer.Value;
+
+	if (n == 0) {
+		rv = AE_NOT_EXIST;
+		goto out;
+	}
+
+	if (obj->Package.Count - 1 != n) {
+		rv = AE_BAD_VALUE;
+		goto out;
+	}
+
+	for (i = 1; i < obj->Package.Count; i++) {
+
+		if (elm[i].Type != ACPI_TYPE_PACKAGE)
+			continue;
+
+		aibs_sensor_add(self, &elm[i]);
+	}
+
+out:
+	if (buf.Pointer != NULL)
+		ACPI_FREE(buf.Pointer);
+
+	if (ACPI_FAILURE(rv)) {
+
+		aprint_error_dev(self, "failed to evaluate "
+		    "%s: %s\n", path, AcpiFormatException(rv));
+	}
+}
+
+static void
+aibs_sensor_add(device_t self, ACPI_OBJECT *obj)
+{
+	struct aibs_softc *sc = device_private(self);
+	struct aibs_sensor *as;
+	int ena, len, lhi, llo;
+	const char *name;
+	ACPI_STATUS rv;
+
+	as = NULL;
+	rv = AE_OK;
+
+	if (obj->Type != ACPI_TYPE_PACKAGE) {
+		rv = AE_TYPE;
+		goto out;
+	}
+
+	/*
+	 * The known formats are:
+	 *
+	 *	index		type		old		new
+	 *	-----		----		---		---
+	 *	0		integer		flags		flags
+	 *	1		string		name		name
+	 *	2		integer		limit1		unknown
+	 *	3		integer		limit2		unknown
+	 *	4		integer		enable		limit1
+	 *	5		integer		-		limit2
+	 *	6		integer		-		enable
+	 */
+	if (sc->sc_model != false) {
+		len = 7;
+		llo = 4;
+		lhi = 5;
+		ena = 6;
+	} else {
+		len = 5;
+		llo = 2;
+		lhi = 3;
+		ena = 4;
+	}
+
+	if (obj->Package.Count != (uint32_t)len) {
+		rv = AE_LIMIT;
+		goto out;
+	}
+
+	if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
+	    obj->Package.Elements[1].Type != ACPI_TYPE_STRING ||
+	    obj->Package.Elements[llo].Type != ACPI_TYPE_INTEGER ||
+	    obj->Package.Elements[lhi].Type != ACPI_TYPE_INTEGER ||
+	    obj->Package.Elements[ena].Type != ACPI_TYPE_INTEGER) {
+		rv = AE_TYPE;
+		goto out;
+	}
+
+	as = kmem_zalloc(sizeof(*as), KM_SLEEP);
+
+	if (as == NULL) {
+		rv = AE_NO_MEMORY;
+		goto out;
+	}
+
+	name = obj->Package.Elements[1].String.Pointer;
+
+	as->as_type = obj->Package.Elements[0].Integer.Value;
+	as->as_liml = obj->Package.Elements[llo].Integer.Value;
+	as->as_limh = obj->Package.Elements[lhi].Integer.Value;
+
+	if (sc->sc_model != false)
+		as->as_limh += as->as_liml;	/* A range in the new model. */
+
+	switch (AIBS_TYPE(as->as_type)) {
+
+	case AIBS_TYPE_FAN:
+		as->as_sensor.units = ENVSYS_SFANRPM;
+		as->as_sensor.flags |= ENVSYS_FMONLIMITS;
+		break;
+
+	case AIBS_TYPE_TEMP:
+		as->as_sensor.units = ENVSYS_STEMP;
+		as->as_sensor.flags |= ENVSYS_FMONLIMITS;
+		break;
+
+	case AIBS_TYPE_VOLT:
+		as->as_sensor.units = ENVSYS_SVOLTS_DC;
+		as->as_sensor.flags |= ENVSYS_FMONLIMITS;
+		break;
+
+	default:
+		rv = AE_TYPE;
+		goto out;
+	}
+
+	(void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc));
+
+	if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) {
+		rv = AE_AML_INTERNAL;
+		goto out;
+	}
+
+	SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list);
+
+out:
+	if (ACPI_FAILURE(rv)) {
+
+		if (as != NULL)
+			kmem_free(as, sizeof(*as));
+
+		aprint_error_dev(self, "failed to add "
+		    "sensor: %s\n",  AcpiFormatException(rv));
+	}
+}
+
+static bool
+aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val)
+{
+	struct aibs_softc *sc = device_private(self);
+	uint32_t type, *ret, cmb[3];
+	ACPI_OBJECT_LIST arg;
+	ACPI_OBJECT cmi, tmp;
+	ACPI_OBJECT *obj;
+	ACPI_BUFFER buf;
+	ACPI_STATUS rv;
+	const char *path;
+
+	if (sc->sc_model != false) {
+
+		path = "GITM";
+
+		cmb[0] = as->as_type;
+		cmb[1] = 0;
+		cmb[2] = 0;
+
+		arg.Count = 1;
+		arg.Pointer = &tmp;
+
+		tmp.Buffer.Length = sizeof(cmb);
+		tmp.Buffer.Pointer = (uint8_t *)cmb;
+		tmp.Type = type = ACPI_TYPE_BUFFER;
+
+	} else {
+
+		arg.Count = 1;
+		arg.Pointer = &cmi;
+
+		cmi.Integer.Value = as->as_type;
+		cmi.Type = type = ACPI_TYPE_INTEGER;
+
+		switch (AIBS_TYPE(as->as_type)) {
+
+		case AIBS_TYPE_FAN:
+			path = "RFAN";
+			break;
+
+		case AIBS_TYPE_TEMP:
+			path = "RTMP";
+			break;
+
+		case AIBS_TYPE_VOLT:
+			path = "RVLT";
+			break;
+
+		default:
+			return false;
+		}
+	}
+
+	buf.Pointer = NULL;
+	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf);
+
+	if (ACPI_FAILURE(rv))
+		goto out;
+
+	obj = buf.Pointer;
+
+	if (obj->Type != type) {
+		rv = AE_TYPE;
+		goto out;
+	}
+
+	if (sc->sc_model != true)
+		*val = obj->Integer.Value;
+	else {
+		/*
+		 * The return buffer contains at least:
+		 *
+		 *	uint32_t buf[0]	 flags
+		 *	uint32_t buf[1]	 return value
+		 *	uint8_t  buf[2-] unknown
+		 */
+		if (obj->Buffer.Length < 8) {
+			rv = AE_BUFFER_OVERFLOW;
+			goto out;
+		}
+
+		ret = (uint32_t *)obj->Buffer.Pointer;
+
+		if (ret[0] == 0) {
+			rv = AE_BAD_VALUE;
+			goto out;
+		}
+
+		*val = ret[1];
+	}
+
+out:
+	if (buf.Pointer != NULL)
+		ACPI_FREE(buf.Pointer);
+
+	if (ACPI_FAILURE(rv)) {
+
+		aprint_error_dev(self, "failed to evaluate "
+		    "%s: %s\n", path, AcpiFormatException(rv));
+
+		return false;
+	}
+
+	return true;
+}
+
+static void
+aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
+{
+	struct aibs_softc *sc = sme->sme_cookie;
+	struct aibs_sensor *tmp, *as = NULL;
+	envsys_data_t *s = edata;
+	uint64_t val = 0;
+
+	SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
+
+		if (tmp->as_sensor.sensor == s->sensor) {
+			as = tmp;
+			break;
+		}
+	}
+
+	if (as == NULL) {
+		aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
+		return;
+	}
+
+	as->as_sensor.state = ENVSYS_SINVALID;
+	as->as_sensor.flags |= ENVSYS_FMONNOTSUPP;
+
+	if (aibs_sensor_value(sc->sc_dev, as, &val) != true)
+		return;
+
+	switch (as->as_sensor.units) {
+
+	case ENVSYS_SFANRPM:
+		as->as_sensor.value_cur = val;
+		break;
+
+	case ENVSYS_STEMP:
+
+		if (val == 0)
+			return;
+
+		as->as_sensor.value_cur = val * 100 * 1000 + 273150000;
+		break;
+
+	case ENVSYS_SVOLTS_DC:
+		as->as_sensor.value_cur = val * 1000;
+		break;
+
+	default:
+		return;
+	}
+
+	as->as_sensor.state = ENVSYS_SVALID;
+	as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP;
+}
+
+static void
+aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
+    sysmon_envsys_lim_t *limits, uint32_t *props)
+{
+	struct aibs_softc *sc = sme->sme_cookie;
+	struct aibs_sensor *tmp, *as = NULL;
+	sysmon_envsys_lim_t *lim = limits;
+	envsys_data_t *s = edata;
+
+	SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
+
+		if (tmp->as_sensor.sensor == s->sensor) {
+			as = tmp;
+			break;
+		}
+	}
+
+	if (as == NULL) {
+		aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
+		return;
+	}
+
+	switch (as->as_sensor.units) {
+
+	case ENVSYS_SFANRPM:
+
+		/*
+		 * Some boards have strange limits for fans.
+		 */
+		if (as->as_liml == 0) {
+			lim->sel_warnmin = as->as_limh;
+			*props = PROP_WARNMIN;
+
+		} else {
+			lim->sel_warnmin = as->as_liml;
+			lim->sel_warnmax = as->as_limh;
+			*props = PROP_WARNMIN | PROP_WARNMAX;
+		}
+
+		break;
+
+	case ENVSYS_STEMP:
+		lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000;
+		lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000;
+
+		*props = PROP_CRITMAX | PROP_WARNMAX;
+		break;
+
+	case ENVSYS_SVOLTS_DC:
+		lim->sel_critmin = as->as_liml * 1000;
+		lim->sel_critmax = as->as_limh * 1000;
+		*props = PROP_CRITMIN | PROP_CRITMAX;
+		break;
+
+	default:
+		return;
+	}
+}
+
+MODULE(MODULE_CLASS_DRIVER, aibs, NULL);
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+aibs_modcmd(modcmd_t cmd, void *aux)
+{
+	int rv = 0;
+
+	switch (cmd) {
+
+	case MODULE_CMD_INIT:
+
+#ifdef _MODULE
+		rv = config_init_component(cfdriver_ioconf_aibs,
+		    cfattach_ioconf_aibs, cfdata_ioconf_aibs);
+#endif
+		break;
+
+	case MODULE_CMD_FINI:
+
+#ifdef _MODULE
+		rv = config_fini_component(cfdriver_ioconf_aibs,
+		    cfattach_ioconf_aibs, cfdata_ioconf_aibs);
+#endif
+		break;
+
+	default:
+		rv = ENOTTY;
+	}
+
+	return rv;
+}

Reply via email to