1 /* $NetBSD: aibs_acpi.c,v 1.7 2021/01/29 15:49:55 thorpej 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.7 2021/01/29 15:49:55 thorpej 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 72 ACPI_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 82 struct 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 91 struct 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 100 static int aibs_match(device_t, cfdata_t, void *); 101 static void aibs_attach(device_t, device_t, void *); 102 static int aibs_detach(device_t, int); 103 104 static void aibs_init(device_t); 105 static void aibs_init_new(device_t); 106 static void aibs_init_old(device_t, int); 107 108 static void aibs_sensor_add(device_t, ACPI_OBJECT *); 109 static bool aibs_sensor_value(device_t, struct aibs_sensor *, uint64_t *); 110 static void aibs_sensor_refresh(struct sysmon_envsys *, envsys_data_t *); 111 static void aibs_sensor_limits(struct sysmon_envsys *, envsys_data_t *, 112 sysmon_envsys_lim_t *, uint32_t *); 113 114 CFATTACH_DECL_NEW(aibs, sizeof(struct aibs_softc), 115 aibs_match, aibs_attach, aibs_detach, NULL); 116 117 static const struct device_compatible_entry compat_data[] = { 118 { .compat = "ATK0110" }, 119 DEVICE_COMPAT_EOL 120 }; 121 122 static int 123 aibs_match(device_t parent, cfdata_t match, void *aux) 124 { 125 struct acpi_attach_args *aa = aux; 126 127 return acpi_compatible_match(aa, compat_data); 128 } 129 130 static void 131 aibs_attach(device_t parent, device_t self, void *aux) 132 { 133 struct aibs_softc *sc = device_private(self); 134 struct acpi_attach_args *aa = aux; 135 136 sc->sc_dev = self; 137 sc->sc_node = aa->aa_node; 138 139 aprint_naive("\n"); 140 aprint_normal(": ASUSTeK AI Booster\n"); 141 142 sc->sc_sme = sysmon_envsys_create(); 143 144 sc->sc_sme->sme_cookie = sc; 145 sc->sc_sme->sme_name = device_xname(self); 146 sc->sc_sme->sme_refresh = aibs_sensor_refresh; 147 sc->sc_sme->sme_get_limits = aibs_sensor_limits; 148 149 aibs_init(self); 150 SIMPLEQ_INIT(&sc->as_head); 151 152 if (sc->sc_model != false) 153 aibs_init_new(self); 154 else { 155 aibs_init_old(self, AIBS_TYPE_FAN); 156 aibs_init_old(self, AIBS_TYPE_TEMP); 157 aibs_init_old(self, AIBS_TYPE_VOLT); 158 } 159 160 (void)pmf_device_register(self, NULL, NULL); 161 162 if (sc->sc_sme->sme_nsensors == 0) { 163 aprint_error_dev(self, "no sensors found\n"); 164 sysmon_envsys_destroy(sc->sc_sme); 165 sc->sc_sme = NULL; 166 return; 167 } 168 169 if (sysmon_envsys_register(sc->sc_sme) != 0) 170 aprint_error_dev(self, "failed to register with sysmon\n"); 171 } 172 173 static int 174 aibs_detach(device_t self, int flags) 175 { 176 struct aibs_softc *sc = device_private(self); 177 struct aibs_sensor *as; 178 179 pmf_device_deregister(self); 180 181 if (sc->sc_sme != NULL) 182 sysmon_envsys_unregister(sc->sc_sme); 183 184 while (SIMPLEQ_FIRST(&sc->as_head) != NULL) { 185 as = SIMPLEQ_FIRST(&sc->as_head); 186 SIMPLEQ_REMOVE_HEAD(&sc->as_head, as_list); 187 kmem_free(as, sizeof(*as)); 188 } 189 190 return 0; 191 } 192 193 static void 194 aibs_init(device_t self) 195 { 196 struct aibs_softc *sc = device_private(self); 197 ACPI_HANDLE tmp; 198 ACPI_STATUS rv; 199 200 /* 201 * Old model uses the tuple { TSIF, VSIF, FSIF } to 202 * enumerate the sensors and { RTMP, RVLT, RFAN } 203 * to obtain the values. New mode uses GGRP for the 204 * enumeration and { GITM, SITM } as accessors. 205 */ 206 rv = AcpiGetHandle(sc->sc_node->ad_handle, "GGRP", &tmp); 207 208 if (ACPI_FAILURE(rv)) { 209 sc->sc_model = false; 210 return; 211 } 212 213 rv = AcpiGetHandle(sc->sc_node->ad_handle, "GITM", &tmp); 214 215 if (ACPI_FAILURE(rv)) { 216 sc->sc_model = false; 217 return; 218 } 219 220 rv = AcpiGetHandle(sc->sc_node->ad_handle, "SITM", &tmp); 221 222 if (ACPI_FAILURE(rv)) { 223 sc->sc_model = false; 224 return; 225 } 226 227 sc->sc_model = true; 228 229 /* 230 * If both the new and the old methods are present, prefer 231 * the old one; GGRP/GITM may not be functional in this case. 232 */ 233 rv = AcpiGetHandle(sc->sc_node->ad_handle, "FSIF", &tmp); 234 235 if (ACPI_FAILURE(rv)) 236 return; 237 238 rv = AcpiGetHandle(sc->sc_node->ad_handle, "TSIF", &tmp); 239 240 if (ACPI_FAILURE(rv)) 241 return; 242 243 rv = AcpiGetHandle(sc->sc_node->ad_handle, "VSIF", &tmp); 244 245 if (ACPI_FAILURE(rv)) 246 return; 247 248 rv = AcpiGetHandle(sc->sc_node->ad_handle, "RFAN", &tmp); 249 250 if (ACPI_FAILURE(rv)) 251 return; 252 253 rv = AcpiGetHandle(sc->sc_node->ad_handle, "RTMP", &tmp); 254 255 if (ACPI_FAILURE(rv)) 256 return; 257 258 rv = AcpiGetHandle(sc->sc_node->ad_handle, "RVLT", &tmp); 259 260 if (ACPI_FAILURE(rv)) 261 return; 262 263 sc->sc_model = false; 264 } 265 266 static void 267 aibs_init_new(device_t self) 268 { 269 struct aibs_softc *sc = device_private(self); 270 ACPI_OBJECT_LIST arg; 271 ACPI_OBJECT id, *obj; 272 ACPI_BUFFER buf; 273 ACPI_STATUS rv; 274 uint32_t i, n; 275 276 arg.Count = 1; 277 arg.Pointer = &id; 278 279 id.Type = ACPI_TYPE_INTEGER; 280 id.Integer.Value = AIBS_MUX_HWMON; 281 282 buf.Pointer = NULL; 283 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 284 285 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GGRP", &arg, &buf); 286 287 if (ACPI_FAILURE(rv)) 288 goto out; 289 290 obj = buf.Pointer; 291 292 if (obj->Type != ACPI_TYPE_PACKAGE) { 293 rv = AE_TYPE; 294 goto out; 295 } 296 297 if (obj->Package.Count > UINT32_MAX) { 298 rv = AE_AML_NUMERIC_OVERFLOW; 299 goto out; 300 } 301 302 n = obj->Package.Count; 303 304 if (n == 0) { 305 rv = AE_NOT_EXIST; 306 goto out; 307 } 308 309 for (i = 0; i < n; i++) 310 aibs_sensor_add(self, &obj->Package.Elements[i]); 311 312 out: 313 if (buf.Pointer != NULL) 314 ACPI_FREE(buf.Pointer); 315 316 if (ACPI_FAILURE(rv)) { 317 318 aprint_error_dev(self, "failed to evaluate " 319 "GGRP: %s\n", AcpiFormatException(rv)); 320 } 321 } 322 323 static void 324 aibs_init_old(device_t self, int type) 325 { 326 struct aibs_softc *sc = device_private(self); 327 char path[] = "?SIF"; 328 ACPI_OBJECT *elm, *obj; 329 ACPI_BUFFER buf; 330 ACPI_STATUS rv; 331 uint32_t i, n; 332 333 switch (type) { 334 335 case AIBS_TYPE_FAN: 336 path[0] = 'F'; 337 break; 338 339 case AIBS_TYPE_TEMP: 340 path[0] = 'T'; 341 break; 342 343 case AIBS_TYPE_VOLT: 344 path[0] = 'V'; 345 break; 346 347 default: 348 return; 349 } 350 351 rv = acpi_eval_struct(sc->sc_node->ad_handle, path, &buf); 352 353 if (ACPI_FAILURE(rv)) 354 goto out; 355 356 obj = buf.Pointer; 357 358 if (obj->Type != ACPI_TYPE_PACKAGE) { 359 rv = AE_TYPE; 360 goto out; 361 } 362 363 elm = obj->Package.Elements; 364 365 if (elm[0].Type != ACPI_TYPE_INTEGER) { 366 rv = AE_TYPE; 367 goto out; 368 } 369 370 if (elm[0].Integer.Value > UINT32_MAX) { 371 rv = AE_AML_NUMERIC_OVERFLOW; 372 goto out; 373 } 374 375 n = elm[0].Integer.Value; 376 377 if (n == 0) { 378 rv = AE_NOT_EXIST; 379 goto out; 380 } 381 382 if (obj->Package.Count - 1 != n) { 383 rv = AE_BAD_VALUE; 384 goto out; 385 } 386 387 for (i = 1; i < obj->Package.Count; i++) { 388 389 if (elm[i].Type != ACPI_TYPE_PACKAGE) 390 continue; 391 392 aibs_sensor_add(self, &elm[i]); 393 } 394 395 out: 396 if (buf.Pointer != NULL) 397 ACPI_FREE(buf.Pointer); 398 399 if (ACPI_FAILURE(rv)) { 400 401 aprint_error_dev(self, "failed to evaluate " 402 "%s: %s\n", path, AcpiFormatException(rv)); 403 } 404 } 405 406 static void 407 aibs_sensor_add(device_t self, ACPI_OBJECT *obj) 408 { 409 struct aibs_softc *sc = device_private(self); 410 struct aibs_sensor *as; 411 int ena, len, lhi, llo; 412 const char *name; 413 ACPI_STATUS rv; 414 415 as = NULL; 416 rv = AE_OK; 417 418 if (obj->Type != ACPI_TYPE_PACKAGE) { 419 rv = AE_TYPE; 420 goto out; 421 } 422 423 /* 424 * The known formats are: 425 * 426 * index type old new 427 * ----- ---- --- --- 428 * 0 integer flags flags 429 * 1 string name name 430 * 2 integer limit1 unknown 431 * 3 integer limit2 unknown 432 * 4 integer enable limit1 433 * 5 integer - limit2 434 * 6 integer - enable 435 */ 436 if (sc->sc_model != false) { 437 len = 7; 438 llo = 4; 439 lhi = 5; 440 ena = 6; 441 } else { 442 len = 5; 443 llo = 2; 444 lhi = 3; 445 ena = 4; 446 } 447 448 if (obj->Package.Count != (uint32_t)len) { 449 rv = AE_LIMIT; 450 goto out; 451 } 452 453 if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER || 454 obj->Package.Elements[1].Type != ACPI_TYPE_STRING || 455 obj->Package.Elements[llo].Type != ACPI_TYPE_INTEGER || 456 obj->Package.Elements[lhi].Type != ACPI_TYPE_INTEGER || 457 obj->Package.Elements[ena].Type != ACPI_TYPE_INTEGER) { 458 rv = AE_TYPE; 459 goto out; 460 } 461 462 as = kmem_zalloc(sizeof(*as), KM_SLEEP); 463 464 name = obj->Package.Elements[1].String.Pointer; 465 466 as->as_type = obj->Package.Elements[0].Integer.Value; 467 as->as_liml = obj->Package.Elements[llo].Integer.Value; 468 as->as_limh = obj->Package.Elements[lhi].Integer.Value; 469 470 if (sc->sc_model != false) 471 as->as_limh += as->as_liml; /* A range in the new model. */ 472 473 as->as_sensor.state = ENVSYS_SINVALID; 474 475 switch (AIBS_TYPE(as->as_type)) { 476 477 case AIBS_TYPE_FAN: 478 as->as_sensor.units = ENVSYS_SFANRPM; 479 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 480 break; 481 482 case AIBS_TYPE_TEMP: 483 as->as_sensor.units = ENVSYS_STEMP; 484 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 485 break; 486 487 case AIBS_TYPE_VOLT: 488 as->as_sensor.units = ENVSYS_SVOLTS_DC; 489 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 490 break; 491 492 default: 493 rv = AE_TYPE; 494 goto out; 495 } 496 497 (void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc)); 498 499 if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) { 500 rv = AE_AML_INTERNAL; 501 goto out; 502 } 503 504 SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list); 505 506 out: 507 if (ACPI_FAILURE(rv)) { 508 509 if (as != NULL) 510 kmem_free(as, sizeof(*as)); 511 512 aprint_error_dev(self, "failed to add " 513 "sensor: %s\n", AcpiFormatException(rv)); 514 } 515 } 516 517 static bool 518 aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val) 519 { 520 struct aibs_softc *sc = device_private(self); 521 uint32_t type, *ret, cmb[3]; 522 ACPI_OBJECT_LIST arg; 523 ACPI_OBJECT cmi, tmp; 524 ACPI_OBJECT *obj; 525 ACPI_BUFFER buf; 526 ACPI_STATUS rv; 527 const char *path; 528 529 if (sc->sc_model != false) { 530 531 path = "GITM"; 532 533 cmb[0] = as->as_type; 534 cmb[1] = 0; 535 cmb[2] = 0; 536 537 arg.Count = 1; 538 arg.Pointer = &tmp; 539 540 tmp.Buffer.Length = sizeof(cmb); 541 tmp.Buffer.Pointer = (uint8_t *)cmb; 542 tmp.Type = type = ACPI_TYPE_BUFFER; 543 544 } else { 545 546 arg.Count = 1; 547 arg.Pointer = &cmi; 548 549 cmi.Integer.Value = as->as_type; 550 cmi.Type = type = ACPI_TYPE_INTEGER; 551 552 switch (AIBS_TYPE(as->as_type)) { 553 554 case AIBS_TYPE_FAN: 555 path = "RFAN"; 556 break; 557 558 case AIBS_TYPE_TEMP: 559 path = "RTMP"; 560 break; 561 562 case AIBS_TYPE_VOLT: 563 path = "RVLT"; 564 break; 565 566 default: 567 return false; 568 } 569 } 570 571 buf.Pointer = NULL; 572 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 573 574 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf); 575 576 if (ACPI_FAILURE(rv)) 577 goto out; 578 579 obj = buf.Pointer; 580 581 if (obj->Type != type) { 582 rv = AE_TYPE; 583 goto out; 584 } 585 586 if (sc->sc_model != true) 587 *val = obj->Integer.Value; 588 else { 589 /* 590 * The return buffer contains at least: 591 * 592 * uint32_t buf[0] flags 593 * uint32_t buf[1] return value 594 * uint8_t buf[2-] unknown 595 */ 596 if (obj->Buffer.Length < 8) { 597 rv = AE_BUFFER_OVERFLOW; 598 goto out; 599 } 600 601 ret = (uint32_t *)obj->Buffer.Pointer; 602 603 if (ret[0] == 0) { 604 rv = AE_BAD_VALUE; 605 goto out; 606 } 607 608 *val = ret[1]; 609 } 610 611 out: 612 if (buf.Pointer != NULL) 613 ACPI_FREE(buf.Pointer); 614 615 if (ACPI_FAILURE(rv)) { 616 617 aprint_error_dev(self, "failed to evaluate " 618 "%s: %s\n", path, AcpiFormatException(rv)); 619 620 return false; 621 } 622 623 return true; 624 } 625 626 static void 627 aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 628 { 629 struct aibs_softc *sc = sme->sme_cookie; 630 struct aibs_sensor *tmp, *as = NULL; 631 envsys_data_t *s = edata; 632 uint64_t val = 0; 633 634 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) { 635 636 if (tmp->as_sensor.sensor == s->sensor) { 637 as = tmp; 638 break; 639 } 640 } 641 642 if (as == NULL) { 643 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n"); 644 return; 645 } 646 647 as->as_sensor.state = ENVSYS_SINVALID; 648 as->as_sensor.flags |= ENVSYS_FMONNOTSUPP; 649 650 if (aibs_sensor_value(sc->sc_dev, as, &val) != true) 651 return; 652 653 switch (as->as_sensor.units) { 654 655 case ENVSYS_SFANRPM: 656 as->as_sensor.value_cur = val; 657 break; 658 659 case ENVSYS_STEMP: 660 661 if (val == 0) 662 return; 663 664 as->as_sensor.value_cur = val * 100 * 1000 + 273150000; 665 break; 666 667 case ENVSYS_SVOLTS_DC: 668 as->as_sensor.value_cur = val * 1000; 669 break; 670 671 default: 672 return; 673 } 674 675 as->as_sensor.state = ENVSYS_SVALID; 676 as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP; 677 } 678 679 static void 680 aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 681 sysmon_envsys_lim_t *limits, uint32_t *props) 682 { 683 struct aibs_softc *sc = sme->sme_cookie; 684 struct aibs_sensor *tmp, *as = NULL; 685 sysmon_envsys_lim_t *lim = limits; 686 envsys_data_t *s = edata; 687 688 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) { 689 690 if (tmp->as_sensor.sensor == s->sensor) { 691 as = tmp; 692 break; 693 } 694 } 695 696 if (as == NULL) { 697 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n"); 698 return; 699 } 700 701 switch (as->as_sensor.units) { 702 703 case ENVSYS_SFANRPM: 704 705 /* 706 * Some boards have strange limits for fans. 707 */ 708 if (as->as_liml == 0) { 709 lim->sel_warnmin = as->as_limh; 710 *props = PROP_WARNMIN; 711 712 } else { 713 lim->sel_warnmin = as->as_liml; 714 lim->sel_warnmax = as->as_limh; 715 *props = PROP_WARNMIN | PROP_WARNMAX; 716 } 717 718 break; 719 720 case ENVSYS_STEMP: 721 lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000; 722 lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000; 723 724 *props = PROP_CRITMAX | PROP_WARNMAX; 725 break; 726 727 case ENVSYS_SVOLTS_DC: 728 lim->sel_critmin = as->as_liml * 1000; 729 lim->sel_critmax = as->as_limh * 1000; 730 *props = PROP_CRITMIN | PROP_CRITMAX; 731 break; 732 733 default: 734 return; 735 } 736 } 737 738 MODULE(MODULE_CLASS_DRIVER, aibs, "sysmon_envsys"); 739 740 #ifdef _MODULE 741 #include "ioconf.c" 742 #endif 743 744 static int 745 aibs_modcmd(modcmd_t cmd, void *aux) 746 { 747 int rv = 0; 748 749 switch (cmd) { 750 751 case MODULE_CMD_INIT: 752 753 #ifdef _MODULE 754 rv = config_init_component(cfdriver_ioconf_aibs, 755 cfattach_ioconf_aibs, cfdata_ioconf_aibs); 756 #endif 757 break; 758 759 case MODULE_CMD_FINI: 760 761 #ifdef _MODULE 762 rv = config_fini_component(cfdriver_ioconf_aibs, 763 cfattach_ioconf_aibs, cfdata_ioconf_aibs); 764 #endif 765 break; 766 767 default: 768 rv = ENOTTY; 769 } 770 771 return rv; 772 } 773