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