1 /* $NetBSD: aibs_acpi.c,v 1.6 2017/06/01 02:45:09 chs 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.6 2017/06/01 02:45:09 chs 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 char* const aibs_hid[] = { 118 "ATK0110", 119 NULL 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 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 133 static void 134 aibs_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 176 static int 177 aibs_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 196 static void 197 aibs_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 269 static void 270 aibs_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 315 out: 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 326 static void 327 aibs_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 398 out: 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 409 static void 410 aibs_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 name = obj->Package.Elements[1].String.Pointer; 468 469 as->as_type = obj->Package.Elements[0].Integer.Value; 470 as->as_liml = obj->Package.Elements[llo].Integer.Value; 471 as->as_limh = obj->Package.Elements[lhi].Integer.Value; 472 473 if (sc->sc_model != false) 474 as->as_limh += as->as_liml; /* A range in the new model. */ 475 476 as->as_sensor.state = ENVSYS_SINVALID; 477 478 switch (AIBS_TYPE(as->as_type)) { 479 480 case AIBS_TYPE_FAN: 481 as->as_sensor.units = ENVSYS_SFANRPM; 482 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 483 break; 484 485 case AIBS_TYPE_TEMP: 486 as->as_sensor.units = ENVSYS_STEMP; 487 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 488 break; 489 490 case AIBS_TYPE_VOLT: 491 as->as_sensor.units = ENVSYS_SVOLTS_DC; 492 as->as_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 493 break; 494 495 default: 496 rv = AE_TYPE; 497 goto out; 498 } 499 500 (void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc)); 501 502 if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) { 503 rv = AE_AML_INTERNAL; 504 goto out; 505 } 506 507 SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list); 508 509 out: 510 if (ACPI_FAILURE(rv)) { 511 512 if (as != NULL) 513 kmem_free(as, sizeof(*as)); 514 515 aprint_error_dev(self, "failed to add " 516 "sensor: %s\n", AcpiFormatException(rv)); 517 } 518 } 519 520 static bool 521 aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val) 522 { 523 struct aibs_softc *sc = device_private(self); 524 uint32_t type, *ret, cmb[3]; 525 ACPI_OBJECT_LIST arg; 526 ACPI_OBJECT cmi, tmp; 527 ACPI_OBJECT *obj; 528 ACPI_BUFFER buf; 529 ACPI_STATUS rv; 530 const char *path; 531 532 if (sc->sc_model != false) { 533 534 path = "GITM"; 535 536 cmb[0] = as->as_type; 537 cmb[1] = 0; 538 cmb[2] = 0; 539 540 arg.Count = 1; 541 arg.Pointer = &tmp; 542 543 tmp.Buffer.Length = sizeof(cmb); 544 tmp.Buffer.Pointer = (uint8_t *)cmb; 545 tmp.Type = type = ACPI_TYPE_BUFFER; 546 547 } else { 548 549 arg.Count = 1; 550 arg.Pointer = &cmi; 551 552 cmi.Integer.Value = as->as_type; 553 cmi.Type = type = ACPI_TYPE_INTEGER; 554 555 switch (AIBS_TYPE(as->as_type)) { 556 557 case AIBS_TYPE_FAN: 558 path = "RFAN"; 559 break; 560 561 case AIBS_TYPE_TEMP: 562 path = "RTMP"; 563 break; 564 565 case AIBS_TYPE_VOLT: 566 path = "RVLT"; 567 break; 568 569 default: 570 return false; 571 } 572 } 573 574 buf.Pointer = NULL; 575 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 576 577 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf); 578 579 if (ACPI_FAILURE(rv)) 580 goto out; 581 582 obj = buf.Pointer; 583 584 if (obj->Type != type) { 585 rv = AE_TYPE; 586 goto out; 587 } 588 589 if (sc->sc_model != true) 590 *val = obj->Integer.Value; 591 else { 592 /* 593 * The return buffer contains at least: 594 * 595 * uint32_t buf[0] flags 596 * uint32_t buf[1] return value 597 * uint8_t buf[2-] unknown 598 */ 599 if (obj->Buffer.Length < 8) { 600 rv = AE_BUFFER_OVERFLOW; 601 goto out; 602 } 603 604 ret = (uint32_t *)obj->Buffer.Pointer; 605 606 if (ret[0] == 0) { 607 rv = AE_BAD_VALUE; 608 goto out; 609 } 610 611 *val = ret[1]; 612 } 613 614 out: 615 if (buf.Pointer != NULL) 616 ACPI_FREE(buf.Pointer); 617 618 if (ACPI_FAILURE(rv)) { 619 620 aprint_error_dev(self, "failed to evaluate " 621 "%s: %s\n", path, AcpiFormatException(rv)); 622 623 return false; 624 } 625 626 return true; 627 } 628 629 static void 630 aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 631 { 632 struct aibs_softc *sc = sme->sme_cookie; 633 struct aibs_sensor *tmp, *as = NULL; 634 envsys_data_t *s = edata; 635 uint64_t val = 0; 636 637 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) { 638 639 if (tmp->as_sensor.sensor == s->sensor) { 640 as = tmp; 641 break; 642 } 643 } 644 645 if (as == NULL) { 646 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n"); 647 return; 648 } 649 650 as->as_sensor.state = ENVSYS_SINVALID; 651 as->as_sensor.flags |= ENVSYS_FMONNOTSUPP; 652 653 if (aibs_sensor_value(sc->sc_dev, as, &val) != true) 654 return; 655 656 switch (as->as_sensor.units) { 657 658 case ENVSYS_SFANRPM: 659 as->as_sensor.value_cur = val; 660 break; 661 662 case ENVSYS_STEMP: 663 664 if (val == 0) 665 return; 666 667 as->as_sensor.value_cur = val * 100 * 1000 + 273150000; 668 break; 669 670 case ENVSYS_SVOLTS_DC: 671 as->as_sensor.value_cur = val * 1000; 672 break; 673 674 default: 675 return; 676 } 677 678 as->as_sensor.state = ENVSYS_SVALID; 679 as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP; 680 } 681 682 static void 683 aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 684 sysmon_envsys_lim_t *limits, uint32_t *props) 685 { 686 struct aibs_softc *sc = sme->sme_cookie; 687 struct aibs_sensor *tmp, *as = NULL; 688 sysmon_envsys_lim_t *lim = limits; 689 envsys_data_t *s = edata; 690 691 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) { 692 693 if (tmp->as_sensor.sensor == s->sensor) { 694 as = tmp; 695 break; 696 } 697 } 698 699 if (as == NULL) { 700 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n"); 701 return; 702 } 703 704 switch (as->as_sensor.units) { 705 706 case ENVSYS_SFANRPM: 707 708 /* 709 * Some boards have strange limits for fans. 710 */ 711 if (as->as_liml == 0) { 712 lim->sel_warnmin = as->as_limh; 713 *props = PROP_WARNMIN; 714 715 } else { 716 lim->sel_warnmin = as->as_liml; 717 lim->sel_warnmax = as->as_limh; 718 *props = PROP_WARNMIN | PROP_WARNMAX; 719 } 720 721 break; 722 723 case ENVSYS_STEMP: 724 lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000; 725 lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000; 726 727 *props = PROP_CRITMAX | PROP_WARNMAX; 728 break; 729 730 case ENVSYS_SVOLTS_DC: 731 lim->sel_critmin = as->as_liml * 1000; 732 lim->sel_critmax = as->as_limh * 1000; 733 *props = PROP_CRITMIN | PROP_CRITMAX; 734 break; 735 736 default: 737 return; 738 } 739 } 740 741 MODULE(MODULE_CLASS_DRIVER, aibs, "sysmon_envsys"); 742 743 #ifdef _MODULE 744 #include "ioconf.c" 745 #endif 746 747 static int 748 aibs_modcmd(modcmd_t cmd, void *aux) 749 { 750 int rv = 0; 751 752 switch (cmd) { 753 754 case MODULE_CMD_INIT: 755 756 #ifdef _MODULE 757 rv = config_init_component(cfdriver_ioconf_aibs, 758 cfattach_ioconf_aibs, cfdata_ioconf_aibs); 759 #endif 760 break; 761 762 case MODULE_CMD_FINI: 763 764 #ifdef _MODULE 765 rv = config_fini_component(cfdriver_ioconf_aibs, 766 cfattach_ioconf_aibs, cfdata_ioconf_aibs); 767 #endif 768 break; 769 770 default: 771 rv = ENOTTY; 772 } 773 774 return rv; 775 } 776