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