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