Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / usb / usba / usba_devdb.c
blobcb45e9aae4e09cf2d334cdb503ac4ec89036e802
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
54 void
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);
83 * usba_devdb_destroy
84 * Free up all the resources being used by this module
86 void
87 usba_devdb_destroy()
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) {
116 break;
117 } else {
118 cfgvar++;
122 return (cfgvar->field);
127 * usba_devdb_get_conf_rec:
128 * Fetch one record from the file
130 static token_t
131 usba_devdb_get_conf_rec(struct _buf *file, usba_configrec_t **rec)
133 token_t token;
134 char tokval[MAXPATHLEN];
135 usba_configrec_t *cfgrec;
136 config_field_t cfgvar;
137 u_longlong_t llptr;
138 u_longlong_t value;
139 enum {
140 USB_NEWVAR, USB_CONFIG_VAR, USB_VAR_EQUAL, USB_VAR_VALUE,
141 USB_ERROR
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)) {
150 switch (token) {
151 case STAR:
152 case POUND:
153 /* skip comments */
154 kobj_find_eol(file);
155 break;
156 case NEWLINE:
157 kobj_newline(file);
158 break;
159 case NAME:
160 case STRING:
161 switch (parse_state) {
162 case USB_NEWVAR:
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",
168 tokval);
169 } else {
170 parse_state = USB_CONFIG_VAR;
172 break;
173 case USB_VAR_VALUE:
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)) {
183 switch (cfgvar) {
184 case USB_SELECTION:
185 cfgrec->selection =
186 (char *)(uintptr_t)llptr;
187 parse_state = USB_NEWVAR;
188 break;
189 case USB_SRNO:
190 cfgrec->serialno =
191 (char *)(uintptr_t)llptr;
192 parse_state = USB_NEWVAR;
193 break;
194 case USB_PATH:
195 cfgrec->pathname =
196 (char *)(uintptr_t)llptr;
197 parse_state = USB_NEWVAR;
198 break;
199 case USB_DRIVER:
200 cfgrec->driver =
201 (char *)(uintptr_t)llptr;
202 parse_state = USB_NEWVAR;
203 break;
204 default:
205 parse_state = USB_ERROR;
207 } else {
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);
214 break;
215 case USB_ERROR:
216 /* just skip */
217 break;
218 default:
219 parse_state = USB_ERROR;
220 kobj_file_err(CE_WARN, file,
221 "Syntax Error: at %s", tokval);
222 break;
224 break;
225 case EQUALS:
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 '='");
231 } else {
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;
239 break;
240 case HEXVAL:
241 case DECVAL:
242 if ((parse_state == USB_VAR_VALUE) && (cfgvar !=
243 USB_NONE)) {
244 (void) kobj_getvalue(tokval, &value);
245 switch (cfgvar) {
246 case USB_VENDOR:
247 cfgrec->idVendor = (int)value;
248 parse_state = USB_NEWVAR;
249 break;
250 case USB_PRODUCT:
251 cfgrec->idProduct = (int)value;
252 parse_state = USB_NEWVAR;
253 break;
254 case USB_CFGNDX:
255 cfgrec->cfg_index = (int)value;
256 parse_state = USB_NEWVAR;
257 break;
258 default:
259 kobj_file_err(CE_WARN, file,
260 "Syntax Error: Invalid value for "
261 "%s",
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);
269 break;
270 default:
271 kobj_file_err(CE_WARN, file, "Syntax Error: at: %s",
272 tokval);
273 parse_state = USB_ERROR;
274 break;
276 token = kobj_lex(file, tokval, sizeof (tokval));
278 *rec = cfgrec;
280 return (token);
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.
290 static void
291 usba_devdb_free_rec(usba_configrec_t *rec)
293 if (rec->selection) {
294 kobj_free_string(rec->selection, strlen(rec->selection) + 1);
296 if (rec->serialno) {
297 kobj_free_string(rec->serialno, strlen(rec->serialno) + 1);
299 if (rec->pathname) {
300 kobj_free_string(rec->pathname, strlen(rec->pathname) + 1);
302 if (rec->driver) {
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.
317 static int
318 usb_devdb_compare_pathnames(char *p1, char *p2)
320 int rval;
321 char *ustr, *hstr;
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);
327 if (p1 && p2) {
328 if (usba_build_devdb == B_TRUE) {
329 /* this is a straight string compare */
330 rval = strcmp(p1, p2);
331 if (rval < 0) {
333 return (-1);
334 } else if (rval > 0) {
336 return (+1);
337 } else {
339 return (0);
341 } else {
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)));
356 if (rval < 0) {
358 return (-1);
359 } else if (rval > 0) {
361 return (+1);
362 } else {
363 /* now compare the ports */
364 hstr = p1 + strlen(p1) -1;
365 ustr = p2 + strlen(p2) -1;
367 if (*hstr < *ustr) {
369 return (-1);
370 } else if (*hstr > *ustr) {
372 return (+1);
373 } else {
374 /* finally got a match */
376 return (0);
380 } else if ((p1 == NULL) && (p2 == NULL)) {
382 return (0);
383 } else {
384 if (p1 == NULL) {
386 return (-1);
387 } else {
389 return (+1);
396 * usba_devdb_compare
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.
402 static int
403 usba_devdb_compare(const void *p1, const void *p2)
405 usba_configrec_t *u1, *u2;
406 int rval;
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) {
418 return (-1);
419 } else if (u1->idVendor > u2->idVendor) {
421 return (+1);
422 } else {
423 /* idvendor match, now check idproduct */
424 if (u1->idProduct < u2->idProduct) {
426 return (-1);
427 } else if (u1->idProduct > u2->idProduct) {
429 return (+1);
430 } else {
431 /* idproduct match, now check serial no. */
432 if (u1->serialno && u2->serialno) {
433 rval = strcmp(u1->serialno, u2->serialno);
434 if (rval > 0) {
436 return (+1);
437 } else if (rval < 0) {
439 return (-1);
440 } else {
441 /* srno. matches */
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));
451 } else {
452 if (u1->serialno == NULL) {
454 return (-1);
455 } else {
457 return (+1);
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.
470 static int
471 usba_devdb_build_device_database()
473 struct _buf *file;
474 usba_configrec_t *user_rec;
475 avl_index_t where;
476 usba_devdb_info_t *dbnode;
477 token_t token;
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) {
485 do {
486 user_rec = NULL;
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,
493 "enable") != 0)) {
494 /* we don't store disabled entries */
495 usba_devdb_free_rec(user_rec);
497 continue;
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) ==
505 NULL) {
506 /* insert new node */
507 avl_insert(&usba_devdb, dbnode, where);
508 } else {
510 * we don't maintain duplicate entries
512 usba_devdb_free_rec(user_rec);
513 kmem_free(dbnode,
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 */
527 return (0);
532 * usba_devdb_destroy_device_database
533 * Destory all records in the tree
535 static void
536 usba_devdb_destroy_device_database()
538 usba_devdb_info_t *dbnode;
539 void *cookie = NULL;
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) {
548 * destroy record
549 * destroy tree node
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.
566 usba_configrec_t *
567 usba_devdb_get_user_preferences(int idVendor, int idProduct, char *serialno,
568 char *pathname)
570 usba_configrec_t *req_rec;
571 usba_devdb_info_t *req_node, *dbnode;
572 avl_index_t where;
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,
596 &where);
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));
603 if (dbnode) {
604 return (dbnode->usb_dev);
605 } else {
606 return (NULL);
612 * usba_devdb_refresh
613 * Reinitializes the device database. It destroys the old one and creates
614 * a new one by re-reading the file.
617 usba_devdb_refresh()
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);
633 return (0);