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