xref: /illumos-gate/usr/src/cmd/devfsadm/cfg_link.c (revision fba27d8741c08c38aa9cf5fd383633304ddad810)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2019, Joyent, Inc.
24  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 #include <devfsadm.h>
30 #include <stdio.h>
31 #include <strings.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <config_admin.h>
37 #include <cfg_link.h>
38 #include <sys/types.h>
39 #include <sys/mkdev.h>
40 #include <sys/hotplug/pci/pcihp.h>
41 
42 #ifdef	DEBUG
43 #define	dprint(args)	devfsadm_errprint args
44 /*
45  * for use in print routine arg list as a shorthand way to locate node via
46  * "prtconf -D" to avoid messy and cluttered debugging code
47  * don't forget the corresponding "%s%d" format
48  */
49 #define	DRVINST(node)	di_driver_name(node), di_instance(node)
50 #else
51 #define	dprint(args)
52 #endif
53 
54 
55 static int	scsi_cfg_creat_cb(di_minor_t minor, di_node_t node);
56 static int	sbd_cfg_creat_cb(di_minor_t minor, di_node_t node);
57 static int	usb_cfg_creat_cb(di_minor_t minor, di_node_t node);
58 static char	*get_roothub(const char *path, void *cb_arg);
59 static int	pci_cfg_creat_cb(di_minor_t minor, di_node_t node);
60 static int	ib_cfg_creat_cb(di_minor_t minor, di_node_t node);
61 static int	sata_cfg_creat_cb(di_minor_t minor, di_node_t node);
62 static int	sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node);
63 static int	ccid_cfg_creat_cb(di_minor_t minor, di_node_t node);
64 
65 static di_node_t	pci_cfg_chassis_node(di_node_t, di_prom_handle_t);
66 static char	*pci_cfg_slotname(di_node_t, di_prom_handle_t, minor_t);
67 static int	pci_cfg_ap_node(minor_t, di_node_t, di_prom_handle_t,
68 		    char *, int, int);
69 static int	pci_cfg_iob_name(di_minor_t, di_node_t, di_prom_handle_t,
70 		    char *, int);
71 static minor_t	pci_cfg_pcidev(di_node_t, di_prom_handle_t);
72 static int	pci_cfg_ap_path(di_minor_t, di_node_t, di_prom_handle_t,
73 		    char *, int, char **);
74 static char	*pci_cfg_info_data(char *);
75 static int	pci_cfg_is_ap_path(di_node_t, di_prom_handle_t);
76 static int	pci_cfg_ap_legacy(di_minor_t, di_node_t, di_prom_handle_t,
77 		    char *, int);
78 static void	pci_cfg_rm_invalid_links(char *, char *);
79 static void	pci_cfg_rm_link(char *);
80 static void	pci_cfg_rm_all(char *);
81 static char	*pci_cfg_devpath(di_node_t, di_minor_t);
82 static di_node_t	pci_cfg_snapshot(di_node_t, di_minor_t,
83 			    di_node_t *, di_minor_t *);
84 
85 /* flag definitions for di_propall_*(); value "0" is always the default flag */
86 #define	DIPROP_PRI_NODE		0x0
87 #define	DIPROP_PRI_PROM		0x1
88 static int	di_propall_lookup_ints(di_prom_handle_t, int,
89 		    dev_t, di_node_t, const char *, int **);
90 static int	di_propall_lookup_strings(di_prom_handle_t, int,
91 		    dev_t, di_node_t, const char *, char **);
92 static int	serid_printable(uint64_t *seridp);
93 static int	di_propall_lookup_slot_names(di_prom_handle_t, int,
94 		    dev_t, di_node_t, di_slot_name_t **);
95 
96 
97 /*
98  * NOTE: The CREATE_DEFER flag is private to this module.
99  *	 NOT to be used by other modules
100  */
101 static devfsadm_create_t cfg_create_cbt[] = {
102 	{ "attachment-point", DDI_NT_SCSI_ATTACHMENT_POINT, NULL,
103 	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
104 	},
105 	{ "attachment-point", DDI_NT_SBD_ATTACHMENT_POINT, NULL,
106 	    TYPE_EXACT, ILEVEL_0, sbd_cfg_creat_cb
107 	},
108 	{ "fc-attachment-point", DDI_NT_FC_ATTACHMENT_POINT, NULL,
109 	    TYPE_EXACT | CREATE_DEFER, ILEVEL_0, scsi_cfg_creat_cb
110 	},
111 	{ "attachment-point", DDI_NT_USB_ATTACHMENT_POINT, NULL,
112 	    TYPE_EXACT, ILEVEL_0, usb_cfg_creat_cb
113 	},
114 	{ "attachment-point", DDI_NT_PCI_ATTACHMENT_POINT, NULL,
115 	    TYPE_EXACT, ILEVEL_0, pci_cfg_creat_cb
116 	},
117 	{ "attachment-point", DDI_NT_IB_ATTACHMENT_POINT, NULL,
118 	    TYPE_EXACT, ILEVEL_0, ib_cfg_creat_cb
119 	},
120 	{ "attachment-point", DDI_NT_SATA_ATTACHMENT_POINT, NULL,
121 	    TYPE_EXACT, ILEVEL_0, sata_cfg_creat_cb
122 	},
123 	{ "attachment-point", DDI_NT_SDCARD_ATTACHMENT_POINT, NULL,
124 	    TYPE_EXACT, ILEVEL_0, sdcard_cfg_creat_cb
125 	},
126 	{ "attachment-point", DDI_NT_CCID_ATTACHMENT_POINT, NULL,
127 	    TYPE_EXACT, ILEVEL_0, ccid_cfg_creat_cb
128 	}
129 };
130 
131 DEVFSADM_CREATE_INIT_V0(cfg_create_cbt);
132 
133 static devfsadm_remove_t cfg_remove_cbt[] = {
134 	{ "attachment-point", SCSI_CFG_LINK_RE, RM_POST,
135 	    ILEVEL_0, devfsadm_rm_all
136 	},
137 	{ "attachment-point", SBD_CFG_LINK_RE, RM_POST,
138 	    ILEVEL_0, devfsadm_rm_all
139 	},
140 	{ "fc-attachment-point", SCSI_CFG_LINK_RE, RM_POST,
141 	    ILEVEL_0, devfsadm_rm_all
142 	},
143 	{ "attachment-point", USB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
144 	    ILEVEL_0, devfsadm_rm_all
145 	},
146 	{ "attachment-point", PCI_CFG_LINK_RE, RM_POST,
147 	    ILEVEL_0, devfsadm_rm_all
148 	},
149 	{ "attachment-point", PCI_CFG_PATH_LINK_RE, RM_POST|RM_HOT,
150 	    ILEVEL_0, pci_cfg_rm_all
151 	},
152 	{ "attachment-point", IB_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
153 	    ILEVEL_0, devfsadm_rm_all
154 	},
155 	{ "attachment-point", SATA_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
156 	    ILEVEL_0, devfsadm_rm_all
157 	},
158 	{ "attachment-point", SDCARD_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
159 	    ILEVEL_0, devfsadm_rm_all
160 	},
161 	{ "attachment-point", CCID_CFG_LINK_RE, RM_POST|RM_HOT|RM_ALWAYS,
162 	    ILEVEL_0, devfsadm_rm_all
163 	}
164 };
165 
166 DEVFSADM_REMOVE_INIT_V0(cfg_remove_cbt);
167 
168 static int
169 scsi_cfg_creat_cb(di_minor_t minor, di_node_t node)
170 {
171 	char path[PATH_MAX + 1];
172 	char *c_num = NULL, *devfs_path, *mn;
173 	devfsadm_enumerate_t rules[3] = {
174 	    {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT},
175 	    {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR},
176 	    {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT}
177 	};
178 
179 	mn = di_minor_name(minor);
180 
181 	if ((devfs_path = di_devfs_path(node)) == NULL) {
182 		return (DEVFSADM_CONTINUE);
183 	}
184 	(void) strcpy(path, devfs_path);
185 	(void) strcat(path, ":");
186 	(void) strcat(path, mn);
187 	di_devfs_path_free(devfs_path);
188 
189 	if (ctrl_enumerate_int(path, 1, &c_num, rules, 3, 0, B_FALSE)
190 	    == DEVFSADM_FAILURE) {
191 		/*
192 		 * Unlike the disks module we don't retry on failure.
193 		 * If we have multiple "c" numbers for a single physical
194 		 * controller due to bug 4045879, we will not assign a
195 		 * c-number/symlink for the controller.
196 		 */
197 		return (DEVFSADM_CONTINUE);
198 	}
199 
200 	(void) strcpy(path, CFG_DIRNAME);
201 	(void) strcat(path, "/c");
202 	(void) strcat(path, c_num);
203 
204 	free(c_num);
205 
206 	(void) devfsadm_mklink(path, node, minor, 0);
207 
208 	return (DEVFSADM_CONTINUE);
209 }
210 
211 static int
212 sbd_cfg_creat_cb(di_minor_t minor, di_node_t node)
213 {
214 	char path[PATH_MAX + 1];
215 
216 	(void) strcpy(path, CFG_DIRNAME);
217 	(void) strcat(path, "/");
218 	(void) strcat(path, di_minor_name(minor));
219 	(void) devfsadm_mklink(path, node, minor, 0);
220 	return (DEVFSADM_CONTINUE);
221 }
222 
223 
224 static int
225 usb_cfg_creat_cb(di_minor_t minor, di_node_t node)
226 {
227 	char *cp, path[PATH_MAX + 1];
228 	devfsadm_enumerate_t rules[1] =
229 		{"^cfg$/^usb([0-9]+)$", 1, MATCH_CALLBACK, NULL, get_roothub};
230 
231 	if ((cp = di_devfs_path(node)) == NULL) {
232 		return (DEVFSADM_CONTINUE);
233 	}
234 
235 	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
236 	di_devfs_path_free(cp);
237 
238 	if (ctrl_enumerate_int(path, 0, &cp, rules, 1, 0, B_FALSE)) {
239 		return (DEVFSADM_CONTINUE);
240 	}
241 
242 	/* create usbN and the symlink */
243 	(void) snprintf(path, sizeof (path), "%s/usb%s/%s", CFG_DIRNAME, cp,
244 	    di_minor_name(minor));
245 	free(cp);
246 
247 	(void) devfsadm_mklink(path, node, minor, 0);
248 
249 	return (DEVFSADM_CONTINUE);
250 }
251 
252 
253 static int
254 sata_cfg_creat_cb(di_minor_t minor, di_node_t node)
255 {
256 	char path[PATH_MAX + 1], l_path[PATH_MAX], *buf, *devfspath;
257 	char *minor_nm;
258 	devfsadm_enumerate_t rules[1] =
259 		{"^cfg$/^sata([0-9]+)$", 1, MATCH_ADDR};
260 
261 	minor_nm = di_minor_name(minor);
262 	if (minor_nm == NULL)
263 		return (DEVFSADM_CONTINUE);
264 
265 	devfspath = di_devfs_path(node);
266 	if (devfspath == NULL)
267 		return (DEVFSADM_CONTINUE);
268 
269 	(void) strlcpy(path, devfspath, sizeof (path));
270 	(void) strlcat(path, ":", sizeof (path));
271 	(void) strlcat(path, minor_nm, sizeof (path));
272 	di_devfs_path_free(devfspath);
273 
274 	/* build the physical path from the components */
275 	if (ctrl_enumerate_int(path, 0, &buf, rules, 1, 0, B_FALSE) ==
276 	    DEVFSADM_FAILURE) {
277 		return (DEVFSADM_CONTINUE);
278 	}
279 
280 	(void) snprintf(l_path, sizeof (l_path), "%s/sata%s/%s", CFG_DIRNAME,
281 	    buf, minor_nm);
282 	free(buf);
283 
284 	(void) devfsadm_mklink(l_path, node, minor, 0);
285 
286 	return (DEVFSADM_CONTINUE);
287 }
288 
289 static int
290 sdcard_cfg_creat_cb(di_minor_t minor, di_node_t node)
291 {
292 	char path[PATH_MAX +1], l_path[PATH_MAX], *buf, *devfspath;
293 	char *minor_nm;
294 	devfsadm_enumerate_t rules[1] =
295 	    {"^cfg$/^sdcard([0-9]+)$", 1, MATCH_ADDR};
296 
297 	minor_nm = di_minor_name(minor);
298 	if (minor_nm == NULL)
299 		return (DEVFSADM_CONTINUE);
300 
301 	devfspath = di_devfs_path(node);
302 	if (devfspath == NULL)
303 		return (DEVFSADM_CONTINUE);
304 
305 	(void) snprintf(path, sizeof (path), "%s:%s", devfspath, minor_nm);
306 	di_devfs_path_free(devfspath);
307 
308 	/* build the physical path from the components */
309 	if (ctrl_enumerate_int(path, 0, &buf, rules, 1, 0, B_FALSE) ==
310 	    DEVFSADM_FAILURE) {
311 		return (DEVFSADM_CONTINUE);
312 	}
313 
314 	(void) snprintf(l_path, sizeof (l_path), "%s/sdcard%s/%s",
315 	    CFG_DIRNAME, buf, minor_nm);
316 	free(buf);
317 
318 	(void) devfsadm_mklink(l_path, node, minor, 0);
319 
320 	return (DEVFSADM_CONTINUE);
321 }
322 
323 /*
324  * get_roothub:
325  *	figure out the root hub path to calculate /dev/cfg/usbN
326  */
327 /* ARGSUSED */
328 static char *
329 get_roothub(const char *path, void *cb_arg)
330 {
331 	int  i, count = 0;
332 	char *physpath, *cp;
333 
334 	/* make a copy */
335 	if ((physpath = strdup(path)) == NULL) {
336 		return (NULL);
337 	}
338 
339 	/*
340 	 * physpath must always have a minor name component
341 	 */
342 	if ((cp = strrchr(physpath, ':')) == NULL) {
343 		free(physpath);
344 		return (NULL);
345 	}
346 	*cp++ = '\0';
347 
348 	/*
349 	 * No '.' in the minor name indicates a roothub port.
350 	 */
351 	if (strchr(cp, '.') == NULL) {
352 		/* roothub device */
353 		return (physpath);
354 	}
355 
356 	while (*cp) {
357 		if (*cp == '.')
358 			count++;
359 		cp++;
360 	}
361 
362 	/* Remove as many trailing path components as there are '.'s */
363 	for (i = 0; i < count; i++) {
364 		if ((cp = strrchr(physpath, '/')) == NULL || (cp == physpath)) {
365 			free(physpath);
366 			return (NULL);
367 		}
368 		/*
369 		 * Check if there is any usb_mid node in the middle
370 		 * and remove the node as if there is an extra '.'
371 		 */
372 		if (strstr(cp, "miscellaneous") != NULL) {
373 			count++;
374 		}
375 		*cp = '\0';
376 	}
377 
378 	/* Remove the usb_mid node immediately before the trailing path */
379 	if ((cp = strrchr(physpath, '/')) != NULL && (cp != physpath)) {
380 		if (strstr(cp, "miscellaneous") != NULL) {
381 			*cp = '\0';
382 		}
383 	}
384 
385 	return (physpath);
386 }
387 
388 
389 /*
390  * returns an allocted string containing the device path for <node> and
391  * <minor>
392  */
393 static char *
394 pci_cfg_devpath(di_node_t node, di_minor_t minor)
395 {
396 	char *path;
397 	char *bufp;
398 	char *minor_nm;
399 	int buflen;
400 
401 	path = di_devfs_path(node);
402 	minor_nm = di_minor_name(minor);
403 	buflen = snprintf(NULL, 0, "%s:%s", path, minor_nm) + 1;
404 
405 	bufp = malloc(sizeof (char) * buflen);
406 	if (bufp != NULL)
407 		(void) snprintf(bufp, buflen, "%s:%s", path, minor_nm);
408 
409 	di_devfs_path_free(path);
410 	return (bufp);
411 }
412 
413 
414 static int
415 di_propall_lookup_ints(di_prom_handle_t ph, int flags,
416     dev_t dev, di_node_t node, const char *prop_name, int **prop_data)
417 {
418 	int rv;
419 
420 	if (flags & DIPROP_PRI_PROM) {
421 		rv = di_prom_prop_lookup_ints(ph, node, prop_name, prop_data);
422 		if (rv < 0)
423 			rv = di_prop_lookup_ints(dev, node, prop_name,
424 			    prop_data);
425 	} else {
426 		rv = di_prop_lookup_ints(dev, node, prop_name, prop_data);
427 		if (rv < 0)
428 			rv = di_prom_prop_lookup_ints(ph, node, prop_name,
429 			    prop_data);
430 	}
431 	return (rv);
432 }
433 
434 
435 static int
436 di_propall_lookup_strings(di_prom_handle_t ph, int flags,
437     dev_t dev, di_node_t node, const char *prop_name, char **prop_data)
438 {
439 	int rv;
440 
441 	if (flags & DIPROP_PRI_PROM) {
442 		rv = di_prom_prop_lookup_strings(ph, node, prop_name,
443 		    prop_data);
444 		if (rv < 0)
445 			rv = di_prop_lookup_strings(dev, node, prop_name,
446 			    prop_data);
447 	} else {
448 		rv = di_prop_lookup_strings(dev, node, prop_name, prop_data);
449 		if (rv < 0)
450 			rv = di_prom_prop_lookup_strings(ph, node, prop_name,
451 			    prop_data);
452 	}
453 	return (rv);
454 }
455 
456 
457 static di_node_t
458 pci_cfg_chassis_node(di_node_t node, di_prom_handle_t ph)
459 {
460 	di_node_t curnode = node;
461 	int *firstchas;
462 
463 	do {
464 		if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, curnode,
465 		    DI_PROP_FIRST_CHAS, &firstchas) >= 0)
466 			return (curnode);
467 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
468 
469 	return (DI_NODE_NIL);
470 }
471 
472 
473 static int
474 di_propall_lookup_slot_names(di_prom_handle_t ph, int flags,
475     dev_t dev, di_node_t node, di_slot_name_t **prop_data)
476 {
477 	int rv;
478 
479 	if (flags & DIPROP_PRI_PROM) {
480 		rv = di_prom_prop_lookup_slot_names(ph, node, prop_data);
481 		if (rv < 0)
482 			rv = di_prop_lookup_slot_names(dev, node, prop_data);
483 	} else {
484 		rv = di_prop_lookup_slot_names(dev, node, prop_data);
485 		if (rv < 0)
486 			rv = di_prom_prop_lookup_slot_names(ph, node,
487 			    prop_data);
488 	}
489 	return (rv);
490 }
491 
492 /*
493  * returns an allocated string containing the slot name for the slot with
494  * device number <pci_dev> on bus <node>
495  */
496 static char *
497 pci_cfg_slotname(di_node_t node, di_prom_handle_t ph, minor_t pci_dev)
498 {
499 #ifdef	DEBUG
500 	char *fnm = "pci_cfg_slotname";
501 #endif
502 	int i, count;
503 	char *name = NULL;
504 	di_slot_name_t *slot_names = NULL;
505 
506 	count = di_propall_lookup_slot_names(ph, 0, DDI_DEV_T_ANY, node,
507 	    &slot_names);
508 	if (count < 0)
509 		return (NULL);
510 
511 	for (i = 0; i < count; i++) {
512 		if (slot_names[i].num == (int)pci_dev) {
513 			name = strdup(slot_names[i].name);
514 			break;
515 		}
516 	}
517 #ifdef	DEBUG
518 	if (name == NULL)
519 		dprint(("%s: slot w/ pci_dev %d not found in %s for %s%d\n",
520 		    fnm, (int)pci_dev, DI_PROP_SLOT_NAMES, DRVINST(node)));
521 #endif
522 	if (count > 0)
523 		di_slot_names_free(count, slot_names);
524 	return (name);
525 }
526 
527 
528 /*
529  * returns non-zero if we can return a valid attachment point name for <node>,
530  * for its slot identified by child pci device number <pci_dev>, through <buf>
531  *
532  * prioritized naming scheme:
533  *	1) <DI_PROP_SLOT_NAMES property>    (see pci_cfg_slotname())
534  *	2) <device-type><DI_PROP_PHYS_SLOT property>
535  *	3) <drv name><drv inst>.<device-type><pci_dev>
536  *
537  * where <device-type> is derived from the DI_PROP_DEV_TYPE property:
538  *	if its value is "pciex" then <device-type> is "pcie"
539  *	else the raw value is used
540  *
541  * if <flags> contains APNODE_DEFNAME, then scheme (3) is used
542  */
543 static int
544 pci_cfg_ap_node(minor_t pci_dev, di_node_t node, di_prom_handle_t ph,
545     char *buf, int bufsz, int flags)
546 {
547 	int *nump;
548 	int rv;
549 	char *str, *devtype;
550 
551 	rv = di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, node,
552 	    DI_PROP_DEV_TYPE, &devtype);
553 	if (rv < 1)
554 		return (0);
555 
556 	if (strcmp(devtype, PROPVAL_PCIEX) == 0)
557 		devtype = DEVTYPE_PCIE;
558 
559 	if (flags & APNODE_DEFNAME)
560 		goto DEF;
561 
562 	str = pci_cfg_slotname(node, ph, pci_dev);
563 	if (str != NULL) {
564 		(void) strlcpy(buf, str, bufsz);
565 		free(str);
566 		return (1);
567 	}
568 
569 	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node,
570 	    DI_PROP_PHYS_SLOT, &nump) > 0) {
571 		if (*nump > 0) {
572 			(void) snprintf(buf, bufsz, "%s%d", devtype, *nump);
573 			return (1);
574 		}
575 	}
576 DEF:
577 	(void) snprintf(buf, bufsz, "%s%d.%s%d",
578 	    di_driver_name(node), di_instance(node), devtype, pci_dev);
579 
580 	return (1);
581 }
582 
583 
584 /*
585  * returns non-zero if we can return a valid expansion chassis name for <node>
586  * through <buf>
587  *
588  * prioritized naming scheme:
589  *	1) <IOB_PRE string><DI_PROP_SERID property: sun specific portion>
590  *	2) <IOB_PRE string><full DI_PROP_SERID property in hex>
591  *	3) <IOB_PRE string>
592  *
593  * DI_PROP_SERID encoding <64-bit int: msb ... lsb>:
594  * <24 bits: IEEE company id><40 bits: serial number>
595  *
596  * sun encoding of 40 bit serial number:
597  * first byte = device type indicator
598  * next 4 bytes = 4 ascii characters
599  *
600  * In the unlikely event that serial id contains non-printable characters
601  * the full 64 bit raw hex string will be used for the attachment point.
602  */
603 /*ARGSUSED*/
604 static int
605 pci_cfg_iob_name(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
606     char *buf, int bufsz)
607 {
608 	int64_t *seridp;
609 	uint64_t serid;
610 	char *idstr;
611 
612 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, DI_PROP_SERID,
613 	    &seridp) < 1) {
614 		(void) strlcpy(buf, IOB_PRE, bufsz);
615 		return (1);
616 	}
617 
618 	serid = (uint64_t)*seridp;
619 
620 	if ((serid >> 40) != (uint64_t)IEEE_SUN_ID ||
621 	    !serid_printable(&serid)) {
622 		(void) snprintf(buf, bufsz, "%s%llx", IOB_PRE, serid);
623 		return (1);
624 	}
625 
626 	/*
627 	 * the serial id is constructed from lower 40 bits of the serialid
628 	 * property and is represented by 5 ascii characters. The first
629 	 * character indicates if the IO Box is PCIe or PCI-X.
630 	 */
631 
632 	serid <<= 24;
633 	idstr = (char *)&serid;
634 	idstr[sizeof (serid) -1] = '\0';
635 
636 	(void) snprintf(buf, bufsz, "%s%s", IOB_PRE, idstr);
637 
638 	return (1);
639 }
640 
641 
642 /*
643  * returns the pci device number for <node> if found, else returns PCIDEV_NIL
644  */
645 static minor_t
646 pci_cfg_pcidev(di_node_t node, di_prom_handle_t ph)
647 {
648 	int rv;
649 	int *regp;
650 
651 	rv = di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, DI_PROP_REG,
652 	    &regp);
653 
654 	if (rv < 1) {
655 		dprint(("pci_cfg_pcidev: property %s not found "
656 		    "for %s%d\n", DI_PROP_REG, DRVINST(node)));
657 		return (PCIDEV_NIL);
658 	}
659 
660 	return (REG_PCIDEV(regp));
661 }
662 
663 
664 /*
665  * returns non-zero when it can successfully return an attachment point
666  * through <ap_path> whose length is less than <ap_pathsz>; returns the full
667  * path of the AP through <pathret> which may be larger than <ap_pathsz>.
668  * Callers need to free <pathret>.  If it cannot return the full path through
669  * <pathret> it will be set to NULL
670  *
671  * The ap path reflects a subset of the device path from an onboard host slot
672  * up to <node>.  We traverse up the device tree starting from <node>, naming
673  * each component using pci_cfg_ap_node().  If we detect that a certain
674  * segment is contained within an expansion chassis, then we skip any bus
675  * nodes in between our current node and the topmost node of the chassis,
676  * which is identified by the DI_PROP_FIRST_CHAS property, and prepend the name
677  * of the expansion chassis as given by pci_cfg_iob_name()
678  *
679  * This scheme is always used for <pathret>.  If however, the size of
680  * <pathret> is greater than <ap_pathsz> then only the default name as given
681  * by pci_cfg_ap_node() for <node> will be used
682  */
683 static int
684 pci_cfg_ap_path(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
685     char *ap_path, int ap_pathsz, char **pathret)
686 {
687 #ifdef	DEBUG
688 	char *fnm = "pci_cfg_ap_path";
689 #endif
690 #define	seplen		(sizeof (AP_PATH_SEP) - 1)
691 #define	iob_pre_len	(sizeof (IOB_PRE) - 1)
692 #define	ap_path_iob_sep_len	(sizeof (AP_PATH_IOB_SEP) - 1)
693 
694 	char *bufptr;
695 	char buf[MAXPATHLEN];
696 	char pathbuf[MAXPATHLEN];
697 	int bufsz;
698 	char *pathptr;
699 	char *pathend = NULL;
700 	int len;
701 	int rv = 0;
702 	int chasflag = 0;
703 	di_node_t curnode = node;
704 	di_node_t chasnode = DI_NODE_NIL;
705 	minor_t pci_dev;
706 
707 	buf[0] = '\0';
708 	pathbuf[0] = '\0';
709 	pathptr = &pathbuf[sizeof (pathbuf) - 1];
710 	*pathptr = '\0';
711 
712 	/*
713 	 * as we traverse up the device tree, we prepend components of our
714 	 * path inside pathbuf, using pathptr and decrementing
715 	 */
716 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
717 	do {
718 		bufptr = buf;
719 		bufsz = sizeof (buf);
720 
721 		chasnode = pci_cfg_chassis_node(curnode, ph);
722 		if (chasnode != DI_NODE_NIL) {
723 			rv = pci_cfg_iob_name(minor, chasnode, ph,
724 			    bufptr, bufsz);
725 			if (rv == 0) {
726 				dprint(("%s: cannot create iob name "
727 				    "for %s%d\n", fnm, DRVINST(node)));
728 				*pathptr = '\0';
729 				goto OUT;
730 			}
731 
732 			(void) strncat(bufptr, AP_PATH_IOB_SEP, bufsz);
733 			len = strlen(bufptr);
734 			bufptr += len;
735 			bufsz -= len - 1;
736 
737 			/* set chasflag when the leaf node is within an iob */
738 			if (curnode == node)
739 				chasflag = 1;
740 		}
741 		rv = pci_cfg_ap_node(pci_dev, curnode, ph, bufptr, bufsz, 0);
742 		if (rv == 0) {
743 			dprint(("%s: cannot create ap node name "
744 			    "for %s%d\n", fnm, DRVINST(node)));
745 			*pathptr = '\0';
746 			goto OUT;
747 		}
748 
749 		/*
750 		 * if we can't fit the entire path in our pathbuf, then use
751 		 * the default short name and nullify pathptr; also, since
752 		 * we prepend in the buffer, we must avoid adding a null char
753 		 */
754 		if (curnode != node) {
755 			pathptr -= seplen;
756 			if (pathptr < pathbuf) {
757 				pathptr = pathbuf;
758 				*pathptr = '\0';
759 				goto DEF;
760 			}
761 			(void) memcpy(pathptr, AP_PATH_SEP, seplen);
762 		}
763 		len = strlen(buf);
764 		pathptr -= len;
765 		if (pathptr < pathbuf) {
766 			pathptr = pathbuf;
767 			*pathptr = '\0';
768 			goto DEF;
769 		}
770 		(void) memcpy(pathptr, buf, len);
771 
772 		/* remember the leaf component */
773 		if (curnode == node)
774 			pathend = pathptr;
775 
776 		/*
777 		 * go no further than the hosts' onboard slots
778 		 */
779 		if (chasnode == DI_NODE_NIL)
780 			break;
781 		curnode = chasnode;
782 
783 		/*
784 		 * the pci device number of the current node is used to
785 		 * identify which slot of the parent's bus (next iteration)
786 		 * the current node is on
787 		 */
788 		pci_dev = pci_cfg_pcidev(curnode, ph);
789 		if (pci_dev == PCIDEV_NIL) {
790 			dprint(("%s: cannot obtain pci device number "
791 			    "for %s%d\n", fnm, DRVINST(node)));
792 			*pathptr = '\0';
793 			goto OUT;
794 		}
795 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
796 
797 	pathbuf[sizeof (pathbuf) - 1] = '\0';
798 	if (strlen(pathptr) < ap_pathsz) {
799 		(void) strlcpy(ap_path, pathptr, ap_pathsz);
800 		rv = 1;
801 		goto OUT;
802 	}
803 
804 DEF:
805 	/*
806 	 * when our name won't fit <ap_pathsz> we use the endpoint/leaf
807 	 * <node>'s name ONLY IF it has a serialid# which will make the apid
808 	 * globally unique
809 	 */
810 	if (chasflag && pathend != NULL) {
811 		if ((strncmp(pathend + iob_pre_len, AP_PATH_IOB_SEP,
812 		    ap_path_iob_sep_len) != 0) &&
813 		    (strlen(pathend) < ap_pathsz)) {
814 			(void) strlcpy(ap_path, pathend, ap_pathsz);
815 			rv = 1;
816 			goto OUT;
817 		}
818 	}
819 
820 	/*
821 	 * if our name still won't fit <ap_pathsz>, then use the leaf <node>'s
822 	 * default name
823 	 */
824 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
825 	rv = pci_cfg_ap_node(pci_dev, node, ph, buf, bufsz, APNODE_DEFNAME);
826 	if (rv == 0) {
827 		dprint(("%s: cannot create default ap node name for %s%d\n",
828 		    fnm, DRVINST(node)));
829 		*pathptr = '\0';
830 		goto OUT;
831 	}
832 	if (strlen(buf) < ap_pathsz) {
833 		(void) strlcpy(ap_path, buf, ap_pathsz);
834 		rv = 1;
835 		goto OUT;
836 	}
837 
838 	/*
839 	 * in this case, cfgadm goes through an expensive process to generate
840 	 * a purely dynamic logical apid: the framework will look through
841 	 * the device tree for attachment point minor nodes and will invoke
842 	 * each plugin responsible for that attachment point class, and if
843 	 * the plugin returns a logical apid that matches the queried apid
844 	 * or matches the default apid generated by the cfgadm framework for
845 	 * that driver/class (occurs when plugin returns an empty logical apid)
846 	 * then that is what it will use
847 	 *
848 	 * it is doubly expensive because the cfgadm pci plugin itself will
849 	 * also search the entire device tree in the absence of a link
850 	 */
851 	rv = 0;
852 	dprint(("%s: cannot create apid for %s%d within length of %d\n",
853 	    fnm, DRVINST(node), ap_pathsz));
854 
855 OUT:
856 	ap_path[ap_pathsz - 1] = '\0';
857 	*pathret = (*pathptr == '\0') ? NULL : strdup(pathptr);
858 	return (rv);
859 
860 #undef	seplen
861 #undef	iob_pre_len
862 #undef	ap_path_iob_sep_len
863 }
864 
865 
866 /*
867  * the DI_PROP_AP_NAMES property contains the first integer section of the
868  * ieee1275 "slot-names" property and functions as a bitmask; see comment for
869  * pci_cfg_slotname()
870  *
871  * we use the name of the attachment point minor node if its pci device
872  * number (encoded in the minor number) is allowed by DI_PROP_AP_NAMES
873  *
874  * returns non-zero if we return a valid attachment point through <path>
875  */
876 static int
877 pci_cfg_ap_legacy(di_minor_t minor, di_node_t node, di_prom_handle_t ph,
878     char *ap_path, int ap_pathsz)
879 {
880 	minor_t pci_dev;
881 	int *anp;
882 
883 	if (di_propall_lookup_ints(ph, 0, DDI_DEV_T_ANY, node, DI_PROP_AP_NAMES,
884 	    &anp) < 1)
885 		return (0);
886 
887 	pci_dev = PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(di_minor_devt(minor));
888 	if ((*anp & (1 << pci_dev)) == 0)
889 		return (0);
890 
891 	(void) strlcpy(ap_path, di_minor_name(minor), ap_pathsz);
892 	return (1);
893 }
894 
895 
896 /*
897  * determine if <node> qualifies for a path style apid
898  */
899 static int
900 pci_cfg_is_ap_path(di_node_t node, di_prom_handle_t ph)
901 {
902 	char *devtype;
903 	di_node_t curnode = node;
904 
905 	do {
906 		if (di_propall_lookup_strings(ph, 0, DDI_DEV_T_ANY, curnode,
907 		    DI_PROP_DEV_TYPE, &devtype) > 0)
908 			if (strcmp(devtype, PROPVAL_PCIEX) == 0)
909 				return (1);
910 	} while ((curnode = di_parent_node(curnode)) != DI_NODE_NIL);
911 
912 	return (0);
913 }
914 
915 
916 /*
917  * takes a full path as returned by <pathret> from pci_cfg_ap_path() and
918  * returns an allocated string intendend to be stored in a devlink info (dli)
919  * file
920  *
921  * data format: "Location: <transformed path>"
922  * where <transformed path> is <path> with occurrances of AP_PATH_SEP
923  * replaced by "/"
924  */
925 static char *
926 pci_cfg_info_data(char *path)
927 {
928 #define	head	"Location: "
929 #define	headlen	(sizeof (head) - 1)
930 #define	seplen	(sizeof (AP_PATH_SEP) - 1)
931 
932 	char *sep, *prev, *np;
933 	char *newpath;
934 	int pathlen = strlen(path);
935 	int len;
936 
937 	newpath = malloc(sizeof (char) * (headlen + pathlen + 1));
938 	np = newpath;
939 	(void) strcpy(np, head);
940 	np += headlen;
941 
942 	prev = path;
943 	while ((sep = strstr(prev, AP_PATH_SEP)) != NULL) {
944 		len = sep - prev;
945 		(void) memcpy(np, prev, len);
946 		np += len;
947 		*np++ = '/';
948 		prev = sep + seplen;
949 	}
950 	(void) strcpy(np, prev);
951 	return (newpath);
952 
953 #undef	head
954 #undef	headlen
955 #undef	seplen
956 }
957 
958 
959 static void
960 pci_cfg_rm_link(char *file)
961 {
962 	char *dlipath;
963 
964 	dlipath = di_dli_name(file);
965 	(void) unlink(dlipath);
966 
967 	devfsadm_rm_all(file);
968 	free(dlipath);
969 }
970 
971 /*
972  * removes all registered devlinks to physical path <physpath> except for
973  * the devlink <valid> if not NULL;
974  * <physpath> must include the minor node
975  */
976 static void
977 pci_cfg_rm_invalid_links(char *physpath, char *valid)
978 {
979 	char **dnp;
980 	char *cp, *vcp;
981 	int i, dnlen;
982 
983 	dnp = devfsadm_lookup_dev_names(physpath, NULL, &dnlen);
984 	if (dnp == NULL)
985 		return;
986 
987 	if (valid != NULL) {
988 		if (strncmp(valid, DEV "/", DEV_LEN + 1) == 0)
989 			vcp = valid + DEV_LEN + 1;
990 		else
991 			vcp = valid;
992 	}
993 
994 	for (i = 0; i < dnlen; i++) {
995 		if (strncmp(dnp[i], DEV "/", DEV_LEN + 1) == 0)
996 			cp = dnp[i] + DEV_LEN + 1;
997 		else
998 			cp = dnp[i];
999 
1000 		if (valid != NULL) {
1001 			if (strcmp(vcp, cp) == 0)
1002 				continue;
1003 		}
1004 		pci_cfg_rm_link(cp);
1005 	}
1006 	devfsadm_free_dev_names(dnp, dnlen);
1007 }
1008 
1009 
1010 /*
1011  * takes a complete devinfo snapshot and returns the root node;
1012  * callers must do a di_fini() on the returned node;
1013  * if the snapshot failed, DI_NODE_NIL is returned instead
1014  *
1015  * if <pci_node> is not DI_NODE_NIL, it will search for the same devinfo node
1016  * in the new snapshot and return it through <ret_node> if it is found,
1017  * else DI_NODE_NIL is returned instead
1018  *
1019  * in addition, if <pci_minor> is not DI_MINOR_NIL, it will also return
1020  * the matching minor in the new snapshot through <ret_minor> if it is found,
1021  * else DI_MINOR_NIL is returned instead
1022  */
1023 static di_node_t
1024 pci_cfg_snapshot(di_node_t pci_node, di_minor_t pci_minor,
1025     di_node_t *ret_node, di_minor_t *ret_minor)
1026 {
1027 	di_node_t root_node;
1028 	di_node_t node;
1029 	di_minor_t minor;
1030 	int pci_inst;
1031 	dev_t pci_devt;
1032 
1033 	*ret_node = DI_NODE_NIL;
1034 	*ret_minor = DI_MINOR_NIL;
1035 
1036 	root_node = di_init("/", DINFOCPYALL);
1037 	if (root_node == DI_NODE_NIL)
1038 		return (DI_NODE_NIL);
1039 
1040 	/*
1041 	 * narrow down search by driver, then instance, then minor
1042 	 */
1043 	if (pci_node == DI_NODE_NIL)
1044 		return (root_node);
1045 
1046 	pci_inst = di_instance(pci_node);
1047 	node = di_drv_first_node(di_driver_name(pci_node), root_node);
1048 	do {
1049 		if (pci_inst == di_instance(node)) {
1050 			*ret_node = node;
1051 			break;
1052 		}
1053 	} while ((node = di_drv_next_node(node)) != DI_NODE_NIL);
1054 
1055 	if (node == DI_NODE_NIL)
1056 		return (root_node);
1057 
1058 	/*
1059 	 * found node, now search minors
1060 	 */
1061 	if (pci_minor == DI_MINOR_NIL)
1062 		return (root_node);
1063 
1064 	pci_devt = di_minor_devt(pci_minor);
1065 	minor = DI_MINOR_NIL;
1066 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1067 		if (pci_devt == di_minor_devt(minor)) {
1068 			*ret_minor = minor;
1069 			break;
1070 		}
1071 	}
1072 	return (root_node);
1073 }
1074 
1075 
1076 static int
1077 pci_cfg_creat_cb(di_minor_t pci_minor, di_node_t pci_node)
1078 {
1079 #ifdef	DEBUG
1080 	char *fnm = "pci_cfg_creat_cb";
1081 #endif
1082 #define	ap_pathsz	(sizeof (ap_path))
1083 
1084 	char ap_path[CFGA_LOG_EXT_LEN];
1085 	char linkbuf[MAXPATHLEN];
1086 	char *fullpath = NULL;
1087 	char *pathinfo = NULL;
1088 	char *devpath = NULL;
1089 	int rv, fd = -1;
1090 	size_t sz;
1091 	di_prom_handle_t ph;
1092 	di_node_t node;
1093 	di_node_t root_node = DI_NODE_NIL;
1094 	di_minor_t minor;
1095 
1096 	ph = di_prom_init();
1097 	if (ph == DI_PROM_HANDLE_NIL) {
1098 		dprint(("%s: di_prom_init() failed for %s%d\n",
1099 		    fnm, DRVINST(pci_node)));
1100 		goto OUT;
1101 	}
1102 
1103 	/*
1104 	 * Since incoming nodes from hotplug events are from snapshots that
1105 	 * do NOT contain parent/ancestor data, we must retake our own
1106 	 * snapshot and search for the target node
1107 	 */
1108 	root_node = pci_cfg_snapshot(pci_node, pci_minor, &node, &minor);
1109 	if (root_node == DI_NODE_NIL || node == DI_NODE_NIL ||
1110 	    minor == DI_MINOR_NIL) {
1111 		dprint(("%s: devinfo snapshot or search failed for %s%d\n",
1112 		    fnm, DRVINST(pci_node)));
1113 		goto OUT;
1114 	}
1115 
1116 	if (pci_cfg_is_ap_path(node, ph)) {
1117 		rv = pci_cfg_ap_path(minor, node, ph, ap_path, ap_pathsz,
1118 		    &fullpath);
1119 		if (rv == 0)
1120 			goto OUT;
1121 
1122 		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1123 		    CFG_DIRNAME, ap_path);
1124 
1125 		/*
1126 		 * We must remove existing links because we may have invalid
1127 		 * apids that are valid links.  Since these are not dangling,
1128 		 * devfsadm will not invoke the remove callback on them.
1129 		 *
1130 		 * What are "invalid apids with valid links"?  Consider swapping
1131 		 * an attachment point bus with another while the system is
1132 		 * down, on the same device path bound to the same drivers
1133 		 * but with the new AP bus having different properties
1134 		 * (e.g. serialid#).  If the previous apid is not removed,
1135 		 * there will now be two different links pointing to the same
1136 		 * attachment point, but only one reflects the correct
1137 		 * logical apid
1138 		 */
1139 		devpath = pci_cfg_devpath(node, minor);
1140 		if (devpath == NULL)
1141 			goto OUT;
1142 		pci_cfg_rm_invalid_links(devpath, linkbuf);
1143 		free(devpath);
1144 
1145 		(void) devfsadm_mklink(linkbuf, node, minor, 0);
1146 
1147 		/*
1148 		 * we store the full logical path of the attachment point for
1149 		 * cfgadm to display in its info field which is useful when
1150 		 * the full logical path exceeds the size limit for logical
1151 		 * apids (CFGA_LOG_EXT_LEN)
1152 		 *
1153 		 * for the cfgadm pci plugin to do the same would be expensive
1154 		 * (i.e. devinfo snapshot + top down exhaustive minor search +
1155 		 * equivalent of pci_cfg_ap_path() on every invocation)
1156 		 *
1157 		 * note that if we do not create a link (pci_cfg_ap_path() is
1158 		 * not successful), that is what cfgadm will do anyways to
1159 		 * create a purely dynamic apid
1160 		 */
1161 		pathinfo = pci_cfg_info_data(fullpath);
1162 		fd = di_dli_openw(linkbuf);
1163 		if (fd < 0)
1164 			goto OUT;
1165 
1166 		sz = strlen(pathinfo) + 1;
1167 		rv = write(fd, pathinfo, sz);
1168 		if (rv < sz) {
1169 			dprint(("%s: could not write full pathinfo to dli "
1170 			    "file for %s%d\n", fnm, DRVINST(node)));
1171 			goto OUT;
1172 		}
1173 		di_dli_close(fd);
1174 	} else {
1175 		rv = pci_cfg_ap_legacy(minor, node, ph, ap_path,
1176 		    ap_pathsz);
1177 		if (rv == 0)
1178 			goto OUT;
1179 
1180 		(void) snprintf(linkbuf, sizeof (linkbuf), "%s/%s",
1181 		    CFG_DIRNAME, ap_path);
1182 		(void) devfsadm_mklink(linkbuf, node, minor, 0);
1183 	}
1184 
1185 OUT:
1186 	if (fd >= 0)
1187 		di_dli_close(fd);
1188 	if (fullpath != NULL)
1189 		free(fullpath);
1190 	if (pathinfo != NULL)
1191 		free(pathinfo);
1192 	if (ph != DI_PROM_HANDLE_NIL)
1193 		di_prom_fini(ph);
1194 	if (root_node != DI_NODE_NIL)
1195 		di_fini(root_node);
1196 	return (DEVFSADM_CONTINUE);
1197 
1198 #undef	ap_pathsz
1199 }
1200 
1201 
1202 static void
1203 pci_cfg_rm_all(char *file)
1204 {
1205 	pci_cfg_rm_link(file);
1206 }
1207 
1208 
1209 /*
1210  * ib_cfg_creat_cb() creates two types of links
1211  * One for the fabric as /dev/cfg/ib
1212  * Another for each HCA seen in the fabric as /dev/cfg/hca:<HCA-GUID>
1213  */
1214 static int
1215 ib_cfg_creat_cb(di_minor_t minor, di_node_t node)
1216 {
1217 	char	*cp;
1218 	char	path[PATH_MAX + 1];
1219 
1220 	if ((cp = di_devfs_path(node)) == NULL) {
1221 		return (DEVFSADM_CONTINUE);
1222 	}
1223 
1224 	(void) snprintf(path, sizeof (path), "%s:%s", cp, di_minor_name(minor));
1225 	di_devfs_path_free(cp);
1226 
1227 	/* create fabric or hca:GUID and the symlink */
1228 	if (strstr(path, "ib:fabric") != NULL) {
1229 		(void) snprintf(path, sizeof (path), "%s/ib", CFG_DIRNAME);
1230 	} else {
1231 		(void) snprintf(path, sizeof (path), "%s/hca:%s", CFG_DIRNAME,
1232 		    di_minor_name(minor));
1233 	}
1234 
1235 	(void) devfsadm_mklink(path, node, minor, 0);
1236 	return (DEVFSADM_CONTINUE);
1237 }
1238 
1239 /*
1240  * This function verifies if the serial id is printable.
1241  */
1242 
1243 static int
1244 serid_printable(uint64_t *seridp)
1245 {
1246 
1247 	char *ptr;
1248 	int i = 0;
1249 
1250 	for (ptr = (char *)seridp+3; i < 5; ptr++, i++)
1251 		if (*ptr < 0x21 || *ptr >= 0x7f)
1252 			return (0);
1253 
1254 	return (1);
1255 
1256 }
1257 
1258 /*
1259  * Create a link for cfgadm that points back to the normal ccid links in
1260  * /dev/ccid.
1261  */
1262 static int
1263 ccid_cfg_creat_cb(di_minor_t minor, di_node_t node)
1264 {
1265 	const char *minor_nm;
1266 	char cfg_path[MAXPATHLEN];
1267 
1268 	if ((minor_nm = di_minor_name(minor)) == NULL) {
1269 		return (DEVFSADM_CONTINUE);
1270 	}
1271 
1272 	(void) snprintf(cfg_path, sizeof (cfg_path), "%s/ccid%d/%s",
1273 	    CFG_DIRNAME, di_instance(node),  minor_nm);
1274 
1275 	(void) devfsadm_mklink(cfg_path, node, minor, 0);
1276 	return (DEVFSADM_CONTINUE);
1277 }
1278