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