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