xref: /illumos-gate/usr/src/cmd/devfsadm/usb_link.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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