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.. 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 #include #include +#include #include #include #include @@ -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 +.\" 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 +.\" Copyright (c) 2006 Constantine A. Murenin +.\" 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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +/* 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 + * + * 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 + * Copyright (c) 2006 Constantine A. Murenin + * Copyright (c) 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 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 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 + * Copyright (c) 2006 Constantine A. Murenin + * 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 +#ifndef NOSYSCTL8HACK + #include +#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 + * Copyright (c) 2003 Henning Brauer + * Copyright (c) 2006 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include + +#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 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 +.\" Copyright (c) 2005 Matthew Gream +.\" Copyright (c) 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. +.\" +.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 + * Copyright (c) 2005 Matthew Gream + * Copyright (c) 2006 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +.\" Copyright (c) 2005 Matthew Gream +.\" Copyright (c) 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. +.\" +.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.