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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 #include <devfsadm.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <limits.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <strings.h> 34 35 extern char *devfsadm_get_devices_dir(); 36 static int usb_process(di_minor_t minor, di_node_t node); 37 38 static void ugen_create_link(char *p_path, char *node_name, 39 di_node_t node, di_minor_t minor); 40 41 42 /* Rules for creating links */ 43 static devfsadm_create_t usb_cbt[] = { 44 { "usb", NULL, "usb_ac", DRV_EXACT, 45 ILEVEL_0, usb_process }, 46 { "usb", NULL, "usb_as", DRV_EXACT, 47 ILEVEL_0, usb_process }, 48 { "usb", NULL, "ddivs_usbc", DRV_EXACT, 49 ILEVEL_0, usb_process }, 50 { "usb", NULL, "usbvc", DRV_EXACT, 51 ILEVEL_0, usb_process }, 52 { "usb", NULL, "hid", DRV_EXACT, 53 ILEVEL_0, usb_process }, 54 { "usb", NULL, "hwarc", DRV_EXACT, 55 ILEVEL_0, usb_process }, 56 { "usb", NULL, "wusb_ca", DRV_EXACT, 57 ILEVEL_0, usb_process }, 58 { "usb", DDI_NT_NEXUS, "hubd", DRV_EXACT|TYPE_EXACT, 59 ILEVEL_0, usb_process }, 60 { "usb", DDI_NT_NEXUS, "ohci", DRV_EXACT|TYPE_EXACT, 61 ILEVEL_0, usb_process }, 62 { "usb", DDI_NT_NEXUS, "ehci", DRV_EXACT|TYPE_EXACT, 63 ILEVEL_0, usb_process }, 64 { "usb", DDI_NT_NEXUS, "xhci", DRV_EXACT|TYPE_EXACT, 65 ILEVEL_0, usb_process }, 66 { "usb", DDI_NT_SCSI_NEXUS, "scsa2usb", DRV_EXACT|TYPE_EXACT, 67 ILEVEL_0, usb_process }, 68 { "usb", DDI_NT_UGEN, "scsa2usb", DRV_EXACT|TYPE_EXACT, 69 ILEVEL_0, usb_process }, 70 { "usb", DDI_NT_NEXUS, "uhci", DRV_EXACT|TYPE_EXACT, 71 ILEVEL_0, usb_process }, 72 { "usb", DDI_NT_UGEN, "ugen", DRV_EXACT|TYPE_EXACT, 73 ILEVEL_0, usb_process }, 74 { "usb", DDI_NT_NEXUS, "usb_mid", DRV_EXACT|TYPE_EXACT, 75 ILEVEL_0, usb_process }, 76 { "usb", DDI_NT_UGEN, "usb_mid", DRV_EXACT|TYPE_EXACT, 77 ILEVEL_0, usb_process }, 78 { "usb", DDI_NT_PRINTER, "usbprn", DRV_EXACT|TYPE_EXACT, 79 ILEVEL_0, usb_process }, 80 { "usb", DDI_NT_UGEN, "usbprn", DRV_EXACT|TYPE_EXACT, 81 ILEVEL_0, usb_process }, 82 { "usb", DDI_NT_NEXUS, "hwahc", DRV_EXACT|TYPE_EXACT, 83 ILEVEL_0, usb_process }, 84 }; 85 86 /* For debug printing (-V filter) */ 87 static char *debug_mid = "usb_mid"; 88 89 DEVFSADM_CREATE_INIT_V0(usb_cbt); 90 91 /* USB device links */ 92 #define USB_LINK_RE_AUDIO "^usb/audio[0-9]+$" 93 #define USB_LINK_RE_AUDIOMUX "^usb/audio-mux[0-9]+$" 94 #define USB_LINK_RE_AUDIOCTL "^usb/audio-control[0-9]+$" 95 #define USB_LINK_RE_AUDIOSTREAM "^usb/audio-stream[0-9]+$" 96 #define USB_LINK_RE_DDIVS_USBC "^usb/ddivs_usbc[0-9]+$" 97 #define USB_LINK_RE_VIDEO "^usb/video[0-9]+$" 98 #define USB_LINK_RE_VIDEO2 "^video[0-9]+$" 99 #define USB_LINK_RE_DEVICE "^usb/device[0-9]+$" 100 #define USB_LINK_RE_HID "^usb/hid[0-9]+$" 101 #define USB_LINK_RE_HUB "^usb/hub[0-9]+$" 102 #define USB_LINK_RE_MASS_STORE "^usb/mass-storage[0-9]+$" 103 #define USB_LINK_RE_UGEN "^usb/[0-9,a-f]+\\.[0-9,a-f]+/[0-9]+/.+$" 104 #define USB_LINK_RE_USBPRN "^usb/printer[0-9]+$" 105 #define USB_LINK_RE_WHOST "^usb/whost[0-9]+$" 106 #define USB_LINK_RE_HWARC "^usb/hwarc[0-9]+$" 107 #define USB_LINK_RE_WUSB_CA "^usb/wusb_ca[0-9]+$" 108 109 /* Rules for removing links */ 110 static devfsadm_remove_t usb_remove_cbt[] = { 111 { "usb", USB_LINK_RE_AUDIO, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 112 devfsadm_rm_all }, 113 { "usb", USB_LINK_RE_AUDIOMUX, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 114 devfsadm_rm_all }, 115 { "usb", USB_LINK_RE_AUDIOCTL, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 116 devfsadm_rm_all }, 117 { "usb", USB_LINK_RE_AUDIOSTREAM, RM_POST | RM_HOT | RM_ALWAYS, 118 ILEVEL_0, devfsadm_rm_all }, 119 { "usb", USB_LINK_RE_DDIVS_USBC, RM_POST | RM_HOT | RM_ALWAYS, 120 ILEVEL_0, devfsadm_rm_all }, 121 { "usb", USB_LINK_RE_VIDEO2, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 122 devfsadm_rm_all }, 123 { "usb", USB_LINK_RE_VIDEO, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 124 devfsadm_rm_all }, 125 { "usb", USB_LINK_RE_DEVICE, RM_POST | RM_HOT, ILEVEL_0, 126 devfsadm_rm_all }, 127 { "usb", USB_LINK_RE_HID, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 128 devfsadm_rm_all }, 129 { "usb", USB_LINK_RE_HUB, RM_POST | RM_HOT, ILEVEL_0, devfsadm_rm_all }, 130 { "usb", USB_LINK_RE_MASS_STORE, RM_POST | RM_HOT | RM_ALWAYS, 131 ILEVEL_0, devfsadm_rm_all }, 132 { "usb", USB_LINK_RE_UGEN, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 133 devfsadm_rm_all }, 134 { "usb", USB_LINK_RE_USBPRN, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 135 devfsadm_rm_link }, 136 { "usb", USB_LINK_RE_WHOST, RM_POST | RM_HOT, ILEVEL_0, 137 devfsadm_rm_all }, 138 { "usb", USB_LINK_RE_HWARC, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 139 devfsadm_rm_all }, 140 { "usb", USB_LINK_RE_WUSB_CA, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0, 141 devfsadm_rm_all } 142 }; 143 144 /* 145 * Rules for different USB devices except ugen which is dynamically 146 * created 147 */ 148 static devfsadm_enumerate_t audio_rules[1] = 149 {"^usb$/^audio([0-9]+)$", 1, MATCH_ALL}; 150 static devfsadm_enumerate_t audio_mux_rules[1] = 151 {"^usb$/^audio-mux([0-9]+)$", 1, MATCH_ALL}; 152 static devfsadm_enumerate_t audio_control_rules[1] = 153 {"^usb$/^audio-control([0-9]+)$", 1, MATCH_ALL}; 154 static devfsadm_enumerate_t audio_stream_rules[1] = 155 {"^usb$/^audio-stream([0-9]+)$", 1, MATCH_ALL}; 156 static devfsadm_enumerate_t ddivs_usbc_rules[1] = 157 {"^usb$/^ddivs_usbc([0-9]+)$", 1, MATCH_ALL}; 158 static devfsadm_enumerate_t video_rules[1] = 159 {"^usb$/^video([0-9]+)$", 1, MATCH_ALL}; 160 static devfsadm_enumerate_t device_rules[1] = 161 {"^usb$/^device([0-9]+)$", 1, MATCH_ALL}; 162 static devfsadm_enumerate_t hid_rules[1] = 163 {"^usb$/^hid([0-9]+)$", 1, MATCH_ALL}; 164 static devfsadm_enumerate_t hub_rules[1] = 165 {"^usb$/^hub([0-9]+)$", 1, MATCH_ALL}; 166 static devfsadm_enumerate_t mass_storage_rules[1] = 167 {"^usb$/^mass-storage([0-9]+)$", 1, MATCH_ALL}; 168 static devfsadm_enumerate_t usbprn_rules[1] = 169 {"^usb$/^printer([0-9]+)$", 1, MATCH_ALL}; 170 static devfsadm_enumerate_t whost_rules[1] = 171 {"^usb$/^whost([0-9]+)$", 1, MATCH_ALL}; 172 static devfsadm_enumerate_t hwarc_rules[1] = 173 {"^usb$/^hwarc([0-9]+)$", 1, MATCH_ALL}; 174 static devfsadm_enumerate_t wusb_ca_rules[1] = 175 {"^usb$/^wusb_ca([0-9]+)$", 1, MATCH_ALL}; 176 177 DEVFSADM_REMOVE_INIT_V0(usb_remove_cbt); 178 179 int 180 minor_init(void) 181 { 182 devfsadm_print(debug_mid, "usb_link: minor_init\n"); 183 return (DEVFSADM_SUCCESS); 184 } 185 186 int 187 minor_fini(void) 188 { 189 devfsadm_print(debug_mid, "usb_link: minor_fini\n"); 190 return (DEVFSADM_SUCCESS); 191 } 192 193 typedef enum { 194 DRIVER_HUBD, 195 DRIVER_OHCI, 196 DRIVER_EHCI, 197 DRIVER_UHCI, 198 DRIVER_XHCI, 199 DRIVER_USB_AC, 200 DRIVER_USB_AS, 201 DRIVER_HID, 202 DRIVER_USB_MID, 203 DRIVER_DDIVS_USBC, 204 DRIVER_SCSA2USB, 205 DRIVER_USBPRN, 206 DRIVER_UGEN, 207 DRIVER_VIDEO, 208 DRIVER_HWAHC, 209 DRIVER_HWARC, 210 DRIVER_WUSB_CA, 211 DRIVER_UNKNOWN 212 } driver_defs_t; 213 214 typedef struct { 215 char *driver_name; 216 driver_defs_t index; 217 } driver_name_table_entry_t; 218 219 driver_name_table_entry_t driver_name_table[] = { 220 { "hubd", DRIVER_HUBD }, 221 { "ohci", DRIVER_OHCI }, 222 { "ehci", DRIVER_EHCI }, 223 { "uhci", DRIVER_UHCI }, 224 { "xhci", DRIVER_XHCI }, 225 { "usb_ac", DRIVER_USB_AC }, 226 { "usb_as", DRIVER_USB_AS }, 227 { "hid", DRIVER_HID }, 228 { "usb_mid", DRIVER_USB_MID }, 229 { "ddivs_usbc", DRIVER_DDIVS_USBC }, 230 { "scsa2usb", DRIVER_SCSA2USB }, 231 { "usbprn", DRIVER_USBPRN }, 232 { "ugen", DRIVER_UGEN }, 233 { "usbvc", DRIVER_VIDEO }, 234 { "hwahc", DRIVER_HWAHC }, 235 { "hwarc", DRIVER_HWARC }, 236 { "wusb_ca", DRIVER_WUSB_CA }, 237 { NULL, DRIVER_UNKNOWN } 238 }; 239 240 /* 241 * This function is called for every usb minor node. 242 * Calls enumerate to assign a logical usb id, and then 243 * devfsadm_mklink to make the link. 244 */ 245 static int 246 usb_process(di_minor_t minor, di_node_t node) 247 { 248 devfsadm_enumerate_t rules[1]; 249 char *l_path, *p_path, *buf, *devfspath; 250 char *minor_nm, *drvr_nm, *name = (char *)NULL; 251 int i; 252 driver_defs_t index; 253 int flags = 0; 254 int create_secondary_link = 0; 255 256 minor_nm = di_minor_name(minor); 257 drvr_nm = di_driver_name(node); 258 if ((minor_nm == NULL) || (drvr_nm == NULL)) { 259 return (DEVFSADM_CONTINUE); 260 } 261 262 devfsadm_print(debug_mid, "usb_process: minor=%s node=%s type=%s\n", 263 minor_nm, di_node_name(node), di_minor_nodetype(minor)); 264 265 devfspath = di_devfs_path(node); 266 if (devfspath == NULL) { 267 devfsadm_print(debug_mid, 268 "USB_process: devfspath is NULL\n"); 269 return (DEVFSADM_CONTINUE); 270 } 271 272 l_path = (char *)malloc(PATH_MAX); 273 if (l_path == NULL) { 274 di_devfs_path_free(devfspath); 275 devfsadm_print(debug_mid, "usb_process: malloc() failed\n"); 276 return (DEVFSADM_CONTINUE); 277 } 278 279 p_path = (char *)malloc(PATH_MAX); 280 if (p_path == NULL) { 281 devfsadm_print(debug_mid, "usb_process: malloc() failed\n"); 282 di_devfs_path_free(devfspath); 283 free(l_path); 284 return (DEVFSADM_CONTINUE); 285 } 286 287 (void) strcpy(p_path, devfspath); 288 (void) strcat(p_path, ":"); 289 (void) strcat(p_path, minor_nm); 290 di_devfs_path_free(devfspath); 291 292 devfsadm_print(debug_mid, "usb_process: path %s\n", p_path); 293 294 for (i = 0; ; i++) { 295 if ((driver_name_table[i].driver_name == NULL) || 296 (strcmp(drvr_nm, driver_name_table[i].driver_name) == 0)) { 297 index = driver_name_table[i].index; 298 break; 299 } 300 } 301 302 if (strcmp(di_minor_nodetype(minor), DDI_NT_UGEN) == 0) { 303 ugen_create_link(p_path, minor_nm, node, minor); 304 free(l_path); 305 free(p_path); 306 return (DEVFSADM_CONTINUE); 307 } 308 309 /* Figure out which rules to apply */ 310 switch (index) { 311 case DRIVER_HUBD: 312 case DRIVER_OHCI: 313 case DRIVER_EHCI: 314 case DRIVER_UHCI: 315 case DRIVER_XHCI: 316 rules[0] = hub_rules[0]; /* For HUBs */ 317 name = "hub"; 318 319 break; 320 case DRIVER_USB_AC: 321 if (strcmp(minor_nm, "sound,audio") == 0) { 322 rules[0] = audio_rules[0]; 323 name = "audio"; /* For audio */ 324 create_secondary_link = 1; 325 } else if (strcmp(minor_nm, "sound,audioctl") == 0) { 326 rules[0] = audio_control_rules[0]; 327 name = "audio-control"; /* For audio */ 328 create_secondary_link = 1; 329 } else if (strcmp(minor_nm, "mux") == 0) { 330 rules[0] = audio_mux_rules[0]; 331 name = "audio-mux"; /* For audio */ 332 } else { 333 free(l_path); 334 free(p_path); 335 return (DEVFSADM_CONTINUE); 336 } 337 break; 338 case DRIVER_USB_AS: 339 rules[0] = audio_stream_rules[0]; 340 name = "audio-stream"; /* For audio */ 341 break; 342 case DRIVER_VIDEO: 343 rules[0] = video_rules[0]; 344 name = "video"; /* For video */ 345 create_secondary_link = 1; 346 break; 347 case DRIVER_HID: 348 rules[0] = hid_rules[0]; 349 name = "hid"; /* For HIDs */ 350 break; 351 case DRIVER_USB_MID: 352 rules[0] = device_rules[0]; 353 name = "device"; /* For other USB devices */ 354 break; 355 case DRIVER_DDIVS_USBC: 356 rules[0] = ddivs_usbc_rules[0]; 357 name = "device"; /* For other USB devices */ 358 break; 359 case DRIVER_SCSA2USB: 360 rules[0] = mass_storage_rules[0]; 361 name = "mass-storage"; /* For mass-storage devices */ 362 break; 363 case DRIVER_USBPRN: 364 rules[0] = usbprn_rules[0]; 365 name = "printer"; 366 break; 367 case DRIVER_HWAHC: 368 if (strcmp(minor_nm, "hwahc") == 0) { 369 rules[0] = whost_rules[0]; 370 name = "whost"; /* For HWA HC */ 371 } else if (strcmp(minor_nm, "hubd") == 0) { 372 rules[0] = hub_rules[0]; 373 name = "hub"; /* For HWA HC */ 374 } else { 375 free(l_path); 376 free(p_path); 377 return (DEVFSADM_CONTINUE); 378 } 379 break; 380 case DRIVER_HWARC: 381 rules[0] = hwarc_rules[0]; 382 name = "hwarc"; /* For UWB HWA Radio Controllers */ 383 break; 384 case DRIVER_WUSB_CA: 385 rules[0] = wusb_ca_rules[0]; 386 name = "wusb_ca"; /* for wusb cable association */ 387 break; 388 default: 389 devfsadm_print(debug_mid, "usb_process: unknown driver=%s\n", 390 drvr_nm); 391 free(l_path); 392 free(p_path); 393 return (DEVFSADM_CONTINUE); 394 } 395 396 /* 397 * build the physical path from the components. 398 * find the logical usb id, and stuff it in buf 399 */ 400 if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) { 401 devfsadm_print(debug_mid, "usb_process: exit/continue\n"); 402 free(l_path); 403 free(p_path); 404 return (DEVFSADM_CONTINUE); 405 } 406 407 (void) snprintf(l_path, PATH_MAX, "usb/%s%s", name, buf); 408 409 devfsadm_print(debug_mid, "usb_process: p_path=%s buf=%s\n", 410 p_path, buf); 411 412 free(buf); 413 414 devfsadm_print(debug_mid, "mklink %s -> %s\n", l_path, p_path); 415 416 (void) devfsadm_mklink(l_path, node, minor, flags); 417 418 if (create_secondary_link) { 419 /* 420 * Create secondary links to make newly hotplugged 421 * usb audio device the primary device. 422 */ 423 if (strcmp(name, "audio") == 0) { 424 (void) devfsadm_secondary_link("audio", l_path, 0); 425 } else if (strcmp(name, "audio-control") == 0) { 426 (void) devfsadm_secondary_link("audioctl", l_path, 0); 427 } else if (strcmp(name, "video") == 0) { 428 (void) devfsadm_secondary_link(l_path + 4, l_path, 0); 429 } 430 } 431 432 free(p_path); 433 free(l_path); 434 435 return (DEVFSADM_CONTINUE); 436 } 437 438 static void 439 ugen_create_link(char *p_path, char *node_name, 440 di_node_t node, di_minor_t minor) 441 { 442 char *buf, s[MAXPATHLEN]; 443 char *lasts = s; 444 char *vid, *pid; 445 char *minor_name; 446 char ugen_RE[128]; 447 devfsadm_enumerate_t ugen_rules[1]; 448 char l_path[PATH_MAX]; 449 int flags = 0; 450 451 devfsadm_print(debug_mid, "ugen_create_link: p_path=%s name=%s\n", 452 p_path, node_name); 453 454 (void) strlcpy(s, node_name, sizeof (s)); 455 456 /* get vid, pid and minor name strings */ 457 vid = strtok_r(lasts, ".", &lasts); 458 pid = strtok_r(NULL, ".", &lasts); 459 minor_name = lasts; 460 461 if ((vid == NULL) || (pid == NULL) || (minor_name == NULL)) { 462 return; 463 } 464 465 /* create regular expression contain vid and pid */ 466 (void) snprintf(ugen_RE, sizeof (ugen_RE), 467 "^usb$/^%s\\.%s$/^([0-9]+)$", vid, pid); 468 devfsadm_print(debug_mid, 469 "ugen_create_link: ugen_RE=%s minor_name=%s\n", 470 ugen_RE, minor_name); 471 472 bzero(ugen_rules, sizeof (ugen_rules)); 473 474 ugen_rules[0].re = ugen_RE; 475 ugen_rules[0].subexp = 1; 476 ugen_rules[0].flags = MATCH_ADDR; 477 478 /* 479 * build the physical path from the components. 480 * find the logical usb id, and stuff it in buf 481 */ 482 if (devfsadm_enumerate_int(p_path, 0, &buf, ugen_rules, 1)) { 483 devfsadm_print(debug_mid, "ugen_create_link: exit/continue\n"); 484 return; 485 } 486 487 (void) snprintf(l_path, sizeof (l_path), "usb/%s.%s/%s/%s", 488 vid, pid, buf, minor_name); 489 490 devfsadm_print(debug_mid, "mklink %s -> %s\n", l_path, p_path); 491 492 (void) devfsadm_mklink(l_path, node, minor, flags); 493 494 free(buf); 495 } 496