xref: /onnv-gate/usr/src/uts/common/io/usb/usba/usba_devdb.c (revision 7632:91aa3d8541b5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56898Sfb209375  * Common Development and Distribution License (the "License").
66898Sfb209375  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
226898Sfb209375  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #define	USBA_FRAMEWORK
280Sstevel@tonic-gate #include <sys/ksynch.h>
297492SZhigang.Lu@Sun.COM #include <sys/strsun.h>
300Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
310Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb_impl.h>
320Sstevel@tonic-gate 
330Sstevel@tonic-gate static usb_log_handle_t	usba_devdb_log_handle;
34880Sfrits uint_t	usba_devdb_errlevel = USB_LOG_L4;
35880Sfrits uint_t	usba_devdb_errmask = (uint_t)-1;
360Sstevel@tonic-gate 
370Sstevel@tonic-gate boolean_t	usba_build_devdb = B_FALSE;
380Sstevel@tonic-gate 
390Sstevel@tonic-gate avl_tree_t	usba_devdb;		/* tree of records */
400Sstevel@tonic-gate static krwlock_t usba_devdb_lock;	/* lock protecting the tree */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate _NOTE(RWLOCK_PROTECTS_DATA(usba_devdb_lock, usba_devdb))
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate  * Reader Writer locks have problem with warlock. warlock is unable to
460Sstevel@tonic-gate  * decode that the structure is local and doesn't need locking
470Sstevel@tonic-gate  */
480Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usba_devdb_info))
490Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usba_configrec))
500Sstevel@tonic-gate 
510Sstevel@tonic-gate /* function prototypes */
520Sstevel@tonic-gate static int usb_devdb_compare_pathnames(char *, char *);
530Sstevel@tonic-gate static int usba_devdb_compare(const void *, const void *);
540Sstevel@tonic-gate static int usba_devdb_build_device_database();
550Sstevel@tonic-gate static void usba_devdb_destroy_device_database();
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /*
580Sstevel@tonic-gate  * usba_devdb_initialization
590Sstevel@tonic-gate  *	Initialize this module that builds the usb device database
600Sstevel@tonic-gate  */
610Sstevel@tonic-gate void
usba_devdb_initialization()620Sstevel@tonic-gate usba_devdb_initialization()
630Sstevel@tonic-gate {
640Sstevel@tonic-gate 	usba_devdb_log_handle = usb_alloc_log_hdl(NULL, "devdb",
650Sstevel@tonic-gate 	    &usba_devdb_errlevel, &usba_devdb_errmask, NULL, 0);
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
680Sstevel@tonic-gate 	    "usba_devdb_initialization");
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 	rw_init(&usba_devdb_lock, NULL, RW_DRIVER, NULL);
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	rw_enter(&usba_devdb_lock, RW_WRITER);
730Sstevel@tonic-gate 
740Sstevel@tonic-gate 	usba_build_devdb = B_TRUE;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate 	/* now create the avl tree */
770Sstevel@tonic-gate 	avl_create(&usba_devdb, usba_devdb_compare,
780Sstevel@tonic-gate 	    sizeof (usba_devdb_info_t),
790Sstevel@tonic-gate 	    offsetof(struct usba_devdb_info, avl_link));
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	(void) usba_devdb_build_device_database();
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	usba_build_devdb = B_FALSE;
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 	rw_exit(&usba_devdb_lock);
860Sstevel@tonic-gate }
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 
890Sstevel@tonic-gate /*
900Sstevel@tonic-gate  * usba_devdb_destroy
910Sstevel@tonic-gate  *	Free up all the resources being used by this module
920Sstevel@tonic-gate  */
930Sstevel@tonic-gate void
usba_devdb_destroy()940Sstevel@tonic-gate usba_devdb_destroy()
950Sstevel@tonic-gate {
960Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
970Sstevel@tonic-gate 	    "usba_devdb_destroy");
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	rw_enter(&usba_devdb_lock, RW_WRITER);
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	usba_devdb_destroy_device_database();
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	rw_exit(&usba_devdb_lock);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	rw_destroy(&usba_devdb_lock);
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	usb_free_log_hdl(usba_devdb_log_handle);
1080Sstevel@tonic-gate }
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate /*
1120Sstevel@tonic-gate  * usba_devdb_get_var_type:
1130Sstevel@tonic-gate  *	returns the field from the token
1140Sstevel@tonic-gate  */
1150Sstevel@tonic-gate static config_field_t
usba_devdb_get_var_type(char * str)1160Sstevel@tonic-gate usba_devdb_get_var_type(char *str)
1170Sstevel@tonic-gate {
1180Sstevel@tonic-gate 	usba_cfg_var_t	*cfgvar;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 	cfgvar = &usba_cfg_varlist[0];
1210Sstevel@tonic-gate 	while (cfgvar->field != USB_NONE) {
1220Sstevel@tonic-gate 		if (strcasecmp(cfgvar->name, str) == NULL) {
1230Sstevel@tonic-gate 			break;
1240Sstevel@tonic-gate 		} else {
1250Sstevel@tonic-gate 			cfgvar++;
1260Sstevel@tonic-gate 		}
1270Sstevel@tonic-gate 	}
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	return (cfgvar->field);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate /*
1340Sstevel@tonic-gate  * usba_devdb_get_conf_rec:
1350Sstevel@tonic-gate  *	Fetch one record from the file
1360Sstevel@tonic-gate  */
1370Sstevel@tonic-gate static token_t
usba_devdb_get_conf_rec(struct _buf * file,usba_configrec_t ** rec)1380Sstevel@tonic-gate usba_devdb_get_conf_rec(struct _buf *file, usba_configrec_t **rec)
1390Sstevel@tonic-gate {
1400Sstevel@tonic-gate 	token_t		token;
1410Sstevel@tonic-gate 	char		tokval[MAXPATHLEN];
1420Sstevel@tonic-gate 	usba_configrec_t	*cfgrec;
1430Sstevel@tonic-gate 	config_field_t	cfgvar;
1440Sstevel@tonic-gate 	u_longlong_t	llptr;
1450Sstevel@tonic-gate 	u_longlong_t	value;
1460Sstevel@tonic-gate 	enum {
1470Sstevel@tonic-gate 		USB_NEWVAR, USB_CONFIG_VAR, USB_VAR_EQUAL, USB_VAR_VALUE,
1486898Sfb209375 		    USB_ERROR
1496898Sfb209375 		    } parse_state = USB_NEWVAR;
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	cfgrec = (usba_configrec_t *)kmem_zalloc(
1520Sstevel@tonic-gate 	    sizeof (usba_configrec_t), KM_SLEEP);
1530Sstevel@tonic-gate 	cfgrec->idVendor = cfgrec->idProduct = cfgrec->cfg_index = -1;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	token = kobj_lex(file, tokval, sizeof (tokval));
1560Sstevel@tonic-gate 	while ((token != EOF) && (token != SEMICOLON)) {
1570Sstevel@tonic-gate 		switch (token) {
1580Sstevel@tonic-gate 		case STAR:
1590Sstevel@tonic-gate 		case POUND:
1600Sstevel@tonic-gate 			/* skip comments */
1610Sstevel@tonic-gate 			kobj_find_eol(file);
1620Sstevel@tonic-gate 			break;
1630Sstevel@tonic-gate 		case NEWLINE:
1640Sstevel@tonic-gate 			kobj_newline(file);
1650Sstevel@tonic-gate 			break;
1660Sstevel@tonic-gate 		case NAME:
1670Sstevel@tonic-gate 		case STRING:
1680Sstevel@tonic-gate 			switch (parse_state) {
1690Sstevel@tonic-gate 			case USB_NEWVAR:
1700Sstevel@tonic-gate 				cfgvar = usba_devdb_get_var_type(tokval);
1710Sstevel@tonic-gate 				if (cfgvar == USB_NONE) {
1720Sstevel@tonic-gate 					parse_state = USB_ERROR;
1730Sstevel@tonic-gate 					kobj_file_err(CE_WARN, file,
1740Sstevel@tonic-gate 					    "Syntax Error: Invalid field %s",
1750Sstevel@tonic-gate 					    tokval);
1760Sstevel@tonic-gate 				} else {
1770Sstevel@tonic-gate 					parse_state = USB_CONFIG_VAR;
1780Sstevel@tonic-gate 				}
1790Sstevel@tonic-gate 				break;
1800Sstevel@tonic-gate 			case USB_VAR_VALUE:
1810Sstevel@tonic-gate 				if ((cfgvar == USB_VENDOR) ||
1820Sstevel@tonic-gate 				    (cfgvar == USB_PRODUCT) ||
1830Sstevel@tonic-gate 				    (cfgvar == USB_CFGNDX)) {
1840Sstevel@tonic-gate 					parse_state = USB_ERROR;
1850Sstevel@tonic-gate 					kobj_file_err(CE_WARN, file,
1860Sstevel@tonic-gate 					    "Syntax Error: Invalid value %s"
1870Sstevel@tonic-gate 					    " for field: %s\n", tokval,
1880Sstevel@tonic-gate 					    usba_cfg_varlist[cfgvar].name);
1890Sstevel@tonic-gate 				} else if (kobj_get_string(&llptr, tokval)) {
1900Sstevel@tonic-gate 					switch (cfgvar) {
1910Sstevel@tonic-gate 					case USB_SELECTION:
1920Sstevel@tonic-gate 						cfgrec->selection =
1930Sstevel@tonic-gate 						    (char *)(uintptr_t)llptr;
1940Sstevel@tonic-gate 						parse_state = USB_NEWVAR;
1950Sstevel@tonic-gate 						break;
1960Sstevel@tonic-gate 					case USB_SRNO:
1970Sstevel@tonic-gate 						cfgrec->serialno =
1980Sstevel@tonic-gate 						    (char *)(uintptr_t)llptr;
1990Sstevel@tonic-gate 						parse_state = USB_NEWVAR;
2000Sstevel@tonic-gate 						break;
2010Sstevel@tonic-gate 					case USB_PATH:
2020Sstevel@tonic-gate 						cfgrec->pathname =
2030Sstevel@tonic-gate 						    (char *)(uintptr_t)llptr;
2040Sstevel@tonic-gate 						parse_state = USB_NEWVAR;
2050Sstevel@tonic-gate 						break;
2060Sstevel@tonic-gate 					case USB_DRIVER:
2070Sstevel@tonic-gate 						cfgrec->driver =
2080Sstevel@tonic-gate 						    (char *)(uintptr_t)llptr;
2090Sstevel@tonic-gate 						parse_state = USB_NEWVAR;
2100Sstevel@tonic-gate 						break;
2110Sstevel@tonic-gate 					default:
2120Sstevel@tonic-gate 						parse_state = USB_ERROR;
2130Sstevel@tonic-gate 					}
2140Sstevel@tonic-gate 				} else {
2150Sstevel@tonic-gate 					parse_state = USB_ERROR;
2160Sstevel@tonic-gate 					kobj_file_err(CE_WARN, file,
2170Sstevel@tonic-gate 					    "Syntax Error: Invalid value %s"
2180Sstevel@tonic-gate 					    " for field: %s\n", tokval,
2190Sstevel@tonic-gate 					    usba_cfg_varlist[cfgvar].name);
2200Sstevel@tonic-gate 				}
2210Sstevel@tonic-gate 				break;
2220Sstevel@tonic-gate 			case USB_ERROR:
2230Sstevel@tonic-gate 				/* just skip */
2240Sstevel@tonic-gate 				break;
2250Sstevel@tonic-gate 			default:
2260Sstevel@tonic-gate 				parse_state = USB_ERROR;
2270Sstevel@tonic-gate 				kobj_file_err(CE_WARN, file,
2280Sstevel@tonic-gate 				    "Syntax Error: at %s", tokval);
2290Sstevel@tonic-gate 				break;
2300Sstevel@tonic-gate 			}
2310Sstevel@tonic-gate 			break;
2320Sstevel@tonic-gate 		case EQUALS:
2330Sstevel@tonic-gate 			if (parse_state == USB_CONFIG_VAR) {
2340Sstevel@tonic-gate 				if (cfgvar == USB_NONE) {
2350Sstevel@tonic-gate 					parse_state = USB_ERROR;
2360Sstevel@tonic-gate 					kobj_file_err(CE_WARN, file,
2370Sstevel@tonic-gate 					    "Syntax Error: unexpected '='");
2380Sstevel@tonic-gate 				} else {
2390Sstevel@tonic-gate 					parse_state = USB_VAR_VALUE;
2400Sstevel@tonic-gate 				}
2410Sstevel@tonic-gate 			} else if (parse_state != USB_ERROR) {
2420Sstevel@tonic-gate 				kobj_file_err(CE_WARN, file,
2430Sstevel@tonic-gate 				    "Syntax Error: unexpected '='");
2440Sstevel@tonic-gate 				parse_state = USB_ERROR;
2450Sstevel@tonic-gate 			}
2460Sstevel@tonic-gate 			break;
2470Sstevel@tonic-gate 		case HEXVAL:
2480Sstevel@tonic-gate 		case DECVAL:
2490Sstevel@tonic-gate 			if ((parse_state == USB_VAR_VALUE) && (cfgvar !=
2500Sstevel@tonic-gate 			    USB_NONE)) {
2510Sstevel@tonic-gate 				(void) kobj_getvalue(tokval, &value);
2520Sstevel@tonic-gate 				switch (cfgvar) {
2530Sstevel@tonic-gate 				case USB_VENDOR:
2540Sstevel@tonic-gate 					cfgrec->idVendor = (int)value;
2550Sstevel@tonic-gate 					parse_state = USB_NEWVAR;
2560Sstevel@tonic-gate 					break;
2570Sstevel@tonic-gate 				case USB_PRODUCT:
2580Sstevel@tonic-gate 					cfgrec->idProduct = (int)value;
2590Sstevel@tonic-gate 					parse_state = USB_NEWVAR;
2600Sstevel@tonic-gate 					break;
2610Sstevel@tonic-gate 				case USB_CFGNDX:
2620Sstevel@tonic-gate 					cfgrec->cfg_index = (int)value;
2630Sstevel@tonic-gate 					parse_state = USB_NEWVAR;
2640Sstevel@tonic-gate 					break;
2650Sstevel@tonic-gate 				default:
2660Sstevel@tonic-gate 					kobj_file_err(CE_WARN, file,
2670Sstevel@tonic-gate 					    "Syntax Error: Invalid value for "
2680Sstevel@tonic-gate 					    "%s",
2690Sstevel@tonic-gate 					    usba_cfg_varlist[cfgvar].name);
2700Sstevel@tonic-gate 				}
2710Sstevel@tonic-gate 			} else if (parse_state != USB_ERROR) {
2720Sstevel@tonic-gate 				parse_state = USB_ERROR;
2730Sstevel@tonic-gate 				kobj_file_err(CE_WARN, file, "Syntax Error:"
2740Sstevel@tonic-gate 				    "unexpected hex/decimal: %s", tokval);
2750Sstevel@tonic-gate 			}
2760Sstevel@tonic-gate 			break;
2770Sstevel@tonic-gate 		default:
2780Sstevel@tonic-gate 			kobj_file_err(CE_WARN, file, "Syntax Error: at: %s",
2790Sstevel@tonic-gate 			    tokval);
2800Sstevel@tonic-gate 			parse_state = USB_ERROR;
2810Sstevel@tonic-gate 			break;
2820Sstevel@tonic-gate 		}
2830Sstevel@tonic-gate 		token = kobj_lex(file, tokval, sizeof (tokval));
2840Sstevel@tonic-gate 	}
2850Sstevel@tonic-gate 	*rec = cfgrec;
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	return (token);
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate  * usba_devdb_free_rec:
2930Sstevel@tonic-gate  *	Free the record allocated in usba_devdb_get_conf_rec.
2940Sstevel@tonic-gate  *	We use kobj_free_string as kobj_get_string allocates memory
2950Sstevel@tonic-gate  *	in mod_sysfile_arena.
2960Sstevel@tonic-gate  */
2970Sstevel@tonic-gate static void
usba_devdb_free_rec(usba_configrec_t * rec)2980Sstevel@tonic-gate usba_devdb_free_rec(usba_configrec_t *rec)
2990Sstevel@tonic-gate {
3000Sstevel@tonic-gate 	if (rec->selection) {
3010Sstevel@tonic-gate 		kobj_free_string(rec->selection, strlen(rec->selection) + 1);
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 	if (rec->serialno) {
3040Sstevel@tonic-gate 		kobj_free_string(rec->serialno, strlen(rec->serialno) + 1);
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate 	if (rec->pathname) {
3070Sstevel@tonic-gate 		kobj_free_string(rec->pathname, strlen(rec->pathname) + 1);
3080Sstevel@tonic-gate 	}
3090Sstevel@tonic-gate 	if (rec->driver) {
3100Sstevel@tonic-gate 		kobj_free_string(rec->driver, strlen(rec->driver) + 1);
3110Sstevel@tonic-gate 	}
3120Sstevel@tonic-gate 	kmem_free(rec, sizeof (usba_configrec_t));
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate /*
3180Sstevel@tonic-gate  * usb_devdb_compare_pathnames:
3190Sstevel@tonic-gate  *	Compare the two pathnames. If we are building the tree, we do a
3200Sstevel@tonic-gate  *	straight string compare to enable correct tree generation. If we
3210Sstevel@tonic-gate  *	are searching for a matching node, we compare only the selected
3220Sstevel@tonic-gate  *	portion of the pathname to give a correct match.
3230Sstevel@tonic-gate  */
3240Sstevel@tonic-gate static int
usb_devdb_compare_pathnames(char * p1,char * p2)3250Sstevel@tonic-gate usb_devdb_compare_pathnames(char *p1, char *p2)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	int	rval;
3280Sstevel@tonic-gate 	char	*ustr, *hstr;
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
3316898Sfb209375 	    "usb_devdb_compare_pathnames: p1=0x%p p2=0x%p",
3326898Sfb209375 	    (void *)p1, (void *)p2);
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	if (p1 && p2) {
3350Sstevel@tonic-gate 		if (usba_build_devdb == B_TRUE) {
3360Sstevel@tonic-gate 			/* this is a straight string compare */
3370Sstevel@tonic-gate 			rval = strcmp(p1, p2);
3380Sstevel@tonic-gate 			if (rval < 0) {
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 				return (-1);
3410Sstevel@tonic-gate 			} else if (rval > 0) {
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 				return (+1);
3440Sstevel@tonic-gate 			} else {
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 				return (0);
3470Sstevel@tonic-gate 			}
3480Sstevel@tonic-gate 		} else {
3490Sstevel@tonic-gate 			/*
3500Sstevel@tonic-gate 			 * Comparing on this is tricky.
3510Sstevel@tonic-gate 			 * p1 is the string hubd is looking for &
3520Sstevel@tonic-gate 			 * p2 is the string in the device db.
3530Sstevel@tonic-gate 			 * At this point hubd knows: ../hubd@P/device@P
3540Sstevel@tonic-gate 			 * while user will specify  ..../hubd@P/keyboard@P
3550Sstevel@tonic-gate 			 * First compare till .../hubd@P
3560Sstevel@tonic-gate 			 * Second compare is just P in "device@P"
3570Sstevel@tonic-gate 			 */
3580Sstevel@tonic-gate 			ustr = strrchr(p2, '/');
3590Sstevel@tonic-gate 			hstr = strrchr(p1, '/');
3607492SZhigang.Lu@Sun.COM 			rval = strncmp(p1, p2,
3617492SZhigang.Lu@Sun.COM 			    MAX(_PTRDIFF(ustr, p2),
3627492SZhigang.Lu@Sun.COM 			    _PTRDIFF(hstr, p1)));
3630Sstevel@tonic-gate 			if (rval < 0) {
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 				return (-1);
3660Sstevel@tonic-gate 			} else if (rval > 0) {
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 				return (+1);
3690Sstevel@tonic-gate 			} else {
3700Sstevel@tonic-gate 				/* now compare the ports */
3710Sstevel@tonic-gate 				hstr = p1 + strlen(p1) -1;
3720Sstevel@tonic-gate 				ustr = p2 + strlen(p2) -1;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 				if (*hstr < *ustr) {
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 					return (-1);
3770Sstevel@tonic-gate 				} else if (*hstr > *ustr) {
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 					return (+1);
3800Sstevel@tonic-gate 				} else {
3810Sstevel@tonic-gate 					/* finally got a match */
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 					return (0);
3840Sstevel@tonic-gate 				}
3850Sstevel@tonic-gate 			}
3860Sstevel@tonic-gate 		}
3870Sstevel@tonic-gate 	} else if ((p1 == NULL) && (p2 == NULL)) {
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 		return (0);
3900Sstevel@tonic-gate 	} else {
3910Sstevel@tonic-gate 		if (p1 == NULL) {
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 			return (-1);
3940Sstevel@tonic-gate 		} else {
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 			return (+1);
3970Sstevel@tonic-gate 		}
3980Sstevel@tonic-gate 	}
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate /*
4030Sstevel@tonic-gate  * usba_devdb_compare
4040Sstevel@tonic-gate  *	Compares the two nodes. Returns -1 when p1 < p2, 0 when p1 == p2
4050Sstevel@tonic-gate  *	and +1 when p1 > p2. This function is invoked by avl_find
4060Sstevel@tonic-gate  *	Here p1 is always the node that we are trying to insert or match in
4070Sstevel@tonic-gate  *	the device database.
4080Sstevel@tonic-gate  */
4090Sstevel@tonic-gate static int
usba_devdb_compare(const void * p1,const void * p2)4100Sstevel@tonic-gate usba_devdb_compare(const void *p1, const void *p2)
4110Sstevel@tonic-gate {
4120Sstevel@tonic-gate 	usba_configrec_t	*u1, *u2;
4130Sstevel@tonic-gate 	int	rval;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	u1 = ((usba_devdb_info_t *)p1)->usb_dev;
4160Sstevel@tonic-gate 	u2 = ((usba_devdb_info_t *)p2)->usb_dev;
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
4190Sstevel@tonic-gate 	    "usba_devdb_compare: p1=0x%p u1=0x%p p2=0x%p u2=0x%p",
420*7632SNick.Todd@Sun.COM 	    p1, (void *)u1, p2, (void *)u2);
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	/* first match vendor id */
4230Sstevel@tonic-gate 	if (u1->idVendor < u2->idVendor) {
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 		return (-1);
4260Sstevel@tonic-gate 	} else if (u1->idVendor > u2->idVendor) {
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 		return (+1);
4290Sstevel@tonic-gate 	} else {
4300Sstevel@tonic-gate 		/* idvendor match, now check idproduct */
4310Sstevel@tonic-gate 		if (u1->idProduct < u2->idProduct) {
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 			return (-1);
4340Sstevel@tonic-gate 		} else if (u1->idProduct > u2->idProduct) {
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 			return (+1);
4370Sstevel@tonic-gate 		} else {
4380Sstevel@tonic-gate 			/* idproduct match, now check serial no. */
4390Sstevel@tonic-gate 			if (u1->serialno && u2->serialno) {
4400Sstevel@tonic-gate 				rval = strcmp(u1->serialno, u2->serialno);
4410Sstevel@tonic-gate 				if (rval > 0) {
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 					return (+1);
4440Sstevel@tonic-gate 				} else if (rval < 0) {
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 					return (-1);
4470Sstevel@tonic-gate 				} else {
4480Sstevel@tonic-gate 					/* srno. matches */
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 					return (usb_devdb_compare_pathnames(
4510Sstevel@tonic-gate 					    u1->pathname, u2->pathname));
4520Sstevel@tonic-gate 				}
4530Sstevel@tonic-gate 			} else if ((u1->serialno == NULL) &&
4540Sstevel@tonic-gate 			    (u2->serialno == NULL)) {
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 				return (usb_devdb_compare_pathnames(
4570Sstevel@tonic-gate 				    u1->pathname, u2->pathname));
4580Sstevel@tonic-gate 			} else {
4590Sstevel@tonic-gate 				if (u1->serialno == NULL) {
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 					return (-1);
4620Sstevel@tonic-gate 				} else {
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 					return (+1);
4650Sstevel@tonic-gate 				}
4660Sstevel@tonic-gate 			}
4670Sstevel@tonic-gate 		}
4680Sstevel@tonic-gate 	}
4690Sstevel@tonic-gate }
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate /*
4730Sstevel@tonic-gate  * usba_devdb_build_device_database
4740Sstevel@tonic-gate  *	Builds a height balanced tree of all the records present in the file.
4750Sstevel@tonic-gate  *	Records that are "not enabled" and are duplicate are discarded.
4760Sstevel@tonic-gate  */
4770Sstevel@tonic-gate static int
usba_devdb_build_device_database()4780Sstevel@tonic-gate usba_devdb_build_device_database()
4790Sstevel@tonic-gate {
4800Sstevel@tonic-gate 	struct _buf	*file;
4810Sstevel@tonic-gate 	usba_configrec_t	*user_rec;
4820Sstevel@tonic-gate 	avl_index_t	where;
4830Sstevel@tonic-gate 	usba_devdb_info_t	*dbnode;
4840Sstevel@tonic-gate 	token_t		token;
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
4870Sstevel@tonic-gate 	    "usba_devdb_build_device_database: Start");
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	file = kobj_open_file(usbconf_file);
4900Sstevel@tonic-gate 	if (file != (struct _buf *)-1) {
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 		do {
4930Sstevel@tonic-gate 			user_rec = NULL;
4940Sstevel@tonic-gate 			token = usba_devdb_get_conf_rec(file, &user_rec);
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 			if (user_rec != NULL) {
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 				if ((user_rec->selection == NULL) ||
4990Sstevel@tonic-gate 				    (strcasecmp(user_rec->selection,
5000Sstevel@tonic-gate 				    "enable") != 0)) {
5010Sstevel@tonic-gate 					/* we don't store disabled entries */
5020Sstevel@tonic-gate 					usba_devdb_free_rec(user_rec);
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 					continue;
5050Sstevel@tonic-gate 				}
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 				dbnode = (usba_devdb_info_t *)kmem_zalloc(
5080Sstevel@tonic-gate 				    sizeof (usba_devdb_info_t), KM_SLEEP);
5090Sstevel@tonic-gate 				dbnode->usb_dev = user_rec;
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 				if (avl_find(&usba_devdb, dbnode, &where) ==
5120Sstevel@tonic-gate 				    NULL) {
5130Sstevel@tonic-gate 					/* insert new node */
5140Sstevel@tonic-gate 					avl_insert(&usba_devdb, dbnode, where);
5150Sstevel@tonic-gate 				} else {
5160Sstevel@tonic-gate 					/*
5170Sstevel@tonic-gate 					 * we don't maintain duplicate entries
5180Sstevel@tonic-gate 					 */
5190Sstevel@tonic-gate 					usba_devdb_free_rec(user_rec);
5200Sstevel@tonic-gate 					kmem_free(dbnode,
5210Sstevel@tonic-gate 					    sizeof (usba_devdb_info_t));
5220Sstevel@tonic-gate 				}
5230Sstevel@tonic-gate 			}
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 		} while (token != EOF);
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 		kobj_close_file(file);
5280Sstevel@tonic-gate 	}
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
5310Sstevel@tonic-gate 	    "usba_devdb_build_device_database: End");
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	/* XXX: return the no. of errors encountered */
5340Sstevel@tonic-gate 	return (0);
5350Sstevel@tonic-gate }
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate /*
5390Sstevel@tonic-gate  * usba_devdb_destroy_device_database
5400Sstevel@tonic-gate  *	Destory all records in the tree
5410Sstevel@tonic-gate  */
5420Sstevel@tonic-gate static void
usba_devdb_destroy_device_database()5430Sstevel@tonic-gate usba_devdb_destroy_device_database()
5440Sstevel@tonic-gate {
5450Sstevel@tonic-gate 	usba_devdb_info_t	*dbnode;
5460Sstevel@tonic-gate 	void			*cookie = NULL;
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
5490Sstevel@tonic-gate 	    "usba_devdb_destroy_device_database");
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	/* while there are nodes in the tree, keep destroying them */
5520Sstevel@tonic-gate 	while ((dbnode = (usba_devdb_info_t *)
5530Sstevel@tonic-gate 	    avl_destroy_nodes(&usba_devdb, &cookie)) != NULL) {
5540Sstevel@tonic-gate 		/*
5550Sstevel@tonic-gate 		 * destroy record
5560Sstevel@tonic-gate 		 * destroy tree node
5570Sstevel@tonic-gate 		 */
5580Sstevel@tonic-gate 		usba_devdb_free_rec(dbnode->usb_dev);
5590Sstevel@tonic-gate 		kmem_free(dbnode, sizeof (usba_devdb_info_t));
5600Sstevel@tonic-gate 	}
5610Sstevel@tonic-gate 	avl_destroy(&usba_devdb);
5620Sstevel@tonic-gate }
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate /*
5660Sstevel@tonic-gate  * usba_devdb_get_user_preferences
5670Sstevel@tonic-gate  *	Returns configrec structure to the caller that contains user
5680Sstevel@tonic-gate  *	preferences for the device pointed by the parameters.
5690Sstevel@tonic-gate  *	The first search is for a record that has serial number and/or
5700Sstevel@tonic-gate  *	a pathname. If search fails, we search for a rule that is generic
5710Sstevel@tonic-gate  *	i.e. without serial no. and pathname.
5720Sstevel@tonic-gate  */
5730Sstevel@tonic-gate usba_configrec_t *
usba_devdb_get_user_preferences(int idVendor,int idProduct,char * serialno,char * pathname)5740Sstevel@tonic-gate usba_devdb_get_user_preferences(int idVendor, int idProduct, char *serialno,
5750Sstevel@tonic-gate     char *pathname)
5760Sstevel@tonic-gate {
5770Sstevel@tonic-gate 	usba_configrec_t		*req_rec;
5780Sstevel@tonic-gate 	usba_devdb_info_t	*req_node, *dbnode;
5790Sstevel@tonic-gate 	avl_index_t		where;
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle,
5820Sstevel@tonic-gate 	    "usba_devdb_get_user_preferences");
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	req_rec = kmem_zalloc(sizeof (usba_configrec_t), KM_SLEEP);
5850Sstevel@tonic-gate 	req_node = kmem_zalloc(sizeof (usba_devdb_info_t), KM_SLEEP);
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	/* fill in the requested parameters */
5880Sstevel@tonic-gate 	req_rec->idVendor = idVendor;
5890Sstevel@tonic-gate 	req_rec->idProduct = idProduct;
5900Sstevel@tonic-gate 	req_rec->serialno = serialno;
5910Sstevel@tonic-gate 	req_rec->pathname = pathname;
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	req_node->usb_dev = req_rec;
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	rw_enter(&usba_devdb_lock, RW_READER);
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	/* try to find a perfect match in the device database */
5980Sstevel@tonic-gate 	dbnode = (usba_devdb_info_t *)avl_find(&usba_devdb, req_node, &where);
5990Sstevel@tonic-gate #ifdef __lock_lint
6000Sstevel@tonic-gate 	(void) usba_devdb_compare(req_node, dbnode);
6010Sstevel@tonic-gate #endif
6020Sstevel@tonic-gate 	if (dbnode == NULL) {
6030Sstevel@tonic-gate 		/* look for a generic rule */
6040Sstevel@tonic-gate 		req_rec->serialno = req_rec->pathname = NULL;
6050Sstevel@tonic-gate 		dbnode = (usba_devdb_info_t *)avl_find(&usba_devdb, req_node,
6060Sstevel@tonic-gate 		    &where);
6070Sstevel@tonic-gate #ifdef __lock_lint
6080Sstevel@tonic-gate 		(void) usba_devdb_compare(req_node, dbnode);
6090Sstevel@tonic-gate #endif
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 	rw_exit(&usba_devdb_lock);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 	kmem_free(req_rec, sizeof (usba_configrec_t));
6140Sstevel@tonic-gate 	kmem_free(req_node, sizeof (usba_devdb_info_t));
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	if (dbnode) {
6170Sstevel@tonic-gate 		return (dbnode->usb_dev);
6180Sstevel@tonic-gate 	} else {
6190Sstevel@tonic-gate 		return (NULL);
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate  * usba_devdb_refresh
6260Sstevel@tonic-gate  *	Reinitializes the device database. It destroys the old one and creates
6270Sstevel@tonic-gate  *	a new one by re-reading the file.
6280Sstevel@tonic-gate  */
6290Sstevel@tonic-gate int
usba_devdb_refresh()6300Sstevel@tonic-gate usba_devdb_refresh()
6310Sstevel@tonic-gate {
6320Sstevel@tonic-gate 	rw_enter(&usba_devdb_lock, RW_WRITER);
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	usba_build_devdb = B_TRUE;
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	/* destroy all nodes in the existing database */
6370Sstevel@tonic-gate 	usba_devdb_destroy_device_database();
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	/* now build a new one */
6400Sstevel@tonic-gate 	(void) usba_devdb_build_device_database();
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	usba_build_devdb = B_FALSE;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	rw_exit(&usba_devdb_lock);
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	return (0);
6470Sstevel@tonic-gate }
648