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
_init(void)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
_fini(void)134 _fini(void)
135 {
136 /* No support for module unload. */
137 return (EBUSY);
138 }
139
140 int
_info(struct modinfo * modinfop)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
acpidev_load_plat_modules(void)148 acpidev_load_plat_modules(void)
149 {
150 return (AE_OK);
151 }
152
153 /* Unload platform specific driver modules. */
154 static void
acpidev_unload_plat_modules(void)155 acpidev_unload_plat_modules(void)
156 {
157 }
158
159 /* Unregister all device class drivers from the device driver lists. */
160 static void
acpidev_class_list_fini(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
acpidev_class_list_init(uint64_t * fp)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
acpidev_create_root_node(void)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
acpidev_initialize(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
acpidev_boot_probe_device(acpidev_op_type_t op_type)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
acpidev_boot_probe(int type)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
acpidev_probe_child(acpidev_walk_info_t * infop)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
acpidev_process_object(acpidev_walk_info_t * infop,int flags)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
acpidev_filter_default(acpidev_walk_info_t * infop,ACPI_HANDLE hdl,acpidev_filter_rule_t * afrp,char * devname,int len)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
acpidev_filter_device(acpidev_walk_info_t * infop,ACPI_HANDLE hdl,acpidev_filter_rule_t * afrp,int entries,char * devname,int len)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 *
acpidev_root_node(void)1116 acpidev_root_node(void)
1117 {
1118 return (acpidev_root_dip);
1119 }
1120
1121 ACPI_STATUS
acpidev_register_class(acpidev_class_list_t ** listpp,acpidev_class_t * clsp,boolean_t tail)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
acpidev_unregister_class(acpidev_class_list_t ** listpp,acpidev_class_t * clsp)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