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