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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #define USBA_FRAMEWORK 30 #include <sys/ksynch.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 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 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 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) == NULL) { 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 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; 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 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 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", p1, p2); 333 334 if (p1 && p2) { 335 if (usba_build_devdb == B_TRUE) { 336 /* this is a straight string compare */ 337 rval = strcmp(p1, p2); 338 if (rval < 0) { 339 340 return (-1); 341 } else if (rval > 0) { 342 343 return (+1); 344 } else { 345 346 return (0); 347 } 348 } else { 349 /* 350 * Comparing on this is tricky. 351 * p1 is the string hubd is looking for & 352 * p2 is the string in the device db. 353 * At this point hubd knows: ../hubd@P/device@P 354 * while user will specify ..../hubd@P/keyboard@P 355 * First compare till .../hubd@P 356 * Second compare is just P in "device@P" 357 */ 358 ustr = strrchr(p2, '/'); 359 hstr = strrchr(p1, '/'); 360 rval = strncmp(p1, p2, MAX(ustr - p2, hstr - p1)); 361 if (rval < 0) { 362 363 return (-1); 364 } else if (rval > 0) { 365 366 return (+1); 367 } else { 368 /* now compare the ports */ 369 hstr = p1 + strlen(p1) -1; 370 ustr = p2 + strlen(p2) -1; 371 372 if (*hstr < *ustr) { 373 374 return (-1); 375 } else if (*hstr > *ustr) { 376 377 return (+1); 378 } else { 379 /* finally got a match */ 380 381 return (0); 382 } 383 } 384 } 385 } else if ((p1 == NULL) && (p2 == NULL)) { 386 387 return (0); 388 } else { 389 if (p1 == NULL) { 390 391 return (-1); 392 } else { 393 394 return (+1); 395 } 396 } 397 } 398 399 400 /* 401 * usba_devdb_compare 402 * Compares the two nodes. Returns -1 when p1 < p2, 0 when p1 == p2 403 * and +1 when p1 > p2. This function is invoked by avl_find 404 * Here p1 is always the node that we are trying to insert or match in 405 * the device database. 406 */ 407 static int 408 usba_devdb_compare(const void *p1, const void *p2) 409 { 410 usba_configrec_t *u1, *u2; 411 int rval; 412 413 u1 = ((usba_devdb_info_t *)p1)->usb_dev; 414 u2 = ((usba_devdb_info_t *)p2)->usb_dev; 415 416 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle, 417 "usba_devdb_compare: p1=0x%p u1=0x%p p2=0x%p u2=0x%p", 418 p1, u1, p2, u2); 419 420 /* first match vendor id */ 421 if (u1->idVendor < u2->idVendor) { 422 423 return (-1); 424 } else if (u1->idVendor > u2->idVendor) { 425 426 return (+1); 427 } else { 428 /* idvendor match, now check idproduct */ 429 if (u1->idProduct < u2->idProduct) { 430 431 return (-1); 432 } else if (u1->idProduct > u2->idProduct) { 433 434 return (+1); 435 } else { 436 /* idproduct match, now check serial no. */ 437 if (u1->serialno && u2->serialno) { 438 rval = strcmp(u1->serialno, u2->serialno); 439 if (rval > 0) { 440 441 return (+1); 442 } else if (rval < 0) { 443 444 return (-1); 445 } else { 446 /* srno. matches */ 447 448 return (usb_devdb_compare_pathnames( 449 u1->pathname, u2->pathname)); 450 } 451 } else if ((u1->serialno == NULL) && 452 (u2->serialno == NULL)) { 453 454 return (usb_devdb_compare_pathnames( 455 u1->pathname, u2->pathname)); 456 } else { 457 if (u1->serialno == NULL) { 458 459 return (-1); 460 } else { 461 462 return (+1); 463 } 464 } 465 } 466 } 467 } 468 469 470 /* 471 * usba_devdb_build_device_database 472 * Builds a height balanced tree of all the records present in the file. 473 * Records that are "not enabled" and are duplicate are discarded. 474 */ 475 static int 476 usba_devdb_build_device_database() 477 { 478 struct _buf *file; 479 usba_configrec_t *user_rec; 480 avl_index_t where; 481 usba_devdb_info_t *dbnode; 482 token_t token; 483 484 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle, 485 "usba_devdb_build_device_database: Start"); 486 487 file = kobj_open_file(usbconf_file); 488 if (file != (struct _buf *)-1) { 489 490 do { 491 user_rec = NULL; 492 token = usba_devdb_get_conf_rec(file, &user_rec); 493 494 if (user_rec != NULL) { 495 496 if ((user_rec->selection == NULL) || 497 (strcasecmp(user_rec->selection, 498 "enable") != 0)) { 499 /* we don't store disabled entries */ 500 usba_devdb_free_rec(user_rec); 501 502 continue; 503 } 504 505 dbnode = (usba_devdb_info_t *)kmem_zalloc( 506 sizeof (usba_devdb_info_t), KM_SLEEP); 507 dbnode->usb_dev = user_rec; 508 509 if (avl_find(&usba_devdb, dbnode, &where) == 510 NULL) { 511 /* insert new node */ 512 avl_insert(&usba_devdb, dbnode, where); 513 } else { 514 /* 515 * we don't maintain duplicate entries 516 */ 517 usba_devdb_free_rec(user_rec); 518 kmem_free(dbnode, 519 sizeof (usba_devdb_info_t)); 520 } 521 } 522 523 } while (token != EOF); 524 525 kobj_close_file(file); 526 } 527 528 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle, 529 "usba_devdb_build_device_database: End"); 530 531 /* XXX: return the no. of errors encountered */ 532 return (0); 533 } 534 535 536 /* 537 * usba_devdb_destroy_device_database 538 * Destory all records in the tree 539 */ 540 static void 541 usba_devdb_destroy_device_database() 542 { 543 usba_devdb_info_t *dbnode; 544 void *cookie = NULL; 545 546 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle, 547 "usba_devdb_destroy_device_database"); 548 549 /* while there are nodes in the tree, keep destroying them */ 550 while ((dbnode = (usba_devdb_info_t *) 551 avl_destroy_nodes(&usba_devdb, &cookie)) != NULL) { 552 /* 553 * destroy record 554 * destroy tree node 555 */ 556 usba_devdb_free_rec(dbnode->usb_dev); 557 kmem_free(dbnode, sizeof (usba_devdb_info_t)); 558 } 559 avl_destroy(&usba_devdb); 560 } 561 562 563 /* 564 * usba_devdb_get_user_preferences 565 * Returns configrec structure to the caller that contains user 566 * preferences for the device pointed by the parameters. 567 * The first search is for a record that has serial number and/or 568 * a pathname. If search fails, we search for a rule that is generic 569 * i.e. without serial no. and pathname. 570 */ 571 usba_configrec_t * 572 usba_devdb_get_user_preferences(int idVendor, int idProduct, char *serialno, 573 char *pathname) 574 { 575 usba_configrec_t *req_rec; 576 usba_devdb_info_t *req_node, *dbnode; 577 avl_index_t where; 578 579 USB_DPRINTF_L4(DPRINT_MASK_DEVDB, usba_devdb_log_handle, 580 "usba_devdb_get_user_preferences"); 581 582 req_rec = kmem_zalloc(sizeof (usba_configrec_t), KM_SLEEP); 583 req_node = kmem_zalloc(sizeof (usba_devdb_info_t), KM_SLEEP); 584 585 /* fill in the requested parameters */ 586 req_rec->idVendor = idVendor; 587 req_rec->idProduct = idProduct; 588 req_rec->serialno = serialno; 589 req_rec->pathname = pathname; 590 591 req_node->usb_dev = req_rec; 592 593 rw_enter(&usba_devdb_lock, RW_READER); 594 595 /* try to find a perfect match in the device database */ 596 dbnode = (usba_devdb_info_t *)avl_find(&usba_devdb, req_node, &where); 597 #ifdef __lock_lint 598 (void) usba_devdb_compare(req_node, dbnode); 599 #endif 600 if (dbnode == NULL) { 601 /* look for a generic rule */ 602 req_rec->serialno = req_rec->pathname = NULL; 603 dbnode = (usba_devdb_info_t *)avl_find(&usba_devdb, req_node, 604 &where); 605 #ifdef __lock_lint 606 (void) usba_devdb_compare(req_node, dbnode); 607 #endif 608 } 609 rw_exit(&usba_devdb_lock); 610 611 kmem_free(req_rec, sizeof (usba_configrec_t)); 612 kmem_free(req_node, sizeof (usba_devdb_info_t)); 613 614 if (dbnode) { 615 return (dbnode->usb_dev); 616 } else { 617 return (NULL); 618 } 619 } 620 621 622 /* 623 * usba_devdb_refresh 624 * Reinitializes the device database. It destroys the old one and creates 625 * a new one by re-reading the file. 626 */ 627 int 628 usba_devdb_refresh() 629 { 630 rw_enter(&usba_devdb_lock, RW_WRITER); 631 632 usba_build_devdb = B_TRUE; 633 634 /* destroy all nodes in the existing database */ 635 usba_devdb_destroy_device_database(); 636 637 /* now build a new one */ 638 (void) usba_devdb_build_device_database(); 639 640 usba_build_devdb = B_FALSE; 641 642 rw_exit(&usba_devdb_lock); 643 644 return (0); 645 } 646