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