1*26947304SEvan Yan /*
2*26947304SEvan Yan  * CDDL HEADER START
3*26947304SEvan Yan  *
4*26947304SEvan Yan  * The contents of this file are subject to the terms of the
5*26947304SEvan Yan  * Common Development and Distribution License (the "License").
6*26947304SEvan Yan  * You may not use this file except in compliance with the License.
7*26947304SEvan Yan  *
8*26947304SEvan Yan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*26947304SEvan Yan  * or http://www.opensolaris.org/os/licensing.
10*26947304SEvan Yan  * See the License for the specific language governing permissions
11*26947304SEvan Yan  * and limitations under the License.
12*26947304SEvan Yan  *
13*26947304SEvan Yan  * When distributing Covered Code, include this CDDL HEADER in each
14*26947304SEvan Yan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*26947304SEvan Yan  * If applicable, add the following below this CDDL HEADER, with the
16*26947304SEvan Yan  * fields enclosed by brackets "[]" replaced with your own identifying
17*26947304SEvan Yan  * information: Portions Copyright [yyyy] [name of copyright owner]
18*26947304SEvan Yan  *
19*26947304SEvan Yan  * CDDL HEADER END
20*26947304SEvan Yan  */
21*26947304SEvan Yan /*
22*26947304SEvan Yan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*26947304SEvan Yan  * Use is subject to license terms.
24*26947304SEvan Yan  */
25*26947304SEvan Yan 
26*26947304SEvan Yan #include <stdio.h>
27*26947304SEvan Yan #include <stdlib.h>
28*26947304SEvan Yan #include <string.h>
29*26947304SEvan Yan #include <errno.h>
30*26947304SEvan Yan #include <libdevinfo.h>
31*26947304SEvan Yan #include <libhotplug.h>
32*26947304SEvan Yan #include <libhotplug_impl.h>
33*26947304SEvan Yan #include <sys/sunddi.h>
34*26947304SEvan Yan #include <sys/ddi_hp.h>
35*26947304SEvan Yan #include "hotplugd_impl.h"
36*26947304SEvan Yan 
37*26947304SEvan Yan /*
38*26947304SEvan Yan  * Define a list of hotplug nodes.
39*26947304SEvan Yan  * (Only used within this module.)
40*26947304SEvan Yan  */
41*26947304SEvan Yan typedef struct {
42*26947304SEvan Yan 	hp_node_t	head;
43*26947304SEvan Yan 	hp_node_t	prev;
44*26947304SEvan Yan } hp_node_list_t;
45*26947304SEvan Yan 
46*26947304SEvan Yan /*
47*26947304SEvan Yan  * Local functions.
48*26947304SEvan Yan  */
49*26947304SEvan Yan static int		copy_devinfo(const char *, const char *, uint_t,
50*26947304SEvan Yan 			    hp_node_t *);
51*26947304SEvan Yan static int		copy_devices(hp_node_t, di_node_t, uint_t, hp_node_t *);
52*26947304SEvan Yan static int		copy_hotplug(hp_node_t, di_node_t, const char *, uint_t,
53*26947304SEvan Yan 			    hp_node_t *);
54*26947304SEvan Yan static char		*base_path(const char *);
55*26947304SEvan Yan static int		search_cb(di_node_t, void *);
56*26947304SEvan Yan static int		check_search(di_node_t, uint_t);
57*26947304SEvan Yan static hp_node_t	new_device_node(hp_node_t, di_node_t);
58*26947304SEvan Yan static hp_node_t	new_hotplug_node(hp_node_t, di_hp_t);
59*26947304SEvan Yan static void		node_list_add(hp_node_list_t *, hp_node_t);
60*26947304SEvan Yan 
61*26947304SEvan Yan /*
62*26947304SEvan Yan  * getinfo()
63*26947304SEvan Yan  *
64*26947304SEvan Yan  *	Build a hotplug information snapshot.  The path, connection,
65*26947304SEvan Yan  *	and flags indicate what information should be included.
66*26947304SEvan Yan  */
67*26947304SEvan Yan int
getinfo(const char * path,const char * connection,uint_t flags,hp_node_t * retp)68*26947304SEvan Yan getinfo(const char *path, const char *connection, uint_t flags, hp_node_t *retp)
69*26947304SEvan Yan {
70*26947304SEvan Yan 	hp_node_t	root = NULL;
71*26947304SEvan Yan 	hp_node_t	child;
72*26947304SEvan Yan 	char		*basepath;
73*26947304SEvan Yan 	int		rv;
74*26947304SEvan Yan 
75*26947304SEvan Yan 	if ((path == NULL) || (retp == NULL))
76*26947304SEvan Yan 		return (EINVAL);
77*26947304SEvan Yan 
78*26947304SEvan Yan 	dprintf("getinfo: path=%s, connection=%s, flags=0x%x\n", path,
79*26947304SEvan Yan 	    (connection == NULL) ? "NULL" : connection, flags);
80*26947304SEvan Yan 
81*26947304SEvan Yan 	/* Allocate the base path */
82*26947304SEvan Yan 	if ((basepath = base_path(path)) == NULL)
83*26947304SEvan Yan 		return (ENOMEM);
84*26947304SEvan Yan 
85*26947304SEvan Yan 	/* Copy in device and hotplug nodes from libdevinfo */
86*26947304SEvan Yan 	if ((rv = copy_devinfo(basepath, connection, flags, &root)) != 0) {
87*26947304SEvan Yan 		hp_fini(root);
88*26947304SEvan Yan 		free(basepath);
89*26947304SEvan Yan 		return (rv);
90*26947304SEvan Yan 	}
91*26947304SEvan Yan 
92*26947304SEvan Yan 	/* Check if there were no connections */
93*26947304SEvan Yan 	if (root == NULL) {
94*26947304SEvan Yan 		dprintf("getinfo: no hotplug connections.\n");
95*26947304SEvan Yan 		free(basepath);
96*26947304SEvan Yan 		return (ENOENT);
97*26947304SEvan Yan 	}
98*26947304SEvan Yan 
99*26947304SEvan Yan 	/* Special case: exclude root nexus from snapshot */
100*26947304SEvan Yan 	if (strcmp(basepath, "/") == 0) {
101*26947304SEvan Yan 		child = root->hp_child;
102*26947304SEvan Yan 		if (root->hp_name != NULL)
103*26947304SEvan Yan 			free(root->hp_name);
104*26947304SEvan Yan 		free(root);
105*26947304SEvan Yan 		root = child;
106*26947304SEvan Yan 		for (child = root; child; child = child->hp_sibling)
107*26947304SEvan Yan 			child->hp_parent = NULL;
108*26947304SEvan Yan 	}
109*26947304SEvan Yan 
110*26947304SEvan Yan 	/* Store a pointer to the base path in each root node */
111*26947304SEvan Yan 	for (child = root; child != NULL; child = child->hp_sibling)
112*26947304SEvan Yan 		child->hp_basepath = basepath;
113*26947304SEvan Yan 
114*26947304SEvan Yan 	/* Copy in usage information from RCM */
115*26947304SEvan Yan 	if (flags & HPINFOUSAGE) {
116*26947304SEvan Yan 		if ((rv = copy_usage(root)) != 0) {
117*26947304SEvan Yan 			(void) hp_fini(root);
118*26947304SEvan Yan 			return (rv);
119*26947304SEvan Yan 		}
120*26947304SEvan Yan 	}
121*26947304SEvan Yan 
122*26947304SEvan Yan 	*retp = root;
123*26947304SEvan Yan 	return (0);
124*26947304SEvan Yan }
125*26947304SEvan Yan 
126*26947304SEvan Yan /*
127*26947304SEvan Yan  * copy_devinfo()
128*26947304SEvan Yan  *
129*26947304SEvan Yan  *	Copy information about device and hotplug nodes from libdevinfo.
130*26947304SEvan Yan  *
131*26947304SEvan Yan  *	When path is set to "/", the results need to be limited only to
132*26947304SEvan Yan  *	branches that contain hotplug information.  An initial search
133*26947304SEvan Yan  * 	is performed to mark which branches contain hotplug nodes.
134*26947304SEvan Yan  */
135*26947304SEvan Yan static int
copy_devinfo(const char * path,const char * connection,uint_t flags,hp_node_t * rootp)136*26947304SEvan Yan copy_devinfo(const char *path, const char *connection, uint_t flags,
137*26947304SEvan Yan     hp_node_t *rootp)
138*26947304SEvan Yan {
139*26947304SEvan Yan 	hp_node_t	hp_root = NULL;
140*26947304SEvan Yan 	di_node_t	di_root;
141*26947304SEvan Yan 	int		rv;
142*26947304SEvan Yan 
143*26947304SEvan Yan 	/* Get libdevinfo snapshot */
144*26947304SEvan Yan 	if ((di_root = di_init(path, DINFOSUBTREE | DINFOHP)) == DI_NODE_NIL)
145*26947304SEvan Yan 		return (errno);
146*26947304SEvan Yan 
147*26947304SEvan Yan 	/* Do initial search pass, if required */
148*26947304SEvan Yan 	if (strcmp(path, "/") == 0) {
149*26947304SEvan Yan 		flags |= HPINFOSEARCH;
150*26947304SEvan Yan 		(void) di_walk_node(di_root, DI_WALK_CLDFIRST, NULL, search_cb);
151*26947304SEvan Yan 	}
152*26947304SEvan Yan 
153*26947304SEvan Yan 	/*
154*26947304SEvan Yan 	 * If a connection is specified, just copy immediate hotplug info.
155*26947304SEvan Yan 	 * Else, copy the device tree normally.
156*26947304SEvan Yan 	 */
157*26947304SEvan Yan 	if (connection != NULL)
158*26947304SEvan Yan 		rv = copy_hotplug(NULL, di_root, connection, flags, &hp_root);
159*26947304SEvan Yan 	else
160*26947304SEvan Yan 		rv = copy_devices(NULL, di_root, flags, &hp_root);
161*26947304SEvan Yan 
162*26947304SEvan Yan 	/* Destroy devinfo snapshot */
163*26947304SEvan Yan 	di_fini(di_root);
164*26947304SEvan Yan 
165*26947304SEvan Yan 	*rootp = (rv == 0) ? hp_root : NULL;
166*26947304SEvan Yan 	return (rv);
167*26947304SEvan Yan }
168*26947304SEvan Yan 
169*26947304SEvan Yan /*
170*26947304SEvan Yan  * copy_devices()
171*26947304SEvan Yan  *
172*26947304SEvan Yan  *	Copy a full branch of device nodes.  Used by copy_devinfo() and
173*26947304SEvan Yan  *	copy_hotplug().
174*26947304SEvan Yan  */
175*26947304SEvan Yan static int
copy_devices(hp_node_t parent,di_node_t dev,uint_t flags,hp_node_t * rootp)176*26947304SEvan Yan copy_devices(hp_node_t parent, di_node_t dev, uint_t flags, hp_node_t *rootp)
177*26947304SEvan Yan {
178*26947304SEvan Yan 	hp_node_list_t	children;
179*26947304SEvan Yan 	hp_node_t	self, branch;
180*26947304SEvan Yan 	di_node_t	child;
181*26947304SEvan Yan 	int		rv = 0;
182*26947304SEvan Yan 
183*26947304SEvan Yan 	/* Initialize results */
184*26947304SEvan Yan 	*rootp = NULL;
185*26947304SEvan Yan 
186*26947304SEvan Yan 	/* Enforce search semantics */
187*26947304SEvan Yan 	if (check_search(dev, flags) == 0)
188*26947304SEvan Yan 		return (0);
189*26947304SEvan Yan 
190*26947304SEvan Yan 	/* Allocate new node for current device */
191*26947304SEvan Yan 	if ((self = new_device_node(parent, dev)) == NULL)
192*26947304SEvan Yan 		return (ENOMEM);
193*26947304SEvan Yan 
194*26947304SEvan Yan 	/*
195*26947304SEvan Yan 	 * If the device has hotplug nodes, then use copy_hotplug()
196*26947304SEvan Yan 	 * instead to build the branch associated with current device.
197*26947304SEvan Yan 	 */
198*26947304SEvan Yan 	if (di_hp_next(dev, DI_HP_NIL) != DI_HP_NIL) {
199*26947304SEvan Yan 		if ((rv = copy_hotplug(self, dev, NULL, flags,
200*26947304SEvan Yan 		    &self->hp_child)) != 0) {
201*26947304SEvan Yan 			free(self);
202*26947304SEvan Yan 			return (rv);
203*26947304SEvan Yan 		}
204*26947304SEvan Yan 		*rootp = self;
205*26947304SEvan Yan 		return (0);
206*26947304SEvan Yan 	}
207*26947304SEvan Yan 
208*26947304SEvan Yan 	/*
209*26947304SEvan Yan 	 * The device does not have hotplug nodes.  Use normal
210*26947304SEvan Yan 	 * approach of iterating through its child device nodes.
211*26947304SEvan Yan 	 */
212*26947304SEvan Yan 	(void) memset(&children, 0, sizeof (hp_node_list_t));
213*26947304SEvan Yan 	for (child = di_child_node(dev); child != DI_NODE_NIL;
214*26947304SEvan Yan 	    child = di_sibling_node(child)) {
215*26947304SEvan Yan 		branch = NULL;
216*26947304SEvan Yan 		if ((rv = copy_devices(self, child, flags, &branch)) != 0) {
217*26947304SEvan Yan 			(void) hp_fini(children.head);
218*26947304SEvan Yan 			free(self);
219*26947304SEvan Yan 			return (rv);
220*26947304SEvan Yan 		}
221*26947304SEvan Yan 		if (branch != NULL)
222*26947304SEvan Yan 			node_list_add(&children, branch);
223*26947304SEvan Yan 	}
224*26947304SEvan Yan 	self->hp_child = children.head;
225*26947304SEvan Yan 
226*26947304SEvan Yan 	/* Done */
227*26947304SEvan Yan 	*rootp = self;
228*26947304SEvan Yan 	return (0);
229*26947304SEvan Yan }
230*26947304SEvan Yan 
231*26947304SEvan Yan /*
232*26947304SEvan Yan  * copy_hotplug()
233*26947304SEvan Yan  *
234*26947304SEvan Yan  *	Copy a full branch of hotplug nodes.  Used by copy_devinfo()
235*26947304SEvan Yan  *	and copy_devices().
236*26947304SEvan Yan  *
237*26947304SEvan Yan  *	If a connection is specified, the results are limited only
238*26947304SEvan Yan  *	to the branch associated with that specific connection.
239*26947304SEvan Yan  */
240*26947304SEvan Yan static int
copy_hotplug(hp_node_t parent,di_node_t dev,const char * connection,uint_t flags,hp_node_t * retp)241*26947304SEvan Yan copy_hotplug(hp_node_t parent, di_node_t dev, const char *connection,
242*26947304SEvan Yan     uint_t flags, hp_node_t *retp)
243*26947304SEvan Yan {
244*26947304SEvan Yan 	hp_node_list_t	connections, ports;
245*26947304SEvan Yan 	hp_node_t	node, port_node;
246*26947304SEvan Yan 	di_node_t	child_dev;
247*26947304SEvan Yan 	di_hp_t		hp, port_hp;
248*26947304SEvan Yan 	uint_t		child_flags;
249*26947304SEvan Yan 	int		rv, physnum;
250*26947304SEvan Yan 
251*26947304SEvan Yan 	/* Stop implementing the HPINFOSEARCH flag */
252*26947304SEvan Yan 	child_flags = flags & ~(HPINFOSEARCH);
253*26947304SEvan Yan 
254*26947304SEvan Yan 	/* Clear lists of discovered ports and connections */
255*26947304SEvan Yan 	(void) memset(&ports, 0, sizeof (hp_node_list_t));
256*26947304SEvan Yan 	(void) memset(&connections, 0, sizeof (hp_node_list_t));
257*26947304SEvan Yan 
258*26947304SEvan Yan 	/*
259*26947304SEvan Yan 	 * Scan virtual ports.
260*26947304SEvan Yan 	 *
261*26947304SEvan Yan 	 * If a connection is specified and it matches a virtual port,
262*26947304SEvan Yan 	 * this will build the branch associated with that connection.
263*26947304SEvan Yan 	 * Else, this will only build branches for virtual ports that
264*26947304SEvan Yan 	 * are not associated with a physical connector.
265*26947304SEvan Yan 	 */
266*26947304SEvan Yan 	for (hp = DI_HP_NIL; (hp = di_hp_next(dev, hp)) != DI_HP_NIL; ) {
267*26947304SEvan Yan 
268*26947304SEvan Yan 		/* Ignore connectors */
269*26947304SEvan Yan 		if (di_hp_type(hp) != DDI_HP_CN_TYPE_VIRTUAL_PORT)
270*26947304SEvan Yan 			continue;
271*26947304SEvan Yan 
272*26947304SEvan Yan 		/*
273*26947304SEvan Yan 		 * Ignore ports associated with connectors, unless
274*26947304SEvan Yan 		 * a specific connection is being sought.
275*26947304SEvan Yan 		 */
276*26947304SEvan Yan 		if ((connection == NULL) && (di_hp_depends_on(hp) != -1))
277*26947304SEvan Yan 			continue;
278*26947304SEvan Yan 
279*26947304SEvan Yan 		/* If a connection is specified, ignore non-matching ports */
280*26947304SEvan Yan 		if ((connection != NULL) &&
281*26947304SEvan Yan 		    (strcmp(di_hp_name(hp), connection) != 0))
282*26947304SEvan Yan 			continue;
283*26947304SEvan Yan 
284*26947304SEvan Yan 		/* Create a new port node */
285*26947304SEvan Yan 		if ((node = new_hotplug_node(parent, hp)) == NULL) {
286*26947304SEvan Yan 			rv = ENOMEM;
287*26947304SEvan Yan 			goto fail;
288*26947304SEvan Yan 		}
289*26947304SEvan Yan 
290*26947304SEvan Yan 		/* Add port node to connection list */
291*26947304SEvan Yan 		node_list_add(&connections, node);
292*26947304SEvan Yan 
293*26947304SEvan Yan 		/* Add branch of child devices to port node */
294*26947304SEvan Yan 		if ((child_dev = di_hp_child(hp)) != DI_NODE_NIL)
295*26947304SEvan Yan 			if ((rv = copy_devices(node, child_dev, child_flags,
296*26947304SEvan Yan 			    &node->hp_child)) != 0)
297*26947304SEvan Yan 				goto fail;
298*26947304SEvan Yan 	}
299*26947304SEvan Yan 
300*26947304SEvan Yan 	/*
301*26947304SEvan Yan 	 * Scan physical connectors.
302*26947304SEvan Yan 	 *
303*26947304SEvan Yan 	 * If a connection is specified, the results will be limited
304*26947304SEvan Yan 	 * only to the branch associated with that connection.
305*26947304SEvan Yan 	 */
306*26947304SEvan Yan 	for (hp = DI_HP_NIL; (hp = di_hp_next(dev, hp)) != DI_HP_NIL; ) {
307*26947304SEvan Yan 
308*26947304SEvan Yan 		/* Ignore ports */
309*26947304SEvan Yan 		if (di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT)
310*26947304SEvan Yan 			continue;
311*26947304SEvan Yan 
312*26947304SEvan Yan 		/* If a connection is specified, ignore non-matching ports */
313*26947304SEvan Yan 		if ((connection != NULL) &&
314*26947304SEvan Yan 		    (strcmp(di_hp_name(hp), connection) != 0))
315*26947304SEvan Yan 			continue;
316*26947304SEvan Yan 
317*26947304SEvan Yan 		/* Create a new connector node */
318*26947304SEvan Yan 		if ((node = new_hotplug_node(parent, hp)) == NULL) {
319*26947304SEvan Yan 			rv = ENOMEM;
320*26947304SEvan Yan 			goto fail;
321*26947304SEvan Yan 		}
322*26947304SEvan Yan 
323*26947304SEvan Yan 		/* Add connector node to connection list */
324*26947304SEvan Yan 		node_list_add(&connections, node);
325*26947304SEvan Yan 
326*26947304SEvan Yan 		/* Add branches of associated port nodes */
327*26947304SEvan Yan 		physnum = di_hp_connection(hp);
328*26947304SEvan Yan 		port_hp = DI_HP_NIL;
329*26947304SEvan Yan 		while ((port_hp = di_hp_next(dev, port_hp)) != DI_HP_NIL) {
330*26947304SEvan Yan 
331*26947304SEvan Yan 			/* Ignore irrelevant connections */
332*26947304SEvan Yan 			if (di_hp_depends_on(port_hp) != physnum)
333*26947304SEvan Yan 				continue;
334*26947304SEvan Yan 
335*26947304SEvan Yan 			/* Add new port node to port list */
336*26947304SEvan Yan 			if ((port_node = new_hotplug_node(node,
337*26947304SEvan Yan 			    port_hp)) == NULL) {
338*26947304SEvan Yan 				rv = ENOMEM;
339*26947304SEvan Yan 				goto fail;
340*26947304SEvan Yan 			}
341*26947304SEvan Yan 			node_list_add(&ports, port_node);
342*26947304SEvan Yan 
343*26947304SEvan Yan 			/* Add branch of child devices */
344*26947304SEvan Yan 			if ((child_dev = di_hp_child(port_hp)) != DI_NODE_NIL) {
345*26947304SEvan Yan 				if ((rv = copy_devices(port_node, child_dev,
346*26947304SEvan Yan 				    child_flags, &port_node->hp_child)) != 0)
347*26947304SEvan Yan 					goto fail;
348*26947304SEvan Yan 			}
349*26947304SEvan Yan 		}
350*26947304SEvan Yan 		node->hp_child = ports.head;
351*26947304SEvan Yan 		(void) memset(&ports, 0, sizeof (hp_node_list_t));
352*26947304SEvan Yan 	}
353*26947304SEvan Yan 
354*26947304SEvan Yan 	if (connections.head == NULL)
355*26947304SEvan Yan 		return (ENXIO);
356*26947304SEvan Yan 	*retp = connections.head;
357*26947304SEvan Yan 	return (0);
358*26947304SEvan Yan 
359*26947304SEvan Yan fail:
360*26947304SEvan Yan 	(void) hp_fini(ports.head);
361*26947304SEvan Yan 	(void) hp_fini(connections.head);
362*26947304SEvan Yan 	return (rv);
363*26947304SEvan Yan }
364*26947304SEvan Yan 
365*26947304SEvan Yan /*
366*26947304SEvan Yan  * base_path()
367*26947304SEvan Yan  *
368*26947304SEvan Yan  *	Normalize the base path of a hotplug information snapshot.
369*26947304SEvan Yan  *	The caller must free the string that is allocated.
370*26947304SEvan Yan  */
371*26947304SEvan Yan static char *
base_path(const char * path)372*26947304SEvan Yan base_path(const char *path)
373*26947304SEvan Yan {
374*26947304SEvan Yan 	char	*base_path;
375*26947304SEvan Yan 	size_t	devices_len;
376*26947304SEvan Yan 
377*26947304SEvan Yan 	devices_len = strlen(S_DEVICES);
378*26947304SEvan Yan 
379*26947304SEvan Yan 	if (strncmp(path, S_DEVICES, devices_len) == 0)
380*26947304SEvan Yan 		base_path = strdup(&path[devices_len]);
381*26947304SEvan Yan 	else
382*26947304SEvan Yan 		base_path = strdup(path);
383*26947304SEvan Yan 
384*26947304SEvan Yan 	return (base_path);
385*26947304SEvan Yan }
386*26947304SEvan Yan 
387*26947304SEvan Yan /*
388*26947304SEvan Yan  * search_cb()
389*26947304SEvan Yan  *
390*26947304SEvan Yan  *	Callback function used by di_walk_node() to search for branches
391*26947304SEvan Yan  *	of the libdevinfo snapshot that contain hotplug nodes.
392*26947304SEvan Yan  */
393*26947304SEvan Yan /*ARGSUSED*/
394*26947304SEvan Yan static int
search_cb(di_node_t node,void * arg)395*26947304SEvan Yan search_cb(di_node_t node, void *arg)
396*26947304SEvan Yan {
397*26947304SEvan Yan 	di_node_t	parent;
398*26947304SEvan Yan 	uint_t		flags;
399*26947304SEvan Yan 
400*26947304SEvan Yan 	(void) di_node_private_set(node, (void *)(uintptr_t)0);
401*26947304SEvan Yan 
402*26947304SEvan Yan 	if (di_hp_next(node, DI_HP_NIL) == DI_HP_NIL)
403*26947304SEvan Yan 		return (DI_WALK_CONTINUE);
404*26947304SEvan Yan 
405*26947304SEvan Yan 	for (parent = node; parent != DI_NODE_NIL;
406*26947304SEvan Yan 	    parent = di_parent_node(parent)) {
407*26947304SEvan Yan 		flags = (uint_t)(uintptr_t)di_node_private_get(parent);
408*26947304SEvan Yan 		flags |= HPINFOSEARCH;
409*26947304SEvan Yan 		(void) di_node_private_set(parent, (void *)(uintptr_t)flags);
410*26947304SEvan Yan 	}
411*26947304SEvan Yan 
412*26947304SEvan Yan 	return (DI_WALK_CONTINUE);
413*26947304SEvan Yan }
414*26947304SEvan Yan 
415*26947304SEvan Yan /*
416*26947304SEvan Yan  * check_search()
417*26947304SEvan Yan  *
418*26947304SEvan Yan  *	Check if a device node was marked by an initial search pass.
419*26947304SEvan Yan  */
420*26947304SEvan Yan static int
check_search(di_node_t dev,uint_t flags)421*26947304SEvan Yan check_search(di_node_t dev, uint_t flags)
422*26947304SEvan Yan {
423*26947304SEvan Yan 	uint_t	dev_flags;
424*26947304SEvan Yan 
425*26947304SEvan Yan 	if (flags & HPINFOSEARCH) {
426*26947304SEvan Yan 		dev_flags = (uint_t)(uintptr_t)di_node_private_get(dev);
427*26947304SEvan Yan 		if ((dev_flags & HPINFOSEARCH) == 0)
428*26947304SEvan Yan 			return (0);
429*26947304SEvan Yan 	}
430*26947304SEvan Yan 
431*26947304SEvan Yan 	return (1);
432*26947304SEvan Yan }
433*26947304SEvan Yan 
434*26947304SEvan Yan /*
435*26947304SEvan Yan  * node_list_add()
436*26947304SEvan Yan  *
437*26947304SEvan Yan  *	Utility function to append one node to a list of hotplug nodes.
438*26947304SEvan Yan  */
439*26947304SEvan Yan static void
node_list_add(hp_node_list_t * listp,hp_node_t node)440*26947304SEvan Yan node_list_add(hp_node_list_t *listp, hp_node_t node)
441*26947304SEvan Yan {
442*26947304SEvan Yan 	if (listp->prev != NULL)
443*26947304SEvan Yan 		listp->prev->hp_sibling = node;
444*26947304SEvan Yan 	else
445*26947304SEvan Yan 		listp->head = node;
446*26947304SEvan Yan 
447*26947304SEvan Yan 	listp->prev = node;
448*26947304SEvan Yan }
449*26947304SEvan Yan 
450*26947304SEvan Yan /*
451*26947304SEvan Yan  * new_device_node()
452*26947304SEvan Yan  *
453*26947304SEvan Yan  *	Build a new hotplug node based on a specified devinfo node.
454*26947304SEvan Yan  */
455*26947304SEvan Yan static hp_node_t
new_device_node(hp_node_t parent,di_node_t dev)456*26947304SEvan Yan new_device_node(hp_node_t parent, di_node_t dev)
457*26947304SEvan Yan {
458*26947304SEvan Yan 	hp_node_t	node;
459*26947304SEvan Yan 	char		*node_name, *bus_addr;
460*26947304SEvan Yan 	char		name[MAXPATHLEN];
461*26947304SEvan Yan 
462*26947304SEvan Yan 	node = (hp_node_t)calloc(1, sizeof (struct hp_node));
463*26947304SEvan Yan 
464*26947304SEvan Yan 	if (node != NULL) {
465*26947304SEvan Yan 		node->hp_parent = parent;
466*26947304SEvan Yan 		node->hp_type = HP_NODE_DEVICE;
467*26947304SEvan Yan 
468*26947304SEvan Yan 		node_name = di_node_name(dev);
469*26947304SEvan Yan 		bus_addr = di_bus_addr(dev);
470*26947304SEvan Yan 		if (bus_addr && (strlen(bus_addr) > 0)) {
471*26947304SEvan Yan 			if (snprintf(name, sizeof (name), "%s@%s", node_name,
472*26947304SEvan Yan 			    bus_addr) >= sizeof (name)) {
473*26947304SEvan Yan 				log_err("Path too long for device node.\n");
474*26947304SEvan Yan 				free(node);
475*26947304SEvan Yan 				return (NULL);
476*26947304SEvan Yan 			}
477*26947304SEvan Yan 			node->hp_name = strdup(name);
478*26947304SEvan Yan 		} else
479*26947304SEvan Yan 			node->hp_name = strdup(node_name);
480*26947304SEvan Yan 	}
481*26947304SEvan Yan 
482*26947304SEvan Yan 	return (node);
483*26947304SEvan Yan }
484*26947304SEvan Yan 
485*26947304SEvan Yan /*
486*26947304SEvan Yan  * new_hotplug_node()
487*26947304SEvan Yan  *
488*26947304SEvan Yan  *	Build a new hotplug node based on a specified devinfo hotplug node.
489*26947304SEvan Yan  */
490*26947304SEvan Yan static hp_node_t
new_hotplug_node(hp_node_t parent,di_hp_t hp)491*26947304SEvan Yan new_hotplug_node(hp_node_t parent, di_hp_t hp)
492*26947304SEvan Yan {
493*26947304SEvan Yan 	hp_node_t	node;
494*26947304SEvan Yan 	char		*s;
495*26947304SEvan Yan 
496*26947304SEvan Yan 	node = (hp_node_t)calloc(1, sizeof (struct hp_node));
497*26947304SEvan Yan 
498*26947304SEvan Yan 	if (node != NULL) {
499*26947304SEvan Yan 		node->hp_parent = parent;
500*26947304SEvan Yan 		node->hp_state = di_hp_state(hp);
501*26947304SEvan Yan 		node->hp_last_change = di_hp_last_change(hp);
502*26947304SEvan Yan 		if ((s = di_hp_name(hp)) != NULL)
503*26947304SEvan Yan 			node->hp_name = strdup(s);
504*26947304SEvan Yan 		if ((s = di_hp_description(hp)) != NULL)
505*26947304SEvan Yan 			node->hp_description = strdup(s);
506*26947304SEvan Yan 		if (di_hp_type(hp) == DDI_HP_CN_TYPE_VIRTUAL_PORT)
507*26947304SEvan Yan 			node->hp_type = HP_NODE_PORT;
508*26947304SEvan Yan 		else
509*26947304SEvan Yan 			node->hp_type = HP_NODE_CONNECTOR;
510*26947304SEvan Yan 	}
511*26947304SEvan Yan 
512*26947304SEvan Yan 	return (node);
513*26947304SEvan Yan }
514