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