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 * Copyright 2023 Oxide Computer Company 26 */ 27 28 /* 29 * Platform specific device enumerator for ACPI specific devices. 30 * "x86 system devices" refers to the suite of hardware components which are 31 * common to the x86 platform and play important roles in the system 32 * architecture but can't be enumerated/discovered through industry-standard 33 * bus specifications. Examples of these x86 system devices include: 34 * * Logical processor/CPU 35 * * Memory device 36 * * Non-PCI discoverable IOMMU or DMA Remapping Engine 37 * * Non-PCI discoverable IOxAPIC 38 * * Non-PCI discoverable HPET (High Precision Event Timer) 39 * * ACPI defined devices, including power button, sleep button, battery etc. 40 * 41 * X86 system devices may be discovered through BIOS/Firmware interfaces, such 42 * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't 43 * covered by any industry-standard bus specifications. 44 * 45 * In order to aid Solaris in flexibly managing x86 system devices, 46 * x86 system devices are placed into a specific firmware device 47 * subtree whose device path is '/devices/fw'. 48 * 49 * This driver populates the firmware device subtree with ACPI-discoverable 50 * system devices if possible. To achieve that, the ACPI object 51 * namespace is abstracted as ACPI virtual buses which host system devices. 52 * Another nexus driver for the ACPI virtual bus will manage all devices 53 * connected to it. 54 * 55 * For more detailed information, please refer to PSARC/2009/104. 56 */ 57 58 #include <sys/types.h> 59 #include <sys/bitmap.h> 60 #include <sys/cmn_err.h> 61 #include <sys/ddi_subrdefs.h> 62 #include <sys/errno.h> 63 #include <sys/modctl.h> 64 #include <sys/mutex.h> 65 #include <sys/note.h> 66 #include <sys/obpdefs.h> 67 #include <sys/sunddi.h> 68 #include <sys/sunndi.h> 69 #include <sys/acpi/acpi.h> 70 #include <sys/acpica.h> 71 #include <sys/acpidev.h> 72 #include <sys/acpidev_dr.h> 73 #include <sys/acpidev_impl.h> 74 75 /* Patchable through /etc/system */ 76 int acpidev_options = 0; 77 int acpidev_debug = 0; 78 79 krwlock_t acpidev_class_lock; 80 acpidev_class_list_t *acpidev_class_list_root = NULL; 81 ulong_t acpidev_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)]; 82 83 /* ACPI device autoconfig global status */ 84 typedef enum acpidev_status { 85 ACPIDEV_STATUS_FAILED = -2, /* ACPI device autoconfig failed */ 86 ACPIDEV_STATUS_DISABLED = -1, /* ACPI device autoconfig disabled */ 87 ACPIDEV_STATUS_UNKNOWN = 0, /* initial status */ 88 ACPIDEV_STATUS_INITIALIZED, /* ACPI device autoconfig initialized */ 89 ACPIDEV_STATUS_FIRST_PASS, /* first probing finished */ 90 ACPIDEV_STATUS_READY /* second probing finished */ 91 } acpidev_status_t; 92 93 static acpidev_status_t acpidev_status = ACPIDEV_STATUS_UNKNOWN; 94 static kmutex_t acpidev_drv_lock; 95 static dev_info_t *acpidev_root_dip = NULL; 96 97 /* Boot time ACPI device enumerator. */ 98 static void acpidev_boot_probe(int type); 99 100 /* DDI module auto configuration interface */ 101 extern struct mod_ops mod_miscops; 102 103 static struct modlmisc modlmisc = { 104 &mod_miscops, 105 "ACPI device enumerator" 106 }; 107 108 static struct modlinkage modlinkage = { 109 MODREV_1, 110 (void *)&modlmisc, 111 NULL 112 }; 113 114 int 115 _init(void) 116 { 117 int err; 118 119 if ((err = mod_install(&modlinkage)) == 0) { 120 bzero(acpidev_object_type_mask, 121 sizeof (acpidev_object_type_mask)); 122 mutex_init(&acpidev_drv_lock, NULL, MUTEX_DRIVER, NULL); 123 rw_init(&acpidev_class_lock, NULL, RW_DEFAULT, NULL); 124 acpidev_dr_init(); 125 impl_bus_add_probe(acpidev_boot_probe); 126 } else { 127 cmn_err(CE_WARN, "!acpidev: failed to install driver."); 128 } 129 130 return (err); 131 } 132 133 int 134 _fini(void) 135 { 136 /* No support for module unload. */ 137 return (EBUSY); 138 } 139 140 int 141 _info(struct modinfo *modinfop) 142 { 143 return (mod_info(&modlinkage, modinfop)); 144 } 145 146 /* Check blacklists and load platform specific driver modules. */ 147 static ACPI_STATUS 148 acpidev_load_plat_modules(void) 149 { 150 return (AE_OK); 151 } 152 153 /* Unload platform specific driver modules. */ 154 static void 155 acpidev_unload_plat_modules(void) 156 { 157 } 158 159 /* Unregister all device class drivers from the device driver lists. */ 160 static void 161 acpidev_class_list_fini(void) 162 { 163 acpidev_unload_plat_modules(); 164 165 (void) acpidev_unregister_class(&acpidev_class_list_usbport, 166 &acpidev_class_usbport); 167 168 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 169 (void) acpidev_unregister_class(&acpidev_class_list_scope, 170 &acpidev_class_pci); 171 (void) acpidev_unregister_class(&acpidev_class_list_device, 172 &acpidev_class_pci); 173 } 174 175 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 176 (void) acpidev_unregister_class(&acpidev_class_list_device, 177 &acpidev_class_memory); 178 } 179 180 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 181 (void) acpidev_unregister_class(&acpidev_class_list_device, 182 &acpidev_class_cpu); 183 (void) acpidev_unregister_class(&acpidev_class_list_scope, 184 &acpidev_class_cpu); 185 (void) acpidev_unregister_class(&acpidev_class_list_root, 186 &acpidev_class_cpu); 187 } 188 189 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 190 (void) acpidev_unregister_class(&acpidev_class_list_device, 191 &acpidev_class_container); 192 } 193 194 (void) acpidev_unregister_class(&acpidev_class_list_device, 195 &acpidev_class_device); 196 (void) acpidev_unregister_class(&acpidev_class_list_root, 197 &acpidev_class_device); 198 199 (void) acpidev_unregister_class(&acpidev_class_list_root, 200 &acpidev_class_scope); 201 } 202 203 /* Register all device class drivers onto the driver lists. */ 204 static ACPI_STATUS 205 acpidev_class_list_init(uint64_t *fp) 206 { 207 ACPI_STATUS rc = AE_OK; 208 209 /* Set bit in mask for supported object types. */ 210 BT_SET(acpidev_object_type_mask, ACPI_TYPE_LOCAL_SCOPE); 211 BT_SET(acpidev_object_type_mask, ACPI_TYPE_DEVICE); 212 213 /* 214 * Register the ACPI scope class driver onto the class driver lists. 215 * Currently only ACPI scope objects under ACPI root node, such as _PR, 216 * _SB, _TZ etc, need to be handled, so only register the scope class 217 * driver onto the root list. 218 */ 219 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, 220 &acpidev_class_scope, B_FALSE))) { 221 goto error_out; 222 } 223 224 /* 225 * Register the ACPI device class driver onto the class driver lists. 226 * The ACPI device class driver should be registered at the tail to 227 * handle all device objects which haven't been handled by other 228 * HID/CID specific device class drivers. 229 */ 230 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, 231 &acpidev_class_device, B_TRUE))) { 232 goto error_root_device; 233 } 234 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device, 235 &acpidev_class_device, B_TRUE))) { 236 goto error_device_device; 237 } 238 239 /* Check and register support for ACPI container device. */ 240 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 241 if (ACPI_FAILURE(acpidev_register_class( 242 &acpidev_class_list_device, &acpidev_class_container, 243 B_FALSE))) { 244 goto error_device_container; 245 } 246 *fp |= ACPI_DEVCFG_CONTAINER; 247 } 248 249 /* Check and register support for ACPI CPU device. */ 250 if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) { 251 /* Handle ACPI CPU Device */ 252 if (ACPI_FAILURE(acpidev_register_class( 253 &acpidev_class_list_device, &acpidev_class_cpu, B_FALSE))) { 254 goto error_device_cpu; 255 } 256 /* Handle ACPI Processor under _PR */ 257 if (ACPI_FAILURE(acpidev_register_class( 258 &acpidev_class_list_scope, &acpidev_class_cpu, B_FALSE))) { 259 goto error_scope_cpu; 260 } 261 /* House-keeping for CPU scan */ 262 if (ACPI_FAILURE(acpidev_register_class( 263 &acpidev_class_list_root, &acpidev_class_cpu, B_FALSE))) { 264 goto error_root_cpu; 265 } 266 BT_SET(acpidev_object_type_mask, ACPI_TYPE_PROCESSOR); 267 *fp |= ACPI_DEVCFG_CPU; 268 } 269 270 /* Check support of ACPI memory devices. */ 271 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 272 /* 273 * Register the ACPI memory class driver onto the 274 * acpidev_class_list_device list because ACPI module 275 * class driver uses that list. 276 */ 277 if (ACPI_FAILURE(acpidev_register_class( 278 &acpidev_class_list_device, &acpidev_class_memory, 279 B_FALSE))) { 280 goto error_device_memory; 281 } 282 *fp |= ACPI_DEVCFG_MEMORY; 283 } 284 285 /* Check support of PCI/PCIex Host Bridge devices. */ 286 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 287 /* 288 * Register pci/pciex class drivers onto 289 * the acpidev_class_list_device class list because ACPI 290 * module class driver uses that list. 291 */ 292 if (ACPI_FAILURE(acpidev_register_class( 293 &acpidev_class_list_device, &acpidev_class_pci, 294 B_FALSE))) { 295 goto error_device_pci; 296 } 297 298 /* 299 * Register pci/pciex class drivers onto the 300 * acpidev_class_list_scope class list. 301 */ 302 if (ACPI_FAILURE(acpidev_register_class( 303 &acpidev_class_list_scope, &acpidev_class_pci, 304 B_FALSE))) { 305 goto error_scope_pci; 306 } 307 308 *fp |= ACPI_DEVCFG_PCI; 309 } 310 311 /* Check support of USB port enumeration */ 312 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_usbport, 313 &acpidev_class_usbport, B_TRUE))) { 314 goto error_usbport; 315 } 316 317 318 /* Check blacklist and load platform specific modules. */ 319 rc = acpidev_load_plat_modules(); 320 if (ACPI_FAILURE(rc)) { 321 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to check blacklist " 322 "or load pratform modules."); 323 goto error_plat; 324 } 325 326 return (AE_OK); 327 328 error_plat: 329 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 330 (void) acpidev_unregister_class(&acpidev_class_list_scope, 331 &acpidev_class_pci); 332 } 333 334 error_usbport: 335 (void) acpidev_unregister_class(&acpidev_class_list_usbport, 336 &acpidev_class_usbport); 337 338 error_scope_pci: 339 if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) { 340 (void) acpidev_unregister_class(&acpidev_class_list_device, 341 &acpidev_class_pci); 342 } 343 error_device_pci: 344 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 345 (void) acpidev_unregister_class(&acpidev_class_list_device, 346 &acpidev_class_memory); 347 } 348 error_device_memory: 349 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 350 (void) acpidev_unregister_class(&acpidev_class_list_root, 351 &acpidev_class_cpu); 352 } 353 error_root_cpu: 354 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 355 (void) acpidev_unregister_class(&acpidev_class_list_scope, 356 &acpidev_class_cpu); 357 } 358 error_scope_cpu: 359 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 360 (void) acpidev_unregister_class(&acpidev_class_list_device, 361 &acpidev_class_cpu); 362 } 363 error_device_cpu: 364 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 365 (void) acpidev_unregister_class(&acpidev_class_list_device, 366 &acpidev_class_container); 367 } 368 error_device_container: 369 (void) acpidev_unregister_class(&acpidev_class_list_device, 370 &acpidev_class_device); 371 error_device_device: 372 (void) acpidev_unregister_class(&acpidev_class_list_root, 373 &acpidev_class_device); 374 error_root_device: 375 (void) acpidev_unregister_class(&acpidev_class_list_root, 376 &acpidev_class_scope); 377 error_out: 378 ACPIDEV_DEBUG(CE_WARN, 379 "!acpidev: failed to register built-in class drivers."); 380 *fp = 0; 381 382 return (AE_ERROR); 383 } 384 385 /* 386 * Called in single threaded context during boot, no protection for 387 * reentrance. 388 */ 389 static ACPI_STATUS 390 acpidev_create_root_node(void) 391 { 392 int rv = AE_OK; 393 dev_info_t *dip = NULL; 394 acpidev_data_handle_t objhdl; 395 char *compatibles[] = { 396 ACPIDEV_HID_ROOTNEX, 397 ACPIDEV_TYPE_ROOTNEX, 398 ACPIDEV_HID_VIRTNEX, 399 ACPIDEV_TYPE_VIRTNEX, 400 }; 401 402 ndi_devi_enter(ddi_root_node()); 403 ASSERT(acpidev_root_dip == NULL); 404 405 /* Query whether device node already exists. */ 406 dip = ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT, -1, 0); 407 if (dip != NULL && ddi_get_parent(dip) == ddi_root_node()) { 408 ndi_devi_exit(ddi_root_node()); 409 cmn_err(CE_WARN, "!acpidev: node /devices/%s already exists, " 410 "disable driver.", ACPIDEV_NODE_NAME_ROOT); 411 return (AE_ALREADY_EXISTS); 412 } 413 414 /* Create the device node if it doesn't exist. */ 415 rv = ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT, 416 (pnode_t)DEVI_SID_NODEID, &dip); 417 if (rv != NDI_SUCCESS) { 418 ndi_devi_exit(ddi_root_node()); 419 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create device node " 420 "for ACPI root with errcode %d.", rv); 421 return (AE_ERROR); 422 } 423 424 /* Build cross reference between dip and ACPI object. */ 425 if (ACPI_FAILURE(acpica_tag_devinfo(dip, ACPI_ROOT_OBJECT))) { 426 (void) ddi_remove_child(dip, 0); 427 ndi_devi_exit(ddi_root_node()); 428 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to tag object %s.", 429 ACPIDEV_OBJECT_NAME_SB); 430 return (AE_ERROR); 431 } 432 433 /* Set device properties. */ 434 rv = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, 435 OBP_COMPATIBLE, ACPIDEV_ARRAY_PARAM(compatibles)); 436 if (rv == NDI_SUCCESS) { 437 rv = ndi_prop_update_string(DDI_DEV_T_NONE, dip, 438 OBP_DEVICETYPE, ACPIDEV_TYPE_ROOTNEX); 439 } 440 if (rv != DDI_SUCCESS) { 441 ACPIDEV_DEBUG(CE_WARN, 442 "!acpidev: failed to set device property for /devices/%s.", 443 ACPIDEV_NODE_NAME_ROOT); 444 goto error_out; 445 } 446 447 /* Manually create an object handle for the root node */ 448 objhdl = acpidev_data_create_handle(ACPI_ROOT_OBJECT); 449 if (objhdl == NULL) { 450 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object " 451 "handle for the root node."); 452 goto error_out; 453 } 454 objhdl->aod_level = 0; 455 objhdl->aod_hdl = ACPI_ROOT_OBJECT; 456 objhdl->aod_dip = dip; 457 objhdl->aod_class = &acpidev_class_scope; 458 objhdl->aod_status = acpidev_query_device_status(ACPI_ROOT_OBJECT); 459 objhdl->aod_iflag = ACPIDEV_ODF_STATUS_VALID | 460 ACPIDEV_ODF_DEVINFO_CREATED | ACPIDEV_ODF_DEVINFO_TAGGED; 461 462 /* Bind device driver. */ 463 (void) ndi_devi_bind_driver(dip, 0); 464 465 acpidev_root_dip = dip; 466 ndi_devi_exit(ddi_root_node()); 467 468 return (AE_OK); 469 470 error_out: 471 (void) acpica_untag_devinfo(dip, ACPI_ROOT_OBJECT); 472 (void) ddi_remove_child(dip, 0); 473 ndi_devi_exit(ddi_root_node()); 474 return (AE_ERROR); 475 } 476 477 static void 478 acpidev_initialize(void) 479 { 480 int rc; 481 char *str = NULL; 482 uint64_t features = 0; 483 484 /* Check whether it has already been initialized. */ 485 if (acpidev_status == ACPIDEV_STATUS_DISABLED) { 486 cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " 487 "disabled by user.\n"); 488 return; 489 } else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) { 490 ACPIDEV_DEBUG(CE_NOTE, 491 "!acpidev: initialization called more than once."); 492 return; 493 } 494 495 /* Check whether ACPI device autoconfig has been disabled by user. */ 496 rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 497 DDI_PROP_DONTPASS, "acpidev-autoconfig", &str); 498 if (rc == DDI_SUCCESS) { 499 if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) { 500 cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " 501 "disabled by user.\n"); 502 ddi_prop_free(str); 503 acpidev_status = ACPIDEV_STATUS_DISABLED; 504 return; 505 } 506 ddi_prop_free(str); 507 } 508 509 /* Initialize acpica subsystem. */ 510 if (ACPI_FAILURE(acpica_init())) { 511 cmn_err(CE_WARN, 512 "!acpidev: failed to initialize acpica subsystem."); 513 acpidev_status = ACPIDEV_STATUS_FAILED; 514 return; 515 } 516 517 /* Check ACPICA subsystem status. */ 518 if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT)) { 519 cmn_err(CE_WARN, "!acpidev: ACPICA hasn't been fully " 520 "initialized, ACPI device autoconfig will be disabled."); 521 acpidev_status = ACPIDEV_STATUS_DISABLED; 522 return; 523 } 524 525 /* Converts acpidev-options from type string to int, if any */ 526 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 527 DDI_PROP_DONTPASS, "acpidev-options", &str) == DDI_PROP_SUCCESS) { 528 long data; 529 rc = ddi_strtol(str, NULL, 0, &data); 530 if (rc == 0) { 531 (void) e_ddi_prop_remove(DDI_DEV_T_NONE, 532 ddi_root_node(), "acpidev-options"); 533 (void) e_ddi_prop_update_int(DDI_DEV_T_NONE, 534 ddi_root_node(), "acpidev-options", data); 535 } 536 ddi_prop_free(str); 537 } 538 /* Get acpidev_options user options. */ 539 acpidev_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), 540 DDI_PROP_DONTPASS, "acpidev-options", acpidev_options); 541 542 /* Check whether ACPI based DR has been disabled by user. */ 543 rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 544 DDI_PROP_DONTPASS, "acpidev-dr", &str); 545 if (rc == DDI_SUCCESS) { 546 if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) { 547 cmn_err(CE_CONT, "?acpidev: ACPI based DR has been " 548 "disabled by user.\n"); 549 acpidev_dr_enable = 0; 550 } 551 ddi_prop_free(str); 552 } 553 554 /* Register all device class drivers. */ 555 if (ACPI_FAILURE(acpidev_class_list_init(&features))) { 556 cmn_err(CE_WARN, 557 "!acpidev: failed to initalize class driver lists."); 558 acpidev_status = ACPIDEV_STATUS_FAILED; 559 return; 560 } 561 562 /* Create root node for ACPI/firmware device subtree. */ 563 if (ACPI_FAILURE(acpidev_create_root_node())) { 564 cmn_err(CE_WARN, "!acpidev: failed to create root node " 565 "for acpi device tree."); 566 acpidev_class_list_fini(); 567 acpidev_status = ACPIDEV_STATUS_FAILED; 568 return; 569 } 570 571 /* Notify acpica to enable ACPI device auto configuration. */ 572 acpica_set_core_feature(ACPI_FEATURE_DEVCFG); 573 acpica_set_devcfg_feature(features); 574 575 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: ACPI device autoconfig initialized."); 576 acpidev_status = ACPIDEV_STATUS_INITIALIZED; 577 } 578 579 /* 580 * Probe devices in ACPI namespace which can't be enumerated by other methods 581 * at boot time. 582 */ 583 static ACPI_STATUS 584 acpidev_boot_probe_device(acpidev_op_type_t op_type) 585 { 586 ACPI_STATUS rc = AE_OK; 587 acpidev_walk_info_t *infop; 588 589 ASSERT(acpidev_root_dip != NULL); 590 ASSERT(op_type == ACPIDEV_OP_BOOT_PROBE || 591 op_type == ACPIDEV_OP_BOOT_REPROBE); 592 593 infop = acpidev_alloc_walk_info(op_type, 0, ACPI_ROOT_OBJECT, 594 &acpidev_class_list_root, NULL); 595 if (infop == NULL) { 596 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info " 597 "object in acpi_boot_probe_device()."); 598 return (AE_ERROR); 599 } 600 /* Enumerate ACPI devices. */ 601 rc = acpidev_probe_child(infop); 602 if (ACPI_FAILURE(rc)) { 603 cmn_err(CE_WARN, "!acpidev: failed to probe child object " 604 "under ACPI root node."); 605 } 606 acpidev_free_walk_info(infop); 607 608 return (rc); 609 } 610 611 /* 612 * Platform specific device prober for ACPI virtual bus. 613 * It will be called in single-threaded environment to enumerate devices in 614 * ACPI namespace at boot time. 615 */ 616 static void 617 acpidev_boot_probe(int type) 618 { 619 ACPI_STATUS rc; 620 621 /* Initialize subsystem on first pass. */ 622 mutex_enter(&acpidev_drv_lock); 623 if (type == 0) { 624 acpidev_initialize(); 625 if (acpidev_status != ACPIDEV_STATUS_INITIALIZED && 626 acpidev_status != ACPIDEV_STATUS_DISABLED) { 627 cmn_err(CE_WARN, "!acpidev: driver disabled due to " 628 "initalization failure."); 629 } 630 } 631 632 /* Probe ACPI devices */ 633 if (type == 0 && acpidev_status == ACPIDEV_STATUS_INITIALIZED) { 634 rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE); 635 if (ACPI_SUCCESS(rc)) { 636 /* 637 * Support of DR operations will be disabled 638 * if failed to initialize DR subsystem. 639 */ 640 rc = acpidev_dr_initialize(acpidev_root_dip); 641 if (ACPI_FAILURE(rc) && rc != AE_SUPPORT) { 642 cmn_err(CE_CONT, "?acpidev: failed to " 643 "initialize DR subsystem."); 644 } 645 acpidev_status = ACPIDEV_STATUS_FIRST_PASS; 646 } else { 647 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to probe ACPI " 648 "devices during boot."); 649 acpidev_status = ACPIDEV_STATUS_FAILED; 650 } 651 } else if (type != 0 && acpidev_status == ACPIDEV_STATUS_FIRST_PASS) { 652 rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE); 653 if (ACPI_SUCCESS(rc)) { 654 acpidev_status = ACPIDEV_STATUS_READY; 655 } else { 656 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to reprobe " 657 "ACPI devices during boot."); 658 acpidev_status = ACPIDEV_STATUS_FAILED; 659 } 660 } else if (acpidev_status != ACPIDEV_STATUS_FAILED && 661 acpidev_status != ACPIDEV_STATUS_DISABLED && 662 acpidev_status != ACPIDEV_STATUS_READY) { 663 cmn_err(CE_WARN, 664 "!acpidev: invalid ACPI device autoconfig global status."); 665 } 666 mutex_exit(&acpidev_drv_lock); 667 } 668 669 ACPI_STATUS 670 acpidev_probe_child(acpidev_walk_info_t *infop) 671 { 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); 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); 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