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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2012 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 <ctype.h> 34 #include <sys/mc_amd.h> 35 #include <bsm/devalloc.h> 36 37 extern int system_labeled; 38 39 static int lp(di_minor_t minor, di_node_t node); 40 static int serial_dialout(di_minor_t minor, di_node_t node); 41 static int serial(di_minor_t minor, di_node_t node); 42 static int diskette(di_minor_t minor, di_node_t node); 43 static int vt00(di_minor_t minor, di_node_t node); 44 static int kdmouse(di_minor_t minor, di_node_t node); 45 static int ipmi(di_minor_t minor, di_node_t node); 46 static int smbios(di_minor_t minor, di_node_t node); 47 static int agp_process(di_minor_t minor, di_node_t node); 48 static int drm_node(di_minor_t minor, di_node_t node); 49 static int mc_node(di_minor_t minor, di_node_t node); 50 static int xsvc(di_minor_t minor, di_node_t node); 51 static int srn(di_minor_t minor, di_node_t node); 52 static int ucode(di_minor_t minor, di_node_t node); 53 static int heci(di_minor_t minor, di_node_t node); 54 55 56 static devfsadm_create_t misc_cbt[] = { 57 { "vt00", "ddi_display", NULL, 58 TYPE_EXACT, ILEVEL_0, vt00 59 }, 60 { "drm", "ddi_display:drm", NULL, 61 TYPE_EXACT, ILEVEL_0, drm_node 62 }, 63 { "mouse", "ddi_mouse", "mouse8042", 64 TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse 65 }, 66 { "pseudo", "ddi_pseudo", "ipmi", 67 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ipmi, 68 }, 69 { "pseudo", "ddi_pseudo", "smbios", 70 TYPE_EXACT | DRV_EXACT, ILEVEL_1, smbios, 71 }, 72 /* floppies share the same class, but not link regex, as hard disks */ 73 { "disk", "ddi_block:diskette", NULL, 74 TYPE_EXACT, ILEVEL_1, diskette 75 }, 76 { "parallel", "ddi_printer", NULL, 77 TYPE_EXACT, ILEVEL_1, lp 78 }, 79 { "serial", "ddi_serial:mb", NULL, 80 TYPE_EXACT, ILEVEL_1, serial 81 }, 82 { "serial", "ddi_serial:dialout,mb", NULL, 83 TYPE_EXACT, ILEVEL_1, serial_dialout 84 }, 85 { "agp", "ddi_agp:pseudo", NULL, 86 TYPE_EXACT, ILEVEL_0, agp_process 87 }, 88 { "agp", "ddi_agp:target", NULL, 89 TYPE_EXACT, ILEVEL_0, agp_process 90 }, 91 { "agp", "ddi_agp:cpugart", NULL, 92 TYPE_EXACT, ILEVEL_0, agp_process 93 }, 94 { "agp", "ddi_agp:master", NULL, 95 TYPE_EXACT, ILEVEL_0, agp_process 96 }, 97 { "pseudo", "ddi_pseudo", NULL, 98 TYPE_EXACT, ILEVEL_0, xsvc 99 }, 100 { "pseudo", "ddi_pseudo", NULL, 101 TYPE_EXACT, ILEVEL_0, srn 102 }, 103 { "memory-controller", "ddi_mem_ctrl", NULL, 104 TYPE_EXACT, ILEVEL_0, mc_node 105 }, 106 { "pseudo", "ddi_pseudo", "ucode", 107 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ucode, 108 }, 109 { "pseudo", "ddi_pseudo", "heci", 110 TYPE_EXACT | DRV_EXACT, ILEVEL_0, heci, 111 } 112 }; 113 114 DEVFSADM_CREATE_INIT_V0(misc_cbt); 115 116 static char *debug_mid = "misc_mid"; 117 118 typedef enum { 119 DRIVER_AGPPSEUDO = 0, 120 DRIVER_AGPTARGET, 121 DRIVER_CPUGART, 122 DRIVER_AGPMASTER_DRM_I915, 123 DRIVER_AGPMASTER_DRM_RADEON, 124 DRIVER_AGPMASTER_VGATEXT, 125 DRIVER_UNKNOWN 126 } driver_defs_t; 127 128 typedef struct { 129 char *driver_name; 130 int index; 131 } driver_name_table_entry_t; 132 133 static driver_name_table_entry_t driver_name_table[] = { 134 { "agpgart", DRIVER_AGPPSEUDO }, 135 { "agptarget", DRIVER_AGPTARGET }, 136 { "amd64_gart", DRIVER_CPUGART }, 137 /* AGP master device managed by drm driver */ 138 { "i915", DRIVER_AGPMASTER_DRM_I915 }, 139 { "radeon", DRIVER_AGPMASTER_DRM_RADEON }, 140 { "vgatext", DRIVER_AGPMASTER_VGATEXT }, 141 { NULL, DRIVER_UNKNOWN } 142 }; 143 144 static devfsadm_enumerate_t agptarget_rules[1] = 145 { "^agp$/^agptarget([0-9]+)$", 1, MATCH_ALL }; 146 static devfsadm_enumerate_t cpugart_rules[1] = 147 { "^agp$/^cpugart([0-9]+)$", 1, MATCH_ALL }; 148 static devfsadm_enumerate_t agpmaster_rules[1] = 149 { "^agp$/^agpmaster([0-9]+)$", 1, MATCH_ALL }; 150 151 static devfsadm_remove_t misc_remove_cbt[] = { 152 { "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS, 153 ILEVEL_0, devfsadm_rm_all 154 }, 155 { "pseudo", "^ucode$", RM_ALWAYS | RM_PRE | RM_HOT, 156 ILEVEL_0, devfsadm_rm_all 157 }, 158 { "mouse", "^kdmouse$", RM_ALWAYS | RM_PRE, 159 ILEVEL_0, devfsadm_rm_all 160 }, 161 { "disk", "^(diskette|rdiskette)([0-9]*)$", 162 RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all 163 }, 164 { "parallel", "^(lp|ecpp)([0-9]+)$", RM_ALWAYS | RM_PRE, 165 ILEVEL_1, devfsadm_rm_all 166 }, 167 { "serial", "^(tty|ttyd)([0-9]+)$", RM_ALWAYS | RM_PRE, 168 ILEVEL_1, devfsadm_rm_all 169 }, 170 { "serial", "^tty[a-z]$", RM_ALWAYS | RM_PRE, 171 ILEVEL_1, devfsadm_rm_all 172 } 173 }; 174 175 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt); 176 177 /* 178 * Handles minor node type "ddi_display", in addition to generic processing 179 * done by display(). 180 * 181 * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility. 182 */ 183 /* ARGSUSED */ 184 int 185 vt00(di_minor_t minor, di_node_t node) 186 { 187 (void) devfsadm_secondary_link("vt00", "fb", 0); 188 return (DEVFSADM_CONTINUE); 189 } 190 191 /* 192 * type=ddi_block:diskette;addr=0,0;minor=c diskette 193 * type=ddi_block:diskette;addr=0,0;minor=c,raw rdiskette 194 * type=ddi_block:diskette;addr1=0;minor=c diskette\A2 195 * type=ddi_block:diskette;addr1=0;minor=c,raw rdiskette\A2 196 */ 197 static int 198 diskette(di_minor_t minor, di_node_t node) 199 { 200 int flags = 0; 201 char *a2; 202 char link[PATH_MAX]; 203 char *addr = di_bus_addr(node); 204 char *mn = di_minor_name(minor); 205 206 if (system_labeled) 207 flags = DA_ADD|DA_FLOPPY; 208 209 if (strcmp(addr, "0,0") == 0) { 210 if (strcmp(mn, "c") == 0) { 211 (void) devfsadm_mklink("diskette", node, minor, flags); 212 } else if (strcmp(mn, "c,raw") == 0) { 213 (void) devfsadm_mklink("rdiskette", node, minor, flags); 214 } 215 216 } 217 218 if (addr[0] == '0') { 219 if ((a2 = strchr(addr, ',')) != NULL) { 220 a2++; 221 if (strcmp(mn, "c") == 0) { 222 (void) strcpy(link, "diskette"); 223 (void) strcat(link, a2); 224 (void) devfsadm_mklink(link, node, minor, 225 flags); 226 } else if (strcmp(mn, "c,raw") == 0) { 227 (void) strcpy(link, "rdiskette"); 228 (void) strcat(link, a2); 229 (void) devfsadm_mklink(link, node, minor, 230 flags); 231 } 232 } 233 } 234 235 return (DEVFSADM_CONTINUE); 236 } 237 238 /* 239 * type=ddi_printer;name=lp;addr=1,3bc lp0 240 * type=ddi_printer;name=lp;addr=1,378 lp1 241 * type=ddi_printer;name=lp;addr=1,278 lp2 242 */ 243 static int 244 lp(di_minor_t minor, di_node_t node) 245 { 246 char *addr = di_bus_addr(node); 247 char *buf; 248 char path[PATH_MAX + 1]; 249 devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL}; 250 251 if (strcmp(addr, "1,3bc") == 0) { 252 (void) devfsadm_mklink("lp0", node, minor, 0); 253 254 } else if (strcmp(addr, "1,378") == 0) { 255 (void) devfsadm_mklink("lp1", node, minor, 0); 256 257 } else if (strcmp(addr, "1,278") == 0) { 258 (void) devfsadm_mklink("lp2", node, minor, 0); 259 } 260 261 if (strcmp(di_driver_name(node), "ecpp") != 0) { 262 return (DEVFSADM_CONTINUE); 263 } 264 265 if ((buf = di_devfs_path(node)) == NULL) { 266 return (DEVFSADM_CONTINUE); 267 } 268 269 (void) snprintf(path, sizeof (path), "%s:%s", 270 buf, di_minor_name(minor)); 271 272 di_devfs_path_free(buf); 273 274 if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) { 275 return (DEVFSADM_CONTINUE); 276 } 277 278 (void) snprintf(path, sizeof (path), "ecpp%s", buf); 279 free(buf); 280 (void) devfsadm_mklink(path, node, minor, 0); 281 return (DEVFSADM_CONTINUE); 282 } 283 284 /* 285 * type=ddi_serial:mb;minor=a tty00 286 * type=ddi_serial:mb;minor=b tty01 287 * type=ddi_serial:mb;minor=c tty02 288 * type=ddi_serial:mb;minor=d tty03 289 */ 290 static int 291 serial(di_minor_t minor, di_node_t node) 292 { 293 294 char *mn = di_minor_name(minor); 295 char link[PATH_MAX]; 296 297 (void) strcpy(link, "tty"); 298 (void) strcat(link, mn); 299 (void) devfsadm_mklink(link, node, minor, 0); 300 301 if (strcmp(mn, "a") == 0) { 302 (void) devfsadm_mklink("tty00", node, minor, 0); 303 304 } else if (strcmp(mn, "b") == 0) { 305 (void) devfsadm_mklink("tty01", node, minor, 0); 306 307 } else if (strcmp(mn, "c") == 0) { 308 (void) devfsadm_mklink("tty02", node, minor, 0); 309 310 } else if (strcmp(mn, "d") == 0) { 311 (void) devfsadm_mklink("tty03", node, minor, 0); 312 } 313 return (DEVFSADM_CONTINUE); 314 } 315 316 /* 317 * type=ddi_serial:dialout,mb;minor=a,cu ttyd0 318 * type=ddi_serial:dialout,mb;minor=b,cu ttyd1 319 * type=ddi_serial:dialout,mb;minor=c,cu ttyd2 320 * type=ddi_serial:dialout,mb;minor=d,cu ttyd3 321 */ 322 static int 323 serial_dialout(di_minor_t minor, di_node_t node) 324 { 325 char *mn = di_minor_name(minor); 326 327 if (strcmp(mn, "a,cu") == 0) { 328 (void) devfsadm_mklink("ttyd0", node, minor, 0); 329 (void) devfsadm_mklink("cua0", node, minor, 0); 330 331 } else if (strcmp(mn, "b,cu") == 0) { 332 (void) devfsadm_mklink("ttyd1", node, minor, 0); 333 (void) devfsadm_mklink("cua1", node, minor, 0); 334 335 } else if (strcmp(mn, "c,cu") == 0) { 336 (void) devfsadm_mklink("ttyd2", node, minor, 0); 337 (void) devfsadm_mklink("cua2", node, minor, 0); 338 339 } else if (strcmp(mn, "d,cu") == 0) { 340 (void) devfsadm_mklink("ttyd3", node, minor, 0); 341 (void) devfsadm_mklink("cua3", node, minor, 0); 342 } 343 return (DEVFSADM_CONTINUE); 344 } 345 346 static int 347 kdmouse(di_minor_t minor, di_node_t node) 348 { 349 (void) devfsadm_mklink("kdmouse", node, minor, 0); 350 return (DEVFSADM_CONTINUE); 351 } 352 353 static int 354 ipmi(di_minor_t minor, di_node_t node) 355 { 356 /* 357 * Follow convention from other systems, and include an instance#, 358 * even though there will only be one. 359 */ 360 (void) devfsadm_mklink("ipmi0", node, minor, 0); 361 return (DEVFSADM_CONTINUE); 362 } 363 364 static int 365 smbios(di_minor_t minor, di_node_t node) 366 { 367 (void) devfsadm_mklink("smbios", node, minor, 0); 368 return (DEVFSADM_CONTINUE); 369 } 370 371 static int 372 agp_process(di_minor_t minor, di_node_t node) 373 { 374 char *minor_nm, *drv_nm; 375 char *devfspath; 376 char *I_path, *p_path, *buf; 377 char *name = (char *)NULL; 378 int i, index; 379 devfsadm_enumerate_t rules[1]; 380 381 minor_nm = di_minor_name(minor); 382 drv_nm = di_driver_name(node); 383 384 if ((minor_nm == NULL) || (drv_nm == NULL)) { 385 return (DEVFSADM_CONTINUE); 386 } 387 388 devfsadm_print(debug_mid, "agp_process: minor=%s node=%s\n", 389 minor_nm, di_node_name(node)); 390 391 devfspath = di_devfs_path(node); 392 if (devfspath == NULL) { 393 devfsadm_print(debug_mid, "agp_process: devfspath is NULL\n"); 394 return (DEVFSADM_CONTINUE); 395 } 396 397 I_path = (char *)malloc(PATH_MAX); 398 399 if (I_path == NULL) { 400 di_devfs_path_free(devfspath); 401 devfsadm_print(debug_mid, "agp_process: malloc failed\n"); 402 return (DEVFSADM_CONTINUE); 403 } 404 405 p_path = (char *)malloc(PATH_MAX); 406 407 if (p_path == NULL) { 408 devfsadm_print(debug_mid, "agp_process: malloc failed\n"); 409 di_devfs_path_free(devfspath); 410 free(I_path); 411 return (DEVFSADM_CONTINUE); 412 } 413 414 (void) strlcpy(p_path, devfspath, PATH_MAX); 415 (void) strlcat(p_path, ":", PATH_MAX); 416 (void) strlcat(p_path, minor_nm, PATH_MAX); 417 di_devfs_path_free(devfspath); 418 419 devfsadm_print(debug_mid, "agp_process: path %s\n", p_path); 420 421 for (i = 0; ; i++) { 422 if ((driver_name_table[i].driver_name == NULL) || 423 (strcmp(drv_nm, driver_name_table[i].driver_name) == 0)) { 424 index = driver_name_table[i].index; 425 break; 426 } 427 } 428 switch (index) { 429 case DRIVER_AGPPSEUDO: 430 devfsadm_print(debug_mid, 431 "agp_process: psdeudo driver name\n"); 432 name = "agpgart"; 433 (void) snprintf(I_path, PATH_MAX, "%s", name); 434 devfsadm_print(debug_mid, 435 "mklink %s -> %s\n", I_path, p_path); 436 437 (void) devfsadm_mklink(I_path, node, minor, 0); 438 439 free(I_path); 440 free(p_path); 441 return (DEVFSADM_CONTINUE); 442 case DRIVER_AGPTARGET: 443 devfsadm_print(debug_mid, 444 "agp_process: target driver name\n"); 445 rules[0] = agptarget_rules[0]; 446 name = "agptarget"; 447 break; 448 case DRIVER_CPUGART: 449 devfsadm_print(debug_mid, 450 "agp_process: cpugart driver name\n"); 451 rules[0] = cpugart_rules[0]; 452 name = "cpugart"; 453 break; 454 case DRIVER_AGPMASTER_DRM_I915: 455 case DRIVER_AGPMASTER_DRM_RADEON: 456 case DRIVER_AGPMASTER_VGATEXT: 457 devfsadm_print(debug_mid, 458 "agp_process: agpmaster driver name\n"); 459 rules[0] = agpmaster_rules[0]; 460 name = "agpmaster"; 461 break; 462 case DRIVER_UNKNOWN: 463 devfsadm_print(debug_mid, 464 "agp_process: unknown driver name=%s\n", drv_nm); 465 free(I_path); 466 free(p_path); 467 return (DEVFSADM_CONTINUE); 468 } 469 470 if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) { 471 devfsadm_print(debug_mid, "agp_process: exit/coninue\n"); 472 free(I_path); 473 free(p_path); 474 return (DEVFSADM_CONTINUE); 475 } 476 477 478 (void) snprintf(I_path, PATH_MAX, "agp/%s%s", name, buf); 479 480 devfsadm_print(debug_mid, "agp_process: p_path=%s buf=%s\n", 481 p_path, buf); 482 483 free(buf); 484 485 devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path); 486 487 (void) devfsadm_mklink(I_path, node, minor, 0); 488 489 free(p_path); 490 free(I_path); 491 492 return (DEVFSADM_CONTINUE); 493 } 494 495 static int 496 drm_node(di_minor_t minor, di_node_t node) 497 { 498 char *minor_nm, *drv_nm; 499 char *devfspath; 500 char *I_path, *p_path, *buf; 501 char *name = "card"; 502 503 devfsadm_enumerate_t drm_rules[1] = {"^dri$/^card([0-9]+)$", 1, 504 MATCH_ALL }; 505 506 507 minor_nm = di_minor_name(minor); 508 drv_nm = di_driver_name(node); 509 if ((minor_nm == NULL) || (drv_nm == NULL)) { 510 return (DEVFSADM_CONTINUE); 511 } 512 513 devfsadm_print(debug_mid, "drm_node: minor=%s node=%s type=%s\n", 514 minor_nm, di_node_name(node), di_minor_nodetype(minor)); 515 516 devfspath = di_devfs_path(node); 517 if (devfspath == NULL) { 518 devfsadm_print(debug_mid, "drm_node: devfspath is NULL\n"); 519 return (DEVFSADM_CONTINUE); 520 } 521 522 I_path = (char *)malloc(PATH_MAX); 523 524 if (I_path == NULL) { 525 di_devfs_path_free(devfspath); 526 devfsadm_print(debug_mid, "drm_node: malloc failed\n"); 527 return (DEVFSADM_CONTINUE); 528 } 529 530 p_path = (char *)malloc(PATH_MAX); 531 532 if (p_path == NULL) { 533 devfsadm_print(debug_mid, "drm_node: malloc failed\n"); 534 di_devfs_path_free(devfspath); 535 free(I_path); 536 return (DEVFSADM_CONTINUE); 537 } 538 539 (void) strlcpy(p_path, devfspath, PATH_MAX); 540 (void) strlcat(p_path, ":", PATH_MAX); 541 (void) strlcat(p_path, minor_nm, PATH_MAX); 542 di_devfs_path_free(devfspath); 543 544 devfsadm_print(debug_mid, "drm_node: p_path %s\n", p_path); 545 546 if (devfsadm_enumerate_int(p_path, 0, &buf, drm_rules, 1)) { 547 free(p_path); 548 devfsadm_print(debug_mid, "drm_node: exit/coninue\n"); 549 return (DEVFSADM_CONTINUE); 550 } 551 (void) snprintf(I_path, PATH_MAX, "dri/%s%s", name, buf); 552 553 devfsadm_print(debug_mid, "drm_node: p_path=%s buf=%s\n", 554 p_path, buf); 555 556 free(buf); 557 558 devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path); 559 (void) devfsadm_mklink(I_path, node, minor, 0); 560 561 free(p_path); 562 free(I_path); 563 564 return (0); 565 } 566 567 /* 568 * /dev/mc/mc<chipid> -> /devices/.../pci1022,1102@<chipid+24>,2:mc-amd 569 */ 570 static int 571 mc_node(di_minor_t minor, di_node_t node) 572 { 573 const char *minorname = di_minor_name(minor); 574 const char *busaddr = di_bus_addr(node); 575 char linkpath[PATH_MAX]; 576 int unitaddr; 577 char *c; 578 579 if (minorname == NULL || busaddr == NULL) 580 return (DEVFSADM_CONTINUE); 581 582 errno = 0; 583 unitaddr = strtol(busaddr, &c, 16); 584 585 if (errno != 0) 586 return (DEVFSADM_CONTINUE); 587 588 if (unitaddr == 0) { 589 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc"); 590 } else if (unitaddr >= MC_AMD_DEV_OFFSET) { 591 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", 592 unitaddr - MC_AMD_DEV_OFFSET); 593 } else { 594 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", 595 minor->dev_minor); 596 } 597 (void) devfsadm_mklink(linkpath, node, minor, 0); 598 return (DEVFSADM_CONTINUE); 599 } 600 601 /* 602 * Creates \M0 devlink for xsvc node 603 */ 604 static int 605 xsvc(di_minor_t minor, di_node_t node) 606 { 607 char *mn; 608 609 if (strcmp(di_node_name(node), "xsvc") != 0) 610 return (DEVFSADM_CONTINUE); 611 612 mn = di_minor_name(minor); 613 if (mn == NULL) 614 return (DEVFSADM_CONTINUE); 615 616 (void) devfsadm_mklink(mn, node, minor, 0); 617 return (DEVFSADM_CONTINUE); 618 } 619 620 /* 621 * Creates \M0 devlink for srn device 622 */ 623 static int 624 srn(di_minor_t minor, di_node_t node) 625 { 626 char *mn; 627 628 if (strcmp(di_node_name(node), "srn") != 0) 629 return (DEVFSADM_CONTINUE); 630 631 mn = di_minor_name(minor); 632 if (mn == NULL) 633 return (DEVFSADM_CONTINUE); 634 635 (void) devfsadm_mklink(mn, node, minor, 0); 636 return (DEVFSADM_CONTINUE); 637 } 638 639 /* 640 * /dev/ucode -> /devices/pseudo/ucode@0:ucode 641 */ 642 static int 643 ucode(di_minor_t minor, di_node_t node) 644 { 645 (void) devfsadm_mklink("ucode", node, minor, 0); 646 return (DEVFSADM_CONTINUE); 647 } 648 649 static int 650 heci(di_minor_t minor, di_node_t node) 651 { 652 if (strcmp(di_minor_name(minor), "AMT") == 0) { 653 (void) devfsadm_mklink("heci", node, minor, 0); 654 } 655 return (DEVFSADM_CONTINUE); 656 } 657