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