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 <b...@anduin.eldar.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd 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 b...@anduin.eldar.org .

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 <b...@anduin.eldar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__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, &reg, 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 <b...@anduin.eldar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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 <b...@anduin.eldar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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

Reply via email to