Home | History | Annotate | Line # | Download | only in i2c
sht4x.c revision 1.3
      1  1.3  pgoyette /*	$NetBSD: sht4x.c,v 1.3 2022/03/30 00:06:50 pgoyette Exp $	*/
      2  1.1      brad 
      3  1.1      brad /*
      4  1.1      brad  * Copyright (c) 2021 Brad Spencer <brad (at) anduin.eldar.org>
      5  1.1      brad  *
      6  1.1      brad  * Permission to use, copy, modify, and distribute this software for any
      7  1.1      brad  * purpose with or without fee is hereby granted, provided that the above
      8  1.1      brad  * copyright notice and this permission notice appear in all copies.
      9  1.1      brad  *
     10  1.1      brad  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  1.1      brad  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  1.1      brad  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  1.1      brad  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  1.1      brad  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  1.1      brad  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  1.1      brad  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  1.1      brad  */
     18  1.1      brad 
     19  1.1      brad #include <sys/cdefs.h>
     20  1.3  pgoyette __KERNEL_RCSID(0, "$NetBSD: sht4x.c,v 1.3 2022/03/30 00:06:50 pgoyette Exp $");
     21  1.1      brad 
     22  1.1      brad /*
     23  1.1      brad   Driver for the Sensirion SHT40/SHT41/SHT45
     24  1.1      brad */
     25  1.1      brad 
     26  1.1      brad #include <sys/param.h>
     27  1.1      brad #include <sys/systm.h>
     28  1.1      brad #include <sys/kernel.h>
     29  1.1      brad #include <sys/device.h>
     30  1.1      brad #include <sys/module.h>
     31  1.1      brad #include <sys/sysctl.h>
     32  1.1      brad #include <sys/mutex.h>
     33  1.1      brad 
     34  1.1      brad #include <dev/sysmon/sysmonvar.h>
     35  1.1      brad #include <dev/i2c/i2cvar.h>
     36  1.1      brad #include <dev/i2c/sht4xreg.h>
     37  1.1      brad #include <dev/i2c/sht4xvar.h>
     38  1.1      brad 
     39  1.1      brad 
     40  1.1      brad static uint8_t 	sht4x_crc(uint8_t *, size_t);
     41  1.1      brad static int 	sht4x_poke(i2c_tag_t, i2c_addr_t, bool);
     42  1.1      brad static int 	sht4x_match(device_t, cfdata_t, void *);
     43  1.1      brad static void 	sht4x_attach(device_t, device_t, void *);
     44  1.1      brad static int 	sht4x_detach(device_t, int);
     45  1.1      brad static void 	sht4x_refresh(struct sysmon_envsys *, envsys_data_t *);
     46  1.1      brad static int 	sht4x_verify_sysctl(SYSCTLFN_ARGS);
     47  1.1      brad static int 	sht4x_verify_sysctl_resolution(SYSCTLFN_ARGS);
     48  1.1      brad static int 	sht4x_verify_sysctl_heateron(SYSCTLFN_ARGS);
     49  1.1      brad static int 	sht4x_verify_sysctl_heatervalue(SYSCTLFN_ARGS);
     50  1.1      brad static int 	sht4x_verify_sysctl_heaterpulse(SYSCTLFN_ARGS);
     51  1.1      brad 
     52  1.1      brad #define SHT4X_DEBUG
     53  1.1      brad #ifdef SHT4X_DEBUG
     54  1.1      brad #define DPRINTF(s, l, x) \
     55  1.1      brad     do { \
     56  1.1      brad 	if (l <= s->sc_sht4xdebug) \
     57  1.1      brad 	    printf x; \
     58  1.1      brad     } while (/*CONSTCOND*/0)
     59  1.1      brad #else
     60  1.1      brad #define DPRINTF(s, l, x)
     61  1.1      brad #endif
     62  1.1      brad 
     63  1.1      brad CFATTACH_DECL_NEW(sht4xtemp, sizeof(struct sht4x_sc),
     64  1.1      brad     sht4x_match, sht4x_attach, sht4x_detach, NULL);
     65  1.1      brad 
     66  1.1      brad static struct sht4x_sensor sht4x_sensors[] = {
     67  1.1      brad 	{
     68  1.1      brad 		.desc = "humidity",
     69  1.1      brad 		.type = ENVSYS_SRELHUMIDITY,
     70  1.1      brad 	},
     71  1.1      brad 	{
     72  1.1      brad 		.desc = "temperature",
     73  1.1      brad 		.type = ENVSYS_STEMP,
     74  1.1      brad 	}
     75  1.1      brad };
     76  1.1      brad 
     77  1.1      brad /* The typical delays are documented in the datasheet for the chip.
     78  1.1      brad    There is no need to be very accurate with these, just rough estimates
     79  1.1      brad    will work fine.
     80  1.1      brad */
     81  1.1      brad 
     82  1.1      brad static struct sht4x_timing sht4x_timings[] = {
     83  1.1      brad 	{
     84  1.1      brad 		.cmd = SHT4X_READ_SERIAL,
     85  1.1      brad 		.typicaldelay = 5000,
     86  1.1      brad 	},
     87  1.1      brad 	{
     88  1.1      brad 		.cmd = SHT4X_SOFT_RESET,
     89  1.1      brad 		.typicaldelay = 1000,
     90  1.1      brad 	},
     91  1.1      brad 	{
     92  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION,
     93  1.1      brad 		.typicaldelay = 8000,
     94  1.1      brad 	},
     95  1.1      brad 	{
     96  1.1      brad 		.cmd = SHT4X_MEASURE_MEDIUM_PRECISION,
     97  1.1      brad 		.typicaldelay = 4000,
     98  1.1      brad 	},
     99  1.1      brad 	{
    100  1.1      brad 		.cmd = SHT4X_MEASURE_LOW_PRECISION,
    101  1.1      brad 		.typicaldelay = 2000,
    102  1.1      brad 	},
    103  1.1      brad 	{
    104  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_1_S,
    105  1.1      brad 		.typicaldelay = 1000000,
    106  1.1      brad 	},
    107  1.1      brad 	{
    108  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_1_S,
    109  1.1      brad 		.typicaldelay = 1000000,
    110  1.1      brad 	},
    111  1.1      brad 	{
    112  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_1_S,
    113  1.1      brad 		.typicaldelay = 1000000,
    114  1.1      brad 	},
    115  1.1      brad 	{
    116  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_TENTH_S,
    117  1.1      brad 		.typicaldelay = 100000,
    118  1.1      brad 	},
    119  1.1      brad 	{
    120  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_TENTH_S,
    121  1.1      brad 		.typicaldelay = 100000,
    122  1.1      brad 	},
    123  1.1      brad 	{
    124  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_TENTH_S,
    125  1.1      brad 		.typicaldelay = 100000,
    126  1.1      brad 	}
    127  1.1      brad };
    128  1.1      brad 
    129  1.1      brad /* Used when the heater is not on to find the command to use for the
    130  1.1      brad  * measurement.
    131  1.1      brad  */
    132  1.1      brad 
    133  1.1      brad static struct sht4x_resolution sht4x_resolutions[] = {
    134  1.1      brad 	{
    135  1.1      brad 		.text = "high",
    136  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION,
    137  1.1      brad 	},
    138  1.1      brad 	{
    139  1.1      brad 		.text = "medium",
    140  1.1      brad 		.cmd = SHT4X_MEASURE_MEDIUM_PRECISION,
    141  1.1      brad 	},
    142  1.1      brad 	{
    143  1.1      brad 		.text = "low",
    144  1.1      brad 		.cmd = SHT4X_MEASURE_LOW_PRECISION,
    145  1.1      brad 	}
    146  1.1      brad };
    147  1.1      brad 
    148  1.1      brad static const char sht4x_resolution_names[] =
    149  1.1      brad     "high, medium, low";
    150  1.1      brad 
    151  1.1      brad static struct sht4x_heaterpulse sht4x_heaterpulses[] = {
    152  1.1      brad 	{
    153  1.1      brad 		.length = "short",
    154  1.1      brad 	},
    155  1.1      brad 	{
    156  1.1      brad 		.length = "long",
    157  1.1      brad 	}
    158  1.1      brad };
    159  1.1      brad 
    160  1.1      brad /* This is consulted when the heater is on for which command is to be
    161  1.1      brad    used for the measurement.
    162  1.1      brad */
    163  1.1      brad 
    164  1.1      brad static struct sht4x_heateron_command sht4x_heateron_commands[] = {
    165  1.1      brad 	{
    166  1.1      brad 		.heatervalue = 1,
    167  1.1      brad 		.pulselength = "short",
    168  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_TENTH_S,
    169  1.1      brad 	},
    170  1.1      brad 	{
    171  1.1      brad 		.heatervalue = 2,
    172  1.1      brad 		.pulselength = "short",
    173  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_TENTH_S,
    174  1.1      brad 	},
    175  1.1      brad 	{
    176  1.1      brad 		.heatervalue = 3,
    177  1.1      brad 		.pulselength = "short",
    178  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_TENTH_S,
    179  1.1      brad 	},
    180  1.1      brad 	{
    181  1.1      brad 		.heatervalue = 1,
    182  1.1      brad 		.pulselength = "long",
    183  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_1_S,
    184  1.1      brad 	},
    185  1.1      brad 	{
    186  1.1      brad 		.heatervalue = 2,
    187  1.1      brad 		.pulselength = "long",
    188  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_1_S,
    189  1.1      brad 	},
    190  1.1      brad 	{
    191  1.1      brad 		.heatervalue = 3,
    192  1.1      brad 		.pulselength = "long",
    193  1.1      brad 		.cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_1_S,
    194  1.1      brad 	}
    195  1.1      brad };
    196  1.1      brad 
    197  1.1      brad static const char sht4x_heaterpulse_names[] =
    198  1.1      brad     "short, long";
    199  1.1      brad 
    200  1.1      brad int
    201  1.1      brad sht4x_verify_sysctl(SYSCTLFN_ARGS)
    202  1.1      brad {
    203  1.1      brad 	int error, t;
    204  1.1      brad 	struct sysctlnode node;
    205  1.1      brad 
    206  1.1      brad 	node = *rnode;
    207  1.1      brad 	t = *(int *)rnode->sysctl_data;
    208  1.1      brad 	node.sysctl_data = &t;
    209  1.1      brad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    210  1.1      brad 	if (error || newp == NULL)
    211  1.1      brad 		return error;
    212  1.1      brad 
    213  1.1      brad 	if (t < 0)
    214  1.1      brad 		return EINVAL;
    215  1.1      brad 
    216  1.1      brad 	*(int *)rnode->sysctl_data = t;
    217  1.1      brad 
    218  1.1      brad 	return 0;
    219  1.1      brad }
    220  1.1      brad 
    221  1.1      brad /* None of the heater and resolutions sysctls change anything on the chip in
    222  1.1      brad    real time.  The values set are used to send different commands depending on
    223  1.1      brad    how they are set up.
    224  1.1      brad 
    225  1.1      brad    What this implies is that the chip could be reset and the driver would not care.
    226  1.1      brad 
    227  1.1      brad */
    228  1.1      brad 
    229  1.1      brad int
    230  1.1      brad sht4x_verify_sysctl_resolution(SYSCTLFN_ARGS)
    231  1.1      brad {
    232  1.1      brad 	char buf[SHT4X_RES_NAME];
    233  1.1      brad 	struct sht4x_sc *sc;
    234  1.1      brad 	struct sysctlnode node;
    235  1.1      brad 	int error = 0;
    236  1.1      brad 	size_t i;
    237  1.1      brad 
    238  1.1      brad 	node = *rnode;
    239  1.1      brad 	sc = node.sysctl_data;
    240  1.1      brad 	(void) memcpy(buf, sc->sc_resolution, SHT4X_RES_NAME);
    241  1.1      brad 	node.sysctl_data = buf;
    242  1.1      brad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    243  1.1      brad 	if (error || newp == NULL)
    244  1.1      brad 		return error;
    245  1.1      brad 
    246  1.1      brad 	for (i = 0; i < __arraycount(sht4x_resolutions); i++) {
    247  1.1      brad 		if (strncmp(node.sysctl_data, sht4x_resolutions[i].text,
    248  1.1      brad 		    SHT4X_RES_NAME) == 0) {
    249  1.1      brad 			break;
    250  1.1      brad 		}
    251  1.1      brad 	}
    252  1.1      brad 
    253  1.1      brad 	if (i == __arraycount(sht4x_resolutions))
    254  1.1      brad 		return EINVAL;
    255  1.1      brad 	(void) memcpy(sc->sc_resolution, node.sysctl_data, SHT4X_RES_NAME);
    256  1.1      brad 
    257  1.1      brad 	return error;
    258  1.1      brad }
    259  1.1      brad 
    260  1.1      brad int
    261  1.1      brad sht4x_verify_sysctl_heateron(SYSCTLFN_ARGS)
    262  1.1      brad {
    263  1.1      brad 	int 		error;
    264  1.1      brad 	bool 		t;
    265  1.1      brad 	struct sht4x_sc *sc;
    266  1.1      brad 	struct sysctlnode node;
    267  1.1      brad 
    268  1.1      brad 	node = *rnode;
    269  1.1      brad 	sc = node.sysctl_data;
    270  1.1      brad 	t = sc->sc_heateron;
    271  1.1      brad 	node.sysctl_data = &t;
    272  1.1      brad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    273  1.1      brad 	if (error || newp == NULL)
    274  1.1      brad 		return error;
    275  1.1      brad 
    276  1.1      brad 	sc->sc_heateron = t;
    277  1.1      brad 
    278  1.1      brad 	return error;
    279  1.1      brad }
    280  1.1      brad 
    281  1.1      brad int
    282  1.1      brad sht4x_verify_sysctl_heatervalue(SYSCTLFN_ARGS)
    283  1.1      brad {
    284  1.1      brad 	int 		error = 0, t;
    285  1.1      brad 	struct sht4x_sc *sc;
    286  1.1      brad 	struct sysctlnode node;
    287  1.1      brad 
    288  1.1      brad 	node = *rnode;
    289  1.1      brad 	sc = node.sysctl_data;
    290  1.1      brad 	t = sc->sc_heaterval;
    291  1.1      brad 	node.sysctl_data = &t;
    292  1.1      brad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    293  1.1      brad 	if (error || newp == NULL)
    294  1.1      brad 		return (error);
    295  1.1      brad 
    296  1.1      brad 	if (t < 1 || t > 3)
    297  1.1      brad 		return (EINVAL);
    298  1.1      brad 
    299  1.1      brad 	sc->sc_heaterval = t;
    300  1.1      brad 
    301  1.1      brad 	return error;
    302  1.1      brad }
    303  1.1      brad 
    304  1.1      brad int
    305  1.1      brad sht4x_verify_sysctl_heaterpulse(SYSCTLFN_ARGS)
    306  1.1      brad {
    307  1.1      brad 	char buf[SHT4X_PULSE_NAME];
    308  1.1      brad 	struct sht4x_sc *sc;
    309  1.1      brad 	struct sysctlnode node;
    310  1.1      brad 	int error = 0;
    311  1.1      brad 	size_t i;
    312  1.1      brad 
    313  1.1      brad 	node = *rnode;
    314  1.1      brad 	sc = node.sysctl_data;
    315  1.1      brad 	(void) memcpy(buf, sc->sc_heaterpulse, SHT4X_PULSE_NAME);
    316  1.1      brad 	node.sysctl_data = buf;
    317  1.1      brad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    318  1.1      brad 	if (error || newp == NULL)
    319  1.1      brad 		return error;
    320  1.1      brad 
    321  1.1      brad 	for (i = 0; i < __arraycount(sht4x_heaterpulses); i++) {
    322  1.1      brad 		if (strncmp(node.sysctl_data, sht4x_heaterpulses[i].length,
    323  1.1      brad 		    SHT4X_RES_NAME) == 0) {
    324  1.1      brad 			break;
    325  1.1      brad 		}
    326  1.1      brad 	}
    327  1.1      brad 
    328  1.1      brad 	if (i == __arraycount(sht4x_heaterpulses))
    329  1.1      brad 		return EINVAL;
    330  1.1      brad 	(void) memcpy(sc->sc_heaterpulse, node.sysctl_data, SHT4X_PULSE_NAME);
    331  1.1      brad 
    332  1.1      brad 	return error;
    333  1.1      brad }
    334  1.1      brad 
    335  1.1      brad static int
    336  1.1      brad sht4x_cmddelay(uint8_t cmd)
    337  1.1      brad {
    338  1.1      brad 	int r = -1;
    339  1.1      brad 
    340  1.1      brad 	for(int i = 0;i < __arraycount(sht4x_timings);i++) {
    341  1.1      brad 		if (cmd == sht4x_timings[i].cmd) {
    342  1.1      brad 			r = sht4x_timings[i].typicaldelay;
    343  1.1      brad 			break;
    344  1.1      brad 		}
    345  1.1      brad 	}
    346  1.1      brad 
    347  1.1      brad 	if (r == -1) {
    348  1.1      brad 		panic("Bad command look up in cmd delay: cmd: %d\n",cmd);
    349  1.1      brad 	}
    350  1.1      brad 
    351  1.1      brad 	return r;
    352  1.1      brad }
    353  1.1      brad 
    354  1.1      brad static int
    355  1.1      brad sht4x_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd,
    356  1.1      brad     uint8_t clen, uint8_t *buf, size_t blen, int readattempts)
    357  1.1      brad {
    358  1.1      brad 	int error;
    359  1.1      brad 	int cmddelay;
    360  1.1      brad 
    361  1.1      brad 	error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,cmd,clen,NULL,0,0);
    362  1.1      brad 
    363  1.1      brad 	/* Every command returns something except for the soft reset
    364  1.1      brad 	   which returns nothing.  This chip is also nice in that pretty
    365  1.1      brad 	   much every command that returns something does it in the same way.
    366  1.1      brad 	*/
    367  1.1      brad 	if (error == 0 && cmd[0] != SHT4X_SOFT_RESET) {
    368  1.1      brad 		cmddelay = sht4x_cmddelay(cmd[0]);
    369  1.1      brad 		delay(cmddelay);
    370  1.1      brad 
    371  1.1      brad 		for (int aint = 0; aint < readattempts; aint++) {
    372  1.1      brad 			error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,buf,blen,0);
    373  1.1      brad 			if (error == 0)
    374  1.1      brad 				break;
    375  1.1      brad 			delay(1000);
    376  1.1      brad 		}
    377  1.1      brad 	}
    378  1.1      brad 
    379  1.1      brad 	return error;
    380  1.1      brad }
    381  1.1      brad 
    382  1.1      brad static int
    383  1.1      brad sht4x_cmdr(struct sht4x_sc *sc, uint8_t cmd, uint8_t *buf, size_t blen)
    384  1.1      brad {
    385  1.1      brad 	return sht4x_cmd(sc->sc_tag, sc->sc_addr, &cmd, 1, buf, blen, sc->sc_readattempts);
    386  1.1      brad }
    387  1.1      brad 
    388  1.1      brad static	uint8_t
    389  1.1      brad sht4x_crc(uint8_t * data, size_t size)
    390  1.1      brad {
    391  1.1      brad 	uint8_t crc = 0xFF;
    392  1.1      brad 
    393  1.1      brad 	for (size_t i = 0; i < size; i++) {
    394  1.1      brad 		crc ^= data[i];
    395  1.1      brad 		for (size_t j = 8; j > 0; j--) {
    396  1.1      brad 			if (crc & 0x80)
    397  1.1      brad 				crc = (crc << 1) ^ 0x131;
    398  1.1      brad 			else
    399  1.1      brad 				crc <<= 1;
    400  1.1      brad 		}
    401  1.1      brad 	}
    402  1.1      brad 	return crc;
    403  1.1      brad }
    404  1.1      brad 
    405  1.1      brad static int
    406  1.1      brad sht4x_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
    407  1.1      brad {
    408  1.1      brad 	uint8_t reg = SHT4X_READ_SERIAL;
    409  1.1      brad 	uint8_t buf[6];
    410  1.1      brad 	int error;
    411  1.1      brad 
    412  1.1      brad 	error = sht4x_cmd(tag, addr, &reg, 1, buf, 6, 10);
    413  1.1      brad 	if (matchdebug) {
    414  1.1      brad 		printf("poke X 1: %d\n", error);
    415  1.1      brad 	}
    416  1.1      brad 	return error;
    417  1.1      brad }
    418  1.1      brad 
    419  1.1      brad static int
    420  1.1      brad sht4x_sysctl_init(struct sht4x_sc *sc)
    421  1.1      brad {
    422  1.1      brad 	int error;
    423  1.1      brad 	const struct sysctlnode *cnode;
    424  1.1      brad 	int sysctlroot_num;
    425  1.1      brad 
    426  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    427  1.1      brad 	    0, CTLTYPE_NODE, device_xname(sc->sc_dev),
    428  1.1      brad 	    SYSCTL_DESCR("sht4x controls"), NULL, 0, NULL, 0, CTL_HW,
    429  1.1      brad 	    CTL_CREATE, CTL_EOL)) != 0)
    430  1.1      brad 		return error;
    431  1.1      brad 
    432  1.1      brad 	sysctlroot_num = cnode->sysctl_num;
    433  1.1      brad 
    434  1.1      brad #ifdef SHT4X_DEBUG
    435  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    436  1.1      brad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
    437  1.1      brad 	    SYSCTL_DESCR("Debug level"), sht4x_verify_sysctl, 0,
    438  1.1      brad 	    &sc->sc_sht4xdebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
    439  1.1      brad 	    CTL_EOL)) != 0)
    440  1.1      brad 		return error;
    441  1.1      brad 
    442  1.1      brad #endif
    443  1.1      brad 
    444  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    445  1.1      brad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
    446  1.1      brad 	    SYSCTL_DESCR("The number of times to attempt to read the values"),
    447  1.1      brad 	    sht4x_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW,
    448  1.1      brad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    449  1.1      brad 		return error;
    450  1.1      brad 
    451  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    452  1.1      brad 	    CTLFLAG_READONLY, CTLTYPE_STRING, "resolutions",
    453  1.1      brad 	    SYSCTL_DESCR("Valid resolutions"), 0, 0,
    454  1.1      brad 	    __UNCONST(sht4x_resolution_names),
    455  1.1      brad 	    sizeof(sht4x_resolution_names) + 1,
    456  1.1      brad 	    CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    457  1.1      brad 		return error;
    458  1.1      brad 
    459  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    460  1.1      brad 	    CTLFLAG_READWRITE, CTLTYPE_STRING, "resolution",
    461  1.1      brad 	    SYSCTL_DESCR("Resolution of RH and Temp"),
    462  1.1      brad 	    sht4x_verify_sysctl_resolution, 0, (void *) sc,
    463  1.1      brad 	    SHT4X_RES_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    464  1.1      brad 		return error;
    465  1.1      brad 
    466  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    467  1.1      brad 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc",
    468  1.1      brad 	    SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc,
    469  1.1      brad 	    0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    470  1.1      brad 		return error;
    471  1.1      brad 
    472  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    473  1.1      brad 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "heateron",
    474  1.1      brad 	    SYSCTL_DESCR("Heater on"), sht4x_verify_sysctl_heateron, 0,
    475  1.1      brad 	    (void *)sc, 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    476  1.1      brad 		return error;
    477  1.1      brad 
    478  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    479  1.1      brad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "heaterstrength",
    480  1.1      brad 	    SYSCTL_DESCR("Heater strength 1 to 3"),
    481  1.1      brad 	    sht4x_verify_sysctl_heatervalue, 0, (void *)sc, 0, CTL_HW,
    482  1.1      brad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    483  1.1      brad 		return error;
    484  1.1      brad 
    485  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    486  1.1      brad 	    CTLFLAG_READONLY, CTLTYPE_STRING, "heaterpulses",
    487  1.1      brad 	    SYSCTL_DESCR("Valid heater pulse lengths"), 0, 0,
    488  1.1      brad 	    __UNCONST(sht4x_heaterpulse_names),
    489  1.1      brad 	    sizeof(sht4x_heaterpulse_names) + 1,
    490  1.1      brad 	    CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    491  1.1      brad 		return error;
    492  1.1      brad 
    493  1.1      brad 	if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
    494  1.1      brad 	    CTLFLAG_READWRITE, CTLTYPE_STRING, "heaterpulse",
    495  1.1      brad 	    SYSCTL_DESCR("Heater pulse length"),
    496  1.1      brad 	    sht4x_verify_sysctl_heaterpulse, 0, (void *) sc,
    497  1.1      brad 	    SHT4X_RES_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
    498  1.1      brad 		return error;
    499  1.1      brad 	return 0;
    500  1.1      brad }
    501  1.1      brad 
    502  1.1      brad static int
    503  1.1      brad sht4x_match(device_t parent, cfdata_t match, void *aux)
    504  1.1      brad {
    505  1.1      brad 	struct i2c_attach_args *ia = aux;
    506  1.1      brad 	int error, match_result;
    507  1.1      brad 	const bool matchdebug = false;
    508  1.1      brad 
    509  1.1      brad 	if (iic_use_direct_match(ia, match, NULL, &match_result))
    510  1.1      brad 		return match_result;
    511  1.1      brad 
    512  1.1      brad 	/* indirect config - check for configured address */
    513  1.1      brad 	if (ia->ia_addr != SHT4X_TYPICAL_ADDR)
    514  1.1      brad 		return 0;
    515  1.1      brad 
    516  1.1      brad 	/*
    517  1.1      brad 	 * Check to see if something is really at this i2c address. This will
    518  1.1      brad 	 * keep phantom devices from appearing
    519  1.1      brad 	 */
    520  1.1      brad 	if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
    521  1.1      brad 		if (matchdebug)
    522  1.1      brad 			printf("in match acquire bus failed\n");
    523  1.1      brad 		return 0;
    524  1.1      brad 	}
    525  1.1      brad 
    526  1.1      brad 	error = sht4x_poke(ia->ia_tag, ia->ia_addr, matchdebug);
    527  1.1      brad 	iic_release_bus(ia->ia_tag, 0);
    528  1.1      brad 
    529  1.1      brad 	return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
    530  1.1      brad }
    531  1.1      brad 
    532  1.1      brad static void
    533  1.1      brad sht4x_attach(device_t parent, device_t self, void *aux)
    534  1.1      brad {
    535  1.1      brad 	struct sht4x_sc *sc;
    536  1.1      brad 	struct i2c_attach_args *ia;
    537  1.1      brad 	int error, i;
    538  1.1      brad 	int ecount = 0;
    539  1.1      brad 	uint8_t buf[6];
    540  1.1      brad 	uint8_t sncrcpt1, sncrcpt2;
    541  1.1      brad 
    542  1.1      brad 	ia = aux;
    543  1.1      brad 	sc = device_private(self);
    544  1.1      brad 
    545  1.1      brad 	sc->sc_dev = self;
    546  1.1      brad 	sc->sc_tag = ia->ia_tag;
    547  1.1      brad 	sc->sc_addr = ia->ia_addr;
    548  1.1      brad 	sc->sc_sht4xdebug = 0;
    549  1.1      brad 	strlcpy(sc->sc_resolution,"high",SHT4X_RES_NAME);
    550  1.1      brad 	sc->sc_readattempts = 10;
    551  1.1      brad 	sc->sc_ignorecrc = false;
    552  1.1      brad 	sc->sc_heateron = false;
    553  1.1      brad 	sc->sc_heaterval = 1;
    554  1.1      brad 	strlcpy(sc->sc_heaterpulse,"short",SHT4X_PULSE_NAME);
    555  1.1      brad 	sc->sc_sme = NULL;
    556  1.1      brad 
    557  1.1      brad 	aprint_normal("\n");
    558  1.1      brad 
    559  1.1      brad 	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
    560  1.1      brad 	sc->sc_numsensors = __arraycount(sht4x_sensors);
    561  1.1      brad 
    562  1.1      brad 	if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
    563  1.1      brad 		aprint_error_dev(self,
    564  1.1      brad 		    "Unable to create sysmon structure\n");
    565  1.1      brad 		sc->sc_sme = NULL;
    566  1.1      brad 		return;
    567  1.1      brad 	}
    568  1.1      brad 	if ((error = sht4x_sysctl_init(sc)) != 0) {
    569  1.1      brad 		aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
    570  1.1      brad 		goto out;
    571  1.1      brad 	}
    572  1.1      brad 
    573  1.1      brad 	error = iic_acquire_bus(sc->sc_tag, 0);
    574  1.1      brad 	if (error) {
    575  1.1      brad 		aprint_error_dev(self, "Could not acquire iic bus: %d\n",
    576  1.1      brad 		    error);
    577  1.1      brad 		goto out;
    578  1.1      brad 	}
    579  1.1      brad 
    580  1.1      brad 	error = sht4x_cmdr(sc, SHT4X_SOFT_RESET, NULL, 0);
    581  1.1      brad 	if (error != 0)
    582  1.1      brad 		aprint_error_dev(self, "Reset failed: %d\n", error);
    583  1.1      brad 
    584  1.1      brad 	delay(1000); /* 1 ms max */
    585  1.1      brad 
    586  1.1      brad 	error = sht4x_cmdr(sc, SHT4X_READ_SERIAL, buf, 6);
    587  1.1      brad 	if (error) {
    588  1.1      brad 		aprint_error_dev(self, "Failed to read serial number: %d\n",
    589  1.1      brad 		    error);
    590  1.1      brad 		ecount++;
    591  1.1      brad 	}
    592  1.1      brad 
    593  1.1      brad 	sncrcpt1 = sht4x_crc(&buf[0],2);
    594  1.1      brad 	sncrcpt2 = sht4x_crc(&buf[3],2);
    595  1.1      brad 
    596  1.1      brad 	DPRINTF(sc, 2, ("%s: read serial number values: %02x%02x - %02x, %02x%02x - %02x ; %02x %02x\n",
    597  1.1      brad 	    device_xname(sc->sc_dev), buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], sncrcpt1, sncrcpt2));
    598  1.1      brad 
    599  1.1      brad 	iic_release_bus(sc->sc_tag, 0);
    600  1.1      brad 	if (error != 0) {
    601  1.1      brad 		aprint_error_dev(self, "Unable to setup device\n");
    602  1.1      brad 		goto out;
    603  1.1      brad 	}
    604  1.1      brad 
    605  1.1      brad 	for (i = 0; i < sc->sc_numsensors; i++) {
    606  1.1      brad 		strlcpy(sc->sc_sensors[i].desc, sht4x_sensors[i].desc,
    607  1.1      brad 		    sizeof(sc->sc_sensors[i].desc));
    608  1.1      brad 
    609  1.1      brad 		sc->sc_sensors[i].units = sht4x_sensors[i].type;
    610  1.1      brad 		sc->sc_sensors[i].state = ENVSYS_SINVALID;
    611  1.1      brad 
    612  1.1      brad 		DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
    613  1.1      brad 		    sc->sc_sensors[i].desc));
    614  1.1      brad 
    615  1.1      brad 		error = sysmon_envsys_sensor_attach(sc->sc_sme,
    616  1.1      brad 		    &sc->sc_sensors[i]);
    617  1.1      brad 		if (error) {
    618  1.1      brad 			aprint_error_dev(self,
    619  1.1      brad 			    "Unable to attach sensor %d: %d\n", i, error);
    620  1.1      brad 			goto out;
    621  1.1      brad 		}
    622  1.1      brad 	}
    623  1.1      brad 
    624  1.1      brad 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
    625  1.1      brad 	sc->sc_sme->sme_cookie = sc;
    626  1.1      brad 	sc->sc_sme->sme_refresh = sht4x_refresh;
    627  1.1      brad 
    628  1.1      brad 	DPRINTF(sc, 2, ("sht4x_attach: registering with envsys\n"));
    629  1.1      brad 
    630  1.1      brad 	if (sysmon_envsys_register(sc->sc_sme)) {
    631  1.1      brad 		aprint_error_dev(self,
    632  1.1      brad 			"unable to register with sysmon\n");
    633  1.1      brad 		sysmon_envsys_destroy(sc->sc_sme);
    634  1.1      brad 		sc->sc_sme = NULL;
    635  1.1      brad 		return;
    636  1.1      brad 	}
    637  1.1      brad 
    638  1.1      brad 	/* There is no documented way to ask the chip what version it is.  This
    639  1.1      brad 	   is likely fine as the only apparent difference is in how precise the
    640  1.1      brad 	   measurements will be.  The actual conversation with the chip is
    641  1.1      brad 	   identical no matter which one you are talking to.
    642  1.1      brad 	*/
    643  1.1      brad 
    644  1.1      brad 	aprint_normal_dev(self, "Sensirion SHT40/SHT41/SHT45, "
    645  1.1      brad 	    "Serial number: %02x%02x%02x%02x%s",
    646  1.1      brad 	    buf[0], buf[1], buf[3], buf[4],
    647  1.1      brad 	    (sncrcpt1 == buf[2] && sncrcpt2 == buf[5]) ? "\n" : " (bad crc)\n");
    648  1.1      brad 	return;
    649  1.1      brad out:
    650  1.1      brad 	sysmon_envsys_destroy(sc->sc_sme);
    651  1.1      brad 	sc->sc_sme = NULL;
    652  1.1      brad }
    653  1.1      brad 
    654  1.1      brad /* If you use the heater on this chip, there is no documented choice but to use
    655  1.1      brad    the highest precision.  If the heater is not in use one may select different
    656  1.1      brad    precisions or repeatability for the measurement.
    657  1.1      brad 
    658  1.1      brad    Further, if the heater is used, it will only be active during the measurement.
    659  1.1      brad    The use of the heater will add delay to the measurement as chip will not
    660  1.1      brad    return anything until the heater pulse time is over.
    661  1.1      brad */
    662  1.1      brad 
    663  1.1      brad static uint8_t
    664  1.1      brad sht4x_compute_measure_command(char *resolution, bool heateron,
    665  1.1      brad     int heatervalue, char *heaterpulse)
    666  1.1      brad {
    667  1.1      brad 	int i;
    668  1.1      brad 	uint8_t r;
    669  1.1      brad 
    670  1.1      brad 	if (heateron == false) {
    671  1.1      brad 		for (i = 0; i < __arraycount(sht4x_resolutions); i++) {
    672  1.1      brad 			if (strncmp(resolution, sht4x_resolutions[i].text,
    673  1.1      brad 			    SHT4X_RES_NAME) == 0) {
    674  1.1      brad 				r = sht4x_resolutions[i].cmd;
    675  1.1      brad 				break;
    676  1.1      brad 			}
    677  1.1      brad 		}
    678  1.1      brad 
    679  1.1      brad 		if (i == __arraycount(sht4x_resolutions))
    680  1.1      brad 			panic("Heater off could not find command for resolution: %s\n",resolution);
    681  1.1      brad 	} else {
    682  1.1      brad 		for (i = 0; i < __arraycount(sht4x_heateron_commands); i++) {
    683  1.1      brad 			if (heatervalue == sht4x_heateron_commands[i].heatervalue &&
    684  1.1      brad 			    strncmp(heaterpulse, sht4x_heateron_commands[i].pulselength,
    685  1.1      brad 			    SHT4X_PULSE_NAME) == 0) {
    686  1.1      brad 				r = sht4x_heateron_commands[i].cmd;
    687  1.1      brad 				break;
    688  1.1      brad 			}
    689  1.1      brad 		}
    690  1.1      brad 
    691  1.1      brad 		if (i == __arraycount(sht4x_heateron_commands))
    692  1.1      brad 			panic("Heater on could not find command for heatervalue, heaterpulse: %d %s\n",
    693  1.1      brad 			    heatervalue,heaterpulse);
    694  1.1      brad 	}
    695  1.1      brad 
    696  1.1      brad 	return r;
    697  1.1      brad }
    698  1.1      brad 
    699  1.1      brad static void
    700  1.1      brad sht4x_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
    701  1.1      brad {
    702  1.1      brad 	struct sht4x_sc *sc;
    703  1.1      brad 	sc = sme->sme_cookie;
    704  1.1      brad 	int error;
    705  1.1      brad 	uint8_t rawdata[6];
    706  1.1      brad 	uint8_t measurement_command;
    707  1.1      brad 	edata->state = ENVSYS_SINVALID;
    708  1.1      brad 
    709  1.1      brad 	mutex_enter(&sc->sc_mutex);
    710  1.1      brad 	error = iic_acquire_bus(sc->sc_tag, 0);
    711  1.1      brad 	if (error) {
    712  1.1      brad 		DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
    713  1.1      brad 		    device_xname(sc->sc_dev), error));
    714  1.1      brad 		goto out;
    715  1.1      brad 	}
    716  1.1      brad 
    717  1.1      brad 	/*
    718  1.1      brad 	  The documented conversion calculations for the raw values are as follows:
    719  1.1      brad 
    720  1.1      brad 	  %RH = (-6 + 125 * rawvalue / 65535)
    721  1.1      brad 
    722  1.1      brad 	  T in Celsius = (-45 + 175 * rawvalue / 65535)
    723  1.1      brad 
    724  1.1      brad 	  It follows then:
    725  1.1      brad 
    726  1.2      brad 	  T in Kelvin = (228.15 + 175 * rawvalue / 65535)
    727  1.1      brad 
    728  1.1      brad 	  given the relationship between Celsius and Kelvin.
    729  1.1      brad 
    730  1.1      brad 	  What follows reorders the calculation a bit and scales it up to avoid
    731  1.1      brad 	  the use of any floating point.  All that would really have to happen
    732  1.1      brad 	  is a scale up to 10^6 for the sysenv framework, which wants
    733  1.1      brad 	  temperature in micro-kelvin and percent relative humidity scaled up
    734  1.1      brad 	  10^6, but since this conversion uses 64 bits due to intermediate
    735  1.1      brad 	  values that are bigger than 32 bits the conversion first scales up to
    736  1.1      brad 	  10^9 and the scales back down by 10^3 at the end.  This preserves some
    737  1.1      brad 	  precision in the conversion that would otherwise be lost.
    738  1.1      brad 	 */
    739  1.1      brad 
    740  1.1      brad 	measurement_command = sht4x_compute_measure_command(sc->sc_resolution,
    741  1.1      brad 	    sc->sc_heateron, sc->sc_heaterval, sc->sc_heaterpulse);
    742  1.1      brad 	DPRINTF(sc, 2, ("%s: Measurement command: %02x\n",
    743  1.1      brad 	    device_xname(sc->sc_dev), measurement_command));
    744  1.1      brad 
    745  1.1      brad 	/* This chip is pretty nice in that all commands are the same length and
    746  1.1      brad 	   return the same result.  What is not so nice is that you can not ask
    747  1.1      brad 	   for temperature and humidity independently.
    748  1.1      brad 
    749  1.1      brad 	   The result will be 16 bits of raw temperature and a CRC byte followed
    750  1.1      brad 	   by 16 bits of humidity followed by a CRC byte.
    751  1.1      brad 	*/
    752  1.1      brad 
    753  1.1      brad 	error = sht4x_cmdr(sc,measurement_command,rawdata,6);
    754  1.1      brad 
    755  1.1      brad 	if (error == 0) {
    756  1.1      brad 		DPRINTF(sc, 2, ("%s: Raw data: %02x%02x %02x - %02x%02x %02x\n",
    757  1.1      brad 		    device_xname(sc->sc_dev), rawdata[0], rawdata[1], rawdata[2],
    758  1.1      brad 		    rawdata[3], rawdata[4], rawdata[5]));
    759  1.1      brad 
    760  1.1      brad 
    761  1.1      brad 		uint8_t *svalptr;
    762  1.1      brad 		uint64_t svalue;
    763  1.1      brad 		int64_t v1;
    764  1.1      brad 		uint64_t v2;
    765  1.1      brad 		uint64_t d1 = 65535;
    766  1.1      brad 		uint64_t mul1;
    767  1.1      brad 		uint64_t mul2;
    768  1.1      brad 		uint64_t div1 = 10000;
    769  1.1      brad 		uint64_t q;
    770  1.1      brad 
    771  1.1      brad 		switch (edata->sensor) {
    772  1.1      brad 		case SHT4X_TEMP_SENSOR:
    773  1.1      brad 			svalptr = &rawdata[0];
    774  1.2      brad 			v1 = 22815; /* this is scaled up already from 228.15 */
    775  1.1      brad 			v2 = 175;
    776  1.1      brad 			mul1 = 10000000000;
    777  1.1      brad 			mul2 = 100000000;
    778  1.1      brad 			break;
    779  1.1      brad 		case SHT4X_HUMIDITY_SENSOR:
    780  1.1      brad 			svalptr = &rawdata[3];
    781  1.1      brad 			v1 = -6;
    782  1.1      brad 			v2 = 125;
    783  1.1      brad 			mul1 = 10000000000;
    784  1.1      brad 			mul2 = 10000000000;
    785  1.1      brad 			break;
    786  1.1      brad 		default:
    787  1.1      brad 			error = EINVAL;
    788  1.1      brad 			break;
    789  1.1      brad 		}
    790  1.1      brad 
    791  1.1      brad 		if (error == 0) {
    792  1.1      brad 			uint8_t testcrc;
    793  1.1      brad 
    794  1.1      brad 			/* Fake out the CRC check if being asked to ignore CRC */
    795  1.1      brad 			if (sc->sc_ignorecrc) {
    796  1.1      brad 				testcrc = *(svalptr + 2);
    797  1.1      brad 			} else {
    798  1.1      brad 				testcrc = sht4x_crc(svalptr,2);
    799  1.1      brad 			}
    800  1.1      brad 
    801  1.1      brad 			if (*(svalptr + 2) == testcrc) {
    802  1.1      brad 				svalue = *svalptr << 8 | *(svalptr + 1);
    803  1.1      brad 				DPRINTF(sc, 2, ("%s: Raw sensor 16 bit: %#jx\n",
    804  1.1      brad 				    device_xname(sc->sc_dev), (uintmax_t)svalue));
    805  1.1      brad 
    806  1.1      brad 				/* Scale up */
    807  1.1      brad 				svalue = svalue * mul1;
    808  1.1      brad 				v1 = v1 * mul2;
    809  1.1      brad 				/* Perform the conversion */
    810  1.1      brad 				q = ((v2 * (svalue / d1)) + v1) / div1;
    811  1.1      brad 
    812  1.1      brad 				DPRINTF(sc, 2, ("%s: Computed sensor: %#jx\n",
    813  1.1      brad 				    device_xname(sc->sc_dev), (uintmax_t)q));
    814  1.1      brad 				/* The results will fit in 32 bits, so nothing will be lost */
    815  1.1      brad 				edata->value_cur = (uint32_t) q;
    816  1.1      brad 				edata->state = ENVSYS_SVALID;
    817  1.1      brad 			} else {
    818  1.1      brad 				error = EINVAL;
    819  1.1      brad 			}
    820  1.1      brad 		}
    821  1.1      brad 	}
    822  1.1      brad 
    823  1.1      brad 	if (error) {
    824  1.1      brad 		DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n",
    825  1.1      brad 		    device_xname(sc->sc_dev), error));
    826  1.1      brad 	}
    827  1.1      brad 
    828  1.1      brad 	iic_release_bus(sc->sc_tag, 0);
    829  1.1      brad out:
    830  1.1      brad 	mutex_exit(&sc->sc_mutex);
    831  1.1      brad }
    832  1.1      brad 
    833  1.1      brad static int
    834  1.1      brad sht4x_detach(device_t self, int flags)
    835  1.1      brad {
    836  1.1      brad 	struct sht4x_sc *sc;
    837  1.1      brad 
    838  1.1      brad 	sc = device_private(self);
    839  1.1      brad 
    840  1.1      brad 	mutex_enter(&sc->sc_mutex);
    841  1.1      brad 
    842  1.1      brad 	/* Remove the sensors */
    843  1.1      brad 	if (sc->sc_sme != NULL) {
    844  1.1      brad 		sysmon_envsys_unregister(sc->sc_sme);
    845  1.1      brad 		sc->sc_sme = NULL;
    846  1.1      brad 	}
    847  1.1      brad 	mutex_exit(&sc->sc_mutex);
    848  1.1      brad 
    849  1.1      brad 	/* Remove the sysctl tree */
    850  1.1      brad 	sysctl_teardown(&sc->sc_sht4xlog);
    851  1.1      brad 
    852  1.1      brad 	/* Remove the mutex */
    853  1.1      brad 	mutex_destroy(&sc->sc_mutex);
    854  1.1      brad 
    855  1.1      brad 	return 0;
    856  1.1      brad }
    857  1.1      brad 
    858  1.3  pgoyette MODULE(MODULE_CLASS_DRIVER, sht4xtemp, "iic,sysmon_envsys");
    859  1.1      brad 
    860  1.1      brad #ifdef _MODULE
    861  1.1      brad #include "ioconf.c"
    862  1.1      brad #endif
    863  1.1      brad 
    864  1.1      brad static int
    865  1.1      brad sht4xtemp_modcmd(modcmd_t cmd, void *opaque)
    866  1.1      brad {
    867  1.1      brad 
    868  1.1      brad 	switch (cmd) {
    869  1.1      brad 	case MODULE_CMD_INIT:
    870  1.1      brad #ifdef _MODULE
    871  1.1      brad 		return config_init_component(cfdriver_ioconf_sht4xtemp,
    872  1.1      brad 		    cfattach_ioconf_sht4xtemp, cfdata_ioconf_sht4xtemp);
    873  1.1      brad #else
    874  1.1      brad 		return 0;
    875  1.1      brad #endif
    876  1.1      brad 	case MODULE_CMD_FINI:
    877  1.1      brad #ifdef _MODULE
    878  1.1      brad 		return config_fini_component(cfdriver_ioconf_sht4xtemp,
    879  1.1      brad 		      cfattach_ioconf_sht4xtemp, cfdata_ioconf_sht4xtemp);
    880  1.1      brad #else
    881  1.1      brad 		return 0;
    882  1.1      brad #endif
    883  1.1      brad 	default:
    884  1.1      brad 		return ENOTTY;
    885  1.1      brad 	}
    886  1.1      brad }
    887