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 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 static void 234 aibs_init_new(device_t self) 235 { 236 struct aibs_softc *sc = device_private(self); 237 ACPI_OBJECT_LIST arg; 238 ACPI_OBJECT id, *obj; 239 ACPI_BUFFER buf; 240 ACPI_STATUS rv; 241 uint32_t i, n; 242 243 arg.Count = 1; 244 arg.Pointer = &id; 245 246 id.Type = ACPI_TYPE_INTEGER; 247 id.Integer.Value = AIBS_MUX_HWMON; 248 249 buf.Pointer = NULL; 250 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 251 252 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GGRP", &arg, &buf); 253 254 if (ACPI_FAILURE(rv)) 255 goto out; 256 257 obj = buf.Pointer; 258 259 if (obj->Type != ACPI_TYPE_PACKAGE) { 260 rv = AE_TYPE; 261 goto out; 262 } 263 264 if (obj->Package.Count > UINT32_MAX) { 265 rv = AE_AML_NUMERIC_OVERFLOW; 266 goto out; 267 } 268 269 n = obj->Package.Count; 270 271 if (n == 0) { 272 rv = AE_NOT_EXIST; 273 goto out; 274 } 275 276 for (i = 0; i < n; i++) 277 aibs_sensor_add(self, &obj->Package.Elements[i]); 278 279 out: 280 if (buf.Pointer != NULL) 281 ACPI_FREE(buf.Pointer); 282 283 if (ACPI_FAILURE(rv)) { 284 285 aprint_error_dev(self, "failed to evaluate " 286 "GGRP: %s\n", AcpiFormatException(rv)); 287 } 288 } 289 290 static void 291 aibs_init_old(device_t self, int type) 292 { 293 struct aibs_softc *sc = device_private(self); 294 char path[] = "?SIF"; 295 ACPI_OBJECT *elm, *obj; 296 ACPI_BUFFER buf; 297 ACPI_STATUS rv; 298 uint32_t i, n; 299 300 switch (type) { 301 302 case AIBS_TYPE_FAN: 303 path[0] = 'F'; 304 break; 305 306 case AIBS_TYPE_TEMP: 307 path[0] = 'T'; 308 break; 309 310 case AIBS_TYPE_VOLT: 311 path[0] = 'V'; 312 break; 313 314 default: 315 return; 316 } 317 318 rv = acpi_eval_struct(sc->sc_node->ad_handle, path, &buf); 319 320 if (ACPI_FAILURE(rv)) 321 goto out; 322 323 obj = buf.Pointer; 324 325 if (obj->Type != ACPI_TYPE_PACKAGE) { 326 rv = AE_TYPE; 327 goto out; 328 } 329 330 elm = obj->Package.Elements; 331 332 if (elm[0].Type != ACPI_TYPE_INTEGER) { 333 rv = AE_TYPE; 334 goto out; 335 } 336 337 if (elm[0].Integer.Value > UINT32_MAX) { 338 rv = AE_AML_NUMERIC_OVERFLOW; 339 goto out; 340 } 341 342 n = elm[0].Integer.Value; 343 344 if (n == 0) { 345 rv = AE_NOT_EXIST; 346 goto out; 347 } 348 349 if (obj->Package.Count - 1 != n) { 350 rv = AE_BAD_VALUE; 351 goto out; 352 } 353 354 for (i = 1; i < obj->Package.Count; i++) { 355 356 if (elm[i].Type != ACPI_TYPE_PACKAGE) 357 continue; 358 359 aibs_sensor_add(self, &elm[i]); 360 } 361 362 out: 363 if (buf.Pointer != NULL) 364 ACPI_FREE(buf.Pointer); 365 366 if (ACPI_FAILURE(rv)) { 367 368 aprint_error_dev(self, "failed to evaluate " 369 "%s: %s\n", path, AcpiFormatException(rv)); 370 } 371 } 372 373 static void 374 aibs_sensor_add(device_t self, ACPI_OBJECT *obj) 375 { 376 struct aibs_softc *sc = device_private(self); 377 struct aibs_sensor *as; 378 int ena, len, lhi, llo; 379 const char *name; 380 ACPI_STATUS rv; 381 382 as = NULL; 383 rv = AE_OK; 384 385 if (obj->Type != ACPI_TYPE_PACKAGE) { 386 rv = AE_TYPE; 387 goto out; 388 } 389 390 /* 391 * The known formats are: 392 * 393 * index type old new 394 * ----- ---- --- --- 395 * 0 integer flags flags 396 * 1 string name name 397 * 2 integer limit1 unknown 398 * 3 integer limit2 unknown 399 * 4 integer enable limit1 400 * 5 integer - limit2 401 * 6 integer - enable 402 */ 403 if (sc->sc_model != false) { 404 len = 7; 405 llo = 4; 406 lhi = 5; 407 ena = 6; 408 } else { 409 len = 5; 410 llo = 2; 411 lhi = 3; 412 ena = 4; 413 } 414 415 if (obj->Package.Count != (uint32_t)len) { 416 rv = AE_LIMIT; 417 goto out; 418 } 419 420 if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER || 421 obj->Package.Elements[1].Type != ACPI_TYPE_STRING || 422 obj->Package.Elements[llo].Type != ACPI_TYPE_INTEGER || 423 obj->Package.Elements[lhi].Type != ACPI_TYPE_INTEGER || 424 obj->Package.Elements[ena].Type != ACPI_TYPE_INTEGER) { 425 rv = AE_TYPE; 426 goto out; 427 } 428 429 as = kmem_zalloc(sizeof(*as), KM_SLEEP); 430 431 if (as == NULL) { 432 rv = AE_NO_MEMORY; 433 goto out; 434 } 435 436 name = obj->Package.Elements[1].String.Pointer; 437 438 as->as_type = obj->Package.Elements[0].Integer.Value; 439 as->as_liml = obj->Package.Elements[llo].Integer.Value; 440 as->as_limh = obj->Package.Elements[lhi].Integer.Value; 441 442 if (sc->sc_model != false) 443 as->as_limh += as->as_liml; /* A range in the new model. */ 444 445 switch (AIBS_TYPE(as->as_type)) { 446 447 case AIBS_TYPE_FAN: 448 as->as_sensor.units = ENVSYS_SFANRPM; 449 as->as_sensor.flags |= ENVSYS_FMONLIMITS; 450 break; 451 452 case AIBS_TYPE_TEMP: 453 as->as_sensor.units = ENVSYS_STEMP; 454 as->as_sensor.flags |= ENVSYS_FMONLIMITS; 455 break; 456 457 case AIBS_TYPE_VOLT: 458 as->as_sensor.units = ENVSYS_SVOLTS_DC; 459 as->as_sensor.flags |= ENVSYS_FMONLIMITS; 460 break; 461 462 default: 463 rv = AE_TYPE; 464 goto out; 465 } 466 467 (void)strlcpy(as->as_sensor.desc, name, sizeof(as->as_sensor.desc)); 468 as->as_sensor.state = ENVSYS_SINVALID; 469 470 if (sysmon_envsys_sensor_attach(sc->sc_sme, &as->as_sensor) != 0) { 471 rv = AE_AML_INTERNAL; 472 goto out; 473 } 474 475 SIMPLEQ_INSERT_TAIL(&sc->as_head, as, as_list); 476 477 out: 478 if (ACPI_FAILURE(rv)) { 479 480 if (as != NULL) 481 kmem_free(as, sizeof(*as)); 482 483 aprint_error_dev(self, "failed to add " 484 "sensor: %s\n", AcpiFormatException(rv)); 485 } 486 } 487 488 static bool 489 aibs_sensor_value(device_t self, struct aibs_sensor *as, uint64_t *val) 490 { 491 struct aibs_softc *sc = device_private(self); 492 uint32_t type, *ret, cmb[3]; 493 ACPI_OBJECT_LIST arg; 494 ACPI_OBJECT cmi, tmp; 495 ACPI_OBJECT *obj; 496 ACPI_BUFFER buf; 497 ACPI_STATUS rv; 498 const char *path; 499 500 if (sc->sc_model != false) { 501 502 path = "GITM"; 503 504 cmb[0] = as->as_type; 505 cmb[1] = 0; 506 cmb[2] = 0; 507 508 arg.Count = 1; 509 arg.Pointer = &tmp; 510 511 tmp.Buffer.Length = sizeof(cmb); 512 tmp.Buffer.Pointer = (uint8_t *)cmb; 513 tmp.Type = type = ACPI_TYPE_BUFFER; 514 515 } else { 516 517 arg.Count = 1; 518 arg.Pointer = &cmi; 519 520 cmi.Integer.Value = as->as_type; 521 cmi.Type = type = ACPI_TYPE_INTEGER; 522 523 switch (AIBS_TYPE(as->as_type)) { 524 525 case AIBS_TYPE_FAN: 526 path = "RFAN"; 527 break; 528 529 case AIBS_TYPE_TEMP: 530 path = "RTMP"; 531 break; 532 533 case AIBS_TYPE_VOLT: 534 path = "RVLT"; 535 break; 536 537 default: 538 return false; 539 } 540 } 541 542 buf.Pointer = NULL; 543 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 544 545 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, &buf); 546 547 if (ACPI_FAILURE(rv)) 548 goto out; 549 550 obj = buf.Pointer; 551 552 if (obj->Type != type) { 553 rv = AE_TYPE; 554 goto out; 555 } 556 557 if (sc->sc_model != true) 558 *val = obj->Integer.Value; 559 else { 560 /* 561 * The return buffer contains at least: 562 * 563 * uint32_t buf[0] flags 564 * uint32_t buf[1] return value 565 * uint8_t buf[2-] unknown 566 */ 567 if (obj->Buffer.Length < 8) { 568 rv = AE_BUFFER_OVERFLOW; 569 goto out; 570 } 571 572 ret = (uint32_t *)obj->Buffer.Pointer; 573 574 if (ret[0] == 0) { 575 rv = AE_BAD_VALUE; 576 goto out; 577 } 578 579 *val = ret[1]; 580 } 581 582 out: 583 if (buf.Pointer != NULL) 584 ACPI_FREE(buf.Pointer); 585 586 if (ACPI_FAILURE(rv)) { 587 588 aprint_error_dev(self, "failed to evaluate " 589 "%s: %s\n", path, AcpiFormatException(rv)); 590 591 return false; 592 } 593 594 return true; 595 } 596 597 static void 598 aibs_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 599 { 600 struct aibs_softc *sc = sme->sme_cookie; 601 struct aibs_sensor *tmp, *as = NULL; 602 envsys_data_t *s = edata; 603 uint64_t val = 0; 604 605 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) { 606 607 if (tmp->as_sensor.sensor == s->sensor) { 608 as = tmp; 609 break; 610 } 611 } 612 613 if (as == NULL) { 614 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n"); 615 return; 616 } 617 618 as->as_sensor.state = ENVSYS_SINVALID; 619 as->as_sensor.flags |= ENVSYS_FMONNOTSUPP; 620 621 if (aibs_sensor_value(sc->sc_dev, as, &val) != true) 622 return; 623 624 switch (as->as_sensor.units) { 625 626 case ENVSYS_SFANRPM: 627 as->as_sensor.value_cur = val; 628 break; 629 630 case ENVSYS_STEMP: 631 632 if (val == 0) 633 return; 634 635 as->as_sensor.value_cur = val * 100 * 1000 + 273150000; 636 break; 637 638 case ENVSYS_SVOLTS_DC: 639 as->as_sensor.value_cur = val * 1000; 640 break; 641 642 default: 643 return; 644 } 645 646 as->as_sensor.state = ENVSYS_SVALID; 647 as->as_sensor.flags &= ~ENVSYS_FMONNOTSUPP; 648 } 649 650 static void 651 aibs_sensor_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 652 sysmon_envsys_lim_t *limits, uint32_t *props) 653 { 654 struct aibs_softc *sc = sme->sme_cookie; 655 struct aibs_sensor *tmp, *as = NULL; 656 sysmon_envsys_lim_t *lim = limits; 657 envsys_data_t *s = edata; 658 659 SIMPLEQ_FOREACH(tmp, &sc->as_head, as_list) { 660 661 if (tmp->as_sensor.sensor == s->sensor) { 662 as = tmp; 663 break; 664 } 665 } 666 667 if (as == NULL) { 668 aprint_debug_dev(sc->sc_dev, "failed to find sensor\n"); 669 return; 670 } 671 672 switch (as->as_sensor.units) { 673 674 case ENVSYS_SFANRPM: 675 676 /* 677 * Some boards have strange limits for fans. 678 */ 679 if (as->as_liml == 0) { 680 lim->sel_warnmin = as->as_limh; 681 *props = PROP_WARNMIN; 682 683 } else { 684 lim->sel_warnmin = as->as_liml; 685 lim->sel_warnmax = as->as_limh; 686 *props = PROP_WARNMIN | PROP_WARNMAX; 687 } 688 689 break; 690 691 case ENVSYS_STEMP: 692 lim->sel_critmax = as->as_limh * 100 * 1000 + 273150000; 693 lim->sel_warnmax = as->as_liml * 100 * 1000 + 273150000; 694 695 *props = PROP_CRITMAX | PROP_WARNMAX; 696 break; 697 698 case ENVSYS_SVOLTS_DC: 699 lim->sel_critmin = as->as_liml * 1000; 700 lim->sel_critmax = as->as_limh * 1000; 701 *props = PROP_CRITMIN | PROP_CRITMAX; 702 break; 703 704 default: 705 return; 706 } 707 } 708 709 MODULE(MODULE_CLASS_DRIVER, aibs, NULL); 710 711 #ifdef _MODULE 712 #include "ioconf.c" 713 #endif 714 715 static int 716 aibs_modcmd(modcmd_t cmd, void *aux) 717 { 718 int rv = 0; 719 720 switch (cmd) { 721 722 case MODULE_CMD_INIT: 723 724 #ifdef _MODULE 725 rv = config_init_component(cfdriver_ioconf_aibs, 726 cfattach_ioconf_aibs, cfdata_ioconf_aibs); 727 #endif 728 break; 729 730 case MODULE_CMD_FINI: 731 732 #ifdef _MODULE 733 rv = config_fini_component(cfdriver_ioconf_aibs, 734 cfattach_ioconf_aibs, cfdata_ioconf_aibs); 735 #endif 736 break; 737 738 default: 739 rv = ENOTTY; 740 } 741 742 return rv; 743 } 744