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 }