Module Name: src
Committed By: brad
Date: Sat Nov 6 13:34:40 UTC 2021
Modified Files:
src/distrib/sets/lists/debug: module.mi
src/distrib/sets/lists/man: mi
src/distrib/sets/lists/modules: mi
src/etc: MAKEDEV.tmpl
src/share/man/man4: Makefile
src/sys/conf: majors
src/sys/dev/i2c: files.i2c
src/sys/modules: Makefile
Added Files:
src/share/man/man4: sht3xtemp.4
src/sys/dev/i2c: sht3x.c sht3xreg.h sht3xvar.h
src/sys/modules/sht3xtemp: Makefile sht3xtemp.ioconf
Log Message:
Driver for the Sensirion SHT30/SHT31/SHT35 temperature and humidity
sensor such as:
https://www.adafruit.com/product/2857
This is a higher priced sensor with a lot of features, including the
ability to do sub-second periodic updates. The driver supports
everything about the sensor except for the alert pin.
To generate a diff of this commit:
cvs rdiff -u -r1.14 -r1.15 src/distrib/sets/lists/debug/module.mi
cvs rdiff -u -r1.1728 -r1.1729 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.148 -r1.149 src/distrib/sets/lists/modules/mi
cvs rdiff -u -r1.226 -r1.227 src/etc/MAKEDEV.tmpl
cvs rdiff -u -r1.718 -r1.719 src/share/man/man4/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man4/sht3xtemp.4
cvs rdiff -u -r1.99 -r1.100 src/sys/conf/majors
cvs rdiff -u -r1.118 -r1.119 src/sys/dev/i2c/files.i2c
cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/sht3x.c src/sys/dev/i2c/sht3xreg.h \
src/sys/dev/i2c/sht3xvar.h
cvs rdiff -u -r1.259 -r1.260 src/sys/modules/Makefile
cvs rdiff -u -r0 -r1.1 src/sys/modules/sht3xtemp/Makefile \
src/sys/modules/sht3xtemp/sht3xtemp.ioconf
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/debug/module.mi
diff -u src/distrib/sets/lists/debug/module.mi:1.14 src/distrib/sets/lists/debug/module.mi:1.15
--- src/distrib/sets/lists/debug/module.mi:1.14 Thu Oct 14 13:54:46 2021
+++ src/distrib/sets/lists/debug/module.mi Sat Nov 6 13:34:40 2021
@@ -1,4 +1,4 @@
-# $NetBSD: module.mi,v 1.14 2021/10/14 13:54:46 brad Exp $
+# $NetBSD: module.mi,v 1.15 2021/11/06 13:34:40 brad Exp $
./usr/libdata/debug/@MODULEDIR@ modules-base-kernel kmod,debug
./usr/libdata/debug/@MODULEDIR@/accf_dataready modules-base-kernel kmod,debug
./usr/libdata/debug/@MODULEDIR@/accf_dataready/accf_dataready.kmod.debug modules-base-kernel kmod,debug
@@ -334,6 +334,8 @@
./usr/libdata/debug/@MODULEDIR@/sequencer/sequencer.kmod.debug modules-base-kernel kmod,debug
./usr/libdata/debug/@MODULEDIR@/sgp40mox modules-base-kernel kmod,debug
./usr/libdata/debug/@MODULEDIR@/sgp40mox/sgp40mox.kmod.debug modules-base-kernel kmod,debug
+./usr/libdata/debug/@MODULEDIR@/sht3xtemp modules-base-kernel kmod,debug
+./usr/libdata/debug/@MODULEDIR@/sht3xtemp/sht3xtemp.kmod.debug modules-base-kernel kmod,debug
./usr/libdata/debug/@MODULEDIR@/sht4xtemp modules-base-kernel kmod,debug
./usr/libdata/debug/@MODULEDIR@/sht4xtemp/sht4xtemp.kmod.debug modules-base-kernel kmod,debug
./usr/libdata/debug/@MODULEDIR@/si70xxtemp modules-base-kernel kmod,debug
Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1728 src/distrib/sets/lists/man/mi:1.1729
--- src/distrib/sets/lists/man/mi:1.1728 Thu Oct 14 13:54:46 2021
+++ src/distrib/sets/lists/man/mi Sat Nov 6 13:34:40 2021
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1728 2021/10/14 13:54:46 brad Exp $
+# $NetBSD: mi,v 1.1729 2021/11/06 13:34:40 brad Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -1725,6 +1725,7 @@
./usr/share/man/cat4/shb.0 man-sys-catman .cat
./usr/share/man/cat4/shmif.0 man-sys-catman .cat
./usr/share/man/cat4/shpcic.0 man-sys-catman .cat
+./usr/share/man/cat4/sht3xtemp.0 man-sys-catman .cat
./usr/share/man/cat4/sht4xtemp.0 man-sys-catman .cat
./usr/share/man/cat4/si.0 man-sys-catman .cat
./usr/share/man/cat4/si70xxtemp.0 man-sys-catman .cat
@@ -4901,6 +4902,7 @@
./usr/share/man/html4/shb.html man-sys-htmlman html
./usr/share/man/html4/shmif.html man-sys-htmlman html
./usr/share/man/html4/shpcic.html man-sys-htmlman html
+./usr/share/man/html4/sht3xtemp.html man-sys-htmlman html
./usr/share/man/html4/sht4xtemp.html man-sys-htmlman html
./usr/share/man/html4/si.html man-sys-htmlman html
./usr/share/man/html4/si70xxtemp.html man-sys-htmlman html
@@ -7983,6 +7985,7 @@
./usr/share/man/man4/shb.4 man-sys-man .man
./usr/share/man/man4/shmif.4 man-sys-man .man
./usr/share/man/man4/shpcic.4 man-sys-man .man
+./usr/share/man/man4/sht3xtemp.4 man-sys-man .man
./usr/share/man/man4/sht4xtemp.4 man-sys-man .man
./usr/share/man/man4/si.4 man-sys-man .man
./usr/share/man/man4/si70xxtemp.4 man-sys-man .man
Index: src/distrib/sets/lists/modules/mi
diff -u src/distrib/sets/lists/modules/mi:1.148 src/distrib/sets/lists/modules/mi:1.149
--- src/distrib/sets/lists/modules/mi:1.148 Thu Oct 14 13:54:46 2021
+++ src/distrib/sets/lists/modules/mi Sat Nov 6 13:34:40 2021
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.148 2021/10/14 13:54:46 brad Exp $
+# $NetBSD: mi,v 1.149 2021/11/06 13:34:40 brad Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -399,6 +399,8 @@
./@MODULEDIR@/sequencer/sequencer.kmod modules-base-kernel kmod
./@MODULEDIR@/sgp40mox modules-base-kernel kmod
./@MODULEDIR@/sgp40mox/sgp40mox.kmod modules-base-kernel kmod
+./@MODULEDIR@/sht3xtemp modules-base-kernel kmod
+./@MODULEDIR@/sht3xtemp/sht3xtemp.kmod modules-base-kernel kmod
./@MODULEDIR@/sht4xtemp modules-base-kernel kmod
./@MODULEDIR@/sht4xtemp/sht4xtemp.kmod modules-base-kernel kmod
./@MODULEDIR@/si70xxtemp modules-base-kernel kmod
Index: src/etc/MAKEDEV.tmpl
diff -u src/etc/MAKEDEV.tmpl:1.226 src/etc/MAKEDEV.tmpl:1.227
--- src/etc/MAKEDEV.tmpl:1.226 Tue Oct 12 17:19:20 2021
+++ src/etc/MAKEDEV.tmpl Sat Nov 6 13:34:40 2021
@@ -1,5 +1,5 @@
#!/bin/sh -
-# $NetBSD: MAKEDEV.tmpl,v 1.226 2021/10/12 17:19:20 christos Exp $
+# $NetBSD: MAKEDEV.tmpl,v 1.227 2021/11/06 13:34:40 brad Exp $
#
# Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc.
# All rights reserved.
@@ -270,6 +270,7 @@
# scsibus* SCSI busses
# se* SCSI Ethernet
# ses* SES/SAF-TE SCSI Devices
+# sht3xtemp* Sensirion SHT3X temperature and humidity device driver
# speaker PC speaker (XXX - installed)
# spi* SPI bus device
# sram battery backuped memory (x68k)
@@ -845,6 +846,7 @@ all)
makedev fw0 fw1 fw2 fw3
makedev ipmi0
makedev qemufwcfg
+ makedev sht3xtemp0
makedev local # do this last
;;
@@ -913,6 +915,10 @@ ramdisk)
makedev floppy md0
;;
+sht3xtemp)
+ makedev sht3xtemp0
+ ;;
+
usbs)
makedev usb usb0 usb1 usb2 usb3 usb4 usb5 usb6 usb7
makedev usb8 usb9 usb10 usb11 usb12 usb13 usb14 usb15
@@ -2250,6 +2256,11 @@ efi)
mkdev efi c %efi_chr% 0 660
;;
+sht3xtemp[0-9]*)
+ unit=${i#sht3xtemp}
+ mkdev sht3xtemp$unit c %sht3xtemp_chr% $unit 664
+ ;;
+
midevend)
%MI_DEVICES_END%
local)
Index: src/share/man/man4/Makefile
diff -u src/share/man/man4/Makefile:1.718 src/share/man/man4/Makefile:1.719
--- src/share/man/man4/Makefile:1.718 Thu Oct 14 13:54:45 2021
+++ src/share/man/man4/Makefile Sat Nov 6 13:34:39 2021
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.718 2021/10/14 13:54:45 brad Exp $
+# $NetBSD: Makefile,v 1.719 2021/11/06 13:34:39 brad Exp $
# @(#)Makefile 8.1 (Berkeley) 6/18/93
MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@@ -56,9 +56,9 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 a
rnd.4 route.4 rs5c372rtc.4 rtk.4 rtsx.4 rtw.4 rtwn.4 rum.4 run.4 \
s390rtc.4 satalink.4 sbus.4 schide.4 \
scsi.4 sctp.4 sd.4 se.4 seeprom.4 sem.4 \
- ses.4 sf.4 sfb.4 sgp40mox.4 sgsmix.4 shb.4 shmif.4 shpcic.4 sht4xtemp.4 \
- si70xxtemp.4 siisata.4 siop.4 sip.4 siside.4 sk.4 sl.4 slide.4 \
- sm.4 smscphy.4 smsh.4 sn.4 sony.4 spc.4 speaker.4 spif.4 sqphy.4 \
+ ses.4 sf.4 sfb.4 sgp40mox.4 sgsmix.4 shb.4 shmif.4 shpcic.4 sht3xtemp.4 \
+ sht4xtemp.4 si70xxtemp.4 siisata.4 siop.4 sip.4 siside.4 sk.4 sl.4 \
+ slide.4 sm.4 smscphy.4 smsh.4 sn.4 sony.4 spc.4 speaker.4 spif.4 sqphy.4 \
srt.4 ss.4 \
ssdfb.4 st.4 ste.4 stge.4 sti.4 stpcide.4 sv.4 \
svwsata.4 swsensor.4 swwdog.4 sysmon.4 \
Index: src/sys/conf/majors
diff -u src/sys/conf/majors:1.99 src/sys/conf/majors:1.100
--- src/sys/conf/majors:1.99 Sun Oct 10 13:03:09 2021
+++ src/sys/conf/majors Sat Nov 6 13:34:39 2021
@@ -1,4 +1,4 @@
-# $NetBSD: majors,v 1.99 2021/10/10 13:03:09 jmcneill Exp $
+# $NetBSD: majors,v 1.100 2021/11/06 13:34:39 brad Exp $
#
# Device majors for Machine-Independent drivers.
#
@@ -93,3 +93,4 @@ device-major wwanc char 358
device-major acpi char 359 acpi
device-major smbios char 360 smbios
device-major efi char 361 efi
+device-major sht3xtemp char 362 sht3xtemp
Index: src/sys/dev/i2c/files.i2c
diff -u src/sys/dev/i2c/files.i2c:1.118 src/sys/dev/i2c/files.i2c:1.119
--- src/sys/dev/i2c/files.i2c:1.118 Thu Oct 14 13:54:46 2021
+++ src/sys/dev/i2c/files.i2c Sat Nov 6 13:34:40 2021
@@ -1,4 +1,4 @@
-# $NetBSD: files.i2c,v 1.118 2021/10/14 13:54:46 brad Exp $
+# $NetBSD: files.i2c,v 1.119 2021/11/06 13:34:40 brad Exp $
obsolete defflag opt_i2cbus.h I2C_SCAN
define i2cbus { }
@@ -395,6 +395,11 @@ device cwfg: sysmon_envsys
attach cwfg at iic
file dev/i2c/cwfg.c cwfg
+# Sensirion SHT30/SHT31/SHT35 Temperature and Humidity sensor
+device sht3xtemp
+attach sht3xtemp at iic
+file dev/i2c/sht3x.c sht3xtemp
+
# Sensirion SHT40/SHT41/SHT45 Temperature and Humidity sensor
device sht4xtemp
attach sht4xtemp at iic
Index: src/sys/modules/Makefile
diff -u src/sys/modules/Makefile:1.259 src/sys/modules/Makefile:1.260
--- src/sys/modules/Makefile:1.259 Thu Oct 14 13:54:45 2021
+++ src/sys/modules/Makefile Sat Nov 6 13:34:39 2021
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.259 2021/10/14 13:54:45 brad Exp $
+# $NetBSD: Makefile,v 1.260 2021/11/06 13:34:39 brad Exp $
.include <bsd.own.mk>
@@ -69,6 +69,7 @@ SUBDIR+= hfs
SUBDIR+= hythygtemp
SUBDIR+= si70xxtemp
SUBDIR+= am2315temp
+SUBDIR+= sht3xtemp
SUBDIR+= sht4xtemp
SUBDIR+= sgp40mox
SUBDIR+= i2cexec
Added files:
Index: src/share/man/man4/sht3xtemp.4
diff -u /dev/null src/share/man/man4/sht3xtemp.4:1.1
--- /dev/null Sat Nov 6 13:34:40 2021
+++ src/share/man/man4/sht3xtemp.4 Sat Nov 6 13:34:39 2021
@@ -0,0 +1,119 @@
+.\" $NetBSD: sht3xtemp.4,v 1.1 2021/11/06 13:34:39 brad Exp $
+.\"
+.\" Copyright (c) 2021 Brad Spencer <[email protected]>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd October 31, 2021
+.Dt SHT3XTEMP 4
+.Os
+.Sh NAME
+.Nm sht3xtemp
+.Nd Driver for Sensirion SHT30/SHT31/SHT35 sensor chip via I2C bus
+.Sh SYNOPSIS
+.Cd "sht3xtemp* at iic? addr 0x44"
+.Cd "sht3xtemp* at iic? addr 0x45"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides measurements from the SHT30/SHT31/SHT35 humidity/temperature
+sensors via the
+.Xr envsys 4
+framework.
+The
+.Nm
+.Ar addr
+argument selects the address at the
+.Xr iic 4
+bus.
+The mode of operation, repeatability, heater controls, periodic update rate
+and crc validity can be changed through
+.Xr sysctl 8
+nodes.
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 3
+variables are provided:
+.Bl -tag -width indent
+.It Li hw.sht3xtemp0.modes
+Lists the modes supported by the driver and chip.
+.It Li hw.sht3xtemp0.mode
+Set the operation mode of the chip. The SHT3X chip can run in a
+single-shot measurement mode or a periodic update mode.
+Use one of the strings listed in
+.Li hw.sht3xtemp.modes .
+.It Li hw.sht3xtemp0.rates
+List the periodic update rates supported by the driver and chip.
+.It Li hw.sht3xtemp0.rate
+Set the periodic update rate when the mode of operation is set to
+periodic. The unit for this is measurements per second, or ART
+which is a mode that operates at an update rate of 4Hz higher
+response time.
+Use one of the strings listed in
+.Li hw.sht3xtemp.rates .
+.Pp
+Since it is possible to have subsecond periodic updates from the
+chip if so desired a device file is provided that can be used to
+get the raw temperature and humidity values outside of the
+.Xr envsys 4
+framework.
+The structure of this output is the raw temperature plus an 8-bit CRC
+followed by the raw humidity plus an 8-bit CRC.
+.It Li hw.sht3xtemp0.repeatabilities
+List the valid values for the repeatability used for a measurement.
+.It Li hw.sht3xtemp0.repeatability
+Set the repeatability for the measurement. The higher the repeatability
+the longer the measurement will take and the more power used.
+Use one of the strings listed in
+.Li hw.sht3xtemp.repeatabilities .
+.It Li hw.sht3xtemp0.ignorecrc
+If set, the crc calculation for %RH and temperature in the measurement phrase
+will be ignored.
+.It Li hw.sht3xtemp0.heateron
+Turn the heater on and off.
+.It Li hw.sht3xtemp0.debug
+If the driver is compiled with
+.Dv SHT3X_DEBUG ,
+this node will appear and can be used to set the debugging level.
+.It Li hw.sht3xtemp0.readattempts
+To read %RH or temperature the chip requires that the command be sent,
+then a delay must be observed before a read can be done to get the values
+back.
+The delays are documented in the datasheet for the chip.
+The driver will attempt to read back the values readattempts number of
+times.
+The default is 10 which should be more than enough for most purposes.
+.El
+.Sh FILES
+.Bl -tag -width "/dev/sht3xtempu" -compact
+.It /dev/sht3xtemp Ns Ar u
+SHT3X device unit
+.Ar u
+file.
+.El
+.Sh SEE ALSO
+.Xr envsys 4 ,
+.Xr iic 4 ,
+.Xr envstat 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Nx 10.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Brad Spencer Aq Mt [email protected] .
Index: src/sys/dev/i2c/sht3x.c
diff -u /dev/null src/sys/dev/i2c/sht3x.c:1.1
--- /dev/null Sat Nov 6 13:34:40 2021
+++ src/sys/dev/i2c/sht3x.c Sat Nov 6 13:34:40 2021
@@ -0,0 +1,2111 @@
+/* $NetBSD: sht3x.c,v 1.1 2021/11/06 13:34:40 brad Exp $ */
+
+/*
+ * Copyright (c) 2021 Brad Spencer <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: sht3x.c,v 1.1 2021/11/06 13:34:40 brad Exp $");
+
+/*
+ Driver for the Sensirion SHT30/SHT31/SHT35
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/sysctl.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/kthread.h>
+#include <sys/pool.h>
+#include <sys/kmem.h>
+
+#include <dev/sysmon/sysmonvar.h>
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/sht3xreg.h>
+#include <dev/i2c/sht3xvar.h>
+
+
+static int sht3x_take_break(void *, bool);
+static int sht3x_get_status_register(void *, uint16_t *, bool);
+static int sht3x_clear_status_register(void *, bool);
+static uint8_t sht3x_crc(uint8_t *, size_t);
+static int sht3x_cmdr(struct sht3x_sc *, uint16_t, uint8_t *, size_t);
+static int sht3x_poke(i2c_tag_t, i2c_addr_t, bool);
+static int sht3x_match(device_t, cfdata_t, void *);
+static void sht3x_attach(device_t, device_t, void *);
+static int sht3x_detach(device_t, int);
+static void sht3x_refresh(struct sysmon_envsys *, envsys_data_t *);
+/* The chip that I had would not allow the limits to actually be set
+ * for reasons which are not obvious. The chip took the command just
+ * fine, but a read back of the limit registers showed that no change
+ * was made, so disable limits for now.
+ */
+#ifdef __did_not_work
+static void sht3x_get_limits(struct sysmon_envsys *, envsys_data_t *,
+ sysmon_envsys_lim_t *, uint32_t *);
+static void sht3x_set_limits(struct sysmon_envsys *, envsys_data_t *,
+ sysmon_envsys_lim_t *, uint32_t *);
+#endif
+static int sht3x_verify_sysctl(SYSCTLFN_ARGS);
+static int sht3x_verify_sysctl_heateron(SYSCTLFN_ARGS);
+static int sht3x_verify_sysctl_modes(SYSCTLFN_ARGS);
+static int sht3x_verify_sysctl_repeatability(SYSCTLFN_ARGS);
+static int sht3x_verify_sysctl_rate(SYSCTLFN_ARGS);
+static int sht3x_set_heater(struct sht3x_sc *);
+static void sht3x_thread(void *);
+static int sht3x_init_periodic_measurement(void *, int *);
+static void sht3x_take_periodic_measurement(void *);
+static void sht3x_start_thread(void *);
+static void sht3x_stop_thread(void *);
+static int sht3x_activate(device_t, enum devact);
+
+#define SHT3X_DEBUG
+#ifdef SHT3X_DEBUG
+#define DPRINTF(s, l, x) \
+ do { \
+ if (l <= s->sc_sht3xdebug) \
+ printf x; \
+ } while (/*CONSTCOND*/0)
+#else
+#define DPRINTF(s, l, x)
+#endif
+
+CFATTACH_DECL_NEW(sht3xtemp, sizeof(struct sht3x_sc),
+ sht3x_match, sht3x_attach, sht3x_detach, sht3x_activate);
+
+extern struct cfdriver sht3xtemp_cd;
+
+static dev_type_open(sht3xopen);
+static dev_type_read(sht3xread);
+static dev_type_close(sht3xclose);
+const struct cdevsw sht3x_cdevsw = {
+ .d_open = sht3xopen,
+ .d_close = sht3xclose,
+ .d_read = sht3xread,
+ .d_write = nowrite,
+ .d_ioctl = noioctl,
+ .d_stop = nostop,
+ .d_tty = notty,
+ .d_poll = nopoll,
+ .d_mmap = nommap,
+ .d_kqfilter = nokqfilter,
+ .d_discard = nodiscard,
+ .d_flag = D_OTHER
+};
+
+static struct sht3x_sensor sht3x_sensors[] = {
+ {
+ .desc = "humidity",
+ .type = ENVSYS_SRELHUMIDITY,
+ },
+ {
+ .desc = "temperature",
+ .type = ENVSYS_STEMP,
+ }
+};
+
+/* The typical delays are MOSTLY documented in the datasheet for the chip.
+ There is no need to be very accurate with these, just rough estimates
+ will work fine.
+*/
+
+static struct sht3x_timing sht3x_timings[] = {
+ {
+ .cmd = SHT3X_SOFT_RESET,
+ .typicaldelay = 3000,
+ },
+ {
+ .cmd = SHT3X_GET_STATUS_REGISTER,
+ .typicaldelay = 100,
+ },
+ {
+ .cmd = SHT3X_BREAK,
+ .typicaldelay = 100,
+ },
+ {
+ .cmd = SHT3X_CLEAR_STATUS_REGISTER,
+ .typicaldelay = 100,
+ },
+ {
+ .cmd = SHT3X_MEASURE_REPEATABILITY_CS_HIGH,
+ .typicaldelay = 15000,
+ },
+ {
+ .cmd = SHT3X_MEASURE_REPEATABILITY_CS_MEDIUM,
+ .typicaldelay = 6000,
+ },
+ {
+ .cmd = SHT3X_MEASURE_REPEATABILITY_CS_LOW,
+ .typicaldelay = 4000,
+ },
+ {
+ .cmd = SHT3X_MEASURE_REPEATABILITY_NOCS_HIGH,
+ .typicaldelay = 15000,
+ },
+ {
+ .cmd = SHT3X_MEASURE_REPEATABILITY_NOCS_MEDIUM,
+ .typicaldelay = 6000,
+ },
+ {
+ .cmd = SHT3X_MEASURE_REPEATABILITY_NOCS_LOW,
+ .typicaldelay = 4000,
+ },
+ {
+ .cmd = SHT3X_WRITE_HIGH_ALERT_SET,
+ .typicaldelay = 5000,
+ },
+ {
+ .cmd = SHT3X_WRITE_HIGH_ALERT_CLEAR,
+ .typicaldelay = 5000,
+ },
+ {
+ .cmd = SHT3X_WRITE_LOW_ALERT_SET,
+ .typicaldelay = 5000,
+ },
+ {
+ .cmd = SHT3X_WRITE_LOW_ALERT_CLEAR,
+ .typicaldelay = 5000,
+ }
+};
+
+/* In single shot mode, find the command */
+
+static struct sht3x_repeatability sht3x_repeatability_ss[] = {
+ {
+ .text = "high",
+ .cmd = SHT3X_MEASURE_REPEATABILITY_NOCS_HIGH,
+ },
+ {
+ .text = "medium",
+ .cmd = SHT3X_MEASURE_REPEATABILITY_NOCS_MEDIUM,
+ },
+ {
+ .text = "low",
+ .cmd = SHT3X_MEASURE_REPEATABILITY_NOCS_LOW,
+ }
+};
+
+
+/* For periodic, look at the repeatability and the rate.
+ * ART is a bit fake here, as the repeatability is not really
+ * used.
+ */
+
+static struct sht3x_periodic sht3x_periodic_rate[] = {
+ {
+ .repeatability = "high",
+ .rate = "0.5mps",
+ .sdelay = 1000,
+ .cmd = SHT3X_HALF_MPS_HIGH,
+ },
+ {
+ .repeatability = "medium",
+ .rate = "0.5mps",
+ .sdelay = 1000,
+ .cmd = SHT3X_HALF_MPS_MEDIUM,
+ },
+ {
+ .repeatability = "low",
+ .rate = "0.5mps",
+ .sdelay = 1000,
+ .cmd = SHT3X_HALF_MPS_LOW,
+ },
+ {
+ .repeatability = "high",
+ .rate = "1.0mps",
+ .sdelay = 500,
+ .cmd = SHT3X_ONE_MPS_HIGH,
+ },
+ {
+ .repeatability = "medium",
+ .rate = "1.0mps",
+ .sdelay = 500,
+ .cmd = SHT3X_ONE_MPS_MEDIUM,
+ },
+ {
+ .repeatability = "low",
+ .rate = "1.0mps",
+ .sdelay = 500,
+ .cmd = SHT3X_ONE_MPS_LOW,
+ },
+ {
+ .repeatability = "high",
+ .rate = "2.0mps",
+ .sdelay = 250,
+ .cmd = SHT3X_TWO_MPS_HIGH,
+ },
+ {
+ .repeatability = "medium",
+ .rate = "2.0mps",
+ .sdelay = 250,
+ .cmd = SHT3X_TWO_MPS_MEDIUM,
+ },
+ {
+ .repeatability = "low",
+ .rate = "2.0mps",
+ .sdelay = 250,
+ .cmd = SHT3X_TWO_MPS_LOW,
+ },
+ {
+ .repeatability = "high",
+ .rate = "4.0mps",
+ .sdelay = 100,
+ .cmd = SHT3X_FOUR_MPS_HIGH,
+ },
+ {
+ .repeatability = "medium",
+ .rate = "4.0mps",
+ .sdelay = 100,
+ .cmd = SHT3X_FOUR_MPS_MEDIUM,
+ },
+ {
+ .repeatability = "low",
+ .rate = "4.0mps",
+ .sdelay = 100,
+ .cmd = SHT3X_FOUR_MPS_LOW,
+ },
+ {
+ .repeatability = "high",
+ .rate = "10.0mps",
+ .sdelay = 50,
+ .cmd = SHT3X_TEN_MPS_HIGH,
+ },
+ {
+ .repeatability = "medium",
+ .rate = "10.0mps",
+ .sdelay = 50,
+ .cmd = SHT3X_FOUR_MPS_MEDIUM,
+ },
+ {
+ .repeatability = "low",
+ .rate = "10.0mps",
+ .sdelay = 50,
+ .cmd = SHT3X_FOUR_MPS_LOW,
+ },
+ {
+ .repeatability = "high",
+ .rate = "ART",
+ .sdelay = 100,
+ .cmd = SHT3X_ART_ENABLE,
+ },
+ {
+ .repeatability = "medium",
+ .rate = "ART",
+ .sdelay = 100,
+ .cmd = SHT3X_ART_ENABLE,
+ },
+ {
+ .repeatability = "low",
+ .rate = "ART",
+ .sdelay = 100,
+ .cmd = SHT3X_ART_ENABLE,
+ }
+};
+
+static const char sht3x_rate_names[] =
+ "0.5mps, 1.0mps, 2.0mps, 4.0mps, 10.0mps, ART";
+
+static const char sht3x_mode_names[] =
+ "single-shot, periodic";
+
+static const char sht3x_repeatability_names[] =
+ "high, medium, low";
+
+static int
+sht3x_take_break(void *aux, bool have_bus)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+ int error = 0;
+
+ if (! have_bus) {
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for breaking %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ }
+ error = sht3x_cmdr(sc, SHT3X_BREAK, NULL, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Error breaking: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+ out:
+ if (! have_bus) {
+ iic_release_bus(sc->sc_tag, 0);
+ }
+
+ sc->sc_isperiodic = false;
+ strlcpy(sc->sc_mode,"single-shot",SHT3X_MODE_NAME);
+
+ return error;
+}
+
+static int
+sht3x_get_status_register(void *aux, uint16_t *reg, bool have_bus)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+ uint8_t buf[3];
+ int error = 0;
+
+ if (! have_bus) {
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for getting status %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ }
+ error = sht3x_cmdr(sc, SHT3X_GET_STATUS_REGISTER, buf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Error getting status: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+ out:
+ if (! have_bus) {
+ iic_release_bus(sc->sc_tag, 0);
+ }
+
+ if (!error) {
+ uint8_t c;
+
+ c = sht3x_crc(&buf[0],2);
+ if (c == buf[2]) {
+ *reg = buf[0] << 8 | buf[1];
+ } else {
+ error = EINVAL;
+ }
+ }
+
+ return error;
+}
+
+static int
+sht3x_clear_status_register(void *aux, bool have_bus)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+ int error = 0;
+
+ if (! have_bus) {
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for clearing status %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ }
+ error = sht3x_cmdr(sc, SHT3X_CLEAR_STATUS_REGISTER, NULL, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Error clear status register: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+ out:
+ if (! have_bus) {
+ iic_release_bus(sc->sc_tag, 0);
+ }
+
+ return error;
+}
+
+void
+sht3x_thread(void *aux)
+{
+ struct sht3x_sc *sc = aux;
+ int error, rv;
+ int sdelay = 100;
+
+ mutex_enter(&sc->sc_threadmutex);
+
+ while (!sc->sc_stopping && !sc->sc_dying) {
+ if (sc->sc_initperiodic) {
+ error = sht3x_init_periodic_measurement(sc,&sdelay);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Error initing periodic measurement "
+ "in thread: %d\n", device_xname(sc->sc_dev), error));
+ }
+ sc->sc_initperiodic = false;
+ }
+ rv = cv_timedwait(&sc->sc_condvar, &sc->sc_threadmutex,
+ mstohz(sdelay));
+ if (rv == EWOULDBLOCK && !sc->sc_stopping && !sc->sc_initperiodic && !sc->sc_dying) {
+ sht3x_take_periodic_measurement(sc);
+ }
+ }
+ mutex_exit(&sc->sc_threadmutex);
+ kthread_exit(0);
+}
+
+int
+sht3x_init_periodic_measurement(void *aux, int *sdelay)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+ int i,error = 0;
+ uint16_t r = 0;
+
+ for (i = 0; i < __arraycount(sht3x_periodic_rate); i++) {
+ if (strncmp(sc->sc_repeatability,sht3x_periodic_rate[i].repeatability,SHT3X_REP_NAME) == 0 &&
+ strncmp(sc->sc_periodic_rate, sht3x_periodic_rate[i].rate,SHT3X_RATE_NAME) == 0) {
+ r = sht3x_periodic_rate[i].cmd;
+ *sdelay = sht3x_periodic_rate[i].sdelay;
+ break;
+ }
+ }
+
+ if (i == __arraycount(sht3x_periodic_rate)) {
+ error = 1;
+ *sdelay = 100;
+ }
+
+ DPRINTF(sc, 2, ("%s: Would init with: %x\n",
+ device_xname(sc->sc_dev), r));
+
+ if (error == 0) {
+ mutex_enter(&sc->sc_mutex);
+
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for initing: "
+ " %d\n", device_xname(sc->sc_dev), error));
+ } else {
+ error = sht3x_take_break(sc,true);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for initing: "
+ " %d\n", device_xname(sc->sc_dev), error));
+ }
+
+ error = sht3x_cmdr(sc, r, NULL, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Error sending periodic measurement command: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+ iic_release_bus(sc->sc_tag, 0);
+ sc->sc_isperiodic = true;
+ strlcpy(sc->sc_mode,"periodic",SHT3X_MODE_NAME);
+ }
+ mutex_exit(&sc->sc_mutex);
+ }
+
+ return error;
+}
+
+static void
+sht3x_take_periodic_measurement(void *aux)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+ int error = 0, data_error = 0;
+ uint8_t rawbuf[6];
+ struct sht3x_read_q *pp;
+
+ mutex_enter(&sc->sc_mutex);
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for getting "
+ "periodic data: %d\n", device_xname(sc->sc_dev), error));
+ } else {
+ uint16_t status_reg;
+
+ error = sht3x_get_status_register(sc, &status_reg, true);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Error getting status register periodic: %d\n",
+ device_xname(sc->sc_dev), error));
+ } else {
+ if (status_reg & SHT3X_RESET_DETECTED) {
+ aprint_error_dev(sc->sc_dev, "Reset detected in periodic mode. Heater may have been reset.\n");
+ delay(3000);
+ sht3x_take_break(sc,true);
+ sht3x_clear_status_register(sc, true);
+ sc->sc_heateron = status_reg & SHT3X_HEATER_STATUS;
+ sc->sc_initperiodic = true;
+ } else {
+ data_error = sht3x_cmdr(sc, SHT3X_PERIODIC_FETCH_DATA, rawbuf, 6);
+ /* EIO is actually expected if the poll interval is faster
+ * than the rate that the sensor is set to. Unfortunally,
+ * this will also mess with the ability to detect an actual problem
+ * with the sensor in periodic mode, so we do the best we can here.
+ */
+ if (data_error && data_error != EIO) {
+ DPRINTF(sc, 2, ("%s: Error sending periodic fetch command: %d\n",
+ device_xname(sc->sc_dev), data_error));
+ }
+ }
+ }
+ iic_release_bus(sc->sc_tag, 0);
+ /* If there was no errors from anything then the data should be
+ * valid.
+ */
+ if (!data_error && !error) {
+ DPRINTF(sc, 2, ("%s: Raw periodic: %x%x - %x -- %x%x - %x\n",
+ device_xname(sc->sc_dev), rawbuf[0], rawbuf[1], rawbuf[2],
+ rawbuf[3], rawbuf[4], rawbuf[5]));
+ memcpy(sc->sc_pbuffer,rawbuf,6);
+
+ if (sc->sc_opened) {
+ mutex_enter(&sc->sc_read_mutex);
+ pp = pool_cache_get(sc->sc_readpool,PR_NOWAIT);
+ if (pp != NULL) {
+ memcpy(pp->measurement,rawbuf,6);
+ DPRINTF(sc, 4, ("%s: Queue insert\n",device_xname(sc->sc_dev)));
+ SIMPLEQ_INSERT_HEAD(&sc->sc_read_queue,pp,read_q);
+ } else {
+ aprint_error("Could not allocate memory for pool read\n");
+ }
+ cv_signal(&sc->sc_condreadready);
+ mutex_exit(&sc->sc_read_mutex);
+ }
+
+ } else {
+ /* We are only going to worry about errors when it was not related
+ * to actually getting data. That is a likely indicator of a problem
+ * with the sensor.
+ */
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Raw periodic with error: %x%x - %x -- %x%x - %x -- %d\n",
+ device_xname(sc->sc_dev), rawbuf[0], rawbuf[1], rawbuf[2],
+ rawbuf[3], rawbuf[4], rawbuf[5], error));
+ uint8_t bad[6] = "dedbef";
+ memcpy(sc->sc_pbuffer, bad, 6);
+ }
+ }
+
+ }
+ mutex_exit(&sc->sc_mutex);
+}
+
+static void
+sht3x_stop_thread(void *aux)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+
+ if (!sc->sc_isperiodic) {
+ return;
+ }
+
+ mutex_enter(&sc->sc_threadmutex);
+ sc->sc_stopping = true;
+ cv_signal(&sc->sc_condvar);
+ mutex_exit(&sc->sc_threadmutex);
+
+ /* wait for the thread to exit */
+ kthread_join(sc->sc_thread);
+
+ mutex_enter(&sc->sc_mutex);
+ sht3x_take_break(sc,false);
+ mutex_exit(&sc->sc_mutex);
+}
+
+static void
+sht3x_start_thread(void *aux)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+ int error;
+
+ error = kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL,
+ sht3x_thread, sc, &sc->sc_thread, "%s", device_xname(sc->sc_dev));
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Unable to create measurement thread: %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+}
+
+int
+sht3x_verify_sysctl(SYSCTLFN_ARGS)
+{
+ int error, t;
+ struct sysctlnode node;
+
+ node = *rnode;
+ t = *(int *)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (t < 0)
+ return EINVAL;
+
+ *(int *)rnode->sysctl_data = t;
+
+ return 0;
+}
+
+int
+sht3x_verify_sysctl_heateron(SYSCTLFN_ARGS)
+{
+ int error;
+ bool t;
+ struct sht3x_sc *sc;
+ struct sysctlnode node;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ t = sc->sc_heateron;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ sc->sc_heateron = t;
+ error = sht3x_set_heater(sc);
+
+ return error;
+}
+
+static int
+sht3x_set_heater(struct sht3x_sc *sc)
+{
+ int error = 0;
+ uint16_t cmd;
+
+ mutex_enter(&sc->sc_mutex);
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s:%s: Failed to acquire bus: %d\n",
+ device_xname(sc->sc_dev), __func__, error));
+ return error;
+ }
+
+ if (sc->sc_heateron) {
+ cmd = SHT3X_HEATER_ENABLE;
+ } else {
+ cmd = SHT3X_HEATER_DISABLE;
+ }
+
+ error = sht3x_cmdr(sc, cmd, NULL, 0);
+
+ iic_release_bus(sc->sc_tag,0);
+ mutex_exit(&sc->sc_mutex);
+
+ return error;
+}
+
+int
+sht3x_verify_sysctl_modes(SYSCTLFN_ARGS)
+{
+ char buf[SHT3X_MODE_NAME];
+ struct sht3x_sc *sc;
+ struct sysctlnode node;
+ int error = 0;
+ bool is_ss = false;
+ bool is_periodic = false;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ (void) memcpy(buf, sc->sc_mode, SHT3X_MODE_NAME);
+ node.sysctl_data = buf;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (sc->sc_opened) {
+ return EINVAL;
+ }
+
+ is_ss = (strncmp(node.sysctl_data, "single-shot", SHT3X_MODE_NAME) == 0);
+ is_periodic = (strncmp(node.sysctl_data, "periodic", SHT3X_MODE_NAME) == 0);
+
+ if (is_ss || is_periodic) {
+ (void) memcpy(sc->sc_mode, node.sysctl_data, SHT3X_MODE_NAME);
+ } else {
+ error = EINVAL;
+ }
+
+ if (error == 0) {
+ if (is_ss) {
+ sht3x_stop_thread(sc);
+ sc->sc_stopping = false;
+ sc->sc_initperiodic = false;
+ sc->sc_isperiodic = false;
+ }
+ if (is_periodic) {
+ sc->sc_stopping = false;
+ sc->sc_initperiodic = true;
+ sc->sc_isperiodic = true;
+ sht3x_start_thread(sc);
+ }
+ }
+
+ return error;
+}
+
+int
+sht3x_verify_sysctl_repeatability(SYSCTLFN_ARGS)
+{
+ char buf[SHT3X_REP_NAME];
+ struct sht3x_sc *sc;
+ struct sysctlnode node;
+ int error = 0;
+ size_t i;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ (void) memcpy(buf, sc->sc_repeatability, SHT3X_REP_NAME);
+ node.sysctl_data = buf;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ for (i = 0; i < __arraycount(sht3x_repeatability_ss); i++) {
+ if (strncmp(node.sysctl_data, sht3x_repeatability_ss[i].text,
+ SHT3X_REP_NAME) == 0) {
+ break;
+ }
+ }
+
+ if (i == __arraycount(sht3x_repeatability_ss))
+ return EINVAL;
+ (void) memcpy(sc->sc_repeatability, node.sysctl_data, SHT3X_REP_NAME);
+
+ if (sc->sc_isperiodic) {
+ sc->sc_initperiodic = true;
+ }
+
+ return error;
+}
+
+int
+sht3x_verify_sysctl_rate(SYSCTLFN_ARGS)
+{
+ char buf[SHT3X_RATE_NAME];
+ struct sht3x_sc *sc;
+ struct sysctlnode node;
+ int error = 0;
+ size_t i;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+ (void) memcpy(buf, sc->sc_periodic_rate, SHT3X_RATE_NAME);
+ node.sysctl_data = buf;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ for (i = 0; i < __arraycount(sht3x_periodic_rate); i++) {
+ if (strncmp(node.sysctl_data, sht3x_periodic_rate[i].rate,
+ SHT3X_RATE_NAME) == 0) {
+ break;
+ }
+ }
+
+ if (i == __arraycount(sht3x_periodic_rate))
+ return EINVAL;
+ (void) memcpy(sc->sc_periodic_rate, node.sysctl_data, SHT3X_RATE_NAME);
+
+ if (sc->sc_isperiodic) {
+ sc->sc_initperiodic = true;
+ }
+
+ return error;
+}
+
+static int
+sht3x_cmddelay(uint16_t cmd)
+{
+ int r = -2;
+
+ for(int i = 0;i < __arraycount(sht3x_timings);i++) {
+ if (cmd == sht3x_timings[i].cmd) {
+ r = sht3x_timings[i].typicaldelay;
+ break;
+ }
+ }
+
+ if (r == -2) {
+ r = -1;
+ }
+
+ return r;
+}
+
+static int
+sht3x_cmd(i2c_tag_t tag, i2c_addr_t addr, uint16_t *cmd,
+ uint8_t clen, uint8_t *buf, size_t blen, int readattempts)
+{
+ int error;
+ int cmddelay;
+ uint8_t cmd8[2];
+
+ /* All commands are two bytes and must be in a proper order */
+ KASSERT(clen == 2);
+
+ cmd8[0] = cmd[0] >> 8;
+ cmd8[1] = cmd[0] & 0x00ff;
+
+ error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,&cmd8[0],clen,NULL,0,0);
+
+ if (error == 0) {
+ cmddelay = sht3x_cmddelay(cmd[0]);
+ if (cmddelay != -1) {
+ delay(cmddelay);
+ }
+
+ /* Not all commands return anything */
+ if (blen > 0) {
+ for (int aint = 0; aint < readattempts; aint++) {
+ error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,buf,blen,0);
+ if (error == 0)
+ break;
+ delay(1000);
+ }
+ }
+ }
+
+ return error;
+}
+
+static int
+sht3x_cmdr(struct sht3x_sc *sc, uint16_t cmd, uint8_t *buf, size_t blen)
+{
+ return sht3x_cmd(sc->sc_tag, sc->sc_addr, &cmd, 2, buf, blen, sc->sc_readattempts);
+}
+
+static uint8_t
+sht3x_crc(uint8_t * data, size_t size)
+{
+ uint8_t crc = 0xFF;
+
+ for (size_t i = 0; i < size; i++) {
+ crc ^= data[i];
+ for (size_t j = 8; j > 0; j--) {
+ if (crc & 0x80)
+ crc = (crc << 1) ^ 0x31;
+ else
+ crc <<= 1;
+ }
+ }
+ return crc;
+}
+
+static int
+sht3x_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
+{
+ uint16_t reg = SHT3X_GET_STATUS_REGISTER;
+ uint8_t buf[3];
+ int error;
+
+ error = sht3x_cmd(tag, addr, ®, 2, buf, 3, 10);
+ if (matchdebug) {
+ printf("poke X 1: %d\n", error);
+ }
+ return error;
+}
+
+static int
+sht3x_sysctl_init(struct sht3x_sc *sc)
+{
+ int error;
+ const struct sysctlnode *cnode;
+ int sysctlroot_num;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ 0, CTLTYPE_NODE, device_xname(sc->sc_dev),
+ SYSCTL_DESCR("sht3x controls"), NULL, 0, NULL, 0, CTL_HW,
+ CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ sysctlroot_num = cnode->sysctl_num;
+
+#ifdef SHT3X_DEBUG
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
+ SYSCTL_DESCR("Debug level"), sht3x_verify_sysctl, 0,
+ &sc->sc_sht3xdebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
+ CTL_EOL)) != 0)
+ return error;
+
+#endif
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
+ SYSCTL_DESCR("The number of times to attempt to read the values"),
+ sht3x_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW,
+ sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READONLY, CTLTYPE_STRING, "modes",
+ SYSCTL_DESCR("Valid modes"), 0, 0,
+ __UNCONST(sht3x_mode_names),
+ sizeof(sht3x_mode_names) + 1,
+ CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_STRING, "mode",
+ SYSCTL_DESCR("Mode for measurement collection"),
+ sht3x_verify_sysctl_modes, 0, (void *) sc,
+ SHT3X_MODE_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READONLY, CTLTYPE_STRING, "repeatabilities",
+ SYSCTL_DESCR("Valid repeatability values"), 0, 0,
+ __UNCONST(sht3x_repeatability_names),
+ sizeof(sht3x_repeatability_names) + 1,
+ CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_STRING, "repeatability",
+ SYSCTL_DESCR("Repeatability of RH and Temp"),
+ sht3x_verify_sysctl_repeatability, 0, (void *) sc,
+ SHT3X_REP_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READONLY, CTLTYPE_STRING, "rates",
+ SYSCTL_DESCR("Valid peridoic rates"), 0, 0,
+ __UNCONST(sht3x_rate_names),
+ sizeof(sht3x_rate_names) + 1,
+ CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_STRING, "rate",
+ SYSCTL_DESCR("Rate for periodic measurements"),
+ sht3x_verify_sysctl_rate, 0, (void *) sc,
+ SHT3X_RATE_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc",
+ SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc,
+ 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_sht3xlog, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_BOOL, "heateron",
+ SYSCTL_DESCR("Heater on"), sht3x_verify_sysctl_heateron, 0,
+ (void *)sc, 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ return 0;
+}
+
+static int
+sht3x_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct i2c_attach_args *ia = aux;
+ int error, match_result;
+ const bool matchdebug = false;
+
+ if (iic_use_direct_match(ia, match, NULL, &match_result))
+ return match_result;
+
+ if (matchdebug) {
+ printf("Looking at ia_addr: %x\n",ia->ia_addr);
+ }
+
+ /* indirect config - check for configured address */
+ if (ia->ia_addr == SHT3X_TYPICAL_ADDR_1 ||
+ ia->ia_addr == SHT3X_TYPICAL_ADDR_2) {
+ /*
+ * Check to see if something is really at this i2c address. This will
+ * keep phantom devices from appearing
+ */
+ if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
+ if (matchdebug)
+ printf("in match acquire bus failed\n");
+ return 0;
+ }
+
+ error = sht3x_poke(ia->ia_tag, ia->ia_addr, matchdebug);
+ iic_release_bus(ia->ia_tag, 0);
+
+ return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
+ } else {
+ return 0;
+ }
+}
+
+static void
+sht3x_attach(device_t parent, device_t self, void *aux)
+{
+ struct sht3x_sc *sc;
+ struct i2c_attach_args *ia;
+ int error, i;
+ int ecount = 0;
+ uint8_t buf[6];
+ uint32_t serialnumber;
+ uint8_t sncrcpt1, sncrcpt2;
+
+ ia = aux;
+ sc = device_private(self);
+
+ sc->sc_dev = self;
+ sc->sc_tag = ia->ia_tag;
+ sc->sc_addr = ia->ia_addr;
+ sc->sc_sht3xdebug = 0;
+ strlcpy(sc->sc_mode,"single-shot",SHT3X_MODE_NAME);
+ sc->sc_isperiodic = false;
+ strlcpy(sc->sc_repeatability,"high",SHT3X_REP_NAME);
+ strlcpy(sc->sc_periodic_rate,"1.0mps",SHT3X_RATE_NAME);
+ sc->sc_readattempts = 10;
+ sc->sc_ignorecrc = false;
+ sc->sc_heateron = false;
+ sc->sc_sme = NULL;
+ sc->sc_stopping = false;
+ sc->sc_initperiodic = false;
+ sc->sc_opened = false;
+ sc->sc_dying = false;
+ sc->sc_readpoolname = NULL;
+
+ aprint_normal("\n");
+
+ mutex_init(&sc->sc_dying_mutex, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&sc->sc_read_mutex, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&sc->sc_threadmutex, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&sc->sc_condvar, "sht3xcv");
+ cv_init(&sc->sc_condreadready, "sht3xread");
+ cv_init(&sc->sc_cond_dying, "sht3xdie");
+ sc->sc_numsensors = __arraycount(sht3x_sensors);
+
+ if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
+ aprint_error_dev(self,
+ "Unable to create sysmon structure\n");
+ sc->sc_sme = NULL;
+ return;
+ }
+ if ((error = sht3x_sysctl_init(sc)) != 0) {
+ aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
+ goto out;
+ }
+
+ sc->sc_readpoolname = kmem_asprintf("sht3xrp%d",device_unit(self));
+ sc->sc_readpool = pool_cache_init(sizeof(struct sht3x_read_q),0,0,0,sc->sc_readpoolname,NULL,IPL_VM,NULL,NULL,NULL);
+ pool_cache_sethiwat(sc->sc_readpool,100);
+
+ SIMPLEQ_INIT(&sc->sc_read_queue);
+
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ aprint_error_dev(self, "Could not acquire iic bus: %d\n",
+ error);
+ goto out;
+ }
+
+ error = sht3x_cmdr(sc, SHT3X_SOFT_RESET, NULL, 0);
+ if (error != 0)
+ aprint_error_dev(self, "Reset failed: %d\n", error);
+
+ error = sht3x_clear_status_register(sc, true);
+ if (error) {
+ aprint_error_dev(self, "Failed to clear status register: %d\n",
+ error);
+ ecount++;
+ }
+
+ uint16_t status_reg;
+ error = sht3x_get_status_register(sc, &status_reg, true);
+ if (error) {
+ aprint_error_dev(self, "Failed to read status register: %d\n",
+ error);
+ ecount++;
+ }
+
+ DPRINTF(sc, 2, ("%s: read status register values: %04x\n",
+ device_xname(sc->sc_dev), status_reg));
+
+ error = sht3x_cmdr(sc, SHT3X_READ_SERIAL_NUMBER, buf, 6);
+ if (error) {
+ aprint_error_dev(self, "Failed to read serial number: %d\n",
+ error);
+ ecount++;
+ }
+
+ sncrcpt1 = sht3x_crc(&buf[0],2);
+ sncrcpt2 = sht3x_crc(&buf[3],2);
+ serialnumber = (buf[0] << 24) | (buf[1] << 16) | (buf[3] << 8) | buf[4];
+
+ DPRINTF(sc, 2, ("%s: read serial number values: %02x%02x - %02x - %02x%02x - %02x -- %02x %02x\n",
+ device_xname(sc->sc_dev), buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], sncrcpt1, sncrcpt2));
+
+ iic_release_bus(sc->sc_tag, 0);
+ if (error != 0) {
+ aprint_error_dev(self, "Unable to setup device\n");
+ goto out;
+ }
+
+ for (i = 0; i < sc->sc_numsensors; i++) {
+ strlcpy(sc->sc_sensors[i].desc, sht3x_sensors[i].desc,
+ sizeof(sc->sc_sensors[i].desc));
+
+ sc->sc_sensors[i].units = sht3x_sensors[i].type;
+ sc->sc_sensors[i].state = ENVSYS_SINVALID;
+#ifdef __did_not_work
+ sc->sc_sensors[i].flags |= ENVSYS_FMONLIMITS;
+#endif
+
+ DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
+ sc->sc_sensors[i].desc));
+
+ error = sysmon_envsys_sensor_attach(sc->sc_sme,
+ &sc->sc_sensors[i]);
+ if (error) {
+ aprint_error_dev(self,
+ "Unable to attach sensor %d: %d\n", i, error);
+ goto out;
+ }
+ }
+
+ sc->sc_sme->sme_name = device_xname(sc->sc_dev);
+ sc->sc_sme->sme_cookie = sc;
+ sc->sc_sme->sme_refresh = sht3x_refresh;
+#ifdef __did_not_work
+ sc->sc_sme->sme_get_limits = sht3x_get_limits;
+ sc->sc_sme->sme_set_limits = sht3x_set_limits;
+#endif
+
+ DPRINTF(sc, 2, ("sht3x_attach: registering with envsys\n"));
+
+ if (sysmon_envsys_register(sc->sc_sme)) {
+ aprint_error_dev(self,
+ "unable to register with sysmon\n");
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+ return;
+ }
+
+ /* There is no documented way to ask the chip what version it is. This
+ is likely fine as the only apparent difference is in how precise the
+ measurements will be. The actual conversation with the chip is
+ identical no matter which one you are talking to.
+ */
+
+ aprint_normal_dev(self, "Sensirion SHT30/SHT31/SHT35, "
+ "Serial number: %x%s",
+ serialnumber,
+ (sncrcpt1 == buf[2] && sncrcpt2 == buf[5]) ? "\n" : " (bad crc)\n");
+ return;
+out:
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+}
+
+static uint16_t
+sht3x_compute_measure_command_ss(char *repeatability)
+{
+ int i;
+ uint16_t r;
+
+ for (i = 0; i < __arraycount(sht3x_repeatability_ss); i++) {
+ if (strncmp(repeatability, sht3x_repeatability_ss[i].text,
+ SHT3X_REP_NAME) == 0) {
+ r = sht3x_repeatability_ss[i].cmd;
+ break;
+ }
+ }
+
+ if (i == __arraycount(sht3x_repeatability_ss))
+ panic("Single-shot could not find command for repeatability: %s\n", repeatability);
+
+ return r;
+}
+
+/*
+ The documented conversion calculations for the raw values are as follows:
+
+ %RH = (-6 + 125 * rawvalue / 65535)
+
+ T in Celsius = (-45 + 175 * rawvalue / 65535)
+
+ It follows then:
+
+ T in Kelvin = (228.15 + 175 * rawvalue / 65535)
+
+ given the relationship between Celsius and Kelvin
+
+ What follows reorders the calculation a bit and scales it up to avoid
+ the use of any floating point. All that would really have to happen
+ is a scale up to 10^6 for the sysenv framework, which wants
+ temperature in micro-kelvin and percent relative humidity scaled up
+ 10^6, but since this conversion uses 64 bits due to intermediate
+ values that are bigger than 32 bits the conversion first scales up to
+ 10^9 and the scales back down by 10^3 at the end. This preserves some
+ precision in the conversion that would otherwise be lost.
+*/
+
+static uint64_t
+sht3x_compute_temp_from_raw(uint8_t msb, uint8_t lsb) {
+ uint64_t svalue;
+ int64_t v1;
+ uint64_t v2;
+ uint64_t d1 = 65535;
+ uint64_t mul1;
+ uint64_t mul2;
+ uint64_t div1 = 10000;
+ uint64_t q;
+
+ svalue = msb << 8 | lsb;
+
+ v1 = 22815; /* this is scaled up already from 228.15 */
+ v2 = 175;
+ mul1 = 10000000000;
+ mul2 = 100000000;
+
+ svalue = svalue * mul1;
+ v1 = v1 * mul2;
+ /* Perform the conversion */
+ q = ((v2 * (svalue / d1)) + v1) / div1;
+
+ return q;
+}
+
+static uint64_t
+sht3x_compute_rh_from_raw(uint8_t msb, uint8_t lsb) {
+ uint64_t svalue;
+ int64_t v1;
+ uint64_t v2;
+ uint64_t d1 = 65535;
+ uint64_t mul1;
+ uint64_t mul2;
+ uint64_t div1 = 10000;
+ uint64_t q;
+
+ svalue = msb << 8 | lsb;
+
+ v1 = 0;
+ v2 = 100;
+ mul1 = 10000000000;
+ mul2 = 10000000000;
+
+ svalue = svalue * mul1;
+ v1 = v1 * mul2;
+ /* Perform the conversion */
+ q = ((v2 * (svalue / d1)) + v1) / div1;
+
+ return q;
+}
+
+/* These are the the same as above except solved for the raw tick rather than
+ * temperature or humidity. These are needed for setting the alert limits, but
+ * since that did not work, disable these too for now.
+ */
+#ifdef __did_not_work
+static uint16_t
+sht3x_compute_raw_from_temp(uint32_t temp)
+{
+ uint64_t i1;
+ uint32_t tempc;
+
+ tempc = temp - 272150000;
+ tempc = tempc / 1000000;
+
+ i1 = (13107 * tempc) + 589815;
+ return (uint16_t)(i1 / 35);
+}
+
+static uint16_t
+sht3x_compute_raw_from_rh(uint32_t mrh)
+{
+ uint64_t i1;
+ uint32_t rh;
+
+ rh = mrh / 1000000;
+
+ i1 = 13107 * rh;
+ return (uint16_t)(i1 / 20);
+}
+#endif
+
+static void
+sht3x_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
+{
+ struct sht3x_sc *sc;
+ sc = sme->sme_cookie;
+ int error;
+ uint8_t rawdata[6];
+ uint16_t measurement_command_ss;
+ uint64_t current_value;
+ uint8_t *svalptr;
+
+ edata->state = ENVSYS_SINVALID;
+
+ mutex_enter(&sc->sc_mutex);
+
+ if (!sc->sc_isperiodic) {
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ measurement_command_ss = sht3x_compute_measure_command_ss(sc->sc_repeatability);
+ DPRINTF(sc, 2, ("%s: Measurement command: %04x\n",
+ device_xname(sc->sc_dev), measurement_command_ss));
+ error = sht3x_cmdr(sc,measurement_command_ss,rawdata,6);
+ if (error == 0) {
+ DPRINTF(sc, 2, ("%s: Raw data ss: %02x%02x %02x - %02x%02x %02x\n",
+ device_xname(sc->sc_dev), rawdata[0], rawdata[1], rawdata[2],
+ rawdata[3], rawdata[4], rawdata[5]));
+ switch (edata->sensor) {
+ case SHT3X_TEMP_SENSOR:
+ svalptr = &rawdata[0];
+ current_value = sht3x_compute_temp_from_raw(rawdata[0],rawdata[1]);
+ break;
+ case SHT3X_HUMIDITY_SENSOR:
+ svalptr = &rawdata[3];
+ current_value = sht3x_compute_rh_from_raw(rawdata[3],rawdata[4]);
+ break;
+ default:
+ error = EINTR;
+ break;
+ }
+
+ if (error == 0) {
+ uint8_t testcrc;
+
+ /* Fake out the CRC check if being asked to ignore CRC */
+ if (sc->sc_ignorecrc) {
+ testcrc = *(svalptr + 2);
+ } else {
+ testcrc = sht3x_crc(svalptr,2);
+ }
+
+ if (*(svalptr + 2) == testcrc) {
+ edata->value_cur = (uint32_t) current_value;
+ edata->state = ENVSYS_SVALID;
+ } else {
+ error = EINVAL;
+ }
+ }
+ }
+
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to get new status in refresh for single-shot %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+
+ uint16_t sbuf;
+ int status_error;
+
+ status_error = sht3x_get_status_register(sc, &sbuf, true);
+
+ if (!status_error) {
+ DPRINTF(sc, 2, ("%s: read status register single-shot: %04x\n",
+ device_xname(sc->sc_dev), sbuf));
+
+ if (sbuf & SHT3X_RESET_DETECTED) {
+ aprint_error_dev(sc->sc_dev, "Reset detected in single shot mode. Heater may have been reset\n");
+ sht3x_clear_status_register(sc, true);
+ }
+
+ sc->sc_heateron = sbuf & SHT3X_HEATER_STATUS;
+ }
+
+ iic_release_bus(sc->sc_tag, 0);
+ } else {
+ error = 0;
+ memcpy(rawdata,sc->sc_pbuffer,6);
+
+ DPRINTF(sc, 2, ("%s: Raw data periodic: %02x%02x %02x - %02x%02x %02x\n",
+ device_xname(sc->sc_dev), rawdata[0], rawdata[1], rawdata[2],
+ rawdata[3], rawdata[4], rawdata[5]));
+
+ switch (edata->sensor) {
+ case SHT3X_TEMP_SENSOR:
+ svalptr = &rawdata[0];
+ current_value = sht3x_compute_temp_from_raw(rawdata[0],rawdata[1]);
+ break;
+ case SHT3X_HUMIDITY_SENSOR:
+ svalptr = &rawdata[3];
+ current_value = sht3x_compute_rh_from_raw(rawdata[3],rawdata[4]);
+ break;
+ default:
+ error = EINTR;
+ break;
+ }
+
+ if (error == 0) {
+ uint8_t testcrc;
+
+ /* Fake out the CRC check if being asked to ignore CRC */
+ if (sc->sc_ignorecrc) {
+ testcrc = *(svalptr + 2);
+ } else {
+ testcrc = sht3x_crc(svalptr,2);
+ }
+
+ if (*(svalptr + 2) == testcrc) {
+ edata->value_cur = (uint32_t) current_value;
+ edata->state = ENVSYS_SVALID;
+ } else {
+ error = EINVAL;
+ }
+ }
+
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to get new status in refresh for periodic %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+ }
+out:
+ mutex_exit(&sc->sc_mutex);
+}
+
+#ifdef __did_not_work
+static void
+sht3x_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
+ sysmon_envsys_lim_t *limits, uint32_t *props)
+{
+ struct sht3x_sc *sc = sme->sme_cookie;
+ uint16_t rawlimitshigh, rawlimitslow;
+ uint16_t templimithigh, rhlimithigh,
+ templimitlow, rhlimitlow;
+ uint8_t templimithighmsb, templimithighlsb,
+ templimitlowmsb, templimitlowlsb;
+ uint8_t rhlimithighmsb, rhlimithighlsb,
+ rhlimitlowmsb, rhlimitlowlsb;
+ int error;
+ uint8_t lbuf[3];
+ uint8_t limitscrchigh, limitskcrchigh,
+ limitscrclow, limitskcrclow;
+
+ *props = 0;
+
+ mutex_enter(&sc->sc_mutex);
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ error = sht3x_cmdr(sc, SHT3X_READ_HIGH_ALERT_SET, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not get high alert: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ rawlimitshigh = (lbuf[0] << 8) | lbuf[1];
+ limitskcrchigh = lbuf[2];
+ limitscrchigh = sht3x_crc(&lbuf[0],2);
+
+ templimithigh = ((rawlimitshigh & 0x1FF) << 7);
+ templimithighmsb = (uint8_t)(templimithigh >> 8);
+ templimithighlsb = (uint8_t)(templimithigh & 0x00FF);
+ DPRINTF(sc, 2, ("%s: Limits high intermediate temp: %04x %04x %02x %02x\n",
+ device_xname(sc->sc_dev), rawlimitshigh, templimithigh, templimithighmsb,
+ templimithighlsb));
+
+ rhlimithigh = (rawlimitshigh & 0xFE00);
+ rhlimithighmsb = (uint8_t)(rhlimithigh >> 8);
+ rhlimithighlsb = (uint8_t)(rhlimithigh & 0x00FF);
+ DPRINTF(sc, 2, ("%s: Limits high intermediate rh: %04x %04x %02x %02x\n",
+ device_xname(sc->sc_dev), rawlimitshigh, rhlimithigh, rhlimithighmsb,
+ rhlimithighlsb));
+
+ DPRINTF(sc, 2, ("%s: Limit high raw: %02x%02x %02x %02x %02x\n",
+ device_xname(sc->sc_dev), lbuf[0], lbuf[1], lbuf[2],
+ limitscrchigh, limitskcrchigh));
+
+ error = sht3x_cmdr(sc, SHT3X_READ_LOW_ALERT_SET, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not get high alert: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ rawlimitslow = (lbuf[0] << 8) | lbuf[1];
+ limitskcrclow = lbuf[2];
+ limitscrclow = sht3x_crc(&lbuf[0],2);
+
+ templimitlow = ((rawlimitslow & 0x1FF) << 7);
+ templimitlowmsb = (uint8_t)(templimitlow >> 8);
+ templimitlowlsb = (uint8_t)(templimitlow & 0x00FF);
+ DPRINTF(sc, 2, ("%s: Limits low intermediate temp: %04x %04x %02x %02x\n",
+ device_xname(sc->sc_dev), rawlimitslow, templimitlow, templimitlowmsb,
+ templimitlowlsb));
+
+ rhlimitlow = (rawlimitslow & 0xFE00);
+ rhlimitlowmsb = (uint8_t)(rhlimitlow >> 8);
+ rhlimitlowlsb = (uint8_t)(rhlimitlow & 0x00FF);
+ DPRINTF(sc, 2, ("%s: Limits low intermediate rh: %04x %04x %02x %02x\n",
+ device_xname(sc->sc_dev), rawlimitslow, rhlimitlow, rhlimitlowmsb,
+ rhlimitlowlsb));
+
+ DPRINTF(sc, 2, ("%s: Limit low raw: %02x%02x %02x %02x %02x\n",
+ device_xname(sc->sc_dev), lbuf[0], lbuf[1], lbuf[2],
+ limitscrclow, limitskcrclow));
+
+
+ switch (edata->sensor) {
+ case SHT3X_TEMP_SENSOR:
+ if (limitscrchigh == limitskcrchigh) {
+ limits->sel_critmax = sht3x_compute_temp_from_raw(templimithighmsb, templimithighlsb);
+ *props |= PROP_CRITMAX;
+ }
+ if (limitscrclow == limitskcrclow) {
+ limits->sel_critmin = sht3x_compute_temp_from_raw(templimitlowmsb, templimitlowlsb);
+ *props |= PROP_CRITMIN;
+ }
+ break;
+ case SHT3X_HUMIDITY_SENSOR:
+ if (limitscrchigh == limitskcrchigh) {
+ limits->sel_critmax = sht3x_compute_rh_from_raw(rhlimithighmsb, rhlimithighlsb);
+ *props |= PROP_CRITMAX;
+ }
+ if (limitscrclow == limitskcrclow) {
+ limits->sel_critmin = sht3x_compute_rh_from_raw(rhlimitlowmsb, rhlimitlowlsb);
+ *props |= PROP_CRITMIN;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (*props != 0)
+ *props |= PROP_DRIVER_LIMITS;
+
+ iic_release_bus(sc->sc_tag, 0);
+ out:
+ mutex_exit(&sc->sc_mutex);
+}
+
+static void
+sht3x_set_alert_limits(void *aux, uint16_t high, uint16_t low, bool have_bus)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+
+ int error = 0;
+ uint8_t hbuf[3];
+ uint8_t lbuf[3];
+
+ if (! have_bus) {
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for setting alerts %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ }
+
+ hbuf[0] = high >> 8;
+ hbuf[1] = high & 0x00FF;
+ hbuf[2] = sht3x_crc(&hbuf[0],2);
+
+ lbuf[0] = low >> 8;
+ lbuf[1] = low & 0x00FF;
+ lbuf[2] = sht3x_crc(&lbuf[0],2);
+
+ error = sht3x_cmdr(sc, SHT3X_WRITE_HIGH_ALERT_SET, hbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set high alert for SET %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ error = sht3x_cmdr(sc, SHT3X_WRITE_HIGH_ALERT_CLEAR, hbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set high alert for CLEAR %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ error = sht3x_cmdr(sc, SHT3X_WRITE_LOW_ALERT_SET, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set low alert for SET %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ error = sht3x_cmdr(sc, SHT3X_WRITE_LOW_ALERT_CLEAR, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set high alert for CLEAR %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+
+ out:
+ if (! have_bus) {
+ iic_release_bus(sc->sc_tag, 0);
+ }
+}
+
+static void
+sht3x_set_alert_limits2(void *aux, uint16_t high, uint16_t low,
+ uint16_t highminusone, uint16_t lowplusone, bool have_bus)
+{
+ struct sht3x_sc *sc;
+ sc = aux;
+
+ int error = 0;
+ uint8_t hbuf[3];
+ uint8_t lbuf[3];
+ uint8_t hbufminusone[3];
+ uint8_t lbufplusone[3];
+
+ if (! have_bus) {
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire iic bus for setting alerts %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ }
+
+ hbuf[0] = high >> 8;
+ hbuf[1] = high & 0x00FF;
+ hbuf[2] = sht3x_crc(&hbuf[0],2);
+
+ lbuf[0] = low >> 8;
+ lbuf[1] = low & 0x00FF;
+ lbuf[2] = sht3x_crc(&lbuf[0],2);
+
+ hbufminusone[0] = highminusone >> 8;
+ hbufminusone[1] = highminusone & 0x00FF;
+ hbufminusone[2] = sht3x_crc(&hbufminusone[0],2);
+
+ lbufplusone[0] = lowplusone >> 8;
+ lbufplusone[1] = lowplusone & 0x00FF;
+ lbufplusone[2] = sht3x_crc(&lbufplusone[0],2);
+
+ DPRINTF(sc, 2, ("%s: Physical SET HIGH %02x %02x %02x\n",
+ device_xname(sc->sc_dev), hbuf[0], hbuf[1], hbuf[2]));
+ error = sht3x_cmdr(sc, SHT3X_WRITE_HIGH_ALERT_SET, hbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set high alert for SET %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ uint16_t sbuf;
+ int status_error;
+ status_error = sht3x_get_status_register(sc, &sbuf, true);
+ DPRINTF(sc, 2, ("%s: In SETTING, status register %04x -- %d\n",
+ device_xname(sc->sc_dev), sbuf, status_error));
+
+ hbuf[0] = 0;
+ hbuf[1] = 0;
+ hbuf[2] = 0;
+ error = sht3x_cmdr(sc, SHT3X_READ_HIGH_ALERT_SET, hbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not read high alert for SET %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ DPRINTF(sc, 2, ("%s: Physical READBACK SET HIGH %02x %02x %02x\n",
+ device_xname(sc->sc_dev), hbuf[0], hbuf[1], hbuf[2]));
+
+ DPRINTF(sc, 2, ("%s: Physical CLEAR HIGH %02x %02x %02x\n",
+ device_xname(sc->sc_dev), hbufminusone[0], hbufminusone[1], hbufminusone[2]));
+ error = sht3x_cmdr(sc, SHT3X_WRITE_HIGH_ALERT_CLEAR, hbufminusone, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set high alert for CLEAR %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ hbufminusone[0] = 0;
+ hbufminusone[1] = 0;
+ hbufminusone[2] = 0;
+ error = sht3x_cmdr(sc, SHT3X_READ_HIGH_ALERT_CLEAR, hbufminusone, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not read high alert for CLEAR %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ DPRINTF(sc, 2, ("%s: Physical READBACK CLEAR HIGH %02x %02x %02x\n",
+ device_xname(sc->sc_dev), hbufminusone[0], hbufminusone[1], hbufminusone[2]));
+
+ DPRINTF(sc, 2, ("%s: Physical SET LOW %02x %02x %02x\n",
+ device_xname(sc->sc_dev), lbuf[0], lbuf[1], lbuf[2]));
+ error = sht3x_cmdr(sc, SHT3X_WRITE_LOW_ALERT_SET, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set low alert for SET %d\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+ DPRINTF(sc, 2, ("%s: Physical CLEAR LOW %02x %02x %02x\n",
+ device_xname(sc->sc_dev), lbufplusone[0], lbufplusone[1], lbufplusone[2]));
+ error = sht3x_cmdr(sc, SHT3X_WRITE_LOW_ALERT_CLEAR, lbufplusone, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not set high alert for CLEAR %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+
+ out:
+ if (! have_bus) {
+ iic_release_bus(sc->sc_tag, 0);
+ }
+}
+
+static void
+sht3x_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
+ sysmon_envsys_lim_t *limits, uint32_t *props)
+{
+ struct sht3x_sc *sc = sme->sme_cookie;
+ uint16_t rawlimitshigh, rawlimitslow;
+ uint16_t rawlimitshighclear, rawlimitslowclear;
+ uint16_t rawlimitshighminusone, rawlimitslowplusone;
+ int error;
+ uint8_t lbuf[3];
+ uint8_t limitscrchigh, limitskcrchigh,
+ limitscrclow, limitskcrclow;
+ uint16_t limithigh, limitlow;
+ uint16_t limithighminusone, limitlowplusone;
+
+ if (limits == NULL) {
+ printf("XXX - Need to set back to default... limits is NULL\n");
+ return;
+ }
+
+ DPRINTF(sc, 2, ("%s: In set_limits - %d -- %d %d\n",
+ device_xname(sc->sc_dev), edata->sensor,
+ limits->sel_critmin, limits->sel_critmax));
+
+ mutex_enter(&sc->sc_mutex);
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ error = sht3x_cmdr(sc, SHT3X_READ_HIGH_ALERT_SET, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not get high alert: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ rawlimitshigh = (lbuf[0] << 8) | lbuf[1];
+ limitskcrchigh = lbuf[2];
+ limitscrchigh = sht3x_crc(&lbuf[0],2);
+
+
+ error = sht3x_cmdr(sc, SHT3X_READ_LOW_ALERT_SET, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not get high alert: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ rawlimitslow = (lbuf[0] << 8) | lbuf[1];
+ limitskcrclow = lbuf[2];
+ limitscrclow = sht3x_crc(&lbuf[0],2);
+
+ error = sht3x_cmdr(sc, SHT3X_READ_HIGH_ALERT_CLEAR, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not get high alert clear: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ rawlimitshighclear = (lbuf[0] << 8) | lbuf[1];
+
+ error = sht3x_cmdr(sc, SHT3X_READ_LOW_ALERT_CLEAR, lbuf, 3);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not get high alert clear: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ rawlimitslowclear = (lbuf[0] << 8) | lbuf[1];
+
+ DPRINTF(sc, 2, ("%s: Set limits current raw limits %04x - %02x %02x ; %04x - %02x %02x ;; %04x %04x\n",
+ device_xname(sc->sc_dev), rawlimitshigh, limitskcrchigh, limitscrchigh,
+ rawlimitslow, limitskcrclow, limitscrclow, rawlimitshighclear, rawlimitslowclear));
+
+ switch (edata->sensor) {
+ case SHT3X_TEMP_SENSOR:
+ limithigh = sht3x_compute_raw_from_temp(limits->sel_critmax);
+ limitlow = sht3x_compute_raw_from_temp(limits->sel_critmin);
+ limithigh = limithigh >> 7;
+ limithighminusone = limithigh - 1;
+ limitlow = limitlow >> 7;
+ limitlowplusone = limitlow + 1;
+ rawlimitshigh = (rawlimitshigh & 0xFE00) | limithigh;
+ rawlimitshighminusone = (rawlimitshigh & 0xFE00) | limithighminusone;
+ rawlimitslow = (rawlimitslow & 0xFE00) | limitlow;
+ rawlimitslowplusone = (rawlimitslow & 0xFE00) | limitlowplusone;
+ DPRINTF(sc, 2, ("%s: Temp new raw limits high/low %04x %04x %04x %04x\n",
+ device_xname(sc->sc_dev), rawlimitshigh, rawlimitslow,
+ rawlimitshighminusone, rawlimitslowplusone));
+ sht3x_set_alert_limits2(sc, rawlimitshigh, rawlimitslow,
+ rawlimitshighminusone, rawlimitslowplusone, true);
+ break;
+ case SHT3X_HUMIDITY_SENSOR:
+ limithigh = sht3x_compute_raw_from_rh(limits->sel_critmax);
+ limitlow = sht3x_compute_raw_from_rh(limits->sel_critmin);
+ limithigh = limithigh & 0xFE00;
+ limitlow = limitlow & 0xFE00;
+ rawlimitshigh = (rawlimitshigh & 0x1FF) | limithigh;
+ rawlimitslow = (rawlimitslow & 0x1FF) | limitlow;
+ DPRINTF(sc, 2, ("%s: RH new raw limits high/low %04x %04x from %x %x\n",
+ device_xname(sc->sc_dev), rawlimitshigh, rawlimitslow, limithigh, limitlow));
+ sht3x_set_alert_limits(sc, rawlimitshigh, rawlimitslow, true);
+ break;
+ default:
+ break;
+ }
+
+ iic_release_bus(sc->sc_tag, 0);
+ out:
+ mutex_exit(&sc->sc_mutex);
+}
+#endif
+
+static int
+sht3xopen(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ struct sht3x_sc *sc;
+
+ sc = device_lookup_private(&sht3xtemp_cd, minor(dev));
+ if (!sc)
+ return (ENXIO);
+
+ if (sc->sc_opened)
+ return (EBUSY);
+
+ mutex_enter(&sc->sc_mutex);
+ sc->sc_opened = true;
+
+ sc->sc_wassingleshot = false;
+ if (!sc->sc_isperiodic) {
+ sc->sc_stopping = false;
+ sc->sc_initperiodic = true;
+ sc->sc_isperiodic = true;
+ sc->sc_wassingleshot = true;
+ sht3x_start_thread(sc);
+ }
+ mutex_exit(&sc->sc_mutex);
+
+ return (0);
+}
+
+static int
+sht3xread(dev_t dev, struct uio *uio, int flags)
+{
+ struct sht3x_sc *sc;
+ struct sht3x_read_q *pp;
+ int error,any;
+
+ sc = device_lookup_private(&sht3xtemp_cd, minor(dev));
+ if (!sc)
+ return (ENXIO);
+
+ while (uio->uio_resid) {
+ any = 0;
+ error = 0;
+ mutex_enter(&sc->sc_read_mutex);
+
+ while (any == 0) {
+ pp = SIMPLEQ_FIRST(&sc->sc_read_queue);
+ if (pp != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_read_queue, read_q);
+ any = 1;
+ break;
+ } else {
+ error = cv_wait_sig(&sc->sc_condreadready,&sc->sc_read_mutex);
+ if (sc->sc_dying)
+ error = EIO;
+ if (error == 0)
+ continue;
+ break;
+ }
+ }
+
+ if (any == 1 && error == 0) {
+ mutex_exit(&sc->sc_read_mutex);
+ pool_cache_put(sc->sc_readpool,pp);
+
+ DPRINTF(sc,2, ("%s: sending %02x%02x %02x -- %02x%02x %02x -- %x\n",device_xname(sc->sc_dev),pp->measurement[0],pp->measurement[1],pp->measurement[2],pp->measurement[3],pp->measurement[4],pp->measurement[5],mutex_owned(&sc->sc_read_mutex)));
+ if ((error = uiomove(&pp->measurement[0], 6, uio)) != 0) {
+ DPRINTF(sc,2, ("%s: send error %d\n",device_xname(sc->sc_dev),error));
+ break;
+ }
+ } else {
+ mutex_exit(&sc->sc_read_mutex);
+ if (error) {
+ break;
+ }
+ }
+ }
+
+ DPRINTF(sc,2, ("%s: loop done: %d\n",device_xname(sc->sc_dev),error));
+ if (sc->sc_dying) {
+ DPRINTF(sc, 2, ("%s: Telling all we are almost dead\n",
+ device_xname(sc->sc_dev)));
+ mutex_enter(&sc->sc_dying_mutex);
+ cv_signal(&sc->sc_cond_dying);
+ mutex_exit(&sc->sc_dying_mutex);
+ }
+ return error;
+}
+
+static int
+sht3xclose(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ struct sht3x_sc *sc;
+ struct sht3x_read_q *pp;
+
+ sc = device_lookup_private(&sht3xtemp_cd, minor(dev));
+
+ if (sc->sc_wassingleshot) {
+ sht3x_stop_thread(sc);
+ sc->sc_stopping = false;
+ sc->sc_initperiodic = false;
+ sc->sc_isperiodic = false;
+ }
+
+ mutex_enter(&sc->sc_mutex);
+ /* Drain any read pools */
+ while ((pp = SIMPLEQ_FIRST(&sc->sc_read_queue)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_read_queue, read_q);
+ pool_cache_put(sc->sc_readpool,pp);
+ }
+
+ /* Say that the device is now free */
+ sc->sc_opened = false;
+ mutex_exit(&sc->sc_mutex);
+
+ return(0);
+}
+
+static int
+sht3x_detach(device_t self, int flags)
+{
+ struct sht3x_sc *sc;
+ struct sht3x_read_q *pp;
+
+ sc = device_private(self);
+
+ if (sc->sc_isperiodic) {
+ sht3x_stop_thread(sc);
+ }
+
+ mutex_enter(&sc->sc_mutex);
+
+ sc->sc_dying = true;
+
+ /* If this is true we are still open, destroy the condvar */
+ if (sc->sc_opened) {
+ mutex_enter(&sc->sc_dying_mutex);
+ mutex_enter(&sc->sc_read_mutex);
+ cv_signal(&sc->sc_condreadready);
+ mutex_exit(&sc->sc_read_mutex);
+ DPRINTF(sc, 2, ("%s: Will wait for anything to exit\n",
+ device_xname(sc->sc_dev)));
+ cv_timedwait_sig(&sc->sc_cond_dying,&sc->sc_dying_mutex,mstohz(5000));
+ mutex_exit(&sc->sc_dying_mutex);
+ cv_destroy(&sc->sc_condreadready);
+ cv_destroy(&sc->sc_cond_dying);
+ }
+
+ /* Drain any read pools */
+ while ((pp = SIMPLEQ_FIRST(&sc->sc_read_queue)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_read_queue, read_q);
+ pool_cache_put(sc->sc_readpool,pp);
+ }
+
+ /* Destroy the pool cache now that nothing is using it */
+ pool_cache_destroy(sc->sc_readpool);
+
+ /* Remove the sensors */
+ if (sc->sc_sme != NULL) {
+ sysmon_envsys_unregister(sc->sc_sme);
+ sc->sc_sme = NULL;
+ }
+ mutex_exit(&sc->sc_mutex);
+
+ /* Remove the sysctl tree */
+ sysctl_teardown(&sc->sc_sht3xlog);
+
+ /* Remove the mutex */
+ mutex_destroy(&sc->sc_mutex);
+ mutex_destroy(&sc->sc_threadmutex);
+ mutex_destroy(&sc->sc_read_mutex);
+ mutex_destroy(&sc->sc_dying_mutex);
+
+ /* Free the poolname string */
+ if (sc->sc_readpoolname != NULL) {
+ kmem_free(sc->sc_readpoolname,strlen(sc->sc_readpoolname) + 1);
+ }
+
+ return 0;
+}
+
+int
+sht3x_activate(device_t self, enum devact act)
+{
+ struct sht3x_sc *sc = device_private(self);
+
+ switch (act) {
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = true;
+ return 0;
+ default:
+ return EOPNOTSUPP;
+ }
+}
+
+MODULE(MODULE_CLASS_DRIVER, sht3xtemp, "i2cexec,sysmon_envsys");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+sht3xtemp_modcmd(modcmd_t cmd, void *opaque)
+{
+ int error;
+#ifdef _MODULE
+ int bmaj = -1, cmaj = -1;
+#endif
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ error = config_init_component(cfdriver_ioconf_sht3xtemp,
+ cfattach_ioconf_sht3xtemp, cfdata_ioconf_sht3xtemp);
+ if (error)
+ aprint_error("%s: unable to init component\n",
+ sht3xtemp_cd.cd_name);
+
+ error = devsw_attach("sht3xtemp", NULL, &bmaj,
+ &sht3x_cdevsw, &cmaj);
+ if (error) {
+ aprint_error("%s: unable to attach devsw\n",
+ sht3xtemp_cd.cd_name);
+ config_fini_component(cfdriver_ioconf_sht3xtemp,
+ cfattach_ioconf_sht3xtemp, cfdata_ioconf_sht3xtemp);
+ }
+ return error;
+#else
+ return 0;
+#endif
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ devsw_detach(NULL, &sht3x_cdevsw);
+ return config_fini_component(cfdriver_ioconf_sht3xtemp,
+ cfattach_ioconf_sht3xtemp, cfdata_ioconf_sht3xtemp);
+#else
+ return 0;
+#endif
+ default:
+ return ENOTTY;
+ }
+}
Index: src/sys/dev/i2c/sht3xreg.h
diff -u /dev/null src/sys/dev/i2c/sht3xreg.h:1.1
--- /dev/null Sat Nov 6 13:34:40 2021
+++ src/sys/dev/i2c/sht3xreg.h Sat Nov 6 13:34:40 2021
@@ -0,0 +1,103 @@
+/* $NetBSD: sht3xreg.h,v 1.1 2021/11/06 13:34:40 brad Exp $ */
+
+/*
+ * Copyright (c) 2021 Brad Spencer <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_I2C_SHT3XREG_H_
+#define _DEV_I2C_SHT3XREG_H_
+
+#define SHT3X_TYPICAL_ADDR_1 0x44
+#define SHT3X_TYPICAL_ADDR_2 0x45
+
+
+/* Measurement repeatability and clock steatching
+ * for single shot measurement command
+ */
+#define SHT3X_MEASURE_REPEATABILITY_CS_HIGH 0x2C06
+#define SHT3X_MEASURE_REPEATABILITY_CS_MEDIUM 0x2C0D
+#define SHT3X_MEASURE_REPEATABILITY_CS_LOW 0x2C10
+#define SHT3X_MEASURE_REPEATABILITY_NOCS_HIGH 0x2400
+#define SHT3X_MEASURE_REPEATABILITY_NOCS_MEDIUM 0x240B
+#define SHT3X_MEASURE_REPEATABILITY_NOCS_LOW 0x2416
+
+/* Periodic measurements ... .5 mps, 1 mps, 2 mps, 4 mps
+ * and 10 mps at various repeatability. One sets up the
+ * desired mps and repeatability and then calls fetch data
+ * to get the data back at the specified period rate
+ */
+#define SHT3X_HALF_MPS_HIGH 0x2032
+#define SHT3X_HALF_MPS_MEDIUM 0x2024
+#define SHT3X_HALF_MPS_LOW 0x202F
+#define SHT3X_ONE_MPS_HIGH 0x2130
+#define SHT3X_ONE_MPS_MEDIUM 0x2126
+#define SHT3X_ONE_MPS_LOW 0x212D
+#define SHT3X_TWO_MPS_HIGH 0x2236
+#define SHT3X_TWO_MPS_MEDIUM 0x2220
+#define SHT3X_TWO_MPS_LOW 0x222B
+#define SHT3X_FOUR_MPS_HIGH 0x2334
+#define SHT3X_FOUR_MPS_MEDIUM 0x2322
+#define SHT3X_FOUR_MPS_LOW 0x2329
+#define SHT3X_TEN_MPS_HIGH 0x2737
+#define SHT3X_TEN_MPS_MEDIUM 0x2721
+#define SHT3X_TEN_MPS_LOW 0x272A
+#define SHT3X_PERIODIC_FETCH_DATA 0xE000
+
+/* ART, accelerated response time. A method of getting periodic
+ * measurements at 4Hz
+ */
+#define SHT3X_ART_ENABLE 0x2B32
+
+/* Break command or stop periodic measurement */
+#define SHT3X_BREAK 0x3093
+
+/* The heater */
+#define SHT3X_HEATER_ENABLE 0x306D
+#define SHT3X_HEATER_DISABLE 0x3066
+
+/* status register */
+#define SHT3X_GET_STATUS_REGISTER 0xF32D
+#define SHT3X_CLEAR_STATUS_REGISTER 0x3041
+/* the bits */
+#define SHT3X_ALERT_PENDING 0x8000
+#define SHT3X_HEATER_STATUS 0x2000
+#define SHT3X_RH_TRACKING_ALERT 0x0800
+#define SHT3X_TEMP_TRACKING_ALERT 0x0400
+#define SHT3X_RESET_DETECTED 0x0010
+#define SHT3X_LAST_COMMAND_STATUS 0x0002
+#define SHT3X_WRITE_DATA_CHECKSUM 0x0001
+
+/* Alert mode */
+#define SHT3X_READ_HIGH_ALERT_SET 0xE11F
+#define SHT3X_READ_HIGH_ALERT_CLEAR 0xE114
+#define SHT3X_READ_LOW_ALERT_SET 0xE102
+#define SHT3X_READ_LOW_ALERT_CLEAR 0xE109
+#define SHT3X_WRITE_HIGH_ALERT_SET 0x611D
+#define SHT3X_WRITE_HIGH_ALERT_CLEAR 0x6116
+#define SHT3X_WRITE_LOW_ALERT_SET 0x6100
+#define SHT3X_WRITE_LOW_ALERT_CLEAR 0x610B
+
+/* Other commands */
+#define SHT3X_SOFT_RESET 0x30A2
+/* this is not documented in the datasheet, but is present in a
+ * lot of example code
+ */
+#define SHT3X_READ_SERIAL_NUMBER 0x3780
+/* this is also not defined in the datasheet, but is present in some
+ * example code. There are, however, no examples of its use.
+ */
+#define SHT3X_NO_SLEEP 0x303E
+
+#endif
Index: src/sys/dev/i2c/sht3xvar.h
diff -u /dev/null src/sys/dev/i2c/sht3xvar.h:1.1
--- /dev/null Sat Nov 6 13:34:40 2021
+++ src/sys/dev/i2c/sht3xvar.h Sat Nov 6 13:34:40 2021
@@ -0,0 +1,92 @@
+/* $NetBSD: sht3xvar.h,v 1.1 2021/11/06 13:34:40 brad Exp $ */
+
+/*
+ * Copyright (c) 2021 Brad Spencer <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_I2C_SHT3XVAR_H_
+#define _DEV_I2C_SHT3XVAR_H_
+
+#define SHT3X_NUM_SENSORS 2
+#define SHT3X_HUMIDITY_SENSOR 0
+#define SHT3X_TEMP_SENSOR 1
+
+#define SHT3X_MODE_NAME 12
+#define SHT3X_REP_NAME 7
+#define SHT3X_RATE_NAME 8
+
+struct sht3x_sc {
+ int sc_sht3xdebug;
+ device_t sc_dev;
+ i2c_tag_t sc_tag;
+ i2c_addr_t sc_addr;
+ kmutex_t sc_dying_mutex; /* for cleaning up */
+ kmutex_t sc_threadmutex; /* for the measurement kthread */
+ kmutex_t sc_mutex; /* for reading the i2c bus */
+ kmutex_t sc_read_mutex; /* for from the data queue */
+ kcondvar_t sc_condvar; /* for shutting down the thread */
+ kcondvar_t sc_condreadready; /* when there is data to be read */
+ kcondvar_t sc_cond_dying; /* interlock when cleaning up */
+ struct lwp *sc_thread;
+ int sc_numsensors;
+ struct sysmon_envsys *sc_sme;
+ struct sysctllog *sc_sht3xlog;
+ envsys_data_t sc_sensors[SHT3X_NUM_SENSORS];
+ bool sc_ignorecrc;
+ char sc_mode[SHT3X_MODE_NAME];
+ bool sc_isperiodic;
+ char sc_repeatability[SHT3X_REP_NAME];
+ char sc_periodic_rate[SHT3X_RATE_NAME];
+ int sc_readattempts;
+ bool sc_heateron;
+ bool sc_stopping;
+ bool sc_initperiodic;
+ uint8_t sc_pbuffer[6];
+ bool sc_dying;
+ bool sc_opened;
+ bool sc_wassingleshot;
+ pool_cache_t sc_readpool;
+ char *sc_readpoolname;
+ SIMPLEQ_HEAD(,sht3x_read_q) sc_read_queue;
+};
+
+struct sht3x_read_q {
+ uint8_t measurement[6];
+ SIMPLEQ_ENTRY(sht3x_read_q) read_q;
+};
+
+struct sht3x_sensor {
+ const char *desc;
+ enum envsys_units type;
+};
+
+struct sht3x_timing {
+ uint16_t cmd;
+ int typicaldelay;
+};
+
+struct sht3x_periodic {
+ const char *repeatability;
+ const char *rate;
+ int sdelay;
+ uint16_t cmd;
+};
+
+struct sht3x_repeatability {
+ const char *text;
+ uint16_t cmd;
+};
+
+#endif
Index: src/sys/modules/sht3xtemp/Makefile
diff -u /dev/null src/sys/modules/sht3xtemp/Makefile:1.1
--- /dev/null Sat Nov 6 13:34:40 2021
+++ src/sys/modules/sht3xtemp/Makefile Sat Nov 6 13:34:39 2021
@@ -0,0 +1,11 @@
+.include "../Makefile.inc"
+
+.PATH: ${S}/dev/i2c
+
+KMOD= sht3xtemp
+IOCONF= sht3xtemp.ioconf
+SRCS= sht3x.c
+
+WARNS= 3
+
+.include <bsd.kmodule.mk>
Index: src/sys/modules/sht3xtemp/sht3xtemp.ioconf
diff -u /dev/null src/sys/modules/sht3xtemp/sht3xtemp.ioconf:1.1
--- /dev/null Sat Nov 6 13:34:40 2021
+++ src/sys/modules/sht3xtemp/sht3xtemp.ioconf Sat Nov 6 13:34:39 2021
@@ -0,0 +1,8 @@
+ioconf sht3xtemp
+
+include "conf/files"
+
+pseudo-root iic*
+
+sht3xtemp* at iic? addr 0x44
+sht3xtemp* at iic? addr 0x45