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