1/* $NetBSD: aibs_acpi.c,v 1.5 2015/04/23 23:23:00 pgoyette Exp $ */
2
3/*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jukka Ruohonen.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */
33/*
34 * Copyright (c) 2009 Constantine A. Murenin <cnst+netbsd@bugmail.mojo.ru>
35 *
36 * Permission to use, copy, modify, and distribute this software for any
37 * purpose with or without fee is hereby granted, provided that the above
38 * copyright notice and this permission notice appear in all copies.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
41 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
42 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
43 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
44 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
45 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
46 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
47 */
48
49#include <sys/cdefs.h>
50__KERNEL_RCSID(0, "$NetBSD: aibs_acpi.c,v 1.5 2015/04/23 23:23:00 pgoyette Exp $");
51
52#include <sys/param.h>
53#include <sys/kmem.h>
54#include <sys/module.h>
55
56#include <dev/acpi/acpireg.h>
57#include <dev/acpi/acpivar.h>
58
59/*
60 * ASUSTeK AI Booster (ACPI ASOC ATK0110).
61 *
62 * This code was originally written for OpenBSD after the techniques
63 * described in the Linux's asus_atk0110.c and FreeBSD's acpi_aiboost.c
64 * were verified to be accurate on the actual hardware kindly provided by
65 * Sam Fourman Jr. It was subsequently ported from OpenBSD to DragonFly BSD,
66 * and then to the NetBSD's sysmon_envsys(9) framework.
67 *
68 * -- Constantine A. Murenin <http://cnst.su/>
69 */
70
71#define _COMPONENT ACPI_RESOURCE_COMPONENT
72ACPI_MODULE_NAME ("acpi_aibs")
73
74#define AIBS_MUX_HWMON 0x00000006
75#define AIBS_MUX_MGMT 0x00000011
76
77#define AIBS_TYPE(x) (((x) >> 16) & 0xff)
78#define AIBS_TYPE_VOLT 2
79#define AIBS_TYPE_TEMP 3
80#define AIBS_TYPE_FAN 4
81
82struct aibs_sensor {
83 envsys_data_t as_sensor;
84 uint64_t as_type;
85 uint64_t as_liml;
86 uint64_t as_limh;
87
88 SIMPLEQ_ENTRY(aibs_sensor) as_list;
89};
90
91struct aibs_softc {
92 device_t sc_dev;
93 struct acpi_devnode *sc_node;
94 struct sysmon_envsys *sc_sme;
95 bool sc_model; /* new model = true */
96
97 SIMPLEQ_HEAD(, aibs_sensor) as_head;
98};
99
100static int aibs_match(device_t, cfdata_t, void *);
101static void aibs_attach(device_t, device_t, void *);
102static int aibs_detach(device_t, int);
103
104static void aibs_init(device_t);
105static void aibs_init_new(device_t);
106static void aibs_init_old(device_t, int);
107
108static void aibs_sensor_add(device_t, ACPI_OBJECT *);
109static bool aibs_sensor_value(device_t, struct aibs_sensor *, uint64_t *);
110static void aibs_sensor_refresh(struct sysmon_envsys *, envsys_data_t *);
111static void aibs_sensor_limits(struct sysmon_envsys *, envsys_data_t *,
112 sysmon_envsys_lim_t *, uint32_t *);
113
114CFATTACH_DECL_NEW(aibs, sizeof(struct aibs_softc),
115 aibs_match, aibs_attach, aibs_detach, NULL);
116
117static const char* const aibs_hid[] = {
118 "ATK0110",
119 NULL
120};
121
122static int
123aibs_match(device_t parent, cfdata_t match, void *aux)
124{
125 struct acpi_attach_args *aa = aux;
126
127 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
128 return 0;
129
130 return acpi_match_hid(aa->aa_node->ad_devinfo, aibs_hid);
131}
132
133static void
134aibs_attach(device_t parent, device_t self, void *aux)
135{
136 struct aibs_softc *sc = device_private(self);
137 struct acpi_attach_args *aa = aux;
138
139 sc->sc_dev = self;
140 sc->sc_node = aa->aa_node;
141
142 aprint_naive("\n");
143 aprint_normal(": ASUSTeK AI Booster\n");
144
145 sc->sc_sme = sysmon_envsys_create();
146
147 sc->sc_sme->sme_cookie = sc;
148 sc->sc_sme->sme_name = device_xname(self);
149 sc->sc_sme->sme_refresh = aibs_sensor_refresh;
150 sc->sc_sme->sme_get_limits = aibs_sensor_limits;
151
152 aibs_init(self);
153 SIMPLEQ_INIT(&sc->as_head);
154
155 if (sc->sc_model != false)
156 aibs_init_new(self);
157 else {
158 aibs_init_old(self, AIBS_TYPE_FAN);
159 aibs_init_old(self, AIBS_TYPE_TEMP);
160 aibs_init_old(self, AIBS_TYPE_VOLT);
161 }
162
163 (void)pmf_device_register(self, NULL, NULL);
164
165 if (sc->sc_sme->sme_nsensors == 0) {
166 aprint_error_dev(self, "no sensors found\n");
167 sysmon_envsys_destroy(sc->sc_sme);
168 sc->sc_sme = NULL;
169 return;
170 }
171
172 if (sysmon_envsys_register(sc->sc_sme) != 0)
173 aprint_error_dev(self, "failed to register with sysmon\n");
174}
175
176static int
177aibs_detach(device_t self, int flags)
178{
179 struct aibs_softc *sc = device_private(self);
180 struct aibs_sensor *as;
181
182 pmf_device_deregister(self);
183
184 if (sc->sc_sme != NULL)
185 sysmon_envsys_unregister(sc->sc_sme);
186
187 while (SIMPLEQ_FIRST(&sc->as_head) != NULL) {
188 as = SIMPLEQ_FIRST(&sc->as_head);
189 SIMPLEQ_REMOVE_HEAD(&sc->as_head, as_list);
190 kmem_free(as, sizeof(*as));
191 }
192
193 return 0;
194}
195
196static void
197aibs_init(device_t self)
198{
199 struct aibs_softc *sc = device_private(self);
200 ACPI_HANDLE tmp;
201 ACPI_STATUS rv;
202
203 /*
204 * Old model uses the tuple { TSIF, VSIF, FSIF } to
205 * enumerate the sensors and { RTMP, RVLT, RFAN }
206 * to obtain the values. New mode uses GGRP for the
207 * enumeration and { GITM, SITM } as accessors.
208 */
209 rv = AcpiGetHandle(sc->sc_node->ad_handle, "GGRP", &tmp);
210
211 if (ACPI_FAILURE(rv)) {
212 sc->sc_model = false;
213 return;
214 }
215
216 rv = AcpiGetHandle(sc->sc_node->ad_handle, "GITM", &tmp);
217
218 if (ACPI_FAILURE(rv)) {
219 sc->sc_model = false;
220 return;
221 }
222
223 rv = AcpiGetHandle(sc->sc_node->ad_handle, "SITM", &tmp);
224
225 if (ACPI_FAILURE(rv)) {
226 sc->sc_model = false;
227 return;
228 }
229
230 sc->sc_model = true;
231
232 /*
233 * If both the new and the old methods are present, prefer
234 * the old one; GGRP/GITM may not be functional in this case.
235 */
236 rv = AcpiGetHandle(sc->sc_node->ad_handle, "FSIF", &tmp);
237
238 if (ACPI_FAILURE(rv))
239 return;
240
241 rv = AcpiGetHandle(sc->sc_node->ad_handle, "TSIF", &tmp);
242
243 if (ACPI_FAILURE(rv))
244 return;
245
246 rv = AcpiGetHandle(sc->sc_node->ad_handle, "VSIF", &tmp);
247
248 if (ACPI_FAILURE(rv))
249 return;
250
251 rv = AcpiGetHandle(sc->sc_node->ad_handle, "RFAN", &tmp);
252
253 if (ACPI_FAILURE(rv))
254 return;
255
256 rv = AcpiGetHandle(sc->sc_node->ad_handle, "RTMP", &tmp);
257
258 if (ACPI_FAILURE(rv))
259 return;
260
261 rv = AcpiGetHandle(sc->sc_node->ad_handle, "RVLT", &tmp);
262
263 if (ACPI_FAILURE(rv))
264 return;
265
266 sc->sc_model = false;
267}
268
269static void
270aibs_init_new(device_t self)
271{
272 struct aibs_softc *sc = device_private(self);
273 ACPI_OBJECT_LIST arg;
274 ACPI_OBJECT id, *obj;
275 ACPI_BUFFER buf;
276 ACPI_STATUS rv;
277 uint32_t i, n;
278
279 arg.Count = 1;
280 arg.Pointer = &id;
281
282 id.Type = ACPI_TYPE_INTEGER;
283 id.Integer.Value = AIBS_MUX_HWMON;
284
285 buf.Pointer = NULL;
286 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
287
288 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GGRP", &arg, &buf);
289
290 if (ACPI_FAILURE(rv))
291 goto out;
292
293 obj = buf.Pointer;
294
295 if (obj->Type != ACPI_TYPE_PACKAGE) {
296 rv = AE_TYPE;
297 goto out;
298 }
299
300 if (obj->Package.Count > UINT32_MAX) {
301 rv = AE_AML_NUMERIC_OVERFLOW;
302 goto out;
303 }
304
305 n = obj->Package.Count;
306
307 if (n == 0) {
308 rv = AE_NOT_EXIST;
309 goto out;
310 }
311
312 for (i = 0; i < n; i++)
313 aibs_sensor_add(self, &obj->Package.Elements[i]);
314
315out:
316 if (buf.Pointer != NULL)
317 ACPI_FREE(buf.Pointer);
318
319 if (ACPI_FAILURE(rv)) {
320
321 aprint_error_dev(self, "failed to evaluate "
322 "GGRP: %s\n", AcpiFormatException(rv));
323 }
324}
325
326static void
327aibs_init_old(device_t self, int type)
328{
329 struct aibs_softc *sc = device_private(self);
330 char path[] = "?SIF";
331 ACPI_OBJECT *elm, *obj;
332 ACPI_BUFFER buf;
333 ACPI_STATUS rv;
334 uint32_t i, n;
335
336 switch (type) {
337
338 case AIBS_TYPE_FAN:
339 path[0] = 'F';
340 break;
341
342 case AIBS_TYPE_TEMP:
343 path[0] = 'T';
344 break;
345
346 case AIBS_TYPE_VOLT:
347 path[0] = 'V';
348 break;
349
350 default:
351 return;
352 }
353
354 rv = acpi_eval_struct(sc->sc_node->ad_handle, path, &buf);
355
356 if (ACPI_FAILURE(rv))
357 goto out;
358
359 obj = buf.Pointer;
360
361 if (obj->Type != ACPI_TYPE_PACKAGE) {
362 rv = AE_TYPE;
363 goto out;
364 }
365
366 elm = obj->Package.Elements;
367
368 if (elm[0].Type != ACPI_TYPE_INTEGER) {
369 rv = AE_TYPE;
370 goto out;
371 }
372
373 if (elm[0].Integer.Value > UINT32_MAX) {
374 rv = AE_AML_NUMERIC_OVERFLOW;
375 goto out;
376 }
377
378 n = elm[0].Integer.Value;
379
380 if (n == 0) {
381 rv = AE_NOT_EXIST;
382 goto out;
383 }
384
385 if (obj->Package.Count - 1 != n) {
386 rv = AE_BAD_VALUE;
387 goto out;
388 }
389
390 for (i = 1; i < obj->Package.Count; i++) {
391
392 if (elm[i].Type != ACPI_TYPE_PACKAGE)
393 continue;
394
395 aibs_sensor_add(self, &elm[i]);
396 }
397
398out:
399 if (buf.Pointer != NULL)
400 ACPI_FREE(buf.Pointer);
401
402 if (ACPI_FAILURE(rv)) {
403
404 aprint_error_dev(self, "failed to evaluate "
405 "%s: %s\n", path, AcpiFormatException(rv));
406 }
407}
408
409static void
410aibs_sensor_add(device_t self, ACPI_OBJECT *obj)
411{
412 struct aibs_softc *sc = device_private(self);
413 struct aibs_sensor *as;
414 int ena, len, lhi, llo;
415 const char *name;
416 ACPI_STATUS rv;
417
418 as = NULL;
419 rv = AE_OK;
420
421 if (obj->Type != ACPI_TYPE_PACKAGE) {
422 rv = AE_TYPE;
423 goto out;
424 }
425
426 /*
427 * The known formats are:
428 *
429 * index type old new
430 * ----- ---- --- ---
431 * 0 integer flags flags
432 * 1 string name name
433 * 2 integer limit1 unknown
434 * 3 integer limit2 unknown
435 * 4 integer enable limit1
436 * 5 integer - limit2
437 * 6 integer - enable
438 */
439 if (sc->sc_model != false) {
440 len = 7;
441 llo = 4;
442 lhi = 5;
443 ena = 6;
444 } else {
445 len = 5;
446 llo = 2;
447 lhi = 3;
448 ena = 4;
449 }
450
451 if (obj->Package.Count != (uint32_t)len) {
452 rv = AE_LIMIT;
453 goto out;
454 }
455
456 if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
457 obj->Package.Elements[1].Type != ACPI_TYPE_STRING ||
458 obj->Package.Elements[llo].Type != ACPI_TYPE_INTEGER ||
459 obj->Package.Elements[lhi].Type != ACPI_TYPE_INTEGER ||
460 obj->Package.Elements[ena].Type != ACPI_TYPE_INTEGER) {
461 rv = AE_TYPE;
462 goto out;
463 }
464
465 as = kmem_zalloc(sizeof(*as), KM_SLEEP);
466
467 if (as == NULL) {
468 rv = AE_NO_MEMORY;
469 goto out;
470 }
471
472 name = obj->Package.Elements[1].String.Pointer;
473
474 as->as_type = obj->Package.Elements[0].Integer.Value;
475 as->as_liml = obj->Package.Elements[llo].Integer.Value;
476 as->as_limh = obj->Package.Elements[lhi].Integer.Value;
477
478 if (sc->sc_model != false)
479 as->as_limh += as->as_liml; /* A range in the new model. */
480
481 as->as_sensor.state = ENVSYS_SINVALID;
482
483 switch (AIBS_TYPE(as->as_type)) {
484
485 case AIBS_TYPE_FAN:
486 as->as_sensor.units = ENVSYS_SFANRPM;
487 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
488 break;
489
490 case AIBS_TYPE_TEMP:
491 as->as_sensor.units = ENVSYS_STEMP;
492 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
493 break;
494
495 case AIBS_TYPE_VOLT:
496 as->as_sensor.units = ENVSYS_SVOLTS_DC;
497 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY;
498 break;
499
500 default:
501 rv = AE_TYPE;
502 goto out;
503 }
504
505 (void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc));
506
507 if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) {
508 rv = AE_AML_INTERNAL;
509 goto out;
510 }
511
512 SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list);
513
514out:
515 if (ACPI_FAILURE(rv)) {
516
517 if (as != NULL)
518 kmem_free(as, sizeof(*as));
519
520 aprint_error_dev(self, "failed to add "
521 "sensor: %s\n", AcpiFormatException(rv));
522 }
523}
524
525static bool
526aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val)
527{
528 struct aibs_softc *sc = device_private(self);
529 uint32_t type, *ret, cmb[3];
530 ACPI_OBJECT_LIST arg;
531 ACPI_OBJECT cmi, tmp;
532 ACPI_OBJECT *obj;
533 ACPI_BUFFER buf;
534 ACPI_STATUS rv;
535 const char *path;
536
537 if (sc->sc_model != false) {
538
539 path = "GITM";
540
541 cmb[0] = as->as_type;
542 cmb[1] = 0;
543 cmb[2] = 0;
544
545 arg.Count = 1;
546 arg.Pointer = &tmp;
547
548 tmp.Buffer.Length = sizeof(cmb);
549 tmp.Buffer.Pointer = (uint8_t *)cmb;
550 tmp.Type = type = ACPI_TYPE_BUFFER;
551
552 } else {
553
554 arg.Count = 1;
555 arg.Pointer = &cmi;
556
557 cmi.Integer.Value = as->as_type;
558 cmi.Type = type = ACPI_TYPE_INTEGER;
559
560 switch (AIBS_TYPE(as->as_type)) {
561
562 case AIBS_TYPE_FAN:
563 path = "RFAN";
564 break;
565
566 case AIBS_TYPE_TEMP:
567 path = "RTMP";
568 break;
569
570 case AIBS_TYPE_VOLT:
571 path = "RVLT";
572 break;
573
574 default:
575 return false;
576 }
577 }
578
579 buf.Pointer = NULL;
580 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
581
582 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf);
583
584 if (ACPI_FAILURE(rv))
585 goto out;
586
587 obj = buf.Pointer;
588
589 if (obj->Type != type) {
590 rv = AE_TYPE;
591 goto out;
592 }
593
594 if (sc->sc_model != true)
595 *val = obj->Integer.Value;
596 else {
597 /*
598 * The return buffer contains at least:
599 *
600 * uint32_t buf[0] flags
601 * uint32_t buf[1] return value
602 * uint8_t buf[2-] unknown
603 */
604 if (obj->Buffer.Length < 8) {
605 rv = AE_BUFFER_OVERFLOW;
606 goto out;
607 }
608
609 ret = (uint32_t *)obj->Buffer.Pointer;
610
611 if (ret[0] == 0) {
612 rv = AE_BAD_VALUE;
613 goto out;
614 }
615
616 *val = ret[1];
617 }
618
619out:
620 if (buf.Pointer != NULL)
621 ACPI_FREE(buf.Pointer);
622
623 if (ACPI_FAILURE(rv)) {
624
625 aprint_error_dev(self, "failed to evaluate "
626 "%s: %s\n", path, AcpiFormatException(rv));
627
628 return false;
629 }
630
631 return true;
632}
633
634static void
635aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
636{
637 struct aibs_softc *sc = sme->sme_cookie;
638 struct aibs_sensor *tmp, *as = NULL;
639 envsys_data_t *s = edata;
640 uint64_t val = 0;
641
642 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
643
644 if (tmp->as_sensor.sensor == s->sensor) {
645 as = tmp;
646 break;
647 }
648 }
649
650 if (as == NULL) {
651 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
652 return;
653 }
654
655 as->as_sensor.state = ENVSYS_SINVALID;
656 as->as_sensor.flags |= ENVSYS_FMONNOTSUPP;
657
658 if (aibs_sensor_value(sc->sc_dev, as, &val) != true)
659 return;
660
661 switch (as->as_sensor.units) {
662
663 case ENVSYS_SFANRPM:
664 as->as_sensor.value_cur = val;
665 break;
666
667 case ENVSYS_STEMP:
668
669 if (val == 0)
670 return;
671
672 as->as_sensor.value_cur = val * 100 * 1000 + 273150000;
673 break;
674
675 case ENVSYS_SVOLTS_DC:
676 as->as_sensor.value_cur = val * 1000;
677 break;
678
679 default:
680 return;
681 }
682
683 as->as_sensor.state = ENVSYS_SVALID;
684 as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP;
685}
686
687static void
688aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
689 sysmon_envsys_lim_t *limits, uint32_t *props)
690{
691 struct aibs_softc *sc = sme->sme_cookie;
692 struct aibs_sensor *tmp, *as = NULL;
693 sysmon_envsys_lim_t *lim = limits;
694 envsys_data_t *s = edata;
695
696 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) {
697
698 if (tmp->as_sensor.sensor == s->sensor) {
699 as = tmp;
700 break;
701 }
702 }
703
704 if (as == NULL) {
705 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n");
706 return;
707 }
708
709 switch (as->as_sensor.units) {
710
711 case ENVSYS_SFANRPM:
712
713 /*
714 * Some boards have strange limits for fans.
715 */
716 if (as->as_liml == 0) {
717 lim->sel_warnmin = as->as_limh;
718 *props = PROP_WARNMIN;
719
720 } else {
721 lim->sel_warnmin = as->as_liml;
722 lim->sel_warnmax = as->as_limh;
723 *props = PROP_WARNMIN | PROP_WARNMAX;
724 }
725
726 break;
727
728 case ENVSYS_STEMP:
729 lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000;
730 lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000;
731
732 *props = PROP_CRITMAX | PROP_WARNMAX;
733 break;
734
735 case ENVSYS_SVOLTS_DC:
736 lim->sel_critmin = as->as_liml * 1000;
737 lim->sel_critmax = as->as_limh * 1000;
738 *props = PROP_CRITMIN | PROP_CRITMAX;
739 break;
740
741 default:
742 return;
743 }
744}
745
746MODULE(MODULE_CLASS_DRIVER, aibs, "sysmon_envsys");
747
748#ifdef _MODULE
749#include "ioconf.c"
750#endif
751
752static int
753aibs_modcmd(modcmd_t cmd, void *aux)
754{
755 int rv = 0;
756
757 switch (cmd) {
758
759 case MODULE_CMD_INIT:
760
761#ifdef _MODULE
762 rv = config_init_component(cfdriver_ioconf_aibs,
763 cfattach_ioconf_aibs, cfdata_ioconf_aibs);
764#endif
765 break;
766
767 case MODULE_CMD_FINI:
768
769#ifdef _MODULE
770 rv = config_fini_component(cfdriver_ioconf_aibs,
771 cfattach_ioconf_aibs, cfdata_ioconf_aibs);
772#endif
773 break;
774
775 default:
776 rv = ENOTTY;
777 }
778
779 return rv;
780}
781