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 * Copyright 2022 Oxide Computer Company 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(3CFGADM) 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 *serial_str = NULL; /* iSerialNumber */ 893 char *cfg_descr = NULL; /* iConfiguration */ 894 uint_t config; /* curr cfg index */ 895 size_t size; /* tmp stuff */ 896 boolean_t flag; /* wether to print ":" or not */ 897 boolean_t free_mfg_str = B_FALSE; 898 boolean_t free_prod_str = B_FALSE; 899 boolean_t free_serial_str = B_FALSE; 900 boolean_t free_cfg_str = B_FALSE; 901 cfga_usb_ret_t rv = CFGA_USB_OK; 902 usb_dev_descr_t *dev_descrp = NULL; /* device descriptor */ 903 904 DPRINTF("fill_in_ap_info:\n"); 905 906 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, 0, 907 (void **)&dev_descrp, &size)) != CFGA_USB_OK) { 908 DPRINTF("fill_in_ap_info: get dev descr failed\n"); 909 return (rv); 910 } 911 912 /* iManufacturer */ 913 mfg_str = USB_UNDEF_STR; 914 if (dev_descrp->iManufacturer != 0) { 915 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 916 HUBD_MFG_STR, (void **)&mfg_str, &size)) != CFGA_USB_OK) { 917 if (rv == CFGA_USB_ZEROLEN) { 918 rv = CFGA_USB_OK; 919 } else { 920 DPRINTF("get iManufacturer failed\n"); 921 goto bailout; 922 } 923 } 924 free_mfg_str = B_TRUE; 925 } 926 927 /* iProduct */ 928 prod_str = USB_UNDEF_STR; 929 if (dev_descrp->iProduct != 0) { 930 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 931 HUBD_PRODUCT_STR, (void **)&prod_str, 932 &size)) != CFGA_USB_OK) { 933 if (rv == CFGA_USB_ZEROLEN) { 934 rv = CFGA_USB_OK; 935 } else { 936 DPRINTF("getting iProduct failed\n"); 937 goto bailout; 938 } 939 } 940 free_prod_str = B_TRUE; 941 } 942 943 /* iSerialNumber */ 944 serial_str = USB_UNDEF_STR; 945 if (dev_descrp->iSerialNumber != 0) { 946 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 947 HUBD_SERIALNO_STR, (void **)&serial_str, 948 &size)) != CFGA_USB_OK) { 949 if (rv == CFGA_USB_ZEROLEN) { 950 rv = CFGA_USB_OK; 951 } else { 952 DPRINTF("getting iSerialNumber failed\n"); 953 goto bailout; 954 } 955 } 956 free_serial_str = B_TRUE; 957 } 958 959 /* Current conifguration */ 960 if ((rv = get_config(ap_id, &config)) != CFGA_USB_OK) { 961 DPRINTF("get_config failed\n"); 962 goto bailout; 963 } 964 965 /* Configuration string descriptor */ 966 cfg_descr = USB_NO_CFG_STR; 967 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING, 968 HUBD_CFG_DESCR_STR, (void **)&cfg_descr, &size)) != CFGA_USB_OK) { 969 if (rv == CFGA_USB_ZEROLEN) { 970 rv = CFGA_USB_OK; 971 flag = B_TRUE; 972 } else { 973 DPRINTF("HUBD_CFG_DESCR_STR failed\n"); 974 goto bailout; 975 } 976 } 977 978 /* add ": " to output coz PSARC case says so */ 979 if ((cfg_descr != (char *)NULL) && rv != CFGA_USB_ZEROLEN) { 980 flag = B_TRUE; 981 free_cfg_str = B_TRUE; 982 } else { 983 flag = B_FALSE; 984 cfg_descr = USB_NO_CFG_STR; 985 } 986 987 /* Dump local buf into passed-in buf. */ 988 (void) snprintf(info_buf, info_size, 989 "Mfg: %s Product: %s Serial: %s NConfigs: %d Config: %d %s%s", 990 mfg_str, prod_str, serial_str, 991 dev_descrp->bNumConfigurations, config, 992 (flag == B_TRUE) ? ": " : "", cfg_descr); 993 994 bailout: 995 if (dev_descrp) { 996 free(dev_descrp); 997 } 998 999 if ((free_mfg_str == B_TRUE) && mfg_str) { 1000 free(mfg_str); 1001 } 1002 1003 if ((free_prod_str == B_TRUE) && prod_str) { 1004 free(prod_str); 1005 } 1006 1007 if ((free_serial_str == B_TRUE) && serial_str) { 1008 free(serial_str); 1009 } 1010 1011 if ((free_cfg_str == B_TRUE) && cfg_descr) { 1012 free(cfg_descr); 1013 } 1014 1015 return (rv); 1016 } 1017 1018 1019 /* ========================================================================== */ 1020 /* Entry points */ 1021 1022 1023 /*ARGSUSED*/ 1024 cfga_err_t 1025 cfga_change_state( 1026 cfga_cmd_t state_change_cmd, 1027 const char *ap_id, 1028 const char *options, 1029 struct cfga_confirm *confp, 1030 struct cfga_msg *msgp, 1031 char **errstring, 1032 cfga_flags_t flags) 1033 { 1034 int ret; 1035 int len; 1036 char *msg; 1037 char *devpath; 1038 nvlist_t *nvl = NULL; 1039 ap_rstate_t rstate; 1040 ap_ostate_t ostate; 1041 devctl_hdl_t hdl = NULL; 1042 cfga_usb_ret_t rv = CFGA_USB_OK; 1043 1044 DPRINTF("cfga_change_state:\n"); 1045 1046 if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) { 1047 (void) cfga_help(msgp, options, flags); 1048 goto bailout; 1049 } 1050 1051 /* 1052 * All subcommands which can change state of device require 1053 * root privileges. 1054 */ 1055 if (geteuid() != 0) { 1056 rv = CFGA_USB_PRIV; 1057 goto bailout; 1058 } 1059 1060 if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &nvl, 0)) != 1061 CFGA_USB_OK) { 1062 goto bailout; 1063 } 1064 1065 switch (state_change_cmd) { 1066 case CFGA_CMD_CONFIGURE: 1067 if ((rv = device_configured(hdl, nvl, &rstate)) != 1068 CFGA_USB_NOT_CONFIGURED) { 1069 goto bailout; 1070 } 1071 1072 if (rstate == AP_RSTATE_EMPTY) { 1073 goto bailout; 1074 } 1075 rv = CFGA_USB_OK; /* Other statuses don't matter */ 1076 1077 if (devctl_ap_configure(hdl, nvl) != 0) { 1078 DPRINTF("cfga_change_state: devctl_ap_configure " 1079 "failed. errno: %d\n", errno); 1080 rv = CFGA_USB_DEVCTL; 1081 } 1082 1083 devpath = usb_get_devicepath(ap_id); 1084 if (devpath == NULL) { 1085 int i; 1086 /* 1087 * try for some time as USB hotplug thread 1088 * takes a while to create the path 1089 * and then eventually give up 1090 */ 1091 for (i = 0; i < 12 && (devpath == NULL); i++) { 1092 (void) sleep(6); 1093 devpath = usb_get_devicepath(ap_id); 1094 } 1095 1096 if (devpath == NULL) { 1097 DPRINTF("cfga_change_state: get device " 1098 "path failed i = %d\n", i); 1099 rv = CFGA_USB_DEVCTL; 1100 break; 1101 } 1102 } 1103 S_FREE(devpath); 1104 break; 1105 case CFGA_CMD_UNCONFIGURE: 1106 if ((rv = device_connected(hdl, nvl, &ostate)) != 1107 CFGA_USB_ALREADY_CONNECTED) { 1108 goto bailout; 1109 } 1110 1111 /* check if it is already unconfigured */ 1112 if ((rv = device_configured(hdl, nvl, &rstate)) == 1113 CFGA_USB_NOT_CONFIGURED) { 1114 goto bailout; 1115 } 1116 rv = CFGA_USB_OK; /* Other statuses don't matter */ 1117 1118 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1119 strlen("Unconfigure") + strlen(ap_id); 1120 if ((msg = (char *)calloc(len + 3, 1)) != NULL) { 1121 (void) snprintf(msg, len + 3, "Unconfigure %s%s\n%s", 1122 USB_CONFIRM_0, ap_id, USB_CONFIRM_1); 1123 } 1124 if (!usb_confirm(confp, msg)) { 1125 free(msg); 1126 cleanup_after_devctl_cmd(hdl, nvl); 1127 return (CFGA_NACK); 1128 } 1129 free(msg); 1130 1131 devpath = usb_get_devicepath(ap_id); 1132 if (devpath == NULL) { 1133 DPRINTF("cfga_change_state: get device path failed\n"); 1134 rv = CFGA_USB_DEVCTL; 1135 break; 1136 } 1137 1138 if ((rv = usb_rcm_offline(ap_id, errstring, devpath, flags)) != 1139 CFGA_USB_OK) { 1140 break; 1141 } 1142 1143 ret = devctl_ap_unconfigure(hdl, nvl); 1144 if (ret != 0) { 1145 DPRINTF("cfga_change_state: devctl_ap_unconfigure " 1146 "failed with errno: %d\n", errno); 1147 rv = CFGA_USB_DEVCTL; 1148 if (errno == EBUSY) { 1149 rv = CFGA_USB_BUSY; 1150 } 1151 (void) usb_rcm_online(ap_id, errstring, devpath, flags); 1152 } else { 1153 (void) usb_rcm_remove(ap_id, errstring, devpath, flags); 1154 } 1155 S_FREE(devpath); 1156 break; 1157 case CFGA_CMD_DISCONNECT: 1158 if ((rv = device_connected(hdl, nvl, &ostate)) != 1159 CFGA_USB_ALREADY_CONNECTED) { 1160 /* 1161 * special case handling for 1162 * SLM based cfgadm disconnects 1163 */ 1164 if (ostate == AP_OSTATE_UNCONFIGURED) 1165 goto bailout; 1166 } 1167 rv = CFGA_USB_OK; /* Other statuses don't matter */ 1168 1169 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1170 strlen("Disconnect") + strlen(ap_id); 1171 if ((msg = (char *)calloc(len + 3, 1)) != NULL) { 1172 (void) snprintf(msg, len + 3, "Disconnect %s%s\n%s", 1173 USB_CONFIRM_0, ap_id, USB_CONFIRM_1); 1174 } 1175 if (!usb_confirm(confp, msg)) { 1176 free(msg); 1177 cleanup_after_devctl_cmd(hdl, nvl); 1178 return (CFGA_NACK); 1179 } 1180 free(msg); 1181 1182 devpath = usb_get_devicepath(ap_id); 1183 if (devpath == NULL) { 1184 DPRINTF("cfga_change_state: get device path failed\n"); 1185 rv = CFGA_USB_DEVCTL; 1186 break; 1187 } 1188 1189 /* only call rcm_offline iff the state was CONFIGURED */ 1190 if (ostate == AP_OSTATE_CONFIGURED) { 1191 if ((rv = usb_rcm_offline(ap_id, errstring, 1192 devpath, flags)) != CFGA_USB_OK) { 1193 break; 1194 } 1195 } 1196 1197 ret = devctl_ap_disconnect(hdl, nvl); 1198 if (ret != 0) { 1199 DPRINTF("cfga_change_state: devctl_ap_disconnect " 1200 "failed with errno: %d\n", errno); 1201 rv = CFGA_USB_DEVCTL; 1202 if (errno == EBUSY) { 1203 rv = CFGA_USB_BUSY; 1204 } 1205 if (ostate == AP_OSTATE_CONFIGURED) { 1206 (void) usb_rcm_online(ap_id, errstring, 1207 devpath, flags); 1208 } 1209 } else { 1210 if (ostate == AP_OSTATE_CONFIGURED) { 1211 (void) usb_rcm_remove(ap_id, errstring, 1212 devpath, flags); 1213 } 1214 } 1215 S_FREE(devpath); 1216 break; 1217 case CFGA_CMD_CONNECT: 1218 case CFGA_CMD_LOAD: 1219 case CFGA_CMD_UNLOAD: 1220 (void) cfga_help(msgp, options, flags); 1221 rv = CFGA_USB_OPNOTSUPP; 1222 break; 1223 case CFGA_CMD_NONE: 1224 default: 1225 (void) cfga_help(msgp, options, flags); 1226 rv = CFGA_USB_INTERNAL_ERROR; 1227 } 1228 1229 bailout: 1230 cleanup_after_devctl_cmd(hdl, nvl); 1231 1232 return (usb_err_msg(errstring, rv, ap_id, errno)); 1233 } 1234 1235 1236 /*ARGSUSED*/ 1237 cfga_err_t 1238 cfga_private_func( 1239 const char *func, 1240 const char *ap_id, 1241 const char *options, 1242 struct cfga_confirm *confp, 1243 struct cfga_msg *msgp, 1244 char **errstring, 1245 cfga_flags_t flags) 1246 { 1247 int len; 1248 char *msg; 1249 nvlist_t *list = NULL; 1250 ap_ostate_t ostate; 1251 devctl_hdl_t hdl = NULL; 1252 cfga_usb_ret_t rv; 1253 usb_dev_descr_t *dev_descrp = NULL; 1254 char *driver = NULL; 1255 1256 DPRINTF("cfga_private_func:\n"); 1257 1258 if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_USB_OK) { 1259 (void) cfga_help(msgp, options, flags); 1260 return (usb_err_msg(errstring, rv, ap_id, errno)); 1261 } 1262 1263 /* 1264 * All subcommands which can change state of device require 1265 * root privileges. 1266 */ 1267 if (geteuid() != 0) { 1268 rv = CFGA_USB_PRIV; 1269 goto bailout; 1270 } 1271 1272 if (func == NULL) { 1273 rv = CFGA_USB_INTERNAL_ERROR; 1274 goto bailout; 1275 } 1276 1277 if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &list, 0)) != 1278 CFGA_USB_OK) { 1279 goto bailout; 1280 } 1281 1282 if ((rv = device_connected(hdl, list, &ostate)) != 1283 CFGA_USB_ALREADY_CONNECTED) { 1284 goto bailout; 1285 } 1286 rv = CFGA_USB_OK; 1287 1288 if (strcmp(func, RESET_DEVICE) == 0) { /* usb_reset? */ 1289 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1290 strlen("Reset") + strlen(ap_id); 1291 if ((msg = (char *)calloc(len + 3, 1)) != NULL) { 1292 (void) snprintf(msg, len + 3, "Reset %s%s\n%s", 1293 USB_CONFIRM_0, ap_id, USB_CONFIRM_1); 1294 } else { 1295 cleanup_after_devctl_cmd(hdl, list); 1296 return (CFGA_NACK); 1297 } 1298 1299 if (!usb_confirm(confp, msg)) { 1300 cleanup_after_devctl_cmd(hdl, list); 1301 return (CFGA_NACK); 1302 } 1303 1304 if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) { 1305 goto bailout; 1306 } 1307 } else if (strncmp(func, USB_CONFIG, sizeof (USB_CONFIG)) == 0) { 1308 uint_t config = 0; 1309 uint_t actual_config; 1310 size_t size; 1311 char *subopts, *value; 1312 uint_t cfg_opt_flag = B_FALSE; 1313 1314 /* these are the only valid options */ 1315 char *cfg_opts[] = { 1316 "config", /* 0 */ 1317 "drv", /* 1 */ 1318 NULL 1319 }; 1320 1321 /* return error if no options are specified */ 1322 subopts = (char *)options; 1323 if (subopts == (char *)NULL) { 1324 DPRINTF("cfga_private_func: no options\n"); 1325 rv = CFGA_USB_OPNOTSUPP; 1326 (void) cfga_help(msgp, options, flags); 1327 goto bailout; 1328 } 1329 1330 /* parse options specified */ 1331 while (*subopts != '\0') { 1332 switch (getsubopt(&subopts, cfg_opts, &value)) { 1333 case 0: /* config */ 1334 if (value == NULL) { 1335 rv = CFGA_USB_OPNOTSUPP; 1336 (void) cfga_help(msgp, 1337 options, flags); 1338 goto bailout; 1339 } else { 1340 errno = 0; 1341 config = strtol(value, 1342 (char **)NULL, 10); 1343 if (errno) { 1344 DPRINTF( 1345 "config conversion" 1346 "failed\n"); 1347 rv = 1348 CFGA_USB_CONFIG_INVAL; 1349 goto bailout; 1350 } 1351 } 1352 cfg_opt_flag = B_TRUE; 1353 break; 1354 1355 case 1: /* drv */ 1356 if (value == NULL) { 1357 rv = CFGA_USB_OPNOTSUPP; 1358 (void) cfga_help(msgp, 1359 options, flags); 1360 goto bailout; 1361 } else { 1362 S_FREE(driver); 1363 driver = strdup(value); 1364 if (driver == NULL) { 1365 rv = 1366 CFGA_USB_INTERNAL_ERROR; 1367 goto bailout; 1368 } 1369 } 1370 break; 1371 1372 default: 1373 rv = CFGA_USB_OPNOTSUPP; 1374 (void) cfga_help(msgp, options, flags); 1375 goto bailout; 1376 } 1377 } 1378 1379 /* config is mandatory */ 1380 if (cfg_opt_flag != B_TRUE) { 1381 rv = CFGA_USB_OPNOTSUPP; 1382 (void) cfga_help(msgp, options, flags); 1383 goto bailout; 1384 } 1385 DPRINTF("config = %x\n", config); 1386 1387 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) + 1388 strlen("Setting") + strlen(ap_id) + 1389 strlen("to USB configuration"); 1390 /* len + 8 to account for config, \n and white space */ 1391 if ((msg = (char *)calloc(len + 8, 1)) != NULL) { 1392 (void) snprintf(msg, len + 8, 1393 "Setting %s%s\nto USB configuration %d\n%s", 1394 USB_CONFIRM_0, ap_id, config, USB_CONFIRM_1); 1395 } else { 1396 rv = CFGA_USB_INTERNAL_ERROR; 1397 goto bailout; 1398 } 1399 1400 if (!usb_confirm(confp, msg)) { 1401 S_FREE(driver); 1402 cleanup_after_devctl_cmd(hdl, list); 1403 return (CFGA_NACK); 1404 } 1405 1406 /* 1407 * Check that the option setting selected is in range. 1408 */ 1409 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, 0, 1410 (void **)&dev_descrp, &size)) != CFGA_USB_OK) { 1411 DPRINTF("cfga_private_func: get dev descr failed\n"); 1412 goto bailout; 1413 } 1414 1415 if (config > dev_descrp->bNumConfigurations - 1) { 1416 DPRINTF("cfga_private_func: config index requested " 1417 "(%d) exceeds bNumConfigurations - 1 (%d)\n", 1418 config, dev_descrp->bNumConfigurations - 1); 1419 rv = CFGA_USB_CONFIG_INVAL; 1420 goto bailout; 1421 } 1422 1423 /* Pass current setting to set_configuration */ 1424 if ((rv = get_config(ap_id, &actual_config)) != CFGA_USB_OK) { 1425 goto bailout; 1426 } 1427 1428 /* check if they match - yes, then nothing to do */ 1429 if (actual_config == config) { 1430 DPRINTF("cfga_private_func: config index requested " 1431 "(%d) matches the actual config value %d\n", 1432 config, actual_config); 1433 rv = CFGA_USB_OK; 1434 goto bailout; 1435 } 1436 1437 /* Save the configuration settings */ 1438 if ((rv = set_configuration(ap_id, config, driver, 1439 dev_descrp, errstring)) != CFGA_USB_OK) { 1440 goto bailout; 1441 } 1442 1443 /* Reset device to force new config to take effect */ 1444 if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) { 1445 goto bailout; 1446 } 1447 1448 } else { 1449 DPRINTF("cfga_private_func: unrecognized command.\n"); 1450 (void) cfga_help(msgp, options, flags); 1451 errno = EINVAL; 1452 1453 return (CFGA_INVAL); 1454 } 1455 1456 bailout: 1457 S_FREE(dev_descrp); 1458 S_FREE(driver); 1459 cleanup_after_devctl_cmd(hdl, list); 1460 1461 return (usb_err_msg(errstring, rv, ap_id, errno)); 1462 } 1463 1464 1465 /*ARGSUSED*/ 1466 cfga_err_t 1467 cfga_test( 1468 const char *ap_id, 1469 const char *options, 1470 struct cfga_msg *msgp, 1471 char **errstring, 1472 cfga_flags_t flags) 1473 { 1474 (void) cfga_help(msgp, options, flags); 1475 return (CFGA_OPNOTSUPP); 1476 } 1477 1478 1479 /*ARGSUSED*/ 1480 cfga_err_t 1481 cfga_list_ext( 1482 const char *ap_id, 1483 cfga_list_data_t **ap_id_list, 1484 int *nlistp, 1485 const char *options, 1486 const char *listopts, 1487 char **errstring, 1488 cfga_flags_t flags) 1489 { 1490 int l_errno; 1491 char *ap_id_log = NULL; 1492 size_t size; 1493 nvlist_t *user_nvlist = NULL; 1494 devctl_hdl_t devctl_hdl = NULL; 1495 cfga_usb_ret_t rv = CFGA_USB_OK; 1496 devctl_ap_state_t devctl_ap_state; 1497 1498 DPRINTF("cfga_list_ext:\n"); 1499 1500 if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) { 1501 goto bailout; 1502 } 1503 1504 if (ap_id_list == NULL || nlistp == NULL) { 1505 DPRINTF("cfga_list_ext: list = NULL or nlistp = NULL\n"); 1506 rv = CFGA_USB_INTERNAL_ERROR; 1507 goto bailout; 1508 } 1509 1510 /* Get ap status */ 1511 if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, &user_nvlist, 1512 DC_RDONLY)) != CFGA_USB_OK) { 1513 goto bailout; 1514 } 1515 1516 if (devctl_ap_getstate(devctl_hdl, user_nvlist, &devctl_ap_state) == 1517 -1) { 1518 DPRINTF("cfga_list_ext: devctl_ap_getstate failed. errno: %d\n", 1519 errno); 1520 cleanup_after_devctl_cmd(devctl_hdl, user_nvlist); 1521 rv = CFGA_USB_DEVCTL; 1522 goto bailout; 1523 } 1524 cleanup_after_devctl_cmd(devctl_hdl, user_nvlist); 1525 1526 /* 1527 * Create cfga_list_data_t struct. 1528 */ 1529 if ((*ap_id_list = 1530 (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) { 1531 DPRINTF("cfga_list_ext: malloc for cfga_list_data_t failed. " 1532 "errno: %d\n", errno); 1533 rv = CFGA_USB_ALLOC_FAIL; 1534 goto bailout; 1535 } 1536 *nlistp = 1; 1537 1538 1539 /* 1540 * Rest of the code fills in the cfga_list_data_t struct. 1541 */ 1542 1543 /* Get /dev/cfg path to corresponding to the physical ap_id */ 1544 /* Remember ap_id_log must be freed */ 1545 rv = (cfga_usb_ret_t)physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id, 1546 &ap_id_log, &l_errno, MATCH_MINOR_NAME); 1547 if (rv != 0) { 1548 rv = CFGA_USB_DEVLINK; 1549 goto bailout; 1550 } 1551 assert(ap_id_log != NULL); 1552 1553 /* Get logical ap-id corresponding to the physical */ 1554 if (strstr(ap_id_log, CFGA_DEV_DIR) == NULL) { 1555 DPRINTF("cfga_list_ext: devlink doesn't contain /dev/cfg\n"); 1556 rv = CFGA_USB_DEVLINK; 1557 goto bailout; 1558 } 1559 (void) strlcpy((*ap_id_list)->ap_log_id, 1560 /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1, 1561 sizeof ((*ap_id_list)->ap_log_id)); 1562 free(ap_id_log); 1563 ap_id_log = NULL; 1564 1565 (void) strlcpy((*ap_id_list)->ap_phys_id, ap_id, 1566 sizeof ((*ap_id_list)->ap_phys_id)); 1567 1568 switch (devctl_ap_state.ap_rstate) { 1569 case AP_RSTATE_EMPTY: 1570 (*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY; 1571 break; 1572 case AP_RSTATE_DISCONNECTED: 1573 (*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED; 1574 break; 1575 case AP_RSTATE_CONNECTED: 1576 (*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED; 1577 break; 1578 default: 1579 rv = CFGA_USB_STATE; 1580 goto bailout; 1581 } 1582 1583 switch (devctl_ap_state.ap_ostate) { 1584 case AP_OSTATE_CONFIGURED: 1585 (*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED; 1586 break; 1587 case AP_OSTATE_UNCONFIGURED: 1588 (*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED; 1589 break; 1590 default: 1591 rv = CFGA_USB_STATE; 1592 goto bailout; 1593 } 1594 1595 switch (devctl_ap_state.ap_condition) { 1596 case AP_COND_OK: 1597 (*ap_id_list)->ap_cond = CFGA_COND_OK; 1598 break; 1599 case AP_COND_FAILING: 1600 (*ap_id_list)->ap_cond = CFGA_COND_FAILING; 1601 break; 1602 case AP_COND_FAILED: 1603 (*ap_id_list)->ap_cond = CFGA_COND_FAILED; 1604 break; 1605 case AP_COND_UNUSABLE: 1606 (*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE; 1607 break; 1608 case AP_COND_UNKNOWN: 1609 (*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN; 1610 break; 1611 default: 1612 rv = CFGA_USB_STATE; 1613 goto bailout; 1614 } 1615 1616 (*ap_id_list)->ap_class[0] = '\0'; /* Filled by libcfgadm */ 1617 (*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition; 1618 (*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change; 1619 (*ap_id_list)->ap_info[0] = '\0'; 1620 1621 if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) { 1622 char *str_p; 1623 size_t str_len; 1624 1625 /* Fill in the info for the -v option display. */ 1626 if ((rv = fill_in_ap_info(ap_id, (*ap_id_list)->ap_info, 1627 sizeof ((*ap_id_list)->ap_info))) != CFGA_USB_OK) { 1628 DPRINTF("cfga_list_ext: fill_in_ap_info failed\n"); 1629 goto bailout; 1630 } 1631 1632 /* Fill in ap_type */ 1633 if ((rv = do_control_ioctl(ap_id, HUBD_GET_CFGADM_NAME, 0, 1634 (void **)&str_p, &size)) != CFGA_USB_OK) { 1635 DPRINTF("cfga_list_ext: do_control_ioctl failed\n"); 1636 goto bailout; 1637 } 1638 1639 (void) strcpy((*ap_id_list)->ap_type, "usb-"); 1640 str_len = strlen((*ap_id_list)->ap_type); 1641 1642 /* 1643 * NOTE: In the cfgadm display the "Type" column is only 12 1644 * chars long. Most USB devices can be displayed here with a 1645 * "usb-" prefix. Only USB keyboard cannot be displayed in 1646 * its entirety as "usb-keybaord" is 13 chars in length. 1647 * It will show up as "usb-kbd". 1648 */ 1649 if (strncasecmp(str_p, "keyboard", 8) != 0) { 1650 (void) strlcpy((*ap_id_list)->ap_type + str_len, str_p, 1651 sizeof ((*ap_id_list)->ap_type) - str_len); 1652 } else { 1653 (void) strlcpy((*ap_id_list)->ap_type + str_len, "kbd", 1654 sizeof ((*ap_id_list)->ap_type) - str_len); 1655 } 1656 1657 free(str_p); 1658 } else { 1659 (void) strcpy((*ap_id_list)->ap_type, 1660 USB_CFGADM_DEFAULT_AP_TYPE); 1661 } 1662 1663 return (usb_err_msg(errstring, rv, ap_id, errno)); 1664 bailout: 1665 if (*ap_id_list != NULL) { 1666 free(*ap_id_list); 1667 } 1668 if (ap_id_log != NULL) { 1669 free(ap_id_log); 1670 } 1671 1672 return (usb_err_msg(errstring, rv, ap_id, errno)); 1673 } 1674 1675 1676 /* 1677 * This routine accepts a variable number of message IDs and constructs 1678 * a corresponding error string which is printed via the message print routine 1679 * argument. 1680 */ 1681 static void 1682 cfga_msg(struct cfga_msg *msgp, const char *str) 1683 { 1684 int len; 1685 char *q; 1686 1687 if (msgp == NULL || msgp->message_routine == NULL) { 1688 DPRINTF("cfga_msg: msg\n"); 1689 return; 1690 } 1691 1692 if ((len = strlen(str)) == 0) { 1693 DPRINTF("cfga_msg: null str\n"); 1694 return; 1695 } 1696 1697 if ((q = (char *)calloc(len + 1, 1)) == NULL) { 1698 DPRINTF("cfga_msg: null q\n"); 1699 return; 1700 } 1701 1702 (void) strcpy(q, str); 1703 (*msgp->message_routine)(msgp->appdata_ptr, q); 1704 1705 free(q); 1706 } 1707 1708 1709 /* ARGSUSED */ 1710 cfga_err_t 1711 cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags) 1712 { 1713 DPRINTF("cfga_help:\n"); 1714 if (options) { 1715 cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_UNKNOWN])); 1716 cfga_msg(msgp, options); 1717 } 1718 1719 cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_HEADER])); 1720 cfga_msg(msgp, usb_help[HELP_CONFIG]); 1721 cfga_msg(msgp, usb_help[HELP_RESET_SLOT]); 1722 cfga_msg(msgp, usb_help[HELP_CONFIG_SLOT]); 1723 1724 return (CFGA_OK); 1725 } 1726 1727 1728 static int 1729 usb_confirm(struct cfga_confirm *confp, char *msg) 1730 { 1731 int rval; 1732 1733 if (confp == NULL || confp->confirm == NULL) { 1734 return (0); 1735 } 1736 1737 rval = (*confp->confirm)(confp->appdata_ptr, msg); 1738 DPRINTF("usb_confirm: %d\n", rval); 1739 1740 return (rval); 1741 } 1742 1743 1744 static char * 1745 usb_get_devicepath(const char *ap_id) 1746 { 1747 char *devpath = NULL; 1748 size_t size; 1749 cfga_usb_ret_t rv; 1750 1751 rv = do_control_ioctl(ap_id, HUBD_GET_DEVICE_PATH, 0, 1752 (void **)&devpath, &size); 1753 1754 if (rv == CFGA_USB_OK) { 1755 DPRINTF("usb_get_devicepath: get device path ioctl ok\n"); 1756 return (devpath); 1757 } else { 1758 DPRINTF("usb_get_devicepath: get device path ioctl failed\n"); 1759 return (NULL); 1760 } 1761 } 1762