xref: /titanic_50/usr/src/cmd/devfsadm/usb_link.c (revision fb3fb4f3d76d55b64440afd0af72775dfad3bd1d)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 
44 
45 /* Rules for creating links */
46 static devfsadm_create_t usb_cbt[] = {
47 	{ "usb", NULL, "usb_ac",	DRV_EXACT,
48 						ILEVEL_0, usb_process },
49 	{ "usb", NULL, "usb_as",	DRV_EXACT,
50 						ILEVEL_0, usb_process },
51 	{ "usb", NULL, "ddivs_usbc",	DRV_EXACT,
52 						ILEVEL_0, usb_process },
53 	{ "usb", NULL, "hid",		DRV_EXACT,
54 						ILEVEL_0, usb_process },
55 	{ "usb", DDI_NT_NEXUS, "hubd",	DRV_EXACT|TYPE_EXACT,
56 						ILEVEL_0, usb_process },
57 	{ "usb", DDI_NT_NEXUS, "ohci",	DRV_EXACT|TYPE_EXACT,
58 						ILEVEL_0, usb_process },
59 	{ "usb", DDI_NT_NEXUS, "ehci",	DRV_EXACT|TYPE_EXACT,
60 						ILEVEL_0, usb_process },
61 	{ "usb", DDI_NT_SCSI_NEXUS, "scsa2usb",	DRV_EXACT|TYPE_EXACT,
62 						ILEVEL_0, usb_process },
63 	{ "usb", DDI_NT_UGEN, "scsa2usb",	DRV_EXACT|TYPE_EXACT,
64 						ILEVEL_0, usb_process },
65 	{ "usb", DDI_NT_NEXUS, "uhci",	DRV_EXACT|TYPE_EXACT,
66 						ILEVEL_0, usb_process },
67 	{ "usb", DDI_NT_UGEN, "ugen",	DRV_EXACT|TYPE_EXACT,
68 						ILEVEL_0, usb_process },
69 	{ "usb", DDI_NT_NEXUS, "usb_mid", DRV_EXACT|TYPE_EXACT,
70 						ILEVEL_0, usb_process },
71 	{ "usb", DDI_NT_UGEN, "usb_mid", DRV_EXACT|TYPE_EXACT,
72 						ILEVEL_0, usb_process },
73 	{ "usb", DDI_NT_PRINTER, "usbprn", DRV_EXACT|TYPE_EXACT,
74 						ILEVEL_0, usb_process },
75 	{ "usb", DDI_NT_UGEN, "usbprn", DRV_EXACT|TYPE_EXACT,
76 						ILEVEL_0, usb_process },
77 };
78 
79 /* For debug printing (-V filter) */
80 static char *debug_mid = "usb_mid";
81 
82 DEVFSADM_CREATE_INIT_V0(usb_cbt);
83 
84 /* USB device links */
85 #define	USB_LINK_RE_AUDIO	"^usb/audio[0-9]+$"
86 #define	USB_LINK_RE_AUDIOMUX	"^usb/audio-mux[0-9]+$"
87 #define	USB_LINK_RE_AUDIOCTL	"^usb/audio-control[0-9]+$"
88 #define	USB_LINK_RE_AUDIOSTREAM	"^usb/audio-stream[0-9]+$"
89 #define	USB_LINK_RE_DDIVS_USBC	"^usb/ddivs_usbc[0-9]+$"
90 #define	USB_LINK_RE_DEVICE	"^usb/device[0-9]+$"
91 #define	USB_LINK_RE_HID		"^usb/hid[0-9]+$"
92 #define	USB_LINK_RE_HUB		"^usb/hub[0-9]+$"
93 #define	USB_LINK_RE_MASS_STORE	"^usb/mass-storage[0-9]+$"
94 #define	USB_LINK_RE_UGEN	"^usb/[0-9,a-f]+\\.[0-9,a-f]+/[0-9]+/.+$"
95 #define	USB_LINK_RE_USBPRN	"^usb/printer[0-9]+$"
96 
97 /* Rules for removing links */
98 static devfsadm_remove_t usb_remove_cbt[] = {
99 	{ "usb", USB_LINK_RE_AUDIO, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
100 			devfsadm_rm_all },
101 	{ "usb", USB_LINK_RE_AUDIOMUX, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
102 			devfsadm_rm_all },
103 	{ "usb", USB_LINK_RE_AUDIOCTL, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
104 			devfsadm_rm_all },
105 	{ "usb", USB_LINK_RE_AUDIOSTREAM, RM_POST | RM_HOT | RM_ALWAYS,
106 			ILEVEL_0, devfsadm_rm_all },
107 	{ "usb", USB_LINK_RE_DDIVS_USBC, RM_POST | RM_HOT | RM_ALWAYS,
108 			ILEVEL_0, devfsadm_rm_all },
109 	{ "usb", USB_LINK_RE_DEVICE, RM_POST | RM_HOT, ILEVEL_0,
110 			devfsadm_rm_all },
111 	{ "usb", USB_LINK_RE_HID, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
112 			devfsadm_rm_all },
113 	{ "usb", USB_LINK_RE_HUB, RM_POST | RM_HOT, ILEVEL_0, devfsadm_rm_all },
114 	{ "usb", USB_LINK_RE_MASS_STORE, RM_POST | RM_HOT | RM_ALWAYS,
115 			ILEVEL_0, devfsadm_rm_all },
116 	{ "usb", USB_LINK_RE_UGEN, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
117 			devfsadm_rm_all },
118 	{ "usb", USB_LINK_RE_USBPRN, RM_POST | RM_HOT | RM_ALWAYS, ILEVEL_0,
119 			devfsadm_rm_link },
120 };
121 
122 /*
123  * Rules for different USB devices except ugen which is dynamically
124  * created
125  */
126 static devfsadm_enumerate_t audio_rules[1] =
127 	{"^usb$/^audio([0-9]+)$", 1, MATCH_ALL};
128 static devfsadm_enumerate_t audio_mux_rules[1] =
129 	{"^usb$/^audio-mux([0-9]+)$", 1, MATCH_ALL};
130 static devfsadm_enumerate_t audio_control_rules[1] =
131 	{"^usb$/^audio-control([0-9]+)$", 1, MATCH_ALL};
132 static devfsadm_enumerate_t audio_stream_rules[1] =
133 	{"^usb$/^audio-stream([0-9]+)$", 1, MATCH_ALL};
134 static devfsadm_enumerate_t ddivs_usbc_rules[1] =
135 	{"^usb$/^ddivs_usbc([0-9]+)$", 1, MATCH_ALL};
136 static devfsadm_enumerate_t device_rules[1] =
137 	{"^usb$/^device([0-9]+)$", 1, MATCH_ALL};
138 static devfsadm_enumerate_t hid_rules[1] =
139 	{"^usb$/^hid([0-9]+)$", 1, MATCH_ALL};
140 static devfsadm_enumerate_t hub_rules[1] =
141 	{"^usb$/^hub([0-9]+)$", 1, MATCH_ALL};
142 static devfsadm_enumerate_t mass_storage_rules[1] =
143 	{"^usb$/^mass-storage([0-9]+)$", 1, MATCH_ALL};
144 static devfsadm_enumerate_t usbprn_rules[1] =
145 	{"^usb$/^printer([0-9]+)$", 1, MATCH_ALL};
146 
147 DEVFSADM_REMOVE_INIT_V0(usb_remove_cbt);
148 
149 int
150 minor_init(void)
151 {
152 	devfsadm_print(debug_mid, "usb_link: minor_init\n");
153 	return (DEVFSADM_SUCCESS);
154 }
155 
156 int
157 minor_fini(void)
158 {
159 	devfsadm_print(debug_mid, "usb_link: minor_fini\n");
160 	return (DEVFSADM_SUCCESS);
161 }
162 
163 typedef enum {
164 	DRIVER_HUBD	= 0,
165 	DRIVER_OHCI	= 1,
166 	DRIVER_EHCI	= 2,
167 	DRIVER_UHCI	= 3,
168 	DRIVER_USB_AC	= 4,
169 	DRIVER_USB_AS	= 5,
170 	DRIVER_HID	= 6,
171 	DRIVER_USB_MID	= 7,
172 	DRIVER_DDIVS_USBC = 8,
173 	DRIVER_SCSA2USB = 9,
174 	DRIVER_USBPRN	= 10,
175 	DRIVER_UGEN	= 11,
176 	DRIVER_UNKNOWN	= 12
177 } driver_defs_t;
178 
179 typedef struct {
180 	char	*driver_name;
181 	int	index;
182 } driver_name_table_entry_t;
183 
184 driver_name_table_entry_t driver_name_table[] = {
185 	{ "hubd",	DRIVER_HUBD },
186 	{ "ohci",	DRIVER_OHCI },
187 	{ "ehci",	DRIVER_EHCI },
188 	{ "uhci",	DRIVER_UHCI },
189 	{ "usb_ac",	DRIVER_USB_AC },
190 	{ "usb_as",	DRIVER_USB_AS },
191 	{ "hid",	DRIVER_HID },
192 	{ "usb_mid",	DRIVER_USB_MID },
193 	{ "ddivs_usbc",	DRIVER_DDIVS_USBC },
194 	{ "scsa2usb",	DRIVER_SCSA2USB },
195 	{ "usbprn",	DRIVER_USBPRN },
196 	{ "ugen",	DRIVER_UGEN },
197 	{ NULL,		DRIVER_UNKNOWN }
198 };
199 
200 
201 /*
202  * This function is called for every usb minor node.
203  * Calls enumerate to assign a logical usb id, and then
204  * devfsadm_mklink to make the link.
205  */
206 static int
207 usb_process(di_minor_t minor, di_node_t node)
208 {
209 	devfsadm_enumerate_t rules[1];
210 	char *l_path, *p_path, *buf, *devfspath;
211 	char *minor_nm, *drvr_nm, *name = (char *)NULL;
212 	int i, index;
213 	int create_secondary_link = 0;
214 
215 	minor_nm = di_minor_name(minor);
216 	drvr_nm = di_driver_name(node);
217 	if ((minor_nm == NULL) || (drvr_nm == NULL)) {
218 		return (DEVFSADM_CONTINUE);
219 	}
220 
221 	devfsadm_print(debug_mid, "usb_process: minor=%s node=%s type=%s\n",
222 		minor_nm, di_node_name(node), di_minor_nodetype(minor));
223 
224 	devfspath = di_devfs_path(node);
225 	if (devfspath == NULL) {
226 		devfsadm_print(debug_mid,
227 		    "USB_process: devfspath is	NULL\n");
228 		return (DEVFSADM_CONTINUE);
229 	}
230 
231 	l_path = (char *)malloc(PATH_MAX);
232 	if (l_path == NULL) {
233 		di_devfs_path_free(devfspath);
234 		devfsadm_print(debug_mid, "usb_process: malloc() failed\n");
235 		return (DEVFSADM_CONTINUE);
236 	}
237 
238 	p_path = (char *)malloc(PATH_MAX);
239 	if (p_path == NULL) {
240 		devfsadm_print(debug_mid, "usb_process: malloc() failed\n");
241 		di_devfs_path_free(devfspath);
242 		free(l_path);
243 		return (DEVFSADM_CONTINUE);
244 	}
245 
246 	(void) strcpy(p_path, devfspath);
247 	(void) strcat(p_path, ":");
248 	(void) strcat(p_path, minor_nm);
249 	di_devfs_path_free(devfspath);
250 
251 	devfsadm_print(debug_mid, "usb_process: path %s\n", p_path);
252 
253 	for (i = 0; ; i++) {
254 		if ((driver_name_table[i].driver_name == NULL) ||
255 		    (strcmp(drvr_nm, driver_name_table[i].driver_name) == 0)) {
256 			index = driver_name_table[i].index;
257 			break;
258 		}
259 	}
260 
261 	if (strcmp(di_minor_nodetype(minor), DDI_NT_UGEN) == 0) {
262 		ugen_create_link(p_path, minor_nm, node, minor);
263 		free(l_path);
264 		free(p_path);
265 		return (DEVFSADM_CONTINUE);
266 	}
267 
268 	/* Figure out which rules to apply */
269 	switch (index) {
270 	case DRIVER_HUBD:
271 	case DRIVER_OHCI:
272 	case DRIVER_EHCI:
273 	case DRIVER_UHCI:
274 		rules[0] = hub_rules[0];	/* For HUBs */
275 		name = "hub";
276 
277 		break;
278 	case DRIVER_USB_AC:
279 		if (strcmp(minor_nm, "sound,audio") == 0) {
280 			rules[0] = audio_rules[0];
281 			name = "audio";		/* For audio */
282 			create_secondary_link = 1;
283 		} else if (strcmp(minor_nm, "sound,audioctl") == 0) {
284 			rules[0] = audio_control_rules[0];
285 			name = "audio-control";		/* For audio */
286 			create_secondary_link = 1;
287 		} else if (strcmp(minor_nm, "mux") == 0) {
288 			rules[0] = audio_mux_rules[0];
289 			name = "audio-mux";		/* For audio */
290 		} else {
291 			free(l_path);
292 			free(p_path);
293 			return (DEVFSADM_CONTINUE);
294 		}
295 		break;
296 	case DRIVER_USB_AS:
297 		rules[0] = audio_stream_rules[0];
298 		name = "audio-stream";		/* For audio */
299 		break;
300 	case DRIVER_HID:
301 		rules[0] = hid_rules[0];
302 		name = "hid";			/* For HIDs */
303 		break;
304 	case DRIVER_USB_MID:
305 		rules[0] = device_rules[0];
306 		name = "device";		/* For other USB devices */
307 		break;
308 	case DRIVER_DDIVS_USBC:
309 		rules[0] = ddivs_usbc_rules[0];
310 		name = "device";		/* For other USB devices */
311 		break;
312 	case DRIVER_SCSA2USB:
313 		rules[0] = mass_storage_rules[0];
314 		name = "mass-storage";		/* For mass-storage devices */
315 		break;
316 	case DRIVER_USBPRN:
317 		rules[0] = usbprn_rules[0];
318 		name = "printer";
319 		break;
320 	default:
321 		devfsadm_print(debug_mid, "usb_process: unknown driver=%s\n",
322 		    drvr_nm);
323 		free(l_path);
324 		free(p_path);
325 		return (DEVFSADM_CONTINUE);
326 	}
327 
328 	/*
329 	 *  build the physical path from the components.
330 	 *  find the logical usb id, and stuff it in buf
331 	 */
332 	if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) {
333 		devfsadm_print(debug_mid, "usb_process: exit/continue\n");
334 		free(l_path);
335 		free(p_path);
336 		return (DEVFSADM_CONTINUE);
337 	}
338 
339 	(void) snprintf(l_path, PATH_MAX, "usb/%s%s", name, buf);
340 
341 	devfsadm_print(debug_mid, "usb_process: p_path=%s buf=%s\n",
342 	    p_path, buf);
343 
344 	free(buf);
345 
346 	devfsadm_print(debug_mid, "mklink %s -> %s\n", l_path, p_path);
347 
348 	(void) devfsadm_mklink(l_path, node, minor, 0);
349 
350 	if (create_secondary_link) {
351 		/*
352 		 * Create secondary links to make newly hotplugged
353 		 * usb audio device the primary device.
354 		 */
355 		if (strcmp(name, "audio") == 0) {
356 			(void) devfsadm_secondary_link("audio", l_path, 0);
357 		} else if (strcmp(name, "audio-control") == 0) {
358 			(void) devfsadm_secondary_link("audioctl", l_path, 0);
359 		}
360 	}
361 
362 	free(p_path);
363 	free(l_path);
364 
365 	return (DEVFSADM_CONTINUE);
366 }
367 
368 static void
369 ugen_create_link(char *p_path, char *node_name,
370     di_node_t node, di_minor_t minor)
371 {
372 	char *buf, s[MAXPATHLEN];
373 	char *lasts = s;
374 	char *vid, *pid;
375 	char *minor_name;
376 	char ugen_RE[128];
377 	devfsadm_enumerate_t ugen_rules[1];
378 	char l_path[PATH_MAX];
379 
380 	devfsadm_print(debug_mid, "ugen_create_link: p_path=%s name=%s\n",
381 	    p_path, node_name);
382 
383 	(void) strlcpy(s, node_name, sizeof (s));
384 
385 	/* get vid, pid and minor name strings */
386 	vid = strtok_r(lasts, ".", &lasts);
387 	pid = strtok_r(NULL, ".", &lasts);
388 	minor_name = lasts;
389 
390 	if ((vid == NULL) || (pid == NULL) || (minor_name == NULL)) {
391 		return;
392 	}
393 
394 	/* create regular expression contain vid and pid */
395 	(void) snprintf(ugen_RE, sizeof (ugen_RE),
396 	    "^usb$/^%s\\.%s$/^([0-9]+)$", vid, pid);
397 	devfsadm_print(debug_mid,
398 	    "ugen_create_link: ugen_RE=%s minor_name=%s\n",
399 	    ugen_RE, minor_name);
400 
401 	bzero(ugen_rules, sizeof (ugen_rules));
402 
403 	ugen_rules[0].re = ugen_RE;
404 	ugen_rules[0].subexp = 1;
405 	ugen_rules[0].flags = MATCH_ADDR;
406 
407 	/*
408 	 *  build the physical path from the components.
409 	 *  find the logical usb id, and stuff it in buf
410 	 */
411 	if (devfsadm_enumerate_int(p_path, 0, &buf, ugen_rules, 1)) {
412 		devfsadm_print(debug_mid, "ugen_create_link: exit/continue\n");
413 		return;
414 	}
415 
416 	(void) snprintf(l_path, sizeof (l_path), "usb/%s.%s/%s/%s",
417 	    vid, pid, buf, minor_name);
418 
419 	devfsadm_print(debug_mid, "mklink %s -> %s\n", l_path, p_path);
420 
421 	(void) devfsadm_mklink(l_path, node, minor, 0);
422 
423 	free(buf);
424 }
425