Page MenuHomeFreeBSD

D8911.diff
No OneTemporary

D8911.diff

Index: etc/Makefile
===================================================================
--- etc/Makefile
+++ etc/Makefile
@@ -48,6 +48,7 @@
rc.subr \
remote \
rpc \
+ sensorsd.conf \
services \
shells \
sysctl.conf \
Index: etc/defaults/rc.conf
===================================================================
--- etc/defaults/rc.conf
+++ etc/defaults/rc.conf
@@ -47,6 +47,8 @@
kldxref_module_path="" # Override kern.module_path. A ';'-delimited list.
powerd_enable="NO" # Run powerd to lower our power usage.
powerd_flags="" # Flags to powerd (if enabled).
+sensorsd_enable="NO" # Run sensorsd to monitor and log sensor state changes
+sensorsd_flags="" # additional flags for sensorsd(8)
tmpmfs="AUTO" # Set to YES to always create an mfs /tmp, NO to never
tmpsize="20m" # Size of mfs /tmp if created
tmpmfs_flags="-S" # Extra mdmfs options for the mfs /tmp
Index: etc/rc.d/Makefile
===================================================================
--- etc/rc.d/Makefile
+++ etc/rc.d/Makefile
@@ -103,6 +103,7 @@
rtsold \
savecore \
securelevel \
+ sensorsd \
serial \
sppp \
statd \
Index: etc/rc.d/sensorsd
===================================================================
--- /dev/null
+++ etc/rc.d/sensorsd
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: sensorsd
+# REQUIRE: syslogd
+#
+
+. /etc/rc.subr
+
+name="sensorsd"
+rcvar="sensorsd_enable"
+command="/usr/sbin/${name}"
+
+load_rc_config $name
+run_rc_command "$1"
Index: etc/sensorsd.conf
===================================================================
--- /dev/null
+++ etc/sensorsd.conf
@@ -0,0 +1,50 @@
+# $FreeBSD$
+# $OpenBSD: sensorsd.conf,v 1.8 2007/08/14 19:02:02 cnst Exp $
+
+#
+# Sample sensorsd.conf file. See sensorsd.conf(5) for details.
+#
+
+# +5 voltage (volts)
+#hw.sensors.lm0.volt3:low=4.8V:high=5.2V
+
+# +12 voltage (volts)
+#hw.sensors.lm0.volt4:low=11.5V:high=12.5V
+
+# Chipset temperature (degrees Celsius)
+#hw.sensors.lm0.temp0:high=50C
+
+# CPU temperature (degrees Celsius)
+#hw.sensors.lm0.temp1:high=60C
+
+# CPU fan (RPM)
+#hw.sensors.lm0.fan1:low=3000
+
+# ignore certain indicators on ipmi(4)
+#hw.sensors.ipmi0.indicator1:istatus
+
+# Warn if any temperature sensor is over 70 degC.
+# This entry will match only those temperature sensors
+# that don't have their own entry.
+#temp:high=70C
+
+
+# By default, sensorsd(8) reports status changes of all sensors that
+# keep their state. Uncomment the following lines if you want to
+# suppress reports about status changes of specific sensor types.
+
+#temp:istatus
+#fan:istatus
+#volt:istatus
+#acvolt:istatus
+#resistance:istatus
+#power:istatus
+#current:istatus
+#watthour:istatus
+#amphour:istatus
+#indicator:istatus
+#raw:istatus
+#percentage:istatus
+#illuminance:istatus
+#drive:istatus
+#timedelta:istatus
Index: lib/libc/gen/sysctl.3
===================================================================
--- lib/libc/gen/sysctl.3
+++ lib/libc/gen/sysctl.3
@@ -284,6 +284,7 @@
.It Dv HW_FLOATINGPT Ta integer Ta no
.It Dv HW_MACHINE_ARCH Ta string Ta no
.It Dv HW_REALMEM Ta integer Ta no
+.It Dv HW_SENSORS Ta node Ta not applicable
.El
.Bl -tag -width 6n
.It Li HW_MACHINE
@@ -308,6 +309,34 @@
The machine dependent architecture type.
.It Li HW_REALMEM
The bytes of real memory.
+.It Li HW_SENSORS
+Third level comprises an array of
+.Li struct sensordev
+structures containing information about devices
+that may attach hardware monitoring sensors.
+.Pp
+Third, fourth and fifth levels together comprise an array of
+.Li struct sensor
+structures containing snapshot readings of hardware monitoring sensors.
+In such usage, third level indicates the numerical representation
+of the sensor device name to which the sensor is attached
+(device's xname and number shall be matched with the help of
+.Li struct sensordev
+structure above),
+fourth level indicates sensor type and
+fifth level is an ordinal sensor number (unique to
+the specified sensor type on the specified sensor device).
+.Pp
+The
+.Sy sensordev
+and
+.Sy sensor
+structures
+and
+.Sy sensor_type
+enumeration
+are defined in
+.In sys/sensors.h .
.El
.Ss CTL_KERN
The string and integer information available for the CTL_KERN level
Index: sbin/sysctl/sysctl.8
===================================================================
--- sbin/sysctl/sysctl.8
+++ sbin/sysctl/sysctl.8
@@ -233,6 +233,7 @@
.It "hw.floatingpoint integer no"
.It "hw.machine_arch string no"
.It "hw.realmem integer no"
+.It "hw.sensors.<xname>.<type><numt> struct no"
.It "machdep.adjkerntz integer yes"
.It "machdep.disable_rtc_set integer yes"
.It "machdep.guessed_bootdev string no"
Index: sbin/sysctl/sysctl.c
===================================================================
--- sbin/sysctl/sysctl.c
+++ sbin/sysctl/sysctl.c
@@ -44,6 +44,7 @@
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
+#include <sys/sensors.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
@@ -740,6 +741,143 @@
#endif
static int
+S_sensor(size_t l2, void *p)
+{
+ struct sensor *s = (struct sensor *)p;
+
+ if (l2 != sizeof(*s)) {
+ warnx("S_sensor %zu != %zu", l2, sizeof(*s));
+ return (1);
+ }
+
+ if (s->flags & SENSOR_FINVALID) {
+ /*
+ * XXX: with this flag, the node should be entirely ignored,
+ * but as the magic-based sysctl(8) is not too flexible, we
+ * simply have to print out that the sensor is invalid.
+ */
+ printf("invalid");
+ return (0);
+ }
+
+ if (s->flags & SENSOR_FUNKNOWN)
+ printf("unknown");
+ else {
+ switch (s->type) {
+ case SENSOR_TEMP:
+ printf("%.2f degC",
+ (s->value - 273150000) / 1000000.0);
+ break;
+ case SENSOR_FANRPM:
+ printf("%lld RPM", (long long int)s->value);
+ break;
+ case SENSOR_VOLTS_DC:
+ printf("%.2f VDC", s->value / 1000000.0);
+ break;
+ case SENSOR_AMPS:
+ printf("%.2f A", s->value / 1000000.0);
+ break;
+ case SENSOR_WATTHOUR:
+ printf("%.2f Wh", s->value / 1000000.0);
+ break;
+ case SENSOR_AMPHOUR:
+ printf("%.2f Ah", s->value / 1000000.0);
+ break;
+ case SENSOR_INDICATOR:
+ printf("%s", s->value ? "On" : "Off");
+ break;
+ case SENSOR_INTEGER:
+ printf("%lld", (long long int)s->value);
+ break;
+ case SENSOR_PERCENT:
+ printf("%.2f%%", s->value / 1000.0);
+ break;
+ case SENSOR_LUX:
+ printf("%.2f lx", s->value / 1000000.0);
+ break;
+ case SENSOR_DRIVE:
+ {
+ const char *name;
+
+ switch (s->value) {
+ case SENSOR_DRIVE_EMPTY:
+ name = "empty";
+ break;
+ case SENSOR_DRIVE_READY:
+ name = "ready";
+ break;
+ case SENSOR_DRIVE_POWERUP:
+ name = "powering up";
+ break;
+ case SENSOR_DRIVE_ONLINE:
+ name = "online";
+ break;
+ case SENSOR_DRIVE_IDLE:
+ name = "idle";
+ break;
+ case SENSOR_DRIVE_ACTIVE:
+ name = "active";
+ break;
+ case SENSOR_DRIVE_REBUILD:
+ name = "rebuilding";
+ break;
+ case SENSOR_DRIVE_POWERDOWN:
+ name = "powering down";
+ break;
+ case SENSOR_DRIVE_FAIL:
+ name = "failed";
+ break;
+ case SENSOR_DRIVE_PFAIL:
+ name = "degraded";
+ break;
+ default:
+ name = "unknown";
+ break;
+ }
+ printf("%s", name);
+ break;
+ }
+ case SENSOR_TIMEDELTA:
+ printf("%.6f secs", s->value / 1000000000.0);
+ break;
+ default:
+ printf("unknown");
+ }
+ }
+
+ if (s->desc[0] != '\0')
+ printf(" (%s)", s->desc);
+
+ switch (s->status) {
+ case SENSOR_S_UNSPEC:
+ break;
+ case SENSOR_S_OK:
+ printf(", OK");
+ break;
+ case SENSOR_S_WARN:
+ printf(", WARNING");
+ break;
+ case SENSOR_S_CRIT:
+ printf(", CRITICAL");
+ break;
+ case SENSOR_S_UNKNOWN:
+ printf(", UNKNOWN");
+ break;
+ }
+
+ if (s->tv.tv_sec) {
+ time_t t = s->tv.tv_sec;
+ char ct[26];
+
+ ctime_r(&t, ct);
+ ct[19] = '\0';
+ printf(", %s.%03ld", ct, s->tv.tv_usec / 1000);
+ }
+
+ return (0);
+}
+
+static int
strIKtoi(const char *str, char **endptrp, const char *fmt)
{
int kelv;
@@ -1061,6 +1199,8 @@
else if (strcmp(fmt, "S,bios_smap_xattr") == 0)
func = S_bios_smap_xattr;
#endif
+ else if (strcmp(fmt, "S,sensor") == 0)
+ func = S_sensor;
else
func = NULL;
if (func) {
Index: share/man/man4/Makefile
===================================================================
--- share/man/man4/Makefile
+++ share/man/man4/Makefile
@@ -230,6 +230,7 @@
ismt.4 \
isp.4 \
ispfw.4 \
+ ${_it.4} \
iwi.4 \
iwifw.4 \
iwm.4 \
@@ -253,6 +254,7 @@
led.4 \
lge.4 \
${_linux.4} \
+ lm.4 \
lm75.4 \
lmc.4 \
lo.4 \
@@ -813,6 +815,7 @@
_if_wpi.4= if_wpi.4
_ipmi.4= ipmi.4
_io.4= io.4
+_it.4= it.4
_linux.4= linux.4
_ndis.4= ndis.4
_nfe.4= nfe.4
Index: share/man/man4/it.4
===================================================================
--- /dev/null
+++ share/man/man4/it.4
@@ -0,0 +1,104 @@
+.\" $FreeBSD$
+.\" $OpenBSD: it.4,v 1.8 2006/09/08 15:09:14 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Julien Bordet <zejames@greygats.org>
+.\" All rights reserved.
+.\"
+.\" 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 AUTHOR ``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 AUTHOR 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.
+.\"
+.Dd 13 September 2007
+.Dt IT 4
+.Os
+.Sh NAME
+.Nm it
+.Nd ITE IT8705F/12F/16F and SiS SiS950 Super I/O Hardware Monitor
+.Sh SYNOPSIS
+.Cd "device isa"
+.Cd "device it"
+.Pp
+In
+.Pa /boot/device.hints :
+.Cd hint.it.0.at="isa"
+.Cd hint.it.0.port="0x290"
+.Cd hint.it.1.at="isa"
+.Cd hint.it.1.port="0xc00"
+.Cd hint.it.2.at="isa"
+.Cd hint.it.2.port="0xd00"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the
+.Tn IT8705F , IT8712F , IT8716F
+and
+.Tn SiS950
+hardware monitors.
+The values are exposed through the
+.Va HW_SENSORS
+.Xr sysctl 3
+interface.
+.Pp
+Most supported devices possess 15 sensors:
+.Bl -column "Sensor" "Units" "Typical" -offset indent
+.It Sy "Sensor" Ta Sy "Units" Ta Sy "Typical Use"
+.It Li "Fan0" Ta "RPM" Ta "CPU Fan"
+.It Li "Fan1" Ta "RPM" Ta "Fan"
+.It Li "Fan2" Ta "RPM" Ta "Fan"
+.It Li "IN0" Ta "uV DC" Ta "Core voltage"
+.It Li "IN1" Ta "uV DC" Ta "Core voltage"
+.It Li "IN2" Ta "uV DC" Ta "+3.3V"
+.It Li "IN3" Ta "uV DC" Ta "+5V"
+.It Li "IN4" Ta "uV DC" Ta "+12V"
+.It Li "IN5" Ta "uV DC" Ta "Unknown"
+.It Li "IN6" Ta "uV DC" Ta "-12V"
+.It Li "IN7" Ta "uV DC" Ta "-5V"
+.It Li "IN8" Ta "uV DC" Ta "VBAT"
+.It Li "Temp" Ta "uK" Ta "Motherboard Temperature"
+.It Li "Temp" Ta "uK" Ta "Motherboard Temperature"
+.It Li "Temp" Ta "uK" Ta "CPU Temperature"
+.El
+.Pp
+For some devices, sensors' names and numbers will be different.
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr sysctl 3 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 3.4 .
+.Fx
+support was added in
+.Fx 7.XXX .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Julien Bordet Aq zejames@greyhats.org .
+It was ported to
+.Fx
+by
+.An Constantine A. Murenin Aq cnst@FreeBSD.org
+as a part of a Google Summer of Code 2007 project.
+.Sh BUGS
+Interrupt support is unimplemented.
Index: share/man/man4/lm.4
===================================================================
--- /dev/null
+++ share/man/man4/lm.4
@@ -0,0 +1,138 @@
+.\" $FreeBSD$
+.\" $OpenBSD: lm.4,v 1.16 2007/05/26 22:38:55 cnst Exp $
+.\" $NetBSD: lm.4,v 1.11 2001/09/22 01:22:49 wiz Exp $
+.\"
+.\" Copyright (c) 2000 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Bill Squier.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" 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.
+.\"
+.Dd 19 August 2007
+.Dt LM 4
+.Os
+.Sh NAME
+.Nm lm
+.Nd NatSemi LM78/79/81 and Winbond Super I/O Hardware Monitor
+.Sh SYNOPSIS
+.Cd "device isa"
+.Cd "device lm"
+.Pp
+In
+.Pa /boot/device.hints :
+.Cd hint.lm.0.at="isa"
+.Cd hint.lm.0.port="0x290"
+.Cd hint.lm.1.at="isa"
+.Cd hint.lm.1.port="0x280"
+.Cd hint.lm.2.at="isa"
+.Cd hint.lm.2.port="0x310"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the
+.Tn National Semiconductor
+LM 78/79/81 and
+.Tn Winbond
+Super I/O
+hardware monitors,
+and registers compatible chips under the
+.Va HW_SENSORS
+.Xr sysctl 3
+tree.
+.Sh HARDWARE
+Chips supported by the
+.Nm
+driver include:
+.Pp
+.Bl -dash -offset indent -compact
+.It
+National Semiconductor LM78 and LM78-J
+.It
+National Semiconductor LM79
+.It
+National Semiconductor LM81
+.It
+Winbond W83627HF, W83627THF, W83637HF and W83697HF
+.It
+Winbond W83627DHG and W83627EHF
+.It
+Winbond W83781D, W83782D and W83783S
+.It
+Winbond W83791D, W83791SD and W83792D
+.It
+ASUS AS99127F
+.El
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr sysctl 3 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Nx 1.5 ;
+.Ox
+support was added in
+.Ox 3.4 ;
+.Fx
+support was added in
+.Fx 7.XXX .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Bill Squier
+and ported to
+.Ox 3.4
+by
+.An Alexander Yurchenko Aq grange@openbsd.org .
+The driver was largely rewritten for
+.Ox 3.9
+by
+.An Mark Kettenis Aq kettenis@openbsd.org .
+The driver was then ported to
+.Fx
+by
+.An Constantine A. Murenin Aq cnst@FreeBSD.org
+as a part of a Google Summer of Code 2007 project.
+.Sh CAVEATS
+Some vendors connect these chips to non-standard thermal diodes and
+resistors.
+This will result in bogus sensor values.
+.Sh BUGS
+Interrupt support is unimplemented.
+.Pp
+There are currently no known pnpbios IDs assigned to LM chips.
+.Pp
+This driver attaches to the Winbond W83791SD chip even though that
+chip does not have any sensors.
Index: share/man/man5/rc.conf.5
===================================================================
--- share/man/man5/rc.conf.5
+++ share/man/man5/rc.conf.5
@@ -309,6 +309,22 @@
these are the flags to pass to the
.Xr powerd 8
daemon.
+.It Va sensorsd_enable
+.Pq Vt bool
+Set to
+.Dq Li NO
+by default.
+Setting this to
+.Dq Li YES
+enables
+.Xr sensorsd 8 ,
+a sensors monitoring and logging daemon.
+.It Va sensorsd_flags
+.Pq Vt str
+Empty by default.
+This variable contains additional flags passed to the
+.Xr sensorsd 8
+program.
.It Va tmpmfs
Controls the creation of a
.Pa /tmp
@@ -4645,6 +4661,7 @@
.Xr rwhod 8 ,
.Xr savecore 8 ,
.Xr sdpd 8 ,
+.Xr sensorsd 8 ,
.Xr sshd 8 ,
.Xr swapon 8 ,
.Xr sysctl 8 ,
Index: share/man/man9/Makefile
===================================================================
--- share/man/man9/Makefile
+++ share/man/man9/Makefile
@@ -268,6 +268,7 @@
securelevel_gt.9 \
selrecord.9 \
sema.9 \
+ sensor_attach.9 \
sf_buf.9 \
sglist.9 \
shm_map.9 \
@@ -1552,6 +1553,13 @@
sema.9 sema_trywait.9 \
sema.9 sema_value.9 \
sema.9 sema_wait.9
+MLINKS+=sensor_attach.9 sensordev_install.9 \
+ sensor_attach.9 sensordev_deinstall.9 \
+ sensor_attach.9 sensor_detach.9 \
+ sensor_attach.9 ksensordev.9 \
+ sensor_attach.9 ksensor.9 \
+ sensor_attach.9 sensor_task_register.9 \
+ sensor_attach.9 sensor_task_unregister.9
MLINKS+=sf_buf.9 sf_buf_alloc.9 \
sf_buf.9 sf_buf_free.9 \
sf_buf.9 sf_buf_kva.9 \
Index: share/man/man9/sensor_attach.9
===================================================================
--- /dev/null
+++ share/man/man9/sensor_attach.9
@@ -0,0 +1,155 @@
+.\" $FreeBSD$
+.\" $OpenBSD: sensor_attach.9,v 1.4 2007/03/22 16:55:31 deraadt Exp $
+.\"
+.\" Copyright (c) 2006 Michael Knudsen <mk@openbsd.org>
+.\" Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
+.\" All rights reserved.
+.\"
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+.\"
+.Dd 19 August 2007
+.Dt SENSOR_ATTACH 9
+.Os
+.Sh NAME
+.Nm sensor_attach ,
+.Nm sensor_detach ,
+.Nm sensordev_install ,
+.Nm sensordev_deinstall ,
+.Nm sensor_task_register ,
+.Nm sensor_task_unregister
+.Nd sensors framework
+.Sh SYNOPSIS
+.In sys/sensors.h
+.Ft void
+.Fn "sensordev_install" "struct ksensordev *sensdev"
+.Ft void
+.Fn "sensordev_deinstall" "struct ksensordev *sensdev"
+.Pp
+.Ft void
+.Fn "sensor_attach" "struct ksensordev *sensdev" "struct ksensor *sens"
+.Ft void
+.Fn "sensor_detach" "struct ksensordev *sensdev" "struct ksensor *sens"
+.Pp
+.Ft int
+.Fn "sensor_task_register" "void *arg" "void (*func)(void *)" "int period"
+.Ft void
+.Fn "sensor_task_unregister" "void *arg"
+.Sh DESCRIPTION
+The
+sensors
+framework API provides a mechanism for manipulation of hardware sensors
+that are available under the
+.Va hw.sensors
+.Xr sysctl 8
+tree.
+.Pp
+.Fn sensor_attach
+adds the sensor specified by the
+.Pa sens
+argument to the sensor device specified by the
+.Pa sensdev
+argument.
+.Fn sensor_detach
+can be used to remove sensors previously added by
+.Fn sensor_attach .
+.Pp
+.Fn sensordev_install
+registers the sensor device specified by the
+.Pa sensdev
+argument so that all sensors that are attached to the device become
+accessible via the sysctl interface.
+.Fn sensordev_deinstall
+can be used to remove sensor devices previously registered by
+.Fn sensordev_install .
+.Pp
+Drivers are responsible for retrieving, interpreting and normalising
+sensor values and updating the sensor struct periodically.
+If the driver needs process context, for example to sleep, it can
+register a task with the sensor framework.
+.Pp
+.Fn sensor_task_register
+is used to register a periodic task to update sensors.
+The
+.Fa func
+argument is a pointer to the function to run with an interval of
+.Fa period
+seconds.
+The
+.Fa arg
+parameter is the argument given to the
+.Fa func
+function.
+The
+.Fn sensor_task_unregister
+removes all tasks previously registered with
+.Fn sensor_task_register
+with an argument of
+.Fa arg .
+.Sh COMPATIBILITY
+.Ss sensor_task
+The
+.Fn sensor_task_register
+and
+.Fn sensor_task_unregister
+functions that are included in
+.Ox 4.2
+and later
+are not compatible with
+.Fx .
+.Fx
+includes an implementation that is similar and compatible
+with an earlier version of
+these
+.Va sensor_task
+functions that was available from
+.Ox 3.9
+until
+.Ox 4.1 .
+.Pp
+Drivers that only call
+.Fn sensor_task_register
+and don't check its return value are not affected by this
+.Va sensor_task
+compatibility notice.
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr sysctl 3 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The sensor framework was written by
+.An Alexander Yurchenko Aq grange@openbsd.org
+and first appeared in
+.Ox 3.4 .
+.An David Gwynne Aq dlg@openbsd.org
+later extended it for
+.Ox 3.8 .
+.An Constantine A. Murenin Aq cnst+openbsd@bugmail.mojo.ru
+extended it even further by introducing the concept of sensor devices in
+.Ox 4.1 .
+.Pp
+The framework was ported to
+.Fx
+by
+.An Constantine A. Murenin Aq cnst@FreeBSD.org
+as a Google Summer of Code 2007 project,
+and first appeared in
+.Fx 7.XXX.
Index: sys/amd64/conf/GENERIC.hints
===================================================================
--- sys/amd64/conf/GENERIC.hints
+++ sys/amd64/conf/GENERIC.hints
@@ -32,3 +32,11 @@
hint.attimer.0.irq="0"
hint.acpi_throttle.0.disabled="1"
hint.p4tcc.0.disabled="1"
+hint.lm.0.at="isa"
+hint.lm.0.port="0x290"
+hint.it.0.at="isa"
+hint.it.0.port="0x290"
+hint.it.1.at="isa"
+hint.it.1.port="0xc00"
+hint.it.2.at="isa"
+hint.it.2.port="0xd00"
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -2173,6 +2173,8 @@
dev/le/lance.c optional le
dev/led/led.c standard
dev/lge/if_lge.c optional lge
+dev/lm/lm78.c optional lm
+dev/lm/lm78_isa.c optional lm isa
dev/lmc/if_lmc.c optional lmc
dev/malo/if_malo.c optional malo
dev/malo/if_malohal.c optional malo
@@ -3618,6 +3620,7 @@
kern/kern_sema.c standard
kern/kern_sendfile.c standard
kern/kern_sharedpage.c standard
+kern/kern_sensors.c standard
kern/kern_shutdown.c standard
kern/kern_sig.c standard
kern/kern_switch.c standard
Index: sys/conf/files.amd64
===================================================================
--- sys/conf/files.amd64
+++ sys/conf/files.amd64
@@ -241,6 +241,7 @@
dev/ipmi/ipmi_ssif.c optional ipmi smbus
dev/ipmi/ipmi_pci.c optional ipmi pci
dev/ipmi/ipmi_linux.c optional ipmi compat_linux32
+dev/it/it.c optional it isa
dev/ixl/if_ixl.c optional ixl pci \
compile-with "${NORMAL_C} -I$S/dev/ixl"
dev/ixl/ixl_pf_main.c optional ixl pci \
Index: sys/conf/files.i386
===================================================================
--- sys/conf/files.i386
+++ sys/conf/files.i386
@@ -286,6 +286,7 @@
dev/ipmi/ipmi_ssif.c optional ipmi smbus
dev/ipmi/ipmi_pci.c optional ipmi pci
dev/ipmi/ipmi_linux.c optional ipmi compat_linux
+dev/it/it.c optional it isa
dev/le/if_le_isa.c optional le isa
dev/mse/mse.c optional mse
dev/mse/mse_isa.c optional mse isa
Index: sys/dev/it/it.c
===================================================================
--- /dev/null
+++ sys/dev/it/it.c
@@ -0,0 +1,403 @@
+/* $FreeBSD$ */
+/* $OpenBSD: it.c,v 1.22 2007/03/22 16:55:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 2003 Julien Bordet <zejames@greyhats.org>
+ * All rights reserved.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <sys/rman.h>
+
+#include <isa/isavar.h>
+#include <sys/systm.h>
+
+#include <sys/sensors.h>
+
+#include <dev/it/itvar.h>
+
+#if defined(ITDEBUG)
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+/*
+ * IT87-compatible chips can typically measure voltages up to 4.096 V.
+ * To measure higher voltages the input is attenuated with (external)
+ * resistors. Negative voltages are measured using a reference
+ * voltage. So we have to convert the sensor values back to real
+ * voltages by applying the appropriate resistor factor.
+ */
+#define RFACT_NONE 10000
+#define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y))
+
+static int it_probe(struct device *);
+static int it_attach(struct device *);
+static int it_detach(struct device *);
+static u_int8_t it_readreg(struct it_softc *, int);
+static void it_writereg(struct it_softc *, int, int);
+static void it_setup_volt(struct it_softc *, int, int);
+static void it_setup_temp(struct it_softc *, int, int);
+static void it_setup_fan(struct it_softc *, int, int);
+
+static void it_generic_stemp(struct it_softc *, struct ksensor *);
+static void it_generic_svolt(struct it_softc *, struct ksensor *);
+static void it_generic_fanrpm(struct it_softc *, struct ksensor *);
+static void it_16bit_fanrpm(struct it_softc *, struct ksensor *);
+
+static void it_refresh_sensor_data(struct it_softc *);
+static void it_refresh(void *);
+
+
+static device_method_t it_methods[] = {
+ /* Methods from the device interface */
+ DEVMETHOD(device_probe, it_probe),
+ DEVMETHOD(device_attach, it_attach),
+ DEVMETHOD(device_detach, it_detach),
+
+ /* Terminate method list */
+ { 0, 0 }
+};
+
+static driver_t it_driver = {
+ "it",
+ it_methods,
+ sizeof (struct it_softc)
+};
+
+static devclass_t it_devclass;
+
+DRIVER_MODULE(it, isa, it_driver, it_devclass, NULL, NULL);
+
+
+static const int it_vrfact[] = {
+ RFACT_NONE,
+ RFACT_NONE,
+ RFACT_NONE,
+ RFACT(68, 100),
+ RFACT(30, 10),
+ RFACT(21, 10),
+ RFACT(83, 20),
+ RFACT(68, 100),
+ RFACT_NONE
+};
+
+static int
+it_probe(struct device *dev)
+{
+ struct resource *iores;
+ int iorid = 0;
+ u_int8_t cr;
+
+ iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (iores == NULL) {
+ DPRINTF(("%s: can't map i/o space\n", __func__));
+ return 1;
+ }
+
+ /* Check Vendor ID */
+ bus_write_1(iores, ITC_ADDR, ITD_CHIPID);
+ cr = bus_read_1(iores, ITC_DATA);
+ bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
+ DPRINTF(("it: vendor id 0x%x\n", cr));
+ if (cr != IT_ID_IT87)
+ return 1;
+
+ return 0;
+}
+
+static int
+it_attach(struct device *dev)
+{
+ struct it_softc *sc = device_get_softc(dev);
+ int fancount;
+ int i;
+ u_int8_t cr;
+
+ sc->sc_dev = dev;
+ sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (sc->sc_iores == NULL) {
+ device_printf(dev, "can't map i/o space\n");
+ return 1;
+ }
+
+ sc->numsensors = IT_NUM_SENSORS;
+
+ cr = it_readreg(sc, ITD_COREID);
+ if (cr >= IT_COREID_12) {
+ /* Force use of 16-bit fan counters. */
+ cr = it_readreg(sc, ITD_FAN_CTL16);
+ it_writereg(sc, ITD_FAN_CTL16, cr | IT_FAN16_MASK);
+ sc->fan16bit = 1;
+ }
+
+ fancount = 5;
+ if (!sc->fan16bit) {
+ fancount -= 2;
+ sc->numsensors -= 2;
+ }
+
+ it_setup_fan(sc, 0, fancount);
+ it_setup_volt(sc, fancount, 9);
+ it_setup_temp(sc, fancount + 9, 3);
+
+ if (sensor_task_register(sc, it_refresh, 5)) {
+ device_printf(sc->sc_dev, "unable to register update task\n");
+ return 1;
+ }
+
+ /* Activate monitoring */
+ cr = it_readreg(sc, ITD_CONFIG);
+ cr |= 0x01 | 0x08;
+ it_writereg(sc, ITD_CONFIG, cr);
+
+ /* Initialize sensors */
+ strlcpy(sc->sensordev.xname, device_get_nameunit(sc->sc_dev),
+ sizeof(sc->sensordev.xname));
+ for (i = 0; i < sc->numsensors; ++i)
+ sensor_attach(&sc->sensordev, &sc->sensors[i]);
+ sensordev_install(&sc->sensordev);
+
+ return 0;
+}
+
+static int
+it_detach(struct device *dev)
+{
+ struct it_softc *sc = device_get_softc(dev);
+ int error;
+
+ sensordev_deinstall(&sc->sensordev);
+ sensor_task_unregister(sc);
+
+ error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid,
+ sc->sc_iores);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static u_int8_t
+it_readreg(struct it_softc *sc, int reg)
+{
+ bus_write_1(sc->sc_iores, ITC_ADDR, reg);
+ return (bus_read_1(sc->sc_iores, ITC_DATA));
+}
+
+static void
+it_writereg(struct it_softc *sc, int reg, int val)
+{
+ bus_write_1(sc->sc_iores, ITC_ADDR, reg);
+ bus_write_1(sc->sc_iores, ITC_DATA, val);
+}
+
+static void
+it_setup_volt(struct it_softc *sc, int start, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ sc->sensors[start + i].type = SENSOR_VOLTS_DC;
+ }
+
+ snprintf(sc->sensors[start + 0].desc, sizeof(sc->sensors[0].desc),
+ "VCORE_A");
+ snprintf(sc->sensors[start + 1].desc, sizeof(sc->sensors[1].desc),
+ "VCORE_B");
+ snprintf(sc->sensors[start + 2].desc, sizeof(sc->sensors[2].desc),
+ "+3.3V");
+ snprintf(sc->sensors[start + 3].desc, sizeof(sc->sensors[3].desc),
+ "+5V");
+ snprintf(sc->sensors[start + 4].desc, sizeof(sc->sensors[4].desc),
+ "+12V");
+ snprintf(sc->sensors[start + 5].desc, sizeof(sc->sensors[5].desc),
+ "Unused");
+ snprintf(sc->sensors[start + 6].desc, sizeof(sc->sensors[6].desc),
+ "-12V");
+ snprintf(sc->sensors[start + 7].desc, sizeof(sc->sensors[7].desc),
+ "+5VSB");
+ snprintf(sc->sensors[start + 8].desc, sizeof(sc->sensors[8].desc),
+ "VBAT");
+}
+
+static void
+it_setup_temp(struct it_softc *sc, int start, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ sc->sensors[start + i].type = SENSOR_TEMP;
+}
+
+static void
+it_setup_fan(struct it_softc *sc, int start, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ sc->sensors[start + i].type = SENSOR_FANRPM;
+}
+
+static void
+it_generic_stemp(struct it_softc *sc, struct ksensor *sensors)
+{
+ int i, sdata;
+
+ sdata = it_readreg(sc, ITD_TEMPENABLE);
+ for (i = 0; i < 3; i++) {
+ /* TMPIN can be enabled either in resistor or diode mode. */
+ if ((sdata & (1 << i)) != 0 ||
+ (sdata & (1 << (i + 3))) != 0)
+ sensors[i].flags &= ~SENSOR_FINVALID;
+ else
+ sensors[i].flags |= SENSOR_FINVALID;
+ }
+ for (i = 0; i < 3; i++) {
+ if ((sensors[i].flags & SENSOR_FINVALID) != 0)
+ continue;
+ sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i);
+ /* Convert temperature to micro-Kelvins. */
+ sensors[i].value = sdata * 1000000 + 273150000;
+ }
+}
+
+static void
+it_generic_svolt(struct it_softc *sc, struct ksensor *sensors)
+{
+ int i, sdata;
+
+ for (i = 0; i < 9; i++) {
+ sdata = it_readreg(sc, ITD_SENSORVOLTBASE + i);
+ DPRINTF(("sdata[volt%d] 0x%x\n", i, sdata));
+ /* each step is 16mV */
+ sensors[i].value = sdata * 16;
+ /* these two values are negative and formula is different */
+ if (i == 5 || i == 6)
+ sensors[i].value -= IT_VREF;
+ /* rfact is (factor * 10^4) */
+ sensors[i].value *= it_vrfact[i];
+ /* division by 10 gets us back to uVDC */
+ sensors[i].value /= 10;
+ if (i == 5 || i == 6)
+ sensors[i].value += IT_VREF * 1000;
+ }
+}
+
+static void
+it_generic_fanrpm(struct it_softc *sc, struct ksensor *sensors)
+{
+ int i, sdata, divisor, odivisor, ndivisor;
+
+ odivisor = ndivisor = divisor = it_readreg(sc, ITD_FAN);
+ for (i = 0; i < 3; i++, divisor >>= 3) {
+ sensors[i].flags &= ~SENSOR_FINVALID;
+ if ((sdata = it_readreg(sc, ITD_SENSORFANBASE + i)) == 0xff) {
+ sensors[i].flags |= SENSOR_FINVALID;
+ if (i == 2)
+ ndivisor ^= 0x40;
+ else {
+ ndivisor &= ~(7 << (i * 3));
+ ndivisor |= ((divisor + 1) & 7) << (i * 3);
+ }
+ } else if (sdata == 0) {
+ sensors[i].value = 0;
+ } else {
+ if (i == 2)
+ divisor = divisor & 1 ? 3 : 1;
+ sensors[i].value = 1350000 / (sdata << (divisor & 7));
+ }
+ }
+ if (ndivisor != odivisor)
+ it_writereg(sc, ITD_FAN, ndivisor);
+}
+
+/* Chips with 0x12 core support 16-bit fan counter with fixed divisor of 2. */
+static void
+it_16bit_fanrpm(struct it_softc *sc, struct ksensor *sensors)
+{
+ unsigned int sdata;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ sdata = it_readreg(sc, ITD_SENSORFANBASE_EXT + i);
+ sdata <<= 8;
+ sdata |= it_readreg(sc, ITD_SENSORFANBASE + i);
+ if (sdata != 0xffff && sdata != 0) {
+ sensors[i].flags &= ~SENSOR_FINVALID;
+ sensors[i].value = (1350000 / 2) / sdata;
+ }
+ else
+ sensors[i].flags |= SENSOR_FINVALID;
+ }
+ for (i = 0; i < 2; i++) {
+ sdata = it_readreg(sc, ITD_SENSORFANBASE2 + 2 * i + 1);
+ sdata <<= 8;
+ sdata |= it_readreg(sc, ITD_SENSORFANBASE2 + 2 * i);
+ if (sdata != 0xffff && sdata != 0) {
+ sensors[3 + i].flags &= ~SENSOR_FINVALID;
+ sensors[3 + i].value = (1350000 / 2) / sdata;
+ }
+ else
+ sensors[3 + i].flags |= SENSOR_FINVALID;
+ }
+}
+
+/*
+ * pre: last read occurred >= 1.5 seconds ago
+ * post: sensors[] current data are the latest from the chip
+ */
+static void
+it_refresh_sensor_data(struct it_softc *sc)
+{
+ /* Refresh our stored data for every sensor */
+ if (sc->fan16bit) {
+ it_16bit_fanrpm(sc, &sc->sensors[0]);
+ it_generic_svolt(sc, &sc->sensors[5]);
+ it_generic_stemp(sc, &sc->sensors[14]);
+ } else {
+ it_generic_fanrpm(sc, &sc->sensors[0]);
+ it_generic_svolt(sc, &sc->sensors[3]);
+ it_generic_stemp(sc, &sc->sensors[12]);
+ }
+}
+
+static void
+it_refresh(void *arg)
+{
+ struct it_softc *sc = (struct it_softc *)arg;
+
+ it_refresh_sensor_data(sc);
+}
Index: sys/dev/it/itvar.h
===================================================================
--- /dev/null
+++ sys/dev/it/itvar.h
@@ -0,0 +1,96 @@
+/* $FreeBSD$ */
+/* $OpenBSD: itvar.h,v 1.4 2007/03/22 16:55:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 2003 Julien Bordet <zejames@greyhats.org>
+ * All rights reserved.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _DEV_ISA_ITVAR_H
+#define _DEV_ISA_ITVAR_H
+
+#define IT_NUM_SENSORS 17
+
+/* chip ids */
+#define IT_ID_IT87 0x90
+#define IT_COREID_12 0x12
+
+/* ctl registers */
+
+#define ITC_ADDR 0x05
+#define ITC_DATA 0x06
+
+/* data registers */
+
+#define ITD_CONFIG 0x00
+#define ITD_ISR1 0x01
+#define ITD_ISR2 0x02
+#define ITD_ISR3 0x03
+#define ITD_SMI1 0x04
+#define ITD_SMI2 0x05
+#define ITD_SMI3 0x06
+#define ITD_IMR1 0x07
+#define ITD_IMR2 0x08
+#define ITD_IMR3 0x09
+#define ITD_VID 0x0a
+#define ITD_FAN 0x0b
+#define ITD_FAN_CTL16 0x0c
+#define IT_FAN16_MASK (1 | 2 | 4)
+
+#define ITD_FANMINBASE 0x10
+#define ITD_FANENABLE 0x13
+
+#define ITD_SENSORFANBASE 0x0d /* Fan from 0x0d to 0x0f */
+#define ITD_SENSORFANBASE_EXT 0x18 /* Extended fan (upper 8 bits) from 0x18 to 0x1a */
+#define ITD_SENSORFANBASE2 0x80 /* 16-bit: 81:80, 83:82 */
+#define ITD_SENSORVOLTBASE 0x20 /* VIN from 0x20 to 0x28 */
+#define ITD_SENSORTEMPBASE 0x29 /* Temperature from 0x29 to 0x2b */
+
+#define ITD_VOLTMAXBASE 0x30
+#define ITD_VOLTMINBASE 0x31
+
+#define ITD_TEMPMAXBASE 0x40
+#define ITD_TEMPMINBASE 0x41
+
+#define ITD_SBUSADDR 0x48
+#define ITD_VOLTENABLE 0x50
+#define ITD_TEMPENABLE 0x51
+
+#define ITD_CHIPID 0x58
+#define ITD_COREID 0x5B
+
+#define IT_VREF (4096) /* Vref = 4.096 V */
+
+struct it_softc {
+ struct device *sc_dev;
+
+ struct resource *sc_iores;
+ int sc_iorid;
+
+ int fan16bit;
+ u_int numsensors;
+ struct ksensor sensors[IT_NUM_SENSORS];
+ struct ksensordev sensordev;
+};
+
+#endif
Index: sys/dev/lm/lm78.c
===================================================================
--- /dev/null
+++ sys/dev/lm/lm78.c
@@ -0,0 +1,1063 @@
+/* $FreeBSD$ */
+/* $OpenBSD: lm78.c,v 1.23 2011/12/06 16:06:07 mpf Exp $ */
+
+/*-
+ * Copyright (c) 2005, 2006 Mark Kettenis
+ * Copyright (c) 2006, 2007 Constantine A. Murenin
+ *
+ * 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/sensors.h>
+#include <machine/bus.h>
+
+#include "lm78var.h"
+#include "wbsioreg.h"
+
+#if defined(LMDEBUG)
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+/*
+ * LM78-compatible chips can typically measure voltages up to 4.096 V.
+ * To measure higher voltages the input is attenuated with (external)
+ * resistors. Negative voltages are measured using inverting op amps
+ * and resistors. So we have to convert the sensor values back to
+ * real voltages by applying the appropriate resistor factor.
+ */
+#define RFACT_NONE 10000
+#define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y))
+#define NRFACT(x, y) (-RFACT_NONE * (x) / (y))
+
+int lm_match(struct lm_softc *);
+int wb_match(struct lm_softc *);
+int def_match(struct lm_softc *);
+
+void lm_setup_sensors(struct lm_softc *, const struct lm_sensor *);
+void lm_refresh(void *);
+
+void lm_refresh_sensor_data(struct lm_softc *);
+void lm_refresh_volt(struct lm_softc *, int);
+void lm_refresh_temp(struct lm_softc *, int);
+void lm_refresh_fanrpm(struct lm_softc *, int);
+
+void wb_refresh_sensor_data(struct lm_softc *);
+void wb_w83637hf_refresh_vcore(struct lm_softc *, int);
+void wb_refresh_nvolt(struct lm_softc *, int);
+void wb_w83627ehf_refresh_nvolt(struct lm_softc *, int);
+void wb_refresh_temp(struct lm_softc *, int);
+void wb_refresh_fanrpm(struct lm_softc *, int);
+void wb_nct6776f_refresh_fanrpm(struct lm_softc *, int);
+void wb_nct6779d_refresh_fanrpm(struct lm_softc *, int);
+void wb_w83792d_refresh_fanrpm(struct lm_softc *, int);
+
+void as_refresh_temp(struct lm_softc *, int);
+
+struct lm_chip {
+ int (*chip_match)(struct lm_softc *);
+};
+
+static const struct lm_chip lm_chips[] = {
+ { wb_match },
+ { lm_match },
+ { def_match } /* Must be last */
+};
+
+static const struct lm_sensor lm78_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(68, 100) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(30, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, NRFACT(240, 60) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, NRFACT(100, 60) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, lm_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor w83627hf_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+/*
+ * The W83627EHF can measure voltages up to 2.048 V instead of the
+ * traditional 4.096 V. For measuring positive voltages, this can be
+ * accounted for by halving the resistor factor. Negative voltages
+ * need special treatment, also because the reference voltage is 2.048 V
+ * instead of the traditional 3.6 V.
+ */
+static const struct lm_sensor w83627ehf_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE / 2},
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(56, 10) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_w83627ehf_refresh_nvolt },
+ { "", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, RFACT_NONE / 2 },
+ { "", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT_NONE / 2 },
+ { "3.3VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE / 2 },
+ { "", SENSOR_VOLTS_DC, 5, 0x52, lm_refresh_volt, RFACT_NONE / 2 },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+/*
+ * w83627dhg is almost identical to w83627ehf, except that
+ * it has 9 instead of 10 voltage sensors
+ */
+static const struct lm_sensor w83627dhg_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE / 2},
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(56, 10) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_w83627ehf_refresh_nvolt },
+ { "", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, RFACT_NONE / 2 },
+ { "", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT_NONE / 2 },
+ { "3.3VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE / 2 },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor nct6776f_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE / 2},
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(56, 10) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_w83627ehf_refresh_nvolt },
+ { "", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, RFACT_NONE / 2 },
+ { "", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT_NONE / 2 },
+ { "3.3VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 34) / 2 },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE / 2 },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 6, 0x56, wb_nct6776f_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 6, 0x58, wb_nct6776f_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 6, 0x5a, wb_nct6776f_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 6, 0x5c, wb_nct6776f_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 6, 0x5e, wb_nct6776f_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor nct6779d_sensors[] = {
+ /*
+ * 0:0x27 - SMIOVT1 temperature source reading (6:0x21).
+ * 0:0x73 - SYSFANOUT fan control temperature reading.
+ * 0:0x75 - CPUFANOUT fan control temperature reading.
+ * 1:0x51 - SMIOVT2 temperature source reading (6:0x22).
+ * 4:0x90 - SYSTIN temperature reading.
+ * 4:0x91 - CPUTIN temperature reading.
+ * 4:0x92 - AUXTIN0 temperature reading.
+ * 4:0x93 - AUXTIN1 temperature reading.
+ * 4:0x94 - AUXTIN2 temperature reading.
+ * 4:0x95 - AUXTIN3 temperature reading.
+ * 4:0xB0 - 13-bit SYSFANIN Fan Count (validity can be determined).
+ * 4:0xB2 - 13-bit CPUFANIN Fan Count (validity can be determined).
+ * 4:0xC0 - SYSFANIN Speed.
+ * 4:0xC2 - CPUFANIN Speed.
+ * 6:0x49-0xff - reserved, but 0x56-0x5e mirror 4:0xC0-...
+ * 7:0x20 - PECI Temperature Reading Register.
+ */
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 4, 0x80, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN1]", SENSOR_VOLTS_DC, 4, 0x81, lm_refresh_volt, RFACT_NONE / 2 },
+ { "AVCC", SENSOR_VOLTS_DC, 4, 0x82, lm_refresh_volt, RFACT_NONE },
+ { "3VCC", SENSOR_VOLTS_DC, 4, 0x83, lm_refresh_volt, RFACT_NONE },
+ { "[VIN0]", SENSOR_VOLTS_DC, 4, 0x84, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN8]", SENSOR_VOLTS_DC, 4, 0x85, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN4]", SENSOR_VOLTS_DC, 4, 0x86, lm_refresh_volt, RFACT_NONE / 2 },
+ { "3VSB", SENSOR_VOLTS_DC, 4, 0x87, lm_refresh_volt, RFACT_NONE },
+ { "VBAT", SENSOR_VOLTS_DC, 4, 0x88, lm_refresh_volt, RFACT_NONE },
+
+ { "VTT", SENSOR_VOLTS_DC, 4, 0x89, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN5]", SENSOR_VOLTS_DC, 4, 0x8a, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN6]", SENSOR_VOLTS_DC, 4, 0x8b, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN2]", SENSOR_VOLTS_DC, 4, 0x8c, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN3]", SENSOR_VOLTS_DC, 4, 0x8d, lm_refresh_volt, RFACT_NONE / 2 },
+ { "[VIN7]", SENSOR_VOLTS_DC, 4, 0x8e, lm_refresh_volt, RFACT_NONE / 2 },
+
+ /* Temperature */
+ { "System", SENSOR_TEMP, 4, 0x90, lm_refresh_temp },
+ { "CPU", SENSOR_TEMP, 4, 0x91, lm_refresh_temp },
+ { "System Fan Control", SENSOR_TEMP, 0, 0x73, wb_refresh_temp },
+ { "CPU Fan Control", SENSOR_TEMP, 0, 0x75, wb_refresh_temp },
+
+ /* Fans */
+ /* both sets seem to work fine */
+#if 1
+ { "System", SENSOR_FANRPM, 4, 0xc0, wb_nct6776f_refresh_fanrpm },
+ { "CPU", SENSOR_FANRPM, 4, 0xc2, wb_nct6776f_refresh_fanrpm },
+ { "Aux0", SENSOR_FANRPM, 4, 0xc4, wb_nct6776f_refresh_fanrpm },
+ { "Aux1", SENSOR_FANRPM, 4, 0xc6, wb_nct6776f_refresh_fanrpm },
+ { "Aux2", SENSOR_FANRPM, 4, 0xc8, wb_nct6776f_refresh_fanrpm },
+#else
+ { "System", SENSOR_FANRPM, 4, 0xb0, wb_nct6779d_refresh_fanrpm },
+ { "CPU", SENSOR_FANRPM, 4, 0xb2, wb_nct6779d_refresh_fanrpm },
+ { "Aux0", SENSOR_FANRPM, 4, 0xb4, wb_nct6779d_refresh_fanrpm },
+ { "Aux1", SENSOR_FANRPM, 4, 0xb6, wb_nct6779d_refresh_fanrpm },
+ { "Aux2", SENSOR_FANRPM, 4, 0xb8, wb_nct6779d_refresh_fanrpm },
+#endif
+
+ { NULL }
+};
+
+static const struct lm_sensor w83637hf_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, wb_w83637hf_refresh_vcore },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT(28, 10) },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 51) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x24, wb_refresh_nvolt, RFACT(232, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(34, 51) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor w83697hf_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+/*
+ * The datasheet doesn't mention the (internal) resistors used for the
+ * +5V, but using the values from the W83782D datasheets seems to
+ * provide sensible results.
+ */
+static const struct lm_sensor w83781d_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, lm_refresh_volt, NRFACT(2100, 604) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, NRFACT(909, 604) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, lm_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor w83782d_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VINR0", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 5, 0x50, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 5, 0x51, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor w83783s_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor w83791d_sensors[] = {
+ /* Voltage */
+ { "VCore", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VINR0", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+ { "5VSB", SENSOR_VOLTS_DC, 0, 0xb0, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 0, 0xb1, lm_refresh_volt, RFACT_NONE },
+ { "VINR1", SENSOR_VOLTS_DC, 0, 0xb2, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc0, wb_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc8, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xba, wb_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xbb, wb_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor w83792d_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x23, wb_refresh_nvolt, RFACT(120, 56) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x26, lm_refresh_volt, RFACT(34, 50) },
+ { "5VSB", SENSOR_VOLTS_DC, 0, 0xb0, lm_refresh_volt, RFACT(17, 33) },
+ { "VBAT", SENSOR_VOLTS_DC, 0, 0xb1, lm_refresh_volt, RFACT_NONE },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc0, wb_refresh_temp },
+ { "", SENSOR_TEMP, 0, 0xc8, wb_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xb8, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xb9, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xba, wb_w83792d_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0xbe, wb_w83792d_refresh_fanrpm },
+
+ { NULL }
+};
+
+static const struct lm_sensor as99127f_sensors[] = {
+ /* Voltage */
+ { "VCore A", SENSOR_VOLTS_DC, 0, 0x20, lm_refresh_volt, RFACT_NONE },
+ { "VCore B", SENSOR_VOLTS_DC, 0, 0x21, lm_refresh_volt, RFACT_NONE },
+ { "+3.3V", SENSOR_VOLTS_DC, 0, 0x22, lm_refresh_volt, RFACT_NONE },
+ { "+5V", SENSOR_VOLTS_DC, 0, 0x23, lm_refresh_volt, RFACT(34, 50) },
+ { "+12V", SENSOR_VOLTS_DC, 0, 0x24, lm_refresh_volt, RFACT(28, 10) },
+ { "-12V", SENSOR_VOLTS_DC, 0, 0x25, wb_refresh_nvolt, RFACT(232, 56) },
+ { "-5V", SENSOR_VOLTS_DC, 0, 0x26, wb_refresh_nvolt, RFACT(120, 56) },
+
+ /* Temperature */
+ { "", SENSOR_TEMP, 0, 0x27, lm_refresh_temp },
+ { "", SENSOR_TEMP, 1, 0x50, as_refresh_temp },
+ { "", SENSOR_TEMP, 2, 0x50, as_refresh_temp },
+
+ /* Fans */
+ { "", SENSOR_FANRPM, 0, 0x28, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x29, lm_refresh_fanrpm },
+ { "", SENSOR_FANRPM, 0, 0x2a, lm_refresh_fanrpm },
+
+ { NULL }
+};
+
+void
+lm_probe(struct lm_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sizeof(lm_chips) / sizeof(lm_chips[0]); i++)
+ if (lm_chips[i].chip_match(sc))
+ break;
+}
+
+void
+lm_attach(struct lm_softc *sc)
+{
+ u_int i, config;
+
+ /* No point in doing anything if we don't have any sensors. */
+ if (sc->numsensors == 0)
+ return;
+
+ if (sensor_task_register(sc, lm_refresh, 5)) {
+ device_printf(sc->sc_dev, "unable to register update task\n");
+ return;
+ }
+
+ /* Start the monitoring loop */
+ config = sc->lm_readreg(sc, LM_CONFIG);
+ sc->lm_writereg(sc, LM_CONFIG, config | 0x01);
+
+ /* Add sensors */
+ strlcpy(sc->sensordev.xname, device_get_nameunit(sc->sc_dev),
+ sizeof(sc->sensordev.xname));
+ for (i = 0; i < sc->numsensors; ++i)
+ sensor_attach(&sc->sensordev, &sc->sensors[i]);
+ sensordev_install(&sc->sensordev);
+}
+
+int
+lm_detach(struct lm_softc *sc)
+{
+ int i;
+
+ /* Remove sensors */
+ sensordev_deinstall(&sc->sensordev);
+ for (i = 0; i < sc->numsensors; i++)
+ sensor_detach(&sc->sensordev, &sc->sensors[i]);
+
+ sensor_task_unregister(sc);
+ if (sc->numsensors != 0)
+ free(sc->sensors, M_DEVBUF);
+
+ return 0;
+}
+
+int
+lm_match(struct lm_softc *sc)
+{
+ int chipid;
+ const char *cdesc;
+ char fulldesc[64];
+
+ /* See if we have an LM78 or LM79. */
+ chipid = sc->lm_readreg(sc, LM_CHIPID) & LM_CHIPID_MASK;
+ switch(chipid) {
+ case LM_CHIPID_LM78:
+ cdesc = "LM78";
+ break;
+ case LM_CHIPID_LM78J:
+ cdesc = "LM78J";
+ break;
+ case LM_CHIPID_LM79:
+ cdesc = "LM79";
+ break;
+ case LM_CHIPID_LM81:
+ cdesc = "LM81";
+ break;
+ default:
+ return 0;
+ }
+ snprintf(fulldesc, sizeof(fulldesc),
+ "National Semiconductor %s Hardware Monitor", cdesc);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+
+ lm_setup_sensors(sc, lm78_sensors);
+ sc->refresh_sensor_data = lm_refresh_sensor_data;
+ return 1;
+}
+
+int
+def_match(struct lm_softc *sc)
+{
+ int chipid;
+ char fulldesc[64];
+
+ chipid = sc->lm_readreg(sc, LM_CHIPID) & LM_CHIPID_MASK;
+ snprintf(fulldesc, sizeof(fulldesc),
+ "unknown Hardware Monitor (ID 0x%x)", chipid);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+
+ lm_setup_sensors(sc, lm78_sensors);
+ sc->refresh_sensor_data = lm_refresh_sensor_data;
+ return 1;
+}
+
+int
+wb_match(struct lm_softc *sc)
+{
+ int banksel, vendid, devid;
+ const char *cdesc;
+ char desc[64];
+ char fulldesc[64];
+
+ /* Read vendor ID */
+ banksel = sc->lm_readreg(sc, WB_BANKSEL);
+ sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_HBAC);
+ vendid = sc->lm_readreg(sc, WB_VENDID) << 8;
+ sc->lm_writereg(sc, WB_BANKSEL, 0);
+ vendid |= sc->lm_readreg(sc, WB_VENDID);
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+ DPRINTF((" winbond vend id 0x%x\n", vendid));
+ if (vendid != WB_VENDID_WINBOND && vendid != WB_VENDID_ASUS)
+ return 0;
+
+ /* Read device/chip ID */
+ sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+ devid = sc->lm_readreg(sc, LM_CHIPID);
+ sc->chipid = sc->lm_readreg(sc, WB_BANK0_CHIPID);
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+ DPRINTF((" winbond chip id 0x%x\n", sc->chipid));
+ switch(sc->chipid) {
+ case WB_CHIPID_W83627HF:
+ cdesc = "W83627HF";
+ lm_setup_sensors(sc, w83627hf_sensors);
+ break;
+ case WB_CHIPID_W83627THF:
+ cdesc = "W83627THF";
+ lm_setup_sensors(sc, w83637hf_sensors);
+ break;
+ case WB_CHIPID_W83627EHF_A:
+ cdesc = "W83627EHF-A";
+ lm_setup_sensors(sc, w83627ehf_sensors);
+ break;
+ case WB_CHIPID_W83627EHF:
+ cdesc = "W83627EHF";
+ lm_setup_sensors(sc, w83627ehf_sensors);
+ break;
+ case WB_CHIPID_W83627DHG:
+ if (sc->sioid == WBSIO_ID_NCT6779D || 1) {
+ cdesc = "NCT6779D";
+ lm_setup_sensors(sc, nct6779d_sensors);
+ } else if (sc->sioid == WBSIO_ID_NCT6776F) {
+ cdesc = "NCT6776F";
+ lm_setup_sensors(sc, nct6776f_sensors);
+ } else {
+ cdesc = "W83627DHG";
+ lm_setup_sensors(sc, w83627dhg_sensors);
+ }
+ break;
+ case WB_CHIPID_W83637HF:
+ cdesc = "W83637HF";
+ sc->lm_writereg(sc, WB_BANKSEL, WB_BANKSEL_B0);
+ if (sc->lm_readreg(sc, WB_BANK0_CONFIG) & WB_CONFIG_VMR9)
+ sc->vrm9 = 1;
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+ lm_setup_sensors(sc, w83637hf_sensors);
+ break;
+ case WB_CHIPID_W83697HF:
+ cdesc = "W83697HF";
+ lm_setup_sensors(sc, w83697hf_sensors);
+ break;
+ case WB_CHIPID_W83781D:
+ case WB_CHIPID_W83781D_2:
+ cdesc = "W83781D";
+ lm_setup_sensors(sc, w83781d_sensors);
+ break;
+ case WB_CHIPID_W83782D:
+ cdesc = "W83782D";
+ lm_setup_sensors(sc, w83782d_sensors);
+ break;
+ case WB_CHIPID_W83783S:
+ cdesc = "W83783S";
+ lm_setup_sensors(sc, w83783s_sensors);
+ break;
+ case WB_CHIPID_W83791D:
+ cdesc = "W83791D";
+ lm_setup_sensors(sc, w83791d_sensors);
+ break;
+ case WB_CHIPID_W83791SD:
+ cdesc = "W83791SD";
+ break;
+ case WB_CHIPID_W83792D:
+ if (devid >= 0x10 && devid <= 0x29)
+ snprintf(desc, sizeof(desc),
+ "W83792D rev %c", 'A' + devid - 0x10);
+ else
+ snprintf(desc, sizeof(desc),
+ "W83792D rev 0x%x", devid);
+ cdesc = desc;
+ lm_setup_sensors(sc, w83792d_sensors);
+ break;
+ case WB_CHIPID_AS99127F:
+ if (vendid == WB_VENDID_ASUS) {
+ cdesc = "AS99127F";
+ lm_setup_sensors(sc, w83781d_sensors);
+ } else {
+ cdesc = "AS99127F rev 2";
+ lm_setup_sensors(sc, as99127f_sensors);
+ }
+ break;
+ default:
+ snprintf(fulldesc, sizeof(fulldesc),
+ "unknown Winbond Hardware Monitor (Chip ID 0x%x)",
+ sc->chipid);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+ /* Handle as a standard LM78. */
+ lm_setup_sensors(sc, lm78_sensors);
+ sc->refresh_sensor_data = lm_refresh_sensor_data;
+ return 1;
+ }
+
+ if (cdesc[0] == 'W')
+ snprintf(fulldesc, sizeof(fulldesc),
+ "Winbond %s Hardware Monitor", cdesc);
+ else
+ snprintf(fulldesc, sizeof(fulldesc),
+ "ASUS %s Hardware Monitor", cdesc);
+ device_set_desc_copy(sc->sc_dev, fulldesc);
+
+ sc->refresh_sensor_data = wb_refresh_sensor_data;
+ return 1;
+}
+
+void
+lm_setup_sensors(struct lm_softc *sc, const struct lm_sensor *sensors)
+{
+ int i;
+
+ for (i = 0; sensors[i].desc; i++)
+ sc->numsensors++;
+ if (sc->numsensors == 0)
+ return;
+
+ sc->sensors = malloc(sizeof(sc->sensors[0]) * sc->numsensors,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ for (i = 0; i < sc->numsensors; i++) {
+ sc->sensors[i].type = sensors[i].type;
+ strlcpy(sc->sensors[i].desc, sensors[i].desc,
+ sizeof(sc->sensors[i].desc));
+ }
+ sc->lm_sensors = sensors;
+}
+
+void
+lm_refresh(void *arg)
+{
+ struct lm_softc *sc = arg;
+
+ sc->refresh_sensor_data(sc);
+}
+
+void
+lm_refresh_sensor_data(struct lm_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->numsensors; i++)
+ sc->lm_sensors[i].refresh(sc, i);
+}
+
+void
+lm_refresh_volt(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ sensor->value = (data << 4);
+ sensor->value *= sc->lm_sensors[n].rfact;
+ sensor->value /= 10;
+}
+
+void
+lm_refresh_temp(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int sdata;
+
+ /*
+ * The data sheet suggests that the range of the temperature
+ * sensor is between -55 degC and +125 degC.
+ */
+ sdata = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (sdata > 0x7d && sdata < 0xc9) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (sdata & 0x80)
+ sdata -= 0x100;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = sdata * 1000000 + 273150000;
+ }
+}
+
+void
+lm_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data, divisor = 1;
+
+ /*
+ * We might get more accurate fan readings by adjusting the
+ * divisor, but that might interfere with APM or other SMM
+ * BIOS code reading the fan speeds.
+ */
+
+ /* FAN3 has a fixed fan divisor. */
+ if (sc->lm_sensors[n].reg == LM_FAN1 ||
+ sc->lm_sensors[n].reg == LM_FAN2) {
+ data = sc->lm_readreg(sc, LM_VIDFAN);
+ if (sc->lm_sensors[n].reg == LM_FAN1)
+ divisor = (data >> 4) & 0x03;
+ else
+ divisor = (data >> 6) & 0x03;
+ }
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (data == 0xff || data == 0x00) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = 1350000 / (data << divisor);
+ }
+}
+
+void
+wb_refresh_sensor_data(struct lm_softc *sc)
+{
+ int banksel, bank, i;
+
+ /*
+ * Properly save and restore bank selection register.
+ */
+
+ banksel = bank = sc->lm_readreg(sc, WB_BANKSEL);
+ for (i = 0; i < sc->numsensors; i++) {
+ if (bank != sc->lm_sensors[i].bank) {
+ bank = sc->lm_sensors[i].bank;
+ sc->lm_writereg(sc, WB_BANKSEL, bank);
+ }
+ sc->lm_sensors[i].refresh(sc, i);
+ }
+ sc->lm_writereg(sc, WB_BANKSEL, banksel);
+}
+
+void
+wb_w83637hf_refresh_vcore(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+
+ /*
+ * Depending on the voltage detection method,
+ * one of the following formulas is used:
+ * VRM8 method: value = raw * 0.016V
+ * VRM9 method: value = raw * 0.00488V + 0.70V
+ */
+ if (sc->vrm9)
+ sensor->value = (data * 4880) + 700000;
+ else
+ sensor->value = (data * 16000);
+}
+
+void
+wb_refresh_nvolt(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ sensor->value = ((data << 4) - WB_VREF);
+ sensor->value *= sc->lm_sensors[n].rfact;
+ sensor->value /= 10;
+ sensor->value += WB_VREF * 1000;
+}
+
+void
+wb_w83627ehf_refresh_nvolt(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int data;
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ sensor->value = ((data << 3) - WB_W83627EHF_VREF);
+ sensor->value *= RFACT(232, 10);
+ sensor->value /= 10;
+ sensor->value += WB_W83627EHF_VREF * 1000;
+}
+
+void
+wb_refresh_temp(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int sdata;
+
+ /*
+ * The data sheet suggests that the range of the temperature
+ * sensor is between -55 degC and +125 degC. However, values
+ * around -48 degC seem to be a very common bogus values.
+ * Since such values are unreasonably low, we use -45 degC for
+ * the lower limit instead.
+ */
+ sdata = sc->lm_readreg(sc, sc->lm_sensors[n].reg) << 1;
+ sdata += sc->lm_readreg(sc, sc->lm_sensors[n].reg + 1) >> 7;
+ if (sdata > 0x0fa && sdata < 0x1a6) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (sdata & 0x100)
+ sdata -= 0x200;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = sdata * 500000 + 273150000;
+ }
+}
+
+void
+wb_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int fan, data, divisor = 0;
+
+ /*
+ * This is madness; the fan divisor bits are scattered all
+ * over the place.
+ */
+
+ if (sc->lm_sensors[n].reg == LM_FAN1 ||
+ sc->lm_sensors[n].reg == LM_FAN2 ||
+ sc->lm_sensors[n].reg == LM_FAN3) {
+ data = sc->lm_readreg(sc, WB_BANK0_VBAT);
+ fan = (sc->lm_sensors[n].reg - LM_FAN1);
+ if ((data >> 5) & (1 << fan))
+ divisor |= 0x04;
+ }
+
+ if (sc->lm_sensors[n].reg == LM_FAN1 ||
+ sc->lm_sensors[n].reg == LM_FAN2) {
+ data = sc->lm_readreg(sc, LM_VIDFAN);
+ if (sc->lm_sensors[n].reg == LM_FAN1)
+ divisor |= (data >> 4) & 0x03;
+ else
+ divisor |= (data >> 6) & 0x03;
+ } else if (sc->lm_sensors[n].reg == LM_FAN3) {
+ data = sc->lm_readreg(sc, WB_PIN);
+ divisor |= (data >> 6) & 0x03;
+ } else if (sc->lm_sensors[n].reg == WB_BANK0_FAN4 ||
+ sc->lm_sensors[n].reg == WB_BANK0_FAN5) {
+ data = sc->lm_readreg(sc, WB_BANK0_FAN45);
+ if (sc->lm_sensors[n].reg == WB_BANK0_FAN4)
+ divisor |= (data >> 0) & 0x07;
+ else
+ divisor |= (data >> 4) & 0x07;
+ }
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (data == 0xff || data == 0x00) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = 1350000 / (data << divisor);
+ }
+}
+
+void
+wb_nct6776f_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int datah, datal;
+
+ datah = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ datal = sc->lm_readreg(sc, sc->lm_sensors[n].reg + 1);
+
+ if (datah == 0xff || (datah == 0 && datal == 0)) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = (datah << 8) | datal;
+ }
+}
+
+void
+wb_nct6779d_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ u_int datah, datal;
+
+ datah = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ datal = sc->lm_readreg(sc, sc->lm_sensors[n].reg + 1);
+
+ if (datah == 0xff) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ sensor->flags &= ~SENSOR_FINVALID;
+ datah = (datah << 5) | (datal & 0x1f);
+ if (datah == 0) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ }
+ sensor->value = 1350000 / datah;
+ }
+}
+
+void
+wb_w83792d_refresh_fanrpm(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int reg, shift, data, divisor = 1;
+
+ switch (sc->lm_sensors[n].reg) {
+ case 0x28:
+ reg = 0x47; shift = 0;
+ break;
+ case 0x29:
+ reg = 0x47; shift = 4;
+ break;
+ case 0x2a:
+ reg = 0x5b; shift = 0;
+ break;
+ case 0xb8:
+ reg = 0x5b; shift = 4;
+ break;
+ case 0xb9:
+ reg = 0x5c; shift = 0;
+ break;
+ case 0xba:
+ reg = 0x5c; shift = 4;
+ break;
+ case 0xbe:
+ reg = 0x9e; shift = 0;
+ break;
+ default:
+ reg = 0; shift = 0;
+ break;
+ }
+
+ data = sc->lm_readreg(sc, sc->lm_sensors[n].reg);
+ if (data == 0xff || data == 0x00) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (reg != 0)
+ divisor = (sc->lm_readreg(sc, reg) >> shift) & 0x7;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = 1350000 / (data << divisor);
+ }
+}
+
+void
+as_refresh_temp(struct lm_softc *sc, int n)
+{
+ struct ksensor *sensor = &sc->sensors[n];
+ int sdata;
+
+ /*
+ * It seems a shorted temperature diode produces an all-ones
+ * bit pattern.
+ */
+ sdata = sc->lm_readreg(sc, sc->lm_sensors[n].reg) << 1;
+ sdata += sc->lm_readreg(sc, sc->lm_sensors[n].reg + 1) >> 7;
+ if (sdata == 0x1ff) {
+ sensor->flags |= SENSOR_FINVALID;
+ sensor->value = 0;
+ } else {
+ if (sdata & 0x100)
+ sdata -= 0x200;
+ sensor->flags &= ~SENSOR_FINVALID;
+ sensor->value = sdata * 500000 + 273150000;
+ }
+}
Index: sys/dev/lm/lm78_isa.c
===================================================================
--- /dev/null
+++ sys/dev/lm/lm78_isa.c
@@ -0,0 +1,253 @@
+/* $FreeBSD$ */
+/* $OpenBSD: lm78_isa.c,v 1.2 2007/07/01 21:48:57 cnst Exp $ */
+
+/*-
+ * Copyright (c) 2005, 2006 Mark Kettenis
+ * Copyright (c) 2007 Constantine A. Murenin, Google Summer of Code
+ *
+ * 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <isa/isavar.h>
+
+#include <sys/systm.h>
+
+#include <sys/sensors.h>
+
+#include <dev/lm/lm78var.h>
+
+/* ISA registers */
+#define LMC_ADDR 0x05
+#define LMC_DATA 0x06
+
+extern struct cfdriver lm_cd;
+
+#if defined(LMDEBUG)
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+struct lm_isa_softc {
+ struct lm_softc sc_lmsc;
+
+ struct resource *sc_iores;
+ int sc_iorid;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+};
+
+static int lm_isa_probe(struct device *);
+static int lm_isa_attach(struct device *);
+static int lm_isa_detach(struct device *);
+u_int8_t lm_isa_readreg(struct lm_softc *, int);
+void lm_isa_writereg(struct lm_softc *, int, int);
+
+static device_method_t lm_isa_methods[] = {
+ /* Methods from the device interface */
+ DEVMETHOD(device_probe, lm_isa_probe),
+ DEVMETHOD(device_attach, lm_isa_attach),
+ DEVMETHOD(device_detach, lm_isa_detach),
+
+ /* Terminate method list */
+ { 0, 0 }
+};
+
+static driver_t lm_isa_driver = {
+ "lm",
+ lm_isa_methods,
+ sizeof (struct lm_isa_softc)
+};
+
+static devclass_t lm_devclass;
+
+DRIVER_MODULE(lm, isa, lm_isa_driver, lm_devclass, NULL, NULL);
+
+int
+lm_isa_probe(struct device *dev)
+{
+ struct lm_isa_softc *sc = device_get_softc(dev);
+ struct resource *iores;
+ int iorid = 0;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ int banksel, vendid, chipid, addr;
+
+ iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (iores == NULL) {
+ DPRINTF(("%s: can't map i/o space\n", __func__));
+ return (1);
+ }
+ iot = rman_get_bustag(iores);
+ ioh = rman_get_bushandle(iores);
+
+ /* Probe for Winbond chips. */
+ bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL);
+ bus_space_write_1(iot, ioh, LMC_DATA, 0);
+ bus_space_write_1(iot, ioh, LMC_ADDR, WB_BANKSEL);
+ banksel = bus_space_read_1(iot, ioh, LMC_DATA);
+ bus_space_write_1(iot, ioh, LMC_ADDR, WB_VENDID);
+ vendid = bus_space_read_1(iot, ioh, LMC_DATA);
+ if (((banksel & 0x80) && vendid == (WB_VENDID_WINBOND >> 8)) ||
+ (!(banksel & 0x80) && vendid == (WB_VENDID_WINBOND & 0xff)))
+ goto found;
+
+ /* Probe for ITE chips (and don't attach if we find one). */
+ bus_space_write_1(iot, ioh, LMC_ADDR, 0x58 /*ITD_CHIPID*/);
+ vendid = bus_space_read_1(iot, ioh, LMC_DATA);
+ if (vendid == 0x90 /*IT_ID_IT87*/)
+ goto notfound;
+
+ /*
+ * Probe for National Semiconductor LM78/79/81.
+ *
+ * XXX This assumes the address has not been changed from the
+ * power up default. This is probably a reasonable
+ * assumption, and if it isn't true, we should be able to
+ * access the chip using the serial bus.
+ */
+ bus_space_write_1(iot, ioh, LMC_ADDR, LM_SBUSADDR);
+ addr = bus_space_read_1(iot, ioh, LMC_DATA);
+ if ((addr & 0xfc) == 0x2c) {
+ bus_space_write_1(iot, ioh, LMC_ADDR, LM_CHIPID);
+ chipid = bus_space_read_1(iot, ioh, LMC_DATA);
+
+ switch (chipid & LM_CHIPID_MASK) {
+ case LM_CHIPID_LM78:
+ case LM_CHIPID_LM78J:
+ case LM_CHIPID_LM79:
+ case LM_CHIPID_LM81:
+ goto found;
+ }
+ }
+
+ notfound:
+ bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
+
+ return (1);
+
+ found:
+ /* Bus-independent probe */
+ sc->sc_lmsc.sc_dev = dev;
+ sc->sc_iot = iot;
+ sc->sc_ioh = ioh;
+ sc->sc_lmsc.lm_writereg = lm_isa_writereg;
+ sc->sc_lmsc.lm_readreg = lm_isa_readreg;
+ lm_probe(&sc->sc_lmsc);
+
+ bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
+ sc->sc_iot = 0;
+ sc->sc_ioh = 0;
+
+ return (0);
+}
+
+int
+lm_isa_attach(struct device *dev)
+{
+ struct lm_isa_softc *sc = device_get_softc(dev);
+#ifdef notyet
+ struct lm_softc *lmsc;
+ int i;
+ u_int8_t sbusaddr;
+#endif
+
+ sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
+ 0ul, ~0ul, 8, RF_ACTIVE);
+ if (sc->sc_iores == NULL) {
+ device_printf(dev, "can't map i/o space\n");
+ return (1);
+ }
+ sc->sc_iot = rman_get_bustag(sc->sc_iores);
+ sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
+
+ /* Bus-independent attachment */
+ lm_attach(&sc->sc_lmsc);
+
+#ifdef notyet
+ /*
+ * Most devices supported by this driver can attach to iic(4)
+ * as well. However, we prefer to attach them to isa(4) since
+ * that causes less overhead and is more reliable. We look
+ * through all previously attached devices, and if we find an
+ * identical chip at the same serial bus address, we stop
+ * updating its sensors and mark them as invalid.
+ */
+
+ sbusaddr = lm_isa_readreg(&sc->sc_lmsc, LM_SBUSADDR);
+ if (sbusaddr == 0)
+ return (0);
+
+ for (i = 0; i < lm_cd.cd_ndevs; i++) {
+ lmsc = lm_cd.cd_devs[i];
+ if (lmsc == &sc->sc_lmsc)
+ continue;
+ if (lmsc && lmsc->sbusaddr == sbusaddr &&
+ lmsc->chipid == sc->sc_lmsc.chipid)
+ config_detach(&lmsc->sc_dev, 0);
+ }
+#endif
+ return (0);
+}
+
+int
+lm_isa_detach(struct device *dev)
+{
+ struct lm_isa_softc *sc = device_get_softc(dev);
+ int error;
+
+ /* Bus-independent detachment */
+ error = lm_detach(&sc->sc_lmsc);
+ if (error)
+ return (error);
+
+ error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid,
+ sc->sc_iores);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+u_int8_t
+lm_isa_readreg(struct lm_softc *lmsc, int reg)
+{
+ struct lm_isa_softc *sc = (struct lm_isa_softc *)lmsc;
+
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_ADDR, reg);
+ return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, LMC_DATA));
+}
+
+void
+lm_isa_writereg(struct lm_softc *lmsc, int reg, int val)
+{
+ struct lm_isa_softc *sc = (struct lm_isa_softc *)lmsc;
+
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_ADDR, reg);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, LMC_DATA, val);
+}
Index: sys/dev/lm/lm78var.h
===================================================================
--- /dev/null
+++ sys/dev/lm/lm78var.h
@@ -0,0 +1,158 @@
+/* $FreeBSD$ */
+/* $OpenBSD: lm78var.h,v 1.17 2011/12/06 16:06:07 mpf Exp $ */
+
+/*-
+ * Copyright (c) 2005, 2006 Mark Kettenis
+ *
+ * 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.
+ */
+
+/*
+ * National Semiconductor LM78/79/81 registers
+ */
+
+#define LM_POST_RAM 0x00 /* POST RAM occupies 0x00 -- 0x1f */
+#define LM_VALUE_RAM 0x20 /* Value RAM occupies 0x20 -- 0x3f */
+#define LM_FAN1 0x28 /* FAN1 reading */
+#define LM_FAN2 0x29 /* FAN2 reading */
+#define LM_FAN3 0x2a /* FAN3 reading */
+
+#define LM_CONFIG 0x40 /* Configuration */
+#define LM_ISR1 0x41 /* Interrupt Status 1 */
+#define LM_ISR2 0x42 /* Interrupt Status 2 */
+#define LM_SMI1 0x43 /* SMI# Mask 1 */
+#define LM_SMI2 0x44 /* SMI# Mask 2 */
+#define LM_NMI1 0x45 /* NMI Mask 1 */
+#define LM_NMI2 0x46 /* NMI Mask 2 */
+#define LM_VIDFAN 0x47 /* VID/Fan Divisor */
+#define LM_SBUSADDR 0x48 /* Serial Bus Address */
+#define LM_CHIPID 0x49 /* Chip Reset/ID */
+
+/* Chip IDs */
+
+#define LM_CHIPID_LM78 0x00
+#define LM_CHIPID_LM78J 0x40
+#define LM_CHIPID_LM79 0xC0
+#define LM_CHIPID_LM81 0x80
+#define LM_CHIPID_MASK 0xfe
+
+/*
+ * Winbond registers
+ *
+ * Several models exists. The W83781D is mostly compatible with the
+ * LM78, but has two extra temperatures. Later models add extra
+ * voltage sensors, fans and bigger fan divisors to accommodate slow
+ * running fans. To accommodate the extra sensors some models have
+ * different memory banks.
+ */
+
+#define WB_T23ADDR 0x4a /* Temperature 2 and 3 Serial Bus Address */
+#define WB_PIN 0x4b /* Pin Control */
+#define WB_BANKSEL 0x4e /* Bank Select */
+#define WB_VENDID 0x4f /* Vendor ID */
+
+/* Bank 0 regs */
+#define WB_BANK0_CHIPID 0x58 /* Chip ID */
+#define WB_BANK0_FAN45 0x5c /* Fan 4/5 Divisor Control (W83791D only) */
+#define WB_BANK0_VBAT 0x5d /* VBAT Monitor Control */
+#define WB_BANK0_FAN4 0xba /* Fan 4 reading (W83791D only) */
+#define WB_BANK0_FAN5 0xbb /* Fan 5 reading (W83791D only) */
+
+#define WB_BANK0_CONFIG 0x18 /* VRM & OVT Config (W83627THF/W83637HF) */
+
+/* Bank 1 registers */
+#define WB_BANK1_T2H 0x50 /* Temperature 2 High Byte */
+#define WB_BANK1_T2L 0x51 /* Temperature 2 Low Byte */
+
+/* Bank 2 registers */
+#define WB_BANK2_T3H 0x50 /* Temperature 3 High Byte */
+#define WB_BANK2_T3L 0x51 /* Temperature 3 Low Byte */
+
+/* Bank 4 registers (W83782D/W83627HF and later models only) */
+#define WB_BANK4_T1OFF 0x54 /* Temperature 1 Offset */
+#define WB_BANK4_T2OFF 0x55 /* Temperature 2 Offset */
+#define WB_BANK4_T3OFF 0x56 /* Temperature 3 Offset */
+
+/* Bank 5 registers (W83782D/W83627HF and later models only) */
+#define WB_BANK5_5VSB 0x50 /* 5VSB reading */
+#define WB_BANK5_VBAT 0x51 /* VBAT reading */
+
+/* Bank selection */
+#define WB_BANKSEL_B0 0x00 /* Bank 0 */
+#define WB_BANKSEL_B1 0x01 /* Bank 1 */
+#define WB_BANKSEL_B2 0x02 /* Bank 2 */
+#define WB_BANKSEL_B3 0x03 /* Bank 3 */
+#define WB_BANKSEL_B4 0x04 /* Bank 4 */
+#define WB_BANKSEL_B5 0x05 /* Bank 5 */
+#define WB_BANKSEL_HBAC 0x80 /* Register 0x4f High Byte Access */
+
+/* Vendor IDs */
+#define WB_VENDID_WINBOND 0x5ca3 /* Winbond */
+#define WB_VENDID_ASUS 0x12c3 /* ASUS */
+
+/* Chip IDs */
+#define WB_CHIPID_W83781D 0x10
+#define WB_CHIPID_W83781D_2 0x11
+#define WB_CHIPID_W83627HF 0x21
+#define WB_CHIPID_AS99127F 0x31 /* Asus W83781D clone */
+#define WB_CHIPID_W83782D 0x30
+#define WB_CHIPID_W83783S 0x40
+#define WB_CHIPID_W83697HF 0x60
+#define WB_CHIPID_W83791D 0x71
+#define WB_CHIPID_W83791SD 0x72
+#define WB_CHIPID_W83792D 0x7a
+#define WB_CHIPID_W83637HF 0x80
+#define WB_CHIPID_W83627EHF_A 0x88 /* early version, only for ASUS MBs */
+#define WB_CHIPID_W83627THF 0x90
+#define WB_CHIPID_W83627EHF 0xa1
+#define WB_CHIPID_W83627DHG 0xc1 /* also used in WBSIO_ID_NCT6776F */
+
+/* Config bits */
+#define WB_CONFIG_VMR9 0x01
+
+/* Reference voltage (mV) */
+#define WB_VREF 3600
+#define WB_W83627EHF_VREF 2048
+
+struct lm_softc;
+
+struct lm_sensor {
+ const char *desc;
+ enum sensor_type type;
+ u_int8_t bank;
+ u_int8_t reg;
+ void (*refresh)(struct lm_softc *, int);
+ int rfact;
+};
+
+struct lm_softc {
+ struct device *sc_dev;
+
+ struct ksensordev sensordev;
+ struct ksensor *sensors;
+ const struct lm_sensor *lm_sensors;
+ u_int numsensors;
+ void (*refresh_sensor_data) (struct lm_softc *);
+
+ u_int8_t (*lm_readreg)(struct lm_softc *, int);
+ void (*lm_writereg)(struct lm_softc *, int, int);
+
+ u_int8_t sbusaddr;
+ u_int8_t chipid;
+ u_int8_t sioid;
+ u_int8_t vrm9;
+};
+
+void lm_probe(struct lm_softc *);
+void lm_attach(struct lm_softc *);
+int lm_detach(struct lm_softc *);
Index: sys/dev/lm/wbsioreg.h
===================================================================
--- /dev/null
+++ sys/dev/lm/wbsioreg.h
@@ -0,0 +1,53 @@
+/* $OpenBSD: wbsioreg.h,v 1.3 2012/07/01 02:15:09 lteo Exp $ */
+/*
+ * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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.
+ */
+
+/*
+ * Winbond LPC Super I/O driver registers
+ */
+
+/* ISA bus registers */
+#define WBSIO_INDEX 0x00 /* Configuration Index Register */
+#define WBSIO_DATA 0x01 /* Configuration Data Register */
+
+#define WBSIO_IOSIZE 0x02 /* ISA I/O space size */
+
+#define WBSIO_CONF_EN_MAGIC 0x87 /* enable configuration mode */
+#define WBSIO_CONF_DS_MAGIC 0xaa /* disable configuration mode */
+
+/* Configuration Space Registers */
+#define WBSIO_LDN 0x07 /* Logical Device Number */
+#define WBSIO_ID 0x20 /* Device ID */
+#define WBSIO_REV 0x21 /* Device Revision */
+
+#define WBSIO_ID_W83627HF 0x52
+#define WBSIO_ID_W83627THF 0x82
+#define WBSIO_ID_W83627EHF 0x88
+#define WBSIO_ID_W83627DHG 0xa0
+#define WBSIO_ID_W83627DHGP 0xb0
+#define WBSIO_ID_W83627UHG 0xa2
+#define WBSIO_ID_W83627SF 0x59
+#define WBSIO_ID_W83637HF 0x70
+#define WBSIO_ID_W83697HF 0x60
+#define WBSIO_ID_NCT6776F 0xc3
+#define WBSIO_ID_NCT6779D 0xc5
+
+/* Logical Device Number (LDN) Assignments */
+#define WBSIO_LDN_HM 0x0b
+
+/* Hardware Monitor Control Registers (LDN B) */
+#define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */
+#define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */
Index: sys/dev/smbus/smbus.h
===================================================================
--- sys/dev/smbus/smbus.h
+++ sys/dev/smbus/smbus.h
@@ -29,13 +29,9 @@
#ifndef __SMBUS_H
#define __SMBUS_H
-#define SMBUS_ADDR_MIN 0x10
-#define SMBUS_ADDR_MAX 0x70
-
struct smbus_softc {
device_t owner; /* smbus owner device structure */
struct mtx lock;
- unsigned char addrs[SMBUS_ADDR_MAX];
};
void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err);
Index: sys/i386/conf/GENERIC.hints
===================================================================
--- sys/i386/conf/GENERIC.hints
+++ sys/i386/conf/GENERIC.hints
@@ -40,3 +40,11 @@
hint.attimer.0.irq="0"
hint.acpi_throttle.0.disabled="1"
hint.p4tcc.0.disabled="1"
+hint.lm.0.at="isa"
+hint.lm.0.port="0x290"
+hint.it.0.at="isa"
+hint.it.0.port="0x290"
+hint.it.1.at="isa"
+hint.it.1.port="0xc00"
+hint.it.2.at="isa"
+hint.it.2.port="0xd00"
Index: sys/kern/kern_sensors.c
===================================================================
--- /dev/null
+++ sys/kern/kern_sensors.c
@@ -0,0 +1,421 @@
+/* $FreeBSD$ */
+/* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 deraadt Exp $ */
+/* $OpenBSD: kern_sysctl.c,v 1.154 2007/06/01 17:29:10 beck Exp $ */
+
+/*-
+ * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
+ * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
+ * Copyright (c) 2007 Constantine A. Murenin <cnst+GSoC2007@FreeBSD.org>
+ *
+ * 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <sys/sysctl.h>
+#include <sys/sensors.h>
+
+int sensordev_count = 0;
+SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list);
+
+struct ksensordev *sensordev_get(int);
+struct ksensor *sensor_find(struct ksensordev *, enum sensor_type, int);
+
+struct sensor_task {
+ void *arg;
+ void (*func)(void *);
+
+ int period;
+ time_t nextrun;
+ volatile int running;
+ TAILQ_ENTRY(sensor_task) entry;
+};
+
+void sensor_task_thread(void *);
+void sensor_task_schedule(struct sensor_task *);
+
+TAILQ_HEAD(, sensor_task) tasklist = TAILQ_HEAD_INITIALIZER(tasklist);
+
+#ifndef NOSYSCTL8HACK
+void sensor_sysctl8magic_install(struct ksensordev *);
+void sensor_sysctl8magic_deinstall(struct ksensordev *);
+#endif
+
+void
+sensordev_install(struct ksensordev *sensdev)
+{
+ struct ksensordev *v, *nv;
+
+ mtx_lock(&Giant);
+ if (sensordev_count == 0) {
+ sensdev->num = 0;
+ SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
+ } else {
+ for (v = SLIST_FIRST(&sensordev_list);
+ (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
+ if (nv->num - v->num > 1)
+ break;
+ sensdev->num = v->num + 1;
+ SLIST_INSERT_AFTER(v, sensdev, list);
+ }
+ sensordev_count++;
+ mtx_unlock(&Giant);
+
+#ifndef NOSYSCTL8HACK
+ sensor_sysctl8magic_install(sensdev);
+#endif
+}
+
+void
+sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
+{
+ struct ksensor *v, *nv;
+ struct ksensors_head *sh;
+ int i;
+
+ mtx_lock(&Giant);
+ sh = &sensdev->sensors_list;
+ if (sensdev->sensors_count == 0) {
+ for (i = 0; i < SENSOR_MAX_TYPES; i++)
+ sensdev->maxnumt[i] = 0;
+ sens->numt = 0;
+ SLIST_INSERT_HEAD(sh, sens, list);
+ } else {
+ for (v = SLIST_FIRST(sh);
+ (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
+ if (v->type == sens->type && (v->type != nv->type ||
+ (v->type == nv->type && nv->numt - v->numt > 1)))
+ break;
+ /* sensors of the same type go after each other */
+ if (v->type == sens->type)
+ sens->numt = v->numt + 1;
+ else
+ sens->numt = 0;
+ SLIST_INSERT_AFTER(v, sens, list);
+ }
+ /* we only increment maxnumt[] if the sensor was added
+ * to the last position of sensors of this type
+ */
+ if (sensdev->maxnumt[sens->type] == sens->numt)
+ sensdev->maxnumt[sens->type]++;
+ sensdev->sensors_count++;
+ mtx_unlock(&Giant);
+}
+
+void
+sensordev_deinstall(struct ksensordev *sensdev)
+{
+ mtx_lock(&Giant);
+ sensordev_count--;
+ SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
+ mtx_unlock(&Giant);
+
+#ifndef NOSYSCTL8HACK
+ sensor_sysctl8magic_deinstall(sensdev);
+#endif
+}
+
+void
+sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
+{
+ struct ksensors_head *sh;
+
+ mtx_lock(&Giant);
+ sh = &sensdev->sensors_list;
+ sensdev->sensors_count--;
+ SLIST_REMOVE(sh, sens, ksensor, list);
+ /* we only decrement maxnumt[] if this is the tail
+ * sensor of this type
+ */
+ if (sens->numt == sensdev->maxnumt[sens->type] - 1)
+ sensdev->maxnumt[sens->type]--;
+ mtx_unlock(&Giant);
+}
+
+struct ksensordev *
+sensordev_get(int num)
+{
+ struct ksensordev *sd;
+
+ SLIST_FOREACH(sd, &sensordev_list, list)
+ if (sd->num == num)
+ return (sd);
+
+ return (NULL);
+}
+
+struct ksensor *
+sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt)
+{
+ struct ksensor *s;
+ struct ksensors_head *sh;
+
+ sh = &sensdev->sensors_list;
+ SLIST_FOREACH(s, sh, list)
+ if (s->type == type && s->numt == numt)
+ return (s);
+
+ return (NULL);
+}
+
+int
+sensor_task_register(void *arg, void (*func)(void *), int period)
+{
+ struct sensor_task *st;
+ int create_thread = 0;
+
+ st = malloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
+ if (st == NULL)
+ return (1);
+
+ st->arg = arg;
+ st->func = func;
+ st->period = period;
+
+ st->running = 1;
+
+ if (TAILQ_EMPTY(&tasklist))
+ create_thread = 1;
+
+ st->nextrun = 0;
+ TAILQ_INSERT_HEAD(&tasklist, st, entry);
+
+ if (create_thread)
+ if (kproc_create(sensor_task_thread, NULL, NULL, 0, 0,
+ "sensors") != 0)
+ panic("sensors kproc");
+
+ wakeup(&tasklist);
+
+ return (0);
+}
+
+void
+sensor_task_unregister(void *arg)
+{
+ struct sensor_task *st;
+
+ TAILQ_FOREACH(st, &tasklist, entry)
+ if (st->arg == arg)
+ st->running = 0;
+}
+
+void
+sensor_task_thread(void *arg)
+{
+ struct sensor_task *st, *nst;
+ time_t now;
+
+ while (!TAILQ_EMPTY(&tasklist)) {
+ while ((nst = TAILQ_FIRST(&tasklist))->nextrun >
+ (now = time_uptime))
+ tsleep(&tasklist, PWAIT, "timeout",
+ (nst->nextrun - now) * hz);
+
+ while ((st = nst) != NULL) {
+ nst = TAILQ_NEXT(st, entry);
+
+ if (st->nextrun > now)
+ break;
+
+ /* take it out while we work on it */
+ TAILQ_REMOVE(&tasklist, st, entry);
+
+ if (!st->running) {
+ free(st, M_DEVBUF);
+ continue;
+ }
+
+ /* run the task */
+ st->func(st->arg);
+ /* stick it back in the tasklist */
+ sensor_task_schedule(st);
+ }
+ }
+
+ kproc_exit(0);
+}
+
+void
+sensor_task_schedule(struct sensor_task *st)
+{
+ struct sensor_task *cst;
+
+ st->nextrun = time_uptime + st->period;
+
+ TAILQ_FOREACH(cst, &tasklist, entry) {
+ if (cst->nextrun > st->nextrun) {
+ TAILQ_INSERT_BEFORE(cst, st, entry);
+ return;
+ }
+ }
+
+ /* must be an empty list, or at the end of the list */
+ TAILQ_INSERT_TAIL(&tasklist, st, entry);
+}
+
+/*
+ * sysctl glue code
+ */
+int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS);
+int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS);
+int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS);
+
+
+#ifndef NOSYSCTL8HACK
+
+SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL,
+ "Hardware Sensors sysctl internal magic");
+SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler,
+ "Hardware Sensors XP MIB interface");
+
+#else /* NOSYSCTL8HACK */
+
+SYSCTL_NODE(_hw, HW_SENSORS, sensors, CTLFLAG_RD, sysctl_sensors_handler,
+ "Hardware Sensors");
+
+#endif /* !NOSYSCTL8HACK */
+
+
+#ifndef NOSYSCTL8HACK
+
+/*
+ * XXX:
+ * FreeBSD's sysctl(9) .oid_handler functionality is not accustomed
+ * for the CTLTYPE_NODE handler to handle the undocumented sysctl
+ * magic calls. As soon as such functionality is developed,
+ * sysctl_sensors_handler() should be converted to handle all such
+ * calls, and these sysctl_add_oid(9) calls should be removed
+ * "with a big axe". This whole sysctl_add_oid(9) business is solely
+ * to please sysctl(8).
+ */
+
+void
+sensor_sysctl8magic_install(struct ksensordev *sensdev)
+{
+ struct sysctl_oid_list *ol;
+ struct sysctl_ctx_list *cl = &sensdev->clist;
+ struct ksensor *s;
+ struct ksensors_head *sh = &sensdev->sensors_list;
+
+ sysctl_ctx_init(cl);
+ ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, &SYSCTL_NODE_CHILDREN(_hw,
+ sensors), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, ""));
+ SLIST_FOREACH(s, sh, list) {
+ char n[32];
+
+ snprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], s->numt);
+ SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT |
+ CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", "");
+ }
+}
+
+void
+sensor_sysctl8magic_deinstall(struct ksensordev *sensdev)
+{
+ struct sysctl_ctx_list *cl = &sensdev->clist;
+
+ sysctl_ctx_free(cl);
+}
+
+#endif /* !NOSYSCTL8HACK */
+
+
+int
+sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
+{
+ struct ksensordev *ksd = arg1;
+ struct sensordev *usd;
+ int error;
+
+ if (req->newptr)
+ return (EPERM);
+
+ /* Grab a copy, to clear the kernel pointers */
+ usd = malloc(sizeof(*usd), M_TEMP, M_WAITOK);
+ bzero(usd, sizeof(*usd));
+ usd->num = ksd->num;
+ strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
+ memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
+ usd->sensors_count = ksd->sensors_count;
+
+ error = SYSCTL_OUT(req, usd, sizeof(struct sensordev));
+
+ free(usd, M_TEMP);
+ return (error);
+}
+
+int
+sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
+{
+ struct ksensor *ks = arg1;
+ struct sensor *us;
+ int error;
+
+ if (req->newptr)
+ return (EPERM);
+
+ /* Grab a copy, to clear the kernel pointers */
+ us = malloc(sizeof(*us), M_TEMP, M_WAITOK);
+ bzero(us, sizeof(*us));
+ memcpy(us->desc, ks->desc, sizeof(ks->desc));
+ us->tv = ks->tv;
+ us->value = ks->value;
+ us->type = ks->type;
+ us->status = ks->status;
+ us->numt = ks->numt;
+ us->flags = ks->flags;
+
+ error = SYSCTL_OUT(req, us, sizeof(struct sensor));
+
+ free(us, M_TEMP);
+ return (error);
+}
+
+int
+sysctl_sensors_handler(SYSCTL_HANDLER_ARGS)
+{
+ int *name = arg1;
+ u_int namelen = arg2;
+ struct ksensordev *ksd;
+ struct ksensor *ks;
+ int dev, numt;
+ enum sensor_type type;
+
+ if (namelen != 1 && namelen != 3)
+ return (ENOTDIR);
+
+ dev = name[0];
+ if ((ksd = sensordev_get(dev)) == NULL)
+ return (ENOENT);
+ if (namelen == 1)
+ return (sysctl_handle_sensordev(NULL, ksd, 0, req));
+
+ type = name[1];
+ numt = name[2];
+ if ((ks = sensor_find(ksd, type, numt)) == NULL)
+ return (ENOENT);
+ return (sysctl_handle_sensor(NULL, ks, 0, req));
+}
Index: sys/modules/Makefile
===================================================================
--- sys/modules/Makefile
+++ sys/modules/Makefile
@@ -185,6 +185,7 @@
${_iser} \
isp \
${_ispfw} \
+ ${_it} \
${_iwi} \
${_iwifw} \
${_iwm} \
@@ -216,6 +217,7 @@
${_linux_common} \
${_linux64} \
linuxkpi \
+ ${_lm} \
lmc \
lpt \
mac_biba \
@@ -631,6 +633,7 @@
_ips= ips
_isci= isci
_ipw= ipw
+_it= it
_iwi= iwi
_iwm= iwm
_iwn= iwn
@@ -641,6 +644,7 @@
_iwmfw= iwmfw
_iwnfw= iwnfw
.endif
+_lm= lm
_mlx4= mlx4
_mlx5= mlx5
.if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \
Index: sys/modules/it/Makefile
===================================================================
--- /dev/null
+++ sys/modules/it/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/it
+
+KMOD= it
+SRCS= it.c
+SRCS+= device_if.h bus_if.h isa_if.h
+
+.include <bsd.kmod.mk>
Index: sys/modules/lm/Makefile
===================================================================
--- /dev/null
+++ sys/modules/lm/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/lm
+
+KMOD= lm
+SRCS= lm78.c lm78_isa.c
+SRCS+= device_if.h bus_if.h isa_if.h
+
+.include <bsd.kmod.mk>
Index: sys/sys/sensors.h
===================================================================
--- /dev/null
+++ sys/sys/sensors.h
@@ -0,0 +1,168 @@
+/* $FreeBSD$ */
+/* $OpenBSD: sensors.h,v 1.23 2007/03/22 16:55:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 Alexander Yurchenko <grange@openbsd.org>
+ * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
+ * All rights reserved.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _SYS_SENSORS_H_
+#define _SYS_SENSORS_H_
+
+/* Sensor types */
+enum sensor_type {
+ SENSOR_TEMP, /* temperature (muK) */
+ SENSOR_FANRPM, /* fan revolution speed */
+ SENSOR_VOLTS_DC, /* voltage (muV DC) */
+ SENSOR_VOLTS_AC, /* voltage (muV AC) */
+ SENSOR_OHMS, /* resistance */
+ SENSOR_WATTS, /* power */
+ SENSOR_AMPS, /* current (muA) */
+ SENSOR_WATTHOUR, /* power capacity */
+ SENSOR_AMPHOUR, /* power capacity */
+ SENSOR_INDICATOR, /* boolean indicator */
+ SENSOR_INTEGER, /* generic integer value */
+ SENSOR_PERCENT, /* percent */
+ SENSOR_LUX, /* illuminance (mulx) */
+ SENSOR_DRIVE, /* disk */
+ SENSOR_TIMEDELTA, /* system time error (nSec) */
+ SENSOR_MAX_TYPES
+};
+
+static const char * const sensor_type_s[SENSOR_MAX_TYPES + 1] = {
+ "temp",
+ "fan",
+ "volt",
+ "acvolt",
+ "resistance",
+ "power",
+ "current",
+ "watthour",
+ "amphour",
+ "indicator",
+ "raw",
+ "percent",
+ "illuminance",
+ "drive",
+ "timedelta",
+ "undefined"
+};
+
+#define SENSOR_DRIVE_EMPTY 1
+#define SENSOR_DRIVE_READY 2
+#define SENSOR_DRIVE_POWERUP 3
+#define SENSOR_DRIVE_ONLINE 4
+#define SENSOR_DRIVE_IDLE 5
+#define SENSOR_DRIVE_ACTIVE 6
+#define SENSOR_DRIVE_REBUILD 7
+#define SENSOR_DRIVE_POWERDOWN 8
+#define SENSOR_DRIVE_FAIL 9
+#define SENSOR_DRIVE_PFAIL 10
+
+/* Sensor states */
+enum sensor_status {
+ SENSOR_S_UNSPEC, /* status is unspecified */
+ SENSOR_S_OK, /* status is ok */
+ SENSOR_S_WARN, /* status is warning */
+ SENSOR_S_CRIT, /* status is critical */
+ SENSOR_S_UNKNOWN /* status is unknown */
+};
+
+/* Sensor data:
+ * New fields should be added at the end to encourage backwards compat
+ */
+struct sensor {
+ char desc[32]; /* sensor description, may be empty */
+ struct timeval tv; /* sensor value last change time */
+ int64_t value; /* current value */
+ enum sensor_type type; /* sensor type */
+ enum sensor_status status; /* sensor status */
+ int numt; /* sensor number of .type type */
+ int flags; /* sensor flags */
+#define SENSOR_FINVALID 0x0001 /* sensor is invalid */
+#define SENSOR_FUNKNOWN 0x0002 /* sensor value is unknown */
+};
+
+/* Sensor device data:
+ * New fields should be added at the end to encourage backwards compat
+ */
+struct sensordev {
+ int num; /* sensordev number */
+ char xname[16]; /* unix device name */
+ int maxnumt[SENSOR_MAX_TYPES];
+ int sensors_count;
+};
+
+#define MAXSENSORDEVICES 32
+
+#ifdef _KERNEL
+#include <sys/queue.h>
+#ifndef NOSYSCTL8HACK
+ #include <sys/sysctl.h>
+#endif
+
+/* Sensor data */
+struct ksensor {
+ SLIST_ENTRY(ksensor) list; /* device-scope list */
+ char desc[32]; /* sensor description, may be empty */
+ struct timeval tv; /* sensor value last change time */
+ int64_t value; /* current value */
+ enum sensor_type type; /* sensor type */
+ enum sensor_status status; /* sensor status */
+ int numt; /* sensor number of .type type */
+ int flags; /* sensor flags, ie. SENSOR_FINVALID */
+};
+SLIST_HEAD(ksensors_head, ksensor);
+
+/* Sensor device data */
+struct ksensordev {
+ SLIST_ENTRY(ksensordev) list;
+ int num; /* sensordev number */
+ char xname[16]; /* unix device name */
+ int maxnumt[SENSOR_MAX_TYPES];
+ int sensors_count;
+ struct ksensors_head sensors_list;
+#ifndef NOSYSCTL8HACK
+ struct sysctl_ctx_list clist; /* XXX: sysctl(9) .oid_handler() for
+ * CTLTYPE_NODE type doesn't support
+ * the undocumented sysctl magic.
+ */
+#endif /* !NOSYSCTL8HACK */
+};
+
+/* struct ksensordev */
+void sensordev_install(struct ksensordev *);
+void sensordev_deinstall(struct ksensordev *);
+
+/* struct ksensor */
+void sensor_attach(struct ksensordev *, struct ksensor *);
+void sensor_detach(struct ksensordev *, struct ksensor *);
+
+/* task scheduling */
+int sensor_task_register(void *, void (*)(void *), int);
+void sensor_task_unregister(void *);
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_SENSORS_H_ */
Index: sys/sys/sysctl.h
===================================================================
--- sys/sys/sysctl.h
+++ sys/sys/sysctl.h
@@ -920,6 +920,7 @@
#define HW_FLOATINGPT 10 /* int: has HW floating point? */
#define HW_MACHINE_ARCH 11 /* string: machine architecture */
#define HW_REALMEM 12 /* int: 'real' memory */
+#define HW_SENSORS 13 /* node: hardware monitors */
/*
* CTL_USER definitions
Index: usr.bin/systat/Makefile
===================================================================
--- usr.bin/systat/Makefile
+++ usr.bin/systat/Makefile
@@ -5,7 +5,7 @@
PROG= systat
SRCS= cmds.c cmdtab.c devs.c fetch.c iostat.c keyboard.c main.c \
- netcmds.c netstat.c pigs.c swap.c icmp.c \
+ netcmds.c netstat.c pigs.c sensors.c swap.c icmp.c \
mode.c ip.c sctp.c tcp.c zarc.c \
vmstat.c convtbl.c ifcmds.c ifstat.c
Index: usr.bin/systat/cmdtab.c
===================================================================
--- usr.bin/systat/cmdtab.c
+++ usr.bin/systat/cmdtab.c
@@ -78,6 +78,9 @@
{ "ifstat", showifstat, fetchifstat, labelifstat,
initifstat, openifstat, closeifstat, cmdifstat,
0, CF_LOADAV },
+ { "sensors", showsensors, fetchsensors, labelsensors,
+ initsensors, opensensors, closesensors, 0,
+ 0, CF_LOADAV },
{ "zarc", showzarc, fetchzarc, labelzarc,
initzarc, openzarc, closezarc, 0,
resetzarc, CF_ZFSARC },
Index: usr.bin/systat/extern.h
===================================================================
--- usr.bin/systat/extern.h
+++ usr.bin/systat/extern.h
@@ -78,6 +78,7 @@
void closekre(WINDOW *);
void closenetstat(WINDOW *);
void closepigs(WINDOW *);
+void closesensors(WINDOW *);
void closeswap(WINDOW *);
void closetcp(WINDOW *);
int cmdifstat(const char *, const char *);
@@ -100,6 +101,7 @@
void fetchkre(void);
void fetchnetstat(void);
void fetchpigs(void);
+void fetchsensors(void);
void fetchswap(void);
void fetchtcp(void);
void getsysctl(const char *, void *, size_t);
@@ -113,6 +115,7 @@
int initkre(void);
int initnetstat(void);
int initpigs(void);
+int initsensors(void);
int initswap(void);
int inittcp(void);
int keyboard(void);
@@ -127,6 +130,7 @@
void labelnetstat(void);
void labelpigs(void);
void labels(void);
+void labelsensors(void);
void labelswap(void);
void labeltcp(void);
void load(void);
@@ -141,6 +145,7 @@
WINDOW *openkre(void);
WINDOW *opennetstat(void);
WINDOW *openpigs(void);
+WINDOW *opensensors(void);
WINDOW *openswap(void);
WINDOW *opentcp(void);
int prefix(const char *, const char *);
@@ -158,6 +163,7 @@
void showkre(void);
void shownetstat(void);
void showpigs(void);
+void showsensors(void);
void showswap(void);
void showtcp(void);
void status(void);
Index: usr.bin/systat/sensors.c
===================================================================
--- /dev/null
+++ usr.bin/systat/sensors.c
@@ -0,0 +1,261 @@
+/* $FreeBSD$ */
+/* $OpenBSD: sensors.c,v 1.12 2007/07/29 04:51:59 cnst Exp $ */
+
+/*-
+ * Copyright (c) 2007 Deanna Phillips <deanna@openbsd.org>
+ * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sensors.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "systat.h"
+#include "extern.h"
+
+struct sensor sensor;
+struct sensordev sensordev;
+int row, sensor_cnt;
+void printline(void);
+static char * fmttime(double);
+
+WINDOW *
+opensensors(void)
+{
+ return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
+}
+
+void
+closesensors(WINDOW *w)
+{
+ if (w == NULL)
+ return;
+ wclear(w);
+ wrefresh(w);
+ delwin(w);
+}
+
+void
+labelsensors(void)
+{
+ wmove(wnd, 0, 0);
+ wclrtobot(wnd);
+ mvwaddstr(wnd, 0, 0, "Sensor");
+ mvwaddstr(wnd, 0, 34, "Value");
+ mvwaddstr(wnd, 0, 45, "Status");
+ mvwaddstr(wnd, 0, 58, "Description");
+}
+
+void
+fetchsensors(void)
+{
+ enum sensor_type type;
+ size_t slen, sdlen;
+ int mib[5], dev, numt;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+ slen = sizeof(struct sensor);
+ sdlen = sizeof(struct sensordev);
+
+ row = 1;
+ sensor_cnt = 0;
+
+ wmove(wnd, row, 0);
+ wclrtobot(wnd);
+
+ for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
+ mib[2] = dev;
+ if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
+ if (errno != ENOENT)
+ warn("sysctl");
+ continue;
+ }
+ for (type = 0; type < SENSOR_MAX_TYPES; type++) {
+ mib[3] = type;
+ for (numt = 0; numt < sensordev.maxnumt[type]; numt++) {
+ mib[4] = numt;
+ if (sysctl(mib, 5, &sensor, &slen, NULL, 0)
+ == -1) {
+ if (errno != ENOENT)
+ warn("sysctl");
+ continue;
+ }
+ if (sensor.flags & SENSOR_FINVALID)
+ continue;
+ sensor_cnt++;
+ printline();
+ }
+ }
+ }
+}
+
+const char *drvstat[] = {
+ NULL,
+ "empty", "ready", "powerup", "online", "idle", "active",
+ "rebuild", "powerdown", "fail", "pfail"
+};
+
+void
+showsensors(void)
+{
+ if (sensor_cnt == 0)
+ mvwaddstr(wnd, row, 0, "No sensors found.");
+}
+
+int
+initsensors(void)
+{
+ return (1);
+}
+
+void
+printline(void)
+{
+ mvwprintw(wnd, row, 0, "%s.%s%d", sensordev.xname,
+ sensor_type_s[sensor.type], sensor.numt);
+ switch (sensor.type) {
+ case SENSOR_TEMP:
+ mvwprintw(wnd, row, 24, "%10.2f degC",
+ (sensor.value - 273150000) / 1000000.0);
+ break;
+ case SENSOR_FANRPM:
+ mvwprintw(wnd, row, 24, "%11lld RPM", sensor.value);
+ break;
+ case SENSOR_VOLTS_DC:
+ mvwprintw(wnd, row, 24, "%10.2f V DC",
+ sensor.value / 1000000.0);
+ break;
+ case SENSOR_AMPS:
+ mvwprintw(wnd, row, 24, "%10.2f A", sensor.value / 1000000.0);
+ break;
+ case SENSOR_INDICATOR:
+ mvwprintw(wnd, row, 24, "%15s", sensor.value? "On" : "Off");
+ break;
+ case SENSOR_INTEGER:
+ mvwprintw(wnd, row, 24, "%11lld raw", sensor.value);
+ break;
+ case SENSOR_PERCENT:
+ mvwprintw(wnd, row, 24, "%14.2f%%", sensor.value / 1000.0);
+ break;
+ case SENSOR_LUX:
+ mvwprintw(wnd, row, 24, "%15.2f lx", sensor.value / 1000000.0);
+ break;
+ case SENSOR_DRIVE:
+ if (0 < sensor.value &&
+ sensor.value < sizeof(drvstat)/sizeof(drvstat[0])) {
+ mvwprintw(wnd, row, 24, "%15s", drvstat[sensor.value]);
+ break;
+ }
+ break;
+ case SENSOR_TIMEDELTA:
+ mvwprintw(wnd, row, 24, "%15s", fmttime(sensor.value / 1000000000.0));
+ break;
+ case SENSOR_WATTHOUR:
+ mvwprintw(wnd, row, 24, "%12.2f Wh", sensor.value / 1000000.0);
+ break;
+ case SENSOR_AMPHOUR:
+ mvwprintw(wnd, row, 24, "%10.2f Ah", sensor.value / 1000000.0);
+ break;
+ default:
+ mvwprintw(wnd, row, 24, "%10lld", sensor.value);
+ break;
+ }
+ if (sensor.desc[0] != '\0')
+ mvwprintw(wnd, row, 58, "(%s)", sensor.desc);
+
+ switch (sensor.status) {
+ case SENSOR_S_UNSPEC:
+ break;
+ case SENSOR_S_UNKNOWN:
+ mvwaddstr(wnd, row, 45, "unknown");
+ break;
+ case SENSOR_S_WARN:
+ mvwaddstr(wnd, row, 45, "WARNING");
+ break;
+ case SENSOR_S_CRIT:
+ mvwaddstr(wnd, row, 45, "CRITICAL");
+ break;
+ case SENSOR_S_OK:
+ mvwaddstr(wnd, row, 45, "OK");
+ break;
+ }
+ row++;
+}
+
+#define SECS_PER_DAY 86400
+#define SECS_PER_HOUR 3600
+#define SECS_PER_MIN 60
+
+static char *
+fmttime(double in)
+{
+ int signbit = 1;
+ int tiny = 0;
+ char *unit;
+#define LEN 32
+ static char outbuf[LEN];
+
+ if (in < 0){
+ signbit = -1;
+ in *= -1;
+ }
+
+ if (in >= SECS_PER_DAY ){
+ unit = "days";
+ in /= SECS_PER_DAY;
+ } else if (in >= SECS_PER_HOUR ){
+ unit = "hr";
+ in /= SECS_PER_HOUR;
+ } else if (in >= SECS_PER_MIN ){
+ unit = "min";
+ in /= SECS_PER_MIN;
+ } else if (in >= 1 ){
+ unit = "s";
+ /* in *= 1; */ /* no op */
+ } else if (in == 0 ){ /* direct comparisons to floats are scary */
+ unit = "s";
+ } else if (in >= 1e-3 ){
+ unit = "ms";
+ in *= 1e3;
+ } else if (in >= 1e-6 ){
+ unit = "us";
+ in *= 1e6;
+ } else if (in >= 1e-9 ){
+ unit = "ns";
+ in *= 1e9;
+ } else {
+ unit = "ps";
+ if (in < 1e-13)
+ tiny = 1;
+ in *= 1e12;
+ }
+
+ snprintf(outbuf, LEN,
+ tiny ? "%s%lf %s" : "%s%.3lf %s",
+ signbit == -1 ? "-" : "", in, unit);
+
+ return outbuf;
+}
Index: usr.bin/systat/systat.1
===================================================================
--- usr.bin/systat/systat.1
+++ usr.bin/systat/systat.1
@@ -97,6 +97,7 @@
.Ic netstat ,
.Ic pigs ,
.Ic sctp ,
+.Ic sensors ,
.Ic swap ,
.Ic tcp ,
.Ic vmstat ,
@@ -307,6 +308,11 @@
If there are more than one swap partition in use,
a total line is also shown.
Areas known to the kernel, but not in use are shown as not available.
+.It Ic sensors
+Display, in the lower window,
+the current values of available hardware sensors,
+in a format similar to that of
+.Xr sysctl 8 .
.It Ic vmstat
Take over the entire display and show a (rather crowded) compendium
of statistics related to virtual memory usage, process scheduling,
@@ -652,6 +658,7 @@
.Xr udp 4 ,
.Xr gstat 8 ,
.Xr iostat 8 ,
+.Xr sysctl 8 ,
.Xr vmstat 8
.Sh HISTORY
The
Index: usr.sbin/Makefile
===================================================================
--- usr.sbin/Makefile
+++ usr.sbin/Makefile
@@ -74,6 +74,7 @@
rpc.statd \
rpc.umntall \
rtprio \
+ sensorsd \
service \
services_mkdb \
sesutil \
Index: usr.sbin/sensorsd/Makefile
===================================================================
--- /dev/null
+++ usr.sbin/sensorsd/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+# $OpenBSD: Makefile,v 1.1 2003/09/24 20:32:49 henning Exp $
+
+PROG= sensorsd
+MAN= sensorsd.8 sensorsd.conf.5
+
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
Index: usr.sbin/sensorsd/sensorsd.8
===================================================================
--- /dev/null
+++ usr.sbin/sensorsd/sensorsd.8
@@ -0,0 +1,93 @@
+.\" $FreeBSD$
+.\" $OpenBSD: sensorsd.8,v 1.16 2007/08/11 20:45:35 cnst Exp $
+.\"
+.\" Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
+.\" Copyright (c) 2007 Constantine A. Murenin <cnst@FreeBSD.org>
+.\"
+.\" 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.
+.\"
+.Dd August 11, 2007
+.Dt SENSORSD 8
+.Os
+.Sh NAME
+.Nm sensorsd
+.Nd hardware sensors monitor
+.Sh SYNOPSIS
+.Nm sensorsd
+.Op Fl d
+.Sh DESCRIPTION
+The
+.Nm
+utility retrieves sensor monitoring data like fan speed,
+temperature, voltage and
+.Xr ami 4
+logical disk status via
+.Xr sysctl 3 .
+When the state of any monitored sensor changes, an alert is sent using
+.Xr syslog 3
+and a command, if specified, is executed.
+.Pp
+By default,
+.Nm
+monitors status changes on all sensors that keep their state,
+thus sensors that automatically provide status do not require
+any additional configuration.
+In addition, for every sensor,
+no matter whether it automatically provides its state or not,
+custom low and high limits may be set,
+so that a local notion of sensor status can be computed by
+.Nm ,
+indicating whether the sensor is within or is exceeding its limits.
+.Pp
+Limit and command values for a particular sensor may be specified in the
+.Xr sensorsd.conf 5
+configuration file.
+This file is reloaded upon receiving
+.Dv SIGHUP .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/sensorsd.conf"
+.It /etc/sensorsd.conf
+Configuration file for
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr sysctl 3 ,
+.Xr syslog 3 ,
+.Xr sensorsd.conf 5 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 3.5 .
+.Sh CAVEATS
+Certain sensors may flip status from time to time.
+To guard against false reports,
+.Nm
+implements a state dumping mechanism.
+However, this inevitably introduces
+an additional delay in status reporting and command execution,
+e.g. one may notice that
+.Nm
+makes its initial report about the state of monitored sensors
+not immediately, but either 1 or 2 minutes after it is being started up.
Index: usr.sbin/sensorsd/sensorsd.c
===================================================================
--- /dev/null
+++ usr.sbin/sensorsd/sensorsd.c
@@ -0,0 +1,648 @@
+/* $FreeBSD$ */
+/* $OpenBSD: sensorsd.c,v 1.34 2007/08/14 17:10:02 cnst Exp $ */
+
+/*-
+ * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
+ * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sensors.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#define RFBUFSIZ 28 /* buffer size for print_sensor */
+#define RFBUFCNT 4 /* ring buffers */
+#define REPORT_PERIOD 60 /* report every n seconds */
+#define CHECK_PERIOD 20 /* check every n seconds */
+
+enum sensorsd_s_status {
+ SENSORSD_S_UNSPEC, /* status is unspecified */
+ SENSORSD_S_INVALID, /* status is invalid, per SENSOR_FINVALID */
+ SENSORSD_S_WITHIN, /* status is within limits */
+ SENSORSD_S_OUTSIDE /* status is outside limits */
+};
+
+struct limits_t {
+ TAILQ_ENTRY(limits_t) entries;
+ enum sensor_type type; /* sensor type */
+ int numt; /* sensor number */
+ int64_t last_val;
+ int64_t lower; /* lower limit */
+ int64_t upper; /* upper limit */
+ char *command; /* failure command */
+ time_t astatus_changed;
+ time_t ustatus_changed;
+ enum sensor_status astatus; /* last automatic status */
+ enum sensor_status astatus2;
+ enum sensorsd_s_status ustatus; /* last user-limit status */
+ enum sensorsd_s_status ustatus2;
+ int acount; /* stat change counter */
+ int ucount; /* stat change counter */
+ u_int8_t flags; /* sensorsd limit flags */
+#define SENSORSD_L_USERLIMIT 0x0001 /* user specified limit */
+#define SENSORSD_L_ISTATUS 0x0002 /* ignore automatic status */
+};
+
+struct sdlim_t {
+ TAILQ_ENTRY(sdlim_t) entries;
+ char dxname[16]; /* device unix name */
+ int dev; /* device number */
+ int sensor_cnt;
+ TAILQ_HEAD(, limits_t) limits;
+};
+
+void usage(void);
+struct sdlim_t *create_sdlim(struct sensordev *);
+void check(void);
+void check_sdlim(struct sdlim_t *);
+void execute(char *);
+void report(time_t);
+void report_sdlim(struct sdlim_t *, time_t);
+static char *print_sensor(enum sensor_type, int64_t);
+void parse_config(char *);
+void parse_config_sdlim(struct sdlim_t *, char **);
+int64_t get_val(char *, int, enum sensor_type);
+void reparse_cfg(int);
+
+static TAILQ_HEAD(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
+
+extern const char *__progname;
+static char *configfile;
+static volatile sig_atomic_t reload = 0;
+static int debug = 0;
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-d]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sensordev sensordev;
+ struct sdlim_t *sdlim;
+ size_t sdlen = sizeof(sensordev);
+ time_t next_report, last_report = 0, next_check;
+ int mib[3], dev;
+ int sleeptime, sensor_cnt = 0, ch;
+
+ while ((ch = getopt(argc, argv, "d")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+
+ for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
+ mib[2] = dev;
+ if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
+ if (errno != ENOENT)
+ warn("sysctl");
+ continue;
+ }
+ sdlim = create_sdlim(&sensordev);
+ TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
+ sensor_cnt += sdlim->sensor_cnt;
+ }
+
+ if (sensor_cnt == 0)
+ errx(1, "no sensors found");
+
+ openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ if (configfile == NULL)
+ if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
+ err(1, "out of memory");
+ parse_config(configfile);
+
+ if (debug == 0 && daemon(0, 0) == -1)
+ err(1, "unable to fork");
+
+ signal(SIGHUP, reparse_cfg);
+ signal(SIGCHLD, SIG_IGN);
+
+ syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
+
+ next_check = next_report = time(NULL);
+
+ for (;;) {
+ if (reload) {
+ parse_config(configfile);
+ syslog(LOG_INFO, "configuration reloaded");
+ reload = 0;
+ }
+ if (next_check <= time(NULL)) {
+ check();
+ next_check = time(NULL) + CHECK_PERIOD;
+ }
+ if (next_report <= time(NULL)) {
+ report(last_report);
+ last_report = next_report;
+ next_report = time(NULL) + REPORT_PERIOD;
+ }
+ if (next_report < next_check)
+ sleeptime = next_report - time(NULL);
+ else
+ sleeptime = next_check - time(NULL);
+ if (sleeptime > 0)
+ sleep(sleeptime);
+ }
+}
+
+struct sdlim_t *
+create_sdlim(struct sensordev *snsrdev)
+{
+ struct sensor sensor;
+ struct sdlim_t *sdlim;
+ struct limits_t *limit;
+ size_t slen = sizeof(sensor);
+ int mib[5], numt;
+ enum sensor_type type;
+
+ if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
+ err(1, "calloc");
+
+ strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+ mib[2] = sdlim->dev = snsrdev->num;
+
+ TAILQ_INIT(&sdlim->limits);
+
+ for (type = 0; type < SENSOR_MAX_TYPES; type++) {
+ mib[3] = type;
+ for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
+ mib[4] = numt;
+ if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
+ if (errno != ENOENT)
+ warn("sysctl");
+ continue;
+ }
+ if ((limit = calloc(1, sizeof(struct limits_t))) ==
+ NULL)
+ err(1, "calloc");
+ limit->type = type;
+ limit->numt = numt;
+ TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
+ sdlim->sensor_cnt++;
+ }
+ }
+
+ return (sdlim);
+}
+
+void
+check(void)
+{
+ struct sdlim_t *sdlim;
+
+ TAILQ_FOREACH(sdlim, &sdlims, entries)
+ check_sdlim(sdlim);
+}
+
+void
+check_sdlim(struct sdlim_t *sdlim)
+{
+ struct sensor sensor;
+ struct limits_t *limit;
+ size_t len;
+ int mib[5];
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_SENSORS;
+ mib[2] = sdlim->dev;
+ len = sizeof(sensor);
+
+ TAILQ_FOREACH(limit, &sdlim->limits, entries) {
+ if ((limit->flags & SENSORSD_L_ISTATUS) &&
+ !(limit->flags & SENSORSD_L_USERLIMIT))
+ continue;
+
+ mib[3] = limit->type;
+ mib[4] = limit->numt;
+ if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+
+ if (!(limit->flags & SENSORSD_L_ISTATUS)) {
+ enum sensor_status newastatus = sensor.status;
+
+ if (limit->astatus != newastatus) {
+ if (limit->astatus2 != newastatus) {
+ limit->astatus2 = newastatus;
+ limit->acount = 0;
+ } else if (++limit->acount >= 3) {
+ limit->last_val = sensor.value;
+ limit->astatus2 =
+ limit->astatus = newastatus;
+ limit->astatus_changed = time(NULL);
+ }
+ }
+ }
+
+ if (limit->flags & SENSORSD_L_USERLIMIT) {
+ enum sensorsd_s_status newustatus;
+
+ if (sensor.flags & SENSOR_FINVALID)
+ newustatus = SENSORSD_S_INVALID;
+ else if (sensor.value > limit->upper ||
+ sensor.value < limit->lower)
+ newustatus = SENSORSD_S_OUTSIDE;
+ else
+ newustatus = SENSORSD_S_WITHIN;
+
+ if (limit->ustatus != newustatus) {
+ if (limit->ustatus2 != newustatus) {
+ limit->ustatus2 = newustatus;
+ limit->ucount = 0;
+ } else if (++limit->ucount >= 3) {
+ limit->last_val = sensor.value;
+ limit->ustatus2 =
+ limit->ustatus = newustatus;
+ limit->ustatus_changed = time(NULL);
+ }
+ }
+ }
+ }
+}
+
+void
+execute(char *command)
+{
+ char sh[] = "sh";
+ char dash_c[] = "-c";
+ char * const argp[] = {sh, dash_c, command, NULL};
+
+ switch (fork()) {
+ case -1:
+ syslog(LOG_CRIT, "execute: fork() failed");
+ break;
+ case 0:
+ execv("/bin/sh", argp);
+ _exit(1);
+ /* NOTREACHED */
+ default:
+ break;
+ }
+}
+
+void
+report(time_t last_report)
+{
+ struct sdlim_t *sdlim;
+
+ TAILQ_FOREACH(sdlim, &sdlims, entries)
+ report_sdlim(sdlim, last_report);
+}
+
+void
+report_sdlim(struct sdlim_t *sdlim, time_t last_report)
+{
+ struct limits_t *limit;
+
+ TAILQ_FOREACH(limit, &sdlim->limits, entries) {
+ if ((limit->astatus_changed <= last_report) &&
+ (limit->ustatus_changed <= last_report))
+ continue;
+
+ if (limit->astatus_changed > last_report) {
+ const char *as = NULL;
+
+ switch (limit->astatus) {
+ case SENSOR_S_UNSPEC:
+ as = "";
+ break;
+ case SENSOR_S_OK:
+ as = ", OK";
+ break;
+ case SENSOR_S_WARN:
+ as = ", WARN";
+ break;
+ case SENSOR_S_CRIT:
+ as = ", CRITICAL";
+ break;
+ case SENSOR_S_UNKNOWN:
+ as = ", UNKNOWN";
+ break;
+ }
+ syslog(LOG_ALERT, "%s.%s%d: %s%s",
+ sdlim->dxname, sensor_type_s[limit->type],
+ limit->numt,
+ print_sensor(limit->type, limit->last_val), as);
+ }
+
+ if (limit->ustatus_changed > last_report) {
+ char us[BUFSIZ];
+
+ switch (limit->ustatus) {
+ case SENSORSD_S_UNSPEC:
+ snprintf(us, sizeof(us),
+ "ustatus uninitialised");
+ break;
+ case SENSORSD_S_INVALID:
+ snprintf(us, sizeof(us), "marked invalid");
+ break;
+ case SENSORSD_S_WITHIN:
+ snprintf(us, sizeof(us), "within limits: %s",
+ print_sensor(limit->type, limit->last_val));
+ break;
+ case SENSORSD_S_OUTSIDE:
+ snprintf(us, sizeof(us), "exceeds limits: %s",
+ print_sensor(limit->type, limit->last_val));
+ break;
+ }
+ syslog(LOG_ALERT, "%s.%s%d: %s",
+ sdlim->dxname, sensor_type_s[limit->type],
+ limit->numt, us);
+ }
+
+ if (limit->command) {
+ int i = 0, n = 0, r;
+ char *cmd = limit->command;
+ char buf[BUFSIZ];
+ int len = sizeof(buf);
+
+ buf[0] = '\0';
+ for (i = n = 0; n < len; ++i) {
+ if (cmd[i] == '\0') {
+ buf[n++] = '\0';
+ break;
+ }
+ if (cmd[i] != '%') {
+ buf[n++] = limit->command[i];
+ continue;
+ }
+ i++;
+ if (cmd[i] == '\0') {
+ buf[n++] = '\0';
+ break;
+ }
+
+ switch (cmd[i]) {
+ case 'x':
+ r = snprintf(&buf[n], len - n, "%s",
+ sdlim->dxname);
+ break;
+ case 't':
+ r = snprintf(&buf[n], len - n, "%s",
+ sensor_type_s[limit->type]);
+ break;
+ case 'n':
+ r = snprintf(&buf[n], len - n, "%d",
+ limit->numt);
+ break;
+ case '2':
+ r = snprintf(&buf[n], len - n, "%s",
+ print_sensor(limit->type,
+ limit->last_val));
+ break;
+ case '3':
+ r = snprintf(&buf[n], len - n, "%s",
+ print_sensor(limit->type,
+ limit->lower));
+ break;
+ case '4':
+ r = snprintf(&buf[n], len - n, "%s",
+ print_sensor(limit->type,
+ limit->upper));
+ break;
+ default:
+ r = snprintf(&buf[n], len - n, "%%%c",
+ cmd[i]);
+ break;
+ }
+ if (r < 0 || (r >= len - n)) {
+ syslog(LOG_CRIT, "could not parse "
+ "command");
+ return;
+ }
+ if (r > 0)
+ n += r;
+ }
+ if (buf[0])
+ execute(buf);
+ }
+ }
+}
+
+static const char *drvstat[] = {
+ NULL, "empty", "ready", "powerup", "online", "idle", "active",
+ "rebuild", "powerdown", "fail", "pfail"
+};
+
+static char *
+print_sensor(enum sensor_type type, int64_t value)
+{
+ static char rfbuf[RFBUFCNT][RFBUFSIZ]; /* ring buffer */
+ static int idx;
+ char *fbuf;
+
+ fbuf = rfbuf[idx++];
+ if (idx == RFBUFCNT)
+ idx = 0;
+
+ switch (type) {
+ case SENSOR_TEMP:
+ snprintf(fbuf, RFBUFSIZ, "%.2f degC",
+ (value - 273150000) / 1000000.0);
+ break;
+ case SENSOR_FANRPM:
+ snprintf(fbuf, RFBUFSIZ, "%" PRIi64 " RPM", value);
+ break;
+ case SENSOR_VOLTS_DC:
+ snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
+ break;
+ case SENSOR_AMPS:
+ snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
+ break;
+ case SENSOR_WATTHOUR:
+ snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
+ break;
+ case SENSOR_AMPHOUR:
+ snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
+ break;
+ case SENSOR_INDICATOR:
+ snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
+ break;
+ case SENSOR_INTEGER:
+ snprintf(fbuf, RFBUFSIZ, "%" PRIi64, value);
+ break;
+ case SENSOR_PERCENT:
+ snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
+ break;
+ case SENSOR_LUX:
+ snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
+ break;
+ case SENSOR_DRIVE:
+ if ((uint64_t)value < sizeof(drvstat)/sizeof(drvstat[0])
+ && value > 0)
+ snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
+ else
+ snprintf(fbuf, RFBUFSIZ, "%" PRIi64 " ???", value);
+ break;
+ case SENSOR_TIMEDELTA:
+ snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
+ break;
+ default:
+ snprintf(fbuf, RFBUFSIZ, "%" PRIi64 " ???", value);
+ }
+
+ return (fbuf);
+}
+
+void
+parse_config(char *cf)
+{
+ struct sdlim_t *sdlim;
+ char **cfa;
+
+ if ((cfa = calloc(2, sizeof(char *))) == NULL)
+ err(1, "calloc");
+ cfa[0] = cf;
+ cfa[1] = NULL;
+
+ TAILQ_FOREACH(sdlim, &sdlims, entries)
+ parse_config_sdlim(sdlim, cfa);
+ free(cfa);
+}
+
+void
+parse_config_sdlim(struct sdlim_t *sdlim, char **cfa)
+{
+ struct limits_t *p;
+ char *buf = NULL, *ebuf = NULL;
+ char node[48];
+
+ TAILQ_FOREACH(p, &sdlim->limits, entries) {
+ snprintf(node, sizeof(node), "hw.sensors.%s.%s%d",
+ sdlim->dxname, sensor_type_s[p->type], p->numt);
+ p->flags = 0;
+ if (cgetent(&buf, cfa, node) != 0)
+ if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
+ continue;
+ if (cgetcap(buf, "istatus", ':'))
+ p->flags |= SENSORSD_L_ISTATUS;
+ if (cgetstr(buf, "low", &ebuf) < 0)
+ ebuf = NULL;
+ p->lower = get_val(ebuf, 0, p->type);
+ if (cgetstr(buf, "high", &ebuf) < 0)
+ ebuf = NULL;
+ p->upper = get_val(ebuf, 1, p->type);
+ if (cgetstr(buf, "command", &ebuf) < 0)
+ ebuf = NULL;
+ if (ebuf)
+ asprintf(&(p->command), "%s", ebuf);
+ free(buf);
+ buf = NULL;
+ if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
+ p->flags |= SENSORSD_L_USERLIMIT;
+ }
+}
+
+int64_t
+get_val(char *buf, int upper, enum sensor_type type)
+{
+ double val;
+ int64_t rval = 0;
+ char *p;
+
+ if (buf == NULL) {
+ if (upper)
+ return (LLONG_MAX);
+ else
+ return (LLONG_MIN);
+ }
+
+ val = strtod(buf, &p);
+ if (buf == p)
+ err(1, "incorrect value: %s", buf);
+
+ switch(type) {
+ case SENSOR_TEMP:
+ switch(*p) {
+ case 'C':
+ printf("C");
+ rval = (val + 273.16) * 1000 * 1000;
+ break;
+ case 'F':
+ printf("F");
+ rval = ((val - 32.0) / 9 * 5 + 273.16) * 1000 * 1000;
+ break;
+ default:
+ errx(1, "unknown unit %s for temp sensor", p);
+ }
+ break;
+ case SENSOR_FANRPM:
+ rval = val;
+ break;
+ case SENSOR_VOLTS_DC:
+ if (*p != 'V')
+ errx(1, "unknown unit %s for voltage sensor", p);
+ rval = val * 1000 * 1000;
+ break;
+ case SENSOR_PERCENT:
+ rval = val * 1000.0;
+ break;
+ case SENSOR_INDICATOR:
+ case SENSOR_INTEGER:
+ case SENSOR_DRIVE:
+ rval = val;
+ break;
+ case SENSOR_AMPS:
+ case SENSOR_WATTHOUR:
+ case SENSOR_AMPHOUR:
+ case SENSOR_LUX:
+ rval = val * 1000 * 1000;
+ break;
+ case SENSOR_TIMEDELTA:
+ rval = val * 1000 * 1000 * 1000;
+ break;
+ default:
+ errx(1, "unsupported sensor type");
+ /* not reached */
+ }
+ free(buf);
+ return (rval);
+}
+
+/* ARGSUSED */
+void
+reparse_cfg(int __unused signo)
+{
+ reload = 1;
+}
Index: usr.sbin/sensorsd/sensorsd.conf.5
===================================================================
--- /dev/null
+++ usr.sbin/sensorsd/sensorsd.conf.5
@@ -0,0 +1,186 @@
+.\" $FreeBSD$
+.\" $OpenBSD: sensorsd.conf.5,v 1.18 2007/08/14 17:10:02 cnst Exp $
+.\"
+.\" Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
+.\" Copyright (c) 2007 Constantine A. Murenin <cnst@FreeBSD.org>
+.\"
+.\" 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.
+.\"
+.Dd August 14, 2007
+.Dt SENSORSD.CONF 5
+.Os
+.Sh NAME
+.Nm sensorsd.conf
+.Nd configuration file for sensorsd
+.Sh DESCRIPTION
+The
+.Nm
+file is read by
+.Xr sensorsd 8
+to configure hardware sensor monitoring.
+Each sensor registered in the system
+is matched by at most one entry in
+.Nm ,
+which may specify high and low limits,
+and whether sensor status changes provided by the driver should be ignored.
+If the limits are crossed or if the status provided by the driver changes,
+.Xr sensorsd 8 's
+alert functionality is triggered and a command, if specified, is
+executed.
+.Pp
+.Nm
+follows the syntax of configuration databases as documented in
+.Xr getcap 3 .
+Sensors may be specified by their full
+.Va hw.sensors
+.Xr sysctl 8
+variable name or by type,
+with the full name taking precedence.
+For example, if an entry
+.Dq hw.sensors.lm0.temp1
+is not found, then an entry for
+.Dq temp
+will instead be looked for.
+.Pp
+The following attributes may be used:
+.Pp
+.Bl -tag -width "commandXX" -offset indent -compact
+.It Li command
+Specify a command to be executed on state change.
+.It Li high
+Specify an upper limit.
+.It Li low
+Specify a lower limit.
+.It Li istatus
+Ignore status provided by the driver.
+.El
+.Pp
+The values for temperature sensors can be given in degrees Celsius or
+Fahrenheit, for voltage sensors in volts, and fan speed sensors take a
+unit-less number representing RPM.
+Values for all other types of sensors can be specified
+in the same units as they appear under the
+.Xr sysctl 8
+.Va hw.sensors
+tree.
+.Pp
+Sensors that provide status (such as those from
+.Xr bio 4 ,
+.Xr esm 4 ,
+or
+.Xr ipmi 4 )
+do not require boundary values specified
+and simply trigger on status transitions.
+If boundaries are specified nonetheless,
+then they are used in addition to automatic status monitoring,
+unless the
+.Dq istatus
+attribute is specified to ignore status values that are provided by the drivers.
+.Pp
+The command is executed when there is any change in sensor state.
+Tokens in the command are substituted as follows:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It %x
+the xname of the device the sensor sits on
+.It %t
+the type of sensor
+.It %n
+the sensor number
+.It %2
+the sensor's current value
+.It %3
+the sensor's low limit
+.It %4
+the sensor's high limit
+.El
+.Pp
+By default,
+.Xr sensorsd 8
+monitors status changes on all sensors that keep their state.
+This behaviour may be altered by using the
+.Dq istatus
+attribute to ignore
+status changes of sensors of a certain type
+or individual sensors.
+.Sh FILES
+.Bl -tag -width "/etc/sensorsd.conf"
+.It /etc/sensorsd.conf
+Configuration file for
+.Xr sensorsd 8 .
+.El
+.Sh EXAMPLES
+In the following configuration file,
+if hw.sensors.ipmi0.temp0 transitions 80C or
+if its status as provided by
+.Xr ipmi 4
+changes, the command
+.Pa /etc/sensorsd/log_warning
+will be executed,
+with the sensor type, number and current value passed to it.
+Alerts will be sent
+if hw.sensors.lm0.volt3 transitions to being within or outside
+a range of 4.8V and 5.2V;
+if the speed of the fan attached to hw.sensors.lm0.fan1
+transitions to being below or above 1000RPM;
+if any RAID volume drive
+changes its status from, for example,
+.Dq OK ,
+such as in the case of drive failure, rebuild, or a complete failure,
+the command
+.Pa /etc/sensorsd/drive
+will be executed, with the sensor number passed to it; however,
+no alerts will be generated for status changes on timedelta sensors.
+For all other sensors whose drivers automatically provide
+sensor status updates, alerts will be generated
+each time those sensors undergo status transitions.
+.Bd -literal -offset indent
+# Comments are allowed
+hw.sensors.ipmi0.temp0:high=80C:command=/etc/sensorsd/log_warning %t %n %2
+hw.sensors.lm0.volt3:low=4.8V:high=5.2V
+hw.sensors.lm0.fan1:low=1000
+drive:command=/etc/sensorsd/drive %n
+timedelta:istatus #ignore status changes for timedelta
+.Ed
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr bio 4 ,
+.Xr esm 4 ,
+.Xr ipmi 4 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 3.5 .
+The format was altered in
+.Ox 4.1
+to accommodate hierarchical device-based sensor addressing.
+The
+.Dq istatus
+attribute was introduced in
+.Ox 4.2 .
+.Sh CAVEATS
+Alert functionality is triggered every time there is a change in sensor state;
+for example, when
+.Xr sensorsd 8
+is started,
+the status of each monitored sensor changes
+from undefined to whatever it is.
+One must keep this in mind when using commands
+that may unconditionally perform adverse actions (e.g.\&
+.Xr shutdown 8 ) ,
+as they will be executed even when all sensors perform to specification.
+If this is undesirable, then a wrapper shell script should be used instead.

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 5, 4:10 AM (1 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29270577
Default Alt Text
D8911.diff (138 KB)

Event Timeline