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