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