Module Name: src Committed By: brad Date: Mon Nov 21 21:24:02 UTC 2022
Modified Files: src/distrib/sets/lists/debug: module.mi src/distrib/sets/lists/man: mi src/distrib/sets/lists/modules: mi src/share/man/man4: Makefile src/sys/dev/i2c: files.i2c src/sys/dev/sysmon: sysmon_envsys_tables.c src/sys/modules: Makefile src/sys/sys: envsys.h src/usr.sbin/envstat: envstat.8 envstat.c Added Files: src/share/man/man4: bmx280thp.4 src/sys/dev/i2c: bmx280.c bmx280reg.h bmx280var.h src/sys/modules/bmx280thp: Makefile bmx280thp.ioconf Log Message: A driver for the Bosch BMP280 / BME280 temperature, humidity and atmospheric pressure sensor. This is an inexpensive to moderately expensive chip available from a large number of places. The driver supports all aspects of the two chips, except for the repeating read mode which would allow for sub-second queries, such as fall detection or perhaps even as an altimeter. This driver also only supports the I2C interface and not the SPI interface. The BME280, the one with humidity, is not fully tested at this point, awaiting upon a breakout board and may not show proper humidity. To generate a diff of this commit: cvs rdiff -u -r1.21 -r1.22 src/distrib/sets/lists/debug/module.mi cvs rdiff -u -r1.1755 -r1.1756 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.155 -r1.156 src/distrib/sets/lists/modules/mi cvs rdiff -u -r1.729 -r1.730 src/share/man/man4/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man4/bmx280thp.4 cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/bmx280.c src/sys/dev/i2c/bmx280reg.h \ src/sys/dev/i2c/bmx280var.h cvs rdiff -u -r1.124 -r1.125 src/sys/dev/i2c/files.i2c cvs rdiff -u -r1.13 -r1.14 src/sys/dev/sysmon/sysmon_envsys_tables.c cvs rdiff -u -r1.272 -r1.273 src/sys/modules/Makefile cvs rdiff -u -r0 -r1.1 src/sys/modules/bmx280thp/Makefile \ src/sys/modules/bmx280thp/bmx280thp.ioconf cvs rdiff -u -r1.38 -r1.39 src/sys/sys/envsys.h cvs rdiff -u -r1.66 -r1.67 src/usr.sbin/envstat/envstat.8 cvs rdiff -u -r1.102 -r1.103 src/usr.sbin/envstat/envstat.c 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.21 src/distrib/sets/lists/debug/module.mi:1.22 --- src/distrib/sets/lists/debug/module.mi:1.21 Thu Nov 17 19:20:06 2022 +++ src/distrib/sets/lists/debug/module.mi Mon Nov 21 21:24:01 2022 @@ -1,4 +1,4 @@ -# $NetBSD: module.mi,v 1.21 2022/11/17 19:20:06 brad Exp $ +# $NetBSD: module.mi,v 1.22 2022/11/21 21:24:01 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 @@ -22,6 +22,8 @@ ./usr/libdata/debug/@MODULEDIR@/blake2s/blake2s.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/blowfish modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/blowfish/blowfish.kmod.debug modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/bmx280thp modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/bmx280thp/bmx280thp.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/bpf modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/bpf/bpf.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/bpf_filter modules-base-kernel kmod,debug Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1755 src/distrib/sets/lists/man/mi:1.1756 --- src/distrib/sets/lists/man/mi:1.1755 Thu Nov 17 19:20:06 2022 +++ src/distrib/sets/lists/man/mi Mon Nov 21 21:24:01 2022 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1755 2022/11/17 19:20:06 brad Exp $ +# $NetBSD: mi,v 1.1756 2022/11/21 21:24:01 brad Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -965,6 +965,7 @@ ./usr/share/man/cat4/bio.0 man-sys-catman .cat ./usr/share/man/cat4/bktr.0 man-sys-catman .cat ./usr/share/man/cat4/bluetooth.0 man-sys-catman .cat +./usr/share/man/cat4/bmx280thp.0 man-sys-catman .cat ./usr/share/man/cat4/bmtphy.0 man-sys-catman .cat ./usr/share/man/cat4/bnx.0 man-sys-catman .cat ./usr/share/man/cat4/boca.0 man-sys-catman .cat @@ -4237,6 +4238,7 @@ ./usr/share/man/html4/bio.html man-sys-htmlman html ./usr/share/man/html4/bktr.html man-sys-htmlman html ./usr/share/man/html4/bluetooth.html man-sys-htmlman html +./usr/share/man/html4/bmx280thp.html man-sys-htmlman html ./usr/share/man/html4/bmtphy.html man-sys-htmlman html ./usr/share/man/html4/bnx.html man-sys-htmlman html ./usr/share/man/html4/boca.html man-sys-htmlman html @@ -7275,6 +7277,7 @@ ./usr/share/man/man4/bio.4 man-sys-man .man ./usr/share/man/man4/bktr.4 man-sys-man .man ./usr/share/man/man4/bluetooth.4 man-sys-man .man +./usr/share/man/man4/bmx280thp.4 man-sys-man .man ./usr/share/man/man4/bmtphy.4 man-sys-man .man ./usr/share/man/man4/bnx.4 man-sys-man .man ./usr/share/man/man4/boca.4 man-sys-man .man Index: src/distrib/sets/lists/modules/mi diff -u src/distrib/sets/lists/modules/mi:1.155 src/distrib/sets/lists/modules/mi:1.156 --- src/distrib/sets/lists/modules/mi:1.155 Thu Nov 17 19:20:06 2022 +++ src/distrib/sets/lists/modules/mi Mon Nov 21 21:24:01 2022 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.155 2022/11/17 19:20:06 brad Exp $ +# $NetBSD: mi,v 1.156 2022/11/21 21:24:01 brad Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -31,6 +31,8 @@ ./@MODULEDIR@/blake2s/blake2s.kmod modules-base-kernel kmod ./@MODULEDIR@/blowfish modules-base-kernel kmod ./@MODULEDIR@/blowfish/blowfish.kmod modules-base-kernel kmod +./@MODULEDIR@/bmx280thp modules-base-kernel kmod +./@MODULEDIR@/bmx280thp/bmx280thp.kmod modules-base-kernel kmod ./@MODULEDIR@/bpf modules-base-kernel kmod ./@MODULEDIR@/bpf/bpf.kmod modules-base-kernel kmod ./@MODULEDIR@/bpf_filter modules-base-kernel kmod Index: src/share/man/man4/Makefile diff -u src/share/man/man4/Makefile:1.729 src/share/man/man4/Makefile:1.730 --- src/share/man/man4/Makefile:1.729 Thu Nov 17 19:20:05 2022 +++ src/share/man/man4/Makefile Mon Nov 21 21:24:00 2022 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.729 2022/11/17 19:20:05 brad Exp $ +# $NetBSD: Makefile,v 1.730 2022/11/21 21:24:00 brad Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ @@ -11,7 +11,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 a auacer.4 audio.4 audiocs.4 auich.4 \ auixp.4 autri.4 auvia.4 awi.4 \ battery_pmu.4 bba.4 bce.4 bcsp.4 be.4 bge.4 bnx.4 bha.4 \ - bio.4 bktr.4 bluetooth.4 bmtphy.4 bpf.4 bpfjit.4 \ + bio.4 bktr.4 bluetooth.4 bmx280thp.4 bmtphy.4 bpf.4 bpfjit.4 \ brgphy.4 bridge.4 bthidev.4 bthub.4 btkbd.4 \ btmagic.4 btms.4 btsco.4 btuart.4 \ bwfm.4 bwi.4 \ Index: src/sys/dev/i2c/files.i2c diff -u src/sys/dev/i2c/files.i2c:1.124 src/sys/dev/i2c/files.i2c:1.125 --- src/sys/dev/i2c/files.i2c:1.124 Thu Nov 17 19:20:06 2022 +++ src/sys/dev/i2c/files.i2c Mon Nov 21 21:24:01 2022 @@ -1,4 +1,4 @@ -# $NetBSD: files.i2c,v 1.124 2022/11/17 19:20:06 brad Exp $ +# $NetBSD: files.i2c,v 1.125 2022/11/21 21:24:01 brad Exp $ obsolete defflag opt_i2cbus.h I2C_SCAN define i2cbus { } @@ -435,3 +435,8 @@ file dev/i2c/scmdi2c.c scmdi2c device aht20temp attach aht20temp at iic file dev/i2c/aht20.c aht20temp + +# Bosch Sensortec BMP280/BME280 Temperature, Humidity and Pressure sensor +device bmx280thp +attach bmx280thp at iic +file dev/i2c/bmx280.c bmx280thp Index: src/sys/dev/sysmon/sysmon_envsys_tables.c diff -u src/sys/dev/sysmon/sysmon_envsys_tables.c:1.13 src/sys/dev/sysmon/sysmon_envsys_tables.c:1.14 --- src/sys/dev/sysmon/sysmon_envsys_tables.c:1.13 Sun May 27 01:39:00 2018 +++ src/sys/dev/sysmon/sysmon_envsys_tables.c Mon Nov 21 21:24:01 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: sysmon_envsys_tables.c,v 1.13 2018/05/27 01:39:00 thorpej Exp $ */ +/* $NetBSD: sysmon_envsys_tables.c,v 1.14 2022/11/21 21:24:01 brad Exp $ */ /*- * Copyright (c) 2007 Juan Romero Pardines. @@ -26,7 +26,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_tables.c,v 1.13 2018/05/27 01:39:00 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_tables.c,v 1.14 2022/11/21 21:24:01 brad Exp $"); #include <sys/types.h> @@ -53,6 +53,7 @@ static const struct sme_descr_entry sme_ { ENVSYS_BATTERY_CHARGE, -1, "Battery charge" }, { ENVSYS_SRELHUMIDITY, -1, "relative Humidity" }, { ENVSYS_LUX, -1, "Illuminance" }, + { ENVSYS_PRESSURE, -1, "pressure" }, { -1, -1, "unknown" } }; Index: src/sys/modules/Makefile diff -u src/sys/modules/Makefile:1.272 src/sys/modules/Makefile:1.273 --- src/sys/modules/Makefile:1.272 Thu Nov 17 19:20:05 2022 +++ src/sys/modules/Makefile Mon Nov 21 21:24:01 2022 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.272 2022/11/17 19:20:05 brad Exp $ +# $NetBSD: Makefile,v 1.273 2022/11/21 21:24:01 brad Exp $ .include <bsd.own.mk> @@ -33,6 +33,7 @@ SUBDIR+= blake2s SUBDIR+= blowfish SUBDIR+= bpf SUBDIR+= bpf_filter +SUBDIR+= bmx280thp SUBDIR+= bufq_disksort SUBDIR+= bufq_fcfs SUBDIR+= bufq_priocscan Index: src/sys/sys/envsys.h diff -u src/sys/sys/envsys.h:1.38 src/sys/sys/envsys.h:1.39 --- src/sys/sys/envsys.h:1.38 Sun May 27 06:40:31 2018 +++ src/sys/sys/envsys.h Mon Nov 21 21:24:01 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: envsys.h,v 1.38 2018/05/27 06:40:31 wiz Exp $ */ +/* $NetBSD: envsys.h,v 1.39 2022/11/21 21:24:01 brad Exp $ */ /*- * Copyright (c) 1999, 2007, 2014 The NetBSD Foundation, Inc. @@ -65,6 +65,7 @@ enum envsys_units { ENVSYS_BATTERY_CHARGE, /* Battery charging/discharging */ ENVSYS_SRELHUMIDITY, /* relative humidity */ ENVSYS_LUX, /* illuminance in lux */ + ENVSYS_PRESSURE, /* pressure in hPa */ ENVSYS_NSENSORS }; Index: src/usr.sbin/envstat/envstat.8 diff -u src/usr.sbin/envstat/envstat.8:1.66 src/usr.sbin/envstat/envstat.8:1.67 --- src/usr.sbin/envstat/envstat.8:1.66 Sat Nov 14 20:07:13 2020 +++ src/usr.sbin/envstat/envstat.8 Mon Nov 21 21:24:02 2022 @@ -1,4 +1,4 @@ -.\" $NetBSD: envstat.8,v 1.66 2020/11/14 20:07:13 wiz Exp $ +.\" $NetBSD: envstat.8,v 1.67 2022/11/21 21:24:02 brad Exp $ .\" .\" Copyright (c) 2000, 2007, 2008, 2009, 2014 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -190,6 +190,8 @@ Watts Watt-hours .It %rH relative Humidity +.It hPa +Hectopascals .El .Sh EXAMPLES To display the Index: src/usr.sbin/envstat/envstat.c diff -u src/usr.sbin/envstat/envstat.c:1.102 src/usr.sbin/envstat/envstat.c:1.103 --- src/usr.sbin/envstat/envstat.c:1.102 Sat May 28 10:36:24 2022 +++ src/usr.sbin/envstat/envstat.c Mon Nov 21 21:24:02 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: envstat.c,v 1.102 2022/05/28 10:36:24 andvar Exp $ */ +/* $NetBSD: envstat.c,v 1.103 2022/11/21 21:24:02 brad Exp $ */ /*- * Copyright (c) 2007, 2008 Juan Romero Pardines. @@ -27,7 +27,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: envstat.c,v 1.102 2022/05/28 10:36:24 andvar Exp $"); +__RCSID("$NetBSD: envstat.c,v 1.103 2022/11/21 21:24:02 brad Exp $"); #endif /* not lint */ #include <stdio.h> @@ -1174,6 +1174,53 @@ do { \ if (!nflag) (void)printf(" %*s", (int)ilen - 3, stype); + /* Pressure */ + } else if (strcmp(sensor->type, "pressure") == 0) { + stype = "hPa"; + + (void)printf("%s%*.3f", sep, flen, + sensor->cur_value / 10000.0); + + ilen = 8; + if (statistics) { + /* show statistics if flag set */ + (void)printf(" %.3f %.3f %.3f", + stats->max / 10000.0, stats->min / 10000.0, stats->avg / 10000.0); + ilen += 2; + } else if (!nflag) { + if (sensor->critmax_value) { + (void)printf(" %*u", (int)ilen, + sensor->critmax_value); + ilen = 8; + } else + ilen += 9; + + if (sensor->warnmax_value) { + (void)printf(" %*u", (int)ilen, + sensor->warnmax_value); + ilen = 8; + } else + ilen += 9; + + if (sensor->warnmin_value) { + (void)printf(" %*u", (int)ilen, + sensor->warnmin_value); + ilen = 8; + } else + ilen += 9; + + if (sensor->critmin_value) { + (void)printf( " %*u", (int)ilen, + sensor->critmin_value); + ilen = 8; + } else + ilen += 9; + + } + + if (!nflag) + (void)printf(" %*s", (int)ilen - 3, stype); + /* everything else */ } else { if (strcmp(sensor->type, "Voltage DC") == 0) Added files: Index: src/share/man/man4/bmx280thp.4 diff -u /dev/null src/share/man/man4/bmx280thp.4:1.1 --- /dev/null Mon Nov 21 21:24:02 2022 +++ src/share/man/man4/bmx280thp.4 Mon Nov 21 21:24:00 2022 @@ -0,0 +1,89 @@ +.\" $NetBSD: bmx280thp.4,v 1.1 2022/11/21 21:24:00 brad Exp $ +.\" +.\" Copyright (c) 2022 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 November 19, 2022 +.Dt BMX280THP 4 +.Os +.Sh NAME +.Nm bmx280thp +.Nd Driver for Bosch BMP280/BME280 sensor chip via I2C bus +.Sh SYNOPSIS +.Cd "bmx280thp* at iic? addr 0x76" +.Cd "bmx280thp* at iic? addr 0x77" +.Sh DESCRIPTION +The +.Nm +driver provides measurements from the BMP280 and BME280 temperature, +humidity and barometric pressure sensors via the +.Xr envsys 4 +framework. +The +.Nm +.Ar addr +argument selects the address at the +.Xr iic 4 +bus. +The percision of the measurement 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.bmx280thp0.osrs_t +.It Li hw.bmx280thp0.osrs_p +.It Li hw.bmx280thp0.osrs_h +These control oversampling of temperature, pressure and humidity. The +valid values are 1, 2, 4, 8, and 16 times oversample. Humidity is only +available if the chip is a BME280. +.It Li hw.bmx280thp0.irr_samples +IRR is a filter that can be used to reduce the noise in the +measurement. The value values are 1 (or off), 2, 5, 11 and 22 samples +to reach >= 75% of the step response. +.It Li hw.bmx280thp0.debug +.It Li hw.bmx280thp0.dump_calibration +If the driver is compiled with +.Dv BMX280_DEBUG , +these nodes will appear and can be used to set the debugging level and +provide the calibration constants, upon refresh, that are stored in the +chip. Since the constants are fixed, this is a boolean node and will +reset back to false once one dump has been performed. +.It Li hw.bmx280thp0.readattempts +A status register tells the driver if the chip is busy with a measurement. +This status register must be polled and readattempts is the number of times +that this poll will be performed. +The default is 25 which should be more than enough for most purposes. +.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 . +.Sh BUGS +The driver does not support the continuous read mode that the BMP280 +and BME280 has. This driver does not support the SPI interface. Index: src/sys/dev/i2c/bmx280.c diff -u /dev/null src/sys/dev/i2c/bmx280.c:1.1 --- /dev/null Mon Nov 21 21:24:02 2022 +++ src/sys/dev/i2c/bmx280.c Mon Nov 21 21:24:01 2022 @@ -0,0 +1,1074 @@ +/* $NetBSD: bmx280.c,v 1.1 2022/11/21 21:24:01 brad Exp $ */ + +/* + * Copyright (c) 2022 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: bmx280.c,v 1.1 2022/11/21 21:24:01 brad Exp $"); + +/* + Driver for the Bosch BMP280/BME280 temperature, humidity (sometimes) and + (usually barometric) pressure sensor +*/ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/module.h> +#include <sys/sysctl.h> +#include <sys/mutex.h> + +#include <dev/sysmon/sysmonvar.h> +#include <dev/i2c/i2cvar.h> +#include <dev/i2c/bmx280reg.h> +#include <dev/i2c/bmx280var.h> + + +static int bmx280_write_register(i2c_tag_t, i2c_addr_t, + uint8_t *, size_t); +static int bmx280_read_register(i2c_tag_t, i2c_addr_t, uint8_t *, + uint8_t *, size_t); +static void bmx280_store_raw_blob_tp(struct bmx280_sc *, uint8_t *); +static void bmx280_store_raw_blob_h(struct bmx280_sc *, uint8_t *); +static int bmx280_poke(i2c_tag_t, i2c_addr_t, bool); +static int bmx280_match(device_t, cfdata_t, void *); +static void bmx280_attach(device_t, device_t, void *); +static int bmx280_detach(device_t, int); +static void bmx280_refresh(struct sysmon_envsys *, envsys_data_t *); +static int bmx280_verify_sysctl(SYSCTLFN_ARGS); +static int bmx280_verify_sysctl_osrs(SYSCTLFN_ARGS); +static int bmx280_verify_sysctl_irr(SYSCTLFN_ARGS); + +#define BMX280_DEBUG +#ifdef BMX280_DEBUG +#define DPRINTF(s, l, x) \ + do { \ + if (l <= s->sc_bmx280debug) \ + printf x; \ + } while (/*CONSTCOND*/0) +#else +#define DPRINTF(s, l, x) +#endif + +CFATTACH_DECL_NEW(bmx280thp, sizeof(struct bmx280_sc), + bmx280_match, bmx280_attach, bmx280_detach, NULL); + +static struct bmx280_sensor bmx280_sensors[] = { + { + .desc = "temperature", + .type = ENVSYS_STEMP, + }, + { + .desc = "pressure", + .type = ENVSYS_PRESSURE, + }, + { + .desc = "humidity", + .type = ENVSYS_SRELHUMIDITY, + } +}; + +static struct bmx280_osrs_list bmx280_osrs[] = { + { + .text = 1, + .mask = BMX280_OSRS_TP_VALUE_X1, + }, + { + .text = 2, + .mask = BMX280_OSRS_TP_VALUE_X2, + }, + { + .text = 4, + .mask = BMX280_OSRS_TP_VALUE_X4, + }, + { + .text = 8, + .mask = BMX280_OSRS_TP_VALUE_X8, + }, + { + .text = 16, + .mask = BMX280_OSRS_TP_VALUE_X16, + } +}; + +static struct bmx280_irr_list bmx280_irr[] = { + { + .text = 1, + .mask = BMX280_FILTER_VALUE_OFF, + }, + { + .text = 2, + .mask = BMX280_FILTER_VALUE_2, + }, + { + .text = 5, + .mask = BMX280_FILTER_VALUE_5, + }, + { + .text = 11, + .mask = BMX280_FILTER_VALUE_11, + }, + { + .text = 22, + .mask = BMX280_FILTER_VALUE_22, + } +}; + +static uint8_t +bmx280_osrs_text_to_mask(int t) +{ + int i; + uint8_t m = 0; + + for (i = 0; i < __arraycount(bmx280_osrs); i++) { + if (t == bmx280_osrs[i].text) { + m = bmx280_osrs[i].mask; + break; + } + } + + return m; +} + +static uint8_t +bmx280_irr_text_to_mask(int t) +{ + int i; + uint8_t m = 0; + + for (i = 0; i < __arraycount(bmx280_irr); i++) { + if (t == bmx280_irr[i].text) { + m = bmx280_irr[i].mask; + break; + } + } + + return m; +} + +int +bmx280_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 +bmx280_verify_sysctl_osrs(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + int error = 0, t; + size_t i; + + node = *rnode; + t = *(int *)rnode->sysctl_data; + node.sysctl_data = &t; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + for (i = 0; i < __arraycount(bmx280_osrs); i++) { + if (t == bmx280_osrs[i].text) { + break; + } + } + + if (i == __arraycount(bmx280_osrs)) + return EINVAL; + + *(int *)rnode->sysctl_data = t; + + return error; +} + +int +bmx280_verify_sysctl_irr(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + int error = 0, t; + size_t i; + + node = *rnode; + t = *(int *)rnode->sysctl_data; + node.sysctl_data = &t; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + for (i = 0; i < __arraycount(bmx280_irr); i++) { + if (t == bmx280_irr[i].text) { + break; + } + } + + if (i == __arraycount(bmx280_irr)) + return EINVAL; + + *(int *)rnode->sysctl_data = t; + + return error; +} + +/* The datasheet was pretty vague as to the byte order... + * in fact, down right deceptive... + */ + +static void +bmx280_store_raw_blob_tp(struct bmx280_sc *sc, uint8_t *b) { + sc->sc_cal_blob.dig_T1 = (uint16_t)b[1] << 8; + sc->sc_cal_blob.dig_T1 = sc->sc_cal_blob.dig_T1 | (uint16_t)b[0]; + sc->sc_cal_blob.dig_T2 = (int16_t)b[3] << 8; + sc->sc_cal_blob.dig_T2 = sc->sc_cal_blob.dig_T2 | (int16_t)b[2]; + sc->sc_cal_blob.dig_T3 = (int16_t)b[5] << 8; + sc->sc_cal_blob.dig_T3 = sc->sc_cal_blob.dig_T3 | (int16_t)b[4]; + + sc->sc_cal_blob.dig_P1 = (uint16_t)b[7] << 8; + sc->sc_cal_blob.dig_P1 = sc->sc_cal_blob.dig_P1 | (uint16_t)b[6]; + sc->sc_cal_blob.dig_P2 = (int16_t)b[9] << 8; + sc->sc_cal_blob.dig_P2 = sc->sc_cal_blob.dig_P2 | (int16_t)b[8]; + sc->sc_cal_blob.dig_P3 = (int16_t)b[11] << 8; + sc->sc_cal_blob.dig_P3 = sc->sc_cal_blob.dig_P3 | (int16_t)b[10]; + sc->sc_cal_blob.dig_P4 = (int16_t)b[13] << 8; + sc->sc_cal_blob.dig_P4 = sc->sc_cal_blob.dig_P4 | (int16_t)b[12]; + sc->sc_cal_blob.dig_P5 = (int16_t)b[15] << 8; + sc->sc_cal_blob.dig_P5 = sc->sc_cal_blob.dig_P5 | (int16_t)b[14]; + sc->sc_cal_blob.dig_P6 = (int16_t)b[17] << 8; + sc->sc_cal_blob.dig_P6 = sc->sc_cal_blob.dig_P6 | (int16_t)b[16]; + sc->sc_cal_blob.dig_P7 = (int16_t)b[19] << 8; + sc->sc_cal_blob.dig_P7 = sc->sc_cal_blob.dig_P7 | (int16_t)b[18]; + sc->sc_cal_blob.dig_P8 = (int16_t)b[21] << 8; + sc->sc_cal_blob.dig_P8 = sc->sc_cal_blob.dig_P8 | (int16_t)b[20]; + sc->sc_cal_blob.dig_P9 = (int16_t)b[23] << 8; + sc->sc_cal_blob.dig_P9 = sc->sc_cal_blob.dig_P9 | (int16_t)b[22]; +} + +static void +bmx280_store_raw_blob_h(struct bmx280_sc *sc, uint8_t *b) { + sc->sc_cal_blob.dig_H1 = (uint8_t)b[0]; + sc->sc_cal_blob.dig_H2 = (int16_t)b[2] << 8; + sc->sc_cal_blob.dig_H2 = sc->sc_cal_blob.dig_H2 | (int16_t)b[1]; + sc->sc_cal_blob.dig_H3 = (uint8_t)b[3]; + sc->sc_cal_blob.dig_H4 = ((int16_t)b[5] & 0x000f) << 8; + sc->sc_cal_blob.dig_H4 = sc->sc_cal_blob.dig_H4 | (int16_t)b[4]; + sc->sc_cal_blob.dig_H5 = (int16_t)b[6] << 4; + sc->sc_cal_blob.dig_H5 = sc->sc_cal_blob.dig_H5 | (((int16_t)b[5] & 0x00f0) >> 4); + sc->sc_cal_blob.dig_H6 = (int8_t)b[7]; +} + +/* For the BMX280, a write consists of sending a I2C START, I2C SLAVE + * address and then pairs of registers and data until a I2C STOP is + * sent. + */ + +static int +bmx280_write_register(i2c_tag_t tag, i2c_addr_t addr, + uint8_t *buf, size_t blen) +{ + int error; + + KASSERT(blen > 0); + /* XXX - there should be a KASSERT for blen at least + being an even number */ + + error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,NULL,0, + buf,blen,0); + + return error; +} + +/* For the BMX280, a read consists of writing on the I2C bus + * a I2C START, I2C SLAVE address, then the starting register. + * If that works, then following will be another I2C START, + * I2C SLAVE address, followed by as many I2C reads that is + * desired and then a I2C STOP + */ + +static int +bmx280_read_register(i2c_tag_t tag, i2c_addr_t addr, uint8_t *reg, + uint8_t *buf, size_t blen) +{ + int error; + + KASSERT(blen > 0); + + error = iic_exec(tag,I2C_OP_WRITE,addr,reg,1,NULL,0,0); + + if (error == 0) { + error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0, + buf,blen,0); + } + + return error; +} + + +static int +bmx280_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug) +{ + uint8_t reg = BMX280_REGISTER_ID; + uint8_t buf[1]; + int error; + + error = bmx280_read_register(tag, addr, ®, buf, 1); + if (matchdebug) { + printf("poke X 1: %d\n", error); + } + return error; +} + +static int +bmx280_sysctl_init(struct bmx280_sc *sc) +{ + int error; + const struct sysctlnode *cnode; + int sysctlroot_num; + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + 0, CTLTYPE_NODE, device_xname(sc->sc_dev), + SYSCTL_DESCR("bmx280 controls"), NULL, 0, NULL, 0, CTL_HW, + CTL_CREATE, CTL_EOL)) != 0) + return error; + + sysctlroot_num = cnode->sysctl_num; + +#ifdef BMX280_DEBUG + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "debug", + SYSCTL_DESCR("Debug level"), bmx280_verify_sysctl, 0, + &sc->sc_bmx280debug, 0, CTL_HW, sysctlroot_num, CTL_CREATE, + CTL_EOL)) != 0) + return error; + + /* It would be nice to have a CTLTYPE_SHORT */ + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_BOOL, "dump_calibration", + SYSCTL_DESCR("Dumps the calibration values to the console"), + bmx280_verify_sysctl, 0, + &sc->sc_bmx280dump, 0, CTL_HW, sysctlroot_num, CTL_CREATE, + CTL_EOL)) != 0) + return error; +#endif + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts", + SYSCTL_DESCR("Read attempts"), bmx280_verify_sysctl, 0, + &sc->sc_readattempts, 0, CTL_HW, sysctlroot_num, CTL_CREATE, + CTL_EOL)) != 0) + return error; + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "osrs_t", + SYSCTL_DESCR("Temperature oversample"), + bmx280_verify_sysctl_osrs, 0, &sc->sc_osrs_t, + 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "osrs_p", + SYSCTL_DESCR("Pressure oversample"), + bmx280_verify_sysctl_osrs, 0, &sc->sc_osrs_p, + 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + + if (sc->sc_has_humidity) { + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "osrs_h", + SYSCTL_DESCR("Humidity oversample"), + bmx280_verify_sysctl_osrs, 0, &sc->sc_osrs_h, + 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + } + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "irr_samples", + SYSCTL_DESCR("IRR samples"), + bmx280_verify_sysctl_irr, 0, &sc->sc_irr_samples, + 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + + return 0; +} + +static int +bmx280_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; + + /* indirect config - check for configured address */ + if (ia->ia_addr != BMX280_TYPICAL_ADDR_1 && + ia->ia_addr != BMX280_TYPICAL_ADDR_2) + return 0; + + /* + * 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 = bmx280_poke(ia->ia_tag, ia->ia_addr, matchdebug); + iic_release_bus(ia->ia_tag, 0); + + return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0; +} + +static void +bmx280_attach(device_t parent, device_t self, void *aux) +{ + struct bmx280_sc *sc; + struct i2c_attach_args *ia; + int error, i; + uint8_t reg, chip_id; + uint8_t buf[2]; + + ia = aux; + sc = device_private(self); + + sc->sc_dev = self; + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + sc->sc_bmx280debug = 0; + sc->sc_bmx280dump = false; + sc->sc_has_humidity = false; + sc->sc_readattempts = 25; + sc->sc_osrs_t = 1; + sc->sc_osrs_p = 4; + sc->sc_osrs_h = 1; + sc->sc_irr_samples = 1; + sc->sc_previous_irr = 0xff; + sc->sc_sme = NULL; + + aprint_normal("\n"); + + mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); + sc->sc_numsensors = __arraycount(bmx280_sensors); + + if ((sc->sc_sme = sysmon_envsys_create()) == NULL) { + aprint_error_dev(self, + "Unable to create sysmon structure\n"); + sc->sc_sme = NULL; + return; + } + + error = iic_acquire_bus(sc->sc_tag, 0); + if (error) { + aprint_error_dev(self, "Could not acquire iic bus: %d\n", + error); + goto out; + } + + buf[0] = BMX280_REGISTER_RESET; + buf[1] = BMX280_TRIGGER_RESET; + error = bmx280_write_register(sc->sc_tag, sc->sc_addr, buf, 2); + if (error) { + aprint_error_dev(self, "Failed to reset chip: %d\n", + error); + } + + delay(100); + + reg = BMX280_REGISTER_ID; + error = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, &chip_id, 1); + if (error) { + aprint_error_dev(self, "Failed to read ID: %d\n", + error); + } + + delay(100); + + DPRINTF(sc, 2, ("%s: read ID value: %02x\n", + device_xname(sc->sc_dev), chip_id)); + + if (chip_id == BMX280_ID_BME280) { + sc->sc_has_humidity = true; + } + + if ((error = bmx280_sysctl_init(sc)) != 0) { + aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error); + goto out; + } + + uint8_t raw_blob_tp[24]; + reg = BMX280_REGISTER_DIG_T1; + error = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, raw_blob_tp, 24); + if (error) { + aprint_error_dev(self, "Failed to read the calibration registers for tp: %d\n", + error); + } + + bmx280_store_raw_blob_tp(sc,raw_blob_tp); + + if (sc->sc_has_humidity) { + uint8_t raw_blob_h[8]; + + reg = BMX280_REGISTER_DIG_H1; + error = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, raw_blob_h, 1); + if (error) { + aprint_error_dev(self, "Failed to read the calibration registers for h1: %d\n", + error); + } + + reg = BMX280_REGISTER_DIG_H2; + error = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, &raw_blob_h[1], 7); + if (error) { + aprint_error_dev(self, "Failed to read the calibration registers for h2 - h6: %d\n", + error); + } + + bmx280_store_raw_blob_h(sc,raw_blob_h); + } + + 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++) { + if (sc->sc_has_humidity == false && + bmx280_sensors[i].type == ENVSYS_SRELHUMIDITY) { + break; + } + + strlcpy(sc->sc_sensors[i].desc, bmx280_sensors[i].desc, + sizeof(sc->sc_sensors[i].desc)); + + sc->sc_sensors[i].units = bmx280_sensors[i].type; + sc->sc_sensors[i].state = ENVSYS_SINVALID; + + 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 = bmx280_refresh; + + DPRINTF(sc, 2, ("bmx280_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; + } + + aprint_normal_dev(self, "Bosch Sensortec %s, Chip ID: 0x%02x\n", + (chip_id == BMX280_ID_BMP280) ? "BMP280" : (chip_id == BMX280_ID_BME280) ? "BME280" : "Unknown chip", + chip_id); + + return; +out: + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; +} + +/* The conversion algorithms are taken from the Bosch datasheet for + * the BMX280 and adapted to the envsys infrastructure. + */ + +static int32_t +bmx280_compensate_T_int32(struct bmx280_calibration_blob *b, + int32_t adc_T, + int32_t *t_fine) +{ + int32_t var1, var2, T; + var1 = ((((adc_T>>3) - ((int32_t)b->dig_T1<<1))) * ((int32_t)b->dig_T2)) >> 11; + var2 = (((((adc_T>>4) - ((int32_t)b->dig_T1)) * ((adc_T>>4) - ((int32_t)b->dig_T1))) >> 12) * + ((int32_t)b->dig_T3)) >> 14; + *t_fine = var1 + var2; + T = (*t_fine * 5 + 128) >> 8; + return T; +} + +/* Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits). + * Output value of 24674867 represents 24674867/256 = 96386.2 Pa = 963.862 hPa + */ +static uint32_t +bmx280_compensate_P_int64(struct bmx280_calibration_blob *b, + int32_t adc_P, + int32_t t_fine) +{ + int64_t var1, var2, p; + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)b->dig_P6; + var2 = var2 + ((var1*(int64_t)b->dig_P5)<<17); + var2 = var2 + (((int64_t)b->dig_P4)<<35); + var1 = ((var1 * var1 * (int64_t)b->dig_P3)>>8) + ((var1 * (int64_t)b->dig_P2)<<12); + var1 = (((((int64_t)1)<<47)+var1))*((int64_t)b->dig_P1)>>33; + if (var1 == 0) { + return 0; /* avoid exception caused by division by zero */ + } + p = 1048576-adc_P; + p = (((p<<31)-var2)*3125)/var1; + var1 = (((int64_t)b->dig_P9) * (p>>13) * (p>>13)) >> 25; + var2 = (((int64_t)b->dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)b->dig_P7)<<4); + return (uint32_t)p; +} + +/* Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits). + * + * Output value of 47445 represents 47445/1024 = 46.333 %RH + */ +static uint32_t +bmx280_compensate_H_int32(struct bmx280_calibration_blob *b, + int32_t adc_H, + int32_t t_fine) +{ + int32_t v_x1_u32r; + v_x1_u32r = (t_fine - ((int32_t)76800)); + v_x1_u32r = (((((adc_H << 14) - (((int32_t)b->dig_H4) << 20) - (((int32_t)b->dig_H5) * + v_x1_u32r)) + ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * + ((int32_t)b->dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)b->dig_H3)) >> 11) + + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)b->dig_H2) + + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)b->dig_H1)) >> 4)); + v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); + v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r); + return (uint32_t)(v_x1_u32r>>12); +} + + +static int +bmx280_set_control_and_trigger(struct bmx280_sc *sc, + uint8_t osrs_t_mask, + uint8_t osrs_p_mask, + uint8_t osrs_h_mask, + uint8_t filter_mask) +{ + uint8_t cr[6]; + int error; + int s = 0; + + cr[0] = cr[1] = cr[2] = cr[3] = cr[4] = cr[5] = 0; + + if (filter_mask != sc->sc_previous_irr) { + cr[s] = BMX280_REGISTER_CONFIG; + s++; + cr[s] = filter_mask << BMX280_CONFIG_FILTER_SHIFT; + s++; + sc->sc_previous_irr = filter_mask; + } + if (sc->sc_has_humidity) { + cr[s] = BMX280_REGISTER_CTRL_HUM; + s++; + cr[s] = osrs_h_mask; + s++; + } + cr[s] = BMX280_REGISTER_CTRL_MEAS; + s++; + cr[s] = osrs_t_mask << BMX280_CTRL_OSRS_T_SHIFT; + cr[s] = cr[s] | osrs_p_mask << BMX280_CTRL_OSRS_P_SHIFT; + cr[s] = cr[s] | BMX280_MODE_FORCED; + s++; + DPRINTF(sc, 2, ("%s: control register set up: num: %d ; %02x %02x ; %02x %02x ; %02x %02x\n", + device_xname(sc->sc_dev), s, cr[0], cr[1], cr[2], cr[3], cr[4], cr[5])); + error = bmx280_write_register(sc->sc_tag, sc->sc_addr, cr, s); + if (error) { + DPRINTF(sc, 2, ("%s: write control registers: %d\n", + device_xname(sc->sc_dev), error)); + error = EINVAL; + } + + return error; +} + +static int +bmx280_wait_for_data(struct bmx280_sc *sc) +{ + uint8_t reg; + uint8_t running = 99; + int c = sc->sc_readattempts; + int error = 0, ierror; + + reg = BMX280_REGISTER_STATUS; + do { + delay(10); + ierror = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, &running, 1); + if (ierror) { + DPRINTF(sc, 2, ("%s: Refresh failed to read back status: %d\n", + device_xname(sc->sc_dev), ierror)); + error = EINVAL; + break; + } + + DPRINTF(sc, 2, ("%s: Refresh status read back: %02x\n", + device_xname(sc->sc_dev), running)); + + c--; + } while (c > 0 && (running & BMX280_STATUS_MEASURING_MASK)); + + return error; +} + +static int +bmx280_read_data(struct bmx280_sc *sc, + int32_t *temp, + int32_t *press, + int32_t *hum, + bool justtemp) +{ + int error = 0, ierror; + int rlen, rtstart, rpstart, rhstart; + int x_temp, x_press, x_hum; + uint8_t raw_press_temp_hum[8], reg; + + raw_press_temp_hum[0] = raw_press_temp_hum[1] = + raw_press_temp_hum[2] = raw_press_temp_hum[3] = + raw_press_temp_hum[4] = raw_press_temp_hum[5] = + raw_press_temp_hum[6] = raw_press_temp_hum[7] = 0; + + if (justtemp) { + reg = BMX280_REGISTER_TEMP_MSB; + rlen = 3; + rtstart = 0; + rpstart = 0; + rhstart = 0; + } else { + reg = BMX280_REGISTER_PRESS_MSB; + if (sc->sc_has_humidity == false) { + rlen = 6; + } else { + rlen = 8; + } + rtstart = 3; + rpstart = 0; + rhstart = 6; + } + + DPRINTF(sc, 2, ("%s: read data: reg: %02x ; len: %d ; tstart: %d ; pstart: %d\n", + device_xname(sc->sc_dev), reg, rlen, rtstart, rpstart)); + + ierror = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, raw_press_temp_hum, rlen); + if (ierror) { + DPRINTF(sc, 2, ("%s: failed to read pressure and temp registers: %d\n", + device_xname(sc->sc_dev), ierror)); + error = EINVAL; + goto out; + } + + DPRINTF(sc, 2, ("%s: raw pressure, temp and hum: %02x %02x %02x - %02x %02x %02x - %02x %02x\n", + device_xname(sc->sc_dev), + raw_press_temp_hum[0], raw_press_temp_hum[1], raw_press_temp_hum[2], + raw_press_temp_hum[3], raw_press_temp_hum[4], raw_press_temp_hum[5], + raw_press_temp_hum[6],raw_press_temp_hum[7])); + + x_temp = raw_press_temp_hum[rtstart] << 12; + x_temp = x_temp | (raw_press_temp_hum[rtstart + 1] << 4); + x_temp = x_temp | (raw_press_temp_hum[rtstart + 2] >> 4); + + DPRINTF(sc, 1, ("%s: intermediate temp: %d (%04x)\n", + device_xname(sc->sc_dev), x_temp, x_temp)); + + *temp = x_temp; + + *hum = 0; + *press = 0; + + if (justtemp == false) { + x_press = raw_press_temp_hum[rpstart] << 12; + x_press = x_press | (raw_press_temp_hum[rpstart + 1] << 4); + x_press = x_press | (raw_press_temp_hum[rpstart + 2] >> 4); + + DPRINTF(sc, 1, ("%s: intermediate pressure: %d (%04x)\n", + device_xname(sc->sc_dev), x_press, x_press)); + *press = x_press; + } + if (sc->sc_has_humidity) { + x_hum = raw_press_temp_hum[rhstart] << 8; + x_hum = x_hum | raw_press_temp_hum[rhstart + 1]; + + DPRINTF(sc, 1, ("%s: intermediate humidity: %d (%02x)\n", + device_xname(sc->sc_dev), x_hum, x_hum)); + *hum = x_hum; + } + + out: + return error; +} + +static void +bmx280_refresh(struct sysmon_envsys * sme, envsys_data_t * edata) +{ + struct bmx280_sc *sc; + sc = sme->sme_cookie; + int error = 0; + int32_t t_fine; + int32_t m_temp, m_press, m_hum; + int32_t comp_temp; + uint32_t comp_press; + uint32_t comp_hum; + edata->state = ENVSYS_SINVALID; + + /* Ya... just do this on a refresh... */ + + if (sc->sc_bmx280dump) { + DPRINTF(sc, 1, ("%s: dig_T1: %d %04x\n",__func__,sc->sc_cal_blob.dig_T1,sc->sc_cal_blob.dig_T1)); + DPRINTF(sc, 1, ("%s: dig_T2: %d %04x\n",__func__,sc->sc_cal_blob.dig_T2,sc->sc_cal_blob.dig_T2)); + DPRINTF(sc, 1, ("%s: dig_T3: %d %04x\n",__func__,sc->sc_cal_blob.dig_T3,sc->sc_cal_blob.dig_T3)); + DPRINTF(sc, 1, ("%s: dig_P1: %d %04x\n",__func__,sc->sc_cal_blob.dig_P1,sc->sc_cal_blob.dig_P1)); + DPRINTF(sc, 1, ("%s: dig_P2: %d %04x\n",__func__,sc->sc_cal_blob.dig_P2,sc->sc_cal_blob.dig_P2)); + DPRINTF(sc, 1, ("%s: dig_P3: %d %04x\n",__func__,sc->sc_cal_blob.dig_P3,sc->sc_cal_blob.dig_P3)); + DPRINTF(sc, 1, ("%s: dig_P4: %d %04x\n",__func__,sc->sc_cal_blob.dig_P4,sc->sc_cal_blob.dig_P4)); + DPRINTF(sc, 1, ("%s: dig_P5: %d %04x\n",__func__,sc->sc_cal_blob.dig_P5,sc->sc_cal_blob.dig_P5)); + DPRINTF(sc, 1, ("%s: dig_P6: %d %04x\n",__func__,sc->sc_cal_blob.dig_P6,sc->sc_cal_blob.dig_P6)); + DPRINTF(sc, 1, ("%s: dig_P7: %d %04x\n",__func__,sc->sc_cal_blob.dig_P7,sc->sc_cal_blob.dig_P7)); + DPRINTF(sc, 1, ("%s: dig_P8: %d %04x\n",__func__,sc->sc_cal_blob.dig_P8,sc->sc_cal_blob.dig_P8)); + DPRINTF(sc, 1, ("%s: dig_P9: %d %04x\n",__func__,sc->sc_cal_blob.dig_P9,sc->sc_cal_blob.dig_P9)); + + if (sc->sc_has_humidity) { + DPRINTF(sc, 1, ("%s: dig_H1: %d %02x\n",__func__,sc->sc_cal_blob.dig_H1,sc->sc_cal_blob.dig_H1)); + DPRINTF(sc, 1, ("%s: dig_H2: %d %04x\n",__func__,sc->sc_cal_blob.dig_H2,sc->sc_cal_blob.dig_H2)); + DPRINTF(sc, 1, ("%s: dig_H3: %d %02x\n",__func__,sc->sc_cal_blob.dig_H3,sc->sc_cal_blob.dig_H3)); + DPRINTF(sc, 1, ("%s: dig_H4: %d %04x\n",__func__,sc->sc_cal_blob.dig_H4,sc->sc_cal_blob.dig_H4)); + DPRINTF(sc, 1, ("%s: dig_H5: %d %04x\n",__func__,sc->sc_cal_blob.dig_H5,sc->sc_cal_blob.dig_H5)); + DPRINTF(sc, 1, ("%s: dig_H6: %d %02x\n",__func__,sc->sc_cal_blob.dig_H6,sc->sc_cal_blob.dig_H6)); + } + + sc->sc_bmx280dump = false; + } + + 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; + } + + if (error == 0) { + switch (edata->sensor) { + case BMX280_TEMP_SENSOR: + /* A temperature reading does not need pressure */ + + error = bmx280_set_control_and_trigger(sc, + bmx280_osrs_text_to_mask(sc->sc_osrs_t), + 0, + 0, + bmx280_irr_text_to_mask(sc->sc_irr_samples)); + + if (error == 0) { + error = bmx280_wait_for_data(sc); + + if (error == 0) { + error = bmx280_read_data(sc, &m_temp, &m_press, &m_hum, true); + + if (error == 0) { + comp_temp = bmx280_compensate_T_int32(&sc->sc_cal_blob, m_temp, &t_fine); + + DPRINTF(sc, 1, ("%s: Refresh compensated temp: %d - t_fine: %d\n", + device_xname(sc->sc_dev), comp_temp, t_fine)); + + /* comp_temp is in Celcius * 100. This converts it to microkelvin */ + + uint32_t q; + + q = (uint32_t)comp_temp; + q = q + 27315; + q = q * 10000; + + DPRINTF(sc, 1, ("%s: Refresh Q: %d\n", __func__, q)); + + edata->value_cur = q; + edata->state = ENVSYS_SVALID; + } + } + } + break; + case BMX280_PRESSURE_SENSOR: + + /* Pressure needs the temp too */ + error = bmx280_set_control_and_trigger(sc, + bmx280_osrs_text_to_mask(sc->sc_osrs_t), + bmx280_osrs_text_to_mask(sc->sc_osrs_p), + 0, + bmx280_irr_text_to_mask(sc->sc_irr_samples)); + + if (error == 0) { + error = bmx280_wait_for_data(sc); + + if (error == 0) { + error = bmx280_read_data(sc, &m_temp, &m_press, &m_hum, false); + + if (error == 0) { + comp_temp = bmx280_compensate_T_int32(&sc->sc_cal_blob, m_temp, &t_fine); + + DPRINTF(sc, 1, ("%s: Refresh compensated temp for pressure: %d - t_fine: %d\n", + device_xname(sc->sc_dev), comp_temp, t_fine)); + + comp_press = bmx280_compensate_P_int64(&sc->sc_cal_blob, m_press, t_fine); + + DPRINTF(sc, 1, ("%s: Refresh compensated pressure: %d\n", + device_xname(sc->sc_dev), comp_press)); + + uint32_t q; + + q = comp_press; + q = q / 256; + q = q * 100; + + DPRINTF(sc, 1, ("%s: Refresh pressure Q: %d\n", __func__, q)); + + edata->value_cur = q; + edata->state = ENVSYS_SVALID; + } + } + } + break; + + case BMX280_HUMIDITY_SENSOR: + + /* Humidity wants temperature */ + + error = bmx280_set_control_and_trigger(sc, + bmx280_osrs_text_to_mask(sc->sc_osrs_t), + 0, + bmx280_osrs_text_to_mask(sc->sc_osrs_h), + bmx280_irr_text_to_mask(sc->sc_irr_samples)); + + if (error == 0) { + error = bmx280_wait_for_data(sc); + + if (error == 0) { + error = bmx280_read_data(sc, &m_temp, &m_press, &m_hum, false); + + if (error == 0) { + comp_temp = bmx280_compensate_T_int32(&sc->sc_cal_blob, m_temp, &t_fine); + + DPRINTF(sc, 1, ("%s: Refresh compensated temp for humidity: %d - t_fine: %d\n", + device_xname(sc->sc_dev), comp_temp, t_fine)); + + comp_hum = bmx280_compensate_H_int32(&sc->sc_cal_blob, m_hum, t_fine); + + DPRINTF(sc, 2, ("%s: Refresh compensated humidity: %d\n", + device_xname(sc->sc_dev), comp_hum)); + + uint32_t q; + + q = comp_hum; + q = q / 1024; + q = q * 100; /* XXX - this probably is not correct */ + + DPRINTF(sc, 1, ("%s: Refresh humidity Q: %d\n", __func__, q)); + + edata->value_cur = q; + edata->state = ENVSYS_SVALID; + } + } + } + break; + } + } + + if (error) { + DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n", + device_xname(sc->sc_dev), error)); + } + + iic_release_bus(sc->sc_tag, 0); +out: + mutex_exit(&sc->sc_mutex); +} + +static int +bmx280_detach(device_t self, int flags) +{ + struct bmx280_sc *sc; + + sc = device_private(self); + + mutex_enter(&sc->sc_mutex); + + /* 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_bmx280log); + + /* Remove the mutex */ + mutex_destroy(&sc->sc_mutex); + + return 0; +} + +MODULE(MODULE_CLASS_DRIVER, bmx280thp, "iic,sysmon_envsys"); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +bmx280thp_modcmd(modcmd_t cmd, void *opaque) +{ + + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + return config_init_component(cfdriver_ioconf_bmx280thp, + cfattach_ioconf_bmx280thp, cfdata_ioconf_bmx280thp); +#else + return 0; +#endif + case MODULE_CMD_FINI: +#ifdef _MODULE + return config_fini_component(cfdriver_ioconf_bmx280thp, + cfattach_ioconf_bmx280thp, cfdata_ioconf_bmx280thp); +#else + return 0; +#endif + default: + return ENOTTY; + } +} Index: src/sys/dev/i2c/bmx280reg.h diff -u /dev/null src/sys/dev/i2c/bmx280reg.h:1.1 --- /dev/null Mon Nov 21 21:24:02 2022 +++ src/sys/dev/i2c/bmx280reg.h Mon Nov 21 21:24:01 2022 @@ -0,0 +1,96 @@ +/* $NetBSD: bmx280reg.h,v 1.1 2022/11/21 21:24:01 brad Exp $ */ + +/* + * Copyright (c) 2022 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_BMX280REG_H_ +#define _DEV_I2C_BMX280REG_H_ + +#define BMX280_TYPICAL_ADDR_1 0x76 +#define BMX280_TYPICAL_ADDR_2 0x77 + +#define BMX280_REGISTER_DIG_T1 0x88 +#define BMX280_REGISTER_DIG_T2 0x8A +#define BMX280_REGISTER_DIG_T3 0x8C +#define BMX280_REGISTER_DIG_P1 0x8E +#define BMX280_REGISTER_DIG_P2 0x90 +#define BMX280_REGISTER_DIG_P3 0x92 +#define BMX280_REGISTER_DIG_P4 0x94 +#define BMX280_REGISTER_DIG_P5 0x96 +#define BMX280_REGISTER_DIG_P6 0x98 +#define BMX280_REGISTER_DIG_P7 0x9A +#define BMX280_REGISTER_DIG_P8 0x9C +#define BMX280_REGISTER_DIG_P9 0x9E +#define BMX280_REGISTER_DIG_H1 0xA1 +#define BMX280_REGISTER_DIG_H2 0xE1 +#define BMX280_REGISTER_DIG_H3 0xE3 +#define BMX280_REGISTER_DIG_H4 0xE4 +#define BMX280_REGISTER_DIG_H5 0xE5 + +#define BMX280_REGISTER_ID 0xD0 +#define BMX280_ID_BMP280 0x58 +#define BMX280_ID_BME280 0x60 + +#define BMX280_REGISTER_RESET 0xE0 +#define BMX280_TRIGGER_RESET 0xB6 + +#define BMX280_REGISTER_CTRL_HUM 0xF2 + +#define BMX280_REGISTER_STATUS 0xF3 +#define BMX280_STATUS_MEASURING_MASK 0x08 +#define BMX280_STATUS_IM_UPDATE_MASK 0x01 + +#define BMX280_REGISTER_CTRL_MEAS 0xF4 +#define BMX280_CTRL_OSRS_T_MASK 0xE0 +#define BMX280_CTRL_OSRS_P_MASK 0x1C +#define BMX280_CTRL_OSRS_T_SHIFT 5 +#define BMX280_CTRL_OSRS_P_SHIFT 2 +#define BMX280_OSRS_TP_VALUE_SKIPPED 0x00 +#define BMX280_OSRS_TP_VALUE_X1 0x01 +#define BMX280_OSRS_TP_VALUE_X2 0x02 +#define BMX280_OSRS_TP_VALUE_X4 0x03 +#define BMX280_OSRS_TP_VALUE_X8 0x04 +#define BMX280_OSRS_TP_VALUE_X16 0x05 +#define BMX280_CTRL_MODE_MASK 0x03 +#define BMX280_MODE_SLEEP 0x00 +#define BMX280_MODE_FORCED 0x01 +#define BMX280_MODE_NORMAL 0x03 + +#define BMX280_REGISTER_CONFIG 0xF5 +#define BMX280_CONFIG_T_SB_MASK 0xE0 +#define BMX280_CONFIG_FILTER_MASK 0x1C +#define BMX280_CONFIG_FILTER_SHIFT 2 +#define BMX280_FILTER_VALUE_OFF 0x00 +#define BMX280_FILTER_VALUE_2 0x01 +#define BMX280_FILTER_VALUE_5 0x02 +#define BMX280_FILTER_VALUE_11 0x04 +#define BMX280_FILTER_VALUE_22 0x05 +#define BMX280_CONFIG_SPI3W_EN_MASK 0x01 + +#define BMX280_REGISTER_PRESS_MSB 0xF7 +#define BMX280_REGISTER_PRESS_LSB 0xF8 +#define BMX280_REGISTER_PRESS_XLSB 0xF9 + +#define BMX280_REGISTER_TEMP_MSB 0xFA +#define BMX280_REGISTER_TEMP_LSB 0xFB +#define BMX280_REGISTER_TEMP_XLSB 0xFC + +#define BMX280_TEMPPRES_XLSB_MASK 0xF0 + +#define BMX280_REGISTER_HUM_MSB 0xFD +#define BMX280_REGISTER_HUM_LSB 0xFE + +#endif Index: src/sys/dev/i2c/bmx280var.h diff -u /dev/null src/sys/dev/i2c/bmx280var.h:1.1 --- /dev/null Mon Nov 21 21:24:02 2022 +++ src/sys/dev/i2c/bmx280var.h Mon Nov 21 21:24:01 2022 @@ -0,0 +1,85 @@ +/* $NetBSD: bmx280var.h,v 1.1 2022/11/21 21:24:01 brad Exp $ */ + +/* + * Copyright (c) 2022 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_BMX280VAR_H_ +#define _DEV_I2C_BMX280VAR_H_ + +#define BMX280_NUM_SENSORS 3 +#define BMX280_TEMP_SENSOR 0 +#define BMX280_PRESSURE_SENSOR 1 +#define BMX280_HUMIDITY_SENSOR 2 + +struct bmx280_calibration_blob { + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + uint8_t dig_H1; + int16_t dig_H2; + uint8_t dig_H3; + int16_t dig_H4; + int16_t dig_H5; + int8_t dig_H6; +}; + +struct bmx280_sc { + int sc_bmx280debug; + device_t sc_dev; + i2c_tag_t sc_tag; + i2c_addr_t sc_addr; + kmutex_t sc_mutex; + int sc_numsensors; + struct sysmon_envsys *sc_sme; + struct sysctllog *sc_bmx280log; + envsys_data_t sc_sensors[BMX280_NUM_SENSORS]; + struct bmx280_calibration_blob sc_cal_blob; + bool sc_has_humidity; + int sc_readattempts; + int sc_osrs_t; + int sc_osrs_p; + int sc_osrs_h; + int sc_irr_samples; + uint8_t sc_previous_irr; + bool sc_bmx280dump; +}; + +struct bmx280_sensor { + const char *desc; + enum envsys_units type; +}; + +struct bmx280_osrs_list { + const int text; + uint8_t mask; +}; + +struct bmx280_irr_list { + const int text; + uint8_t mask; +}; + +#endif Index: src/sys/modules/bmx280thp/Makefile diff -u /dev/null src/sys/modules/bmx280thp/Makefile:1.1 --- /dev/null Mon Nov 21 21:24:02 2022 +++ src/sys/modules/bmx280thp/Makefile Mon Nov 21 21:24:01 2022 @@ -0,0 +1,11 @@ +.include "../Makefile.inc" + +.PATH: ${S}/dev/i2c + +KMOD= bmx280thp +IOCONF= bmx280thp.ioconf +SRCS= bmx280.c + +WARNS= 3 + +.include <bsd.kmodule.mk> Index: src/sys/modules/bmx280thp/bmx280thp.ioconf diff -u /dev/null src/sys/modules/bmx280thp/bmx280thp.ioconf:1.1 --- /dev/null Mon Nov 21 21:24:02 2022 +++ src/sys/modules/bmx280thp/bmx280thp.ioconf Mon Nov 21 21:24:01 2022 @@ -0,0 +1,8 @@ +ioconf bmx280thp + +include "conf/files" + +pseudo-root iic* + +bmx280thp* at iic? addr 0x76 +bmx280thp* at iic? addr 0x77