diff --git a/etc/Makefile b/etc/Makefile index 6a2a4d3..9e7615c 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -17,7 +17,7 @@ BIN1= auth.conf \ phones profile protocols \ rc rc.bsdextended rc.firewall rc.initdiskless \ rc.sendmail rc.shutdown \ - rc.subr remote rpc services shells \ + rc.subr remote rpc sensorsd.conf services shells \ sysctl.conf syslog.conf termcap.small .if exists(${.CURDIR}/etc.${MACHINE}/ttys) diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 238c907..77bbc5d 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -43,6 +43,8 @@ kldxref_clobber="NO" # Overwrite old linker.hints at boot. 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 diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index 802d717..ff4899f 100755 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -32,7 +32,7 @@ FILES= DAEMON FILESYSTEMS LOGIN NETWORKING SERVERS \ random rarpd resolv rfcomm_pppd_server root \ route6d routed routing rpcbind rtadvd rtsold rwho \ savecore sdpd securelevel sendmail \ - serial sppp statd static_arp stf swap1 \ + sensorsd serial sppp statd static_arp stf swap1 \ syscons sysctl syslogd \ timed tmp \ ugidfw \ diff --git a/etc/rc.d/sensorsd b/etc/rc.d/sensorsd new file mode 100644 index 0000000..51bd970 --- /dev/null +++ b/etc/rc.d/sensorsd @@ -0,0 +1,17 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: sensorsd +# REQUIRE: syslogd +# + +. /etc/rc.subr + +name="sensorsd" +rcvar=`set_rcvar` +command="/usr/sbin/${name}" + +load_rc_config $name +run_rc_command "$1" diff --git a/etc/sensorsd.conf b/etc/sensorsd.conf new file mode 100644 index 0000000..3b41b28 --- /dev/null +++ b/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 diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index 9eba624..d8b3ecc 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -285,6 +285,7 @@ privilege may change the value. .It "HW_FLOATINGPT integer no" .It "HW_MACHINE_ARCH string no" .It "HW_REALMEM integer no" +.It "HW_SENSORS node not applicable" .El .Bl -tag -width 6n .It Li HW_MACHINE @@ -309,6 +310,34 @@ Nonzero if the floating point support is in hardware. 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 diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8 index 1f5c8e6..626489a 100644 --- a/sbin/sysctl/sysctl.8 +++ b/sbin/sysctl/sysctl.8 @@ -220,6 +220,7 @@ denote .It "hw.floatingpoint integer no .It "hw.machine_arch string no .It "hw.realmem integer no +.It "hw.sensors.. struct no .It "machdep.console_device dev_t no .It "machdep.adjkerntz integer yes .It "machdep.disable_rtc_set integer yes diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index d96450b..93428bc 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -44,6 +44,7 @@ static const char rcsid[] = #include #include #include +#include #include #include #include @@ -420,6 +421,143 @@ S_vmtotal(int l2, void *p) } static int +S_sensor(int l2, void *p) +{ + struct sensor *s = (struct sensor *)p; + + if (l2 != sizeof(*s)) { + warnx("S_sensor %d != %lu", l2, (unsigned long)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(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 T_dev_t(int l2, void *p) { dev_t *d = (dev_t *)p; @@ -686,6 +824,8 @@ show_var(int *oid, int nlen) func = S_loadavg; else if (strcmp(fmt, "S,vmtotal") == 0) func = S_vmtotal; + else if (strcmp(fmt, "S,sensor") == 0) + func = S_sensor; else if (strcmp(fmt, "T,dev_t") == 0) func = T_dev_t; else diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 014be50..59947ca 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -164,6 +164,7 @@ MAN= aac.4 \ iscsi_initiator.4 \ isp.4 \ ispfw.4 \ + ${_it.4} \ iwi.4 \ iwifw.4 \ iwn.4 \ @@ -184,6 +185,7 @@ MAN= aac.4 \ lge.4 \ ${_lindev.4} \ ${_linux.4} \ + lm.4 \ lmc.4 \ lo.4 \ lp.4 \ @@ -652,6 +654,7 @@ _if_urtw.4= if_urtw.4 _if_wpi.4= if_wpi.4 _ipmi.4= ipmi.4 _io.4= io.4 +_it.4= it.4 _lindev.4= lindev.4 _linux.4= linux.4 _ndis.4= ndis.4 diff --git a/share/man/man4/coretemp.4 b/share/man/man4/coretemp.4 index ce4eb36..a9ac4b8 100644 --- a/share/man/man4/coretemp.4 +++ b/share/man/man4/coretemp.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 23, 2007 +.Dd September 13, 2007 .Dt CORETEMP 4 .Os .Sh NAME @@ -51,12 +51,20 @@ The driver provides support for the on-die digital thermal sensor present in Intel Core and newer CPUs. .Pp -The -.Nm -driver reports each core's temperature through a sysctl node in the -corresponding CPU device's sysctl tree, named -.Va dev.cpu.%d.temperature . +The values are exposed through the +.Va HW_SENSORS +.Xr sysctl 3 +tree. +For example: +.Bd -literal -offset indent +%sysctl hw.sensors +hw.sensors.cpu0.temp0: 28.00 degC +hw.sensors.cpu1.temp0: 29.00 degC +.Ed .Sh SEE ALSO +.Xr systat 1 , +.Xr sysctl 3 , +.Xr sensorsd 8 , .Xr sysctl 8 .Sh HISTORY The diff --git a/share/man/man4/it.4 b/share/man/man4/it.4 new file mode 100644 index 0000000..6b65628 --- /dev/null +++ b/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. diff --git a/share/man/man4/lm.4 b/share/man/man4/lm.4 new file mode 100644 index 0000000..1c731b4 --- /dev/null +++ b/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. diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5 index 734e7a3..38d39f5 100644 --- a/share/man/man5/rc.conf.5 +++ b/share/man/man5/rc.conf.5 @@ -242,6 +242,22 @@ is set to 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 @@ -4349,6 +4365,7 @@ on your system to find this information. .Xr rwhod 8 , .Xr savecore 8 , .Xr sdpd 8 , +.Xr sensorsd 8 , .Xr sshd 8 , .Xr swapon 8 , .Xr sysctl 8 , diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 4e8b0ce..301bc12 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -228,6 +228,7 @@ MAN= accept_filter.9 \ securelevel_gt.9 \ selrecord.9 \ sema.9 \ + sensor_attach.9 \ sf_buf.9 \ sglist.9 \ signal.9 \ @@ -1045,6 +1046,13 @@ MLINKS+=sema.9 sema_destroy.9 \ 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 \ diff --git a/share/man/man9/sensor_attach.9 b/share/man/man9/sensor_attach.9 new file mode 100644 index 0000000..ce4bb20 --- /dev/null +++ b/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. diff --git a/sys/amd64/conf/GENERIC.hints b/sys/amd64/conf/GENERIC.hints index 731124d..3ae9e0d 100644 --- a/sys/amd64/conf/GENERIC.hints +++ b/sys/amd64/conf/GENERIC.hints @@ -27,3 +27,11 @@ hint.ppc.0.irq="7" hint.atrtc.0.at="isa" hint.atrtc.0.port="0x70" hint.atrtc.0.irq="8" +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" diff --git a/sys/conf/files b/sys/conf/files index 12f5e4d..6847ed0 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1222,6 +1222,8 @@ dev/le/if_le_pci.c optional le pci 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 @@ -2096,6 +2098,7 @@ kern/kern_rmlock.c standard kern/kern_rwlock.c standard kern/kern_sdt.c optional kdtrace_hooks kern/kern_sema.c standard +kern/kern_sensors.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_switch.c standard diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index c0d5d70..57b6e07 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -183,6 +183,7 @@ dev/ipmi/ipmi_smbios.c optional ipmi 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/fdc/fdc.c optional fdc dev/fdc/fdc_acpi.c optional fdc dev/fdc/fdc_isa.c optional fdc isa diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 6b13071..facd570 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -201,6 +201,7 @@ dev/ipmi/ipmi_smbios.c optional ipmi 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/kbd/kbd.c optional atkbd | sc | ukbd dev/le/if_le_isa.c optional le isa dev/lindev/full.c optional lindev diff --git a/sys/dev/coretemp/coretemp.c b/sys/dev/coretemp/coretemp.c index 0226c8f..f1f994f 100644 --- a/sys/dev/coretemp/coretemp.c +++ b/sys/dev/coretemp/coretemp.c @@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include +#include #include /* for curthread */ #include @@ -50,10 +50,13 @@ __FBSDID("$FreeBSD$"); #define TZ_ZEROC 2732 +extern int smp_cpus; + struct coretemp_softc { - device_t sc_dev; - int sc_tjmax; - struct sysctl_oid *sc_oid; + struct ksensordev sc_sensordev; + struct ksensor sc_sensor; + device_t sc_dev; + int sc_tjmax; }; /* @@ -65,7 +68,7 @@ static int coretemp_attach(device_t dev); static int coretemp_detach(device_t dev); static int coretemp_get_temp(device_t dev); -static int coretemp_get_temp_sysctl(SYSCTL_HANDLER_ARGS); +static void coretemp_refresh(void *arg); static device_method_t coretemp_methods[] = { /* Device interface */ @@ -189,14 +192,17 @@ coretemp_attach(device_t dev) } /* - * Add the "temperature" MIB to dev.cpu.N. + * Add hw.sensors.cpuN.temp0 MIB. */ - sc->sc_oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(pdev), - SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), - OID_AUTO, "temperature", - CTLTYPE_INT | CTLFLAG_RD, - dev, 0, coretemp_get_temp_sysctl, "IK", - "Current temperature"); + strlcpy(sc->sc_sensordev.xname, device_get_nameunit(pdev), + sizeof(sc->sc_sensordev.xname)); + sc->sc_sensor.type = SENSOR_TEMP; + sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); + if (sensor_task_register(sc, coretemp_refresh, 2)) { + device_printf(dev, "unable to register update task\n"); + return (ENXIO); + } + sensordev_install(&sc->sc_sensordev); return (0); } @@ -206,7 +212,8 @@ coretemp_detach(device_t dev) { struct coretemp_softc *sc = device_get_softc(dev); - sysctl_remove_oid(sc->sc_oid, 1, 0); + sensordev_deinstall(&sc->sc_sensordev); + sensor_task_unregister(sc); return (0); } @@ -221,25 +228,21 @@ coretemp_get_temp(device_t dev) struct coretemp_softc *sc = device_get_softc(dev); char stemp[16]; - thread_lock(curthread); - sched_bind(curthread, cpu); - thread_unlock(curthread); - /* - * The digital temperature reading is located at bit 16 - * of MSR_THERM_STATUS. - * - * There is a bit on that MSR that indicates whether the - * temperature is valid or not. - * - * The temperature is computed by subtracting the temperature - * reading by Tj(max). + * Bind to specific CPU to read the correct temperature. + * If not all CPUs are initialised, then only read from + * cpu0, returning -1 on all other CPUs. */ - msr = rdmsr(MSR_THERM_STATUS); - - thread_lock(curthread); - sched_unbind(curthread); - thread_unlock(curthread); + if (smp_cpus > 1) { + thread_lock(curthread); + sched_bind(curthread, cpu); + msr = rdmsr(MSR_THERM_STATUS); + sched_unbind(curthread); + thread_unlock(curthread); + } else if (cpu != 0) + return (-1); + else + msr = rdmsr(MSR_THERM_STATUS); /* * Check for Thermal Status and Thermal Status Log. @@ -279,13 +282,21 @@ coretemp_get_temp(device_t dev) return (temp); } -static int -coretemp_get_temp_sysctl(SYSCTL_HANDLER_ARGS) +static void +coretemp_refresh(void *arg) { - device_t dev = (device_t) arg1; + struct coretemp_softc *sc = arg; + device_t dev = sc->sc_dev; + struct ksensor *s = &sc->sc_sensor; int temp; temp = coretemp_get_temp(dev) * 10 + TZ_ZEROC; - return (sysctl_handle_int(oidp, &temp, 0, req)); + if (temp == -1) { + s->flags |= SENSOR_FINVALID; + s->value = 0; + } else { + s->flags &= ~SENSOR_FINVALID; + s->value = temp * 1000000 + 273150000; + } } diff --git a/sys/dev/it/it.c b/sys/dev/it/it.c new file mode 100644 index 0000000..3ccf328 --- /dev/null +++ b/sys/dev/it/it.c @@ -0,0 +1,371 @@ +/* $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 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) { /* XXX perhaps >= ? */ + /* 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; + } + + it_setup_fan(sc, 0, 3); + it_setup_volt(sc, 3, 9); + it_setup_temp(sc, 12, 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; + + for (i = 0; i < 3; i++) { + sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i); + /* Convert temperature to Fahrenheit degres */ + 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)); + /* voltage returned as (mV >> 4) */ + sensors[i].value = (sdata << 4); + /* these two values are negative and formula is different */ + if (i == 5 || i == 6) + sensors[i].value = ((sdata << 4) - 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; + } +} + +/* + * 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 */ + it_generic_stemp(sc, &sc->sensors[12]); + it_generic_svolt(sc, &sc->sensors[3]); + if (sc->fan16bit) + it_16bit_fanrpm(sc, &sc->sensors[0]); + else + it_generic_fanrpm(sc, &sc->sensors[0]); +} + +static void +it_refresh(void *arg) +{ + struct it_softc *sc = (struct it_softc *)arg; + + it_refresh_sensor_data(sc); +} diff --git a/sys/dev/it/itvar.h b/sys/dev/it/itvar.h new file mode 100644 index 0000000..d50f6e3 --- /dev/null +++ b/sys/dev/it/itvar.h @@ -0,0 +1,95 @@ +/* $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 15 + +/* 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_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 diff --git a/sys/dev/lm/lm78.c b/sys/dev/lm/lm78.c new file mode 100644 index 0000000..311a789 --- /dev/null +++ b/sys/dev/lm/lm78.c @@ -0,0 +1,909 @@ +/* $FreeBSD$ */ +/* $OpenBSD: lm78.c,v 1.18 2007/05/26 22:47:39 cnst 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 + +#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 *, 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_w83792d_refresh_fanrpm(struct lm_softc *, int); + +void as_refresh_temp(struct lm_softc *, int); + +struct lm_chip { + int (*chip_match)(struct lm_softc *); +}; + +struct lm_chip lm_chips[] = { + { wb_match }, + { lm_match }, + { def_match } /* Must be last */ +}; + +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 } +}; + +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. + */ +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 + */ +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 } +}; + +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 } +}; + +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. + */ +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 } +}; + +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 } +}; + +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 } +}; + +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 } +}; + +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 } +}; + +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); + + 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: + cdesc = "W83627EHF"; + lm_setup_sensors(sc, w83627ehf_sensors); + break; + case WB_CHIPID_W83627DHG: + 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, struct lm_sensor *sensors) +{ + int i; + + for (i = 0; sensors[i].desc; i++) { + sc->sensors[i].type = sensors[i].type; + strlcpy(sc->sensors[i].desc, sensors[i].desc, + sizeof(sc->sensors[i].desc)); + sc->numsensors++; + } + 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_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; + } +} diff --git a/sys/dev/lm/lm78_isa.c b/sys/dev/lm/lm78_isa.c new file mode 100644 index 0000000..7449d00 --- /dev/null +++ b/sys/dev/lm/lm78_isa.c @@ -0,0 +1,251 @@ +/* $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); + 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); +} diff --git a/sys/dev/lm/lm78var.h b/sys/dev/lm/lm78var.h new file mode 100644 index 0000000..1b0ce32 --- /dev/null +++ b/sys/dev/lm/lm78var.h @@ -0,0 +1,158 @@ +/* $FreeBSD$ */ +/* $OpenBSD: lm78var.h,v 1.12 2007/05/25 02:26:43 cnst 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 accomodate slow + * running fans. To accomodate 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_W83627THF 0x90 +#define WB_CHIPID_W83627EHF 0xa1 +#define WB_CHIPID_W83627DHG 0xc1 + +/* Config bits */ +#define WB_CONFIG_VMR9 0x01 + +/* Reference voltage (mV) */ +#define WB_VREF 3600 +#define WB_W83627EHF_VREF 2048 + +#define WB_MAX_SENSORS 19 + +struct lm_softc; + +struct lm_sensor { + 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 ksensor sensors[WB_MAX_SENSORS]; + struct ksensordev sensordev; + 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 vrm9; +}; + +void lm_probe(struct lm_softc *); +void lm_attach(struct lm_softc *); +int lm_detach(struct lm_softc *); diff --git a/sys/i386/conf/GENERIC.hints b/sys/i386/conf/GENERIC.hints index 8e37ff1..4e95fd8 100644 --- a/sys/i386/conf/GENERIC.hints +++ b/sys/i386/conf/GENERIC.hints @@ -35,3 +35,11 @@ hint.ppc.0.irq="7" hint.atrtc.0.at="isa" hint.atrtc.0.port="0x70" hint.atrtc.0.irq="8" +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" diff --git a/sys/kern/kern_sensors.c b/sys/kern/kern_sensors.c new file mode 100644 index 0000000..5252427 --- /dev/null +++ b/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)); +} diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 8899140..742658d 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -139,6 +139,7 @@ SUBDIR= ${_3dfx} \ iscsi \ isp \ ispfw \ + ${_it} \ ${_iwi} \ ${_iwifw} \ ${_iwn} \ @@ -160,6 +161,7 @@ SUBDIR= ${_3dfx} \ ${_linprocfs} \ ${_linsysfs} \ ${_linux} \ + ${_lm} \ lmc \ lpt \ mac_biba \ @@ -462,12 +464,14 @@ _ipmi= ipmi _ips= ips _ipw= ipw _ipwfw= ipwfw +_it= it _iwi= iwi _iwifw= iwifw _iwn= iwn _iwnfw= iwnfw _ixgb= ixgb _ixgbe= ixgbe +_lm= lm _mly= mly _nfe= nfe _nve= nve @@ -533,6 +537,7 @@ _ipmi= ipmi _ips= ips _ipw= ipw _ipwfw= ipwfw +_it= it _iwi= iwi _iwifw= iwifw _iwn= iwn @@ -543,6 +548,7 @@ _lindev= lindev _linprocfs= linprocfs _linsysfs= linsysfs _linux= linux +_lm= lm _mly= mly _ndis= ndis _nfe= nfe diff --git a/sys/modules/it/Makefile b/sys/modules/it/Makefile new file mode 100644 index 0000000..f2f064b --- /dev/null +++ b/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 diff --git a/sys/modules/lm/Makefile b/sys/modules/lm/Makefile new file mode 100644 index 0000000..cb0791a --- /dev/null +++ b/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 diff --git a/sys/sys/sensors.h b/sys/sys/sensors.h new file mode 100644 index 0000000..15306ad --- /dev/null +++ b/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_ */ diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index aa7b8df..5588357 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -515,7 +515,8 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); #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_MAXID 13 /* number of valid hw ids */ +#define HW_SENSORS 13 /* node: hardware monitors */ +#define HW_MAXID 14 /* number of valid hw ids */ #define CTL_HW_NAMES { \ { 0, 0 }, \ @@ -531,6 +532,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); { "floatingpoint", CTLTYPE_INT }, \ { "machine_arch", CTLTYPE_STRING }, \ { "realmem", CTLTYPE_ULONG }, \ + { "sensors", CTLTYPE_NODE}, \ } /* diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile index 6a7e53d..640c388 100644 --- a/usr.bin/systat/Makefile +++ b/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 \ - mbufs.c netcmds.c netstat.c pigs.c swap.c icmp.c \ + mbufs.c netcmds.c netstat.c pigs.c sensors.c swap.c icmp.c \ mode.c ip.c tcp.c \ vmstat.c convtbl.c ifcmds.c ifstat.c diff --git a/usr.bin/systat/cmdtab.c b/usr.bin/systat/cmdtab.c index a8daa13..d2e6190 100644 --- a/usr.bin/systat/cmdtab.c +++ b/usr.bin/systat/cmdtab.c @@ -82,6 +82,9 @@ struct cmdtab cmdtab[] = { { "ifstat", showifstat, fetchifstat, labelifstat, initifstat, openifstat, closeifstat, cmdifstat, 0, CF_LOADAV }, + { "sensors", showsensors, fetchsensors, labelsensors, + initsensors, opensensors, closesensors, 0, + 0, CF_LOADAV }, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0 } }; struct cmdtab *curcmd = &cmdtab[0]; diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h index 84af89c..29220c0 100644 --- a/usr.bin/systat/extern.h +++ b/usr.bin/systat/extern.h @@ -82,6 +82,7 @@ void closekre(WINDOW *); void closembufs(WINDOW *); void closenetstat(WINDOW *); void closepigs(WINDOW *); +void closesensors(WINDOW *); void closeswap(WINDOW *); void closetcp(WINDOW *); int cmdifstat(const char *, const char *); @@ -105,6 +106,7 @@ void fetchkre(void); void fetchmbufs(void); void fetchnetstat(void); void fetchpigs(void); +void fetchsensors(void); void fetchswap(void); void fetchtcp(void); void getsysctl(const char *, void *, size_t); @@ -119,6 +121,7 @@ int initkre(void); int initmbufs(void); int initnetstat(void); int initpigs(void); +int initsensors(void); int initswap(void); int inittcp(void); int keyboard(void); @@ -134,6 +137,7 @@ void labelmbufs(void); void labelnetstat(void); void labelpigs(void); void labels(void); +void labelsensors(void); void labelswap(void); void labeltcp(void); void load(void); @@ -149,6 +153,7 @@ WINDOW *openkre(void); WINDOW *openmbufs(void); WINDOW *opennetstat(void); WINDOW *openpigs(void); +WINDOW *opensensors(void); WINDOW *openswap(void); WINDOW *opentcp(void); int prefix(const char *, const char *); @@ -167,6 +172,7 @@ void showkre(void); void showmbufs(void); void shownetstat(void); void showpigs(void); +void showsensors(void); void showswap(void); void showtcp(void); void status(void); diff --git a/usr.bin/systat/sensors.c b/usr.bin/systat/sensors.c new file mode 100644 index 0000000..8af57c2 --- /dev/null +++ b/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; +} diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1 index 9d392d9..4679d09 100644 --- a/usr.bin/systat/systat.1 +++ b/usr.bin/systat/systat.1 @@ -101,6 +101,7 @@ to be one of: .Ic mbufs , .Ic netstat , .Ic pigs , +.Ic sensors , .Ic swap , .Ic tcp , or @@ -286,6 +287,11 @@ Areas known to the kernel, but not in use are shown as not available. .It Ic mbufs Display, in the lower window, the number of mbufs allocated for particular uses, i.e., data, socket structures, etc. +.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, @@ -607,6 +613,7 @@ For port names. .Xr udp 4 , .Xr gstat 8 , .Xr iostat 8 , +.Xr sysctl 8 , .Xr vmstat 8 .Sh HISTORY The diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 045cb05..a697cf3 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -166,6 +166,7 @@ SUBDIR= ${_ac} \ ${_sa} \ ${_sade} \ ${_sendmail} \ + sensorsd \ service \ services_mkdb \ setfib \ diff --git a/usr.sbin/sensorsd/Makefile b/usr.sbin/sensorsd/Makefile new file mode 100644 index 0000000..153f542 --- /dev/null +++ b/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 diff --git a/usr.sbin/sensorsd/sensorsd.8 b/usr.sbin/sensorsd/sensorsd.8 new file mode 100644 index 0000000..a1f15d6 --- /dev/null +++ b/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. diff --git a/usr.sbin/sensorsd/sensorsd.c b/usr.sbin/sensorsd/sensorsd.c new file mode 100644 index 0000000..024e074 --- /dev/null +++ b/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); + +TAILQ_HEAD(, sdlim_t) sdlims = TAILQ_HEAD_INITIALIZER(sdlims); + +extern const char *__progname; +char *configfile; +volatile sig_atomic_t reload = 0; +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); + } + } +} + +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; +} diff --git a/usr.sbin/sensorsd/sensorsd.conf.5 b/usr.sbin/sensorsd/sensorsd.conf.5 new file mode 100644 index 0000000..96aa3d7 --- /dev/null +++ b/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.