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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #define USBA_FRAMEWORK
28 #include <sys/ksynch.h>
29 #include <sys/strsun.h>
30 #include <sys/usb/usba/usba_impl.h>
31 #include <sys/usb/usba/usba_devdb_impl.h>
33 static usb_log_handle_t usba_devdb_log_handle
;
34 uint_t usba_devdb_errlevel
= USB_LOG_L4
;
35 uint_t usba_devdb_errmask
= (uint_t
)-1;
37 boolean_t usba_build_devdb
= B_FALSE
;
39 avl_tree_t usba_devdb
; /* tree of records */
40 static krwlock_t usba_devdb_lock
; /* lock protecting the tree */
42 _NOTE(RWLOCK_PROTECTS_DATA(usba_devdb_lock
, usba_devdb
))
44 /* function prototypes */
45 static int usb_devdb_compare_pathnames(char *, char *);
46 static int usba_devdb_compare(const void *, const void *);
47 static int usba_devdb_build_device_database();
48 static void usba_devdb_destroy_device_database();
51 * usba_devdb_initialization
52 * Initialize this module that builds the usb device database
55 usba_devdb_initialization()
57 usba_devdb_log_handle
= usb_alloc_log_hdl(NULL
, "devdb",
58 &usba_devdb_errlevel
, &usba_devdb_errmask
, NULL
, 0);
60 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
61 "usba_devdb_initialization");
63 rw_init(&usba_devdb_lock
, NULL
, RW_DRIVER
, NULL
);
65 rw_enter(&usba_devdb_lock
, RW_WRITER
);
67 usba_build_devdb
= B_TRUE
;
69 /* now create the avl tree */
70 avl_create(&usba_devdb
, usba_devdb_compare
,
71 sizeof (usba_devdb_info_t
),
72 offsetof(struct usba_devdb_info
, avl_link
));
74 (void) usba_devdb_build_device_database();
76 usba_build_devdb
= B_FALSE
;
78 rw_exit(&usba_devdb_lock
);
84 * Free up all the resources being used by this module
89 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
90 "usba_devdb_destroy");
92 rw_enter(&usba_devdb_lock
, RW_WRITER
);
94 usba_devdb_destroy_device_database();
96 rw_exit(&usba_devdb_lock
);
98 rw_destroy(&usba_devdb_lock
);
100 usb_free_log_hdl(usba_devdb_log_handle
);
105 * usba_devdb_get_var_type:
106 * returns the field from the token
108 static config_field_t
109 usba_devdb_get_var_type(char *str
)
111 usba_cfg_var_t
*cfgvar
;
113 cfgvar
= &usba_cfg_varlist
[0];
114 while (cfgvar
->field
!= USB_NONE
) {
115 if (strcasecmp(cfgvar
->name
, str
) == 0) {
122 return (cfgvar
->field
);
127 * usba_devdb_get_conf_rec:
128 * Fetch one record from the file
131 usba_devdb_get_conf_rec(struct _buf
*file
, usba_configrec_t
**rec
)
134 char tokval
[MAXPATHLEN
];
135 usba_configrec_t
*cfgrec
;
136 config_field_t cfgvar
;
140 USB_NEWVAR
, USB_CONFIG_VAR
, USB_VAR_EQUAL
, USB_VAR_VALUE
,
142 } parse_state
= USB_NEWVAR
;
144 cfgrec
= (usba_configrec_t
*)kmem_zalloc(
145 sizeof (usba_configrec_t
), KM_SLEEP
);
146 cfgrec
->idVendor
= cfgrec
->idProduct
= cfgrec
->cfg_index
= -1;
148 token
= kobj_lex(file
, tokval
, sizeof (tokval
));
149 while ((token
!= EOF
) && (token
!= SEMICOLON
)) {
161 switch (parse_state
) {
163 cfgvar
= usba_devdb_get_var_type(tokval
);
164 if (cfgvar
== USB_NONE
) {
165 parse_state
= USB_ERROR
;
166 kobj_file_err(CE_WARN
, file
,
167 "Syntax Error: Invalid field %s",
170 parse_state
= USB_CONFIG_VAR
;
174 if ((cfgvar
== USB_VENDOR
) ||
175 (cfgvar
== USB_PRODUCT
) ||
176 (cfgvar
== USB_CFGNDX
)) {
177 parse_state
= USB_ERROR
;
178 kobj_file_err(CE_WARN
, file
,
179 "Syntax Error: Invalid value %s"
180 " for field: %s\n", tokval
,
181 usba_cfg_varlist
[cfgvar
].name
);
182 } else if (kobj_get_string(&llptr
, tokval
)) {
186 (char *)(uintptr_t)llptr
;
187 parse_state
= USB_NEWVAR
;
191 (char *)(uintptr_t)llptr
;
192 parse_state
= USB_NEWVAR
;
196 (char *)(uintptr_t)llptr
;
197 parse_state
= USB_NEWVAR
;
201 (char *)(uintptr_t)llptr
;
202 parse_state
= USB_NEWVAR
;
205 parse_state
= USB_ERROR
;
208 parse_state
= USB_ERROR
;
209 kobj_file_err(CE_WARN
, file
,
210 "Syntax Error: Invalid value %s"
211 " for field: %s\n", tokval
,
212 usba_cfg_varlist
[cfgvar
].name
);
219 parse_state
= USB_ERROR
;
220 kobj_file_err(CE_WARN
, file
,
221 "Syntax Error: at %s", tokval
);
226 if (parse_state
== USB_CONFIG_VAR
) {
227 if (cfgvar
== USB_NONE
) {
228 parse_state
= USB_ERROR
;
229 kobj_file_err(CE_WARN
, file
,
230 "Syntax Error: unexpected '='");
232 parse_state
= USB_VAR_VALUE
;
234 } else if (parse_state
!= USB_ERROR
) {
235 kobj_file_err(CE_WARN
, file
,
236 "Syntax Error: unexpected '='");
237 parse_state
= USB_ERROR
;
242 if ((parse_state
== USB_VAR_VALUE
) && (cfgvar
!=
244 (void) kobj_getvalue(tokval
, &value
);
247 cfgrec
->idVendor
= (int)value
;
248 parse_state
= USB_NEWVAR
;
251 cfgrec
->idProduct
= (int)value
;
252 parse_state
= USB_NEWVAR
;
255 cfgrec
->cfg_index
= (int)value
;
256 parse_state
= USB_NEWVAR
;
259 kobj_file_err(CE_WARN
, file
,
260 "Syntax Error: Invalid value for "
262 usba_cfg_varlist
[cfgvar
].name
);
264 } else if (parse_state
!= USB_ERROR
) {
265 parse_state
= USB_ERROR
;
266 kobj_file_err(CE_WARN
, file
, "Syntax Error:"
267 "unexpected hex/decimal: %s", tokval
);
271 kobj_file_err(CE_WARN
, file
, "Syntax Error: at: %s",
273 parse_state
= USB_ERROR
;
276 token
= kobj_lex(file
, tokval
, sizeof (tokval
));
285 * usba_devdb_free_rec:
286 * Free the record allocated in usba_devdb_get_conf_rec.
287 * We use kobj_free_string as kobj_get_string allocates memory
288 * in mod_sysfile_arena.
291 usba_devdb_free_rec(usba_configrec_t
*rec
)
293 if (rec
->selection
) {
294 kobj_free_string(rec
->selection
, strlen(rec
->selection
) + 1);
297 kobj_free_string(rec
->serialno
, strlen(rec
->serialno
) + 1);
300 kobj_free_string(rec
->pathname
, strlen(rec
->pathname
) + 1);
303 kobj_free_string(rec
->driver
, strlen(rec
->driver
) + 1);
305 kmem_free(rec
, sizeof (usba_configrec_t
));
311 * usb_devdb_compare_pathnames:
312 * Compare the two pathnames. If we are building the tree, we do a
313 * straight string compare to enable correct tree generation. If we
314 * are searching for a matching node, we compare only the selected
315 * portion of the pathname to give a correct match.
318 usb_devdb_compare_pathnames(char *p1
, char *p2
)
323 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
324 "usb_devdb_compare_pathnames: p1=0x%p p2=0x%p",
325 (void *)p1
, (void *)p2
);
328 if (usba_build_devdb
== B_TRUE
) {
329 /* this is a straight string compare */
330 rval
= strcmp(p1
, p2
);
334 } else if (rval
> 0) {
343 * Comparing on this is tricky.
344 * p1 is the string hubd is looking for &
345 * p2 is the string in the device db.
346 * At this point hubd knows: ../hubd@P/device@P
347 * while user will specify ..../hubd@P/keyboard@P
348 * First compare till .../hubd@P
349 * Second compare is just P in "device@P"
351 ustr
= strrchr(p2
, '/');
352 hstr
= strrchr(p1
, '/');
353 rval
= strncmp(p1
, p2
,
354 MAX(_PTRDIFF(ustr
, p2
),
355 _PTRDIFF(hstr
, p1
)));
359 } else if (rval
> 0) {
363 /* now compare the ports */
364 hstr
= p1
+ strlen(p1
) -1;
365 ustr
= p2
+ strlen(p2
) -1;
370 } else if (*hstr
> *ustr
) {
374 /* finally got a match */
380 } else if ((p1
== NULL
) && (p2
== NULL
)) {
397 * Compares the two nodes. Returns -1 when p1 < p2, 0 when p1 == p2
398 * and +1 when p1 > p2. This function is invoked by avl_find
399 * Here p1 is always the node that we are trying to insert or match in
400 * the device database.
403 usba_devdb_compare(const void *p1
, const void *p2
)
405 usba_configrec_t
*u1
, *u2
;
408 u1
= ((usba_devdb_info_t
*)p1
)->usb_dev
;
409 u2
= ((usba_devdb_info_t
*)p2
)->usb_dev
;
411 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
412 "usba_devdb_compare: p1=0x%p u1=0x%p p2=0x%p u2=0x%p",
413 p1
, (void *)u1
, p2
, (void *)u2
);
415 /* first match vendor id */
416 if (u1
->idVendor
< u2
->idVendor
) {
419 } else if (u1
->idVendor
> u2
->idVendor
) {
423 /* idvendor match, now check idproduct */
424 if (u1
->idProduct
< u2
->idProduct
) {
427 } else if (u1
->idProduct
> u2
->idProduct
) {
431 /* idproduct match, now check serial no. */
432 if (u1
->serialno
&& u2
->serialno
) {
433 rval
= strcmp(u1
->serialno
, u2
->serialno
);
437 } else if (rval
< 0) {
443 return (usb_devdb_compare_pathnames(
444 u1
->pathname
, u2
->pathname
));
446 } else if ((u1
->serialno
== NULL
) &&
447 (u2
->serialno
== NULL
)) {
449 return (usb_devdb_compare_pathnames(
450 u1
->pathname
, u2
->pathname
));
452 if (u1
->serialno
== NULL
) {
466 * usba_devdb_build_device_database
467 * Builds a height balanced tree of all the records present in the file.
468 * Records that are "not enabled" and are duplicate are discarded.
471 usba_devdb_build_device_database()
474 usba_configrec_t
*user_rec
;
476 usba_devdb_info_t
*dbnode
;
479 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
480 "usba_devdb_build_device_database: Start");
482 file
= kobj_open_file(usbconf_file
);
483 if (file
!= (struct _buf
*)-1) {
487 token
= usba_devdb_get_conf_rec(file
, &user_rec
);
489 if (user_rec
!= NULL
) {
491 if ((user_rec
->selection
== NULL
) ||
492 (strcasecmp(user_rec
->selection
,
494 /* we don't store disabled entries */
495 usba_devdb_free_rec(user_rec
);
500 dbnode
= (usba_devdb_info_t
*)kmem_zalloc(
501 sizeof (usba_devdb_info_t
), KM_SLEEP
);
502 dbnode
->usb_dev
= user_rec
;
504 if (avl_find(&usba_devdb
, dbnode
, &where
) ==
506 /* insert new node */
507 avl_insert(&usba_devdb
, dbnode
, where
);
510 * we don't maintain duplicate entries
512 usba_devdb_free_rec(user_rec
);
514 sizeof (usba_devdb_info_t
));
518 } while (token
!= EOF
);
520 kobj_close_file(file
);
523 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
524 "usba_devdb_build_device_database: End");
526 /* XXX: return the no. of errors encountered */
532 * usba_devdb_destroy_device_database
533 * Destory all records in the tree
536 usba_devdb_destroy_device_database()
538 usba_devdb_info_t
*dbnode
;
541 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
542 "usba_devdb_destroy_device_database");
544 /* while there are nodes in the tree, keep destroying them */
545 while ((dbnode
= (usba_devdb_info_t
*)
546 avl_destroy_nodes(&usba_devdb
, &cookie
)) != NULL
) {
551 usba_devdb_free_rec(dbnode
->usb_dev
);
552 kmem_free(dbnode
, sizeof (usba_devdb_info_t
));
554 avl_destroy(&usba_devdb
);
559 * usba_devdb_get_user_preferences
560 * Returns configrec structure to the caller that contains user
561 * preferences for the device pointed by the parameters.
562 * The first search is for a record that has serial number and/or
563 * a pathname. If search fails, we search for a rule that is generic
564 * i.e. without serial no. and pathname.
567 usba_devdb_get_user_preferences(int idVendor
, int idProduct
, char *serialno
,
570 usba_configrec_t
*req_rec
;
571 usba_devdb_info_t
*req_node
, *dbnode
;
574 USB_DPRINTF_L4(DPRINT_MASK_DEVDB
, usba_devdb_log_handle
,
575 "usba_devdb_get_user_preferences");
577 req_rec
= kmem_zalloc(sizeof (usba_configrec_t
), KM_SLEEP
);
578 req_node
= kmem_zalloc(sizeof (usba_devdb_info_t
), KM_SLEEP
);
580 /* fill in the requested parameters */
581 req_rec
->idVendor
= idVendor
;
582 req_rec
->idProduct
= idProduct
;
583 req_rec
->serialno
= serialno
;
584 req_rec
->pathname
= pathname
;
586 req_node
->usb_dev
= req_rec
;
588 rw_enter(&usba_devdb_lock
, RW_READER
);
590 /* try to find a perfect match in the device database */
591 dbnode
= (usba_devdb_info_t
*)avl_find(&usba_devdb
, req_node
, &where
);
592 if (dbnode
== NULL
) {
593 /* look for a generic rule */
594 req_rec
->serialno
= req_rec
->pathname
= NULL
;
595 dbnode
= (usba_devdb_info_t
*)avl_find(&usba_devdb
, req_node
,
598 rw_exit(&usba_devdb_lock
);
600 kmem_free(req_rec
, sizeof (usba_configrec_t
));
601 kmem_free(req_node
, sizeof (usba_devdb_info_t
));
604 return (dbnode
->usb_dev
);
613 * Reinitializes the device database. It destroys the old one and creates
614 * a new one by re-reading the file.
619 rw_enter(&usba_devdb_lock
, RW_WRITER
);
621 usba_build_devdb
= B_TRUE
;
623 /* destroy all nodes in the existing database */
624 usba_devdb_destroy_device_database();
626 /* now build a new one */
627 (void) usba_devdb_build_device_database();
629 usba_build_devdb
= B_FALSE
;
631 rw_exit(&usba_devdb_lock
);