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