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