Home | History | Annotate | Download | only in devfsadm
      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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <regex.h>
     27 #include <devfsadm.h>
     28 #include <stdio.h>
     29 #include <strings.h>
     30 #include <stdlib.h>
     31 #include <limits.h>
     32 #include <sys/zone.h>
     33 #include <sys/zcons.h>
     34 #include <sys/cpuid_drv.h>
     35 
     36 static int display(di_minor_t minor, di_node_t node);
     37 static int parallel(di_minor_t minor, di_node_t node);
     38 static int node_slash_minor(di_minor_t minor, di_node_t node);
     39 static int driver_minor(di_minor_t minor, di_node_t node);
     40 static int node_name(di_minor_t minor, di_node_t node);
     41 static int minor_name(di_minor_t minor, di_node_t node);
     42 static int wifi_minor_name(di_minor_t minor, di_node_t node);
     43 static int conskbd(di_minor_t minor, di_node_t node);
     44 static int consms(di_minor_t minor, di_node_t node);
     45 static int power_button(di_minor_t minor, di_node_t node);
     46 static int fc_port(di_minor_t minor, di_node_t node);
     47 static int printer_create(di_minor_t minor, di_node_t node);
     48 static int se_hdlc_create(di_minor_t minor, di_node_t node);
     49 static int ppm(di_minor_t minor, di_node_t node);
     50 static int gpio(di_minor_t minor, di_node_t node);
     51 static int av_create(di_minor_t minor, di_node_t node);
     52 static int tsalarm_create(di_minor_t minor, di_node_t node);
     53 static int ntwdt_create(di_minor_t minor, di_node_t node);
     54 static int zcons_create(di_minor_t minor, di_node_t node);
     55 static int cpuid(di_minor_t minor, di_node_t node);
     56 static int glvc(di_minor_t minor, di_node_t node);
     57 static int ses_callback(di_minor_t minor, di_node_t node);
     58 static int kmdrv_create(di_minor_t minor, di_node_t node);
     59 
     60 static devfsadm_create_t misc_cbt[] = {
     61 	{ "pseudo", "ddi_pseudo", "(^sad$)",
     62 	    TYPE_EXACT | DRV_RE, ILEVEL_0, node_slash_minor
     63 	},
     64 	{ "pseudo", "ddi_pseudo", "zsh",
     65 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, driver_minor
     66 	},
     67 	{ "network", "ddi_network", NULL,
     68 	    TYPE_EXACT, ILEVEL_0, minor_name
     69 	},
     70 	{ "wifi", "ddi_network:wifi", NULL,
     71 	    TYPE_EXACT, ILEVEL_0, wifi_minor_name
     72 	},
     73 	{ "display", "ddi_display", NULL,
     74 	    TYPE_EXACT, ILEVEL_0, display
     75 	},
     76 	{ "parallel", "ddi_parallel", NULL,
     77 	    TYPE_EXACT, ILEVEL_0, parallel
     78 	},
     79 	{ "enclosure", DDI_NT_SCSI_ENCLOSURE, NULL,
     80 	    TYPE_EXACT, ILEVEL_0, ses_callback
     81 	},
     82 	{ "pseudo", "ddi_pseudo", "(^winlock$)|(^pm$)",
     83 	    TYPE_EXACT | DRV_RE, ILEVEL_0, node_name
     84 	},
     85 	{ "pseudo", "ddi_pseudo", "conskbd",
     86 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, conskbd
     87 	},
     88 	{ "pseudo", "ddi_pseudo", "consms",
     89 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, consms
     90 	},
     91 	{ "pseudo", "ddi_pseudo", "rsm",
     92 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name
     93 	},
     94 	{ "pseudo", "ddi_pseudo",
     95 	    "(^lockstat$)|(^SUNW,rtvc$)|(^vol$)|(^log$)|(^sy$)|"
     96 	    "(^ksyms$)|(^clone$)|(^tl$)|(^tnf$)|(^kstat$)|(^mdesc$)|(^eeprom$)|"
     97 	    "(^ptsl$)|(^mm$)|(^wc$)|(^dump$)|(^cn$)|(^svvslo$)|(^ptm$)|"
     98 	    "(^ptc$)|(^openeepr$)|(^poll$)|(^sysmsg$)|(^random$)|(^trapstat$)|"
     99 	    "(^cryptoadm$)|(^crypto$)|(^pool$)|(^poolctl$)|(^bl$)|(^kmdb$)|"
    100 	    "(^sysevent$)|(^kssl$)|(^physmem$)",
    101 	    TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name
    102 	},
    103 	{ "pseudo", "ddi_pseudo",
    104 	    "(^ip$)|(^tcp$)|(^udp$)|(^icmp$)|(^sctp$)|"
    105 	    "(^ip6$)|(^tcp6$)|(^udp6$)|(^icmp6$)|(^sctp6$)|"
    106 	    "(^rts$)|(^arp$)|(^ipsecah$)|(^ipsecesp$)|(^keysock$)|(^spdsock$)|"
    107 	    "(^nca$)|(^rds$)|(^sdp$)|(^ipnet$)|(^dlpistub$)|(^bpf$)",
    108 	    TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name
    109 	},
    110 	{ "pseudo", "ddi_pseudo",
    111 	    "(^ipf$)|(^ipnat$)|(^ipstate$)|(^ipauth$)|"
    112 	    "(^ipsync$)|(^ipscan$)|(^iplookup$)",
    113 	    TYPE_EXACT | DRV_RE, ILEVEL_0, minor_name,
    114 	},
    115 	{ "pseudo", "ddi_pseudo", "dld",
    116 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, node_name
    117 	},
    118 	{ "pseudo", "ddi_pseudo",
    119 	    "(^kdmouse$)|(^rootprop$)",
    120 	    TYPE_EXACT | DRV_RE, ILEVEL_0, node_name
    121 	},
    122 	{ "pseudo", "ddi_pseudo", "tod",
    123 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, node_name
    124 	},
    125 	{ "pseudo", "ddi_pseudo", "envctrl(two)?",
    126 	    TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name,
    127 	},
    128 	{ "pseudo", "ddi_pseudo", "fcode",
    129 	    TYPE_EXACT | DRV_RE, ILEVEL_0, minor_name,
    130 	},
    131 	{ "power_button", "ddi_power_button", NULL,
    132 	    TYPE_EXACT, ILEVEL_0, power_button,
    133 	},
    134 	{ "FC port", "ddi_ctl:devctl", "fp",
    135 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, fc_port
    136 	},
    137 	{ "printer", "ddi_printer", NULL,
    138 	    TYPE_EXACT, ILEVEL_0, printer_create
    139 	},
    140 	{ "pseudo", "ddi_pseudo", "se",
    141 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, se_hdlc_create
    142 	},
    143 	{ "ppm",  "ddi_ppm", NULL,
    144 	    TYPE_EXACT, ILEVEL_0, ppm
    145 	},
    146 	{ "pseudo", "ddi_pseudo", "gpio_87317",
    147 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, gpio
    148 	},
    149 	{ "pseudo", "ddi_pseudo", "sckmdrv",
    150 	    TYPE_EXACT | DRV_RE, ILEVEL_0, kmdrv_create,
    151 	},
    152 	{ "pseudo", "ddi_pseudo", "oplkmdrv",
    153 	    TYPE_EXACT | DRV_RE, ILEVEL_0, kmdrv_create,
    154 	},
    155 	{ "av", "^ddi_av:(isoch|async)$", NULL,
    156 	    TYPE_RE, ILEVEL_0, av_create,
    157 	},
    158 	{ "pseudo", "ddi_pseudo", "tsalarm",
    159 	    TYPE_EXACT | DRV_RE, ILEVEL_0, tsalarm_create,
    160 	},
    161 	{ "pseudo", "ddi_pseudo", "ntwdt",
    162 	    TYPE_EXACT | DRV_RE, ILEVEL_0, ntwdt_create,
    163 	},
    164 	{ "pseudo", "ddi_pseudo", "daplt",
    165 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name
    166 	},
    167 	{ "pseudo", "ddi_pseudo", "zcons",
    168 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, zcons_create,
    169 	},
    170 	{ "pseudo", "ddi_pseudo", CPUID_DRIVER_NAME,
    171 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, cpuid,
    172 	},
    173 	{ "pseudo", "ddi_pseudo", "glvc",
    174 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, glvc,
    175 	},
    176 	{ "pseudo", "ddi_pseudo", "dm2s",
    177 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name,
    178 	},
    179 	{ "pseudo", "ddi_pseudo", "nsmb",
    180 	    TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name,
    181 	},
    182 	{ "pseudo", "ddi_pseudo", "mem_cache",
    183 	    TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name,
    184 	},
    185 	{ "pseudo", "ddi_pseudo", "fm",
    186 	    TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name,
    187 	},
    188 	{ "pseudo", "ddi_pseudo", "tpm",
    189 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name
    190 	},
    191 };
    192 
    193 DEVFSADM_CREATE_INIT_V0(misc_cbt);
    194 
    195 static devfsadm_remove_t misc_remove_cbt[] = {
    196 	{ "pseudo", "^profile$",
    197 	    RM_PRE | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    198 	},
    199 	{ "pseudo", "^rsm$",
    200 	    RM_PRE | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    201 	},
    202 	{ "printer", "^printers/[0-9]+$",
    203 	    RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    204 	},
    205 	{ "av", "^av/[0-9]+/(async|isoch)$",
    206 	    RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    207 	},
    208 	{ "pseudo", "^daplt$",
    209 	    RM_PRE | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    210 	},
    211 	{ "pseudo", "^zcons/" ZONENAME_REGEXP "/(" ZCONS_MASTER_NAME "|"
    212 		ZCONS_SLAVE_NAME ")$",
    213 	    RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    214 	},
    215 	{ "pseudo", "^" CPUID_SELF_NAME "$", RM_ALWAYS | RM_PRE | RM_HOT,
    216 	    ILEVEL_0, devfsadm_rm_all
    217 	},
    218 	{ "enclosure", "^es/ses[0-9]+$", RM_POST,
    219 		ILEVEL_0, devfsadm_rm_all
    220 	},
    221 	{ "pseudo", "^pfil$",
    222 	    RM_PRE | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    223 	},
    224 	{ "pseudo", "^tpm$",
    225 	    RM_PRE | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
    226 	},
    227 };
    228 
    229 /* Rules for gpio devices */
    230 static devfsadm_enumerate_t gpio_rules[1] =
    231 	{"^gpio([0-9]+)$", 1, MATCH_ALL};
    232 
    233 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt);
    234 
    235 /*
    236  * Handles minor node type "ddi_display".
    237  *
    238  * type=ddi_display fbs/\M0 fb\N0
    239  */
    240 static int
    241 display(di_minor_t minor, di_node_t node)
    242 {
    243 	char l_path[PATH_MAX + 1], contents[PATH_MAX + 1], *buf;
    244 	devfsadm_enumerate_t rules[1] = {"^fb([0-9]+)$", 1, MATCH_ALL};
    245 	char *mn = di_minor_name(minor);
    246 
    247 	/* create fbs/\M0 primary link */
    248 	(void) strcpy(l_path, "fbs/");
    249 	(void) strcat(l_path, mn);
    250 	(void) devfsadm_mklink(l_path, node, minor, 0);
    251 
    252 	/* create fb\N0 which links to fbs/\M0 */
    253 	if (devfsadm_enumerate_int(l_path, 0, &buf, rules, 1)) {
    254 		return (DEVFSADM_CONTINUE);
    255 	}
    256 	(void) strcpy(contents, l_path);
    257 	(void) strcpy(l_path, "fb");
    258 	(void) strcat(l_path, buf);
    259 	free(buf);
    260 	(void) devfsadm_secondary_link(l_path, contents, 0);
    261 	return (DEVFSADM_CONTINUE);
    262 }
    263 
    264 /*
    265  * Handles minor node type "ddi_parallel".
    266  * type=ddi_parallel;name=mcpp     mcpp\N0
    267  */
    268 static int
    269 parallel(di_minor_t minor, di_node_t node)
    270 {
    271 	char path[PATH_MAX + 1], *buf;
    272 	devfsadm_enumerate_t rules[1] = {"mcpp([0-9]+)$", 1, MATCH_ALL};
    273 
    274 
    275 	if (strcmp(di_node_name(node), "mcpp") != 0) {
    276 		return (DEVFSADM_CONTINUE);
    277 	}
    278 
    279 	if (NULL == (buf = di_devfs_path(node))) {
    280 		return (DEVFSADM_CONTINUE);
    281 	}
    282 
    283 	(void) snprintf(path, sizeof (path), "%s:%s",
    284 	    buf, di_minor_name(minor));
    285 
    286 	di_devfs_path_free(buf);
    287 
    288 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) {
    289 		return (DEVFSADM_CONTINUE);
    290 	}
    291 	(void) snprintf(path, sizeof (path), "mcpp%s", buf);
    292 	free(buf);
    293 
    294 	(void) devfsadm_mklink(path, node, minor, 0);
    295 	return (DEVFSADM_CONTINUE);
    296 }
    297 
    298 static int
    299 ses_callback(di_minor_t minor, di_node_t node)
    300 {
    301 	char l_path[PATH_MAX];
    302 	char *buf;
    303 	char *devfspath;
    304 	char p_path[PATH_MAX];
    305 	devfsadm_enumerate_t re[] = {"^es$/^ses([0-9]+)$", 1, MATCH_ALL};
    306 
    307 	/* find devices path -- need to free mem */
    308 	if (NULL == (devfspath = di_devfs_path(node))) {
    309 		return (DEVFSADM_CONTINUE);
    310 	}
    311 
    312 	(void) snprintf(p_path, sizeof (p_path), "%s:%s", devfspath,
    313 	    di_minor_name(minor));
    314 
    315 
    316 	/* find next number to use; buf is an ascii number */
    317 	if (devfsadm_enumerate_int(p_path, 0, &buf, re, 1)) {
    318 		/* free memory */
    319 		di_devfs_path_free(devfspath);
    320 		return (DEVFSADM_CONTINUE);
    321 	}
    322 
    323 	(void) snprintf(l_path, sizeof (l_path), "es/ses%s", buf);
    324 
    325 	(void) devfsadm_mklink(l_path, node, minor, 0);
    326 	/* free memory */
    327 	free(buf);
    328 	di_devfs_path_free(devfspath);
    329 	return (DEVFSADM_CONTINUE);
    330 
    331 }
    332 
    333 static int
    334 node_slash_minor(di_minor_t minor, di_node_t node)
    335 {
    336 
    337 	char path[PATH_MAX + 1];
    338 
    339 	(void) strcpy(path, di_node_name(node));
    340 	(void) strcat(path, "/");
    341 	(void) strcat(path, di_minor_name(minor));
    342 	(void) devfsadm_mklink(path, node, minor, 0);
    343 	return (DEVFSADM_CONTINUE);
    344 }
    345 
    346 static int
    347 driver_minor(di_minor_t minor, di_node_t node)
    348 {
    349 	char path[PATH_MAX + 1];
    350 
    351 	(void) strcpy(path, di_driver_name(node));
    352 	(void) strcat(path, di_minor_name(minor));
    353 	(void) devfsadm_mklink(path, node, minor, 0);
    354 	return (DEVFSADM_CONTINUE);
    355 }
    356 
    357 /*
    358  * Handles links of the form:
    359  * type=ddi_pseudo;name=xyz  \D
    360  */
    361 static int
    362 node_name(di_minor_t minor, di_node_t node)
    363 {
    364 	(void) devfsadm_mklink(di_node_name(node), node, minor, 0);
    365 	return (DEVFSADM_CONTINUE);
    366 }
    367 
    368 /*
    369  * Handles links of the form:
    370  * type=ddi_pseudo;name=xyz  \M0
    371  */
    372 static int
    373 minor_name(di_minor_t minor, di_node_t node)
    374 {
    375 	char *mn = di_minor_name(minor);
    376 
    377 	(void) devfsadm_mklink(mn, node, minor, 0);
    378 	if (strcmp(mn, "icmp") == 0) {
    379 		(void) devfsadm_mklink("rawip", node, minor, 0);
    380 	}
    381 	if (strcmp(mn, "icmp6") == 0) {
    382 		(void) devfsadm_mklink("rawip6", node, minor, 0);
    383 	}
    384 	if (strcmp(mn, "ipf") == 0) {
    385 		(void) devfsadm_mklink("ipl", node, minor, 0);
    386 	}
    387 	return (DEVFSADM_CONTINUE);
    388 }
    389 
    390 /*
    391  * create links at /dev/wifi for wifi minor node
    392  */
    393 static int
    394 wifi_minor_name(di_minor_t minor, di_node_t node)
    395 {
    396 	char buf[256];
    397 	char *mn = di_minor_name(minor);
    398 
    399 	(void) snprintf(buf, sizeof (buf), "%s%s", "wifi/", mn);
    400 	(void) devfsadm_mklink(buf, node, minor, 0);
    401 
    402 	return (DEVFSADM_CONTINUE);
    403 }
    404 
    405 static int
    406 conskbd(di_minor_t minor, di_node_t node)
    407 {
    408 	(void) devfsadm_mklink("kbd", node, minor, 0);
    409 	return (DEVFSADM_CONTINUE);
    410 }
    411 
    412 static int
    413 consms(di_minor_t minor, di_node_t node)
    414 {
    415 	(void) devfsadm_mklink("mouse", node, minor, 0);
    416 	return (DEVFSADM_CONTINUE);
    417 }
    418 
    419 static int
    420 power_button(di_minor_t minor, di_node_t node)
    421 {
    422 	(void) devfsadm_mklink("power_button", node, minor, 0);
    423 	return (DEVFSADM_CONTINUE);
    424 }
    425 
    426 static int
    427 fc_port(di_minor_t minor, di_node_t node)
    428 {
    429 	devfsadm_enumerate_t rules[1] = {"fc/fp([0-9]+)$", 1, MATCH_ALL};
    430 	char *buf, path[PATH_MAX + 1];
    431 	char *ptr;
    432 
    433 	if (NULL == (ptr = di_devfs_path(node))) {
    434 		return (DEVFSADM_CONTINUE);
    435 	}
    436 
    437 	(void) strcpy(path, ptr);
    438 	(void) strcat(path, ":");
    439 	(void) strcat(path, di_minor_name(minor));
    440 
    441 	di_devfs_path_free(ptr);
    442 
    443 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) != 0) {
    444 		return (DEVFSADM_CONTINUE);
    445 	}
    446 
    447 	(void) strcpy(path, "fc/fp");
    448 	(void) strcat(path, buf);
    449 	free(buf);
    450 
    451 	(void) devfsadm_mklink(path, node, minor, 0);
    452 	return (DEVFSADM_CONTINUE);
    453 }
    454 
    455 /*
    456  * Handles:
    457  *	minor node type "ddi_printer".
    458  * 	rules of the form: type=ddi_printer;name=bpp  \M0
    459  */
    460 static int
    461 printer_create(di_minor_t minor, di_node_t node)
    462 {
    463 	char *mn;
    464 	char path[PATH_MAX + 1], *buf;
    465 	devfsadm_enumerate_t rules[1] = {"^printers$/^([0-9]+)$", 1, MATCH_ALL};
    466 
    467 	mn = di_minor_name(minor);
    468 
    469 	if (strcmp(di_driver_name(node), "bpp") == 0) {
    470 		(void) devfsadm_mklink(mn, node, minor, 0);
    471 	}
    472 
    473 	if (NULL == (buf = di_devfs_path(node))) {
    474 		return (DEVFSADM_CONTINUE);
    475 	}
    476 
    477 	(void) snprintf(path, sizeof (path), "%s:%s", buf, mn);
    478 	di_devfs_path_free(buf);
    479 
    480 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) {
    481 		return (DEVFSADM_CONTINUE);
    482 	}
    483 
    484 	(void) snprintf(path, sizeof (path), "printers/%s", buf);
    485 	free(buf);
    486 
    487 	(void) devfsadm_mklink(path, node, minor, 0);
    488 
    489 	return (DEVFSADM_CONTINUE);
    490 }
    491 
    492 /*
    493  * Handles links of the form:
    494  * type=ddi_pseudo;name=se;minor2=hdlc	se_hdlc\N0
    495  * type=ddi_pseudo;name=serial;minor2=hdlc	se_hdlc\N0
    496  */
    497 static int
    498 se_hdlc_create(di_minor_t minor, di_node_t node)
    499 {
    500 	devfsadm_enumerate_t rules[1] = {"^se_hdlc([0-9]+)$", 1, MATCH_ALL};
    501 	char *buf, path[PATH_MAX + 1];
    502 	char *ptr;
    503 	char *mn;
    504 
    505 	mn = di_minor_name(minor);
    506 
    507 	/* minor node should be of the form: "?,hdlc" */
    508 	if (strcmp(mn + 1, ",hdlc") != 0) {
    509 		return (DEVFSADM_CONTINUE);
    510 	}
    511 
    512 	if (NULL == (ptr = di_devfs_path(node))) {
    513 		return (DEVFSADM_CONTINUE);
    514 	}
    515 
    516 	(void) strcpy(path, ptr);
    517 	(void) strcat(path, ":");
    518 	(void) strcat(path, mn);
    519 
    520 	di_devfs_path_free(ptr);
    521 
    522 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1) != 0) {
    523 		return (DEVFSADM_CONTINUE);
    524 	}
    525 
    526 	(void) strcpy(path, "se_hdlc");
    527 	(void) strcat(path, buf);
    528 	free(buf);
    529 
    530 	(void) devfsadm_mklink(path, node, minor, 0);
    531 
    532 	return (DEVFSADM_CONTINUE);
    533 }
    534 
    535 static int
    536 gpio(di_minor_t minor, di_node_t node)
    537 {
    538 	char l_path[PATH_MAX], p_path[PATH_MAX], *buf, *devfspath;
    539 	char *minor_nm, *drvr_nm;
    540 
    541 
    542 	minor_nm = di_minor_name(minor);
    543 	drvr_nm = di_driver_name(node);
    544 	if ((minor_nm == NULL) || (drvr_nm == NULL)) {
    545 		return (DEVFSADM_CONTINUE);
    546 	}
    547 
    548 	devfspath = di_devfs_path(node);
    549 
    550 	(void) strcpy(p_path, devfspath);
    551 	(void) strcat(p_path, ":");
    552 	(void) strcat(p_path, minor_nm);
    553 	di_devfs_path_free(devfspath);
    554 
    555 	/* build the physical path from the components */
    556 	if (devfsadm_enumerate_int(p_path, 0, &buf, gpio_rules, 1)) {
    557 		return (DEVFSADM_CONTINUE);
    558 	}
    559 
    560 	(void) snprintf(l_path, sizeof (l_path), "%s%s", "gpio", buf);
    561 
    562 	free(buf);
    563 
    564 	(void) devfsadm_mklink(l_path, node, minor, 0);
    565 
    566 	return (DEVFSADM_CONTINUE);
    567 }
    568 
    569 /*
    570  * Creates /dev/ppm nodes for Platform Specific PM module
    571  */
    572 static int
    573 ppm(di_minor_t minor, di_node_t node)
    574 {
    575 	(void) devfsadm_mklink("ppm", node, minor, 0);
    576 	return (DEVFSADM_CONTINUE);
    577 }
    578 
    579 /*
    580  * Handles:
    581  *	/dev/av/[0-9]+/(async|isoch)
    582  */
    583 static int
    584 av_create(di_minor_t minor, di_node_t node)
    585 {
    586 	devfsadm_enumerate_t rules[1] = {"^av$/^([0-9]+)$", 1, MATCH_ADDR};
    587 	char	*minor_str;
    588 	char	path[PATH_MAX + 1];
    589 	char	*buf;
    590 
    591 	if ((buf = di_devfs_path(node)) == NULL) {
    592 		return (DEVFSADM_CONTINUE);
    593 	}
    594 
    595 	minor_str = di_minor_name(minor);
    596 	(void) snprintf(path, sizeof (path), "%s:%s", buf, minor_str);
    597 	di_devfs_path_free(buf);
    598 
    599 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) {
    600 		return (DEVFSADM_CONTINUE);
    601 	}
    602 
    603 	(void) snprintf(path, sizeof (path), "av/%s/%s", buf, minor_str);
    604 	free(buf);
    605 
    606 	(void) devfsadm_mklink(path, node, minor, 0);
    607 
    608 	return (DEVFSADM_CONTINUE);
    609 }
    610 
    611 /*
    612  * Creates /dev/lom and /dev/tsalarm:ctl for tsalarm node
    613  */
    614 static int
    615 tsalarm_create(di_minor_t minor, di_node_t node)
    616 {
    617 	char buf[PATH_MAX + 1];
    618 	char *mn = di_minor_name(minor);
    619 
    620 	(void) snprintf(buf, sizeof (buf), "%s%s", di_node_name(node), ":ctl");
    621 
    622 	(void) devfsadm_mklink(mn, node, minor, 0);
    623 	(void) devfsadm_mklink(buf, node, minor, 0);
    624 
    625 	return (DEVFSADM_CONTINUE);
    626 }
    627 
    628 /*
    629  * Creates /dev/ntwdt for ntwdt node
    630  */
    631 static int
    632 ntwdt_create(di_minor_t minor, di_node_t node)
    633 {
    634 	(void) devfsadm_mklink("ntwdt", node, minor, 0);
    635 	return (DEVFSADM_CONTINUE);
    636 }
    637 
    638 static int
    639 zcons_create(di_minor_t minor, di_node_t node)
    640 {
    641 	char	*minor_str;
    642 	char	*zonename;
    643 	char	path[MAXPATHLEN];
    644 
    645 	minor_str = di_minor_name(minor);
    646 
    647 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zonename",
    648 	    &zonename) == -1) {
    649 		return (DEVFSADM_CONTINUE);
    650 	}
    651 
    652 	(void) snprintf(path, sizeof (path), "zcons/%s/%s", zonename,
    653 	    minor_str);
    654 	(void) devfsadm_mklink(path, node, minor, 0);
    655 
    656 	return (DEVFSADM_CONTINUE);
    657 }
    658 
    659 /*
    660  *	/dev/cpu/self/cpuid 	->	/devices/pseudo/cpuid@0:self
    661  */
    662 static int
    663 cpuid(di_minor_t minor, di_node_t node)
    664 {
    665 	(void) devfsadm_mklink(CPUID_SELF_NAME, node, minor, 0);
    666 	return (DEVFSADM_CONTINUE);
    667 }
    668 
    669 /*
    670  * For device
    671  *      /dev/spfma -> /devices/virtual-devices/fma@5:glvc
    672  */
    673 static int
    674 glvc(di_minor_t minor, di_node_t node)
    675 {
    676 	char node_name[MAXNAMELEN + 1];
    677 
    678 	(void) strcpy(node_name, di_node_name(node));
    679 
    680 	if (strncmp(node_name, "fma", 3) == 0) {
    681 		/* Only one fma channel */
    682 		(void) devfsadm_mklink("spfma", node, minor, 0);
    683 	}
    684 	return (DEVFSADM_CONTINUE);
    685 }
    686 
    687 /*
    688  * Handles links of the form:
    689  * type=ddi_pseudo;name=sckmdrv		kmdrv\M0
    690  * type=ddi_pseudo;name=oplkmdrv	kmdrv\M0
    691  */
    692 static int
    693 kmdrv_create(di_minor_t minor, di_node_t node)
    694 {
    695 
    696 	(void) devfsadm_mklink("kmdrv", node, minor, 0);
    697 	return (DEVFSADM_CONTINUE);
    698 }
    699