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 2018 OmniOS Community Edition (OmniOSce) Association. 25 */ 26 27 28 #include "cfga_usb.h" 29 30 31 /* function prototypes */ 32 cfga_err_t usb_err_msg(char **, cfga_usb_ret_t, const char *, int); 33 extern cfga_usb_ret_t usb_rcm_offline(const char *, char **, char *, 34 cfga_flags_t); 35 extern cfga_usb_ret_t usb_rcm_online(const char *, char **, char *, 36 cfga_flags_t); 37 extern cfga_usb_ret_t usb_rcm_remove(const char *, char **, char *, 38 cfga_flags_t); 39 static int usb_confirm(struct cfga_confirm *, char *); 40 static char *usb_get_devicepath(const char *); 41 42 /* 43 * This file contains the entry points to the plugin as defined in the 44 * config_admin(3X) man page. 45 */ 46 47 /* 48 * Set the version number for the cfgadm library's use. 49 */ 50 int cfga_version = CFGA_HSL_V2; 51 52 #define HELP_HEADER 1 53 #define HELP_CONFIG 2 54 #define HELP_RESET_SLOT 3 55 #define HELP_CONFIG_SLOT 4 56 #define HELP_UNKNOWN 5 57 58 /* Help messages */ 59 static char * 60 usb_help[] = { 61 NULL, 62 "USB specific commands:\n", 63 " cfgadm -c [configure|unconfigure|disconnect] ap_id [ap_id...]\n", 64 " cfgadm -x usb_reset ap_id [ap_id...]\n", 65 " cfgadm -x usb_config -o config=<index of desired configuration> ap_id\n", 66 "\tunknown command or option: ", 67 NULL 68 }; /* End help messages */ 69 70 /* Error messages */ 71 static msgcvt_t 72 usb_error_msgs[] = { 73 /* CFGA_USB_OK */ 74 { CVT, CFGA_OK, "ok" }, 75 76 /* CFGA_USB_UNKNOWN */ 77 { CVT, CFGA_LIB_ERROR, "Unknown message; internal error" }, 78 79 /* CFGA_USB_INTERNAL_ERROR */ 80 { CVT, CFGA_LIB_ERROR, "Internal error" }, 81 82 /* CFGA_USB_OPTIONS */ 83 { CVT, CFGA_ERROR, "Hardware specific options not supported" }, 84 85 /* CFGA_USB_DYNAMIC_AP */ 86 { CVT, CFGA_INVAL, "Dynamic attachment points not supported" }, 87 88 /* CFGA_USB_AP */ 89 { CVT, CFGA_APID_NOEXIST, "" }, 90 91 /* CFGA_USB_PORT */ 92 { CVT, CFGA_LIB_ERROR, "Cannot determine hub port number for " }, 93 94 /* CFGA_USB_DEVCTL */ 95 { CVT, CFGA_ERROR, "Cannot issue devctl to " }, 96 97 /* CFGA_USB_NOT_CONNECTED */ 98 { CVT, CFGA_INVAL, "No device connected to " }, 99 100 /* CFGA_USB_NOT_CONFIGURED */ 101 { CVT, CFGA_INVAL, "No device configured to " }, 102 103 /* CFGA_USB_ALREADY_CONNECTED */ 104 { CVT, CFGA_INSUFFICENT_CONDITION, 105 "Device already connected; cannot connect again " }, 106 107 /* CFGA_USB_ALREADY_CONFIGURED */ 108 { CVT, CFGA_INVAL, "device already configured for " }, 109 110 /* CFGA_USB_OPEN */ 111 { CVT, CFGA_LIB_ERROR, "Cannot open " }, 112 113 /* CFGA_USB_IOCTL */ 114 { CVT, CFGA_ERROR, "Driver ioctl failed " }, 115 116 /* CFGA_USB_BUSY */ 117 { CVT, CFGA_SYSTEM_BUSY, "" }, 118 119 /* CFGA_USB_ALLOC_FAIL */ 120 { CVT, CFGA_LIB_ERROR, "Memory allocation failure" }, 121 122 /* CFGA_USB_OPNOTSUPP */ 123 { CVT, CFGA_OPNOTSUPP, "Operation not supported" }, 124 125 /* CFGA_USB_DEVLINK */ 126 { CVT, CFGA_LIB_ERROR, "Could not find /dev/cfg link for " }, 127 128 /* CFGA_USB_STATE */ 129 { CVT, CFGA_LIB_ERROR, "Internal error: Unrecognized ap state" }, 130 131 /* CFGA_USB_CONFIG_INVAL */ 132 { CVT, CFGA_ERROR, 133 "Specified configuration index unrecognized or exceeds " 134 "maximum available" }, 135 136 /* CFGA_USB_PRIV */ 137 { CVT, CFGA_PRIV, "" }, 138 139 /* CFGA_USB_NVLIST */ 140 { CVT, CFGA_ERROR, "Internal error (nvlist)" }, 141 142 /* CFGA_USB_ZEROLEN */ 143 { CVT, CFGA_ERROR, "Internal error (zerolength string)" }, 144 145 /* CFGA_USB_CONFIG_FILE */ 146 { CVT, CFGA_ERROR, 147 "Cannot open/fstat/read USB system configuration file" }, 148 149 /* CFGA_USB_LOCK_FILE */ 150 { CVT, CFGA_ERROR, "Cannot lock USB system configuration file" }, 151 152 /* CFGA_USB_UNLOCK_FILE */ 153 { CVT, CFGA_ERROR, "Cannot unlock USB system configuration file" }, 154 155 /* CFGA_USB_ONE_CONFIG */ 156 { CVT, CFGA_ERROR, 157 "Operation not supported for devices with one configuration" }, 158 159 /* CFGA_USB_RCM_HANDLE Errors */ 160 { CVT, CFGA_ERROR, "cannot get RCM handle"}, 161 162 /* CFGA_USB_RCM_ONLINE */ 163 { CVT, CFGA_SYSTEM_BUSY, "failed to online: "}, 164 165 /* CFGA_USB_RCM_OFFLINE */ 166 { CVT, CFGA_SYSTEM_BUSY, "failed to offline: "}, 167 168 /* CFGA_USB_RCM_INFO */ 169 { CVT, CFGA_ERROR, "failed to query: "} 170 171 }; /* End error messages */ 172 173 174 /* ========================================================================= */ 175 /* 176 * The next two funcs imported verbatim from cfgadm_scsi. 177 * physpath_to_devlink is the only func directly used by cfgadm_usb. 178 * get_link supports it. 179 */ 180 181 /* 182 * Routine to search the /dev directory or a subtree of /dev. 183 */ 184 static int 185 get_link(di_devlink_t devlink, void *arg) 186 { 187 walk_link_t *larg = (walk_link_t *)arg; 188 189 /* 190 * When path is specified, it's the node path without minor 191 * name. Therefore, the ../.. prefixes needs to be stripped. 192 */ 193 if (larg->path) { 194 char *content = (char *)di_devlink_content(devlink); 195 char *start = strstr(content, "/devices/"); 196 197 /* line content must have minor node */ 198 if (start == NULL || 199 strncmp(start, larg->path, larg->len) != 0 || 200 start[larg->len] != ':') { 201 202 return (DI_WALK_CONTINUE); 203 } 204 } 205 206 *(larg->linkpp) = strdup(di_devlink_path(devlink)); 207 208 return (DI_WALK_TERMINATE); 209 } 210 211 212 /* ARGSUSED */ 213 static ucfga_ret_t 214 physpath_to_devlink( 215 const char *basedir, 216 const char *node_path, 217 char **logpp, 218 int *l_errnop, 219 int match_minor) 220 { 221 walk_link_t larg; 222 di_devlink_handle_t hdl; 223 char *minor_path; 224 225 if ((hdl = di_devlink_init(NULL, 0)) == NULL) { 226 *l_errnop = errno; 227 return (UCFGA_LIB_ERR); 228 } 229 230 *logpp = NULL; 231 larg.linkpp = logpp; 232 if (match_minor) { 233 minor_path = (char *)node_path + strlen("/devices"); 234 larg.path = NULL; 235 } else { 236 minor_path = NULL; 237 larg.len = strlen(node_path); 238 larg.path = (char *)node_path; 239 } 240 241 (void) di_devlink_walk(hdl, "^cfg/", minor_path, DI_PRIMARY_LINK, 242 (void *)&larg, get_link); 243 244 (void) di_devlink_fini(&hdl); 245 246 if (*logpp == NULL) { 247 *l_errnop = errno; 248 return (UCFGA_LIB_ERR); 249 } 250 251 return (UCFGA_OK); 252 } 253 254 255 /* ========================================================================= */ 256 /* Utilities */ 257 258 /* 259 * Given the index into a table (msgcvt_t) of messages, get the message 260 * string, converting it to the proper locale if necessary. 261 * NOTE: See cfga_usb.h 262 */ 263 static const char * 264 get_msg(uint_t msg_index, msgcvt_t *msg_tbl, uint_t tbl_size) 265 { 266 if (msg_index >= tbl_size) { 267 DPRINTF("get_error_msg: bad error msg index: %d\n", msg_index); 268 msg_index = CFGA_USB_UNKNOWN; 269 } 270 271 return ((msg_tbl[msg_index].intl) ? 272 dgettext(TEXT_DOMAIN, msg_tbl[msg_index].msgstr) : 273 msg_tbl[msg_index].msgstr); 274 } 275 276 277 /* 278 * Allocates and creates a message string (in *ret_str), 279 * by concatenating all the (char *) args together, in order. 280 * Last arg MUST be NULL. 281 */ 282 static void 283 set_msg(char **ret_str, ...) 284 { 285 char *str; 286 size_t total_len; 287 va_list valist; 288 289 va_start(valist, ret_str); 290 291 total_len = (*ret_str == NULL) ? 0 : strlen(*ret_str); 292 293 while ((str = va_arg(valist, char *)) != NULL) { 294 size_t len = strlen(str); 295 char *old_str = *ret_str; 296 297 *ret_str = (char *)realloc(*ret_str, total_len + len + 1); 298 if (*ret_str == NULL) { 299 /* We're screwed */ 300 free(old_str); 301 DPRINTF("set_msg: realloc failed.\n"); 302 va_end(valist); 303 return; 304 } 305 306 (void) strcpy(*ret_str + total_len, str); 307 total_len += len; 308 } 309 310 va_end(valist); 311 } 312 313 314 /* 315 * Error message handling. 316 * For the rv passed in, looks up the corresponding error message string(s), 317 * internationalized it if necessary, and concatenates it into a new 318 * memory buffer, and points *errstring to it. 319 * Note not all rvs will result in an error message return, as not all 320 * error conditions warrant a USB-specific error message. 321 * 322 * Some messages may display ap_id or errno, which is why they are passed 323 * in. 324 */ 325 cfga_err_t 326 usb_err_msg(char **errstring, cfga_usb_ret_t rv, const char *ap_id, int l_errno) 327 { 328 if (errstring == NULL) { 329 330 return (usb_error_msgs[rv].cfga_err); 331 } 332 333 /* 334 * Generate the appropriate USB-specific error message(s) (if any). 335 */ 336 switch (rv) { 337 case CFGA_USB_OK: 338 /* Special case - do nothing. */ 339 break; 340 341 case CFGA_USB_UNKNOWN: 342 case CFGA_USB_DYNAMIC_AP: 343 case CFGA_USB_INTERNAL_ERROR: 344 case CFGA_USB_OPTIONS: 345 case CFGA_USB_ALLOC_FAIL: 346 case CFGA_USB_STATE: 347 case CFGA_USB_CONFIG_INVAL: 348 case CFGA_USB_PRIV: 349 case CFGA_USB_OPNOTSUPP: 350 /* These messages require no additional strings passed. */ 351 set_msg(errstring, ERR_STR(rv), NULL); 352 break; 353 354 case CFGA_USB_AP: 355 case CFGA_USB_PORT: 356 case CFGA_USB_NOT_CONNECTED: 357 case CFGA_USB_NOT_CONFIGURED: 358 case CFGA_USB_ALREADY_CONNECTED: 359 case CFGA_USB_ALREADY_CONFIGURED: 360 case CFGA_USB_BUSY: 361 case CFGA_USB_DEVLINK: 362 case CFGA_USB_RCM_HANDLE: 363 case CFGA_USB_RCM_ONLINE: 364 case CFGA_USB_RCM_OFFLINE: 365 case CFGA_USB_RCM_INFO: 366 case CFGA_USB_DEVCTL: 367 /* These messages also print ap_id. */ 368 (void) set_msg(errstring, ERR_STR(rv), 369 "ap_id: ", ap_id, "", NULL); 370 break; 371 372 case CFGA_USB_IOCTL: 373 case CFGA_USB_NVLIST: 374 case CFGA_USB_CONFIG_FILE: 375 case CFGA_USB_ONE_CONFIG: 376 /* These messages also print errno. */ 377 { 378 char *errno_str = l_errno ? strerror(l_errno) : ""; 379 380 set_msg(errstring, ERR_STR(rv), errno_str, 381 l_errno ? "\n" : "", NULL); 382 break; 383 } 384 385 case CFGA_USB_OPEN: 386 /* These messages also apid and errno. */ 387 { 388 char *errno_str = l_errno ? strerror(l_errno) : ""; 389 390 set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "\n", 391 errno_str, l_errno ? "\n" : "", NULL); 392 break; 393 } 394 395 default: 396 DPRINTF("usb_err_msg: Unrecognized message index: %d\n", rv); 397 set_msg(errstring, ERR_STR(CFGA_USB_INTERNAL_ERROR), NULL); 398 399 } /* end switch */ 400 401 /* 402 * Determine the proper error code to send back to the cfgadm library. 403 */ 404 return (usb_error_msgs[rv].cfga_err); 405 } 406 407 408 /* 409 * Ensure the ap_id passed is in the correct (physical ap_id) form: 410 * path/device:xx[.xx]+ 411 * where xx is a one or two-digit number. 412 * 413 * Note the library always calls the plugin with a physical ap_id. 414 */ 415 static int 416 verify_valid_apid(const char *ap_id) 417 { 418 char *l_ap_id; 419 420 if (ap_id == NULL) { 421 return (-1); 422 } 423 424 l_ap_id = strrchr(ap_id, *MINOR_SEP); 425 l_ap_id++; 426 427 if (strspn(l_ap_id, "0123456789.") != strlen(l_ap_id)) { 428 /* Bad characters in the ap_id. */ 429 return (-1); 430 } 431 432 if (strstr(l_ap_id, "..") != NULL) { 433 /* ap_id has 1..2 or more than 2 dots */ 434 return (-1); 435 } 436 437 return (0); 438 } 439 440 441 /* 442 * Verify the params passed in are valid. 443 */ 444 static cfga_usb_ret_t 445 verify_params( 446 const char *ap_id, 447 const char *options, 448 char **errstring) 449 { 450 if (errstring != NULL) { 451 *errstring = NULL; 452 } 453 454 if (options != NULL) { 455 DPRINTF("verify_params: hardware-specific options not " 456 "supported.\n"); 457 return (CFGA_USB_OPTIONS); 458 } 459 460 /* Dynamic attachment points not supported (yet). */ 461 if (GET_DYN(ap_id) != NULL) { 462 DPRINTF("verify_params: dynamic ap_id passed\n"); 463 return (CFGA_USB_DYNAMIC_AP); 464 } 465 466 if (verify_valid_apid(ap_id) != 0) { 467 DPRINTF("verify_params: not a USB ap_id.\n"); 468 return (CFGA_USB_AP); 469 } 470 471 return (CFGA_USB_OK); 472 } 473 474 475 /* 476 * Takes a validated ap_id and extracts the port number. 477 */ 478 static cfga_usb_ret_t 479 get_port_num(const char *ap_id, uint_t *port) 480 { 481 char *port_nbr_str; 482 char *temp; 483 484 port_nbr_str = strrchr(ap_id, *MINOR_SEP) + strlen(MINOR_SEP); 485 if ((temp = strrchr(ap_id, (int)*PORT_SEPERATOR)) != 0) { 486 port_nbr_str = temp + strlen(PORT_SEPERATOR); 487 } 488 489 errno = 0; 490 *port = strtol(port_nbr_str, NULL, 10); 491 if (errno) { 492 DPRINTF("get_port_num: conversion of port str failed\n"); 493 return (CFGA_USB_PORT); 494 } 495 496 return (CFGA_USB_OK); 497 } 498 499 500 /* 501 * Pair of routines to set up for/clean up after a devctl_ap_* lib call. 502 */ 503 static void 504 cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist) 505 { 506 if (user_nvlist != NULL) { 507 nvlist_free(user_nvlist); 508 } 509 if (devctl_hdl != NULL) { 510 devctl_release(devctl_hdl); 511 } 512 } 513 514 515 static cfga_usb_ret_t 516 setup_for_devctl_cmd(const char *ap_id, devctl_hdl_t *devctl_hdl, 517 nvlist_t **user_nvlistp, uint_t oflag) 518 { 519 uint32_t port; 520 cfga_usb_ret_t rv = CFGA_USB_OK; 521 522 DPRINTF("setup_for_devctl_cmd: oflag=%d\n", oflag); 523 524 /* Get a handle to the ap */ 525 if ((*devctl_hdl = devctl_ap_acquire((char *)ap_id, oflag)) == NULL) { 526 DPRINTF("setup_for_devctl_cmd: devctl_ap_acquire failed with " 527 "errno: %d\n", errno); 528 rv = CFGA_USB_DEVCTL; 529 goto bailout; 530 } 531 532 /* Set up to pass port number down to driver */ 533 if (nvlist_alloc(user_nvlistp, NV_UNIQUE_NAME_TYPE, 0) != 0) { 534 DPRINTF("setup_for_devctl: nvlist_alloc failed, errno: %d\n", 535 errno); 536 *user_nvlistp = NULL; /* Prevent possible incorrect free in */ 537 /* cleanup_after_devctl_cmd */ 538 rv = CFGA_USB_NVLIST; 539 goto bailout; 540 } 541 542 if ((rv = get_port_num(ap_id, &port)) != CFGA_USB_OK) { 543 DPRINTF("setup_for_devctl_cmd: get_port_num, errno: %d\n", 544 errno); 545 goto bailout; 546 } 547 548 /* creates an int32_t entry */ 549 if (nvlist_add_int32(*user_nvlistp, PORT, port) == -1) { 550 DPRINTF("setup_for_devctl_cmd: nvlist_add_int32 failed. " 551 "errno: %d\n", errno); 552 rv = CFGA_USB_NVLIST; 553 goto bailout; 554 } 555 556 return (rv); 557 558 bailout: 559 cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp); 560 561 return (rv); 562 } 563 564 565 /* 566 * Ensure that there's a device actually connected to the ap 567 */ 568 static cfga_usb_ret_t 569 device_configured(devctl_hdl_t hdl, nvlist_t *nvl, ap_rstate_t *rstate) 570 { 571 cfga_usb_ret_t rv; 572 devctl_ap_state_t devctl_ap_state; 573 574 DPRINTF("device_configured:\n"); 575 if (devctl_ap_getstate(hdl, nvl, &devctl_ap_state) == -1) { 576 DPRINTF("devctl_ap_getstate failed, errno: %d\n", errno); 577 return (CFGA_USB_DEVCTL); 578 } 579 580 rv = CFGA_USB_ALREADY_CONFIGURED; 581 *rstate = devctl_ap_state.ap_rstate; 582 if (devctl_ap_state.ap_ostate != AP_OSTATE_CONFIGURED) { 583 return (CFGA_USB_NOT_CONFIGURED); 584 } 585 586 return (rv); 587 } 588 589 590 /* 591 * Ensure that there's a device actually connected to the ap 592 */ 593 static cfga_usb_ret_t 594 device_connected(devctl_hdl_t hdl, nvlist_t *list, ap_ostate_t *ostate) 595 { 596 cfga_usb_ret_t rv = CFGA_USB_ALREADY_CONNECTED; 597 devctl_ap_state_t devctl_ap_state; 598 599 DPRINTF("device_connected:\n"); 600 601 if (devctl_ap_getstate(hdl, list, &devctl_ap_state) == -1) { 602 DPRINTF("devctl_ap_getstate failed, errno: %d\n", errno); 603 return (CFGA_USB_DEVCTL); 604 } 605 606 *ostate = devctl_ap_state.ap_ostate; 607 if (devctl_ap_state.ap_rstate != AP_RSTATE_CONNECTED) { 608 return (CFGA_USB_NOT_CONNECTED); 609 } 610 611 return (rv); 612 } 613 614 615 /* 616 * Given a subcommand to the DEVCTL_AP_CONTROL ioctl, rquest the size of 617 * the data to be returned, allocate a buffer, then get the data. 618 * Returns *descrp (which must be freed) and size. 619 * 620 * Note USB_DESCR_TYPE_STRING returns an ASCII NULL-terminated string, 621 * not a string descr. 622 */ 623 cfga_usb_ret_t 624 do_control_ioctl(const char *ap_id, uint_t subcommand, uint_t arg, 625 void **descrp, size_t *sizep) 626 { 627 int fd = -1; 628 uint_t port; 629 uint32_t local_size; 630 cfga_usb_ret_t rv = CFGA_USB_OK; 631 struct hubd_ioctl_data ioctl_data; 632 633 assert(descrp != NULL); 634 *descrp = NULL; 635 assert(sizep != NULL); 636 637 if ((rv = get_port_num(ap_id, &port)) != CFGA_USB_OK) { 638 goto bailout; 639 } 640 641 if ((fd = open(ap_id, O_RDONLY)) == -1) { 642 DPRINTF("do_control_ioctl: open failed: errno:%d\n", errno); 643 rv = CFGA_USB_OPEN; 644 if (errno == EBUSY) { 645 rv = CFGA_USB_BUSY; 646 } 647 goto bailout; 648 } 649 650 ioctl_data.cmd = subcommand; 651 ioctl_data.port = port; 652 ioctl_data.misc_arg = (uint_t)arg; 653 654 /* 655 * Find out how large a buf we need to get the data. 656 * 657 * Note the ioctls only accept/return a 32-bit int for a get_size 658 * to avoid 32/64 and BE/LE issues. 659 */ 660 ioctl_data.get_size = B_TRUE; 661 ioctl_data.buf = (caddr_t)&local_size; 662 ioctl_data.bufsiz = sizeof (local_size); 663 664 if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) { 665 DPRINTF("do_control_ioctl: size ioctl failed: errno:%d\n", 666 errno); 667 rv = CFGA_USB_IOCTL; 668 goto bailout; 669 } 670 *sizep = local_size; 671 672 if (subcommand == USB_DESCR_TYPE_STRING && 673 arg == HUBD_CFG_DESCR_STR && local_size == 0) { 674 /* Zero-length data - nothing to do. */ 675 rv = CFGA_USB_ZEROLEN; 676 goto bailout; 677 } 678 if (subcommand == HUBD_REFRESH_DEVDB) { 679 /* Already done - no data transfer; nothing left to do. */ 680 goto bailout; 681 } 682 683 if ((*descrp = malloc(*sizep)) == NULL) { 684 DPRINTF("do_control_ioctl: malloc failed\n"); 685 rv = CFGA_USB_ALLOC_FAIL; 686 goto bailout; 687 } 688 689 /* Get the data */ 690 ioctl_data.get_size = B_FALSE; 691 ioctl_data.buf = *descrp; 692 ioctl_data.bufsiz = *sizep; 693 694 if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) { 695 DPRINTF("do_control_ioctl: ioctl failed: errno:%d\n", 696 errno); 697 rv = CFGA_USB_IOCTL; 698 goto bailout; 699 } 700 701 (void) close(fd); 702 703 return (rv); 704 705 706 bailout: 707 if (fd != -1) { 708 (void) close(fd); 709 } 710 if (*descrp != NULL) { 711 free(*descrp); 712 *descrp = NULL; 713 } 714 715 if (rv == CFGA_USB_IOCTL && errno == EBUSY) { 716 rv = CFGA_USB_BUSY; /* Provide more useful msg */ 717 } 718 719 return (rv); 720 } 721 722 723 /* ========================================================================= */ 724 /* 725 * Support funcs called directly from cfga_* entry points. 726 */ 727 728 729 /* 730 * Invoked from cfga_private_func. 731 * Modify the USB persistant configuration file so that the device 732 * represented by ap_id will henceforth be initialized to the desired 733 * configuration setting (configuration index). 734 */ 735 static cfga_usb_ret_t 736 set_configuration(const char *ap_id, uint_t config, char *driver, 737 usb_dev_descr_t *descrp, char **errstring) 738 { 739 char *serial_no = NULL; 740 char *dev_path = NULL; 741 char *tmp; 742 size_t size; 743 cfga_usb_ret_t rv = CFGA_USB_OK; 744 745 DPRINTF("set_configuration: ap_id: %s, config:%d\n", ap_id, config); 746 747 /* Only one bNumConfigurations, don't allow this operation */ 748 if (descrp->bNumConfigurations == 1) { 749 DPRINTF("device supports %d configurations\n", 750 descrp->bNumConfigurations); 751 rv = CFGA_USB_ONE_CONFIG; 752 goto bailout; 753 } 754 755 /* get the serial number string if it exists */ 756 if (descrp->iSerialNumber != 0) { 757 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 758 HUBD_SERIALNO_STR, (void **)&serial_no, &size)) != 759 CFGA_USB_OK) { 760 if (rv != CFGA_USB_ZEROLEN) { 761 DPRINTF("set_configuration: get serial " 762 "no string failed\n"); 763 goto bailout; 764 } 765 } 766 } 767 768 dev_path = usb_get_devicepath(ap_id); 769 if (dev_path == NULL) { 770 DPRINTF("get device path failed\n"); 771 rv = CFGA_USB_DEVCTL; 772 goto bailout; 773 } 774 775 DPRINTF("calling add_entry: vid: 0x%x pid:0x%x config:0x%x,", 776 descrp->idVendor, descrp->idProduct, config); 777 DPRINTF("serial_no: %s\n\tdev_path: %s\n\tdriver: %s\n", serial_no ? 778 serial_no : "", dev_path ? dev_path : "", driver ? driver : ""); 779 780 /* 781 * the devicepath should be an absolute path. 782 * So, if path has leading "/devices" - nuke it. 783 */ 784 if (strncmp(dev_path, "/devices/", 9) == 0) { 785 tmp = dev_path + 8; 786 } else { 787 tmp = dev_path; 788 } 789 790 /* Save an entry in the USBCONF_FILE */ 791 if ((rv = add_entry( 792 "enable", /* Always to "enable" */ 793 descrp->idVendor, /* vendorId */ 794 descrp->idProduct, /* ProductId */ 795 config, /* new cfgndx */ 796 serial_no, /* serial no string */ 797 tmp, /* device path */ 798 driver, /* Driver (optional) */ 799 errstring)) 800 != CFGA_USB_OK) { 801 DPRINTF("set_configuration: add_entry failed\n"); 802 goto bailout; 803 } 804 805 /* Notify hubd that it needs to refresh its db. */ 806 if ((rv = do_control_ioctl(ap_id, HUBD_REFRESH_DEVDB, 0, 807 (void **)&dev_path, &size)) != CFGA_USB_OK) { 808 DPRINTF("set_configuration: HUBD_REFRESH_DEVDB failed\n"); 809 goto bailout; 810 } 811 812 bailout: 813 if (dev_path) { 814 free(dev_path); 815 } 816 if (serial_no) { 817 free(serial_no); 818 } 819 820 return (rv); 821 } 822 823 824 /* 825 * Invoked from cfga_private_func() and fill_in_ap_info(). 826 * Call into USBA and get the current configuration setting for this device, 827 */ 828 static cfga_usb_ret_t 829 get_config(const char *ap_id, uint_t *config) 830 { 831 size_t size; 832 uint_t *config_val = NULL; 833 cfga_usb_ret_t rv; 834 835 if ((rv = do_control_ioctl(ap_id, HUBD_GET_CURRENT_CONFIG, 0, 836 (void **)&config_val, &size)) != CFGA_USB_OK) { 837 DPRINTF("get_config: get current config descr failed\n"); 838 goto bailout; 839 } 840 *config = *config_val; 841 842 bailout: 843 free(config_val); 844 return (rv); 845 } 846 847 848 /* 849 * Invoked from cfga_private_func. 850 * it does an unconfigure of the device followed by a configure, 851 * thus essentially resetting the device. 852 */ 853 static cfga_usb_ret_t 854 reset_device(devctl_hdl_t devctl_hdl, nvlist_t *nvl) 855 { 856 cfga_usb_ret_t rv; 857 858 DPRINTF("reset_device: \n"); 859 860 /* 861 * Disconnect and reconfigure the device. 862 * Note this forces the new default config to take effect. 863 */ 864 if (devctl_ap_disconnect(devctl_hdl, nvl) != 0) { 865 DPRINTF("devctl_ap_unconfigure failed, errno: %d\n", errno); 866 rv = CFGA_USB_DEVCTL; 867 if (errno == EBUSY) { 868 rv = CFGA_USB_BUSY; /* Provide more useful msg */ 869 } 870 871 return (rv); 872 } 873 874 if (devctl_ap_configure(devctl_hdl, nvl) != 0) { 875 DPRINTF(" devctl_ap_configure failed, errno = %d\n", errno); 876 return (CFGA_USB_DEVCTL); 877 } 878 879 return (CFGA_USB_OK); 880 } 881 882 883 /* 884 * Called from cfga_list_ext. 885 * Fills in the 'misc_info' field in the cfga buffer (displayed with -lv). 886 */ 887 static cfga_usb_ret_t 888 fill_in_ap_info(const char *ap_id, char *info_buf, size_t info_size) 889 { 890 char *mfg_str = NULL; /* iManufacturer */ 891 char *prod_str = NULL; /* iProduct */ 892 char *cfg_descr = NULL; /* iConfiguration */ 893 uint_t config; /* curr cfg index */ 894 size_t size; /* tmp stuff */ 895 boolean_t flag; /* wether to print ":" or not */ 896 boolean_t free_mfg_str = B_FALSE; 897 boolean_t free_prod_str = B_FALSE; 898 boolean_t free_cfg_str = B_FALSE; 899 cfga_usb_ret_t rv = CFGA_USB_OK; 900 usb_dev_descr_t *dev_descrp = NULL; /* device descriptor */ 901 902 DPRINTF("fill_in_ap_info:\n"); 903 904 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, 0, 905 (void **)&dev_descrp, &size)) != CFGA_USB_OK) { 906 DPRINTF("fill_in_ap_info: get dev descr failed\n"); 907 return (rv); 908 } 909 910 /* iManufacturer */ 911 mfg_str = USB_UNDEF_STR; 912 if (dev_descrp->iManufacturer != 0) { 913 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 914 HUBD_MFG_STR, (void **)&mfg_str, &size)) != CFGA_USB_OK) { 915 if (rv == CFGA_USB_ZEROLEN) { 916 rv = CFGA_USB_OK; 917 } else { 918 DPRINTF("get iManufacturer failed\n"); 919 goto bailout; 920 } 921 } 922 free_mfg_str = B_TRUE; 923 } 924 925 /* iProduct */ 926 prod_str = USB_UNDEF_STR; 927 if (dev_descrp->iProduct != 0) { 928 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 929 HUBD_PRODUCT_STR, (void **)&prod_str, 930 &size)) != CFGA_USB_OK) { 931 if (rv == CFGA_USB_ZEROLEN) { 932 rv = CFGA_USB_OK; 933 } else { 934 DPRINTF("getting iProduct failed\n"); 935 goto bailout; 936 } 937 } 938 free_prod_str = B_TRUE; 939 } 940 941 /* Current conifguration */ 942 if ((rv = get_config(ap_id, &config)) != CFGA_USB_OK) { 943 DPRINTF("get_config failed\n"); 944 goto bailout; 945 } 946 947 /* Configuration string descriptor */ 948 cfg_descr = USB_NO_CFG_STR; 949 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 950 HUBD_CFG_DESCR_STR, (void **)&cfg_descr, &size)) != CFGA_USB_OK) { 951 if (rv == CFGA_USB_ZEROLEN) { 952 rv = CFGA_USB_OK; 953 flag = B_TRUE; 954 } else { 955 DPRINTF("HUBD_CFG_DESCR_STR failed\n"); 956 goto bailout; 957 } 958 } 959 960 /* add ": " to output coz PSARC case says so */ 961 if ((cfg_descr != (char *)NULL) && rv != CFGA_USB_ZEROLEN) { 962 flag = B_TRUE; 963 free_cfg_str = B_TRUE; 964 } else { 965 flag = B_FALSE; 966 cfg_descr = USB_NO_CFG_STR; 967 } 968 969 /* Dump local buf into passed-in buf. */ 970 (void) snprintf(info_buf, info_size, 971 "Mfg: %s Product: %s NConfigs: %d Config: %d %s%s", mfg_str, 972 prod_str, dev_descrp->bNumConfigurations, config, 973 (flag == B_TRUE) ? ": " : "", cfg_descr); 974 975 bailout: 976 if (dev_descrp) { 977 free(dev_descrp); 978 } 979 980 if ((free_mfg_str == B_TRUE) && mfg_str) { 981 free(mfg_str); 982 } 983 984 if ((free_prod_str == B_TRUE) && prod_str) { 985 free(prod_str); 986 } 987 988 if ((free_cfg_str == B_TRUE) && cfg_descr) { 989 free(cfg_descr); 990 } 991 992 return (rv); 993 } 994 995 996 /* ========================================================================== */ 997 /* Entry points */ 998 999 1000 /*ARGSUSED*/ 1001 cfga_err_t 1002 cfga_change_state( 1003 cfga_cmd_t state_change_cmd, 1004 const char *ap_id, 1005 const char *options, 1006 struct cfga_confirm *confp, 1007 struct cfga_msg *msgp, 1008 char **errstring, 1009 cfga_flags_t flags) 1010 { 1011 int ret; 1012 int len; 1013 char *msg; 1014 char *devpath; 1015 nvlist_t *nvl = NULL; 1016 ap_rstate_t rstate; 1017 ap_ostate_t ostate; 1018 devctl_hdl_t hdl = NULL; 1019 cfga_usb_ret_t rv = CFGA_USB_OK; 1020 1021 DPRINTF("cfga_change_state:\n"); 1022 1023 if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) { 1024 (void) cfga_help(msgp, options, flags); 1025 goto bailout; 1026 } 1027 1028 /* 1029 * All subcommands which can change state of device require 1030 * root privileges. 1031 */ 1032 if (geteuid() != 0) { 1033 rv = CFGA_USB_PRIV; 1034 goto bailout; 1035 } 1036 1037 if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &nvl, 0)) != 1038 CFGA_USB_OK) { 1039 goto bailout; 1040 } 1041 1042 switch (state_change_cmd) { 1043 case CFGA_CMD_CONFIGURE: 1044 if ((rv = device_configured(hdl, nvl, &rstate)) != 1045 CFGA_USB_NOT_CONFIGURED) { 1046 goto bailout; 1047 } 1048 1049 if (rstate == AP_RSTATE_EMPTY) { 1050 goto bailout; 1051 } 1052 rv = CFGA_USB_OK; /* Other statuses don't matter */ 1053 1054 if (devctl_ap_configure(hdl, nvl) != 0) { 1055 DPRINTF("cfga_change_state: devctl_ap_configure " 1056 "failed. errno: %d\n", errno); 1057 rv = CFGA_USB_DEVCTL; 1058 } 1059 1060 devpath = usb_get_devicepath(ap_id); 1061 if (devpath == NULL) { 1062 int i; 1063 /* 1064 * try for some time as USB hotplug thread 1065 * takes a while to create the path 1066 * and then eventually give up 1067 */ 1068 for (i = 0; i < 12 && (devpath == NULL); i++) { 1069 (void) sleep(6); 1070 devpath = usb_get_devicepath(ap_id); 1071 } 1072 1073 if (devpath == NULL) { 1074 DPRINTF("cfga_change_state: get device " 1075 "path failed i = %d\n", i); 1076 rv = CFGA_USB_DEVCTL; 1077 break; 1078 } 1079 } 1080 S_FREE(devpath); 1081 break; 1082 case CFGA_CMD_UNCONFIGURE: 1083 if ((rv = device_connected(hdl, nvl, &ostate)) != 1084 CFGA_USB_ALREADY_CONNECTED) { 1085 goto bailout; 1086 } 1087 1088 /* check if it is already unconfigured */ 1089 if ((rv = device_configured(hdl, nvl, &rstate)) == 1090 CFGA_USB_NOT_CONFIGURED) { 1091 goto bailout; 1092 } 1093 rv = CFGA_USB_OK; /* Other statuses don't matter */ 1094 1095 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1096 strlen("Unconfigure") + strlen(ap_id); 1097 if ((msg = (char *)calloc(len + 3, 1)) != NULL) { 1098 (void) snprintf(msg, len + 3, "Unconfigure %s%s\n%s", 1099 USB_CONFIRM_0, ap_id, USB_CONFIRM_1); 1100 } 1101 if (!usb_confirm(confp, msg)) { 1102 free(msg); 1103 cleanup_after_devctl_cmd(hdl, nvl); 1104 return (CFGA_NACK); 1105 } 1106 free(msg); 1107 1108 devpath = usb_get_devicepath(ap_id); 1109 if (devpath == NULL) { 1110 DPRINTF("cfga_change_state: get device path failed\n"); 1111 rv = CFGA_USB_DEVCTL; 1112 break; 1113 } 1114 1115 if ((rv = usb_rcm_offline(ap_id, errstring, devpath, flags)) != 1116 CFGA_USB_OK) { 1117 break; 1118 } 1119 1120 ret = devctl_ap_unconfigure(hdl, nvl); 1121 if (ret != 0) { 1122 DPRINTF("cfga_change_state: devctl_ap_unconfigure " 1123 "failed with errno: %d\n", errno); 1124 rv = CFGA_USB_DEVCTL; 1125 if (errno == EBUSY) { 1126 rv = CFGA_USB_BUSY; 1127 } 1128 (void) usb_rcm_online(ap_id, errstring, devpath, flags); 1129 } else { 1130 (void) usb_rcm_remove(ap_id, errstring, devpath, flags); 1131 } 1132 S_FREE(devpath); 1133 break; 1134 case CFGA_CMD_DISCONNECT: 1135 if ((rv = device_connected(hdl, nvl, &ostate)) != 1136 CFGA_USB_ALREADY_CONNECTED) { 1137 /* 1138 * special case handling for 1139 * SLM based cfgadm disconnects 1140 */ 1141 if (ostate == AP_OSTATE_UNCONFIGURED) 1142 goto bailout; 1143 } 1144 rv = CFGA_USB_OK; /* Other statuses don't matter */ 1145 1146 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1147 strlen("Disconnect") + strlen(ap_id); 1148 if ((msg = (char *)calloc(len + 3, 1)) != NULL) { 1149 (void) snprintf(msg, len + 3, "Disconnect %s%s\n%s", 1150 USB_CONFIRM_0, ap_id, USB_CONFIRM_1); 1151 } 1152 if (!usb_confirm(confp, msg)) { 1153 free(msg); 1154 cleanup_after_devctl_cmd(hdl, nvl); 1155 return (CFGA_NACK); 1156 } 1157 free(msg); 1158 1159 devpath = usb_get_devicepath(ap_id); 1160 if (devpath == NULL) { 1161 DPRINTF("cfga_change_state: get device path failed\n"); 1162 rv = CFGA_USB_DEVCTL; 1163 break; 1164 } 1165 1166 /* only call rcm_offline iff the state was CONFIGURED */ 1167 if (ostate == AP_OSTATE_CONFIGURED) { 1168 if ((rv = usb_rcm_offline(ap_id, errstring, 1169 devpath, flags)) != CFGA_USB_OK) { 1170 break; 1171 } 1172 } 1173 1174 ret = devctl_ap_disconnect(hdl, nvl); 1175 if (ret != 0) { 1176 DPRINTF("cfga_change_state: devctl_ap_disconnect " 1177 "failed with errno: %d\n", errno); 1178 rv = CFGA_USB_DEVCTL; 1179 if (errno == EBUSY) { 1180 rv = CFGA_USB_BUSY; 1181 } 1182 if (ostate == AP_OSTATE_CONFIGURED) { 1183 (void) usb_rcm_online(ap_id, errstring, 1184 devpath, flags); 1185 } 1186 } else { 1187 if (ostate == AP_OSTATE_CONFIGURED) { 1188 (void) usb_rcm_remove(ap_id, errstring, 1189 devpath, flags); 1190 } 1191 } 1192 S_FREE(devpath); 1193 break; 1194 case CFGA_CMD_CONNECT: 1195 case CFGA_CMD_LOAD: 1196 case CFGA_CMD_UNLOAD: 1197 (void) cfga_help(msgp, options, flags); 1198 rv = CFGA_USB_OPNOTSUPP; 1199 break; 1200 case CFGA_CMD_NONE: 1201 default: 1202 (void) cfga_help(msgp, options, flags); 1203 rv = CFGA_USB_INTERNAL_ERROR; 1204 } 1205 1206 bailout: 1207 cleanup_after_devctl_cmd(hdl, nvl); 1208 1209 return (usb_err_msg(errstring, rv, ap_id, errno)); 1210 } 1211 1212 1213 /*ARGSUSED*/ 1214 cfga_err_t 1215 cfga_private_func( 1216 const char *func, 1217 const char *ap_id, 1218 const char *options, 1219 struct cfga_confirm *confp, 1220 struct cfga_msg *msgp, 1221 char **errstring, 1222 cfga_flags_t flags) 1223 { 1224 int len; 1225 char *msg; 1226 nvlist_t *list = NULL; 1227 ap_ostate_t ostate; 1228 devctl_hdl_t hdl = NULL; 1229 cfga_usb_ret_t rv; 1230 usb_dev_descr_t *dev_descrp = NULL; 1231 char *driver = NULL; 1232 1233 DPRINTF("cfga_private_func:\n"); 1234 1235 if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_USB_OK) { 1236 (void) cfga_help(msgp, options, flags); 1237 return (usb_err_msg(errstring, rv, ap_id, errno)); 1238 } 1239 1240 /* 1241 * All subcommands which can change state of device require 1242 * root privileges. 1243 */ 1244 if (geteuid() != 0) { 1245 rv = CFGA_USB_PRIV; 1246 goto bailout; 1247 } 1248 1249 if (func == NULL) { 1250 rv = CFGA_USB_INTERNAL_ERROR; 1251 goto bailout; 1252 } 1253 1254 if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &list, 0)) != 1255 CFGA_USB_OK) { 1256 goto bailout; 1257 } 1258 1259 if ((rv = device_connected(hdl, list, &ostate)) != 1260 CFGA_USB_ALREADY_CONNECTED) { 1261 goto bailout; 1262 } 1263 rv = CFGA_USB_OK; 1264 1265 if (strcmp(func, RESET_DEVICE) == 0) { /* usb_reset? */ 1266 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1267 strlen("Reset") + strlen(ap_id); 1268 if ((msg = (char *)calloc(len + 3, 1)) != NULL) { 1269 (void) snprintf(msg, len + 3, "Reset %s%s\n%s", 1270 USB_CONFIRM_0, ap_id, USB_CONFIRM_1); 1271 } else { 1272 cleanup_after_devctl_cmd(hdl, list); 1273 return (CFGA_NACK); 1274 } 1275 1276 if (!usb_confirm(confp, msg)) { 1277 cleanup_after_devctl_cmd(hdl, list); 1278 return (CFGA_NACK); 1279 } 1280 1281 if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) { 1282 goto bailout; 1283 } 1284 } else if (strncmp(func, USB_CONFIG, sizeof (USB_CONFIG)) == 0) { 1285 uint_t config = 0; 1286 uint_t actual_config; 1287 size_t size; 1288 char *subopts, *value; 1289 uint_t cfg_opt_flag = B_FALSE; 1290 1291 /* these are the only valid options */ 1292 char *cfg_opts[] = { 1293 "config", /* 0 */ 1294 "drv", /* 1 */ 1295 NULL 1296 }; 1297 1298 /* return error if no options are specified */ 1299 subopts = (char *)options; 1300 if (subopts == (char *)NULL) { 1301 DPRINTF("cfga_private_func: no options\n"); 1302 rv = CFGA_USB_OPNOTSUPP; 1303 (void) cfga_help(msgp, options, flags); 1304 goto bailout; 1305 } 1306 1307 /* parse options specified */ 1308 while (*subopts != '\0') { 1309 switch (getsubopt(&subopts, cfg_opts, &value)) { 1310 case 0: /* config */ 1311 if (value == NULL) { 1312 rv = CFGA_USB_OPNOTSUPP; 1313 (void) cfga_help(msgp, 1314 options, flags); 1315 goto bailout; 1316 } else { 1317 errno = 0; 1318 config = strtol(value, 1319 (char **)NULL, 10); 1320 if (errno) { 1321 DPRINTF( 1322 "config conversion" 1323 "failed\n"); 1324 rv = 1325 CFGA_USB_CONFIG_INVAL; 1326 goto bailout; 1327 } 1328 } 1329 cfg_opt_flag = B_TRUE; 1330 break; 1331 1332 case 1: /* drv */ 1333 if (value == NULL) { 1334 rv = CFGA_USB_OPNOTSUPP; 1335 (void) cfga_help(msgp, 1336 options, flags); 1337 goto bailout; 1338 } else { 1339 S_FREE(driver); 1340 driver = strdup(value); 1341 if (driver == NULL) { 1342 rv = 1343 CFGA_USB_INTERNAL_ERROR; 1344 goto bailout; 1345 } 1346 } 1347 break; 1348 1349 default: 1350 rv = CFGA_USB_OPNOTSUPP; 1351 (void) cfga_help(msgp, options, flags); 1352 goto bailout; 1353 } 1354 } 1355 1356 /* config is mandatory */ 1357 if (cfg_opt_flag != B_TRUE) { 1358 rv = CFGA_USB_OPNOTSUPP; 1359 (void) cfga_help(msgp, options, flags); 1360 goto bailout; 1361 } 1362 DPRINTF("config = %x\n", config); 1363 1364 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1365 strlen("Setting") + strlen(ap_id) + 1366 strlen("to USB configuration"); 1367 /* len + 8 to account for config, \n and white space */ 1368 if ((msg = (char *)calloc(len + 8, 1)) != NULL) { 1369 (void) snprintf(msg, len + 8, 1370 "Setting %s%s\nto USB configuration %d\n%s", 1371 USB_CONFIRM_0, ap_id, config, USB_CONFIRM_1); 1372 } else { 1373 rv = CFGA_USB_INTERNAL_ERROR; 1374 goto bailout; 1375 } 1376 1377 if (!usb_confirm(confp, msg)) { 1378 S_FREE(driver); 1379 cleanup_after_devctl_cmd(hdl, list); 1380 return (CFGA_NACK); 1381 } 1382 1383 /* 1384 * Check that the option setting selected is in range. 1385 */ 1386 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, 0, 1387 (void **)&dev_descrp, &size)) != CFGA_USB_OK) { 1388 DPRINTF("cfga_private_func: get dev descr failed\n"); 1389 goto bailout; 1390 } 1391 1392 if (config > dev_descrp->bNumConfigurations - 1) { 1393 DPRINTF("cfga_private_func: config index requested " 1394 "(%d) exceeds bNumConfigurations - 1 (%d)\n", 1395 config, dev_descrp->bNumConfigurations - 1); 1396 rv = CFGA_USB_CONFIG_INVAL; 1397 goto bailout; 1398 } 1399 1400 /* Pass current setting to set_configuration */ 1401 if ((rv = get_config(ap_id, &actual_config)) != CFGA_USB_OK) { 1402 goto bailout; 1403 } 1404 1405 /* check if they match - yes, then nothing to do */ 1406 if (actual_config == config) { 1407 DPRINTF("cfga_private_func: config index requested " 1408 "(%d) matches the actual config value %d\n", 1409 config, actual_config); 1410 rv = CFGA_USB_OK; 1411 goto bailout; 1412 } 1413 1414 /* Save the configuration settings */ 1415 if ((rv = set_configuration(ap_id, config, driver, 1416 dev_descrp, errstring)) != CFGA_USB_OK) { 1417 goto bailout; 1418 } 1419 1420 /* Reset device to force new config to take effect */ 1421 if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) { 1422 goto bailout; 1423 } 1424 1425 } else { 1426 DPRINTF("cfga_private_func: unrecognized command.\n"); 1427 (void) cfga_help(msgp, options, flags); 1428 errno = EINVAL; 1429 1430 return (CFGA_INVAL); 1431 } 1432 1433 bailout: 1434 S_FREE(dev_descrp); 1435 S_FREE(driver); 1436 cleanup_after_devctl_cmd(hdl, list); 1437 1438 return (usb_err_msg(errstring, rv, ap_id, errno)); 1439 } 1440 1441 1442 /*ARGSUSED*/ 1443 cfga_err_t 1444 cfga_test( 1445 const char *ap_id, 1446 const char *options, 1447 struct cfga_msg *msgp, 1448 char **errstring, 1449 cfga_flags_t flags) 1450 { 1451 (void) cfga_help(msgp, options, flags); 1452 return (CFGA_OPNOTSUPP); 1453 } 1454 1455 1456 /*ARGSUSED*/ 1457 cfga_err_t 1458 cfga_list_ext( 1459 const char *ap_id, 1460 cfga_list_data_t **ap_id_list, 1461 int *nlistp, 1462 const char *options, 1463 const char *listopts, 1464 char **errstring, 1465 cfga_flags_t flags) 1466 { 1467 int l_errno; 1468 char *ap_id_log = NULL; 1469 size_t size; 1470 nvlist_t *user_nvlist = NULL; 1471 devctl_hdl_t devctl_hdl = NULL; 1472 cfga_usb_ret_t rv = CFGA_USB_OK; 1473 devctl_ap_state_t devctl_ap_state; 1474 1475 DPRINTF("cfga_list_ext:\n"); 1476 1477 if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) { 1478 goto bailout; 1479 } 1480 1481 if (ap_id_list == NULL || nlistp == NULL) { 1482 DPRINTF("cfga_list_ext: list = NULL or nlistp = NULL\n"); 1483 rv = CFGA_USB_INTERNAL_ERROR; 1484 goto bailout; 1485 } 1486 1487 /* Get ap status */ 1488 if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, &user_nvlist, 1489 DC_RDONLY)) != CFGA_USB_OK) { 1490 goto bailout; 1491 } 1492 1493 if (devctl_ap_getstate(devctl_hdl, user_nvlist, &devctl_ap_state) == 1494 -1) { 1495 DPRINTF("cfga_list_ext: devctl_ap_getstate failed. errno: %d\n", 1496 errno); 1497 cleanup_after_devctl_cmd(devctl_hdl, user_nvlist); 1498 rv = CFGA_USB_DEVCTL; 1499 goto bailout; 1500 } 1501 cleanup_after_devctl_cmd(devctl_hdl, user_nvlist); 1502 1503 /* 1504 * Create cfga_list_data_t struct. 1505 */ 1506 if ((*ap_id_list = 1507 (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) { 1508 DPRINTF("cfga_list_ext: malloc for cfga_list_data_t failed. " 1509 "errno: %d\n", errno); 1510 rv = CFGA_USB_ALLOC_FAIL; 1511 goto bailout; 1512 } 1513 *nlistp = 1; 1514 1515 1516 /* 1517 * Rest of the code fills in the cfga_list_data_t struct. 1518 */ 1519 1520 /* Get /dev/cfg path to corresponding to the physical ap_id */ 1521 /* Remember ap_id_log must be freed */ 1522 rv = (cfga_usb_ret_t)physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id, 1523 &ap_id_log, &l_errno, MATCH_MINOR_NAME); 1524 if (rv != 0) { 1525 rv = CFGA_USB_DEVLINK; 1526 goto bailout; 1527 } 1528 assert(ap_id_log != NULL); 1529 1530 /* Get logical ap-id corresponding to the physical */ 1531 if (strstr(ap_id_log, CFGA_DEV_DIR) == NULL) { 1532 DPRINTF("cfga_list_ext: devlink doesn't contain /dev/cfg\n"); 1533 rv = CFGA_USB_DEVLINK; 1534 goto bailout; 1535 } 1536 (void) strlcpy((*ap_id_list)->ap_log_id, 1537 /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1, 1538 sizeof ((*ap_id_list)->ap_log_id)); 1539 free(ap_id_log); 1540 ap_id_log = NULL; 1541 1542 (void) strlcpy((*ap_id_list)->ap_phys_id, ap_id, 1543 sizeof ((*ap_id_list)->ap_phys_id)); 1544 1545 switch (devctl_ap_state.ap_rstate) { 1546 case AP_RSTATE_EMPTY: 1547 (*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY; 1548 break; 1549 case AP_RSTATE_DISCONNECTED: 1550 (*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED; 1551 break; 1552 case AP_RSTATE_CONNECTED: 1553 (*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED; 1554 break; 1555 default: 1556 rv = CFGA_USB_STATE; 1557 goto bailout; 1558 } 1559 1560 switch (devctl_ap_state.ap_ostate) { 1561 case AP_OSTATE_CONFIGURED: 1562 (*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED; 1563 break; 1564 case AP_OSTATE_UNCONFIGURED: 1565 (*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED; 1566 break; 1567 default: 1568 rv = CFGA_USB_STATE; 1569 goto bailout; 1570 } 1571 1572 switch (devctl_ap_state.ap_condition) { 1573 case AP_COND_OK: 1574 (*ap_id_list)->ap_cond = CFGA_COND_OK; 1575 break; 1576 case AP_COND_FAILING: 1577 (*ap_id_list)->ap_cond = CFGA_COND_FAILING; 1578 break; 1579 case AP_COND_FAILED: 1580 (*ap_id_list)->ap_cond = CFGA_COND_FAILED; 1581 break; 1582 case AP_COND_UNUSABLE: 1583 (*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE; 1584 break; 1585 case AP_COND_UNKNOWN: 1586 (*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN; 1587 break; 1588 default: 1589 rv = CFGA_USB_STATE; 1590 goto bailout; 1591 } 1592 1593 (*ap_id_list)->ap_class[0] = '\0'; /* Filled by libcfgadm */ 1594 (*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition; 1595 (*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change; 1596 (*ap_id_list)->ap_info[0] = '\0'; 1597 1598 if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) { 1599 char *str_p; 1600 size_t str_len; 1601 1602 /* Fill in the info for the -v option display. */ 1603 if ((rv = fill_in_ap_info(ap_id, (*ap_id_list)->ap_info, 1604 sizeof ((*ap_id_list)->ap_info))) != CFGA_USB_OK) { 1605 DPRINTF("cfga_list_ext: fill_in_ap_info failed\n"); 1606 goto bailout; 1607 } 1608 1609 /* Fill in ap_type */ 1610 if ((rv = do_control_ioctl(ap_id, HUBD_GET_CFGADM_NAME, 0, 1611 (void **)&str_p, &size)) != CFGA_USB_OK) { 1612 DPRINTF("cfga_list_ext: do_control_ioctl failed\n"); 1613 goto bailout; 1614 } 1615 1616 (void) strcpy((*ap_id_list)->ap_type, "usb-"); 1617 str_len = strlen((*ap_id_list)->ap_type); 1618 1619 /* 1620 * NOTE: In the cfgadm display the "Type" column is only 12 1621 * chars long. Most USB devices can be displayed here with a 1622 * "usb-" prefix. Only USB keyboard cannot be displayed in 1623 * its entirety as "usb-keybaord" is 13 chars in length. 1624 * It will show up as "usb-kbd". 1625 */ 1626 if (strncasecmp(str_p, "keyboard", 8) != 0) { 1627 (void) strlcpy((*ap_id_list)->ap_type + str_len, str_p, 1628 sizeof ((*ap_id_list)->ap_type) - str_len); 1629 } else { 1630 (void) strlcpy((*ap_id_list)->ap_type + str_len, "kbd", 1631 sizeof ((*ap_id_list)->ap_type) - str_len); 1632 } 1633 1634 free(str_p); 1635 } else { 1636 (void) strcpy((*ap_id_list)->ap_type, 1637 USB_CFGADM_DEFAULT_AP_TYPE); 1638 } 1639 1640 return (usb_err_msg(errstring, rv, ap_id, errno)); 1641 bailout: 1642 if (*ap_id_list != NULL) { 1643 free(*ap_id_list); 1644 } 1645 if (ap_id_log != NULL) { 1646 free(ap_id_log); 1647 } 1648 1649 return (usb_err_msg(errstring, rv, ap_id, errno)); 1650 } 1651 1652 1653 /* 1654 * This routine accepts a variable number of message IDs and constructs 1655 * a corresponding error string which is printed via the message print routine 1656 * argument. 1657 */ 1658 static void 1659 cfga_msg(struct cfga_msg *msgp, const char *str) 1660 { 1661 int len; 1662 char *q; 1663 1664 if (msgp == NULL || msgp->message_routine == NULL) { 1665 DPRINTF("cfga_msg: msg\n"); 1666 return; 1667 } 1668 1669 if ((len = strlen(str)) == 0) { 1670 DPRINTF("cfga_msg: null str\n"); 1671 return; 1672 } 1673 1674 if ((q = (char *)calloc(len + 1, 1)) == NULL) { 1675 DPRINTF("cfga_msg: null q\n"); 1676 return; 1677 } 1678 1679 (void) strcpy(q, str); 1680 (*msgp->message_routine)(msgp->appdata_ptr, q); 1681 1682 free(q); 1683 } 1684 1685 1686 /* ARGSUSED */ 1687 cfga_err_t 1688 cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags) 1689 { 1690 DPRINTF("cfga_help:\n"); 1691 if (options) { 1692 cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_UNKNOWN])); 1693 cfga_msg(msgp, options); 1694 } 1695 1696 cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_HEADER])); 1697 cfga_msg(msgp, usb_help[HELP_CONFIG]); 1698 cfga_msg(msgp, usb_help[HELP_RESET_SLOT]); 1699 cfga_msg(msgp, usb_help[HELP_CONFIG_SLOT]); 1700 1701 return (CFGA_OK); 1702 } 1703 1704 1705 static int 1706 usb_confirm(struct cfga_confirm *confp, char *msg) 1707 { 1708 int rval; 1709 1710 if (confp == NULL || confp->confirm == NULL) { 1711 return (0); 1712 } 1713 1714 rval = (*confp->confirm)(confp->appdata_ptr, msg); 1715 DPRINTF("usb_confirm: %d\n", rval); 1716 1717 return (rval); 1718 } 1719 1720 1721 static char * 1722 usb_get_devicepath(const char *ap_id) 1723 { 1724 char *devpath = NULL; 1725 size_t size; 1726 cfga_usb_ret_t rv; 1727 1728 rv = do_control_ioctl(ap_id, HUBD_GET_DEVICE_PATH, 0, 1729 (void **)&devpath, &size); 1730 1731 if (rv == CFGA_USB_OK) { 1732 DPRINTF("usb_get_devicepath: get device path ioctl ok\n"); 1733 return (devpath); 1734 } else { 1735 DPRINTF("usb_get_devicepath: get device path ioctl failed\n"); 1736 return (NULL); 1737 } 1738 } 1739