1 /* $NetBSD: sgp40.c,v 1.2 2021/10/20 17:52:44 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Brad Spencer <brad@anduin.eldar.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGL`IGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/cdefs.h> 20 __KERNEL_RCSID(0, "$NetBSD: sgp40.c,v 1.2 2021/10/20 17:52:44 christos Exp $"); 21 22 /* 23 Driver for the Sensirion SGP40 MOx gas sensor for air quality 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/kernel.h> 29 #include <sys/device.h> 30 #include <sys/module.h> 31 #include <sys/sysctl.h> 32 #include <sys/mutex.h> 33 #include <sys/condvar.h> 34 #include <sys/kthread.h> 35 36 #include <dev/sysmon/sysmonvar.h> 37 #include <dev/i2c/i2cvar.h> 38 #include <dev/i2c/sgp40reg.h> 39 #include <dev/i2c/sgp40var.h> 40 41 #include <dev/i2c/sensirion_arch_config.h> 42 #include <dev/i2c/sensirion_voc_algorithm.h> 43 44 static uint8_t sgp40_crc(uint8_t *, size_t); 45 static int sgp40_cmdr(struct sgp40_sc *, uint16_t, uint8_t *, uint8_t, 46 uint8_t *, size_t); 47 static int sgp40_poke(i2c_tag_t, i2c_addr_t, bool); 48 static int sgp40_match(device_t, cfdata_t, void *); 49 static void sgp40_attach(device_t, device_t, void *); 50 static int sgp40_detach(device_t, int); 51 static void sgp40_refresh(struct sysmon_envsys *, envsys_data_t *); 52 static int sgp40_verify_sysctl(SYSCTLFN_ARGS); 53 static int sgp40_verify_temp_sysctl(SYSCTLFN_ARGS); 54 static int sgp40_verify_rh_sysctl(SYSCTLFN_ARGS); 55 static void sgp40_thread(void *); 56 static void sgp40_stop_thread(void *); 57 static void sgp40_take_measurement(void *, VocAlgorithmParams *); 58 59 #define SGP40_DEBUG 60 #ifdef SGP40_DEBUG 61 #define DPRINTF(s, l, x) \ 62 do { \ 63 if (l <= s->sc_sgp40debug) \ 64 printf x; \ 65 } while (/*CONSTCOND*/0) 66 #else 67 #define DPRINTF(s, l, x) 68 #endif 69 70 CFATTACH_DECL_NEW(sgp40mox, sizeof(struct sgp40_sc), 71 sgp40_match, sgp40_attach, sgp40_detach, NULL); 72 73 static struct sgp40_sensor sgp40_sensors[] = { 74 { 75 .desc = "VOC index", 76 .type = ENVSYS_INTEGER, 77 } 78 }; 79 80 static struct sgp40_timing sgp40_timings[] = { 81 { 82 .cmd = SGP40_MEASURE_RAW, 83 .typicaldelay = 25000, 84 }, 85 { 86 .cmd = SGP40_MEASURE_TEST, 87 .typicaldelay = 240000, 88 }, 89 { 90 .cmd = SGP40_HEATER_OFF, 91 .typicaldelay = 100, 92 }, 93 { 94 .cmd = SGP40_GET_SERIAL_NUMBER, 95 .typicaldelay = 100, 96 }, 97 { 98 .cmd = SGP40_GET_FEATURESET, 99 .typicaldelay = 1000, 100 } 101 }; 102 103 void 104 sgp40_thread(void *aux) 105 { 106 struct sgp40_sc *sc = aux; 107 int rv; 108 VocAlgorithmParams voc_algorithm_params; 109 110 mutex_enter(&sc->sc_threadmutex); 111 112 VocAlgorithm_init(&voc_algorithm_params); 113 114 while (!sc->sc_stopping) { 115 rv = cv_timedwait(&sc->sc_condvar, &sc->sc_threadmutex, 116 mstohz(1000)); 117 if (rv == EWOULDBLOCK && !sc->sc_stopping) { 118 sgp40_take_measurement(sc,&voc_algorithm_params); 119 } 120 } 121 mutex_exit(&sc->sc_threadmutex); 122 kthread_exit(0); 123 } 124 125 static void 126 sgp40_stop_thread(void *aux) 127 { 128 struct sgp40_sc *sc; 129 sc = aux; 130 int error; 131 132 mutex_enter(&sc->sc_threadmutex); 133 sc->sc_stopping = true; 134 cv_signal(&sc->sc_condvar); 135 mutex_exit(&sc->sc_threadmutex); 136 137 /* wait for the thread to exit */ 138 kthread_join(sc->sc_thread); 139 140 mutex_enter(&sc->sc_mutex); 141 error = iic_acquire_bus(sc->sc_tag, 0); 142 if (error) { 143 DPRINTF(sc, 2, ("%s: Could not acquire iic bus for heater off " 144 "in stop thread: %d\n", device_xname(sc->sc_dev), error)); 145 goto out; 146 } 147 error = sgp40_cmdr(sc, SGP40_HEATER_OFF, NULL, 0, NULL, 0); 148 if (error) { 149 DPRINTF(sc, 2, ("%s: Error turning heater off: %d\n", 150 device_xname(sc->sc_dev), error)); 151 } 152 out: 153 iic_release_bus(sc->sc_tag, 0); 154 mutex_exit(&sc->sc_mutex); 155 } 156 157 static int 158 sgp40_compute_temp_comp(int unconverted) 159 { 160 /* 161 * The published algorithm for this conversion is: 162 * (temp_in_celcius + 45) * 65535 / 175 163 * 164 * However, this did not exactly yield the results that 165 * the example in the data sheet, so something a little 166 * different was done. 167 * 168 * (temp_in_celcius + 45) * 65536 / 175 169 * 170 * This was also scaled up by 10^2 and then scaled back to 171 * preserve some percision. 37449 is simply (65536 * 100) / 175 172 * and rounded. 173 */ 174 175 return (((unconverted + 45) * 100) * 37449) / 10000; 176 } 177 178 static int 179 sgp40_compute_rh_comp(int unconverted) 180 { 181 int q; 182 183 /* 184 * The published algorithm for this conversion is: 185 * %rh * 65535 / 100 186 * 187 * However, this did not exactly yield the results that 188 * the example in the data sheet, so something a little 189 * different was done. 190 * 191 * %rh * 65536 / 100 192 * 193 * This was also scaled up by 10^2 and then scaled back to 194 * preserve some percision. The value is also latched to 65535 195 * as an upper limit. 196 */ 197 198 q = ((unconverted * 100) * 65536) / 10000; 199 if (q > 65535) 200 q = 65535; 201 return q; 202 } 203 204 static void 205 sgp40_take_measurement(void *aux, VocAlgorithmParams* params) 206 { 207 struct sgp40_sc *sc; 208 sc = aux; 209 uint8_t args[6]; 210 uint8_t buf[3]; 211 uint16_t rawmeasurement; 212 int error; 213 uint8_t crc; 214 uint16_t convertedrh, convertedtemp; 215 int32_t voc_index; 216 217 mutex_enter(&sc->sc_mutex); 218 convertedrh = (uint16_t)sgp40_compute_rh_comp(sc->sc_rhcomp); 219 convertedtemp = (uint16_t)sgp40_compute_temp_comp(sc->sc_tempcomp); 220 221 DPRINTF(sc, 2, ("%s: Converted RH and Temp: %04x %04x\n", 222 device_xname(sc->sc_dev), convertedrh, convertedtemp)); 223 224 args[0] = convertedrh >> 8; 225 args[1] = convertedrh & 0x00ff; 226 args[2] = sgp40_crc(&args[0], 2); 227 args[3] = convertedtemp >> 8; 228 args[4] = convertedtemp & 0x00ff; 229 args[5] = sgp40_crc(&args[3], 2); 230 231 /* 232 * The VOC algoritm has a black out time when it first starts to run 233 * and does not return any indicator that is going on, so voc_index 234 * in that case would be 0.. however, that is also a valid response 235 * otherwise, although an unlikely one. 236 */ 237 error = iic_acquire_bus(sc->sc_tag, 0); 238 if (error) { 239 DPRINTF(sc, 2, ("%s: Could not acquire iic bus for take " 240 "measurement: %d\n", device_xname(sc->sc_dev), error)); 241 sc->sc_voc = 0; 242 sc->sc_vocvalid = false; 243 goto out; 244 } 245 246 error = sgp40_cmdr(sc, SGP40_MEASURE_RAW, args, 6, buf, 3); 247 iic_release_bus(sc->sc_tag, 0); 248 if (error) { 249 DPRINTF(sc, 2, ("%s: Failed to get measurement %d\n", 250 device_xname(sc->sc_dev), error)); 251 goto out; 252 } 253 254 crc = sgp40_crc(&buf[0], 2); 255 DPRINTF(sc, 2, ("%s: Raw ticks and crc: %02x%02x %02x " 256 "%02x\n", device_xname(sc->sc_dev), buf[0], buf[1], 257 buf[2], crc)); 258 if (buf[2] != crc) 259 goto out; 260 261 rawmeasurement = buf[0] << 8; 262 rawmeasurement |= buf[1]; 263 VocAlgorithm_process(params, rawmeasurement, 264 &voc_index); 265 DPRINTF(sc, 2, ("%s: VOC index: %d\n", 266 device_xname(sc->sc_dev), voc_index)); 267 sc->sc_voc = voc_index; 268 sc->sc_vocvalid = true; 269 270 mutex_exit(&sc->sc_mutex); 271 return; 272 out: 273 sc->sc_voc = 0; 274 sc->sc_vocvalid = false; 275 mutex_exit(&sc->sc_mutex); 276 } 277 278 int 279 sgp40_verify_sysctl(SYSCTLFN_ARGS) 280 { 281 int error, t; 282 struct sysctlnode node; 283 284 node = *rnode; 285 t = *(int *)rnode->sysctl_data; 286 node.sysctl_data = &t; 287 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 288 if (error || newp == NULL) 289 return error; 290 291 if (t < 0) 292 return EINVAL; 293 294 *(int *)rnode->sysctl_data = t; 295 296 return 0; 297 } 298 299 int 300 sgp40_verify_temp_sysctl(SYSCTLFN_ARGS) 301 { 302 int error, t; 303 struct sysctlnode node; 304 305 node = *rnode; 306 t = *(int *)rnode->sysctl_data; 307 node.sysctl_data = &t; 308 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 309 if (error || newp == NULL) 310 return error; 311 312 if (t < -45 || t > 130) 313 return EINVAL; 314 315 *(int *)rnode->sysctl_data = t; 316 317 return 0; 318 } 319 320 int 321 sgp40_verify_rh_sysctl(SYSCTLFN_ARGS) 322 { 323 int error, t; 324 struct sysctlnode node; 325 326 node = *rnode; 327 t = *(int *)rnode->sysctl_data; 328 node.sysctl_data = &t; 329 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 330 if (error || newp == NULL) 331 return error; 332 333 if (t < 0 || t > 100) 334 return EINVAL; 335 336 *(int *)rnode->sysctl_data = t; 337 338 return 0; 339 } 340 341 static int 342 sgp40_cmddelay(uint16_t cmd) 343 { 344 int r = -1; 345 346 for(int i = 0;i < __arraycount(sgp40_timings);i++) { 347 if (cmd == sgp40_timings[i].cmd) { 348 r = sgp40_timings[i].typicaldelay; 349 break; 350 } 351 } 352 353 if (r == -1) { 354 panic("sgp40: Bad command look up in cmd delay: cmd: %d\n", 355 cmd); 356 } 357 358 return r; 359 } 360 361 static int 362 sgp40_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd, 363 uint8_t clen, uint8_t *buf, size_t blen, int readattempts) 364 { 365 int error; 366 int cmddelay; 367 uint16_t cmd16; 368 369 cmd16 = cmd[0] << 8; 370 cmd16 = cmd16 | cmd[1]; 371 372 error = iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, cmd, clen, NULL, 0, 373 0); 374 if (error) 375 return error; 376 377 /* 378 * Every command returns something except for turning the heater off 379 * and the general soft reset which returns nothing. 380 */ 381 if (cmd16 == SGP40_HEATER_OFF) 382 return 0; 383 /* 384 * Every command has a particular delay for how long 385 * it typically takes and the max time it will take. 386 */ 387 cmddelay = sgp40_cmddelay(cmd16); 388 delay(cmddelay); 389 390 for (int aint = 0; aint < readattempts; aint++) { 391 error = iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0, 392 buf, blen, 0); 393 if (error == 0) 394 break; 395 delay(1000); 396 } 397 398 return error; 399 } 400 401 static int 402 sgp40_cmdr(struct sgp40_sc *sc, uint16_t cmd, uint8_t *extraargs, 403 uint8_t argslen, uint8_t *buf, size_t blen) 404 { 405 uint8_t fullcmd[8]; 406 uint8_t cmdlen; 407 int n; 408 409 /* 410 * The biggest documented command + arguments is 8 uint8_t bytes long. 411 * Catch anything that ties to have an arglen more than 6 412 */ 413 KASSERT(argslen <= 6); 414 415 memset(fullcmd, 0, 8); 416 417 fullcmd[0] = cmd >> 8; 418 fullcmd[1] = cmd & 0x00ff; 419 cmdlen = 2; 420 421 n = 0; 422 while (extraargs != NULL && n < argslen && cmdlen <= 7) { 423 fullcmd[cmdlen] = extraargs[n]; 424 cmdlen++; 425 n++; 426 } 427 DPRINTF(sc, 2, ("%s: Full command and arguments: %02x %02x %02x %02x " 428 "%02x %02x %02x %02x\n", 429 device_xname(sc->sc_dev), fullcmd[0], fullcmd[1], 430 fullcmd[2], fullcmd[3], fullcmd[4], fullcmd[5], 431 fullcmd[6], fullcmd[7])); 432 return sgp40_cmd(sc->sc_tag, sc->sc_addr, fullcmd, cmdlen, buf, blen, 433 sc->sc_readattempts); 434 } 435 436 static uint8_t 437 sgp40_crc(uint8_t * data, size_t size) 438 { 439 uint8_t crc = 0xFF; 440 441 for (size_t i = 0; i < size; i++) { 442 crc ^= data[i]; 443 for (size_t j = 8; j > 0; j--) { 444 if (crc & 0x80) 445 crc = (crc << 1) ^ 0x31; 446 else 447 crc <<= 1; 448 } 449 } 450 return crc; 451 } 452 453 static int 454 sgp40_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug) 455 { 456 uint8_t reg[2]; 457 uint8_t buf[9]; 458 int error; 459 460 /* 461 * Possible bug... this command may not work if the chip is not idle, 462 * however, it appears to be used by a lot of other code as a probe. 463 */ 464 reg[0] = SGP40_GET_SERIAL_NUMBER >> 8; 465 reg[1] = SGP40_GET_SERIAL_NUMBER & 0x00ff; 466 467 error = sgp40_cmd(tag, addr, reg, 2, buf, 9, 10); 468 if (matchdebug) { 469 printf("poke X 1: %d\n", error); 470 } 471 return error; 472 } 473 474 static int 475 sgp40_sysctl_init(struct sgp40_sc *sc) 476 { 477 int error; 478 const struct sysctlnode *cnode; 479 int sysctlroot_num; 480 481 if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode, 482 0, CTLTYPE_NODE, device_xname(sc->sc_dev), 483 SYSCTL_DESCR("SGP40 controls"), NULL, 0, NULL, 0, CTL_HW, 484 CTL_CREATE, CTL_EOL)) != 0) 485 return error; 486 487 sysctlroot_num = cnode->sysctl_num; 488 489 #ifdef SGP40_DEBUG 490 if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode, 491 CTLFLAG_READWRITE, CTLTYPE_INT, "debug", 492 SYSCTL_DESCR("Debug level"), sgp40_verify_sysctl, 0, 493 &sc->sc_sgp40debug, 0, CTL_HW, sysctlroot_num, CTL_CREATE, 494 CTL_EOL)) != 0) 495 return error; 496 497 #endif 498 499 if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode, 500 CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts", 501 SYSCTL_DESCR("The number of times to attempt to read the values"), 502 sgp40_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW, 503 sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 504 return error; 505 506 if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode, 507 CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc", 508 SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc, 509 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 510 return error; 511 512 if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode, 513 0, CTLTYPE_NODE, "compensation", 514 SYSCTL_DESCR("SGP40 measurement compensations"), NULL, 0, NULL, 0, 515 CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 516 return error; 517 int compensation_num = cnode->sysctl_num; 518 519 if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode, 520 CTLFLAG_READWRITE, CTLTYPE_INT, "temperature", 521 SYSCTL_DESCR("Temperature compensation in celsius"), 522 sgp40_verify_temp_sysctl, 0, &sc->sc_tempcomp, 0, CTL_HW, 523 sysctlroot_num, compensation_num, CTL_CREATE, CTL_EOL)) != 0) 524 return error; 525 526 if ((error = sysctl_createv(&sc->sc_sgp40log, 0, NULL, &cnode, 527 CTLFLAG_READWRITE, CTLTYPE_INT, "humidity", 528 SYSCTL_DESCR("Humidity compensation in %RH"), 529 sgp40_verify_rh_sysctl, 0, &sc->sc_rhcomp, 0, CTL_HW, 530 sysctlroot_num, compensation_num, CTL_CREATE, CTL_EOL)) != 0) 531 return error; 532 533 return 0; 534 } 535 536 static int 537 sgp40_match(device_t parent, cfdata_t match, void *aux) 538 { 539 struct i2c_attach_args *ia = aux; 540 int error, match_result; 541 const bool matchdebug = false; 542 543 if (matchdebug) 544 printf("in match\n"); 545 546 if (iic_use_direct_match(ia, match, NULL, &match_result)) 547 return match_result; 548 549 /* indirect config - check for configured address */ 550 if (ia->ia_addr != SGP40_TYPICAL_ADDR) 551 return 0; 552 553 /* 554 * Check to see if something is really at this i2c address. This will 555 * keep phantom devices from appearing 556 */ 557 if (iic_acquire_bus(ia->ia_tag, 0) != 0) { 558 if (matchdebug) 559 printf("in match acquire bus failed\n"); 560 return 0; 561 } 562 563 error = sgp40_poke(ia->ia_tag, ia->ia_addr, matchdebug); 564 iic_release_bus(ia->ia_tag, 0); 565 566 return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0; 567 } 568 569 static void 570 sgp40_attach(device_t parent, device_t self, void *aux) 571 { 572 struct sgp40_sc *sc; 573 struct i2c_attach_args *ia; 574 int error, i; 575 int ecount = 0; 576 uint8_t buf[9]; 577 uint8_t tstcrc; 578 uint16_t chiptestvalue; 579 uint64_t serial_number = 0; 580 uint8_t sn_crc1, sn_crc2, sn_crc3, sn_crcv1, sn_crcv2, sn_crcv3; 581 uint8_t fs_crc, fs_crcv; 582 uint16_t featureset; 583 584 ia = aux; 585 sc = device_private(self); 586 587 sc->sc_dev = self; 588 sc->sc_tag = ia->ia_tag; 589 sc->sc_addr = ia->ia_addr; 590 sc->sc_sgp40debug = 0; 591 sc->sc_readattempts = 10; 592 sc->sc_ignorecrc = false; 593 sc->sc_stopping = false; 594 sc->sc_voc = 0; 595 sc->sc_vocvalid = false; 596 sc->sc_tempcomp = SGP40_DEFAULT_TEMP_COMP; 597 sc->sc_rhcomp = SGP40_DEFAULT_RH_COMP; 598 sc->sc_sme = NULL; 599 600 aprint_normal("\n"); 601 602 mutex_init(&sc->sc_threadmutex, MUTEX_DEFAULT, IPL_NONE); 603 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); 604 cv_init(&sc->sc_condvar, "sgp40cv"); 605 sc->sc_numsensors = __arraycount(sgp40_sensors); 606 607 if ((sc->sc_sme = sysmon_envsys_create()) == NULL) { 608 aprint_error_dev(self, 609 "Unable to create sysmon structure\n"); 610 sc->sc_sme = NULL; 611 return; 612 } 613 if ((error = sgp40_sysctl_init(sc)) != 0) { 614 aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error); 615 goto out; 616 } 617 618 error = iic_acquire_bus(sc->sc_tag, 0); 619 if (error) { 620 aprint_error_dev(self, "Could not acquire iic bus: %d\n", 621 error); 622 goto out; 623 } 624 625 /* 626 * Usually one would reset the chip here, but that is not possible 627 * without resetting the entire bus, so we won't do that. 628 * 629 * What we will do is make sure that the chip is idle by running the 630 * turn-the-heater command. 631 */ 632 633 error = sgp40_cmdr(sc, SGP40_HEATER_OFF, NULL, 0, NULL, 0); 634 if (error) { 635 aprint_error_dev(self, "Failed to turn off the heater: %d\n", 636 error); 637 ecount++; 638 } 639 640 error = sgp40_cmdr(sc, SGP40_GET_SERIAL_NUMBER, NULL, 0, buf, 9); 641 if (error) { 642 aprint_error_dev(self, "Failed to get serial number: %d\n", 643 error); 644 ecount++; 645 } 646 647 sn_crc1 = sgp40_crc(&buf[0], 2); 648 sn_crc2 = sgp40_crc(&buf[3], 2); 649 sn_crc3 = sgp40_crc(&buf[6], 2); 650 sn_crcv1 = buf[2]; 651 sn_crcv2 = buf[5]; 652 sn_crcv3 = buf[8]; 653 serial_number = buf[0]; 654 serial_number = (serial_number << 8) | buf[1]; 655 serial_number = (serial_number << 8) | buf[3]; 656 serial_number = (serial_number << 8) | buf[4]; 657 serial_number = (serial_number << 8) | buf[6]; 658 serial_number = (serial_number << 8) | buf[7]; 659 660 DPRINTF(sc, 2, ("%s: raw serial number: %02x %02x %02x %02x %02x %02x " 661 "%02x %02x %02x\n", 662 device_xname(sc->sc_dev), buf[0], buf[1], buf[2], buf[3], buf[4], 663 buf[5], buf[6], buf[7], buf[8])); 664 665 error = sgp40_cmdr(sc, SGP40_GET_FEATURESET, NULL, 0, buf, 3); 666 if (error) { 667 aprint_error_dev(self, "Failed to get featureset: %d\n", 668 error); 669 ecount++; 670 } 671 672 fs_crc = sgp40_crc(&buf[0], 2); 673 fs_crcv = buf[2]; 674 featureset = buf[0]; 675 featureset = (featureset << 8) | buf[1]; 676 677 DPRINTF(sc, 2, ("%s: raw feature set: %02x %02x %02x\n", 678 device_xname(sc->sc_dev), buf[0], buf[1], buf[2])); 679 680 error = sgp40_cmdr(sc, SGP40_MEASURE_TEST, NULL, 0, buf, 3); 681 if (error) { 682 aprint_error_dev(self, "Failed to perform a chip test: %d\n", 683 error); 684 ecount++; 685 } 686 687 tstcrc = sgp40_crc(&buf[0], 2); 688 689 DPRINTF(sc, 2, ("%s: chip test values: %02x%02x - %02x ; %02x\n", 690 device_xname(sc->sc_dev), buf[0], buf[1], buf[2], tstcrc)); 691 692 iic_release_bus(sc->sc_tag, 0); 693 if (error != 0) { 694 aprint_error_dev(self, "Unable to setup device\n"); 695 goto out; 696 } 697 698 chiptestvalue = buf[0] << 8; 699 chiptestvalue |= buf[1]; 700 701 for (i = 0; i < sc->sc_numsensors; i++) { 702 strlcpy(sc->sc_sensors[i].desc, sgp40_sensors[i].desc, 703 sizeof(sc->sc_sensors[i].desc)); 704 705 sc->sc_sensors[i].units = sgp40_sensors[i].type; 706 sc->sc_sensors[i].state = ENVSYS_SINVALID; 707 708 DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i, 709 sc->sc_sensors[i].desc)); 710 711 error = sysmon_envsys_sensor_attach(sc->sc_sme, 712 &sc->sc_sensors[i]); 713 if (error) { 714 aprint_error_dev(self, 715 "Unable to attach sensor %d: %d\n", i, error); 716 goto out; 717 } 718 } 719 720 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 721 sc->sc_sme->sme_cookie = sc; 722 sc->sc_sme->sme_refresh = sgp40_refresh; 723 724 DPRINTF(sc, 2, ("sgp40_attach: registering with envsys\n")); 725 726 if (sysmon_envsys_register(sc->sc_sme)) { 727 aprint_error_dev(self, 728 "unable to register with sysmon\n"); 729 sysmon_envsys_destroy(sc->sc_sme); 730 sc->sc_sme = NULL; 731 return; 732 } 733 734 error = kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, 735 sgp40_thread, sc, &sc->sc_thread, "%s", device_xname(sc->sc_dev)); 736 if (error) { 737 aprint_error_dev(self,"Unable to create measurement thread\n"); 738 goto out; 739 } 740 741 aprint_normal_dev(self, "Sensirion SGP40, Serial number: %jx%s" 742 "Feature set word: 0x%jx%s%s%s", serial_number, 743 (sn_crc1 == sn_crcv1 && sn_crc2 == sn_crcv2 && sn_crc3 == sn_crcv3) 744 ? ", " : " (bad crc), ", 745 (uintmax_t)featureset, 746 (fs_crc == fs_crcv) ? ", " : " (bad crc), ", 747 (chiptestvalue == SGP40_TEST_RESULTS_ALL_PASSED) ? 748 "All chip tests passed" : 749 (chiptestvalue == SGP40_TEST_RESULTS_SOME_FAILED) ? 750 "Some chip tests failed" : 751 "Unknown test results", 752 (tstcrc == buf[2]) ? "\n" : " (bad crc)\n"); 753 return; 754 out: 755 sysmon_envsys_destroy(sc->sc_sme); 756 sc->sc_sme = NULL; 757 } 758 759 static void 760 sgp40_refresh(struct sysmon_envsys * sme, envsys_data_t * edata) 761 { 762 struct sgp40_sc *sc; 763 sc = sme->sme_cookie; 764 765 mutex_enter(&sc->sc_mutex); 766 if (sc->sc_vocvalid == true) { 767 edata->value_cur = (uint32_t)sc->sc_voc; 768 edata->state = ENVSYS_SVALID; 769 } else { 770 edata->state = ENVSYS_SINVALID; 771 } 772 mutex_exit(&sc->sc_mutex); 773 } 774 775 static int 776 sgp40_detach(device_t self, int flags) 777 { 778 struct sgp40_sc *sc; 779 780 sc = device_private(self); 781 782 /* stop the measurement thread */ 783 sgp40_stop_thread(sc); 784 785 /* Remove the sensors */ 786 mutex_enter(&sc->sc_mutex); 787 if (sc->sc_sme != NULL) { 788 sysmon_envsys_unregister(sc->sc_sme); 789 sc->sc_sme = NULL; 790 } 791 mutex_exit(&sc->sc_mutex); 792 793 /* Remove the sysctl tree */ 794 sysctl_teardown(&sc->sc_sgp40log); 795 796 /* Remove the mutex */ 797 mutex_destroy(&sc->sc_mutex); 798 mutex_destroy(&sc->sc_threadmutex); 799 800 return 0; 801 } 802 803 MODULE(MODULE_CLASS_DRIVER, sgp40mox, "i2cexec,sysmon_envsys"); 804 805 #ifdef _MODULE 806 #include "ioconf.c" 807 #endif 808 809 static int 810 sgp40mox_modcmd(modcmd_t cmd, void *opaque) 811 { 812 813 switch (cmd) { 814 case MODULE_CMD_INIT: 815 #ifdef _MODULE 816 return config_init_component(cfdriver_ioconf_sgp40mox, 817 cfattach_ioconf_sgp40mox, cfdata_ioconf_sgp40mox); 818 #else 819 return 0; 820 #endif 821 case MODULE_CMD_FINI: 822 #ifdef _MODULE 823 return config_fini_component(cfdriver_ioconf_sgp40mox, 824 cfattach_ioconf_sgp40mox, cfdata_ioconf_sgp40mox); 825 #else 826 return 0; 827 #endif 828 default: 829 return ENOTTY; 830 } 831 } 832