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 mc_node(di_minor_t minor, di_node_t node);
48 static int xsvc(di_minor_t minor, di_node_t node);
49 static int srn(di_minor_t minor, di_node_t node);
50 static int ucode(di_minor_t minor, di_node_t node);
51
52
53 static devfsadm_create_t misc_cbt[] = {
54 { "vt00", "ddi_display", NULL,
55 TYPE_EXACT, ILEVEL_0, vt00
56 },
57 { "mouse", "ddi_mouse", "mouse8042",
58 TYPE_EXACT | DRV_EXACT, ILEVEL_0, kdmouse
59 },
60 { "pseudo", "ddi_pseudo", "ipmi",
61 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ipmi,
62 },
63 { "pseudo", "ddi_pseudo", "smbios",
64 TYPE_EXACT | DRV_EXACT, ILEVEL_1, smbios,
65 },
66 /* floppies share the same class, but not link regex, as hard disks */
67 { "disk", "ddi_block:diskette", NULL,
68 TYPE_EXACT, ILEVEL_1, diskette
69 },
70 { "parallel", "ddi_printer", NULL,
71 TYPE_EXACT, ILEVEL_1, lp
72 },
73 { "serial", "ddi_serial:mb", NULL,
74 TYPE_EXACT, ILEVEL_1, serial
75 },
76 { "serial", "ddi_serial:dialout,mb", NULL,
77 TYPE_EXACT, ILEVEL_1, serial_dialout
78 },
79 { "pseudo", "ddi_pseudo", NULL,
80 TYPE_EXACT, ILEVEL_0, xsvc
81 },
82 { "pseudo", "ddi_pseudo", NULL,
83 TYPE_EXACT, ILEVEL_0, srn
84 },
85 { "memory-controller", "ddi_mem_ctrl", NULL,
86 TYPE_EXACT, ILEVEL_0, mc_node
87 },
88 { "pseudo", "ddi_pseudo", "ucode",
89 TYPE_EXACT | DRV_EXACT, ILEVEL_0, ucode,
90 },
91 };
92
93 DEVFSADM_CREATE_INIT_V0(misc_cbt);
94
95 static devfsadm_remove_t misc_remove_cbt[] = {
96 { "vt", "vt[0-9][0-9]", RM_PRE|RM_ALWAYS,
97 ILEVEL_0, devfsadm_rm_all
98 },
99 { "pseudo", "^ucode$", RM_ALWAYS | RM_PRE | RM_HOT,
100 ILEVEL_0, devfsadm_rm_all
101 },
102 { "mouse", "^kdmouse$", RM_ALWAYS | RM_PRE,
103 ILEVEL_0, devfsadm_rm_all
104 },
105 { "disk", "^(diskette|rdiskette)([0-9]*)$",
106 RM_ALWAYS | RM_PRE, ILEVEL_1, devfsadm_rm_all
107 },
108 { "parallel", "^(lp|ecpp)([0-9]+)$", RM_ALWAYS | RM_PRE,
109 ILEVEL_1, devfsadm_rm_all
110 },
111 { "serial", "^(tty|ttyd)([0-9]+)$", RM_ALWAYS | RM_PRE,
112 ILEVEL_1, devfsadm_rm_all
113 },
114 { "serial", "^tty[a-z]$", RM_ALWAYS | RM_PRE,
115 ILEVEL_1, devfsadm_rm_all
116 }
117 };
118
119 DEVFSADM_REMOVE_INIT_V0(misc_remove_cbt);
120
121 /*
122 * Handles minor node type "ddi_display", in addition to generic processing
123 * done by display().
124 *
125 * This creates a /dev/vt00 link to /dev/fb, for backwards compatibility.
126 */
127 /* ARGSUSED */
128 int
vt00(di_minor_t minor,di_node_t node)129 vt00(di_minor_t minor, di_node_t node)
130 {
131 (void) devfsadm_secondary_link("vt00", "fb", 0);
132 return (DEVFSADM_CONTINUE);
133 }
134
135 /*
136 * type=ddi_block:diskette;addr=0,0;minor=c diskette
137 * type=ddi_block:diskette;addr=0,0;minor=c,raw rdiskette
138 * type=ddi_block:diskette;addr1=0;minor=c diskette\A2
139 * type=ddi_block:diskette;addr1=0;minor=c,raw rdiskette\A2
140 */
141 static int
diskette(di_minor_t minor,di_node_t node)142 diskette(di_minor_t minor, di_node_t node)
143 {
144 int flags = 0;
145 char *a2;
146 char link[PATH_MAX];
147 char *addr = di_bus_addr(node);
148 char *mn = di_minor_name(minor);
149
150 if (system_labeled)
151 flags = DA_ADD|DA_FLOPPY;
152
153 if (strcmp(addr, "0,0") == 0) {
154 if (strcmp(mn, "c") == 0) {
155 (void) devfsadm_mklink("diskette", node, minor, flags);
156 } else if (strcmp(mn, "c,raw") == 0) {
157 (void) devfsadm_mklink("rdiskette", node, minor, flags);
158 }
159
160 }
161
162 if (addr[0] == '0') {
163 if ((a2 = strchr(addr, ',')) != NULL) {
164 a2++;
165 if (strcmp(mn, "c") == 0) {
166 (void) strcpy(link, "diskette");
167 (void) strcat(link, a2);
168 (void) devfsadm_mklink(link, node, minor,
169 flags);
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,
174 flags);
175 }
176 }
177 }
178
179 return (DEVFSADM_CONTINUE);
180 }
181
182 /*
183 * type=ddi_printer;name=lp;addr=1,3bc lp0
184 * type=ddi_printer;name=lp;addr=1,378 lp1
185 * type=ddi_printer;name=lp;addr=1,278 lp2
186 */
187 static int
lp(di_minor_t minor,di_node_t node)188 lp(di_minor_t minor, di_node_t node)
189 {
190 char *addr = di_bus_addr(node);
191 char *buf;
192 char path[PATH_MAX + 1];
193 devfsadm_enumerate_t rules[1] = {"^ecpp([0-9]+)$", 1, MATCH_ALL};
194
195 if (strcmp(addr, "1,3bc") == 0) {
196 (void) devfsadm_mklink("lp0", node, minor, 0);
197
198 } else if (strcmp(addr, "1,378") == 0) {
199 (void) devfsadm_mklink("lp1", node, minor, 0);
200
201 } else if (strcmp(addr, "1,278") == 0) {
202 (void) devfsadm_mklink("lp2", node, minor, 0);
203 }
204
205 if (strcmp(di_driver_name(node), "ecpp") != 0) {
206 return (DEVFSADM_CONTINUE);
207 }
208
209 if ((buf = di_devfs_path(node)) == NULL) {
210 return (DEVFSADM_CONTINUE);
211 }
212
213 (void) snprintf(path, sizeof (path), "%s:%s",
214 buf, di_minor_name(minor));
215
216 di_devfs_path_free(buf);
217
218 if (devfsadm_enumerate_int(path, 0, &buf, rules, 1)) {
219 return (DEVFSADM_CONTINUE);
220 }
221
222 (void) snprintf(path, sizeof (path), "ecpp%s", buf);
223 free(buf);
224 (void) devfsadm_mklink(path, node, minor, 0);
225 return (DEVFSADM_CONTINUE);
226 }
227
228 /*
229 * type=ddi_serial:mb;minor=a tty00
230 * type=ddi_serial:mb;minor=b tty01
231 * type=ddi_serial:mb;minor=c tty02
232 * type=ddi_serial:mb;minor=d tty03
233 */
234 static int
serial(di_minor_t minor,di_node_t node)235 serial(di_minor_t minor, di_node_t node)
236 {
237
238 char *mn = di_minor_name(minor);
239 char link[PATH_MAX];
240
241 (void) strcpy(link, "tty");
242 (void) strcat(link, mn);
243 (void) devfsadm_mklink(link, node, minor, 0);
244
245 if (strcmp(mn, "a") == 0) {
246 (void) devfsadm_mklink("tty00", node, minor, 0);
247
248 } else if (strcmp(mn, "b") == 0) {
249 (void) devfsadm_mklink("tty01", node, minor, 0);
250
251 } else if (strcmp(mn, "c") == 0) {
252 (void) devfsadm_mklink("tty02", node, minor, 0);
253
254 } else if (strcmp(mn, "d") == 0) {
255 (void) devfsadm_mklink("tty03", node, minor, 0);
256 }
257 return (DEVFSADM_CONTINUE);
258 }
259
260 /*
261 * type=ddi_serial:dialout,mb;minor=a,cu ttyd0
262 * type=ddi_serial:dialout,mb;minor=b,cu ttyd1
263 * type=ddi_serial:dialout,mb;minor=c,cu ttyd2
264 * type=ddi_serial:dialout,mb;minor=d,cu ttyd3
265 */
266 static int
serial_dialout(di_minor_t minor,di_node_t node)267 serial_dialout(di_minor_t minor, di_node_t node)
268 {
269 char *mn = di_minor_name(minor);
270
271 if (strcmp(mn, "a,cu") == 0) {
272 (void) devfsadm_mklink("ttyd0", node, minor, 0);
273 (void) devfsadm_mklink("cua0", node, minor, 0);
274
275 } else if (strcmp(mn, "b,cu") == 0) {
276 (void) devfsadm_mklink("ttyd1", node, minor, 0);
277 (void) devfsadm_mklink("cua1", node, minor, 0);
278
279 } else if (strcmp(mn, "c,cu") == 0) {
280 (void) devfsadm_mklink("ttyd2", node, minor, 0);
281 (void) devfsadm_mklink("cua2", node, minor, 0);
282
283 } else if (strcmp(mn, "d,cu") == 0) {
284 (void) devfsadm_mklink("ttyd3", node, minor, 0);
285 (void) devfsadm_mklink("cua3", node, minor, 0);
286 }
287 return (DEVFSADM_CONTINUE);
288 }
289
290 static int
kdmouse(di_minor_t minor,di_node_t node)291 kdmouse(di_minor_t minor, di_node_t node)
292 {
293 (void) devfsadm_mklink("kdmouse", node, minor, 0);
294 return (DEVFSADM_CONTINUE);
295 }
296
297 static int
ipmi(di_minor_t minor,di_node_t node)298 ipmi(di_minor_t minor, di_node_t node)
299 {
300 /*
301 * Follow convention from other systems, and include an instance#,
302 * even though there will only be one.
303 */
304 (void) devfsadm_mklink("ipmi0", node, minor, 0);
305 return (DEVFSADM_CONTINUE);
306 }
307
308 static int
smbios(di_minor_t minor,di_node_t node)309 smbios(di_minor_t minor, di_node_t node)
310 {
311 (void) devfsadm_mklink("smbios", node, minor, 0);
312 return (DEVFSADM_CONTINUE);
313 }
314
315 /*
316 * /dev/mc/mc<chipid> -> /devices/.../pci1022,1102@<chipid+24>,2:mc-amd
317 */
318 static int
mc_node(di_minor_t minor,di_node_t node)319 mc_node(di_minor_t minor, di_node_t node)
320 {
321 const char *minorname = di_minor_name(minor);
322 const char *busaddr = di_bus_addr(node);
323 char linkpath[PATH_MAX];
324 int unitaddr;
325 char *c;
326
327 if (minorname == NULL || busaddr == NULL)
328 return (DEVFSADM_CONTINUE);
329
330 errno = 0;
331 unitaddr = strtol(busaddr, &c, 16);
332
333 if (errno != 0)
334 return (DEVFSADM_CONTINUE);
335
336 if (unitaddr == 0) {
337 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc");
338 } else if (unitaddr >= MC_AMD_DEV_OFFSET) {
339 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u",
340 unitaddr - MC_AMD_DEV_OFFSET);
341 } else {
342 (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u",
343 minor->dev_minor);
344 }
345 (void) devfsadm_mklink(linkpath, node, minor, 0);
346 return (DEVFSADM_CONTINUE);
347 }
348
349 /*
350 * Creates \M0 devlink for xsvc node
351 */
352 static int
xsvc(di_minor_t minor,di_node_t node)353 xsvc(di_minor_t minor, di_node_t node)
354 {
355 char *mn;
356
357 if (strcmp(di_node_name(node), "xsvc") != 0)
358 return (DEVFSADM_CONTINUE);
359
360 mn = di_minor_name(minor);
361 if (mn == NULL)
362 return (DEVFSADM_CONTINUE);
363
364 (void) devfsadm_mklink(mn, node, minor, 0);
365 return (DEVFSADM_CONTINUE);
366 }
367
368 /*
369 * Creates \M0 devlink for srn device
370 */
371 static int
srn(di_minor_t minor,di_node_t node)372 srn(di_minor_t minor, di_node_t node)
373 {
374 char *mn;
375
376 if (strcmp(di_node_name(node), "srn") != 0)
377 return (DEVFSADM_CONTINUE);
378
379 mn = di_minor_name(minor);
380 if (mn == NULL)
381 return (DEVFSADM_CONTINUE);
382
383 (void) devfsadm_mklink(mn, node, minor, 0);
384 return (DEVFSADM_CONTINUE);
385 }
386
387 /*
388 * /dev/ucode -> /devices/pseudo/ucode@0:ucode
389 */
390 static int
ucode(di_minor_t minor,di_node_t node)391 ucode(di_minor_t minor, di_node_t node)
392 {
393 (void) devfsadm_mklink("ucode", node, minor, 0);
394 return (DEVFSADM_CONTINUE);
395 }
396