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