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 <[email protected]>
@@ -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 [email protected]
+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 <[email protected]>
+ *
+ * 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;
+}