1b72d5b75SMichael Corcoran /*
2b72d5b75SMichael Corcoran * CDDL HEADER START
3b72d5b75SMichael Corcoran *
4b72d5b75SMichael Corcoran * The contents of this file are subject to the terms of the
5b72d5b75SMichael Corcoran * Common Development and Distribution License (the "License").
6b72d5b75SMichael Corcoran * You may not use this file except in compliance with the License.
7b72d5b75SMichael Corcoran *
8b72d5b75SMichael Corcoran * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b72d5b75SMichael Corcoran * or http://www.opensolaris.org/os/licensing.
10b72d5b75SMichael Corcoran * See the License for the specific language governing permissions
11b72d5b75SMichael Corcoran * and limitations under the License.
12b72d5b75SMichael Corcoran *
13b72d5b75SMichael Corcoran * When distributing Covered Code, include this CDDL HEADER in each
14b72d5b75SMichael Corcoran * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b72d5b75SMichael Corcoran * If applicable, add the following below this CDDL HEADER, with the
16b72d5b75SMichael Corcoran * fields enclosed by brackets "[]" replaced with your own identifying
17b72d5b75SMichael Corcoran * information: Portions Copyright [yyyy] [name of copyright owner]
18b72d5b75SMichael Corcoran *
19b72d5b75SMichael Corcoran * CDDL HEADER END
20b72d5b75SMichael Corcoran */
21b72d5b75SMichael Corcoran
22b72d5b75SMichael Corcoran /*
23b72d5b75SMichael Corcoran * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24b72d5b75SMichael Corcoran * Use is subject to license terms.
25*35786f68SRobert Mustacchi * Copyright (c) 2018, Joyent, Inc.
26b72d5b75SMichael Corcoran */
27b72d5b75SMichael Corcoran /*
28a3114836SGerry Liu * Copyright (c) 2009-2010, Intel Corporation.
29b72d5b75SMichael Corcoran * All rights reserved.
30b72d5b75SMichael Corcoran */
31b72d5b75SMichael Corcoran
32b72d5b75SMichael Corcoran #include <sys/types.h>
33b72d5b75SMichael Corcoran #include <sys/cmn_err.h>
34a3114836SGerry Liu #include <sys/note.h>
35b72d5b75SMichael Corcoran #include <sys/sysmacros.h>
36b72d5b75SMichael Corcoran #include <sys/sunddi.h>
37b72d5b75SMichael Corcoran #include <sys/sunndi.h>
38b72d5b75SMichael Corcoran #include <sys/acpi/acpi.h>
39b72d5b75SMichael Corcoran #include <sys/acpica.h>
40b72d5b75SMichael Corcoran #include <sys/acpidev.h>
41b72d5b75SMichael Corcoran #include <sys/acpidev_impl.h>
42b72d5b75SMichael Corcoran #include <util/sscanf.h>
43b72d5b75SMichael Corcoran
44b72d5b75SMichael Corcoran /* Data structures used to extract the numeric unit address from string _UID. */
45b72d5b75SMichael Corcoran static acpidev_pseudo_uid_head_t acpidev_uid_heads[ACPIDEV_CLASS_ID_MAX];
46b72d5b75SMichael Corcoran static char *acpidev_uid_formats[] = {
47b72d5b75SMichael Corcoran "%u",
48b72d5b75SMichael Corcoran };
49b72d5b75SMichael Corcoran
50b72d5b75SMichael Corcoran static char *acpidev_unknown_object_name = "<unknown>";
51b72d5b75SMichael Corcoran
52b72d5b75SMichael Corcoran int
acpidev_query_device_status(ACPI_HANDLE hdl)53b72d5b75SMichael Corcoran acpidev_query_device_status(ACPI_HANDLE hdl)
54b72d5b75SMichael Corcoran {
55b72d5b75SMichael Corcoran int status;
56b72d5b75SMichael Corcoran
57b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
58b72d5b75SMichael Corcoran if (hdl == NULL) {
59b72d5b75SMichael Corcoran ACPIDEV_DEBUG(CE_WARN,
60a3114836SGerry Liu "!acpidev: hdl is NULL in acpidev_query_device_status().");
61b72d5b75SMichael Corcoran return (0);
62b72d5b75SMichael Corcoran }
63b72d5b75SMichael Corcoran
64*35786f68SRobert Mustacchi if (ACPI_FAILURE(acpica_get_object_status(hdl, &status))) {
65b72d5b75SMichael Corcoran /*
66*35786f68SRobert Mustacchi * When the object status is not present, it will generally be
67*35786f68SRobert Mustacchi * set to the default value as per the ACPI specification (6.3.7
68*35786f68SRobert Mustacchi * _STA (Status)). However, there are other possible cases of
69*35786f68SRobert Mustacchi * ACPI failures. As this code is not aware of them and has
70*35786f68SRobert Mustacchi * always treated all failures like the not-present set. Do the
71*35786f68SRobert Mustacchi * same for the time being.
72b72d5b75SMichael Corcoran */
73b72d5b75SMichael Corcoran status = 0xF;
74b72d5b75SMichael Corcoran }
75b72d5b75SMichael Corcoran
76b72d5b75SMichael Corcoran return (status);
77b72d5b75SMichael Corcoran }
78b72d5b75SMichael Corcoran
79b72d5b75SMichael Corcoran boolean_t
acpidev_check_device_present(int status)80b72d5b75SMichael Corcoran acpidev_check_device_present(int status)
81b72d5b75SMichael Corcoran {
82b72d5b75SMichael Corcoran /*
83b72d5b75SMichael Corcoran * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit
84b72d5b75SMichael Corcoran * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists.
85b72d5b75SMichael Corcoran */
86b72d5b75SMichael Corcoran if (status & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) {
87b72d5b75SMichael Corcoran return (B_TRUE);
88b72d5b75SMichael Corcoran }
89b72d5b75SMichael Corcoran
90b72d5b75SMichael Corcoran return (B_FALSE);
91b72d5b75SMichael Corcoran }
92b72d5b75SMichael Corcoran
93b72d5b75SMichael Corcoran boolean_t
acpidev_check_device_enabled(int stat)94b72d5b75SMichael Corcoran acpidev_check_device_enabled(int stat)
95b72d5b75SMichael Corcoran {
96b72d5b75SMichael Corcoran /*
97b72d5b75SMichael Corcoran * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit
98b72d5b75SMichael Corcoran * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists.
99b72d5b75SMichael Corcoran * Return true if device exists and has been enabled.
100b72d5b75SMichael Corcoran */
101b72d5b75SMichael Corcoran if ((stat & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) &&
102b72d5b75SMichael Corcoran (stat & ACPI_STA_DEVICE_ENABLED)) {
103b72d5b75SMichael Corcoran return (B_TRUE);
104b72d5b75SMichael Corcoran }
105b72d5b75SMichael Corcoran
106b72d5b75SMichael Corcoran return (B_FALSE);
107b72d5b75SMichael Corcoran }
108b72d5b75SMichael Corcoran
109b72d5b75SMichael Corcoran boolean_t
acpidev_match_device_id(ACPI_DEVICE_INFO * infop,char ** ids,int count)110b72d5b75SMichael Corcoran acpidev_match_device_id(ACPI_DEVICE_INFO *infop, char **ids, int count)
111b72d5b75SMichael Corcoran {
112b72d5b75SMichael Corcoran int i, j;
113b72d5b75SMichael Corcoran
114b72d5b75SMichael Corcoran ASSERT(infop != NULL);
115b72d5b75SMichael Corcoran ASSERT(ids != NULL || count == 0);
116b72d5b75SMichael Corcoran /* Special case to match all devices if count is 0. */
117b72d5b75SMichael Corcoran if (count == 0) {
118b72d5b75SMichael Corcoran return (B_TRUE);
119b72d5b75SMichael Corcoran } else if (infop == NULL || ids == NULL) {
120a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters in "
121b72d5b75SMichael Corcoran "acpidev_match_device_id().");
122b72d5b75SMichael Corcoran return (B_FALSE);
123b72d5b75SMichael Corcoran }
124b72d5b75SMichael Corcoran
125b72d5b75SMichael Corcoran /* Match _HID first. */
126b72d5b75SMichael Corcoran if (infop->Valid & ACPI_VALID_HID) {
127b72d5b75SMichael Corcoran for (i = 0; i < count; i++) {
12857190917SDana Myers if (strncmp(ids[i], infop->HardwareId.String,
12957190917SDana Myers infop->HardwareId.Length) == 0) {
130b72d5b75SMichael Corcoran return (B_TRUE);
131b72d5b75SMichael Corcoran }
132b72d5b75SMichael Corcoran }
133b72d5b75SMichael Corcoran }
134b72d5b75SMichael Corcoran
135b72d5b75SMichael Corcoran /* Match _CID next. */
136b72d5b75SMichael Corcoran if (infop->Valid & ACPI_VALID_CID) {
137b72d5b75SMichael Corcoran for (i = 0; i < count; i++) {
13857190917SDana Myers for (j = 0; j < infop->CompatibleIdList.Count; j++) {
139b72d5b75SMichael Corcoran if (strncmp(ids[i],
14057190917SDana Myers infop->CompatibleIdList.Ids[j].String,
14157190917SDana Myers infop->CompatibleIdList.Ids[j].Length)
14257190917SDana Myers == 0) {
143b72d5b75SMichael Corcoran return (B_TRUE);
144b72d5b75SMichael Corcoran }
145b72d5b75SMichael Corcoran }
146b72d5b75SMichael Corcoran }
147b72d5b75SMichael Corcoran }
148b72d5b75SMichael Corcoran
149b72d5b75SMichael Corcoran return (B_FALSE);
150b72d5b75SMichael Corcoran }
151b72d5b75SMichael Corcoran
152b72d5b75SMichael Corcoran struct acpidev_get_device_arg {
153b72d5b75SMichael Corcoran boolean_t skip_non_exist;
154b72d5b75SMichael Corcoran int id_count;
155b72d5b75SMichael Corcoran char **device_ids;
156b72d5b75SMichael Corcoran void *user_arg;
157b72d5b75SMichael Corcoran ACPI_WALK_CALLBACK user_func;
158b72d5b75SMichael Corcoran };
159b72d5b75SMichael Corcoran
160b72d5b75SMichael Corcoran static ACPI_STATUS
acpidev_get_device_callback(ACPI_HANDLE hdl,UINT32 level,void * arg,void ** retval)161b72d5b75SMichael Corcoran acpidev_get_device_callback(ACPI_HANDLE hdl, UINT32 level, void *arg,
162b72d5b75SMichael Corcoran void **retval)
163b72d5b75SMichael Corcoran {
164b72d5b75SMichael Corcoran ACPI_STATUS rc;
165b72d5b75SMichael Corcoran ACPI_DEVICE_INFO *infop;
166b72d5b75SMichael Corcoran struct acpidev_get_device_arg *argp;
167*35786f68SRobert Mustacchi int status;
168b72d5b75SMichael Corcoran
169b72d5b75SMichael Corcoran argp = (struct acpidev_get_device_arg *)arg;
170b72d5b75SMichael Corcoran ASSERT(argp != NULL);
171b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
172b72d5b75SMichael Corcoran
173b72d5b75SMichael Corcoran /* Query object information. */
17457190917SDana Myers rc = AcpiGetObjectInfo(hdl, &infop);
175b72d5b75SMichael Corcoran if (ACPI_FAILURE(rc)) {
176b72d5b75SMichael Corcoran cmn_err(CE_WARN, "!acpidev: failed to get ACPI object info "
177b72d5b75SMichael Corcoran "in acpidev_get_device_callback().");
178b72d5b75SMichael Corcoran return (AE_CTRL_DEPTH);
179b72d5b75SMichael Corcoran }
180b72d5b75SMichael Corcoran
181*35786f68SRobert Mustacchi rc = acpica_get_object_status(hdl, &status);
182*35786f68SRobert Mustacchi
183b72d5b75SMichael Corcoran /*
184b72d5b75SMichael Corcoran * Skip scanning of children if the device is neither PRESENT nor
185b72d5b75SMichael Corcoran * FUNCTIONING.
186b72d5b75SMichael Corcoran * Please refer to ACPI Spec3.0b Sec 6.3.1 and 6.5.1.
187b72d5b75SMichael Corcoran */
188*35786f68SRobert Mustacchi if (argp->skip_non_exist && rc == AE_OK &&
189*35786f68SRobert Mustacchi !acpidev_check_device_present(status)) {
190b72d5b75SMichael Corcoran rc = AE_CTRL_DEPTH;
191b72d5b75SMichael Corcoran /* Call user callback if matched. */
192b72d5b75SMichael Corcoran } else if (acpidev_match_device_id(infop, argp->device_ids,
193b72d5b75SMichael Corcoran argp->id_count)) {
194b72d5b75SMichael Corcoran rc = argp->user_func(hdl, level, argp->user_arg, retval);
195b72d5b75SMichael Corcoran } else {
196b72d5b75SMichael Corcoran rc = AE_OK;
197b72d5b75SMichael Corcoran }
198b72d5b75SMichael Corcoran
199b72d5b75SMichael Corcoran /* Free ACPI object info buffer. */
200b72d5b75SMichael Corcoran AcpiOsFree(infop);
201b72d5b75SMichael Corcoran
202b72d5b75SMichael Corcoran return (rc);
203b72d5b75SMichael Corcoran }
204b72d5b75SMichael Corcoran
205b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_get_device_by_id(ACPI_HANDLE hdl,char ** ids,int count,int maxdepth,boolean_t skip_non_exist,ACPI_WALK_CALLBACK userfunc,void * userarg,void ** retval)206b72d5b75SMichael Corcoran acpidev_get_device_by_id(ACPI_HANDLE hdl, char **ids, int count,
207b72d5b75SMichael Corcoran int maxdepth, boolean_t skip_non_exist,
208b72d5b75SMichael Corcoran ACPI_WALK_CALLBACK userfunc, void *userarg, void **retval)
209b72d5b75SMichael Corcoran {
210b72d5b75SMichael Corcoran ACPI_STATUS rc;
211b72d5b75SMichael Corcoran struct acpidev_get_device_arg arg;
212b72d5b75SMichael Corcoran
213b72d5b75SMichael Corcoran ASSERT(userfunc != NULL);
214b72d5b75SMichael Corcoran if (hdl == NULL || userfunc == NULL || (ids == NULL && count != 0)) {
215a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters "
216b72d5b75SMichael Corcoran "in acpidev_get_device_by_id().");
217b72d5b75SMichael Corcoran return (AE_BAD_PARAMETER);
218b72d5b75SMichael Corcoran }
219b72d5b75SMichael Corcoran
220b72d5b75SMichael Corcoran /* Enumerate all descendant objects. */
221b72d5b75SMichael Corcoran arg.skip_non_exist = skip_non_exist;
222b72d5b75SMichael Corcoran arg.device_ids = ids;
223b72d5b75SMichael Corcoran arg.id_count = count;
224b72d5b75SMichael Corcoran arg.user_arg = userarg;
225b72d5b75SMichael Corcoran arg.user_func = userfunc;
226b72d5b75SMichael Corcoran rc = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, maxdepth,
22757190917SDana Myers &acpidev_get_device_callback, NULL, &arg, retval);
228b72d5b75SMichael Corcoran
229b72d5b75SMichael Corcoran return (rc);
230b72d5b75SMichael Corcoran }
231b72d5b75SMichael Corcoran
232b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_walk_apic(ACPI_BUFFER * bufp,ACPI_HANDLE hdl,char * method,acpidev_apic_walker_t func,void * context)233b72d5b75SMichael Corcoran acpidev_walk_apic(ACPI_BUFFER *bufp, ACPI_HANDLE hdl, char *method,
234b72d5b75SMichael Corcoran acpidev_apic_walker_t func, void *context)
235b72d5b75SMichael Corcoran {
236b72d5b75SMichael Corcoran ACPI_STATUS rc;
237b72d5b75SMichael Corcoran ssize_t len;
238b72d5b75SMichael Corcoran ACPI_BUFFER buf;
2393b17bf65SMichael Corcoran ACPI_OBJECT *obj;
240b72d5b75SMichael Corcoran ACPI_SUBTABLE_HEADER *ap;
241b72d5b75SMichael Corcoran ACPI_TABLE_MADT *mp = NULL;
242b72d5b75SMichael Corcoran
243b72d5b75SMichael Corcoran ASSERT(func != NULL);
244b72d5b75SMichael Corcoran if (func == NULL) {
245b72d5b75SMichael Corcoran ACPIDEV_DEBUG(CE_WARN,
246a3114836SGerry Liu "!acpidev: invalid parameters for acpidev_walk_apic().");
247b72d5b75SMichael Corcoran return (AE_BAD_PARAMETER);
248b72d5b75SMichael Corcoran }
249b72d5b75SMichael Corcoran
250b72d5b75SMichael Corcoran buf.Pointer = NULL;
251b72d5b75SMichael Corcoran buf.Length = ACPI_ALLOCATE_BUFFER;
252b72d5b75SMichael Corcoran
253b72d5b75SMichael Corcoran /* A walk buffer was passed in if bufp isn't NULL. */
254b72d5b75SMichael Corcoran if (bufp != NULL) {
255b72d5b75SMichael Corcoran ap = (ACPI_SUBTABLE_HEADER *)(bufp->Pointer);
256b72d5b75SMichael Corcoran len = bufp->Length;
257b72d5b75SMichael Corcoran } else if (method != NULL) {
258b72d5b75SMichael Corcoran /*
259b72d5b75SMichael Corcoran * Otherwise, if we have an evaluate method, we get the walk
2603b17bf65SMichael Corcoran * buffer from a successful invocation of
2613b17bf65SMichael Corcoran * AcpiEvaluateObjectTyped().
262b72d5b75SMichael Corcoran */
263b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
2643b17bf65SMichael Corcoran rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf,
2653b17bf65SMichael Corcoran ACPI_TYPE_BUFFER);
2663b17bf65SMichael Corcoran if (ACPI_SUCCESS(rc)) {
2673b17bf65SMichael Corcoran ASSERT(buf.Length >= sizeof (*obj));
2683b17bf65SMichael Corcoran obj = buf.Pointer;
2693b17bf65SMichael Corcoran ap = (ACPI_SUBTABLE_HEADER *)obj->Buffer.Pointer;
2703b17bf65SMichael Corcoran len = obj->Buffer.Length;
2713b17bf65SMichael Corcoran } else {
2723b17bf65SMichael Corcoran if (rc != AE_NOT_FOUND)
2733b17bf65SMichael Corcoran cmn_err(CE_WARN, "!acpidev: failed to evaluate "
2743b17bf65SMichael Corcoran "%s in acpidev_walk_apic().", method);
275b72d5b75SMichael Corcoran return (rc);
276b72d5b75SMichael Corcoran }
277b72d5b75SMichael Corcoran } else {
278b72d5b75SMichael Corcoran /* As a last resort, walk the MADT table. */
279b72d5b75SMichael Corcoran rc = AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **)&mp);
280b72d5b75SMichael Corcoran if (ACPI_FAILURE(rc)) {
281b72d5b75SMichael Corcoran cmn_err(CE_WARN, "!acpidev: failed to get MADT table "
282b72d5b75SMichael Corcoran "in acpidev_walk_apic().");
283b72d5b75SMichael Corcoran return (rc);
284b72d5b75SMichael Corcoran }
285b72d5b75SMichael Corcoran ap = (ACPI_SUBTABLE_HEADER *)(mp + 1);
286b72d5b75SMichael Corcoran len = mp->Header.Length - sizeof (*mp);
287b72d5b75SMichael Corcoran }
288b72d5b75SMichael Corcoran
289b72d5b75SMichael Corcoran ASSERT(len >= 0);
290b72d5b75SMichael Corcoran for (rc = AE_OK; len > 0 && ACPI_SUCCESS(rc); len -= ap->Length,
291b72d5b75SMichael Corcoran ap = (ACPI_SUBTABLE_HEADER *)(((char *)ap) + ap->Length)) {
292b72d5b75SMichael Corcoran ASSERT(len >= sizeof (ACPI_SUBTABLE_HEADER));
293b72d5b75SMichael Corcoran if (len <= sizeof (ACPI_SUBTABLE_HEADER) ||
294b72d5b75SMichael Corcoran ap->Length <= sizeof (ACPI_SUBTABLE_HEADER) ||
295b72d5b75SMichael Corcoran len < ap->Length) {
296b72d5b75SMichael Corcoran cmn_err(CE_WARN,
297b72d5b75SMichael Corcoran "!acpidev: invalid APIC entry in MADT/_MAT.");
298b72d5b75SMichael Corcoran break;
299b72d5b75SMichael Corcoran }
300b72d5b75SMichael Corcoran rc = (*func)(ap, context);
301b72d5b75SMichael Corcoran }
302b72d5b75SMichael Corcoran
303b72d5b75SMichael Corcoran if (buf.Pointer != NULL) {
304b72d5b75SMichael Corcoran AcpiOsFree(buf.Pointer);
305b72d5b75SMichael Corcoran }
306b72d5b75SMichael Corcoran
307b72d5b75SMichael Corcoran return (rc);
308b72d5b75SMichael Corcoran }
309b72d5b75SMichael Corcoran
310b72d5b75SMichael Corcoran char *
acpidev_get_object_name(ACPI_HANDLE hdl)311b72d5b75SMichael Corcoran acpidev_get_object_name(ACPI_HANDLE hdl)
312b72d5b75SMichael Corcoran {
313b72d5b75SMichael Corcoran ACPI_BUFFER buf;
314b72d5b75SMichael Corcoran char *objname = acpidev_unknown_object_name;
315b72d5b75SMichael Corcoran
316b72d5b75SMichael Corcoran buf.Length = ACPI_ALLOCATE_BUFFER;
317b72d5b75SMichael Corcoran buf.Pointer = NULL;
318b72d5b75SMichael Corcoran if (ACPI_SUCCESS(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) {
319b72d5b75SMichael Corcoran ASSERT(buf.Pointer != NULL);
320b72d5b75SMichael Corcoran objname = (char *)buf.Pointer;
321b72d5b75SMichael Corcoran }
322b72d5b75SMichael Corcoran
323b72d5b75SMichael Corcoran return (objname);
324b72d5b75SMichael Corcoran }
325b72d5b75SMichael Corcoran
326b72d5b75SMichael Corcoran void
acpidev_free_object_name(char * objname)327b72d5b75SMichael Corcoran acpidev_free_object_name(char *objname)
328b72d5b75SMichael Corcoran {
329b72d5b75SMichael Corcoran if (objname != acpidev_unknown_object_name && objname != NULL) {
330b72d5b75SMichael Corcoran AcpiOsFree(objname);
331b72d5b75SMichael Corcoran }
332b72d5b75SMichael Corcoran }
333b72d5b75SMichael Corcoran
334b72d5b75SMichael Corcoran acpidev_walk_info_t *
acpidev_alloc_walk_info(acpidev_op_type_t op_type,int lvl,ACPI_HANDLE hdl,acpidev_class_list_t ** listpp,acpidev_walk_info_t * pinfop)335b72d5b75SMichael Corcoran acpidev_alloc_walk_info(acpidev_op_type_t op_type, int lvl, ACPI_HANDLE hdl,
336b72d5b75SMichael Corcoran acpidev_class_list_t **listpp, acpidev_walk_info_t *pinfop)
337b72d5b75SMichael Corcoran {
338b72d5b75SMichael Corcoran acpidev_walk_info_t *infop = NULL;
339b72d5b75SMichael Corcoran acpidev_data_handle_t datap = NULL;
340b72d5b75SMichael Corcoran
341b72d5b75SMichael Corcoran ASSERT(0 <= lvl && lvl < ACPIDEV_MAX_ENUM_LEVELS);
342b72d5b75SMichael Corcoran infop = kmem_zalloc(sizeof (*infop), KM_SLEEP);
343b72d5b75SMichael Corcoran infop->awi_op_type = op_type;
344b72d5b75SMichael Corcoran infop->awi_level = lvl;
345b72d5b75SMichael Corcoran infop->awi_parent = pinfop;
346b72d5b75SMichael Corcoran infop->awi_class_list = listpp;
347b72d5b75SMichael Corcoran infop->awi_hdl = hdl;
348b72d5b75SMichael Corcoran infop->awi_name = acpidev_get_object_name(hdl);
349b72d5b75SMichael Corcoran
350b72d5b75SMichael Corcoran /* Cache ACPI device information. */
35157190917SDana Myers if (ACPI_FAILURE(AcpiGetObjectInfo(hdl, &infop->awi_info))) {
352b72d5b75SMichael Corcoran cmn_err(CE_WARN, "!acpidev: failed to get object info for %s "
353b72d5b75SMichael Corcoran "in acpidev_alloc_walk_info().", infop->awi_name);
354b72d5b75SMichael Corcoran acpidev_free_object_name(infop->awi_name);
355b72d5b75SMichael Corcoran kmem_free(infop, sizeof (*infop));
356b72d5b75SMichael Corcoran return (NULL);
357b72d5b75SMichael Corcoran }
358b72d5b75SMichael Corcoran
359b72d5b75SMichael Corcoran /*
360b72d5b75SMichael Corcoran * Get or create an ACPI object data handle, which will be used to
361b72d5b75SMichael Corcoran * maintain object status information.
362b72d5b75SMichael Corcoran */
363b72d5b75SMichael Corcoran if ((datap = acpidev_data_get_handle(hdl)) != NULL) {
364b72d5b75SMichael Corcoran ASSERT(datap->aod_hdl == hdl);
365b72d5b75SMichael Corcoran ASSERT(datap->aod_level == lvl);
366b72d5b75SMichael Corcoran } else if ((datap = acpidev_data_create_handle(hdl)) != NULL) {
367b72d5b75SMichael Corcoran datap->aod_level = lvl;
368b72d5b75SMichael Corcoran datap->aod_hdl = hdl;
369b72d5b75SMichael Corcoran } else {
370a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object "
371b72d5b75SMichael Corcoran "handle for %s in acpidev_alloc_walk_info().",
372b72d5b75SMichael Corcoran infop->awi_name);
373b72d5b75SMichael Corcoran AcpiOsFree(infop->awi_info);
374b72d5b75SMichael Corcoran acpidev_free_object_name(infop->awi_name);
375b72d5b75SMichael Corcoran kmem_free(infop, sizeof (*infop));
376b72d5b75SMichael Corcoran return (NULL);
377b72d5b75SMichael Corcoran }
378b72d5b75SMichael Corcoran infop->awi_data = datap;
379b72d5b75SMichael Corcoran /* Sync DEVICE_CREATED flag. */
380b72d5b75SMichael Corcoran if (datap->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) {
381b72d5b75SMichael Corcoran ASSERT(datap->aod_dip != NULL);
382b72d5b75SMichael Corcoran ASSERT(datap->aod_class != NULL);
383b72d5b75SMichael Corcoran infop->awi_dip = datap->aod_dip;
384b72d5b75SMichael Corcoran infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED;
385b72d5b75SMichael Corcoran }
386b72d5b75SMichael Corcoran
387b72d5b75SMichael Corcoran return (infop);
388b72d5b75SMichael Corcoran }
389b72d5b75SMichael Corcoran
390b72d5b75SMichael Corcoran void
acpidev_free_walk_info(acpidev_walk_info_t * infop)391b72d5b75SMichael Corcoran acpidev_free_walk_info(acpidev_walk_info_t *infop)
392b72d5b75SMichael Corcoran {
393b72d5b75SMichael Corcoran /*
394b72d5b75SMichael Corcoran * The ACPI object data handle will only be released when the
395b72d5b75SMichael Corcoran * corresponding object is going to be destroyed.
396b72d5b75SMichael Corcoran */
397b72d5b75SMichael Corcoran if (infop != NULL) {
398b72d5b75SMichael Corcoran if (infop->awi_info != NULL) {
399b72d5b75SMichael Corcoran AcpiOsFree(infop->awi_info);
400b72d5b75SMichael Corcoran }
401b72d5b75SMichael Corcoran if (infop->awi_name != NULL) {
402b72d5b75SMichael Corcoran acpidev_free_object_name(infop->awi_name);
403b72d5b75SMichael Corcoran }
404b72d5b75SMichael Corcoran kmem_free(infop, sizeof (*infop));
405b72d5b75SMichael Corcoran }
406b72d5b75SMichael Corcoran }
407b72d5b75SMichael Corcoran
408b72d5b75SMichael Corcoran dev_info_t *
acpidev_walk_info_get_pdip(acpidev_walk_info_t * infop)409b72d5b75SMichael Corcoran acpidev_walk_info_get_pdip(acpidev_walk_info_t *infop)
410b72d5b75SMichael Corcoran {
411b72d5b75SMichael Corcoran while (infop != NULL) {
412b72d5b75SMichael Corcoran if (infop->awi_dip != NULL) {
413b72d5b75SMichael Corcoran return (infop->awi_dip);
414b72d5b75SMichael Corcoran }
415b72d5b75SMichael Corcoran infop = infop->awi_parent;
416b72d5b75SMichael Corcoran }
417b72d5b75SMichael Corcoran
418b72d5b75SMichael Corcoran return (NULL);
419b72d5b75SMichael Corcoran }
420b72d5b75SMichael Corcoran
421b72d5b75SMichael Corcoran /*
422b72d5b75SMichael Corcoran * Called to release resources when the corresponding object is going
423b72d5b75SMichael Corcoran * to be destroyed.
424b72d5b75SMichael Corcoran */
425b72d5b75SMichael Corcoran static void
acpidev_free_object_handler(ACPI_HANDLE hdl,void * data)426a3114836SGerry Liu acpidev_free_object_handler(ACPI_HANDLE hdl, void *data)
427b72d5b75SMichael Corcoran {
428a3114836SGerry Liu _NOTE(ARGUNUSED(hdl));
429a3114836SGerry Liu
430b72d5b75SMichael Corcoran acpidev_data_handle_t objhdl = data;
431b72d5b75SMichael Corcoran
432a3114836SGerry Liu if (objhdl->aod_class != NULL) {
433a3114836SGerry Liu atomic_dec_32(&objhdl->aod_class->adc_refcnt);
434a3114836SGerry Liu objhdl->aod_class = NULL;
435a3114836SGerry Liu }
436b72d5b75SMichael Corcoran kmem_free(objhdl, sizeof (acpidev_data_handle_t));
437b72d5b75SMichael Corcoran }
438b72d5b75SMichael Corcoran
439b72d5b75SMichael Corcoran acpidev_data_handle_t
acpidev_data_get_handle(ACPI_HANDLE hdl)440b72d5b75SMichael Corcoran acpidev_data_get_handle(ACPI_HANDLE hdl)
441b72d5b75SMichael Corcoran {
442b72d5b75SMichael Corcoran void *ptr;
443b72d5b75SMichael Corcoran acpidev_data_handle_t objhdl = NULL;
444b72d5b75SMichael Corcoran
445a3114836SGerry Liu if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_free_object_handler, &ptr))) {
446b72d5b75SMichael Corcoran objhdl = (acpidev_data_handle_t)ptr;
447b72d5b75SMichael Corcoran }
448b72d5b75SMichael Corcoran
449b72d5b75SMichael Corcoran return (objhdl);
450b72d5b75SMichael Corcoran }
451b72d5b75SMichael Corcoran
452b72d5b75SMichael Corcoran acpidev_data_handle_t
acpidev_data_create_handle(ACPI_HANDLE hdl)453b72d5b75SMichael Corcoran acpidev_data_create_handle(ACPI_HANDLE hdl)
454b72d5b75SMichael Corcoran {
455b72d5b75SMichael Corcoran acpidev_data_handle_t objhdl;
456b72d5b75SMichael Corcoran
457b72d5b75SMichael Corcoran objhdl = kmem_zalloc(sizeof (*objhdl), KM_SLEEP);
458a3114836SGerry Liu objhdl->aod_bdtype = ACPIDEV_INVALID_BOARD;
459a3114836SGerry Liu objhdl->aod_bdnum = UINT32_MAX;
460a3114836SGerry Liu objhdl->aod_portid = UINT32_MAX;
461a3114836SGerry Liu objhdl->aod_class_id = ACPIDEV_CLASS_ID_INVALID;
462a3114836SGerry Liu
463a3114836SGerry Liu if (ACPI_FAILURE(AcpiAttachData(hdl, acpidev_free_object_handler,
464b72d5b75SMichael Corcoran (void *)objhdl))) {
465b72d5b75SMichael Corcoran cmn_err(CE_WARN,
466b72d5b75SMichael Corcoran "!acpidev: failed to attach handle data to object.");
467b72d5b75SMichael Corcoran kmem_free(objhdl, sizeof (*objhdl));
468b72d5b75SMichael Corcoran return (NULL);
469b72d5b75SMichael Corcoran }
470b72d5b75SMichael Corcoran
471b72d5b75SMichael Corcoran return (objhdl);
472b72d5b75SMichael Corcoran }
473b72d5b75SMichael Corcoran
474b72d5b75SMichael Corcoran void
acpidev_data_destroy_handle(ACPI_HANDLE hdl)475b72d5b75SMichael Corcoran acpidev_data_destroy_handle(ACPI_HANDLE hdl)
476b72d5b75SMichael Corcoran {
477b72d5b75SMichael Corcoran void *ptr;
478a3114836SGerry Liu acpidev_data_handle_t objhdl = NULL;
479b72d5b75SMichael Corcoran
480a3114836SGerry Liu if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_free_object_handler, &ptr)) &&
481a3114836SGerry Liu ACPI_SUCCESS(AcpiDetachData(hdl, acpidev_free_object_handler))) {
482a3114836SGerry Liu objhdl = ptr;
483a3114836SGerry Liu if (objhdl->aod_class != NULL) {
484a3114836SGerry Liu atomic_dec_32(&objhdl->aod_class->adc_refcnt);
485a3114836SGerry Liu objhdl->aod_class = NULL;
486a3114836SGerry Liu }
487b72d5b75SMichael Corcoran kmem_free(ptr, sizeof (acpidev_data_handle_t));
488b72d5b75SMichael Corcoran }
489b72d5b75SMichael Corcoran }
490b72d5b75SMichael Corcoran
491b72d5b75SMichael Corcoran ACPI_HANDLE
acpidev_data_get_object(acpidev_data_handle_t hdl)492b72d5b75SMichael Corcoran acpidev_data_get_object(acpidev_data_handle_t hdl)
493b72d5b75SMichael Corcoran {
494b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
495b72d5b75SMichael Corcoran return ((hdl != NULL) ? hdl->aod_hdl : NULL);
496b72d5b75SMichael Corcoran }
497b72d5b75SMichael Corcoran
498b72d5b75SMichael Corcoran dev_info_t *
acpidev_data_get_devinfo(acpidev_data_handle_t hdl)499b72d5b75SMichael Corcoran acpidev_data_get_devinfo(acpidev_data_handle_t hdl)
500b72d5b75SMichael Corcoran {
501b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
502b72d5b75SMichael Corcoran if (hdl == NULL ||
503b72d5b75SMichael Corcoran (hdl->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) == 0) {
504b72d5b75SMichael Corcoran return (NULL);
505b72d5b75SMichael Corcoran } else {
506b72d5b75SMichael Corcoran ASSERT(hdl->aod_dip != NULL);
507b72d5b75SMichael Corcoran return (hdl->aod_dip);
508b72d5b75SMichael Corcoran }
509b72d5b75SMichael Corcoran }
510b72d5b75SMichael Corcoran
511b72d5b75SMichael Corcoran int
acpidev_data_get_status(acpidev_data_handle_t hdl)512b72d5b75SMichael Corcoran acpidev_data_get_status(acpidev_data_handle_t hdl)
513b72d5b75SMichael Corcoran {
514b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
515b72d5b75SMichael Corcoran if (hdl == NULL ||
516b72d5b75SMichael Corcoran (hdl->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0) {
517b72d5b75SMichael Corcoran return (0);
518b72d5b75SMichael Corcoran } else {
519b72d5b75SMichael Corcoran return (hdl->aod_status);
520b72d5b75SMichael Corcoran }
521b72d5b75SMichael Corcoran }
522b72d5b75SMichael Corcoran
523b72d5b75SMichael Corcoran void
acpidev_data_set_flag(acpidev_data_handle_t hdl,uint32_t flag)524b72d5b75SMichael Corcoran acpidev_data_set_flag(acpidev_data_handle_t hdl, uint32_t flag)
525b72d5b75SMichael Corcoran {
526b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
527a3114836SGerry Liu atomic_or_32(&hdl->aod_eflag, flag);
528b72d5b75SMichael Corcoran }
529b72d5b75SMichael Corcoran
530b72d5b75SMichael Corcoran void
acpidev_data_clear_flag(acpidev_data_handle_t hdl,uint32_t flag)531b72d5b75SMichael Corcoran acpidev_data_clear_flag(acpidev_data_handle_t hdl, uint32_t flag)
532b72d5b75SMichael Corcoran {
533b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
534a3114836SGerry Liu atomic_and_32(&hdl->aod_eflag, ~flag);
535b72d5b75SMichael Corcoran }
536b72d5b75SMichael Corcoran
537b72d5b75SMichael Corcoran uint32_t
acpidev_data_get_flag(acpidev_data_handle_t hdl,uint32_t flag)538b72d5b75SMichael Corcoran acpidev_data_get_flag(acpidev_data_handle_t hdl, uint32_t flag)
539b72d5b75SMichael Corcoran {
540b72d5b75SMichael Corcoran ASSERT(hdl != NULL);
541b72d5b75SMichael Corcoran return (hdl->aod_eflag & flag);
542b72d5b75SMichael Corcoran }
543b72d5b75SMichael Corcoran
544a3114836SGerry Liu boolean_t
acpidev_data_dr_capable(acpidev_data_handle_t hdl)545a3114836SGerry Liu acpidev_data_dr_capable(acpidev_data_handle_t hdl)
546a3114836SGerry Liu {
547a3114836SGerry Liu ASSERT(hdl != NULL);
548a3114836SGerry Liu return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_CAPABLE);
549a3114836SGerry Liu }
550a3114836SGerry Liu
551a3114836SGerry Liu boolean_t
acpidev_data_dr_ready(acpidev_data_handle_t hdl)552a3114836SGerry Liu acpidev_data_dr_ready(acpidev_data_handle_t hdl)
553a3114836SGerry Liu {
554a3114836SGerry Liu ASSERT(hdl != NULL);
555a3114836SGerry Liu return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_READY);
556a3114836SGerry Liu }
557a3114836SGerry Liu
558a3114836SGerry Liu boolean_t
acpidev_data_dr_failed(acpidev_data_handle_t hdl)559a3114836SGerry Liu acpidev_data_dr_failed(acpidev_data_handle_t hdl)
560a3114836SGerry Liu {
561a3114836SGerry Liu ASSERT(hdl != NULL);
562a3114836SGerry Liu return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_FAILED);
563a3114836SGerry Liu }
564a3114836SGerry Liu
565b72d5b75SMichael Corcoran static char *
acpidev_generate_pseudo_unitaddr(char * uid,acpidev_class_id_t cid,char * buf,size_t len)566b72d5b75SMichael Corcoran acpidev_generate_pseudo_unitaddr(char *uid, acpidev_class_id_t cid,
567b72d5b75SMichael Corcoran char *buf, size_t len)
568b72d5b75SMichael Corcoran {
569b72d5b75SMichael Corcoran acpidev_pseudo_uid_t *up, **pp;
570b72d5b75SMichael Corcoran
571b72d5b75SMichael Corcoran ASSERT(len >= 64);
572b72d5b75SMichael Corcoran ASSERT(cid >= 0 && cid < ACPIDEV_CLASS_ID_MAX);
573b72d5b75SMichael Corcoran if (cid < 0 || cid >= ACPIDEV_CLASS_ID_MAX) {
574b72d5b75SMichael Corcoran return (NULL);
575b72d5b75SMichael Corcoran }
576b72d5b75SMichael Corcoran
577b72d5b75SMichael Corcoran mutex_enter(&acpidev_uid_heads[cid].apuh_lock);
578b72d5b75SMichael Corcoran for (pp = &acpidev_uid_heads[cid].apuh_first; *pp != NULL;
579b72d5b75SMichael Corcoran pp = &(*pp)->apu_next) {
580b72d5b75SMichael Corcoran if (strcmp(uid, (*pp)->apu_uid) == 0 &&
581b72d5b75SMichael Corcoran (*pp)->apu_cid == cid) {
582b72d5b75SMichael Corcoran break;
583b72d5b75SMichael Corcoran }
584b72d5b75SMichael Corcoran }
585b72d5b75SMichael Corcoran /* uid doesn't exist, create one and insert it into the list. */
586b72d5b75SMichael Corcoran if (*pp == NULL) {
587b72d5b75SMichael Corcoran up = kmem_zalloc(sizeof (*up), KM_SLEEP);
588b72d5b75SMichael Corcoran up->apu_uid = ddi_strdup(uid, KM_SLEEP);
589b72d5b75SMichael Corcoran up->apu_cid = cid;
590b72d5b75SMichael Corcoran up->apu_nid = acpidev_uid_heads[cid].apuh_id++;
591b72d5b75SMichael Corcoran *pp = up;
592b72d5b75SMichael Corcoran }
593b72d5b75SMichael Corcoran ASSERT(*pp != NULL);
594b72d5b75SMichael Corcoran mutex_exit(&acpidev_uid_heads[cid].apuh_lock);
595b72d5b75SMichael Corcoran
596b72d5b75SMichael Corcoran /*
597b72d5b75SMichael Corcoran * Generate a special format unit address with three fields to
598b72d5b75SMichael Corcoran * guarantee uniqueness. Normal unit addresses for ACPI devices have
599b72d5b75SMichael Corcoran * either one or two fields.
600b72d5b75SMichael Corcoran */
601b72d5b75SMichael Corcoran if (snprintf(buf, len, "%u,%u,0", (*pp)->apu_nid, cid) > len) {
602b72d5b75SMichael Corcoran return (NULL);
603b72d5b75SMichael Corcoran }
604b72d5b75SMichael Corcoran
605b72d5b75SMichael Corcoran return (buf);
606b72d5b75SMichael Corcoran }
607b72d5b75SMichael Corcoran
608b72d5b75SMichael Corcoran static char *
acpidev_gen_unitaddr(char * uid,char * fmt,char * buf,size_t len)609b72d5b75SMichael Corcoran acpidev_gen_unitaddr(char *uid, char *fmt, char *buf, size_t len)
610b72d5b75SMichael Corcoran {
611b72d5b75SMichael Corcoran size_t i, cnt;
612b72d5b75SMichael Corcoran uint_t id1, id2;
613b72d5b75SMichael Corcoran
614b72d5b75SMichael Corcoran ASSERT(len >= 64);
615b72d5b75SMichael Corcoran if (fmt == NULL || strlen(fmt) == 0) {
616b72d5b75SMichael Corcoran return (NULL);
617b72d5b75SMichael Corcoran }
618b72d5b75SMichael Corcoran
619b72d5b75SMichael Corcoran /*
620b72d5b75SMichael Corcoran * Count '%' in format string to protect sscanf().
621b72d5b75SMichael Corcoran * Only support '%u' and '%x', and maximum 2 conversions.
622b72d5b75SMichael Corcoran */
623b72d5b75SMichael Corcoran for (cnt = 0, i = 0; fmt[i] != 0 && cnt <= 2; i++) {
624b72d5b75SMichael Corcoran if (fmt[i] != '%') {
625b72d5b75SMichael Corcoran continue;
626b72d5b75SMichael Corcoran } else if (fmt[i + 1] == 'u' || fmt[i + 1] == 'x') {
627b72d5b75SMichael Corcoran /* Skip next character. */
628b72d5b75SMichael Corcoran i++;
629b72d5b75SMichael Corcoran cnt++;
630b72d5b75SMichael Corcoran } else {
631b72d5b75SMichael Corcoran /* Invalid conversion, stop walking. */
632b72d5b75SMichael Corcoran cnt = SIZE_MAX;
633b72d5b75SMichael Corcoran }
634b72d5b75SMichael Corcoran }
635b72d5b75SMichael Corcoran if (cnt != 1 && cnt != 2) {
636b72d5b75SMichael Corcoran ACPIDEV_DEBUG(CE_WARN,
637a3114836SGerry Liu "!acpidev: invalid uid format string '%s'.", fmt);
638b72d5b75SMichael Corcoran return (NULL);
639b72d5b75SMichael Corcoran }
640b72d5b75SMichael Corcoran
641b72d5b75SMichael Corcoran /* Scan uid and generate unitaddr. */
642b72d5b75SMichael Corcoran if (sscanf(uid, fmt, &id1, &id2) != cnt) {
643b72d5b75SMichael Corcoran return (NULL);
644b72d5b75SMichael Corcoran }
645b72d5b75SMichael Corcoran /*
646b72d5b75SMichael Corcoran * Reverse the order of the two IDs to match the requirements of the
647b72d5b75SMichael Corcoran * hotplug driver.
648b72d5b75SMichael Corcoran */
649b72d5b75SMichael Corcoran if (cnt == 2 && snprintf(buf, len, "%u,%u", id2, id1) >= len) {
650b72d5b75SMichael Corcoran ACPIDEV_DEBUG(CE_WARN,
651a3114836SGerry Liu "!acpidev: generated unitaddr is too long.");
652b72d5b75SMichael Corcoran return (NULL);
653b72d5b75SMichael Corcoran } else if (cnt == 1 && snprintf(buf, len, "%u", id1) >= len) {
654b72d5b75SMichael Corcoran ACPIDEV_DEBUG(CE_WARN,
655a3114836SGerry Liu "!acpidev: generated unitaddr is too long.");
656b72d5b75SMichael Corcoran return (NULL);
657b72d5b75SMichael Corcoran }
658b72d5b75SMichael Corcoran
659b72d5b75SMichael Corcoran return (buf);
660b72d5b75SMichael Corcoran }
661b72d5b75SMichael Corcoran
662b72d5b75SMichael Corcoran char *
acpidev_generate_unitaddr(char * uid,char ** fmts,size_t nfmt,char * buf,size_t len)663b72d5b75SMichael Corcoran acpidev_generate_unitaddr(char *uid, char **fmts, size_t nfmt,
664b72d5b75SMichael Corcoran char *buf, size_t len)
665b72d5b75SMichael Corcoran {
666b72d5b75SMichael Corcoran size_t i;
667b72d5b75SMichael Corcoran uint_t count = 0;
668b72d5b75SMichael Corcoran ulong_t val;
669b72d5b75SMichael Corcoran char **formats = NULL;
670b72d5b75SMichael Corcoran char *rbuf = NULL;
671b72d5b75SMichael Corcoran char *endp = NULL;
672b72d5b75SMichael Corcoran
673b72d5b75SMichael Corcoran ASSERT(len >= 64);
674b72d5b75SMichael Corcoran
675b72d5b75SMichael Corcoran /* Use _UID as unit address if it's a decimal integer. */
676b72d5b75SMichael Corcoran if (ddi_strtoul(uid, &endp, 10, &val) == 0 &&
677b72d5b75SMichael Corcoran (endp == NULL || *endp == 0)) {
678b72d5b75SMichael Corcoran if (snprintf(buf, len, "%s", uid) >= len) {
679b72d5b75SMichael Corcoran return (NULL);
680b72d5b75SMichael Corcoran } else {
681b72d5b75SMichael Corcoran return (buf);
682b72d5b75SMichael Corcoran }
683b72d5b75SMichael Corcoran }
684b72d5b75SMichael Corcoran
685b72d5b75SMichael Corcoran /* First handle uid format strings from device property. */
686b72d5b75SMichael Corcoran if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(),
687b72d5b75SMichael Corcoran DDI_PROP_DONTPASS,
688b72d5b75SMichael Corcoran ACPIDEV_PROP_NAME_UID_FORMAT, &formats, &count) == DDI_SUCCESS) {
689b72d5b75SMichael Corcoran /* Walk through format strings and try to generate unitaddr. */
690b72d5b75SMichael Corcoran for (i = 0; i < count && rbuf == NULL; i++) {
691b72d5b75SMichael Corcoran rbuf = acpidev_gen_unitaddr(uid, formats[i], buf, len);
692b72d5b75SMichael Corcoran }
693b72d5b75SMichael Corcoran ddi_prop_free(formats);
694b72d5b75SMichael Corcoran }
695b72d5b75SMichael Corcoran
696b72d5b75SMichael Corcoran /* Then handle embedded uid format strings. */
697b72d5b75SMichael Corcoran if (fmts != NULL) {
698b72d5b75SMichael Corcoran for (i = 0; i < nfmt && rbuf == NULL; i++) {
699b72d5b75SMichael Corcoran rbuf = acpidev_gen_unitaddr(uid, fmts[i], buf, len);
700b72d5b75SMichael Corcoran }
701b72d5b75SMichael Corcoran }
702b72d5b75SMichael Corcoran
703b72d5b75SMichael Corcoran return (rbuf);
704b72d5b75SMichael Corcoran }
705b72d5b75SMichael Corcoran
706b72d5b75SMichael Corcoran /*
707b72d5b75SMichael Corcoran * The Solaris device "unit-address" property is composed of a comma-delimited
708b72d5b75SMichael Corcoran * list of hexadecimal values. According to the ACPI spec, the ACPI _UID method
709b72d5b75SMichael Corcoran * could return an integer or a string. If it returns an integer, it is used
710b72d5b75SMichael Corcoran * as the unit-address as is. If _UID returns a string, we try to extract some
711b72d5b75SMichael Corcoran * meaningful integers to compose the unit-address property. If we fail to
712b72d5b75SMichael Corcoran * extract any integers, a pseudo-sequential number will be generated for the
713b72d5b75SMichael Corcoran * unit-address.
714b72d5b75SMichael Corcoran */
715b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_set_unitaddr(acpidev_walk_info_t * infop,char ** fmts,size_t nfmt,char * unitaddr)716b72d5b75SMichael Corcoran acpidev_set_unitaddr(acpidev_walk_info_t *infop, char **fmts, size_t nfmt,
717b72d5b75SMichael Corcoran char *unitaddr)
718b72d5b75SMichael Corcoran {
719b72d5b75SMichael Corcoran char unit[64];
720b72d5b75SMichael Corcoran
721b72d5b75SMichael Corcoran ASSERT(infop != NULL);
722b72d5b75SMichael Corcoran ASSERT(infop->awi_dip != NULL);
723b72d5b75SMichael Corcoran ASSERT(infop->awi_info != NULL);
724b72d5b75SMichael Corcoran if (infop == NULL || infop->awi_dip == NULL ||
725b72d5b75SMichael Corcoran infop->awi_info == NULL) {
726b72d5b75SMichael Corcoran ACPIDEV_DEBUG(CE_WARN,
727a3114836SGerry Liu "!acpidev: invalid parameters in acpidev_set_unitaddr().");
728b72d5b75SMichael Corcoran return (AE_BAD_PARAMETER);
729b72d5b75SMichael Corcoran }
730b72d5b75SMichael Corcoran
731b72d5b75SMichael Corcoran if (infop->awi_info->Valid & ACPI_VALID_UID) {
732b72d5b75SMichael Corcoran if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip,
73357190917SDana Myers ACPIDEV_PROP_NAME_ACPI_UID,
73457190917SDana Myers infop->awi_info->UniqueId.String) != NDI_SUCCESS) {
735b72d5b75SMichael Corcoran cmn_err(CE_WARN,
736b72d5b75SMichael Corcoran "!acpidev: failed to set UID property for %s.",
737b72d5b75SMichael Corcoran infop->awi_name);
738b72d5b75SMichael Corcoran return (AE_ERROR);
739b72d5b75SMichael Corcoran }
740b72d5b75SMichael Corcoran }
741b72d5b75SMichael Corcoran
742b72d5b75SMichael Corcoran if (unitaddr == NULL && (infop->awi_info->Valid & ACPI_VALID_UID)) {
743b72d5b75SMichael Corcoran /* Try to generate unit address from _UID. */
744b72d5b75SMichael Corcoran if (fmts == NULL) {
745b72d5b75SMichael Corcoran fmts = acpidev_uid_formats;
746b72d5b75SMichael Corcoran nfmt = sizeof (acpidev_uid_formats) / sizeof (char *);
747b72d5b75SMichael Corcoran }
748b72d5b75SMichael Corcoran unitaddr = acpidev_generate_unitaddr(
74957190917SDana Myers infop->awi_info->UniqueId.String, fmts, nfmt,
750b72d5b75SMichael Corcoran unit, sizeof (unit));
751b72d5b75SMichael Corcoran /* Generate pseudo sequential unit address. */
752b72d5b75SMichael Corcoran if (unitaddr == NULL) {
753b72d5b75SMichael Corcoran unitaddr = acpidev_generate_pseudo_unitaddr(
75457190917SDana Myers infop->awi_info->UniqueId.String,
755b72d5b75SMichael Corcoran infop->awi_class_curr->adc_class_id,
756b72d5b75SMichael Corcoran unit, sizeof (unit));
757b72d5b75SMichael Corcoran }
758b72d5b75SMichael Corcoran if (unitaddr == NULL) {
759b72d5b75SMichael Corcoran cmn_err(CE_WARN, "!acpidev: failed to generate unit "
760b72d5b75SMichael Corcoran "address from %s.",
76157190917SDana Myers infop->awi_info->UniqueId.String);
762b72d5b75SMichael Corcoran return (AE_ERROR);
763b72d5b75SMichael Corcoran }
764b72d5b75SMichael Corcoran }
765b72d5b75SMichael Corcoran if (unitaddr == NULL) {
766b72d5b75SMichael Corcoran /*
767b72d5b75SMichael Corcoran * Some ACPI objects may have no _UID method available, so we
768b72d5b75SMichael Corcoran * can't generate the "unit-address" property for them.
769b72d5b75SMichael Corcoran * On the other hand, it's legal to support such a device
770b72d5b75SMichael Corcoran * without a unit address, so return success here.
771b72d5b75SMichael Corcoran */
772b72d5b75SMichael Corcoran return (AE_OK);
773b72d5b75SMichael Corcoran }
774b72d5b75SMichael Corcoran
775b72d5b75SMichael Corcoran if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip,
776b72d5b75SMichael Corcoran ACPIDEV_PROP_NAME_UNIT_ADDR, unitaddr) != NDI_SUCCESS) {
777b72d5b75SMichael Corcoran cmn_err(CE_WARN, "!acpidev: failed to set unitaddr for %s.",
778b72d5b75SMichael Corcoran infop->awi_name);
779b72d5b75SMichael Corcoran return (AE_ERROR);
780b72d5b75SMichael Corcoran }
781b72d5b75SMichael Corcoran
782b72d5b75SMichael Corcoran return (AE_OK);
783b72d5b75SMichael Corcoran }
784b72d5b75SMichael Corcoran
785b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_set_compatible(acpidev_walk_info_t * infop,char ** compat,int acount)786b72d5b75SMichael Corcoran acpidev_set_compatible(acpidev_walk_info_t *infop, char **compat, int acount)
787b72d5b75SMichael Corcoran {
788b72d5b75SMichael Corcoran int count, i, j;
789b72d5b75SMichael Corcoran char **compatible = NULL;
790b72d5b75SMichael Corcoran ACPI_DEVICE_INFO *di;
791b72d5b75SMichael Corcoran
792b72d5b75SMichael Corcoran /*
793b72d5b75SMichael Corcoran * Generate compatible list for device based on:
794b72d5b75SMichael Corcoran * * Device HID if available
795b72d5b75SMichael Corcoran * * Device CIDs if available
796b72d5b75SMichael Corcoran * * property array passed in
797b72d5b75SMichael Corcoran */
798b72d5b75SMichael Corcoran ASSERT(infop != NULL);
799b72d5b75SMichael Corcoran ASSERT(infop->awi_dip != NULL);
800b72d5b75SMichael Corcoran ASSERT(infop->awi_info != NULL);
801b72d5b75SMichael Corcoran ASSERT(compat != NULL || acount == 0);
802b72d5b75SMichael Corcoran if (infop == NULL || infop->awi_dip == NULL ||
803b72d5b75SMichael Corcoran infop->awi_info == NULL || (compat == NULL && acount != 0)) {
804a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters "
805b72d5b75SMichael Corcoran "in acpidev_set_compatible().");
806b72d5b75SMichael Corcoran return (AE_BAD_PARAMETER);
807b72d5b75SMichael Corcoran }
808b72d5b75SMichael Corcoran
809b72d5b75SMichael Corcoran /* Compute string count. */
810b72d5b75SMichael Corcoran count = acount;
811b72d5b75SMichael Corcoran di = infop->awi_info;
812b72d5b75SMichael Corcoran if (di->Valid & ACPI_VALID_HID) {
813b72d5b75SMichael Corcoran count++;
814b72d5b75SMichael Corcoran }
815b72d5b75SMichael Corcoran if (di->Valid & ACPI_VALID_CID) {
81657190917SDana Myers count += di->CompatibleIdList.Count;
817b72d5b75SMichael Corcoran }
818b72d5b75SMichael Corcoran compatible = kmem_zalloc(sizeof (char *) * count, KM_SLEEP);
819b72d5b75SMichael Corcoran
820b72d5b75SMichael Corcoran /* Generate string array. */
821b72d5b75SMichael Corcoran i = 0;
822b72d5b75SMichael Corcoran if (di->Valid & ACPI_VALID_HID) {
82357190917SDana Myers compatible[i++] = di->HardwareId.String;
824b72d5b75SMichael Corcoran }
825b72d5b75SMichael Corcoran if (di->Valid & ACPI_VALID_CID) {
82657190917SDana Myers for (j = 0; j < di->CompatibleIdList.Count; j++) {
82757190917SDana Myers compatible[i++] = di->CompatibleIdList.Ids[j].String;
828b72d5b75SMichael Corcoran }
829b72d5b75SMichael Corcoran }
830b72d5b75SMichael Corcoran for (j = 0; j < acount; j++) {
831b72d5b75SMichael Corcoran compatible[i++] = compat[j];
832b72d5b75SMichael Corcoran }
833b72d5b75SMichael Corcoran ASSERT(i == count);
834b72d5b75SMichael Corcoran
835b72d5b75SMichael Corcoran /* Set "compatible" property. */
836b72d5b75SMichael Corcoran if (ndi_prop_update_string_array(DDI_DEV_T_NONE, infop->awi_dip,
837b72d5b75SMichael Corcoran OBP_COMPATIBLE, compatible, count) != NDI_SUCCESS) {
838b72d5b75SMichael Corcoran cmn_err(CE_WARN, "!acpidev: failed to set compatible "
839b72d5b75SMichael Corcoran "property for %s in acpidev_set_compatible().",
840b72d5b75SMichael Corcoran infop->awi_name);
841b72d5b75SMichael Corcoran kmem_free(compatible, count * sizeof (char *));
842b72d5b75SMichael Corcoran return (AE_ERROR);
843b72d5b75SMichael Corcoran }
844b72d5b75SMichael Corcoran kmem_free(compatible, count * sizeof (char *));
845b72d5b75SMichael Corcoran
846b72d5b75SMichael Corcoran return (AE_OK);
847b72d5b75SMichael Corcoran }
848a3114836SGerry Liu
849a3114836SGerry Liu /* Evaluate _OST method under object, which is used to support hotplug event. */
850a3114836SGerry Liu ACPI_STATUS
acpidev_eval_ost(ACPI_HANDLE hdl,uint32_t code,uint32_t status,char * bufp,size_t len)851a3114836SGerry Liu acpidev_eval_ost(ACPI_HANDLE hdl, uint32_t code, uint32_t status,
852a3114836SGerry Liu char *bufp, size_t len)
853a3114836SGerry Liu {
854a3114836SGerry Liu ACPI_STATUS rc;
855a3114836SGerry Liu ACPI_OBJECT args[3];
856a3114836SGerry Liu ACPI_OBJECT_LIST arglist;
857a3114836SGerry Liu
858a3114836SGerry Liu args[0].Type = ACPI_TYPE_INTEGER;
859a3114836SGerry Liu args[0].Integer.Value = code;
860a3114836SGerry Liu args[1].Type = ACPI_TYPE_INTEGER;
861a3114836SGerry Liu args[1].Integer.Value = status;
862a3114836SGerry Liu args[2].Type = ACPI_TYPE_BUFFER;
863a3114836SGerry Liu args[2].Buffer.Pointer = (UINT8 *)bufp;
864a3114836SGerry Liu args[2].Buffer.Length = (UINT32)len;
865a3114836SGerry Liu if (bufp == NULL || len == 0) {
866a3114836SGerry Liu arglist.Count = 2;
867a3114836SGerry Liu } else {
868a3114836SGerry Liu arglist.Count = 3;
869a3114836SGerry Liu }
870a3114836SGerry Liu arglist.Pointer = args;
871a3114836SGerry Liu rc = AcpiEvaluateObject(hdl, ACPIDEV_METHOD_NAME_OST, &arglist, NULL);
872a3114836SGerry Liu if (rc != AE_OK && rc != AE_NOT_FOUND) {
873a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN,
874a3114836SGerry Liu "!acpidev: failed to evaluate _OST method, code 0x%x.", rc);
875a3114836SGerry Liu }
876a3114836SGerry Liu
877a3114836SGerry Liu return (rc);
878a3114836SGerry Liu }
879a3114836SGerry Liu
880a3114836SGerry Liu ACPI_STATUS
acpidev_eval_ej0(ACPI_HANDLE hdl)881a3114836SGerry Liu acpidev_eval_ej0(ACPI_HANDLE hdl)
882a3114836SGerry Liu {
883a3114836SGerry Liu ACPI_STATUS rc;
884a3114836SGerry Liu ACPI_OBJECT args[1];
885a3114836SGerry Liu ACPI_OBJECT_LIST arglist;
886a3114836SGerry Liu
887a3114836SGerry Liu /*
888a3114836SGerry Liu * Quotation from ACPI spec 4.0 section 6.3.3.
889a3114836SGerry Liu * Arg0 An Integer containing a device ejection control
890a3114836SGerry Liu * 0 Cancel a mark for ejection request (EJ0 will never be called
891a3114836SGerry Liu * with this value)
892a3114836SGerry Liu * 1 Hot eject or mark for ejection
893a3114836SGerry Liu */
894a3114836SGerry Liu args[0].Type = ACPI_TYPE_INTEGER;
895a3114836SGerry Liu args[0].Integer.Value = 1;
896a3114836SGerry Liu arglist.Count = 1;
897a3114836SGerry Liu arglist.Pointer = args;
898a3114836SGerry Liu rc = AcpiEvaluateObject(hdl, ACPIDEV_METHOD_NAME_EJ0, &arglist, NULL);
899a3114836SGerry Liu if (rc != AE_OK) {
900a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN,
901a3114836SGerry Liu "!acpidev: failed to evaluate _EJ0 method, code 0x%x.", rc);
902a3114836SGerry Liu }
903a3114836SGerry Liu
904a3114836SGerry Liu return (rc);
905a3114836SGerry Liu }
906a3114836SGerry Liu
907a3114836SGerry Liu ACPI_STATUS
acpidev_eval_pxm(ACPI_HANDLE hdl,uint32_t * idp)908a3114836SGerry Liu acpidev_eval_pxm(ACPI_HANDLE hdl, uint32_t *idp)
909a3114836SGerry Liu {
910a3114836SGerry Liu int pxmid;
911a3114836SGerry Liu
912a3114836SGerry Liu ASSERT(idp != NULL);
913a3114836SGerry Liu
914a3114836SGerry Liu /*
915a3114836SGerry Liu * Try to evaluate ACPI _PXM method to get proximity doamin id.
916a3114836SGerry Liu * Quotation from ACPI4.0:
917a3114836SGerry Liu * If the Local APIC ID / Local SAPIC ID / Local x2APIC ID of a
918a3114836SGerry Liu * dynamically added processor is not present in the System Resource
919a3114836SGerry Liu * Affinity Table (SRAT), a _PXM object must exist for the processor's
920a3114836SGerry Liu * device or one of its ancestors in the ACPI Namespace.
921a3114836SGerry Liu */
922a3114836SGerry Liu while (hdl != NULL) {
923a3114836SGerry Liu if (ACPI_SUCCESS(acpica_eval_int(hdl,
924a3114836SGerry Liu ACPIDEV_METHOD_NAME_PXM, &pxmid))) {
925a3114836SGerry Liu *idp = (uint32_t)pxmid;
926a3114836SGerry Liu return (AE_OK);
927a3114836SGerry Liu }
928a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetParent(hdl, &hdl))) {
929a3114836SGerry Liu break;
930a3114836SGerry Liu }
931a3114836SGerry Liu }
932a3114836SGerry Liu
933a3114836SGerry Liu return (AE_NOT_FOUND);
934a3114836SGerry Liu }
935