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