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