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 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 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 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 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 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 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 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 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 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 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 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 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