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.
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.
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]
21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
31 #include <sys/types.h>
35 extern char *devfsadm_get_devices_dir();
36 static int usb_process(di_minor_t minor
, di_node_t node
);
38 static void ugen_create_link(char *p_path
, char *node_name
,
39 di_node_t node
, di_minor_t minor
);
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
},
84 /* For debug printing (-V filter) */
85 static char *debug_mid
= "usb_mid";
87 DEVFSADM_CREATE_INIT_V0(usb_cbt
);
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]+$"
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
,
111 { "usb", USB_LINK_RE_AUDIOMUX
, RM_POST
| RM_HOT
| RM_ALWAYS
, ILEVEL_0
,
113 { "usb", USB_LINK_RE_AUDIOCTL
, RM_POST
| RM_HOT
| RM_ALWAYS
, ILEVEL_0
,
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
,
121 { "usb", USB_LINK_RE_VIDEO
, RM_POST
| RM_HOT
| RM_ALWAYS
, ILEVEL_0
,
123 { "usb", USB_LINK_RE_DEVICE
, RM_POST
| RM_HOT
, ILEVEL_0
,
125 { "usb", USB_LINK_RE_HID
, RM_POST
| RM_HOT
| RM_ALWAYS
, ILEVEL_0
,
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
,
132 { "usb", USB_LINK_RE_USBPRN
, RM_POST
| RM_HOT
| RM_ALWAYS
, ILEVEL_0
,
134 { "usb", USB_LINK_RE_WHOST
, RM_POST
| RM_HOT
, ILEVEL_0
,
136 { "usb", USB_LINK_RE_HWARC
, RM_POST
| RM_HOT
| RM_ALWAYS
, ILEVEL_0
,
138 { "usb", USB_LINK_RE_WUSB_CA
, RM_POST
| RM_HOT
| RM_ALWAYS
, ILEVEL_0
,
143 * Rules for different USB devices except ugen which is dynamically
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
};
175 DEVFSADM_REMOVE_INIT_V0(usb_remove_cbt
);
180 devfsadm_print(debug_mid
, "usb_link: minor_init\n");
181 return (DEVFSADM_SUCCESS
);
187 devfsadm_print(debug_mid
, "usb_link: minor_fini\n");
188 return (DEVFSADM_SUCCESS
);
200 DRIVER_DDIVS_USBC
= 8,
214 } driver_name_table_entry_t
;
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
}
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.
242 usb_process(di_minor_t minor
, di_node_t node
)
244 devfsadm_enumerate_t rules
[1];
245 char *l_path
, *p_path
, *buf
, *devfspath
;
246 char *minor_nm
, *drvr_nm
, *name
= (char *)NULL
;
249 int create_secondary_link
= 0;
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
);
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
));
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
);
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
);
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
);
279 return (DEVFSADM_CONTINUE
);
282 (void) strcpy(p_path
, devfspath
);
283 (void) strcat(p_path
, ":");
284 (void) strcat(p_path
, minor_nm
);
285 di_devfs_path_free(devfspath
);
287 devfsadm_print(debug_mid
, "usb_process: path %s\n", p_path
);
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
;
297 if (strcmp(di_minor_nodetype(minor
), DDI_NT_UGEN
) == 0) {
298 ugen_create_link(p_path
, minor_nm
, node
, minor
);
301 return (DEVFSADM_CONTINUE
);
304 /* Figure out which rules to apply */
310 rules
[0] = hub_rules
[0]; /* For HUBs */
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 */
329 return (DEVFSADM_CONTINUE
);
333 rules
[0] = audio_stream_rules
[0];
334 name
= "audio-stream"; /* For audio */
337 rules
[0] = video_rules
[0];
338 name
= "video"; /* For video */
339 create_secondary_link
= 1;
342 rules
[0] = hid_rules
[0];
343 name
= "hid"; /* For HIDs */
346 rules
[0] = device_rules
[0];
347 name
= "device"; /* For other USB devices */
349 case DRIVER_DDIVS_USBC
:
350 rules
[0] = ddivs_usbc_rules
[0];
351 name
= "device"; /* For other USB devices */
353 case DRIVER_SCSA2USB
:
354 rules
[0] = mass_storage_rules
[0];
355 name
= "mass-storage"; /* For mass-storage devices */
358 rules
[0] = usbprn_rules
[0];
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 */
371 return (DEVFSADM_CONTINUE
);
375 rules
[0] = hwarc_rules
[0];
376 name
= "hwarc"; /* For UWB HWA Radio Controllers */
379 rules
[0] = wusb_ca_rules
[0];
380 name
= "wusb_ca"; /* for wusb cable association */
383 devfsadm_print(debug_mid
, "usb_process: unknown driver=%s\n",
387 return (DEVFSADM_CONTINUE
);
391 * build the physical path from the components.
392 * find the logical usb id, and stuff it in buf
394 if (devfsadm_enumerate_int(p_path
, 0, &buf
, rules
, 1)) {
395 devfsadm_print(debug_mid
, "usb_process: exit/continue\n");
398 return (DEVFSADM_CONTINUE
);
401 (void) snprintf(l_path
, PATH_MAX
, "usb/%s%s", name
, buf
);
403 devfsadm_print(debug_mid
, "usb_process: p_path=%s buf=%s\n",
408 devfsadm_print(debug_mid
, "mklink %s -> %s\n", l_path
, p_path
);
410 (void) devfsadm_mklink(l_path
, node
, minor
, flags
);
412 if (create_secondary_link
) {
414 * Create secondary links to make newly hotplugged
415 * usb audio device the primary device.
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);
429 return (DEVFSADM_CONTINUE
);
433 ugen_create_link(char *p_path
, char *node_name
,
434 di_node_t node
, di_minor_t minor
)
436 char *buf
, s
[MAXPATHLEN
];
441 devfsadm_enumerate_t ugen_rules
[1];
442 char l_path
[PATH_MAX
];
445 devfsadm_print(debug_mid
, "ugen_create_link: p_path=%s name=%s\n",
448 (void) strlcpy(s
, node_name
, sizeof (s
));
450 /* get vid, pid and minor name strings */
451 vid
= strtok_r(lasts
, ".", &lasts
);
452 pid
= strtok_r(NULL
, ".", &lasts
);
455 if ((vid
== NULL
) || (pid
== NULL
) || (minor_name
== NULL
)) {
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
);
466 bzero(ugen_rules
, sizeof (ugen_rules
));
468 ugen_rules
[0].re
= ugen_RE
;
469 ugen_rules
[0].subexp
= 1;
470 ugen_rules
[0].flags
= MATCH_ADDR
;
473 * build the physical path from the components.
474 * find the logical usb id, and stuff it in buf
476 if (devfsadm_enumerate_int(p_path
, 0, &buf
, ugen_rules
, 1)) {
477 devfsadm_print(debug_mid
, "ugen_create_link: exit/continue\n");
481 (void) snprintf(l_path
, sizeof (l_path
), "usb/%s.%s/%s/%s",
482 vid
, pid
, buf
, minor_name
);
484 devfsadm_print(debug_mid
, "mklink %s -> %s\n", l_path
, p_path
);
486 (void) devfsadm_mklink(l_path
, node
, minor
, flags
);