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