1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2009-2010, Intel Corporation. 23 * All rights reserved. 24 */ 25 26 /* 27 * Platform specific device enumerator for ACPI specific devices. 28 * "x86 system devices" refers to the suite of hardware components which are 29 * common to the x86 platform and play important roles in the system 30 * architecture but can't be enumerated/discovered through industry-standard 31 * bus specifications. Examples of these x86 system devices include: 32 * * Logical processor/CPU 33 * * Memory device 34 * * Non-PCI discoverable IOMMU or DMA Remapping Engine 35 * * Non-PCI discoverable IOxAPIC 36 * * Non-PCI discoverable HPET (High Precision Event Timer) 37 * * ACPI defined devices, including power button, sleep button, battery etc. 38 * 39 * X86 system devices may be discovered through BIOS/Firmware interfaces, such 40 * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't 41 * covered by any industry-standard bus specifications. 42 * 43 * In order to aid Solaris in flexibly managing x86 system devices, 44 * x86 system devices are placed into a specific firmware device 45 * subtree whose device path is '/devices/fw'. 46 * 47 * This driver populates the firmware device subtree with ACPI-discoverable 48 * system devices if possible. To achieve that, the ACPI object 49 * namespace is abstracted as ACPI virtual buses which host system devices. 50 * Another nexus driver for the ACPI virtual bus will manage all devices 51 * connected to it. 52 * 53 * For more detailed information, please refer to PSARC/2009/104. 54 */ 55 56 #include <sys/types.h> 57 #include <sys/bitmap.h> 58 #include <sys/cmn_err.h> 59 #include <sys/ddi_subrdefs.h> 60 #include <sys/errno.h> 61 #include <sys/modctl.h> 62 #include <sys/mutex.h> 63 #include <sys/note.h> 64 #include <sys/obpdefs.h> 65 #include <sys/sunddi.h> 66 #include <sys/sunndi.h> 67 #include <sys/acpi/acpi.h> 68 #include <sys/acpica.h> 69 #include <sys/acpidev.h> 70 #include <sys/acpidev_dr.h> 71 #include <sys/acpidev_impl.h> 72 73 /* Patchable through /etc/system */ 74 int acpidev_options = 0; 75 int acpidev_debug = 0; 76 77 krwlock_t acpidev_class_lock; 78 acpidev_class_list_t *acpidev_class_list_root = NULL; 79 ulong_t acpidev_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)]; 80 81 /* ACPI device autoconfig global status */ 82 typedef enum acpidev_status { 83 ACPIDEV_STATUS_FAILED = -2, /* ACPI device autoconfig failed */ 84 ACPIDEV_STATUS_DISABLED = -1, /* ACPI device autoconfig disabled */ 85 ACPIDEV_STATUS_UNKNOWN = 0, /* initial status */ 86 ACPIDEV_STATUS_INITIALIZED, /* ACPI device autoconfig initialized */ 87 ACPIDEV_STATUS_FIRST_PASS, /* first probing finished */ 88 ACPIDEV_STATUS_READY /* second probing finished */ 89 } acpidev_status_t; 90 91 static acpidev_status_t acpidev_status = ACPIDEV_STATUS_UNKNOWN; 92 static kmutex_t acpidev_drv_lock; 93 static dev_info_t *acpidev_root_dip = NULL; 94 95 /* Boot time ACPI device enumerator. */ 96 static void acpidev_boot_probe(int type); 97 98 /* DDI module auto configuration interface */ 99 extern struct mod_ops mod_miscops; 100 101 static struct modlmisc modlmisc = { 102 &mod_miscops, 103 "ACPI device enumerator" 104 }; 105 106 static struct modlinkage modlinkage = { 107 MODREV_1, 108 { (void *)&modlmisc, NULL } 109 }; 110 111 int 112 _init(void) 113 { 114 int err; 115 116 if ((err = mod_install(&modlinkage)) == 0) { 117 bzero(acpidev_object_type_mask, 118 sizeof (acpidev_object_type_mask)); 119 mutex_init(&acpidev_drv_lock, NULL, MUTEX_DRIVER, NULL); 120 rw_init(&acpidev_class_lock, NULL, RW_DEFAULT, NULL); 121 acpidev_dr_init(); 122 impl_bus_add_probe(acpidev_boot_probe); 123 } else { 124 cmn_err(CE_WARN, "!acpidev: failed to install driver."); 125 } 126 127 return (err); 128 } 129 130 int 131 _fini(void) 132 { 133 /* No support for module unload. */ 134 return (EBUSY); 135 } 136 137 int 138 _info(struct modinfo *modinfop) 139 { 140 return (mod_info(&modlinkage, modinfop)); 141 } 142 143 /* Check blacklists and load platform specific driver modules. */ 144 static ACPI_STATUS 145 acpidev_load_plat_modules(void) 146 { 147 return (AE_OK); 148 } 149 150 /* Unload platform specific driver modules. */ 151 static void 152 acpidev_unload_plat_modules(void) 153 { 154 } 155 156 /* Unregister all device class drivers from the device driver lists. */ 157 static void 158 acpidev_class_list_fini(void) 159 { 160 acpidev_unload_plat_modules(); 161 162 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 163 (void) acpidev_unregister_class(&acpidev_class_list_scope, 164 &acpidev_class_pci); 165 (void) acpidev_unregister_class(&acpidev_class_list_device, 166 &acpidev_class_pci); 167 } 168 169 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 170 (void) acpidev_unregister_class(&acpidev_class_list_device, 171 &acpidev_class_memory); 172 } 173 174 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 175 (void) acpidev_unregister_class(&acpidev_class_list_device, 176 &acpidev_class_cpu); 177 (void) acpidev_unregister_class(&acpidev_class_list_scope, 178 &acpidev_class_cpu); 179 (void) acpidev_unregister_class(&acpidev_class_list_root, 180 &acpidev_class_cpu); 181 } 182 183 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 184 (void) acpidev_unregister_class(&acpidev_class_list_device, 185 &acpidev_class_container); 186 } 187 188 (void) acpidev_unregister_class(&acpidev_class_list_device, 189 &acpidev_class_device); 190 (void) acpidev_unregister_class(&acpidev_class_list_root, 191 &acpidev_class_device); 192 193 (void) acpidev_unregister_class(&acpidev_class_list_root, 194 &acpidev_class_scope); 195 } 196 197 /* Register all device class drivers onto the driver lists. */ 198 static ACPI_STATUS 199 acpidev_class_list_init(uint64_t *fp) 200 { 201 ACPI_STATUS rc = AE_OK; 202 203 /* Set bit in mask for supported object types. */ 204 BT_SET(acpidev_object_type_mask, ACPI_TYPE_LOCAL_SCOPE); 205 BT_SET(acpidev_object_type_mask, ACPI_TYPE_DEVICE); 206 207 /* 208 * Register the ACPI scope class driver onto the class driver lists. 209 * Currently only ACPI scope objects under ACPI root node, such as _PR, 210 * _SB, _TZ etc, need to be handled, so only register the scope class 211 * driver onto the root list. 212 */ 213 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, 214 &acpidev_class_scope, B_FALSE))) { 215 goto error_out; 216 } 217 218 /* 219 * Register the ACPI device class driver onto the class driver lists. 220 * The ACPI device class driver should be registered at the tail to 221 * handle all device objects which haven't been handled by other 222 * HID/CID specific device class drivers. 223 */ 224 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, 225 &acpidev_class_device, B_TRUE))) { 226 goto error_root_device; 227 } 228 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device, 229 &acpidev_class_device, B_TRUE))) { 230 goto error_device_device; 231 } 232 233 /* Check and register support for ACPI container device. */ 234 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 235 if (ACPI_FAILURE(acpidev_register_class( 236 &acpidev_class_list_device, &acpidev_class_container, 237 B_FALSE))) { 238 goto error_device_container; 239 } 240 *fp |= ACPI_DEVCFG_CONTAINER; 241 } 242 243 /* Check and register support for ACPI CPU device. */ 244 if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) { 245 /* Handle ACPI CPU Device */ 246 if (ACPI_FAILURE(acpidev_register_class( 247 &acpidev_class_list_device, &acpidev_class_cpu, B_FALSE))) { 248 goto error_device_cpu; 249 } 250 /* Handle ACPI Processor under _PR */ 251 if (ACPI_FAILURE(acpidev_register_class( 252 &acpidev_class_list_scope, &acpidev_class_cpu, B_FALSE))) { 253 goto error_scope_cpu; 254 } 255 /* House-keeping for CPU scan */ 256 if (ACPI_FAILURE(acpidev_register_class( 257 &acpidev_class_list_root, &acpidev_class_cpu, B_FALSE))) { 258 goto error_root_cpu; 259 } 260 BT_SET(acpidev_object_type_mask, ACPI_TYPE_PROCESSOR); 261 *fp |= ACPI_DEVCFG_CPU; 262 } 263 264 /* Check support of ACPI memory devices. */ 265 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 266 /* 267 * Register the ACPI memory class driver onto the 268 * acpidev_class_list_device list because ACPI module 269 * class driver uses that list. 270 */ 271 if (ACPI_FAILURE(acpidev_register_class( 272 &acpidev_class_list_device, &acpidev_class_memory, 273 B_FALSE))) { 274 goto error_device_memory; 275 } 276 *fp |= ACPI_DEVCFG_MEMORY; 277 } 278 279 /* Check support of PCI/PCIex Host Bridge devices. */ 280 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 281 /* 282 * Register pci/pciex class drivers onto 283 * the acpidev_class_list_device class list because ACPI 284 * module class driver uses that list. 285 */ 286 if (ACPI_FAILURE(acpidev_register_class( 287 &acpidev_class_list_device, &acpidev_class_pci, 288 B_FALSE))) { 289 goto error_device_pci; 290 } 291 292 /* 293 * Register pci/pciex class drivers onto the 294 * acpidev_class_list_scope class list. 295 */ 296 if (ACPI_FAILURE(acpidev_register_class( 297 &acpidev_class_list_scope, &acpidev_class_pci, 298 B_FALSE))) { 299 goto error_scope_pci; 300 } 301 302 *fp |= ACPI_DEVCFG_PCI; 303 } 304 305 /* Check blacklist and load platform specific modules. */ 306 rc = acpidev_load_plat_modules(); 307 if (ACPI_FAILURE(rc)) { 308 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to check blacklist " 309 "or load pratform modules."); 310 goto error_plat; 311 } 312 313 return (AE_OK); 314 315 error_plat: 316 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 317 (void) acpidev_unregister_class(&acpidev_class_list_scope, 318 &acpidev_class_pci); 319 } 320 error_scope_pci: 321 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 322 (void) acpidev_unregister_class(&acpidev_class_list_device, 323 &acpidev_class_pci); 324 } 325 error_device_pci: 326 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 327 (void) acpidev_unregister_class(&acpidev_class_list_device, 328 &acpidev_class_memory); 329 } 330 error_device_memory: 331 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 332 (void) acpidev_unregister_class(&acpidev_class_list_root, 333 &acpidev_class_cpu); 334 } 335 error_root_cpu: 336 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 337 (void) acpidev_unregister_class(&acpidev_class_list_scope, 338 &acpidev_class_cpu); 339 } 340 error_scope_cpu: 341 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 342 (void) acpidev_unregister_class(&acpidev_class_list_device, 343 &acpidev_class_cpu); 344 } 345 error_device_cpu: 346 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 347 (void) acpidev_unregister_class(&acpidev_class_list_device, 348 &acpidev_class_container); 349 } 350 error_device_container: 351 (void) acpidev_unregister_class(&acpidev_class_list_device, 352 &acpidev_class_device); 353 error_device_device: 354 (void) acpidev_unregister_class(&acpidev_class_list_root, 355 &acpidev_class_device); 356 error_root_device: 357 (void) acpidev_unregister_class(&acpidev_class_list_root, 358 &acpidev_class_scope); 359 error_out: 360 ACPIDEV_DEBUG(CE_WARN, 361 "!acpidev: failed to register built-in class drivers."); 362 *fp = 0; 363 364 return (AE_ERROR); 365 } 366 367 /* 368 * Called in single threaded context during boot, no protection for 369 * reentrance. 370 */ 371 static ACPI_STATUS 372 acpidev_create_root_node(void) 373 { 374 int circ, rv = AE_OK; 375 dev_info_t *dip = NULL; 376 acpidev_data_handle_t objhdl; 377 char *compatibles[] = { 378 ACPIDEV_HID_ROOTNEX, 379 ACPIDEV_TYPE_ROOTNEX, 380 ACPIDEV_HID_VIRTNEX, 381 ACPIDEV_TYPE_VIRTNEX, 382 }; 383 384 ndi_devi_enter(ddi_root_node(), &circ); 385 ASSERT(acpidev_root_dip == NULL); 386 387 /* Query whether device node already exists. */ 388 dip = ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT, -1, 0); 389 if (dip != NULL && ddi_get_parent(dip) == ddi_root_node()) { 390 ndi_devi_exit(ddi_root_node(), circ); 391 cmn_err(CE_WARN, "!acpidev: node /devices/%s already exists, " 392 "disable driver.", ACPIDEV_NODE_NAME_ROOT); 393 return (AE_ALREADY_EXISTS); 394 } 395 396 /* Create the device node if it doesn't exist. */ 397 rv = ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT, 398 (pnode_t)DEVI_SID_NODEID, &dip); 399 if (rv != NDI_SUCCESS) { 400 ndi_devi_exit(ddi_root_node(), circ); 401 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create device node " 402 "for ACPI root with errcode %d.", rv); 403 return (AE_ERROR); 404 } 405 406 /* Build cross reference between dip and ACPI object. */ 407 if (ACPI_FAILURE(acpica_tag_devinfo(dip, ACPI_ROOT_OBJECT))) { 408 (void) ddi_remove_child(dip, 0); 409 ndi_devi_exit(ddi_root_node(), circ); 410 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to tag object %s.", 411 ACPIDEV_OBJECT_NAME_SB); 412 return (AE_ERROR); 413 } 414 415 /* Set device properties. */ 416 rv = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, 417 OBP_COMPATIBLE, ACPIDEV_ARRAY_PARAM(compatibles)); 418 if (rv == NDI_SUCCESS) { 419 rv = ndi_prop_update_string(DDI_DEV_T_NONE, dip, 420 OBP_DEVICETYPE, ACPIDEV_TYPE_ROOTNEX); 421 } 422 if (rv != DDI_SUCCESS) { 423 ACPIDEV_DEBUG(CE_WARN, 424 "!acpidev: failed to set device property for /devices/%s.", 425 ACPIDEV_NODE_NAME_ROOT); 426 goto error_out; 427 } 428 429 /* Manually create an object handle for the root node */ 430 objhdl = acpidev_data_create_handle(ACPI_ROOT_OBJECT); 431 if (objhdl == NULL) { 432 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object " 433 "handle for the root node."); 434 goto error_out; 435 } 436 objhdl->aod_level = 0; 437 objhdl->aod_hdl = ACPI_ROOT_OBJECT; 438 objhdl->aod_dip = dip; 439 objhdl->aod_class = &acpidev_class_scope; 440 objhdl->aod_status = acpidev_query_device_status(ACPI_ROOT_OBJECT); 441 objhdl->aod_iflag = ACPIDEV_ODF_STATUS_VALID | 442 ACPIDEV_ODF_DEVINFO_CREATED | ACPIDEV_ODF_DEVINFO_TAGGED; 443 444 /* Bind device driver. */ 445 (void) ndi_devi_bind_driver(dip, 0); 446 447 acpidev_root_dip = dip; 448 ndi_devi_exit(ddi_root_node(), circ); 449 450 return (AE_OK); 451 452 error_out: 453 (void) acpica_untag_devinfo(dip, ACPI_ROOT_OBJECT); 454 (void) ddi_remove_child(dip, 0); 455 ndi_devi_exit(ddi_root_node(), circ); 456 return (AE_ERROR); 457 } 458 459 static void 460 acpidev_initialize(void) 461 { 462 int rc; 463 char *str = NULL; 464 uint64_t features = 0; 465 466 /* Check whether it has already been initialized. */ 467 if (acpidev_status == ACPIDEV_STATUS_DISABLED) { 468 cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " 469 "disabled by user.\n"); 470 return; 471 } else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) { 472 ACPIDEV_DEBUG(CE_NOTE, 473 "!acpidev: initialization called more than once."); 474 return; 475 } 476 477 /* Check whether ACPI device autoconfig has been disabled by user. */ 478 rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 479 DDI_PROP_DONTPASS, "acpidev-autoconfig", &str); 480 if (rc == DDI_SUCCESS) { 481 if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) { 482 cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " 483 "disabled by user.\n"); 484 ddi_prop_free(str); 485 acpidev_status = ACPIDEV_STATUS_DISABLED; 486 return; 487 } 488 ddi_prop_free(str); 489 } 490 491 /* Initialize acpica subsystem. */ 492 if (ACPI_FAILURE(acpica_init())) { 493 cmn_err(CE_WARN, 494 "!acpidev: failed to initialize acpica subsystem."); 495 acpidev_status = ACPIDEV_STATUS_FAILED; 496 return; 497 } 498 499 /* Check ACPICA subsystem status. */ 500 if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT)) { 501 cmn_err(CE_WARN, "!acpidev: ACPICA hasn't been fully " 502 "initialized, ACPI device autoconfig will be disabled."); 503 acpidev_status = ACPIDEV_STATUS_DISABLED; 504 return; 505 } 506 507 /* Converts acpidev-options from type string to int, if any */ 508 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 509 DDI_PROP_DONTPASS, "acpidev-options", &str) == DDI_PROP_SUCCESS) { 510 long data; 511 rc = ddi_strtol(str, NULL, 0, &data); 512 if (rc == 0) { 513 (void) e_ddi_prop_remove(DDI_DEV_T_NONE, 514 ddi_root_node(), "acpidev-options"); 515 (void) e_ddi_prop_update_int(DDI_DEV_T_NONE, 516 ddi_root_node(), "acpidev-options", data); 517 } 518 ddi_prop_free(str); 519 } 520 /* Get acpidev_options user options. */ 521 acpidev_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), 522 DDI_PROP_DONTPASS, "acpidev-options", acpidev_options); 523 524 /* Check whether ACPI based DR has been disabled by user. */ 525 rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 526 DDI_PROP_DONTPASS, "acpidev-dr", &str); 527 if (rc == DDI_SUCCESS) { 528 if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) { 529 cmn_err(CE_CONT, "?acpidev: ACPI based DR has been " 530 "disabled by user.\n"); 531 acpidev_dr_enable = 0; 532 } 533 ddi_prop_free(str); 534 } 535 536 /* Register all device class drivers. */ 537 if (ACPI_FAILURE(acpidev_class_list_init(&features))) { 538 cmn_err(CE_WARN, 539 "!acpidev: failed to initalize class driver lists."); 540 acpidev_status = ACPIDEV_STATUS_FAILED; 541 return; 542 } 543 544 /* Create root node for ACPI/firmware device subtree. */ 545 if (ACPI_FAILURE(acpidev_create_root_node())) { 546 cmn_err(CE_WARN, "!acpidev: failed to create root node " 547 "for acpi device tree."); 548 acpidev_class_list_fini(); 549 acpidev_status = ACPIDEV_STATUS_FAILED; 550 return; 551 } 552 553 /* Notify acpica to enable ACPI device auto configuration. */ 554 acpica_set_core_feature(ACPI_FEATURE_DEVCFG); 555 acpica_set_devcfg_feature(features); 556 557 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: ACPI device autoconfig initialized."); 558 acpidev_status = ACPIDEV_STATUS_INITIALIZED; 559 } 560 561 /* 562 * Probe devices in ACPI namespace which can't be enumerated by other methods 563 * at boot time. 564 */ 565 static ACPI_STATUS 566 acpidev_boot_probe_device(acpidev_op_type_t op_type) 567 { 568 ACPI_STATUS rc = AE_OK; 569 acpidev_walk_info_t *infop; 570 571 ASSERT(acpidev_root_dip != NULL); 572 ASSERT(op_type == ACPIDEV_OP_BOOT_PROBE || 573 op_type == ACPIDEV_OP_BOOT_REPROBE); 574 575 infop = acpidev_alloc_walk_info(op_type, 0, ACPI_ROOT_OBJECT, 576 &acpidev_class_list_root, NULL); 577 if (infop == NULL) { 578 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info " 579 "object in acpi_boot_probe_device()."); 580 return (AE_ERROR); 581 } 582 /* Enumerate ACPI devices. */ 583 rc = acpidev_probe_child(infop); 584 if (ACPI_FAILURE(rc)) { 585 cmn_err(CE_WARN, "!acpidev: failed to probe child object " 586 "under ACPI root node."); 587 } 588 acpidev_free_walk_info(infop); 589 590 return (rc); 591 } 592 593 /* 594 * Platform specific device prober for ACPI virtual bus. 595 * It will be called in single-threaded environment to enumerate devices in 596 * ACPI namespace at boot time. 597 */ 598 static void 599 acpidev_boot_probe(int type) 600 { 601 ACPI_STATUS rc; 602 603 /* Initialize subsystem on first pass. */ 604 mutex_enter(&acpidev_drv_lock); 605 if (type == 0) { 606 acpidev_initialize(); 607 if (acpidev_status != ACPIDEV_STATUS_INITIALIZED && 608 acpidev_status != ACPIDEV_STATUS_DISABLED) { 609 cmn_err(CE_WARN, "!acpidev: driver disabled due to " 610 "initalization failure."); 611 } 612 } 613 614 /* Probe ACPI devices */ 615 if (type == 0 && acpidev_status == ACPIDEV_STATUS_INITIALIZED) { 616 rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE); 617 if (ACPI_SUCCESS(rc)) { 618 /* 619 * Support of DR operations will be disabled 620 * if failed to initialize DR subsystem. 621 */ 622 rc = acpidev_dr_initialize(acpidev_root_dip); 623 if (ACPI_FAILURE(rc) && rc != AE_SUPPORT) { 624 cmn_err(CE_CONT, "?acpidev: failed to " 625 "initialize DR subsystem."); 626 } 627 acpidev_status = ACPIDEV_STATUS_FIRST_PASS; 628 } else { 629 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to probe ACPI " 630 "devices during boot."); 631 acpidev_status = ACPIDEV_STATUS_FAILED; 632 } 633 } else if (type != 0 && acpidev_status == ACPIDEV_STATUS_FIRST_PASS) { 634 rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE); 635 if (ACPI_SUCCESS(rc)) { 636 acpidev_status = ACPIDEV_STATUS_READY; 637 } else { 638 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to reprobe " 639 "ACPI devices during boot."); 640 acpidev_status = ACPIDEV_STATUS_FAILED; 641 } 642 } else if (acpidev_status != ACPIDEV_STATUS_FAILED && 643 acpidev_status != ACPIDEV_STATUS_DISABLED && 644 acpidev_status != ACPIDEV_STATUS_READY) { 645 cmn_err(CE_WARN, 646 "!acpidev: invalid ACPI device autoconfig global status."); 647 } 648 mutex_exit(&acpidev_drv_lock); 649 } 650 651 ACPI_STATUS 652 acpidev_probe_child(acpidev_walk_info_t *infop) 653 { 654 int circ; 655 dev_info_t *pdip; 656 ACPI_STATUS res, rc = AE_OK; 657 ACPI_HANDLE child; 658 ACPI_OBJECT_TYPE type; 659 acpidev_class_list_t *it; 660 acpidev_walk_info_t *cinfop; 661 acpidev_data_handle_t datap; 662 663 /* Validate parameter first. */ 664 ASSERT(infop != NULL); 665 if (infop == NULL) { 666 ACPIDEV_DEBUG(CE_WARN, 667 "!acpidev: infop is NULL in acpidev_probe_child()."); 668 return (AE_BAD_PARAMETER); 669 } 670 ASSERT(infop->awi_level < ACPIDEV_MAX_ENUM_LEVELS - 1); 671 if (infop->awi_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) { 672 ACPIDEV_DEBUG(CE_WARN, "!acpidev: recursive level is too deep " 673 "in acpidev_probe_child()."); 674 return (AE_BAD_PARAMETER); 675 } 676 ASSERT(infop->awi_class_list != NULL); 677 ASSERT(infop->awi_hdl != NULL); 678 ASSERT(infop->awi_info != NULL); 679 ASSERT(infop->awi_name != NULL); 680 ASSERT(infop->awi_data != NULL); 681 if (infop->awi_class_list == NULL || infop->awi_hdl == NULL || 682 infop->awi_info == NULL || infop->awi_name == NULL || 683 infop->awi_data == NULL) { 684 ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL fields in " 685 "acpidev_probe_child()."); 686 return (AE_BAD_PARAMETER); 687 } 688 pdip = acpidev_walk_info_get_pdip(infop); 689 if (pdip == NULL) { 690 ACPIDEV_DEBUG(CE_WARN, 691 "!acpidev: pdip is NULL in acpidev_probe_child()."); 692 return (AE_BAD_PARAMETER); 693 } 694 695 ndi_devi_enter(pdip, &circ); 696 rw_enter(&acpidev_class_lock, RW_READER); 697 698 /* Call pre-probe callback functions. */ 699 for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 700 if (it->acl_class->adc_pre_probe == NULL) { 701 continue; 702 } 703 infop->awi_class_curr = it->acl_class; 704 if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) { 705 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to pre-probe " 706 "device of type %s under %s.", 707 it->acl_class->adc_class_name, infop->awi_name); 708 } 709 } 710 711 /* Walk child objects. */ 712 child = NULL; 713 while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY, 714 infop->awi_hdl, child, &child))) { 715 /* Skip object if we're not interested in it. */ 716 if (ACPI_FAILURE(AcpiGetType(child, &type)) || 717 type > ACPI_TYPE_NS_NODE_MAX || 718 BT_TEST(acpidev_object_type_mask, type) == 0) { 719 continue; 720 } 721 722 /* It's another hotplug-capable board, skip it. */ 723 if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE && 724 acpidev_dr_device_is_board(child)) { 725 continue; 726 } 727 728 /* Allocate the walk info structure. */ 729 cinfop = acpidev_alloc_walk_info(infop->awi_op_type, 730 infop->awi_level + 1, child, NULL, infop); 731 if (cinfop == NULL) { 732 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate " 733 "walk info child object of %s.", 734 infop->awi_name); 735 /* Mark error and continue to handle next child. */ 736 rc = AE_ERROR; 737 continue; 738 } 739 740 /* 741 * Remember the class list used to handle this object. 742 * It should be the same list for different passes of scans. 743 */ 744 ASSERT(cinfop->awi_data != NULL); 745 datap = cinfop->awi_data; 746 if (cinfop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { 747 datap->aod_class_list = infop->awi_class_list; 748 } 749 750 /* Call registered process callbacks. */ 751 for (it = *(infop->awi_class_list); it != NULL; 752 it = it->acl_next) { 753 if (it->acl_class->adc_probe == NULL) { 754 continue; 755 } 756 cinfop->awi_class_curr = it->acl_class; 757 res = it->acl_class->adc_probe(cinfop); 758 if (ACPI_FAILURE(res)) { 759 rc = res; 760 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to " 761 "process object of type %s under %s.", 762 it->acl_class->adc_class_name, 763 infop->awi_name); 764 } 765 } 766 767 /* Free resources. */ 768 acpidev_free_walk_info(cinfop); 769 } 770 771 /* Call post-probe callback functions. */ 772 for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 773 if (it->acl_class->adc_post_probe == NULL) { 774 continue; 775 } 776 infop->awi_class_curr = it->acl_class; 777 if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) { 778 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to post-probe " 779 "device of type %s under %s.", 780 it->acl_class->adc_class_name, infop->awi_name); 781 } 782 } 783 784 rw_exit(&acpidev_class_lock); 785 ndi_devi_exit(pdip, circ); 786 787 return (rc); 788 } 789 790 ACPI_STATUS 791 acpidev_process_object(acpidev_walk_info_t *infop, int flags) 792 { 793 ACPI_STATUS rc = AE_OK; 794 char *devname; 795 dev_info_t *dip, *pdip; 796 ACPI_HANDLE hdl; 797 ACPI_DEVICE_INFO *adip; 798 acpidev_class_t *clsp; 799 acpidev_data_handle_t datap; 800 acpidev_filter_result_t res; 801 802 /* Validate parameters first. */ 803 ASSERT(infop != NULL); 804 if (infop == NULL) { 805 ACPIDEV_DEBUG(CE_WARN, 806 "!acpidev: infop is NULL in acpidev_process_object()."); 807 return (AE_BAD_PARAMETER); 808 } 809 ASSERT(infop->awi_hdl != NULL); 810 ASSERT(infop->awi_info != NULL); 811 ASSERT(infop->awi_data != NULL); 812 ASSERT(infop->awi_class_curr != NULL); 813 ASSERT(infop->awi_class_curr->adc_filter != NULL); 814 hdl = infop->awi_hdl; 815 adip = infop->awi_info; 816 datap = infop->awi_data; 817 clsp = infop->awi_class_curr; 818 if (hdl == NULL || datap == NULL || adip == NULL || clsp == NULL || 819 clsp->adc_filter == NULL) { 820 ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL pointer in " 821 "acpidev_process_object()."); 822 return (AE_BAD_PARAMETER); 823 } 824 pdip = acpidev_walk_info_get_pdip(infop); 825 if (pdip == NULL) { 826 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get pdip for %s " 827 "in acpidev_process_object().", infop->awi_name); 828 return (AE_BAD_PARAMETER); 829 } 830 831 /* 832 * Check whether the object has already been handled. 833 * Tag and child dip pointer are used to indicate the object has been 834 * handled by the ACPI auto configure driver. It has the 835 * following usages: 836 * 1) Prevent creating dip for objects which already have a dip 837 * when reloading the ACPI auto configure driver. 838 * 2) Prevent creating multiple dips for ACPI objects with ACPI 839 * aliases. Currently ACPICA framework has no way to tell whether 840 * an object is an alias or not for some types of object. So tag 841 * is used to indicate that the object has been handled. 842 * 3) Prevent multiple class drivers from creating multiple devices for 843 * the same ACPI object. 844 */ 845 if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) && 846 (flags & ACPIDEV_PROCESS_FLAG_CHECK) && 847 !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) && 848 (infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED)) { 849 ASSERT(infop->awi_dip != NULL); 850 ACPIDEV_DEBUG(CE_NOTE, 851 "!acpidev: device has already been created for object %s.", 852 infop->awi_name); 853 return (AE_ALREADY_EXISTS); 854 } 855 856 /* 857 * Determine action according to following rules based on device 858 * status returned by _STA method. Please refer to ACPI3.0b section 859 * 6.3.1 and 6.5.1. 860 * present functioning enabled Action 861 * 0 0 x Do nothing 862 * 1 x 0 Do nothing 863 * 1 x 1 Create node and scan child 864 * x 1 0 Do nothing 865 * x 1 1 Create node and scan child 866 */ 867 if ((datap->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0 || 868 (flags & ACPIDEV_PROCESS_FLAG_SYNCSTATUS)) { 869 if (adip->Valid & ACPI_VALID_STA) { 870 datap->aod_status = adip->CurrentStatus; 871 } else { 872 datap->aod_status = acpidev_query_device_status(hdl); 873 } 874 datap->aod_iflag |= ACPIDEV_ODF_STATUS_VALID; 875 } 876 if (!acpidev_check_device_enabled(datap->aod_status)) { 877 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: object %s doesn't exist.", 878 infop->awi_name); 879 /* 880 * Need to scan for hotplug-capable boards even if object 881 * doesn't exist or has been disabled during the first pass. 882 * So just disable creating device node and keep on scanning. 883 */ 884 if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { 885 flags &= ~ACPIDEV_PROCESS_FLAG_CREATE; 886 } else { 887 return (AE_NOT_EXIST); 888 } 889 } 890 891 ASSERT(infop->awi_data != NULL); 892 ASSERT(infop->awi_parent != NULL); 893 ASSERT(infop->awi_parent->awi_data != NULL); 894 if (flags & ACPIDEV_PROCESS_FLAG_CREATE) { 895 mutex_enter(&(DEVI(pdip)->devi_lock)); 896 /* 897 * Put the device into offline state if its parent is in 898 * offline state. 899 */ 900 if (DEVI_IS_DEVICE_OFFLINE(pdip)) { 901 flags |= ACPIDEV_PROCESS_FLAG_OFFLINE; 902 } 903 mutex_exit(&(DEVI(pdip)->devi_lock)); 904 } 905 906 /* Evaluate filtering rules and generate device name. */ 907 devname = kmem_zalloc(ACPIDEV_MAX_NAMELEN + 1, KM_SLEEP); 908 (void) memcpy(devname, (char *)&adip->Name, sizeof (adip->Name)); 909 if (flags & ACPIDEV_PROCESS_FLAG_CREATE) { 910 res = clsp->adc_filter(infop, devname, ACPIDEV_MAX_NAMELEN); 911 } else { 912 res = clsp->adc_filter(infop, NULL, 0); 913 } 914 915 /* Create device if requested. */ 916 if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) && 917 !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) && 918 !(infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED) && 919 (res == ACPIDEV_FILTER_DEFAULT || res == ACPIDEV_FILTER_CREATE)) { 920 int ret; 921 922 /* 923 * Allocate dip and set default properties. 924 * Properties can be overriden in class specific init routines. 925 */ 926 ASSERT(infop->awi_dip == NULL); 927 ndi_devi_alloc_sleep(pdip, devname, (pnode_t)DEVI_SID_NODEID, 928 &dip); 929 infop->awi_dip = dip; 930 ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, 931 OBP_DEVICETYPE, clsp->adc_dev_type); 932 if (ret != NDI_SUCCESS) { 933 ACPIDEV_DEBUG(CE_WARN, 934 "!acpidev: failed to set device property for %s.", 935 infop->awi_name); 936 (void) ddi_remove_child(dip, 0); 937 infop->awi_dip = NULL; 938 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 939 return (AE_ERROR); 940 } 941 942 /* Build cross reference between dip and ACPI object. */ 943 if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0 && 944 ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) { 945 cmn_err(CE_WARN, 946 "!acpidev: failed to tag object %s.", 947 infop->awi_name); 948 (void) ddi_remove_child(dip, 0); 949 infop->awi_dip = NULL; 950 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 951 return (AE_ERROR); 952 } 953 954 /* Call class specific initialization callback. */ 955 if (clsp->adc_init != NULL && 956 ACPI_FAILURE(clsp->adc_init(infop))) { 957 ACPIDEV_DEBUG(CE_WARN, 958 "!acpidev: failed to initialize device %s.", 959 infop->awi_name); 960 if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) { 961 (void) acpica_untag_devinfo(dip, hdl); 962 } 963 (void) ddi_remove_child(dip, 0); 964 infop->awi_dip = NULL; 965 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 966 return (AE_ERROR); 967 } 968 969 /* Set device into offline state if requested. */ 970 if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) { 971 mutex_enter(&(DEVI(dip)->devi_lock)); 972 DEVI_SET_DEVICE_OFFLINE(dip); 973 mutex_exit(&(DEVI(dip)->devi_lock)); 974 } 975 976 /* Mark status */ 977 infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED; 978 datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_CREATED; 979 datap->aod_dip = dip; 980 datap->aod_class = clsp; 981 /* Hold reference count on class driver. */ 982 atomic_inc_32(&clsp->adc_refcnt); 983 if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) { 984 datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_TAGGED; 985 } 986 987 /* Bind device driver. */ 988 if ((flags & ACPIDEV_PROCESS_FLAG_NOBIND) != 0) { 989 mutex_enter(&(DEVI(dip)->devi_lock)); 990 DEVI(dip)->devi_flags |= DEVI_NO_BIND; 991 mutex_exit(&(DEVI(dip)->devi_lock)); 992 } else { 993 (void) ndi_devi_bind_driver(dip, 0); 994 } 995 996 /* Hold reference on branch when hot-adding devices. */ 997 if (flags & ACPIDEV_PROCESS_FLAG_HOLDBRANCH) { 998 e_ddi_branch_hold(dip); 999 } 1000 } 1001 1002 /* Free resources */ 1003 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 1004 rc = AE_OK; 1005 1006 /* Recursively scan child objects if requested. */ 1007 switch (res) { 1008 case ACPIDEV_FILTER_DEFAULT: 1009 /* FALLTHROUGH */ 1010 case ACPIDEV_FILTER_SCAN: 1011 /* Check if we need to scan child. */ 1012 if ((flags & ACPIDEV_PROCESS_FLAG_SCAN) && 1013 !(infop->awi_flags & ACPIDEV_WI_DISABLE_SCAN) && 1014 !(infop->awi_flags & ACPIDEV_WI_CHILD_SCANNED)) { 1015 /* probe child object. */ 1016 rc = acpidev_probe_child(infop); 1017 if (ACPI_FAILURE(rc)) { 1018 ACPIDEV_DEBUG(CE_WARN, 1019 "!acpidev: failed to probe subtree of %s.", 1020 infop->awi_name); 1021 rc = AE_ERROR; 1022 } 1023 /* Mark object as scanned. */ 1024 infop->awi_flags |= ACPIDEV_WI_CHILD_SCANNED; 1025 } 1026 break; 1027 1028 case ACPIDEV_FILTER_CREATE: 1029 /* FALLTHROUGH */ 1030 case ACPIDEV_FILTER_CONTINUE: 1031 /* FALLTHROUGH */ 1032 case ACPIDEV_FILTER_SKIP: 1033 break; 1034 1035 case ACPIDEV_FILTER_FAILED: 1036 ACPIDEV_DEBUG(CE_WARN, 1037 "!acpidev: failed to probe device for %s.", 1038 infop->awi_name); 1039 rc = AE_ERROR; 1040 break; 1041 1042 default: 1043 cmn_err(CE_WARN, 1044 "!acpidev: unknown filter result code %d.", res); 1045 rc = AE_ERROR; 1046 break; 1047 } 1048 1049 return (rc); 1050 } 1051 1052 acpidev_filter_result_t 1053 acpidev_filter_default(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, 1054 acpidev_filter_rule_t *afrp, char *devname, int len) 1055 { 1056 _NOTE(ARGUNUSED(hdl)); 1057 1058 ASSERT(afrp != NULL); 1059 ASSERT(devname == NULL || len >= ACPIDEV_MAX_NAMELEN); 1060 if (infop->awi_level < afrp->adf_minlvl || 1061 infop->awi_level > afrp->adf_maxlvl) { 1062 return (ACPIDEV_FILTER_CONTINUE); 1063 } else if (afrp->adf_pattern != NULL && 1064 strncmp(afrp->adf_pattern, 1065 (char *)&infop->awi_info->Name, 1066 sizeof (infop->awi_info->Name))) { 1067 return (ACPIDEV_FILTER_CONTINUE); 1068 } 1069 if (afrp->adf_replace != NULL && devname != NULL) { 1070 (void) strlcpy(devname, afrp->adf_replace, len); 1071 } 1072 1073 return (afrp->adf_retcode); 1074 } 1075 1076 acpidev_filter_result_t 1077 acpidev_filter_device(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, 1078 acpidev_filter_rule_t *afrp, int entries, char *devname, int len) 1079 { 1080 acpidev_filter_result_t res; 1081 1082 /* Evaluate filtering rules. */ 1083 for (; entries > 0; entries--, afrp++) { 1084 if (afrp->adf_filter_func != NULL) { 1085 res = afrp->adf_filter_func(infop, hdl, afrp, 1086 devname, len); 1087 } else { 1088 res = acpidev_filter_default(infop, hdl, afrp, 1089 devname, len); 1090 } 1091 if (res == ACPIDEV_FILTER_DEFAULT || 1092 res == ACPIDEV_FILTER_SCAN) { 1093 infop->awi_class_list = afrp->adf_class_list; 1094 break; 1095 } 1096 } 1097 1098 return (res); 1099 } 1100 1101 dev_info_t * 1102 acpidev_root_node(void) 1103 { 1104 return (acpidev_root_dip); 1105 } 1106 1107 ACPI_STATUS 1108 acpidev_register_class(acpidev_class_list_t **listpp, acpidev_class_t *clsp, 1109 boolean_t tail) 1110 { 1111 ACPI_STATUS rc; 1112 acpidev_class_list_t *item; 1113 acpidev_class_list_t *temp; 1114 1115 ASSERT(clsp != NULL); 1116 ASSERT(listpp != NULL); 1117 if (listpp == NULL || clsp == NULL) { 1118 ACPIDEV_DEBUG(CE_WARN, 1119 "!acpidev: invalid parameter in acpidev_register_class()."); 1120 return (AE_BAD_PARAMETER); 1121 } else if (clsp->adc_version != ACPIDEV_CLASS_REV) { 1122 cmn_err(CE_WARN, 1123 "!acpidev: class driver %s version mismatch.", 1124 clsp->adc_class_name); 1125 return (AE_BAD_DATA); 1126 } 1127 1128 rc = AE_OK; 1129 item = kmem_zalloc(sizeof (*item), KM_SLEEP); 1130 item->acl_class = clsp; 1131 rw_enter(&acpidev_class_lock, RW_WRITER); 1132 /* Check for duplicated item. */ 1133 for (temp = *listpp; temp != NULL; temp = temp->acl_next) { 1134 if (temp->acl_class == clsp) { 1135 cmn_err(CE_WARN, 1136 "!acpidev: register duplicate class driver %s.", 1137 clsp->adc_class_name); 1138 rc = AE_ALREADY_EXISTS; 1139 break; 1140 } 1141 } 1142 if (ACPI_SUCCESS(rc)) { 1143 if (tail) { 1144 while (*listpp) { 1145 listpp = &(*listpp)->acl_next; 1146 } 1147 } 1148 item->acl_next = *listpp; 1149 *listpp = item; 1150 } 1151 rw_exit(&acpidev_class_lock); 1152 if (ACPI_FAILURE(rc)) { 1153 kmem_free(item, sizeof (*item)); 1154 } 1155 1156 return (rc); 1157 } 1158 1159 ACPI_STATUS 1160 acpidev_unregister_class(acpidev_class_list_t **listpp, 1161 acpidev_class_t *clsp) 1162 { 1163 ACPI_STATUS rc = AE_NOT_FOUND; 1164 acpidev_class_list_t *temp; 1165 1166 ASSERT(clsp != NULL); 1167 ASSERT(listpp != NULL); 1168 if (listpp == NULL || clsp == NULL) { 1169 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter " 1170 "in acpidev_unregister_class()."); 1171 return (AE_BAD_PARAMETER); 1172 } 1173 1174 rw_enter(&acpidev_class_lock, RW_WRITER); 1175 for (temp = NULL; *listpp; listpp = &(*listpp)->acl_next) { 1176 if ((*listpp)->acl_class == clsp) { 1177 temp = *listpp; 1178 *listpp = (*listpp)->acl_next; 1179 break; 1180 } 1181 } 1182 if (temp == NULL) { 1183 ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) doesn't exist " 1184 "in acpidev_unregister_class().", 1185 (void *)clsp, clsp->adc_class_name); 1186 rc = AE_NOT_FOUND; 1187 } else if (temp->acl_class->adc_refcnt != 0) { 1188 ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) is still in use " 1189 "in acpidev_unregister_class()..", 1190 (void *)clsp, clsp->adc_class_name); 1191 rc = AE_ERROR; 1192 } else { 1193 kmem_free(temp, sizeof (*temp)); 1194 rc = AE_OK; 1195 } 1196 rw_exit(&acpidev_class_lock); 1197 1198 return (rc); 1199 }