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 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2019, Joyent, Inc.
25 */
26
27
28 #define USBA_FRAMEWORK
29 #include <sys/ksynch.h>
30 #include <sys/strsun.h>
31 #include <sys/usb/usba/usba_impl.h>
32 #include <sys/usb/usba/usba_devdb_impl.h>
33
34 static usb_log_handle_t usba_devdb_log_handle;
35 uint_t usba_devdb_errlevel = USB_LOG_L4;
36 uint_t usba_devdb_errmask = (uint_t)-1;
37
38 boolean_t usba_build_devdb = B_FALSE;
39
40 avl_tree_t usba_devdb; /* tree of records */
41 static krwlock_t usba_devdb_lock; /* lock protecting the tree */
42
43 _NOTE(RWLOCK_PROTECTS_DATA(usba_devdb_lock, usba_devdb))
44
45 /*
46 * Reader Writer locks have problem with warlock. warlock is unable to
47 * decode that the structure is local and doesn't need locking
48 */
49 _NOTE(SCHEME_PROTECTS_DATA("unshared", usba_devdb_info))
50 _NOTE(SCHEME_PROTECTS_DATA("unshared", usba_configrec))
51
52 /* function prototypes */
53 static int usb_devdb_compare_pathnames(char *, char *);
54 static int usba_devdb_compare(const void *, const void *);
55 static int usba_devdb_build_device_database();
56 static void usba_devdb_destroy_device_database();
57
58 /*
59 * usba_devdb_initialization
60 * Initialize this module that builds the usb device database
61 */
62 void
usba_devdb_initialization()63 usba_devdb_initialization()
64 {
65 usba_devdb_log_handle = usb_alloc_log_hdl(NULL, "devdb",
66 &usba_devdb_errlevel, &usba_devdb_errmask, NULL, 0);
67
68 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
69 "usba_devdb_initialization");
70
71 rw_init(&usba_devdb_lock, NULL, RW_DRIVER, NULL);
72
73 rw_enter(&usba_devdb_lock, RW_WRITER);
74
75 usba_build_devdb = B_TRUE;
76
77 /* now create the avl tree */
78 avl_create(&usba_devdb, usba_devdb_compare,
79 sizeof (usba_devdb_info_t),
80 offsetof(struct usba_devdb_info, avl_link));
81
82 (void) usba_devdb_build_device_database();
83
84 usba_build_devdb = B_FALSE;
85
86 rw_exit(&usba_devdb_lock);
87 }
88
89
90 /*
91 * usba_devdb_destroy
92 * Free up all the resources being used by this module
93 */
94 void
usba_devdb_destroy()95 usba_devdb_destroy()
96 {
97 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
98 "usba_devdb_destroy");
99
100 rw_enter(&usba_devdb_lock, RW_WRITER);
101
102 usba_devdb_destroy_device_database();
103
104 rw_exit(&usba_devdb_lock);
105
106 rw_destroy(&usba_devdb_lock);
107
108 usb_free_log_hdl(usba_devdb_log_handle);
109 }
110
111
112 /*
113 * usba_devdb_get_var_type:
114 * returns the field from the token
115 */
116 static config_field_t
usba_devdb_get_var_type(char * str)117 usba_devdb_get_var_type(char *str)
118 {
119 usba_cfg_var_t *cfgvar;
120
121 cfgvar = &usba_cfg_varlist[0];
122 while (cfgvar->field != USB_NONE) {
123 if (strcasecmp(cfgvar->name, str) == 0) {
124 break;
125 } else {
126 cfgvar++;
127 }
128 }
129
130 return (cfgvar->field);
131 }
132
133
134 /*
135 * usba_devdb_get_conf_rec:
136 * Fetch one record from the file
137 */
138 static token_t
usba_devdb_get_conf_rec(struct _buf * file,usba_configrec_t ** rec)139 usba_devdb_get_conf_rec(struct _buf *file, usba_configrec_t **rec)
140 {
141 token_t token;
142 char tokval[MAXPATHLEN];
143 usba_configrec_t *cfgrec;
144 config_field_t cfgvar = USB_NONE;
145 u_longlong_t llptr;
146 u_longlong_t value;
147 enum {
148 USB_NEWVAR, USB_CONFIG_VAR, USB_VAR_EQUAL, USB_VAR_VALUE,
149 USB_ERROR
150 } parse_state = USB_NEWVAR;
151
152 cfgrec = (usba_configrec_t *)kmem_zalloc(
153 sizeof (usba_configrec_t), KM_SLEEP);
154 cfgrec->idVendor = cfgrec->idProduct = cfgrec->cfg_index = -1;
155
156 token = kobj_lex(file, tokval, sizeof (tokval));
157 while ((token != EOF) && (token != SEMICOLON)) {
158 switch (token) {
159 case STAR:
160 case POUND:
161 /* skip comments */
162 kobj_find_eol(file);
163 break;
164 case NEWLINE:
165 kobj_newline(file);
166 break;
167 case NAME:
168 case STRING:
169 switch (parse_state) {
170 case USB_NEWVAR:
171 cfgvar = usba_devdb_get_var_type(tokval);
172 if (cfgvar == USB_NONE) {
173 parse_state = USB_ERROR;
174 kobj_file_err(CE_WARN, file,
175 "Syntax Error: Invalid field %s",
176 tokval);
177 } else {
178 parse_state = USB_CONFIG_VAR;
179 }
180 break;
181 case USB_VAR_VALUE:
182 if ((cfgvar == USB_VENDOR) ||
183 (cfgvar == USB_PRODUCT) ||
184 (cfgvar == USB_CFGNDX)) {
185 parse_state = USB_ERROR;
186 kobj_file_err(CE_WARN, file,
187 "Syntax Error: Invalid value %s"
188 " for field: %s\n", tokval,
189 usba_cfg_varlist[cfgvar].name);
190 } else if (kobj_get_string(&llptr, tokval)) {
191 switch (cfgvar) {
192 case USB_SELECTION:
193 cfgrec->selection =
194 (char *)(uintptr_t)llptr;
195 parse_state = USB_NEWVAR;
196 break;
197 case USB_SRNO:
198 cfgrec->serialno =
199 (char *)(uintptr_t)llptr;
200 parse_state = USB_NEWVAR;
201 break;
202 case USB_PATH:
203 cfgrec->pathname =
204 (char *)(uintptr_t)llptr;
205 parse_state = USB_NEWVAR;
206 break;
207 case USB_DRIVER:
208 cfgrec->driver =
209 (char *)(uintptr_t)llptr;
210 parse_state = USB_NEWVAR;
211 break;
212 default:
213 parse_state = USB_ERROR;
214 }
215 } else {
216 parse_state = USB_ERROR;
217 kobj_file_err(CE_WARN, file,
218 "Syntax Error: Invalid value %s"
219 " for field: %s\n", tokval,
220 usba_cfg_varlist[cfgvar].name);
221 }
222 break;
223 case USB_ERROR:
224 /* just skip */
225 break;
226 default:
227 parse_state = USB_ERROR;
228 kobj_file_err(CE_WARN, file,
229 "Syntax Error: at %s", tokval);
230 break;
231 }
232 break;
233 case EQUALS:
234 if (parse_state == USB_CONFIG_VAR) {
235 if (cfgvar == USB_NONE) {
236 parse_state = USB_ERROR;
237 kobj_file_err(CE_WARN, file,
238 "Syntax Error: unexpected '='");
239 } else {
240 parse_state = USB_VAR_VALUE;
241 }
242 } else if (parse_state != USB_ERROR) {
243 kobj_file_err(CE_WARN, file,
244 "Syntax Error: unexpected '='");
245 parse_state = USB_ERROR;
246 }
247 break;
248 case HEXVAL:
249 case DECVAL:
250 if ((parse_state == USB_VAR_VALUE) && (cfgvar !=
251 USB_NONE)) {
252 (void) kobj_getvalue(tokval, &value);
253 switch (cfgvar) {
254 case USB_VENDOR:
255 cfgrec->idVendor = (int)value;
256 parse_state = USB_NEWVAR;
257 break;
258 case USB_PRODUCT:
259 cfgrec->idProduct = (int)value;
260 parse_state = USB_NEWVAR;
261 break;
262 case USB_CFGNDX:
263 cfgrec->cfg_index = (int)value;
264 parse_state = USB_NEWVAR;
265 break;
266 default:
267 kobj_file_err(CE_WARN, file,
268 "Syntax Error: Invalid value for "
269 "%s",
270 usba_cfg_varlist[cfgvar].name);
271 }
272 } else if (parse_state != USB_ERROR) {
273 parse_state = USB_ERROR;
274 kobj_file_err(CE_WARN, file, "Syntax Error:"
275 "unexpected hex/decimal: %s", tokval);
276 }
277 break;
278 default:
279 kobj_file_err(CE_WARN, file, "Syntax Error: at: %s",
280 tokval);
281 parse_state = USB_ERROR;
282 break;
283 }
284 token = kobj_lex(file, tokval, sizeof (tokval));
285 }
286 *rec = cfgrec;
287
288 return (token);
289 }
290
291
292 /*
293 * usba_devdb_free_rec:
294 * Free the record allocated in usba_devdb_get_conf_rec.
295 * We use kobj_free_string as kobj_get_string allocates memory
296 * in mod_sysfile_arena.
297 */
298 static void
usba_devdb_free_rec(usba_configrec_t * rec)299 usba_devdb_free_rec(usba_configrec_t *rec)
300 {
301 if (rec->selection) {
302 kobj_free_string(rec->selection, strlen(rec->selection) + 1);
303 }
304 if (rec->serialno) {
305 kobj_free_string(rec->serialno, strlen(rec->serialno) + 1);
306 }
307 if (rec->pathname) {
308 kobj_free_string(rec->pathname, strlen(rec->pathname) + 1);
309 }
310 if (rec->driver) {
311 kobj_free_string(rec->driver, strlen(rec->driver) + 1);
312 }
313 kmem_free(rec, sizeof (usba_configrec_t));
314 }
315
316
317
318 /*
319 * usb_devdb_compare_pathnames:
320 * Compare the two pathnames. If we are building the tree, we do a
321 * straight string compare to enable correct tree generation. If we
322 * are searching for a matching node, we compare only the selected
323 * portion of the pathname to give a correct match.
324 */
325 static int
usb_devdb_compare_pathnames(char * p1,char * p2)326 usb_devdb_compare_pathnames(char *p1, char *p2)
327 {
328 int rval;
329 char *ustr, *hstr;
330
331 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
332 "usb_devdb_compare_pathnames: p1=0x%p p2=0x%p",
333 (void *)p1, (void *)p2);
334
335 if (p1 && p2) {
336 if (usba_build_devdb == B_TRUE) {
337 /* this is a straight string compare */
338 rval = strcmp(p1, p2);
339 if (rval < 0) {
340
341 return (-1);
342 } else if (rval > 0) {
343
344 return (+1);
345 } else {
346
347 return (0);
348 }
349 } else {
350 /*
351 * Comparing on this is tricky.
352 * p1 is the string hubd is looking for &
353 * p2 is the string in the device db.
354 * At this point hubd knows: ../hubd@P/device@P
355 * while user will specify ..../hubd@P/keyboard@P
356 * First compare till .../hubd@P
357 * Second compare is just P in "device@P"
358 */
359 ustr = strrchr(p2, '/');
360 hstr = strrchr(p1, '/');
361 rval = strncmp(p1, p2,
362 MAX(_PTRDIFF(ustr, p2),
363 _PTRDIFF(hstr, p1)));
364 if (rval < 0) {
365
366 return (-1);
367 } else if (rval > 0) {
368
369 return (+1);
370 } else {
371 /* now compare the ports */
372 hstr = p1 + strlen(p1) -1;
373 ustr = p2 + strlen(p2) -1;
374
375 if (*hstr < *ustr) {
376
377 return (-1);
378 } else if (*hstr > *ustr) {
379
380 return (+1);
381 } else {
382 /* finally got a match */
383
384 return (0);
385 }
386 }
387 }
388 } else if ((p1 == NULL) && (p2 == NULL)) {
389
390 return (0);
391 } else {
392 if (p1 == NULL) {
393
394 return (-1);
395 } else {
396
397 return (+1);
398 }
399 }
400 }
401
402
403 /*
404 * usba_devdb_compare
405 * Compares the two nodes. Returns -1 when p1 < p2, 0 when p1 == p2
406 * and +1 when p1 > p2. This function is invoked by avl_find
407 * Here p1 is always the node that we are trying to insert or match in
408 * the device database.
409 */
410 static int
usba_devdb_compare(const void * p1,const void * p2)411 usba_devdb_compare(const void *p1, const void *p2)
412 {
413 usba_configrec_t *u1, *u2;
414 int rval;
415
416 u1 = ((usba_devdb_info_t *)p1)->usb_dev;
417 u2 = ((usba_devdb_info_t *)p2)->usb_dev;
418
419 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
420 "usba_devdb_compare: p1=0x%p u1=0x%p p2=0x%p u2=0x%p",
421 p1, (void *)u1, p2, (void *)u2);
422
423 /* first match vendor id */
424 if (u1->idVendor < u2->idVendor) {
425
426 return (-1);
427 } else if (u1->idVendor > u2->idVendor) {
428
429 return (+1);
430 } else {
431 /* idvendor match, now check idproduct */
432 if (u1->idProduct < u2->idProduct) {
433
434 return (-1);
435 } else if (u1->idProduct > u2->idProduct) {
436
437 return (+1);
438 } else {
439 /* idproduct match, now check serial no. */
440 if (u1->serialno && u2->serialno) {
441 rval = strcmp(u1->serialno, u2->serialno);
442 if (rval > 0) {
443
444 return (+1);
445 } else if (rval < 0) {
446
447 return (-1);
448 } else {
449 /* srno. matches */
450
451 return (usb_devdb_compare_pathnames(
452 u1->pathname, u2->pathname));
453 }
454 } else if ((u1->serialno == NULL) &&
455 (u2->serialno == NULL)) {
456
457 return (usb_devdb_compare_pathnames(
458 u1->pathname, u2->pathname));
459 } else {
460 if (u1->serialno == NULL) {
461
462 return (-1);
463 } else {
464
465 return (+1);
466 }
467 }
468 }
469 }
470 }
471
472
473 /*
474 * usba_devdb_build_device_database
475 * Builds a height balanced tree of all the records present in the file.
476 * Records that are "not enabled" and are duplicate are discarded.
477 */
478 static int
usba_devdb_build_device_database()479 usba_devdb_build_device_database()
480 {
481 struct _buf *file;
482 usba_configrec_t *user_rec;
483 avl_index_t where;
484 usba_devdb_info_t *dbnode;
485 token_t token;
486
487 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
488 "usba_devdb_build_device_database: Start");
489
490 file = kobj_open_file(usbconf_file);
491 if (file != (struct _buf *)-1) {
492
493 do {
494 user_rec = NULL;
495 token = usba_devdb_get_conf_rec(file, &user_rec);
496
497 if (user_rec != NULL) {
498
499 if ((user_rec->selection == NULL) ||
500 (strcasecmp(user_rec->selection,
501 "enable") != 0)) {
502 /* we don't store disabled entries */
503 usba_devdb_free_rec(user_rec);
504
505 continue;
506 }
507
508 dbnode = (usba_devdb_info_t *)kmem_zalloc(
509 sizeof (usba_devdb_info_t), KM_SLEEP);
510 dbnode->usb_dev = user_rec;
511
512 if (avl_find(&usba_devdb, dbnode, &where) ==
513 NULL) {
514 /* insert new node */
515 avl_insert(&usba_devdb, dbnode, where);
516 } else {
517 /*
518 * we don't maintain duplicate entries
519 */
520 usba_devdb_free_rec(user_rec);
521 kmem_free(dbnode,
522 sizeof (usba_devdb_info_t));
523 }
524 }
525
526 } while (token != EOF);
527
528 kobj_close_file(file);
529 }
530
531 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
532 "usba_devdb_build_device_database: End");
533
534 /* XXX: return the no. of errors encountered */
535 return (0);
536 }
537
538
539 /*
540 * usba_devdb_destroy_device_database
541 * Destory all records in the tree
542 */
543 static void
usba_devdb_destroy_device_database()544 usba_devdb_destroy_device_database()
545 {
546 usba_devdb_info_t *dbnode;
547 void *cookie = NULL;
548
549 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
550 "usba_devdb_destroy_device_database");
551
552 /* while there are nodes in the tree, keep destroying them */
553 while ((dbnode = (usba_devdb_info_t *)
554 avl_destroy_nodes(&usba_devdb, &cookie)) != NULL) {
555 /*
556 * destroy record
557 * destroy tree node
558 */
559 usba_devdb_free_rec(dbnode->usb_dev);
560 kmem_free(dbnode, sizeof (usba_devdb_info_t));
561 }
562 avl_destroy(&usba_devdb);
563 }
564
565
566 /*
567 * usba_devdb_get_user_preferences
568 * Returns configrec structure to the caller that contains user
569 * preferences for the device pointed by the parameters.
570 * The first search is for a record that has serial number and/or
571 * a pathname. If search fails, we search for a rule that is generic
572 * i.e. without serial no. and pathname.
573 */
574 usba_configrec_t *
usba_devdb_get_user_preferences(int idVendor,int idProduct,char * serialno,char * pathname)575 usba_devdb_get_user_preferences(int idVendor, int idProduct, char *serialno,
576 char *pathname)
577 {
578 usba_configrec_t *req_rec;
579 usba_devdb_info_t *req_node, *dbnode;
580 avl_index_t where;
581
582 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
583 "usba_devdb_get_user_preferences");
584
585 req_rec = kmem_zalloc(sizeof (usba_configrec_t), KM_SLEEP);
586 req_node = kmem_zalloc(sizeof (usba_devdb_info_t), KM_SLEEP);
587
588 /* fill in the requested parameters */
589 req_rec->idVendor = idVendor;
590 req_rec->idProduct = idProduct;
591 req_rec->serialno = serialno;
592 req_rec->pathname = pathname;
593
594 req_node->usb_dev = req_rec;
595
596 rw_enter(&usba_devdb_lock, RW_READER);
597
598 /* try to find a perfect match in the device database */
599 dbnode = (usba_devdb_info_t *)avl_find(&usba_devdb, req_node, &where);
600 #ifdef __lock_lint
601 (void) usba_devdb_compare(req_node, dbnode);
602 #endif
603 if (dbnode == NULL) {
604 /* look for a generic rule */
605 req_rec->serialno = req_rec->pathname = NULL;
606 dbnode = (usba_devdb_info_t *)avl_find(&usba_devdb, req_node,
607 &where);
608 #ifdef __lock_lint
609 (void) usba_devdb_compare(req_node, dbnode);
610 #endif
611 }
612 rw_exit(&usba_devdb_lock);
613
614 kmem_free(req_rec, sizeof (usba_configrec_t));
615 kmem_free(req_node, sizeof (usba_devdb_info_t));
616
617 if (dbnode) {
618 return (dbnode->usb_dev);
619 } else {
620 return (NULL);
621 }
622 }
623
624
625 /*
626 * usba_devdb_refresh
627 * Reinitializes the device database. It destroys the old one and creates
628 * a new one by re-reading the file.
629 */
630 int
usba_devdb_refresh()631 usba_devdb_refresh()
632 {
633 rw_enter(&usba_devdb_lock, RW_WRITER);
634
635 usba_build_devdb = B_TRUE;
636
637 /* destroy all nodes in the existing database */
638 usba_devdb_destroy_device_database();
639
640 /* now build a new one */
641 (void) usba_devdb_build_device_database();
642
643 usba_build_devdb = B_FALSE;
644
645 rw_exit(&usba_devdb_lock);
646
647 return (0);
648 }
649