Home | History | Annotate | Download | only in devfsadm
      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