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