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