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