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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <regex.h>
30 #include <devfsadm.h>
31 #include <stdio.h>
32 #include <strings.h>
33 #include <stdlib.h>
34 #include <limits.h>
35 #include <ctype.h>
36 
37 static int lp(di_minor_t minor, di_node_t node);
38 static int serial_dialout(di_minor_t minor, di_node_t node);
39 static int serial(di_minor_t minor, di_node_t node);
40 static int diskette(di_minor_t minor, di_node_t node);
41 static int vt00(di_minor_t minor, di_node_t node);
42 static int kdmouse(di_minor_t minor, di_node_t node);
43 static int bmc(di_minor_t minor, di_node_t node);
44 static int smbios(di_minor_t minor, di_node_t node);
45 static int agp_process(di_minor_t minor, di_node_t node);
46 
47 static devfsadm_create_t misc_cbt[] = {
48 	{ "vt00", "ddi_display", NULL,
49 	    TYPE_EXACT, ILEVEL_0,	vt00
50 	},
51 	{ "mouse", "ddi_mouse", "mouse8042",
52 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse
53 	},
54 	{ "pseudo", "ddi_pseudo", "bmc",
55 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, bmc,
56 	},
57 	{ "pseudo", "ddi_pseudo", "smbios",
58 	    TYPE_EXACT | DRV_EXACT, ILEVEL_1, smbios,
59 	},
60 	{ "disk",  "ddi_block:diskette", NULL,
61 	    TYPE_EXACT, ILEVEL_1, diskette
62 	},
63 	{ "parallel",  "ddi_printer", NULL,
64 	    TYPE_EXACT, ILEVEL_1, lp
65 	},
66 	{ "serial", "ddi_serial:mb", NULL,
67 	    TYPE_EXACT, ILEVEL_1, serial
68 	},
69 	{ "serial",  "ddi_serial:dialout,mb", NULL,
70 	    TYPE_EXACT, ILEVEL_1, serial_dialout
71 	},
72 	{"agp", "ddi_agp:pseudo", NULL,
73 	    TYPE_EXACT, ILEVEL_0, agp_process
74 	},
75 	{"agp", "ddi_agp:target", NULL,
76 	    TYPE_EXACT, ILEVEL_0, agp_process
77 	},
78 	{"agp", "ddi_agp:cpugart", NULL,
79 	    TYPE_EXACT, ILEVEL_0, agp_process
80 	},
81 	{"agp", "ddi_agp:master", NULL,
82 	    TYPE_EXACT, ILEVEL_0, agp_process
83 	}
84 };
85 
86 DEVFSADM_CREATE_INIT_V0(misc_cbt);
87 
88 static char *debug_mid = "agp_mid";
89 
90 typedef enum {
91 	DRIVER_AGPPSEUDO = 0,
92 	DRIVER_AGPTARGET,
93 	DRIVER_CPUGART,
94 	DRIVER_AGPMASTER,
95 	DRIVER_UNKNOWN
96 } driver_defs_t;
97 
98 typedef struct {
99 	char	*driver_name;
100 	int	index;
101 } driver_name_table_entry_t;
102 
103 static driver_name_table_entry_t driver_name_table[] = {
104 	{ "agpgart",		DRIVER_AGPPSEUDO },
105 	{ "agptarget",		DRIVER_AGPTARGET },
106 	{ "amd64_gart",		DRIVER_CPUGART },
107 	{ "vgatext",		DRIVER_AGPMASTER },
108 	{ NULL,			DRIVER_UNKNOWN }
109 };
110 
111 static devfsadm_enumerate_t agptarget_rules[1] =
112 	{ "^agp$/^agptarget([0-9]+)$", 1, MATCH_ALL };
113 static devfsadm_enumerate_t cpugart_rules[1] =
114 	{ "^agp$/^cpugart([0-9]+)$", 1, MATCH_ALL };
115 static devfsadm_enumerate_t agpmaster_rules[1] =
116 	{  "^agp$/^agpmaster([0-9]+)$", 1, MATCH_ALL };
117 
118 static devfsadm_remove_t misc_remove_cbt[] = {
119 	{ "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS,
120 		ILEVEL_0, devfsadm_rm_all
121 	}
122 };
123 
124 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt);
125 
126 /*
127  * Handles minor node type "ddi_display", in addition to generic processing
128  * done by display().
129  *
130  * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility.
131  */
132 /* ARGSUSED */
133 int
134 vt00(di_minor_t minor, di_node_t node)
135 {
136 	(void) devfsadm_secondary_link("vt00", "fb", 0);
137 	return (DEVFSADM_CONTINUE);
138 }
139 
140 /*
141  * type=ddi_block:diskette;addr=0,0;minor=c        diskette
142  * type=ddi_block:diskette;addr=0,0;minor=c,raw    rdiskette
143  * type=ddi_block:diskette;addr1=0;minor=c diskette\A2
144  * type=ddi_block:diskette;addr1=0;minor=c,raw     rdiskette\A2
145  */
146 static int
147 diskette(di_minor_t minor, di_node_t node)
148 {
149 	char *a2;
150 	char link[PATH_MAX];
151 	char *addr = di_bus_addr(node);
152 	char *mn = di_minor_name(minor);
153 
154 	if (strcmp(addr, "0,0") == 0) {
155 		if (strcmp(mn, "c") == 0) {
156 			(void) devfsadm_mklink("diskette", node, minor, 0);
157 		} else if (strcmp(mn, "c,raw") == 0) {
158 			(void) devfsadm_mklink("rdiskette", node, minor, 0);
159 		}
160 
161 	}
162 
163 	if (addr[0] == '0') {
164 		if ((a2 = strchr(addr, ',')) != NULL) {
165 			a2++;
166 			if (strcmp(mn, "c") == 0) {
167 				(void) strcpy(link, "diskette");
168 				(void) strcat(link, a2);
169 				(void) devfsadm_mklink(link, node, minor, 0);
170 			} else if (strcmp(mn, "c,raw") == 0) {
171 				(void) strcpy(link, "rdiskette");
172 				(void) strcat(link, a2);
173 				(void) devfsadm_mklink(link, node, minor, 0);
174 			}
175 		}
176 	}
177 
178 	return (DEVFSADM_CONTINUE);
179 }
180 
181 /*
182  * type=ddi_printer;name=lp;addr=1,3bc      lp0
183  * type=ddi_printer;name=lp;addr=1,378      lp1
184  * type=ddi_printer;name=lp;addr=1,278      lp2
185  */
186 static int
187 lp(di_minor_t minor, di_node_t node)
188 {
189 	char *addr = di_bus_addr(node);
190 	char *buf;
191 	char path[PATH_MAX + 1];
192 	devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL};
193 
194 	if (strcmp(addr, "1,3bc") == 0) {
195 		(void) devfsadm_mklink("lp0", node, minor, 0);
196 
197 	} else if (strcmp(addr, "1,378") == 0) {
198 		(void) devfsadm_mklink("lp1", node, minor, 0);
199 
200 	} else if (strcmp(addr, "1,278") == 0) {
201 		(void) devfsadm_mklink("lp2", node, minor, 0);
202 	}
203 
204 	if (strcmp(di_driver_name(node), "ecpp") != 0) {
205 		return (DEVFSADM_CONTINUE);
206 	}
207 
208 	if ((buf = di_devfs_path(node)) == NULL) {
209 		return (DEVFSADM_CONTINUE);
210 	}
211 
212 	(void) snprintf(path, sizeof (path), "%s:%s",
213 	    buf, di_minor_name(minor));
214 
215 	di_devfs_path_free(buf);
216 
217 	if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) {
218 		return (DEVFSADM_CONTINUE);
219 	}
220 
221 	(void) snprintf(path, sizeof (path), "ecpp%s", buf);
222 	free(buf);
223 	(void) devfsadm_mklink(path, node, minor, 0);
224 	return (DEVFSADM_CONTINUE);
225 }
226 
227 /*
228  * type=ddi_serial:mb;minor=a      tty00
229  * type=ddi_serial:mb;minor=b      tty01
230  * type=ddi_serial:mb;minor=c      tty02
231  * type=ddi_serial:mb;minor=d      tty03
232  */
233 static int
234 serial(di_minor_t minor, di_node_t node)
235 {
236 
237 	char *mn = di_minor_name(minor);
238 	char link[PATH_MAX];
239 
240 	(void) strcpy(link, "tty");
241 	(void) strcat(link, mn);
242 	(void) devfsadm_mklink(link, node, minor, 0);
243 
244 	if (strcmp(mn, "a") == 0) {
245 		(void) devfsadm_mklink("tty00", node, minor, 0);
246 
247 	} else if (strcmp(mn, "b") == 0) {
248 		(void) devfsadm_mklink("tty01", node, minor, 0);
249 
250 	} else if (strcmp(mn, "c") == 0) {
251 		(void) devfsadm_mklink("tty02", node, minor, 0);
252 
253 	} else if (strcmp(mn, "d") == 0) {
254 		(void) devfsadm_mklink("tty03", node, minor, 0);
255 	}
256 	return (DEVFSADM_CONTINUE);
257 }
258 
259 /*
260  * type=ddi_serial:dialout,mb;minor=a,cu   ttyd0
261  * type=ddi_serial:dialout,mb;minor=b,cu   ttyd1
262  * type=ddi_serial:dialout,mb;minor=c,cu   ttyd2
263  * type=ddi_serial:dialout,mb;minor=d,cu   ttyd3
264  */
265 static int
266 serial_dialout(di_minor_t minor, di_node_t node)
267 {
268 	char *mn = di_minor_name(minor);
269 
270 	if (strcmp(mn, "a,cu") == 0) {
271 		(void) devfsadm_mklink("ttyd0", node, minor, 0);
272 		(void) devfsadm_mklink("cua0", node, minor, 0);
273 
274 	} else if (strcmp(mn, "b,cu") == 0) {
275 		(void) devfsadm_mklink("ttyd1", node, minor, 0);
276 		(void) devfsadm_mklink("cua1", node, minor, 0);
277 
278 	} else if (strcmp(mn, "c,cu") == 0) {
279 		(void) devfsadm_mklink("ttyd2", node, minor, 0);
280 		(void) devfsadm_mklink("cua2", node, minor, 0);
281 
282 	} else if (strcmp(mn, "d,cu") == 0) {
283 		(void) devfsadm_mklink("ttyd3", node, minor, 0);
284 		(void) devfsadm_mklink("cua3", node, minor, 0);
285 	}
286 	return (DEVFSADM_CONTINUE);
287 }
288 
289 static int
290 kdmouse(di_minor_t minor, di_node_t node)
291 {
292 	(void) devfsadm_mklink("kdmouse", node, minor, 0);
293 	return (DEVFSADM_CONTINUE);
294 }
295 
296 static int
297 bmc(di_minor_t minor, di_node_t node)
298 {
299 	(void) devfsadm_mklink("bmc", node, minor, 0);
300 	return (DEVFSADM_CONTINUE);
301 }
302 
303 static int
304 smbios(di_minor_t minor, di_node_t node)
305 {
306 	(void) devfsadm_mklink("smbios", node, minor, 0);
307 	return (DEVFSADM_CONTINUE);
308 }
309 
310 static int
311 agp_process(di_minor_t minor, di_node_t node)
312 {
313 	char *minor_nm, *drv_nm;
314 	char *devfspath;
315 	char *I_path, *p_path, *buf;
316 	char *name = (char *)NULL;
317 	int i, index;
318 	devfsadm_enumerate_t rules[1];
319 
320 	minor_nm = di_minor_name(minor);
321 	drv_nm = di_driver_name(node);
322 
323 	if ((minor_nm == NULL) || (drv_nm == NULL)) {
324 		return (DEVFSADM_CONTINUE);
325 	}
326 
327 	devfsadm_print(debug_mid, "agp_process: minor=%s node=%s\n",
328 		minor_nm, di_node_name(node));
329 
330 	devfspath = di_devfs_path(node);
331 	if (devfspath == NULL) {
332 		devfsadm_print(debug_mid, "agp_process: devfspath is NULL\n");
333 		return (DEVFSADM_CONTINUE);
334 	}
335 
336 	I_path = (char *)malloc(PATH_MAX);
337 
338 	if (I_path == NULL) {
339 		di_devfs_path_free(devfspath);
340 		devfsadm_print(debug_mid,  "agp_process: malloc failed\n");
341 		return (DEVFSADM_CONTINUE);
342 	}
343 
344 	p_path = (char *)malloc(PATH_MAX);
345 
346 	if (p_path == NULL) {
347 		devfsadm_print(debug_mid,  "agp_process: malloc failed\n");
348 		di_devfs_path_free(devfspath);
349 		free(I_path);
350 		return (DEVFSADM_CONTINUE);
351 	}
352 
353 	(void) strlcpy(p_path, devfspath, PATH_MAX);
354 	(void) strlcat(p_path, ":", PATH_MAX);
355 	(void) strlcat(p_path, minor_nm, PATH_MAX);
356 	di_devfs_path_free(devfspath);
357 
358 	devfsadm_print(debug_mid, "agp_process: path %s\n", p_path);
359 
360 	for (i = 0; ; i++) {
361 		if ((driver_name_table[i].driver_name == NULL) ||
362 		    (strcmp(drv_nm, driver_name_table[i].driver_name) == 0)) {
363 			index = driver_name_table[i].index;
364 			break;
365 		}
366 	}
367 	switch (index) {
368 	case DRIVER_AGPPSEUDO:
369 		devfsadm_print(debug_mid,
370 			    "agp_process: psdeudo driver name\n");
371 		name = "agpgart";
372 		(void) snprintf(I_path, PATH_MAX, "%s", name);
373 		devfsadm_print(debug_mid,
374 		    "mklink %s -> %s\n", I_path, p_path);
375 
376 		(void) devfsadm_mklink(I_path, node, minor, 0);
377 
378 		free(I_path);
379 		free(p_path);
380 		return (DEVFSADM_CONTINUE);
381 	case DRIVER_AGPTARGET:
382 		devfsadm_print(debug_mid,
383 			    "agp_process: target driver name\n");
384 		rules[0] = agptarget_rules[0];
385 		name = "agptarget";
386 		break;
387 	case DRIVER_CPUGART:
388 		devfsadm_print(debug_mid,
389 			    "agp_process: cpugart driver name\n");
390 		rules[0] = cpugart_rules[0];
391 		name = "cpugart";
392 		break;
393 	case DRIVER_AGPMASTER:
394 		devfsadm_print(debug_mid,
395 			    "agp_process: agpmaster driver name\n");
396 		rules[0] = agpmaster_rules[0];
397 		name = "agpmaster";
398 		break;
399 	case DRIVER_UNKNOWN:
400 		devfsadm_print(debug_mid,
401 			    "agp_process: unknown driver name=%s\n", drv_nm);
402 		free(I_path);
403 		free(p_path);
404 		return (DEVFSADM_CONTINUE);
405 	}
406 
407 	if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) {
408 		devfsadm_print(debug_mid, "agp_process: exit/coninue\n");
409 		free(I_path);
410 		free(p_path);
411 		return (DEVFSADM_CONTINUE);
412 	}
413 
414 
415 	(void) snprintf(I_path, PATH_MAX, "agp/%s%s", name, buf);
416 
417 	devfsadm_print(debug_mid, "agp_process: p_path=%s buf=%s\n",
418 		    p_path, buf);
419 
420 	free(buf);
421 
422 	devfsadm_print(debug_mid, "mklink %s -> %s\n", I_path, p_path);
423 
424 	(void) devfsadm_mklink(I_path, node, minor, 0);
425 
426 	free(p_path);
427 	free(I_path);
428 
429 	return (DEVFSADM_CONTINUE);
430 }
431