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