xref: /onnv-gate/usr/src/lib/cfgadm_plugins/usb/common/cfga_usb.c (revision 7492:2387323b838f)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7492SZhigang.Lu@Sun.COM  * Common Development and Distribution License (the "License").
6*7492SZhigang.Lu@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7492SZhigang.Lu@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include "cfga_usb.h"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /* function prototypes */
310Sstevel@tonic-gate cfga_err_t		usb_err_msg(char **, cfga_usb_ret_t, const char *, int);
320Sstevel@tonic-gate extern cfga_usb_ret_t	usb_rcm_offline(const char *, char **, char *,
330Sstevel@tonic-gate 			    cfga_flags_t);
340Sstevel@tonic-gate extern cfga_usb_ret_t	usb_rcm_online(const char *, char **, char *,
350Sstevel@tonic-gate 			    cfga_flags_t);
360Sstevel@tonic-gate extern cfga_usb_ret_t	usb_rcm_remove(const char *, char **, char *,
370Sstevel@tonic-gate 			    cfga_flags_t);
380Sstevel@tonic-gate static int		usb_confirm(struct cfga_confirm *, char *);
390Sstevel@tonic-gate static char 		*usb_get_devicepath(const char *);
400Sstevel@tonic-gate 
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate  * This file contains the entry points to the plugin as defined in the
430Sstevel@tonic-gate  * config_admin(3X) man page.
440Sstevel@tonic-gate  */
450Sstevel@tonic-gate 
460Sstevel@tonic-gate /*
470Sstevel@tonic-gate  * Set the version number for the cfgadm library's use.
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate int cfga_version = CFGA_HSL_V2;
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #define	HELP_HEADER		1
520Sstevel@tonic-gate #define	HELP_CONFIG		2
530Sstevel@tonic-gate #define	HELP_RESET_SLOT		3
540Sstevel@tonic-gate #define	HELP_CONFIG_SLOT	4
550Sstevel@tonic-gate #define	HELP_UNKNOWN		5
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /* Help messages */
580Sstevel@tonic-gate static char *
590Sstevel@tonic-gate usb_help[] = {
600Sstevel@tonic-gate NULL,
610Sstevel@tonic-gate "USB specific commands:\n",
620Sstevel@tonic-gate " cfgadm -c [configure|unconfigure|disconnect] ap_id [ap_id...]\n",
630Sstevel@tonic-gate " cfgadm -x usb_reset ap_id [ap_id...]\n",
640Sstevel@tonic-gate " cfgadm -x usb_config -o config=<index of desired configuration>  ap_id\n",
650Sstevel@tonic-gate "\tunknown command or option: ",
660Sstevel@tonic-gate NULL
670Sstevel@tonic-gate };	/* End help messages */
680Sstevel@tonic-gate 
690Sstevel@tonic-gate /* Error messages */
700Sstevel@tonic-gate static msgcvt_t
710Sstevel@tonic-gate usb_error_msgs[] = {
720Sstevel@tonic-gate 	/* CFGA_USB_OK	*/
730Sstevel@tonic-gate 	{ CVT, CFGA_OK, "ok" },
740Sstevel@tonic-gate 
750Sstevel@tonic-gate 	/* CFGA_USB_UNKNOWN	*/
760Sstevel@tonic-gate 	{ CVT, CFGA_LIB_ERROR, "Unknown message; internal error" },
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 	/* CFGA_USB_INTERNAL_ERROR	*/
790Sstevel@tonic-gate 	{ CVT, CFGA_LIB_ERROR, "Internal error" },
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	/* CFGA_USB_OPTIONS	*/
820Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "Hardware specific options not supported" },
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 	/* CFGA_USB_DYNAMIC_AP	*/
850Sstevel@tonic-gate 	{ CVT, CFGA_INVAL, "Dynamic attachment points not supported" },
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 	/* CFGA_USB_AP	*/
880Sstevel@tonic-gate 	{ CVT, CFGA_APID_NOEXIST, "" },
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	/* CFGA_USB_PORT	*/
910Sstevel@tonic-gate 	{ CVT, CFGA_LIB_ERROR, "Cannot determine hub port number for " },
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 	/* CFGA_USB_DEVCTL	*/
940Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "Cannot issue devctl to " },
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	/* CFGA_USB_NOT_CONNECTED	*/
970Sstevel@tonic-gate 	{ CVT, CFGA_INVAL, "No device connected to " },
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	/* CFGA_USB_NOT_CONFIGURED	*/
1000Sstevel@tonic-gate 	{ CVT, CFGA_INVAL, "No device configured to " },
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	/* CFGA_USB_ALREADY_CONNECTED	*/
1030Sstevel@tonic-gate 	{ CVT, CFGA_INSUFFICENT_CONDITION,
1040Sstevel@tonic-gate 		"Device already connected; cannot connect again " },
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	/* CFGA_USB_ALREADY_CONFIGURED	*/
1070Sstevel@tonic-gate 	{ CVT, CFGA_INVAL, "device already configured for " },
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	/* CFGA_USB_OPEN	*/
1100Sstevel@tonic-gate 	{ CVT, CFGA_LIB_ERROR, "Cannot open " },
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	/* CFGA_USB_IOCTL	*/
1130Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "Driver ioctl failed " },
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	/* CFGA_USB_BUSY	*/
1160Sstevel@tonic-gate 	{ CVT, CFGA_SYSTEM_BUSY, "" },
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	/* CFGA_USB_ALLOC_FAIL	*/
1190Sstevel@tonic-gate 	{ CVT, CFGA_LIB_ERROR, "Memory allocation failure" },
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	/* CFGA_USB_OPNOTSUPP	*/
1220Sstevel@tonic-gate 	{ CVT, CFGA_OPNOTSUPP, "Operation not supported" },
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 	/* CFGA_USB_DEVLINK	*/
1250Sstevel@tonic-gate 	{ CVT, CFGA_LIB_ERROR, "Could not find /dev/cfg link for " },
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	/* CFGA_USB_STATE	*/
1280Sstevel@tonic-gate 	{ CVT, CFGA_LIB_ERROR, "Internal error: Unrecognized ap state" },
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	/* CFGA_USB_CONFIG_INVAL	*/
1310Sstevel@tonic-gate 	{ CVT, CFGA_ERROR,
1320Sstevel@tonic-gate 		"Specified configuration index unrecognized or exceeds "
1330Sstevel@tonic-gate 		"maximum available" },
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	/* CFGA_USB_PRIV	*/
1360Sstevel@tonic-gate 	{ CVT, CFGA_PRIV, "" },
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	/* CFGA_USB_NVLIST	*/
1390Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "Internal error (nvlist)" },
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	/* CFGA_USB_ZEROLEN	*/
1420Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "Internal error (zerolength string)" },
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	/* CFGA_USB_CONFIG_FILE	*/
1450Sstevel@tonic-gate 	{ CVT, CFGA_ERROR,
1460Sstevel@tonic-gate 	"Cannot open/fstat/read USB system configuration file" },
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	/* CFGA_USB_LOCK_FILE */
1490Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "Cannot lock USB system configuration file" },
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	/* CFGA_USB_UNLOCK_FILE */
1520Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "Cannot unlock USB system configuration file" },
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	/* CFGA_USB_ONE_CONFIG	*/
1550Sstevel@tonic-gate 	{ CVT, CFGA_ERROR,
1560Sstevel@tonic-gate 	"Operation not supported for devices with one configuration" },
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 	/* CFGA_USB_RCM_HANDLE Errors */
1590Sstevel@tonic-gate 	{ CVT, CFGA_ERROR, "cannot get RCM handle"},
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	/* CFGA_USB_RCM_ONLINE */
1620Sstevel@tonic-gate 	{ CVT, CFGA_SYSTEM_BUSY,   "failed to online: "},
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	/* CFGA_USB_RCM_OFFLINE */
1650Sstevel@tonic-gate 	{ CVT, CFGA_SYSTEM_BUSY,   "failed to offline: "},
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	/* CFGA_USB_RCM_INFO */
1680Sstevel@tonic-gate 	{ CVT, CFGA_ERROR,   "failed to query: "}
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate };	/* End error messages */
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate /* ========================================================================= */
1740Sstevel@tonic-gate /*
1750Sstevel@tonic-gate  * The next two funcs imported verbatim from cfgadm_scsi.
1760Sstevel@tonic-gate  * physpath_to_devlink is the only func directly used by cfgadm_usb.
1770Sstevel@tonic-gate  * get_link supports it.
1780Sstevel@tonic-gate  */
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate /*
1810Sstevel@tonic-gate  * Routine to search the /dev directory or a subtree of /dev.
1820Sstevel@tonic-gate  */
1830Sstevel@tonic-gate static int
get_link(di_devlink_t devlink,void * arg)1840Sstevel@tonic-gate get_link(di_devlink_t devlink, void *arg)
1850Sstevel@tonic-gate {
1860Sstevel@tonic-gate 	walk_link_t *larg = (walk_link_t *)arg;
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	/*
1890Sstevel@tonic-gate 	 * When path is specified, it's the node path without minor
1900Sstevel@tonic-gate 	 * name. Therefore, the ../.. prefixes needs to be stripped.
1910Sstevel@tonic-gate 	 */
1920Sstevel@tonic-gate 	if (larg->path) {
1930Sstevel@tonic-gate 		char *content = (char *)di_devlink_content(devlink);
1940Sstevel@tonic-gate 		char *start = strstr(content, "/devices/");
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 		/* line content must have minor node */
1970Sstevel@tonic-gate 		if (start == NULL ||
1980Sstevel@tonic-gate 		    strncmp(start, larg->path, larg->len) != 0 ||
1990Sstevel@tonic-gate 		    start[larg->len] != ':') {
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 			return (DI_WALK_CONTINUE);
2020Sstevel@tonic-gate 		}
2030Sstevel@tonic-gate 	}
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	*(larg->linkpp) = strdup(di_devlink_path(devlink));
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	return (DI_WALK_TERMINATE);
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate /* ARGSUSED */
2120Sstevel@tonic-gate static ucfga_ret_t
physpath_to_devlink(const char * basedir,const char * node_path,char ** logpp,int * l_errnop,int match_minor)2130Sstevel@tonic-gate physpath_to_devlink(
2140Sstevel@tonic-gate 	const char *basedir,
2150Sstevel@tonic-gate 	const char *node_path,
2160Sstevel@tonic-gate 	char **logpp,
2170Sstevel@tonic-gate 	int *l_errnop,
2180Sstevel@tonic-gate 	int match_minor)
2190Sstevel@tonic-gate {
2200Sstevel@tonic-gate 	walk_link_t larg;
2210Sstevel@tonic-gate 	di_devlink_handle_t hdl;
2220Sstevel@tonic-gate 	char *minor_path;
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
2250Sstevel@tonic-gate 		*l_errnop = errno;
2260Sstevel@tonic-gate 		return (UCFGA_LIB_ERR);
2270Sstevel@tonic-gate 	}
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	*logpp = NULL;
2300Sstevel@tonic-gate 	larg.linkpp = logpp;
2310Sstevel@tonic-gate 	if (match_minor) {
2320Sstevel@tonic-gate 		minor_path = (char *)node_path + strlen("/devices");
2330Sstevel@tonic-gate 		larg.path = NULL;
2340Sstevel@tonic-gate 	} else {
2350Sstevel@tonic-gate 		minor_path = NULL;
2360Sstevel@tonic-gate 		larg.len = strlen(node_path);
2370Sstevel@tonic-gate 		larg.path = (char *)node_path;
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	(void) di_devlink_walk(hdl, "^cfg/", minor_path, DI_PRIMARY_LINK,
2410Sstevel@tonic-gate 	    (void *)&larg, get_link);
2420Sstevel@tonic-gate 
243*7492SZhigang.Lu@Sun.COM 	(void) di_devlink_fini(&hdl);
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	if (*logpp == NULL) {
2460Sstevel@tonic-gate 		*l_errnop = errno;
2470Sstevel@tonic-gate 		return (UCFGA_LIB_ERR);
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	return (UCFGA_OK);
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate /* ========================================================================= */
2550Sstevel@tonic-gate /* Utilities */
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate /*
2580Sstevel@tonic-gate  * Given the index into a table (msgcvt_t) of messages, get the message
2590Sstevel@tonic-gate  * string, converting it to the proper locale if necessary.
2600Sstevel@tonic-gate  * NOTE: See cfga_usb.h
2610Sstevel@tonic-gate  */
2620Sstevel@tonic-gate static const char *
get_msg(uint_t msg_index,msgcvt_t * msg_tbl,uint_t tbl_size)2630Sstevel@tonic-gate get_msg(uint_t msg_index, msgcvt_t *msg_tbl, uint_t tbl_size)
2640Sstevel@tonic-gate {
2650Sstevel@tonic-gate 	if (msg_index >= tbl_size) {
2660Sstevel@tonic-gate 		DPRINTF("get_error_msg: bad error msg index: %d\n", msg_index);
2670Sstevel@tonic-gate 		msg_index = CFGA_USB_UNKNOWN;
2680Sstevel@tonic-gate 	}
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	return ((msg_tbl[msg_index].intl) ?
2710Sstevel@tonic-gate 	    dgettext(TEXT_DOMAIN, msg_tbl[msg_index].msgstr) :
2720Sstevel@tonic-gate 	    msg_tbl[msg_index].msgstr);
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate /*
2770Sstevel@tonic-gate  * Allocates and creates a message string (in *ret_str),
2780Sstevel@tonic-gate  * by concatenating all the (char *) args together, in order.
2790Sstevel@tonic-gate  * Last arg MUST be NULL.
2800Sstevel@tonic-gate  */
2810Sstevel@tonic-gate static void
set_msg(char ** ret_str,...)2820Sstevel@tonic-gate set_msg(char **ret_str, ...)
2830Sstevel@tonic-gate {
2840Sstevel@tonic-gate 	char	*str;
2850Sstevel@tonic-gate 	size_t	total_len;
2860Sstevel@tonic-gate 	va_list	valist;
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	va_start(valist, ret_str);
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	total_len = (*ret_str == NULL) ? 0 : strlen(*ret_str);
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	while ((str = va_arg(valist, char *)) != NULL) {
2930Sstevel@tonic-gate 		size_t	len = strlen(str);
2940Sstevel@tonic-gate 		char	*old_str = *ret_str;
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 		*ret_str = (char *)realloc(*ret_str, total_len + len + 1);
2970Sstevel@tonic-gate 		if (*ret_str == NULL) {
2980Sstevel@tonic-gate 			/* We're screwed */
2990Sstevel@tonic-gate 			free(old_str);
3000Sstevel@tonic-gate 			DPRINTF("set_msg: realloc failed.\n");
3010Sstevel@tonic-gate 			va_end(valist);
3020Sstevel@tonic-gate 			return;
3030Sstevel@tonic-gate 		}
3040Sstevel@tonic-gate 
305*7492SZhigang.Lu@Sun.COM 		(void) strcpy(*ret_str + total_len, str);
3060Sstevel@tonic-gate 		total_len += len;
3070Sstevel@tonic-gate 	}
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	va_end(valist);
3100Sstevel@tonic-gate }
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate /*
3140Sstevel@tonic-gate  * Error message handling.
3150Sstevel@tonic-gate  * For the rv passed in, looks up the corresponding error message string(s),
3160Sstevel@tonic-gate  * internationalized it if necessary, and concatenates it into a new
3170Sstevel@tonic-gate  * memory buffer, and points *errstring to it.
3180Sstevel@tonic-gate  * Note not all rvs will result in an error message return, as not all
3190Sstevel@tonic-gate  * error conditions warrant a USB-specific error message.
3200Sstevel@tonic-gate  *
3210Sstevel@tonic-gate  * Some messages may display ap_id or errno, which is why they are passed
3220Sstevel@tonic-gate  * in.
3230Sstevel@tonic-gate  */
3240Sstevel@tonic-gate cfga_err_t
usb_err_msg(char ** errstring,cfga_usb_ret_t rv,const char * ap_id,int l_errno)3250Sstevel@tonic-gate usb_err_msg(char **errstring, cfga_usb_ret_t rv, const char *ap_id, int l_errno)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	if (errstring == NULL) {
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 		return (usb_error_msgs[rv].cfga_err);
3300Sstevel@tonic-gate 	}
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	/*
3330Sstevel@tonic-gate 	 * Generate the appropriate USB-specific error message(s) (if any).
3340Sstevel@tonic-gate 	 */
3350Sstevel@tonic-gate 	switch (rv) {
3360Sstevel@tonic-gate 	case CFGA_USB_OK:
3370Sstevel@tonic-gate 	/* Special case - do nothing.  */
3380Sstevel@tonic-gate 		break;
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	case CFGA_USB_UNKNOWN:
3410Sstevel@tonic-gate 	case CFGA_USB_DYNAMIC_AP:
3420Sstevel@tonic-gate 	case CFGA_USB_INTERNAL_ERROR:
3430Sstevel@tonic-gate 	case CFGA_USB_OPTIONS:
3440Sstevel@tonic-gate 	case CFGA_USB_ALLOC_FAIL:
3450Sstevel@tonic-gate 	case CFGA_USB_STATE:
3460Sstevel@tonic-gate 	case CFGA_USB_CONFIG_INVAL:
3470Sstevel@tonic-gate 	case CFGA_USB_PRIV:
3480Sstevel@tonic-gate 	case CFGA_USB_OPNOTSUPP:
3490Sstevel@tonic-gate 	/* These messages require no additional strings passed. */
3500Sstevel@tonic-gate 		set_msg(errstring, ERR_STR(rv), NULL);
3510Sstevel@tonic-gate 		break;
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	case CFGA_USB_AP:
3540Sstevel@tonic-gate 	case CFGA_USB_PORT:
3550Sstevel@tonic-gate 	case CFGA_USB_NOT_CONNECTED:
3560Sstevel@tonic-gate 	case CFGA_USB_NOT_CONFIGURED:
3570Sstevel@tonic-gate 	case CFGA_USB_ALREADY_CONNECTED:
3580Sstevel@tonic-gate 	case CFGA_USB_ALREADY_CONFIGURED:
3590Sstevel@tonic-gate 	case CFGA_USB_BUSY:
3600Sstevel@tonic-gate 	case CFGA_USB_DEVLINK:
3610Sstevel@tonic-gate 	case CFGA_USB_RCM_HANDLE:
3620Sstevel@tonic-gate 	case CFGA_USB_RCM_ONLINE:
3630Sstevel@tonic-gate 	case CFGA_USB_RCM_OFFLINE:
3640Sstevel@tonic-gate 	case CFGA_USB_RCM_INFO:
3650Sstevel@tonic-gate 	case CFGA_USB_DEVCTL:
3660Sstevel@tonic-gate 	/* These messages also print ap_id.  */
367*7492SZhigang.Lu@Sun.COM 		(void) set_msg(errstring, ERR_STR(rv),
368*7492SZhigang.Lu@Sun.COM 		    "ap_id: ", ap_id, "", NULL);
3690Sstevel@tonic-gate 		break;
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 	case CFGA_USB_IOCTL:
3720Sstevel@tonic-gate 	case CFGA_USB_NVLIST:
3730Sstevel@tonic-gate 	case CFGA_USB_CONFIG_FILE:
3740Sstevel@tonic-gate 	case CFGA_USB_ONE_CONFIG:
3750Sstevel@tonic-gate 	/* These messages also print errno.  */
3760Sstevel@tonic-gate 	{
3770Sstevel@tonic-gate 		char *errno_str = l_errno ? strerror(l_errno) : "";
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 		set_msg(errstring, ERR_STR(rv), errno_str,
3800Sstevel@tonic-gate 		    l_errno ? "\n" : "", NULL);
3810Sstevel@tonic-gate 		break;
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	case CFGA_USB_OPEN:
3850Sstevel@tonic-gate 	/* These messages also apid and errno.  */
3860Sstevel@tonic-gate 	{
3870Sstevel@tonic-gate 		char *errno_str = l_errno ? strerror(l_errno) : "";
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 		set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "\n",
3900Sstevel@tonic-gate 		    errno_str, l_errno ? "\n" : "", NULL);
3910Sstevel@tonic-gate 		break;
3920Sstevel@tonic-gate 	}
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 	default:
3950Sstevel@tonic-gate 		DPRINTF("usb_err_msg: Unrecognized message index: %d\n", rv);
3960Sstevel@tonic-gate 		set_msg(errstring, ERR_STR(CFGA_USB_INTERNAL_ERROR), NULL);
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	}	/* end switch */
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	/*
4010Sstevel@tonic-gate 	 * Determine the proper error code to send back to the cfgadm library.
4020Sstevel@tonic-gate 	 */
4030Sstevel@tonic-gate 	return (usb_error_msgs[rv].cfga_err);
4040Sstevel@tonic-gate }
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate /*
4080Sstevel@tonic-gate  * Ensure the ap_id passed is in the correct (physical ap_id) form:
4090Sstevel@tonic-gate  *     path/device:xx[.xx]+
4100Sstevel@tonic-gate  * where xx is a one or two-digit number.
4110Sstevel@tonic-gate  *
4120Sstevel@tonic-gate  * Note the library always calls the plugin with a physical ap_id.
4130Sstevel@tonic-gate  */
4140Sstevel@tonic-gate static int
verify_valid_apid(const char * ap_id)4150Sstevel@tonic-gate verify_valid_apid(const char *ap_id)
4160Sstevel@tonic-gate {
4170Sstevel@tonic-gate 	char	*l_ap_id;
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	if (ap_id == NULL) {
4200Sstevel@tonic-gate 		return (-1);
4210Sstevel@tonic-gate 	}
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	l_ap_id = strrchr(ap_id, *MINOR_SEP);
4240Sstevel@tonic-gate 	l_ap_id++;
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	if (strspn(l_ap_id, "0123456789.") != strlen(l_ap_id)) {
4270Sstevel@tonic-gate 		/* Bad characters in the ap_id. */
4280Sstevel@tonic-gate 		return (-1);
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	if (strstr(l_ap_id, "..") != NULL) {
4320Sstevel@tonic-gate 		/* ap_id has 1..2 or more than 2 dots */
4330Sstevel@tonic-gate 		return (-1);
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	return (0);
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate /*
4410Sstevel@tonic-gate  * Verify the params passed in are valid.
4420Sstevel@tonic-gate  */
4430Sstevel@tonic-gate static cfga_usb_ret_t
verify_params(const char * ap_id,const char * options,char ** errstring)4440Sstevel@tonic-gate verify_params(
4450Sstevel@tonic-gate 	const char *ap_id,
4460Sstevel@tonic-gate 	const char *options,
4470Sstevel@tonic-gate 	char **errstring)
4480Sstevel@tonic-gate {
4490Sstevel@tonic-gate 	if (errstring != NULL) {
4500Sstevel@tonic-gate 		*errstring = NULL;
4510Sstevel@tonic-gate 	}
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	if (options != NULL) {
4540Sstevel@tonic-gate 		DPRINTF("verify_params: hardware-specific options not "
4550Sstevel@tonic-gate 		    "supported.\n");
4560Sstevel@tonic-gate 		return (CFGA_USB_OPTIONS);
4570Sstevel@tonic-gate 	}
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	/* Dynamic attachment points not supported (yet). */
4600Sstevel@tonic-gate 	if (GET_DYN(ap_id) != NULL) {
4610Sstevel@tonic-gate 		DPRINTF("verify_params: dynamic ap_id passed\n");
4620Sstevel@tonic-gate 		return (CFGA_USB_DYNAMIC_AP);
4630Sstevel@tonic-gate 	}
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	if (verify_valid_apid(ap_id) != 0) {
4660Sstevel@tonic-gate 		DPRINTF("verify_params: not a USB ap_id.\n");
4670Sstevel@tonic-gate 		return (CFGA_USB_AP);
4680Sstevel@tonic-gate 	}
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	return (CFGA_USB_OK);
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate /*
4750Sstevel@tonic-gate  * Takes a validated ap_id and extracts the port number.
4760Sstevel@tonic-gate  */
4770Sstevel@tonic-gate static cfga_usb_ret_t
get_port_num(const char * ap_id,uint_t * port)4780Sstevel@tonic-gate get_port_num(const char *ap_id, uint_t *port)
4790Sstevel@tonic-gate {
4800Sstevel@tonic-gate 	char *port_nbr_str;
4810Sstevel@tonic-gate 	char *temp;
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	port_nbr_str = strrchr(ap_id, *MINOR_SEP) + strlen(MINOR_SEP);
4840Sstevel@tonic-gate 	if ((temp = strrchr(ap_id, (int)*PORT_SEPERATOR)) != 0) {
4850Sstevel@tonic-gate 		port_nbr_str = temp + strlen(PORT_SEPERATOR);
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 	errno = 0;
4890Sstevel@tonic-gate 	*port = strtol(port_nbr_str, NULL, 10);
4900Sstevel@tonic-gate 	if (errno) {
4910Sstevel@tonic-gate 		DPRINTF("get_port_num: conversion of port str failed\n");
4920Sstevel@tonic-gate 		return (CFGA_USB_PORT);
4930Sstevel@tonic-gate 	}
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	return (CFGA_USB_OK);
4960Sstevel@tonic-gate }
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate /*
5000Sstevel@tonic-gate  * Pair of routines to set up for/clean up after a devctl_ap_* lib call.
5010Sstevel@tonic-gate  */
5020Sstevel@tonic-gate static void
cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl,nvlist_t * user_nvlist)5030Sstevel@tonic-gate cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist)
5040Sstevel@tonic-gate {
5050Sstevel@tonic-gate 	if (user_nvlist != NULL) {
5060Sstevel@tonic-gate 		nvlist_free(user_nvlist);
5070Sstevel@tonic-gate 	}
5080Sstevel@tonic-gate 	if (devctl_hdl != NULL) {
5090Sstevel@tonic-gate 		devctl_release(devctl_hdl);
5100Sstevel@tonic-gate 	}
5110Sstevel@tonic-gate }
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate static cfga_usb_ret_t
setup_for_devctl_cmd(const char * ap_id,devctl_hdl_t * devctl_hdl,nvlist_t ** user_nvlistp,uint_t oflag)5150Sstevel@tonic-gate setup_for_devctl_cmd(const char *ap_id, devctl_hdl_t *devctl_hdl,
5160Sstevel@tonic-gate     nvlist_t **user_nvlistp, uint_t oflag)
5170Sstevel@tonic-gate {
5180Sstevel@tonic-gate 	uint32_t	port;
5190Sstevel@tonic-gate 	cfga_usb_ret_t	rv = CFGA_USB_OK;
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	DPRINTF("setup_for_devctl_cmd: oflag=%d\n", oflag);
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	/* Get a handle to the ap */
5240Sstevel@tonic-gate 	if ((*devctl_hdl = devctl_ap_acquire((char *)ap_id, oflag)) == NULL) {
5250Sstevel@tonic-gate 		DPRINTF("setup_for_devctl_cmd: devctl_ap_acquire failed with "
5260Sstevel@tonic-gate 		    "errno: %d\n", errno);
5270Sstevel@tonic-gate 		rv = CFGA_USB_DEVCTL;
5280Sstevel@tonic-gate 		goto bailout;
5290Sstevel@tonic-gate 	}
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	/* Set up to pass port number down to driver */
5320Sstevel@tonic-gate 	if (nvlist_alloc(user_nvlistp, NV_UNIQUE_NAME_TYPE, NULL) != 0) {
5330Sstevel@tonic-gate 		DPRINTF("setup_for_devctl: nvlist_alloc failed, errno: %d\n",
5340Sstevel@tonic-gate 		    errno);
5350Sstevel@tonic-gate 		*user_nvlistp = NULL;	/* Prevent possible incorrect free in */
5360Sstevel@tonic-gate 					/* cleanup_after_devctl_cmd */
5370Sstevel@tonic-gate 		rv = CFGA_USB_NVLIST;
5380Sstevel@tonic-gate 		goto bailout;
5390Sstevel@tonic-gate 	}
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	if ((rv = get_port_num(ap_id, &port)) != CFGA_USB_OK) {
5420Sstevel@tonic-gate 		DPRINTF("setup_for_devctl_cmd: get_port_num, errno: %d\n",
5430Sstevel@tonic-gate 		    errno);
5440Sstevel@tonic-gate 		goto bailout;
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	/* creates an int32_t entry */
5480Sstevel@tonic-gate 	if (nvlist_add_int32(*user_nvlistp, PORT, port) == -1) {
5490Sstevel@tonic-gate 		DPRINTF("setup_for_devctl_cmd: nvlist_add_int32 failed. "
5500Sstevel@tonic-gate 		    "errno: %d\n", errno);
5510Sstevel@tonic-gate 		rv = CFGA_USB_NVLIST;
5520Sstevel@tonic-gate 		goto bailout;
5530Sstevel@tonic-gate 	}
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	return (rv);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate bailout:
5580Sstevel@tonic-gate 	cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp);
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	return (rv);
5610Sstevel@tonic-gate }
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate /*
5650Sstevel@tonic-gate  * Ensure that there's a device actually connected to the ap
5660Sstevel@tonic-gate  */
5670Sstevel@tonic-gate static cfga_usb_ret_t
device_configured(devctl_hdl_t hdl,nvlist_t * nvl,ap_rstate_t * rstate)5680Sstevel@tonic-gate device_configured(devctl_hdl_t hdl, nvlist_t *nvl, ap_rstate_t *rstate)
5690Sstevel@tonic-gate {
5700Sstevel@tonic-gate 	cfga_usb_ret_t		rv;
5710Sstevel@tonic-gate 	devctl_ap_state_t	devctl_ap_state;
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	DPRINTF("device_configured:\n");
5740Sstevel@tonic-gate 	if (devctl_ap_getstate(hdl, nvl, &devctl_ap_state) == -1) {
5750Sstevel@tonic-gate 		DPRINTF("devctl_ap_getstate failed, errno: %d\n", errno);
5760Sstevel@tonic-gate 		return (CFGA_USB_DEVCTL);
5770Sstevel@tonic-gate 	}
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 	rv = CFGA_USB_ALREADY_CONFIGURED;
5800Sstevel@tonic-gate 	*rstate = devctl_ap_state.ap_rstate;
5810Sstevel@tonic-gate 	if (devctl_ap_state.ap_ostate != AP_OSTATE_CONFIGURED) {
5820Sstevel@tonic-gate 		return (CFGA_USB_NOT_CONFIGURED);
5830Sstevel@tonic-gate 	}
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	return (rv);
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate /*
5900Sstevel@tonic-gate  * Ensure that there's a device actually connected to the ap
5910Sstevel@tonic-gate  */
5920Sstevel@tonic-gate static cfga_usb_ret_t
device_connected(devctl_hdl_t hdl,nvlist_t * list,ap_ostate_t * ostate)5930Sstevel@tonic-gate device_connected(devctl_hdl_t hdl, nvlist_t *list, ap_ostate_t *ostate)
5940Sstevel@tonic-gate {
5950Sstevel@tonic-gate 	cfga_usb_ret_t		rv = CFGA_USB_ALREADY_CONNECTED;
5960Sstevel@tonic-gate 	devctl_ap_state_t	devctl_ap_state;
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	DPRINTF("device_connected:\n");
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 	if (devctl_ap_getstate(hdl, list, &devctl_ap_state) == -1) {
6010Sstevel@tonic-gate 		DPRINTF("devctl_ap_getstate failed, errno: %d\n", errno);
6020Sstevel@tonic-gate 		return (CFGA_USB_DEVCTL);
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	*ostate =  devctl_ap_state.ap_ostate;
6060Sstevel@tonic-gate 	if (devctl_ap_state.ap_rstate != AP_RSTATE_CONNECTED) {
6070Sstevel@tonic-gate 		return (CFGA_USB_NOT_CONNECTED);
6080Sstevel@tonic-gate 	}
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	return (rv);
6110Sstevel@tonic-gate }
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate /*
6150Sstevel@tonic-gate  * Given a subcommand to the DEVCTL_AP_CONTROL ioctl, rquest the size of
6160Sstevel@tonic-gate  * the data to be returned, allocate a buffer, then get the data.
6170Sstevel@tonic-gate  * Returns *descrp (which must be freed) and size.
6180Sstevel@tonic-gate  *
6190Sstevel@tonic-gate  * Note USB_DESCR_TYPE_STRING returns an ASCII NULL-terminated string,
6200Sstevel@tonic-gate  * not a string descr.
6210Sstevel@tonic-gate  */
6220Sstevel@tonic-gate cfga_usb_ret_t
do_control_ioctl(const char * ap_id,uint_t subcommand,uint_t arg,void ** descrp,size_t * sizep)6230Sstevel@tonic-gate do_control_ioctl(const char *ap_id, uint_t subcommand, uint_t arg,
6240Sstevel@tonic-gate 	void **descrp, size_t *sizep)
6250Sstevel@tonic-gate {
6260Sstevel@tonic-gate 	int			fd = -1;
6270Sstevel@tonic-gate 	uint_t			port;
6280Sstevel@tonic-gate 	uint32_t		local_size;
6290Sstevel@tonic-gate 	cfga_usb_ret_t		rv = CFGA_USB_OK;
6300Sstevel@tonic-gate 	struct hubd_ioctl_data	ioctl_data;
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	assert(descrp != NULL);
6330Sstevel@tonic-gate 	*descrp = NULL;
6340Sstevel@tonic-gate 	assert(sizep != NULL);
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	if ((rv = get_port_num(ap_id, &port)) != CFGA_USB_OK) {
6370Sstevel@tonic-gate 		goto bailout;
6380Sstevel@tonic-gate 	}
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	if ((fd = open(ap_id, O_RDONLY)) == -1) {
6410Sstevel@tonic-gate 		DPRINTF("do_control_ioctl: open failed: errno:%d\n", errno);
6420Sstevel@tonic-gate 		rv = CFGA_USB_OPEN;
6430Sstevel@tonic-gate 		if (errno == EBUSY) {
6440Sstevel@tonic-gate 			rv = CFGA_USB_BUSY;
6450Sstevel@tonic-gate 		}
6460Sstevel@tonic-gate 		goto bailout;
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	ioctl_data.cmd = subcommand;
6500Sstevel@tonic-gate 	ioctl_data.port = port;
6510Sstevel@tonic-gate 	ioctl_data.misc_arg = (uint_t)arg;
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	/*
6540Sstevel@tonic-gate 	 * Find out how large a buf we need to get the data.
6550Sstevel@tonic-gate 	 *
6560Sstevel@tonic-gate 	 * Note the ioctls only accept/return a 32-bit int for a get_size
6570Sstevel@tonic-gate 	 * to avoid 32/64 and BE/LE issues.
6580Sstevel@tonic-gate 	 */
6590Sstevel@tonic-gate 	ioctl_data.get_size = B_TRUE;
6600Sstevel@tonic-gate 	ioctl_data.buf = (caddr_t)&local_size;
6610Sstevel@tonic-gate 	ioctl_data.bufsiz = sizeof (local_size);
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
6640Sstevel@tonic-gate 		DPRINTF("do_control_ioctl: size ioctl failed: errno:%d\n",
6650Sstevel@tonic-gate 		    errno);
6660Sstevel@tonic-gate 		rv = CFGA_USB_IOCTL;
6670Sstevel@tonic-gate 		goto bailout;
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 	*sizep = local_size;
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	if (subcommand == USB_DESCR_TYPE_STRING &&
6720Sstevel@tonic-gate 	    arg == HUBD_CFG_DESCR_STR && local_size == 0) {
6730Sstevel@tonic-gate 		/* Zero-length data - nothing to do.  */
6740Sstevel@tonic-gate 		rv = CFGA_USB_ZEROLEN;
6750Sstevel@tonic-gate 		goto bailout;
6760Sstevel@tonic-gate 	}
6770Sstevel@tonic-gate 	if (subcommand == HUBD_REFRESH_DEVDB) {
6780Sstevel@tonic-gate 		/* Already done - no data transfer; nothing left to do. */
6790Sstevel@tonic-gate 		goto bailout;
6800Sstevel@tonic-gate 	}
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	if ((*descrp = malloc(*sizep)) == NULL) {
6830Sstevel@tonic-gate 		DPRINTF("do_control_ioctl: malloc failed\n");
6840Sstevel@tonic-gate 		rv = CFGA_USB_ALLOC_FAIL;
6850Sstevel@tonic-gate 		goto bailout;
6860Sstevel@tonic-gate 	}
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	/* Get the data */
6890Sstevel@tonic-gate 	ioctl_data.get_size = B_FALSE;
6900Sstevel@tonic-gate 	ioctl_data.buf = *descrp;
6910Sstevel@tonic-gate 	ioctl_data.bufsiz = *sizep;
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 	if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
6940Sstevel@tonic-gate 		DPRINTF("do_control_ioctl: ioctl failed: errno:%d\n",
6950Sstevel@tonic-gate 		    errno);
6960Sstevel@tonic-gate 		rv = CFGA_USB_IOCTL;
6970Sstevel@tonic-gate 		goto bailout;
6980Sstevel@tonic-gate 	}
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 	(void) close(fd);
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	return (rv);
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate bailout:
7060Sstevel@tonic-gate 	if (fd != -1) {
7070Sstevel@tonic-gate 		(void) close(fd);
7080Sstevel@tonic-gate 	}
7090Sstevel@tonic-gate 	if (*descrp != NULL) {
7100Sstevel@tonic-gate 		free(*descrp);
7110Sstevel@tonic-gate 		*descrp = NULL;
7120Sstevel@tonic-gate 	}
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 	if (rv == CFGA_USB_IOCTL && errno == EBUSY) {
7150Sstevel@tonic-gate 		rv = CFGA_USB_BUSY;	/* Provide more useful msg */
7160Sstevel@tonic-gate 	}
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 	return (rv);
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate /* ========================================================================= */
7230Sstevel@tonic-gate /*
7240Sstevel@tonic-gate  * Support funcs called directly from cfga_* entry points.
7250Sstevel@tonic-gate  */
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate /*
7290Sstevel@tonic-gate  * Invoked from cfga_private_func.
7300Sstevel@tonic-gate  * Modify the USB persistant configuration file so that the device
7310Sstevel@tonic-gate  * represented by ap_id will henceforth be initialized to the desired
7320Sstevel@tonic-gate  * configuration setting (configuration index).
7330Sstevel@tonic-gate  */
7340Sstevel@tonic-gate static cfga_usb_ret_t
set_configuration(const char * ap_id,uint_t config,char * driver,usb_dev_descr_t * descrp,char ** errstring)7350Sstevel@tonic-gate set_configuration(const char *ap_id, uint_t config, char *driver,
7360Sstevel@tonic-gate     usb_dev_descr_t *descrp, char **errstring)
7370Sstevel@tonic-gate {
7380Sstevel@tonic-gate 	char		*serial_no = NULL;
7390Sstevel@tonic-gate 	char		*dev_path = NULL;
7400Sstevel@tonic-gate 	char		*tmp;
7410Sstevel@tonic-gate 	size_t		size;
7420Sstevel@tonic-gate 	cfga_usb_ret_t	rv = CFGA_USB_OK;
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	DPRINTF("set_configuration: ap_id: %s, config:%d\n", ap_id, config);
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	/* Only one bNumConfigurations, don't allow this operation */
7470Sstevel@tonic-gate 	if (descrp->bNumConfigurations == 1) {
7480Sstevel@tonic-gate 		DPRINTF("device supports %d configurations\n",
7490Sstevel@tonic-gate 		    descrp->bNumConfigurations);
7500Sstevel@tonic-gate 		rv = CFGA_USB_ONE_CONFIG;
7510Sstevel@tonic-gate 		goto bailout;
7520Sstevel@tonic-gate 	}
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 	/* get the serial number string if it exists */
7550Sstevel@tonic-gate 	if (descrp->iSerialNumber != 0) {
7560Sstevel@tonic-gate 		if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING,
7570Sstevel@tonic-gate 		    HUBD_SERIALNO_STR, (void **)&serial_no, &size)) !=
7580Sstevel@tonic-gate 		    CFGA_USB_OK) {
7590Sstevel@tonic-gate 			if (rv != CFGA_USB_ZEROLEN) {
7600Sstevel@tonic-gate 				DPRINTF("set_configuration: get serial "
7610Sstevel@tonic-gate 				    "no string failed\n");
7620Sstevel@tonic-gate 				goto bailout;
7630Sstevel@tonic-gate 			}
7640Sstevel@tonic-gate 		}
7650Sstevel@tonic-gate 	}
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	dev_path = usb_get_devicepath(ap_id);
7680Sstevel@tonic-gate 	if (dev_path == NULL) {
7690Sstevel@tonic-gate 		DPRINTF("get device path failed\n");
7700Sstevel@tonic-gate 		rv = CFGA_USB_DEVCTL;
7710Sstevel@tonic-gate 		goto bailout;
7720Sstevel@tonic-gate 	}
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate 	DPRINTF("calling add_entry: vid: 0x%x pid:0x%x config:0x%x,",
7750Sstevel@tonic-gate 	    descrp->idVendor, descrp->idProduct, config);
7760Sstevel@tonic-gate 	DPRINTF("serial_no: %s\n\tdev_path: %s\n\tdriver: %s\n", serial_no ?
7770Sstevel@tonic-gate 	    serial_no : "", dev_path ? dev_path : "", driver ? driver : "");
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	/*
7800Sstevel@tonic-gate 	 * the devicepath should be an absolute path.
7810Sstevel@tonic-gate 	 * So, if path has leading "/devices" - nuke it.
7820Sstevel@tonic-gate 	 */
7830Sstevel@tonic-gate 	if (strncmp(dev_path, "/devices/", 9) == 0) {
7840Sstevel@tonic-gate 		tmp = dev_path + 8;
7850Sstevel@tonic-gate 	} else {
7860Sstevel@tonic-gate 		tmp = dev_path;
7870Sstevel@tonic-gate 	}
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	/* Save an entry in the USBCONF_FILE  */
7900Sstevel@tonic-gate 	if ((rv = add_entry(
7910Sstevel@tonic-gate 	    "enable",		/* Always to "enable" */
7920Sstevel@tonic-gate 	    descrp->idVendor,	/* vendorId */
7930Sstevel@tonic-gate 	    descrp->idProduct,	/* ProductId */
7940Sstevel@tonic-gate 	    config,		/* new cfgndx */
7950Sstevel@tonic-gate 	    serial_no,		/* serial no string */
7960Sstevel@tonic-gate 	    tmp,		/* device path */
7970Sstevel@tonic-gate 	    driver,		/* Driver (optional) */
7980Sstevel@tonic-gate 	    errstring))
7990Sstevel@tonic-gate 	    != CFGA_USB_OK) {
8000Sstevel@tonic-gate 		DPRINTF("set_configuration: add_entry failed\n");
8010Sstevel@tonic-gate 		goto bailout;
8020Sstevel@tonic-gate 	}
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	/* Notify hubd that it needs to refresh its db.  */
8050Sstevel@tonic-gate 	if ((rv = do_control_ioctl(ap_id, HUBD_REFRESH_DEVDB, NULL,
8060Sstevel@tonic-gate 	    (void **)&dev_path, &size)) != CFGA_USB_OK) {
8070Sstevel@tonic-gate 		DPRINTF("set_configuration: HUBD_REFRESH_DEVDB failed\n");
8080Sstevel@tonic-gate 		goto bailout;
8090Sstevel@tonic-gate 	}
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate bailout:
8120Sstevel@tonic-gate 	if (dev_path) {
8130Sstevel@tonic-gate 		free(dev_path);
8140Sstevel@tonic-gate 	}
8150Sstevel@tonic-gate 	if (serial_no) {
8160Sstevel@tonic-gate 		free(serial_no);
8170Sstevel@tonic-gate 	}
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	return (rv);
8200Sstevel@tonic-gate }
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate /*
8240Sstevel@tonic-gate  * Invoked from cfga_private_func() and fill_in_ap_info().
8250Sstevel@tonic-gate  * Call into USBA and get the current configuration setting for this device,
8260Sstevel@tonic-gate  */
8270Sstevel@tonic-gate static cfga_usb_ret_t
get_config(const char * ap_id,uint_t * config)8280Sstevel@tonic-gate get_config(const char *ap_id, uint_t *config)
8290Sstevel@tonic-gate {
8300Sstevel@tonic-gate 	size_t		size;
8310Sstevel@tonic-gate 	uint_t		*config_val = NULL;
8320Sstevel@tonic-gate 	cfga_usb_ret_t	rv;
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	if ((rv = do_control_ioctl(ap_id, HUBD_GET_CURRENT_CONFIG, NULL,
8350Sstevel@tonic-gate 	    (void **)&config_val, &size)) != CFGA_USB_OK) {
8360Sstevel@tonic-gate 		DPRINTF("get_config: get current config descr failed\n");
8370Sstevel@tonic-gate 		goto bailout;
8380Sstevel@tonic-gate 	}
8390Sstevel@tonic-gate 	*config = *config_val;
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate bailout:
8420Sstevel@tonic-gate 	free(config_val);
8430Sstevel@tonic-gate 	return (rv);
8440Sstevel@tonic-gate }
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate /*
8480Sstevel@tonic-gate  * Invoked from cfga_private_func.
8490Sstevel@tonic-gate  * it does an unconfigure of the device followed by a configure,
8500Sstevel@tonic-gate  * thus essentially resetting the device.
8510Sstevel@tonic-gate  */
8520Sstevel@tonic-gate static cfga_usb_ret_t
reset_device(devctl_hdl_t devctl_hdl,nvlist_t * nvl)8530Sstevel@tonic-gate reset_device(devctl_hdl_t devctl_hdl, nvlist_t *nvl)
8540Sstevel@tonic-gate {
8550Sstevel@tonic-gate 	cfga_usb_ret_t	rv;
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	DPRINTF("reset_device: \n");
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	/*
8600Sstevel@tonic-gate 	 * Disconnect and reconfigure the device.
8610Sstevel@tonic-gate 	 * Note this forces the new default config to take effect.
8620Sstevel@tonic-gate 	 */
8630Sstevel@tonic-gate 	if (devctl_ap_disconnect(devctl_hdl, nvl) != 0) {
8640Sstevel@tonic-gate 		DPRINTF("devctl_ap_unconfigure failed, errno: %d\n", errno);
8650Sstevel@tonic-gate 		rv = CFGA_USB_DEVCTL;
8660Sstevel@tonic-gate 		if (errno == EBUSY) {
8670Sstevel@tonic-gate 			rv = CFGA_USB_BUSY;	/* Provide more useful msg */
8680Sstevel@tonic-gate 		}
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 		return (rv);
8710Sstevel@tonic-gate 	}
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	if (devctl_ap_configure(devctl_hdl, nvl) != 0) {
8740Sstevel@tonic-gate 		DPRINTF(" devctl_ap_configure failed, errno = %d\n", errno);
8750Sstevel@tonic-gate 		return (CFGA_USB_DEVCTL);
8760Sstevel@tonic-gate 	}
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	return (CFGA_USB_OK);
8790Sstevel@tonic-gate }
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate /*
8830Sstevel@tonic-gate  * Called from cfga_list_ext.
8840Sstevel@tonic-gate  * Fills in the 'misc_info' field in the cfga buffer (displayed with -lv).
8850Sstevel@tonic-gate  */
8860Sstevel@tonic-gate static cfga_usb_ret_t
fill_in_ap_info(const char * ap_id,char * info_buf,size_t info_size)8870Sstevel@tonic-gate fill_in_ap_info(const char *ap_id, char *info_buf, size_t info_size)
8880Sstevel@tonic-gate {
8890Sstevel@tonic-gate 	char			*mfg_str = NULL;	/* iManufacturer */
8900Sstevel@tonic-gate 	char			*prod_str = NULL;	/* iProduct */
8910Sstevel@tonic-gate 	char			*cfg_descr = NULL;	/* iConfiguration */
8920Sstevel@tonic-gate 	uint_t			config;			/* curr cfg index */
8930Sstevel@tonic-gate 	size_t			size;			/* tmp stuff */
8940Sstevel@tonic-gate 	boolean_t		flag;		/* wether to print ":" or not */
8950Sstevel@tonic-gate 	boolean_t		free_mfg_str = B_FALSE;
8960Sstevel@tonic-gate 	boolean_t		free_prod_str = B_FALSE;
8970Sstevel@tonic-gate 	boolean_t		free_cfg_str = B_FALSE;
8980Sstevel@tonic-gate 	cfga_usb_ret_t		rv = CFGA_USB_OK;
8990Sstevel@tonic-gate 	usb_dev_descr_t		*dev_descrp = NULL;	/* device descriptor */
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 	DPRINTF("fill_in_ap_info:\n");
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, NULL,
9040Sstevel@tonic-gate 	    (void **)&dev_descrp, &size)) != CFGA_USB_OK) {
9050Sstevel@tonic-gate 		DPRINTF("fill_in_ap_info: get dev descr failed\n");
9060Sstevel@tonic-gate 		return (rv);
9070Sstevel@tonic-gate 	}
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	/* iManufacturer */
9100Sstevel@tonic-gate 	mfg_str = USB_UNDEF_STR;
9110Sstevel@tonic-gate 	if (dev_descrp->iManufacturer != 0) {
9120Sstevel@tonic-gate 		if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING,
9130Sstevel@tonic-gate 		    HUBD_MFG_STR, (void **)&mfg_str, &size)) != CFGA_USB_OK) {
9140Sstevel@tonic-gate 			if (rv == CFGA_USB_ZEROLEN) {
9150Sstevel@tonic-gate 				rv = CFGA_USB_OK;
9160Sstevel@tonic-gate 			} else {
9170Sstevel@tonic-gate 				DPRINTF("get iManufacturer failed\n");
9180Sstevel@tonic-gate 				goto bailout;
9190Sstevel@tonic-gate 			}
9200Sstevel@tonic-gate 		}
9210Sstevel@tonic-gate 		free_mfg_str = B_TRUE;
9220Sstevel@tonic-gate 	}
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	/* iProduct */
9250Sstevel@tonic-gate 	prod_str = USB_UNDEF_STR;
9260Sstevel@tonic-gate 	if (dev_descrp->iProduct != 0) {
9270Sstevel@tonic-gate 		if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING,
9280Sstevel@tonic-gate 		    HUBD_PRODUCT_STR, (void **)&prod_str,
9290Sstevel@tonic-gate 		    &size)) != CFGA_USB_OK) {
9300Sstevel@tonic-gate 			if (rv == CFGA_USB_ZEROLEN) {
9310Sstevel@tonic-gate 				rv = CFGA_USB_OK;
9320Sstevel@tonic-gate 			} else {
9330Sstevel@tonic-gate 				DPRINTF("getting iProduct failed\n");
9340Sstevel@tonic-gate 				goto bailout;
9350Sstevel@tonic-gate 			}
9360Sstevel@tonic-gate 		}
9370Sstevel@tonic-gate 		free_prod_str = B_TRUE;
9380Sstevel@tonic-gate 	}
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	/* Current conifguration */
9410Sstevel@tonic-gate 	if ((rv = get_config(ap_id, &config)) != CFGA_USB_OK) {
9420Sstevel@tonic-gate 		DPRINTF("get_config failed\n");
9430Sstevel@tonic-gate 		goto bailout;
9440Sstevel@tonic-gate 	}
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate 	/* Configuration string descriptor */
9470Sstevel@tonic-gate 	cfg_descr = USB_NO_CFG_STR;
9480Sstevel@tonic-gate 	if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING,
9490Sstevel@tonic-gate 	    HUBD_CFG_DESCR_STR, (void **)&cfg_descr, &size)) != CFGA_USB_OK) {
9500Sstevel@tonic-gate 		if (rv == CFGA_USB_ZEROLEN) {
9510Sstevel@tonic-gate 			rv = CFGA_USB_OK;
9520Sstevel@tonic-gate 			flag = B_TRUE;
9530Sstevel@tonic-gate 		} else {
9540Sstevel@tonic-gate 			DPRINTF("HUBD_CFG_DESCR_STR failed\n");
9550Sstevel@tonic-gate 			goto bailout;
9560Sstevel@tonic-gate 		}
9570Sstevel@tonic-gate 	}
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	/* add ": " to output coz PSARC case says so */
9600Sstevel@tonic-gate 	if ((cfg_descr != (char *)NULL) && rv != CFGA_USB_ZEROLEN) {
9610Sstevel@tonic-gate 		flag = B_TRUE;
9620Sstevel@tonic-gate 		free_cfg_str = B_TRUE;
9630Sstevel@tonic-gate 	} else {
9640Sstevel@tonic-gate 		flag = B_FALSE;
9650Sstevel@tonic-gate 		cfg_descr = USB_NO_CFG_STR;
9660Sstevel@tonic-gate 	}
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	/* Dump local buf into passed-in buf. */
9690Sstevel@tonic-gate 	(void) snprintf(info_buf, info_size,
9700Sstevel@tonic-gate 	    "Mfg: %s  Product: %s  NConfigs: %d  Config: %d  %s%s", mfg_str,
9710Sstevel@tonic-gate 	    prod_str, dev_descrp->bNumConfigurations, config,
9720Sstevel@tonic-gate 	    (flag == B_TRUE) ? ": " : "", cfg_descr);
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate bailout:
9750Sstevel@tonic-gate 	if (dev_descrp) {
9760Sstevel@tonic-gate 		free(dev_descrp);
9770Sstevel@tonic-gate 	}
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 	if ((free_mfg_str == B_TRUE) && mfg_str) {
9800Sstevel@tonic-gate 		free(mfg_str);
9810Sstevel@tonic-gate 	}
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	if ((free_prod_str == B_TRUE) && prod_str) {
9840Sstevel@tonic-gate 		free(prod_str);
9850Sstevel@tonic-gate 	}
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate 	if ((free_cfg_str == B_TRUE) && cfg_descr) {
9880Sstevel@tonic-gate 		free(cfg_descr);
9890Sstevel@tonic-gate 	}
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	return (rv);
9920Sstevel@tonic-gate }
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate /* ========================================================================== */
9960Sstevel@tonic-gate /* Entry points */
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate /*ARGSUSED*/
10000Sstevel@tonic-gate cfga_err_t
cfga_change_state(cfga_cmd_t state_change_cmd,const char * ap_id,const char * options,struct cfga_confirm * confp,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)10010Sstevel@tonic-gate cfga_change_state(
10020Sstevel@tonic-gate 	cfga_cmd_t state_change_cmd,
10030Sstevel@tonic-gate 	const char *ap_id,
10040Sstevel@tonic-gate 	const char *options,
10050Sstevel@tonic-gate 	struct cfga_confirm *confp,
10060Sstevel@tonic-gate 	struct cfga_msg *msgp,
10070Sstevel@tonic-gate 	char **errstring,
10080Sstevel@tonic-gate 	cfga_flags_t flags)
10090Sstevel@tonic-gate {
10100Sstevel@tonic-gate 	int		ret;
10110Sstevel@tonic-gate 	int		len;
10120Sstevel@tonic-gate 	char		*msg;
10130Sstevel@tonic-gate 	char		*devpath;
10140Sstevel@tonic-gate 	nvlist_t	*nvl = NULL;
10150Sstevel@tonic-gate 	ap_rstate_t	rstate;
10160Sstevel@tonic-gate 	ap_ostate_t	ostate;
10170Sstevel@tonic-gate 	devctl_hdl_t	hdl = NULL;
10180Sstevel@tonic-gate 	cfga_usb_ret_t	rv = CFGA_USB_OK;
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate 	DPRINTF("cfga_change_state:\n");
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) {
10230Sstevel@tonic-gate 		(void) cfga_help(msgp, options, flags);
10240Sstevel@tonic-gate 		goto bailout;
10250Sstevel@tonic-gate 	}
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 	/*
10280Sstevel@tonic-gate 	 * All subcommands which can change state of device require
10290Sstevel@tonic-gate 	 * root privileges.
10300Sstevel@tonic-gate 	 */
10310Sstevel@tonic-gate 	if (geteuid() != 0) {
10320Sstevel@tonic-gate 		rv = CFGA_USB_PRIV;
10330Sstevel@tonic-gate 		goto bailout;
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &nvl, 0)) !=
10370Sstevel@tonic-gate 	    CFGA_USB_OK) {
10380Sstevel@tonic-gate 		goto bailout;
10390Sstevel@tonic-gate 	}
10400Sstevel@tonic-gate 
10410Sstevel@tonic-gate 	switch (state_change_cmd) {
10420Sstevel@tonic-gate 	case CFGA_CMD_CONFIGURE:
10430Sstevel@tonic-gate 		if ((rv = device_configured(hdl, nvl, &rstate)) !=
10440Sstevel@tonic-gate 		    CFGA_USB_NOT_CONFIGURED) {
10450Sstevel@tonic-gate 			goto bailout;
10460Sstevel@tonic-gate 		}
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 		if (rstate == AP_RSTATE_EMPTY) {
10490Sstevel@tonic-gate 			goto bailout;
10500Sstevel@tonic-gate 		}
10510Sstevel@tonic-gate 		rv = CFGA_USB_OK;	/* Other statuses don't matter */
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 		if (devctl_ap_configure(hdl, nvl) != 0) {
10540Sstevel@tonic-gate 			DPRINTF("cfga_change_state: devctl_ap_configure "
10550Sstevel@tonic-gate 			    "failed.  errno: %d\n", errno);
10560Sstevel@tonic-gate 			rv = CFGA_USB_DEVCTL;
10570Sstevel@tonic-gate 		}
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 		devpath = usb_get_devicepath(ap_id);
10600Sstevel@tonic-gate 		if (devpath == NULL) {
10610Sstevel@tonic-gate 			int i;
10620Sstevel@tonic-gate 			/*
10630Sstevel@tonic-gate 			 * try for some time as USB hotplug thread
10640Sstevel@tonic-gate 			 * takes a while to create the path
10650Sstevel@tonic-gate 			 * and then eventually give up
10660Sstevel@tonic-gate 			 */
10670Sstevel@tonic-gate 			for (i = 0; i < 12 && (devpath == NULL); i++) {
1068*7492SZhigang.Lu@Sun.COM 				(void) sleep(6);
10690Sstevel@tonic-gate 				devpath = usb_get_devicepath(ap_id);
10700Sstevel@tonic-gate 			}
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate 			if (devpath == NULL) {
10730Sstevel@tonic-gate 				DPRINTF("cfga_change_state: get device "
10740Sstevel@tonic-gate 				    "path failed i = %d\n", i);
10750Sstevel@tonic-gate 				rv = CFGA_USB_DEVCTL;
10760Sstevel@tonic-gate 				break;
10770Sstevel@tonic-gate 			}
10780Sstevel@tonic-gate 		}
10790Sstevel@tonic-gate 		S_FREE(devpath);
10800Sstevel@tonic-gate 		break;
10810Sstevel@tonic-gate 	case CFGA_CMD_UNCONFIGURE:
10820Sstevel@tonic-gate 		if ((rv = device_connected(hdl, nvl, &ostate)) !=
10830Sstevel@tonic-gate 		    CFGA_USB_ALREADY_CONNECTED) {
10840Sstevel@tonic-gate 			goto bailout;
10850Sstevel@tonic-gate 		}
10860Sstevel@tonic-gate 
10870Sstevel@tonic-gate 		/* check if it is already unconfigured */
10880Sstevel@tonic-gate 		if ((rv = device_configured(hdl, nvl, &rstate)) ==
10890Sstevel@tonic-gate 		    CFGA_USB_NOT_CONFIGURED) {
10900Sstevel@tonic-gate 			goto bailout;
10910Sstevel@tonic-gate 		}
10920Sstevel@tonic-gate 		rv = CFGA_USB_OK;	/* Other statuses don't matter */
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate 		len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1095*7492SZhigang.Lu@Sun.COM 		    strlen("Unconfigure") + strlen(ap_id);
10960Sstevel@tonic-gate 		if ((msg = (char *)calloc(len + 3, 1)) != NULL) {
10970Sstevel@tonic-gate 			(void) snprintf(msg, len + 3, "Unconfigure %s%s\n%s",
10980Sstevel@tonic-gate 			    USB_CONFIRM_0, ap_id, USB_CONFIRM_1);
10990Sstevel@tonic-gate 		}
11000Sstevel@tonic-gate 		if (!usb_confirm(confp, msg)) {
11010Sstevel@tonic-gate 			free(msg);
11020Sstevel@tonic-gate 			cleanup_after_devctl_cmd(hdl, nvl);
11030Sstevel@tonic-gate 			return (CFGA_NACK);
11040Sstevel@tonic-gate 		}
11050Sstevel@tonic-gate 		free(msg);
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 		devpath = usb_get_devicepath(ap_id);
11080Sstevel@tonic-gate 		if (devpath == NULL) {
11090Sstevel@tonic-gate 			DPRINTF("cfga_change_state: get device path failed\n");
11100Sstevel@tonic-gate 			rv = CFGA_USB_DEVCTL;
11110Sstevel@tonic-gate 			break;
11120Sstevel@tonic-gate 		}
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 		if ((rv = usb_rcm_offline(ap_id, errstring, devpath, flags)) !=
11150Sstevel@tonic-gate 		    CFGA_USB_OK) {
11160Sstevel@tonic-gate 			break;
11170Sstevel@tonic-gate 		}
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 		ret = devctl_ap_unconfigure(hdl, nvl);
11200Sstevel@tonic-gate 		if (ret != 0) {
11210Sstevel@tonic-gate 			DPRINTF("cfga_change_state: devctl_ap_unconfigure "
11220Sstevel@tonic-gate 			    "failed with errno: %d\n", errno);
11230Sstevel@tonic-gate 			rv = CFGA_USB_DEVCTL;
11240Sstevel@tonic-gate 			if (errno == EBUSY) {
11250Sstevel@tonic-gate 				rv = CFGA_USB_BUSY;
11260Sstevel@tonic-gate 			}
11270Sstevel@tonic-gate 			(void) usb_rcm_online(ap_id, errstring, devpath, flags);
11280Sstevel@tonic-gate 		} else {
11290Sstevel@tonic-gate 			(void) usb_rcm_remove(ap_id, errstring, devpath, flags);
11300Sstevel@tonic-gate 		}
11310Sstevel@tonic-gate 		S_FREE(devpath);
11320Sstevel@tonic-gate 		break;
11330Sstevel@tonic-gate 	case CFGA_CMD_DISCONNECT:
11340Sstevel@tonic-gate 		if ((rv = device_connected(hdl, nvl, &ostate)) !=
11350Sstevel@tonic-gate 		    CFGA_USB_ALREADY_CONNECTED) {
11360Sstevel@tonic-gate 			/*
11370Sstevel@tonic-gate 			 * special case handling for
11380Sstevel@tonic-gate 			 * SLM based cfgadm disconnects
11390Sstevel@tonic-gate 			 */
11400Sstevel@tonic-gate 			if (ostate == AP_OSTATE_UNCONFIGURED)
11410Sstevel@tonic-gate 				goto bailout;
11420Sstevel@tonic-gate 		}
11430Sstevel@tonic-gate 		rv = CFGA_USB_OK;	/* Other statuses don't matter */
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate 		len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1146*7492SZhigang.Lu@Sun.COM 		    strlen("Disconnect") + strlen(ap_id);
11470Sstevel@tonic-gate 		if ((msg = (char *)calloc(len + 3, 1)) != NULL) {
11480Sstevel@tonic-gate 			(void) snprintf(msg, len + 3, "Disconnect %s%s\n%s",
11490Sstevel@tonic-gate 			    USB_CONFIRM_0, ap_id, USB_CONFIRM_1);
11500Sstevel@tonic-gate 		}
11510Sstevel@tonic-gate 		if (!usb_confirm(confp, msg)) {
11520Sstevel@tonic-gate 			free(msg);
11530Sstevel@tonic-gate 			cleanup_after_devctl_cmd(hdl, nvl);
11540Sstevel@tonic-gate 			return (CFGA_NACK);
11550Sstevel@tonic-gate 		}
11560Sstevel@tonic-gate 		free(msg);
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 		devpath = usb_get_devicepath(ap_id);
11590Sstevel@tonic-gate 		if (devpath == NULL) {
11600Sstevel@tonic-gate 			DPRINTF("cfga_change_state: get device path failed\n");
11610Sstevel@tonic-gate 			rv = CFGA_USB_DEVCTL;
11620Sstevel@tonic-gate 			break;
11630Sstevel@tonic-gate 		}
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 		/* only call rcm_offline iff the state was CONFIGURED */
11660Sstevel@tonic-gate 		if (ostate == AP_OSTATE_CONFIGURED) {
11670Sstevel@tonic-gate 			if ((rv = usb_rcm_offline(ap_id, errstring,
11680Sstevel@tonic-gate 			    devpath, flags)) != CFGA_USB_OK) {
11690Sstevel@tonic-gate 				break;
11700Sstevel@tonic-gate 			}
11710Sstevel@tonic-gate 		}
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 		ret = devctl_ap_disconnect(hdl, nvl);
11740Sstevel@tonic-gate 		if (ret != 0) {
11750Sstevel@tonic-gate 			DPRINTF("cfga_change_state: devctl_ap_disconnect "
11760Sstevel@tonic-gate 			    "failed with errno: %d\n", errno);
11770Sstevel@tonic-gate 			rv = CFGA_USB_DEVCTL;
11780Sstevel@tonic-gate 			if (errno == EBUSY) {
11790Sstevel@tonic-gate 				rv = CFGA_USB_BUSY;
11800Sstevel@tonic-gate 			}
11810Sstevel@tonic-gate 			if (ostate == AP_OSTATE_CONFIGURED) {
11820Sstevel@tonic-gate 				(void) usb_rcm_online(ap_id, errstring,
11830Sstevel@tonic-gate 				    devpath, flags);
11840Sstevel@tonic-gate 			}
11850Sstevel@tonic-gate 		} else {
11860Sstevel@tonic-gate 			if (ostate == AP_OSTATE_CONFIGURED) {
11870Sstevel@tonic-gate 				(void) usb_rcm_remove(ap_id, errstring,
11880Sstevel@tonic-gate 				    devpath, flags);
11890Sstevel@tonic-gate 			}
11900Sstevel@tonic-gate 		}
11910Sstevel@tonic-gate 		S_FREE(devpath);
11920Sstevel@tonic-gate 		break;
11930Sstevel@tonic-gate 	case CFGA_CMD_CONNECT:
11940Sstevel@tonic-gate 	case CFGA_CMD_LOAD:
11950Sstevel@tonic-gate 	case CFGA_CMD_UNLOAD:
11960Sstevel@tonic-gate 		(void) cfga_help(msgp, options, flags);
11970Sstevel@tonic-gate 		rv = CFGA_USB_OPNOTSUPP;
11980Sstevel@tonic-gate 		break;
11990Sstevel@tonic-gate 	case CFGA_CMD_NONE:
12000Sstevel@tonic-gate 	default:
12010Sstevel@tonic-gate 		(void) cfga_help(msgp, options, flags);
12020Sstevel@tonic-gate 		rv = CFGA_USB_INTERNAL_ERROR;
12030Sstevel@tonic-gate 	}
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate bailout:
12060Sstevel@tonic-gate 	cleanup_after_devctl_cmd(hdl, nvl);
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	return (usb_err_msg(errstring, rv, ap_id, errno));
12090Sstevel@tonic-gate }
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate /*ARGSUSED*/
12130Sstevel@tonic-gate cfga_err_t
cfga_private_func(const char * func,const char * ap_id,const char * options,struct cfga_confirm * confp,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)12140Sstevel@tonic-gate cfga_private_func(
12150Sstevel@tonic-gate 	const char *func,
12160Sstevel@tonic-gate 	const char *ap_id,
12170Sstevel@tonic-gate 	const char *options,
12180Sstevel@tonic-gate 	struct cfga_confirm *confp,
12190Sstevel@tonic-gate 	struct cfga_msg *msgp,
12200Sstevel@tonic-gate 	char **errstring,
12210Sstevel@tonic-gate 	cfga_flags_t flags)
12220Sstevel@tonic-gate {
12230Sstevel@tonic-gate 	int			len;
12240Sstevel@tonic-gate 	char			*msg;
12250Sstevel@tonic-gate 	nvlist_t		*list = NULL;
12260Sstevel@tonic-gate 	ap_ostate_t		ostate;
12270Sstevel@tonic-gate 	devctl_hdl_t 		hdl = NULL;
12280Sstevel@tonic-gate 	cfga_usb_ret_t		rv;
12290Sstevel@tonic-gate 	usb_dev_descr_t		*dev_descrp = NULL;
12300Sstevel@tonic-gate 	char			*driver = NULL;
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 	DPRINTF("cfga_private_func:\n");
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate 	if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_USB_OK) {
12350Sstevel@tonic-gate 		(void) cfga_help(msgp, options, flags);
12360Sstevel@tonic-gate 		return (usb_err_msg(errstring, rv, ap_id, errno));
12370Sstevel@tonic-gate 	}
12380Sstevel@tonic-gate 
12390Sstevel@tonic-gate 	/*
12400Sstevel@tonic-gate 	 * All subcommands which can change state of device require
12410Sstevel@tonic-gate 	 * root privileges.
12420Sstevel@tonic-gate 	 */
12430Sstevel@tonic-gate 	if (geteuid() != 0) {
12440Sstevel@tonic-gate 		rv = CFGA_USB_PRIV;
12450Sstevel@tonic-gate 		goto bailout;
12460Sstevel@tonic-gate 	}
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate 	if (func == NULL) {
12490Sstevel@tonic-gate 		rv = CFGA_USB_INTERNAL_ERROR;
12500Sstevel@tonic-gate 		goto bailout;
12510Sstevel@tonic-gate 	}
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 	if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &list, 0)) !=
12540Sstevel@tonic-gate 	    CFGA_USB_OK) {
12550Sstevel@tonic-gate 		goto bailout;
12560Sstevel@tonic-gate 	}
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 	if ((rv = device_connected(hdl, list, &ostate)) !=
12590Sstevel@tonic-gate 	    CFGA_USB_ALREADY_CONNECTED) {
12600Sstevel@tonic-gate 		goto bailout;
12610Sstevel@tonic-gate 	}
12620Sstevel@tonic-gate 	rv = CFGA_USB_OK;
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	if (strcmp(func, RESET_DEVICE) == 0) {	/* usb_reset? */
12650Sstevel@tonic-gate 		len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1266*7492SZhigang.Lu@Sun.COM 		    strlen("Reset") + strlen(ap_id);
12670Sstevel@tonic-gate 		if ((msg = (char *)calloc(len + 3, 1)) != NULL) {
12680Sstevel@tonic-gate 			(void) snprintf(msg, len + 3, "Reset %s%s\n%s",
12690Sstevel@tonic-gate 			    USB_CONFIRM_0, ap_id, USB_CONFIRM_1);
12700Sstevel@tonic-gate 		} else {
12710Sstevel@tonic-gate 			cleanup_after_devctl_cmd(hdl, list);
12720Sstevel@tonic-gate 			return (CFGA_NACK);
12730Sstevel@tonic-gate 		}
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 		if (!usb_confirm(confp, msg)) {
12760Sstevel@tonic-gate 			cleanup_after_devctl_cmd(hdl, list);
12770Sstevel@tonic-gate 			return (CFGA_NACK);
12780Sstevel@tonic-gate 		}
12790Sstevel@tonic-gate 
12800Sstevel@tonic-gate 		if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) {
12810Sstevel@tonic-gate 			goto bailout;
12820Sstevel@tonic-gate 		}
12830Sstevel@tonic-gate 	} else if (strncmp(func, USB_CONFIG, sizeof (USB_CONFIG)) == 0) {
12840Sstevel@tonic-gate 		uint_t	config = 0;
12850Sstevel@tonic-gate 		uint_t	actual_config;
12860Sstevel@tonic-gate 		size_t	size;
12870Sstevel@tonic-gate 		char	*subopts, *value;
12880Sstevel@tonic-gate 		uint_t	cfg_opt_flag = B_FALSE;
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate 		/* these are the only valid options */
12910Sstevel@tonic-gate 		char *cfg_opts[] = {
12920Sstevel@tonic-gate 			"config",	/* 0 */
12930Sstevel@tonic-gate 			"drv",		/* 1 */
12940Sstevel@tonic-gate 			NULL
12950Sstevel@tonic-gate 		};
12960Sstevel@tonic-gate 
12970Sstevel@tonic-gate 		/* return error if no options are specified */
12980Sstevel@tonic-gate 		subopts = (char *)options;
12990Sstevel@tonic-gate 		if (subopts == (char *)NULL) {
13000Sstevel@tonic-gate 			DPRINTF("cfga_private_func: no options\n");
13010Sstevel@tonic-gate 			rv = CFGA_USB_OPNOTSUPP;
13020Sstevel@tonic-gate 			(void) cfga_help(msgp, options, flags);
13030Sstevel@tonic-gate 			goto bailout;
13040Sstevel@tonic-gate 		}
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate 		/* parse options specified */
13070Sstevel@tonic-gate 		while (*subopts != '\0') {
13080Sstevel@tonic-gate 			switch (getsubopt(&subopts, cfg_opts, &value)) {
1309*7492SZhigang.Lu@Sun.COM 			case 0: /* config */
1310*7492SZhigang.Lu@Sun.COM 				if (value == NULL) {
1311*7492SZhigang.Lu@Sun.COM 					rv = CFGA_USB_OPNOTSUPP;
1312*7492SZhigang.Lu@Sun.COM 					(void) cfga_help(msgp,
1313*7492SZhigang.Lu@Sun.COM 					    options, flags);
1314*7492SZhigang.Lu@Sun.COM 					goto bailout;
1315*7492SZhigang.Lu@Sun.COM 				} else {
1316*7492SZhigang.Lu@Sun.COM 					errno = 0;
1317*7492SZhigang.Lu@Sun.COM 					config = strtol(value,
1318*7492SZhigang.Lu@Sun.COM 					    (char **)NULL, 10);
1319*7492SZhigang.Lu@Sun.COM 					if (errno) {
1320*7492SZhigang.Lu@Sun.COM 						DPRINTF(
1321*7492SZhigang.Lu@Sun.COM 						    "config conversion"
1322*7492SZhigang.Lu@Sun.COM 						    "failed\n");
1323*7492SZhigang.Lu@Sun.COM 						rv =
1324*7492SZhigang.Lu@Sun.COM 						    CFGA_USB_CONFIG_INVAL;
13250Sstevel@tonic-gate 						goto bailout;
13260Sstevel@tonic-gate 					}
1327*7492SZhigang.Lu@Sun.COM 				}
1328*7492SZhigang.Lu@Sun.COM 				cfg_opt_flag = B_TRUE;
1329*7492SZhigang.Lu@Sun.COM 				break;
13300Sstevel@tonic-gate 
1331*7492SZhigang.Lu@Sun.COM 			case 1: /* drv */
1332*7492SZhigang.Lu@Sun.COM 				if (value == NULL) {
1333*7492SZhigang.Lu@Sun.COM 					rv = CFGA_USB_OPNOTSUPP;
1334*7492SZhigang.Lu@Sun.COM 					(void) cfga_help(msgp,
1335*7492SZhigang.Lu@Sun.COM 					    options, flags);
1336*7492SZhigang.Lu@Sun.COM 					goto bailout;
1337*7492SZhigang.Lu@Sun.COM 				} else {
1338*7492SZhigang.Lu@Sun.COM 					S_FREE(driver);
1339*7492SZhigang.Lu@Sun.COM 					driver = strdup(value);
1340*7492SZhigang.Lu@Sun.COM 					if (driver == NULL) {
1341*7492SZhigang.Lu@Sun.COM 						rv =
1342*7492SZhigang.Lu@Sun.COM 						    CFGA_USB_INTERNAL_ERROR;
13430Sstevel@tonic-gate 						goto bailout;
13440Sstevel@tonic-gate 					}
1345*7492SZhigang.Lu@Sun.COM 				}
1346*7492SZhigang.Lu@Sun.COM 				break;
13470Sstevel@tonic-gate 
1348*7492SZhigang.Lu@Sun.COM 			default:
1349*7492SZhigang.Lu@Sun.COM 				rv = CFGA_USB_OPNOTSUPP;
1350*7492SZhigang.Lu@Sun.COM 				(void) cfga_help(msgp, options, flags);
1351*7492SZhigang.Lu@Sun.COM 				goto bailout;
13520Sstevel@tonic-gate 			}
13530Sstevel@tonic-gate 		}
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 		/* config is mandatory */
13560Sstevel@tonic-gate 		if (cfg_opt_flag != B_TRUE) {
13570Sstevel@tonic-gate 			rv = CFGA_USB_OPNOTSUPP;
13580Sstevel@tonic-gate 			(void) cfga_help(msgp, options, flags);
13590Sstevel@tonic-gate 			goto bailout;
13600Sstevel@tonic-gate 		}
13610Sstevel@tonic-gate 		DPRINTF("config = %x\n", config);
13620Sstevel@tonic-gate 
13630Sstevel@tonic-gate 		len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1364*7492SZhigang.Lu@Sun.COM 		    strlen("Setting") + strlen(ap_id) +
1365*7492SZhigang.Lu@Sun.COM 		    strlen("to USB configuration");
13660Sstevel@tonic-gate 		/* len + 8 to account for config, \n and white space */
13670Sstevel@tonic-gate 		if ((msg = (char *)calloc(len + 8, 1)) != NULL) {
13680Sstevel@tonic-gate 			(void) snprintf(msg, len + 8,
13690Sstevel@tonic-gate 			    "Setting %s%s\nto USB configuration %d\n%s",
13700Sstevel@tonic-gate 			    USB_CONFIRM_0, ap_id, config, USB_CONFIRM_1);
13710Sstevel@tonic-gate 		} else {
13720Sstevel@tonic-gate 			rv = CFGA_USB_INTERNAL_ERROR;
13730Sstevel@tonic-gate 			goto bailout;
13740Sstevel@tonic-gate 		}
13750Sstevel@tonic-gate 
13760Sstevel@tonic-gate 		if (!usb_confirm(confp, msg)) {
13770Sstevel@tonic-gate 			S_FREE(driver);
13780Sstevel@tonic-gate 			cleanup_after_devctl_cmd(hdl, list);
13790Sstevel@tonic-gate 			return (CFGA_NACK);
13800Sstevel@tonic-gate 		}
13810Sstevel@tonic-gate 
13820Sstevel@tonic-gate 		/*
13830Sstevel@tonic-gate 		 * Check that the option setting selected is in range.
13840Sstevel@tonic-gate 		 */
13850Sstevel@tonic-gate 		if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, NULL,
13860Sstevel@tonic-gate 		    (void **)&dev_descrp, &size)) != CFGA_USB_OK) {
13870Sstevel@tonic-gate 			DPRINTF("cfga_private_func: get dev descr failed\n");
13880Sstevel@tonic-gate 			goto bailout;
13890Sstevel@tonic-gate 		}
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 		if (config > dev_descrp->bNumConfigurations - 1) {
13920Sstevel@tonic-gate 			DPRINTF("cfga_private_func: config index requested "
13930Sstevel@tonic-gate 			    "(%d) exceeds bNumConfigurations - 1 (%d)\n",
13940Sstevel@tonic-gate 			    config, dev_descrp->bNumConfigurations - 1);
13950Sstevel@tonic-gate 			rv = CFGA_USB_CONFIG_INVAL;
13960Sstevel@tonic-gate 			goto bailout;
13970Sstevel@tonic-gate 		}
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 		/* Pass current setting to set_configuration */
14000Sstevel@tonic-gate 		if ((rv = get_config(ap_id, &actual_config)) != CFGA_USB_OK) {
14010Sstevel@tonic-gate 			goto bailout;
14020Sstevel@tonic-gate 		}
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate 		/* check if they match - yes, then nothing to do */
14050Sstevel@tonic-gate 		if (actual_config == config) {
14060Sstevel@tonic-gate 			DPRINTF("cfga_private_func: config index requested "
14070Sstevel@tonic-gate 			    "(%d)  matches the actual config value %d\n",
14080Sstevel@tonic-gate 			    config, actual_config);
14090Sstevel@tonic-gate 			rv = CFGA_USB_OK;
14100Sstevel@tonic-gate 			goto bailout;
14110Sstevel@tonic-gate 		}
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 		/* Save the configuration settings  */
14140Sstevel@tonic-gate 		if ((rv = set_configuration(ap_id, config, driver,
14150Sstevel@tonic-gate 		    dev_descrp, errstring)) != CFGA_USB_OK) {
14160Sstevel@tonic-gate 			goto bailout;
14170Sstevel@tonic-gate 		}
14180Sstevel@tonic-gate 
14190Sstevel@tonic-gate 		/* Reset device to force new config to take effect */
14200Sstevel@tonic-gate 		if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) {
14210Sstevel@tonic-gate 			goto bailout;
14220Sstevel@tonic-gate 		}
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 	} else {
14250Sstevel@tonic-gate 		DPRINTF("cfga_private_func: unrecognized command.\n");
14260Sstevel@tonic-gate 		(void) cfga_help(msgp, options, flags);
14270Sstevel@tonic-gate 		errno = EINVAL;
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 		return (CFGA_INVAL);
14300Sstevel@tonic-gate 	}
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate bailout:
14330Sstevel@tonic-gate 	S_FREE(dev_descrp);
14340Sstevel@tonic-gate 	S_FREE(driver);
14350Sstevel@tonic-gate 	cleanup_after_devctl_cmd(hdl, list);
14360Sstevel@tonic-gate 
14370Sstevel@tonic-gate 	return (usb_err_msg(errstring, rv, ap_id, errno));
14380Sstevel@tonic-gate }
14390Sstevel@tonic-gate 
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate /*ARGSUSED*/
14420Sstevel@tonic-gate cfga_err_t
cfga_test(const char * ap_id,const char * options,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)14430Sstevel@tonic-gate cfga_test(
14440Sstevel@tonic-gate 	const char *ap_id,
14450Sstevel@tonic-gate 	const char *options,
14460Sstevel@tonic-gate 	struct cfga_msg *msgp,
14470Sstevel@tonic-gate 	char **errstring,
14480Sstevel@tonic-gate 	cfga_flags_t flags)
14490Sstevel@tonic-gate {
14500Sstevel@tonic-gate 	(void) cfga_help(msgp, options, flags);
14510Sstevel@tonic-gate 	return (CFGA_OPNOTSUPP);
14520Sstevel@tonic-gate }
14530Sstevel@tonic-gate 
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate /*ARGSUSED*/
14560Sstevel@tonic-gate cfga_err_t
cfga_list_ext(const char * ap_id,cfga_list_data_t ** ap_id_list,int * nlistp,const char * options,const char * listopts,char ** errstring,cfga_flags_t flags)14570Sstevel@tonic-gate cfga_list_ext(
14580Sstevel@tonic-gate 	const char *ap_id,
14590Sstevel@tonic-gate 	cfga_list_data_t **ap_id_list,
14600Sstevel@tonic-gate 	int *nlistp,
14610Sstevel@tonic-gate 	const char *options,
14620Sstevel@tonic-gate 	const char *listopts,
14630Sstevel@tonic-gate 	char **errstring,
14640Sstevel@tonic-gate 	cfga_flags_t flags)
14650Sstevel@tonic-gate {
14660Sstevel@tonic-gate 	int			l_errno;
14670Sstevel@tonic-gate 	char			*ap_id_log = NULL;
14680Sstevel@tonic-gate 	size_t			size;
14690Sstevel@tonic-gate 	nvlist_t		*user_nvlist = NULL;
14700Sstevel@tonic-gate 	devctl_hdl_t		devctl_hdl = NULL;
14710Sstevel@tonic-gate 	cfga_usb_ret_t		rv = CFGA_USB_OK;
14720Sstevel@tonic-gate 	devctl_ap_state_t	devctl_ap_state;
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate 	DPRINTF("cfga_list_ext:\n");
14750Sstevel@tonic-gate 
14760Sstevel@tonic-gate 	if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) {
14770Sstevel@tonic-gate 		(void) cfga_help(NULL, options, flags);
14780Sstevel@tonic-gate 		goto bailout;
14790Sstevel@tonic-gate 	}
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate 	if (ap_id_list == NULL || nlistp == NULL) {
14820Sstevel@tonic-gate 		DPRINTF("cfga_list_ext: list = NULL or nlistp = NULL\n");
14830Sstevel@tonic-gate 		rv = CFGA_USB_INTERNAL_ERROR;
14840Sstevel@tonic-gate 		(void) cfga_help(NULL, options, flags);
14850Sstevel@tonic-gate 		goto bailout;
14860Sstevel@tonic-gate 	}
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate 	/* Get ap status */
14890Sstevel@tonic-gate 	if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, &user_nvlist,
14900Sstevel@tonic-gate 	    DC_RDONLY)) != CFGA_USB_OK) {
14910Sstevel@tonic-gate 		goto bailout;
14920Sstevel@tonic-gate 	}
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate 	if (devctl_ap_getstate(devctl_hdl, user_nvlist, &devctl_ap_state) ==
14950Sstevel@tonic-gate 	    -1) {
14960Sstevel@tonic-gate 		DPRINTF("cfga_list_ext: devctl_ap_getstate failed. errno: %d\n",
14970Sstevel@tonic-gate 		    errno);
14980Sstevel@tonic-gate 		cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
14990Sstevel@tonic-gate 		rv = CFGA_USB_DEVCTL;
15000Sstevel@tonic-gate 		goto bailout;
15010Sstevel@tonic-gate 	}
15020Sstevel@tonic-gate 	cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate 	/*
15050Sstevel@tonic-gate 	 * Create cfga_list_data_t struct.
15060Sstevel@tonic-gate 	 */
15070Sstevel@tonic-gate 	if ((*ap_id_list =
15080Sstevel@tonic-gate 	    (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) {
15090Sstevel@tonic-gate 		DPRINTF("cfga_list_ext: malloc for cfga_list_data_t failed. "
15100Sstevel@tonic-gate 		    "errno: %d\n", errno);
15110Sstevel@tonic-gate 		rv = CFGA_USB_ALLOC_FAIL;
15120Sstevel@tonic-gate 		goto bailout;
15130Sstevel@tonic-gate 	}
15140Sstevel@tonic-gate 	*nlistp = 1;
15150Sstevel@tonic-gate 
15160Sstevel@tonic-gate 
15170Sstevel@tonic-gate 	/*
15180Sstevel@tonic-gate 	 * Rest of the code fills in the cfga_list_data_t struct.
15190Sstevel@tonic-gate 	 */
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate 	/* Get /dev/cfg path to corresponding to the physical ap_id */
15220Sstevel@tonic-gate 	/* Remember ap_id_log must be freed */
15230Sstevel@tonic-gate 	rv = (cfga_usb_ret_t)physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id,
15240Sstevel@tonic-gate 	    &ap_id_log, &l_errno, MATCH_MINOR_NAME);
15250Sstevel@tonic-gate 	if (rv != 0) {
15260Sstevel@tonic-gate 		rv = CFGA_USB_DEVLINK;
15270Sstevel@tonic-gate 		goto bailout;
15280Sstevel@tonic-gate 	}
15290Sstevel@tonic-gate 	assert(ap_id_log != NULL);
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate 	/* Get logical ap-id corresponding to the physical */
15320Sstevel@tonic-gate 	if (strstr(ap_id_log, CFGA_DEV_DIR) == NULL) {
15330Sstevel@tonic-gate 		DPRINTF("cfga_list_ext: devlink doesn't contain /dev/cfg\n");
15340Sstevel@tonic-gate 		rv = CFGA_USB_DEVLINK;
15350Sstevel@tonic-gate 		goto bailout;
15360Sstevel@tonic-gate 	}
15370Sstevel@tonic-gate 	(void) strlcpy((*ap_id_list)->ap_log_id,
15380Sstevel@tonic-gate 	    /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1,
15390Sstevel@tonic-gate 	    sizeof ((*ap_id_list)->ap_log_id));
15400Sstevel@tonic-gate 	free(ap_id_log);
15410Sstevel@tonic-gate 	ap_id_log = NULL;
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 	(void) strlcpy((*ap_id_list)->ap_phys_id, ap_id,
15440Sstevel@tonic-gate 	    sizeof ((*ap_id_list)->ap_phys_id));
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 	switch (devctl_ap_state.ap_rstate) {
15470Sstevel@tonic-gate 		case AP_RSTATE_EMPTY:
15480Sstevel@tonic-gate 			(*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY;
15490Sstevel@tonic-gate 			break;
15500Sstevel@tonic-gate 		case AP_RSTATE_DISCONNECTED:
15510Sstevel@tonic-gate 			(*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED;
15520Sstevel@tonic-gate 			break;
15530Sstevel@tonic-gate 		case AP_RSTATE_CONNECTED:
15540Sstevel@tonic-gate 			(*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED;
15550Sstevel@tonic-gate 			break;
15560Sstevel@tonic-gate 		default:
15570Sstevel@tonic-gate 			rv = CFGA_USB_STATE;
15580Sstevel@tonic-gate 			goto bailout;
15590Sstevel@tonic-gate 	}
15600Sstevel@tonic-gate 
15610Sstevel@tonic-gate 	switch (devctl_ap_state.ap_ostate) {
15620Sstevel@tonic-gate 		case AP_OSTATE_CONFIGURED:
15630Sstevel@tonic-gate 			(*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED;
15640Sstevel@tonic-gate 			break;
15650Sstevel@tonic-gate 		case AP_OSTATE_UNCONFIGURED:
15660Sstevel@tonic-gate 			(*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED;
15670Sstevel@tonic-gate 			break;
15680Sstevel@tonic-gate 		default:
15690Sstevel@tonic-gate 			rv = CFGA_USB_STATE;
15700Sstevel@tonic-gate 			goto bailout;
15710Sstevel@tonic-gate 	}
15720Sstevel@tonic-gate 
15730Sstevel@tonic-gate 	switch (devctl_ap_state.ap_condition) {
15740Sstevel@tonic-gate 		case AP_COND_OK:
15750Sstevel@tonic-gate 			(*ap_id_list)->ap_cond = CFGA_COND_OK;
15760Sstevel@tonic-gate 			break;
15770Sstevel@tonic-gate 		case AP_COND_FAILING:
15780Sstevel@tonic-gate 			(*ap_id_list)->ap_cond = CFGA_COND_FAILING;
15790Sstevel@tonic-gate 			break;
15800Sstevel@tonic-gate 		case AP_COND_FAILED:
15810Sstevel@tonic-gate 			(*ap_id_list)->ap_cond = CFGA_COND_FAILED;
15820Sstevel@tonic-gate 			break;
15830Sstevel@tonic-gate 		case AP_COND_UNUSABLE:
15840Sstevel@tonic-gate 			(*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE;
15850Sstevel@tonic-gate 			break;
15860Sstevel@tonic-gate 		case AP_COND_UNKNOWN:
15870Sstevel@tonic-gate 			(*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN;
15880Sstevel@tonic-gate 			break;
15890Sstevel@tonic-gate 		default:
15900Sstevel@tonic-gate 			rv = CFGA_USB_STATE;
15910Sstevel@tonic-gate 			goto bailout;
15920Sstevel@tonic-gate 	}
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 	(*ap_id_list)->ap_class[0] = '\0';	/* Filled by libcfgadm */
15950Sstevel@tonic-gate 	(*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition;
15960Sstevel@tonic-gate 	(*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change;
15970Sstevel@tonic-gate 	(*ap_id_list)->ap_info[0] = NULL;
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) {
16000Sstevel@tonic-gate 		char *str_p;
16010Sstevel@tonic-gate 		size_t	str_len;
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 		/* Fill in the info for the -v option display.  */
16040Sstevel@tonic-gate 		if ((rv = fill_in_ap_info(ap_id, (*ap_id_list)->ap_info,
16050Sstevel@tonic-gate 		    sizeof ((*ap_id_list)->ap_info))) != CFGA_USB_OK) {
16060Sstevel@tonic-gate 			DPRINTF("cfga_list_ext: fill_in_ap_info failed\n");
16070Sstevel@tonic-gate 			goto bailout;
16080Sstevel@tonic-gate 		}
16090Sstevel@tonic-gate 
16100Sstevel@tonic-gate 		/* Fill in ap_type */
16110Sstevel@tonic-gate 		if ((rv = do_control_ioctl(ap_id, HUBD_GET_CFGADM_NAME, NULL,
16120Sstevel@tonic-gate 		    (void **)&str_p, &size)) != CFGA_USB_OK) {
16130Sstevel@tonic-gate 			DPRINTF("cfga_list_ext: do_control_ioctl failed\n");
16140Sstevel@tonic-gate 			goto bailout;
16150Sstevel@tonic-gate 		}
16160Sstevel@tonic-gate 
16170Sstevel@tonic-gate 		(void) strcpy((*ap_id_list)->ap_type, "usb-");
16180Sstevel@tonic-gate 		str_len = strlen((*ap_id_list)->ap_type);
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate 		/*
16210Sstevel@tonic-gate 		 * NOTE: In the cfgadm display the "Type" column is only 12
16220Sstevel@tonic-gate 		 * chars long. Most USB devices can be displayed here with a
16230Sstevel@tonic-gate 		 * "usb-" prefix. Only USB keyboard cannot be displayed in
16240Sstevel@tonic-gate 		 * its entirety as "usb-keybaord" is 13 chars in length.
16250Sstevel@tonic-gate 		 * It will show up as "usb-kbd".
16260Sstevel@tonic-gate 		 */
16270Sstevel@tonic-gate 		if (strncasecmp(str_p, "keyboard", 8) != 0) {
16280Sstevel@tonic-gate 			(void) strlcpy((*ap_id_list)->ap_type + str_len, str_p,
16290Sstevel@tonic-gate 			    sizeof ((*ap_id_list)->ap_type) - str_len);
16300Sstevel@tonic-gate 		} else {
16310Sstevel@tonic-gate 			(void) strlcpy((*ap_id_list)->ap_type + str_len, "kbd",
16320Sstevel@tonic-gate 			    sizeof ((*ap_id_list)->ap_type) - str_len);
16330Sstevel@tonic-gate 		}
16340Sstevel@tonic-gate 
16350Sstevel@tonic-gate 		free(str_p);
16360Sstevel@tonic-gate 	} else {
16370Sstevel@tonic-gate 		(void) strcpy((*ap_id_list)->ap_type,
16380Sstevel@tonic-gate 		    USB_CFGADM_DEFAULT_AP_TYPE);
16390Sstevel@tonic-gate 	}
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 	return (usb_err_msg(errstring, rv, ap_id, errno));
16420Sstevel@tonic-gate bailout:
16430Sstevel@tonic-gate 	if (*ap_id_list != NULL) {
16440Sstevel@tonic-gate 		free(*ap_id_list);
16450Sstevel@tonic-gate 	}
16460Sstevel@tonic-gate 	if (ap_id_log != NULL) {
16470Sstevel@tonic-gate 		free(ap_id_log);
16480Sstevel@tonic-gate 	}
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate 	return (usb_err_msg(errstring, rv, ap_id, errno));
16510Sstevel@tonic-gate }
16520Sstevel@tonic-gate 
16530Sstevel@tonic-gate 
16540Sstevel@tonic-gate /*
16550Sstevel@tonic-gate  * This routine accepts a variable number of message IDs and constructs
16560Sstevel@tonic-gate  * a corresponding error string which is printed via the message print routine
16570Sstevel@tonic-gate  * argument.
16580Sstevel@tonic-gate  */
16590Sstevel@tonic-gate static void
cfga_msg(struct cfga_msg * msgp,const char * str)16600Sstevel@tonic-gate cfga_msg(struct cfga_msg *msgp, const char *str)
16610Sstevel@tonic-gate {
16620Sstevel@tonic-gate 	int len;
16630Sstevel@tonic-gate 	char *q;
16640Sstevel@tonic-gate 
16650Sstevel@tonic-gate 	if (msgp == NULL || msgp->message_routine == NULL) {
16660Sstevel@tonic-gate 		DPRINTF("cfga_msg: msg\n");
16670Sstevel@tonic-gate 		return;
16680Sstevel@tonic-gate 	}
16690Sstevel@tonic-gate 
16700Sstevel@tonic-gate 	if ((len = strlen(str)) == 0) {
16710Sstevel@tonic-gate 		DPRINTF("cfga_msg: null str\n");
16720Sstevel@tonic-gate 		return;
16730Sstevel@tonic-gate 	}
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate 	if ((q = (char *)calloc(len + 1, 1)) == NULL) {
16760Sstevel@tonic-gate 		DPRINTF("cfga_msg: null q\n");
16770Sstevel@tonic-gate 		return;
16780Sstevel@tonic-gate 	}
16790Sstevel@tonic-gate 
1680*7492SZhigang.Lu@Sun.COM 	(void) strcpy(q, str);
16810Sstevel@tonic-gate 	(*msgp->message_routine)(msgp->appdata_ptr, q);
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 	free(q);
16840Sstevel@tonic-gate }
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate /* ARGSUSED */
16880Sstevel@tonic-gate cfga_err_t
cfga_help(struct cfga_msg * msgp,const char * options,cfga_flags_t flags)16890Sstevel@tonic-gate cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
16900Sstevel@tonic-gate {
16910Sstevel@tonic-gate 	DPRINTF("cfga_help:\n");
16920Sstevel@tonic-gate 	if (options) {
16930Sstevel@tonic-gate 		cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_UNKNOWN]));
16940Sstevel@tonic-gate 		cfga_msg(msgp, options);
16950Sstevel@tonic-gate 	}
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 	cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_HEADER]));
16980Sstevel@tonic-gate 	cfga_msg(msgp, usb_help[HELP_CONFIG]);
16990Sstevel@tonic-gate 	cfga_msg(msgp, usb_help[HELP_RESET_SLOT]);
17000Sstevel@tonic-gate 	cfga_msg(msgp, usb_help[HELP_CONFIG_SLOT]);
17010Sstevel@tonic-gate 
17020Sstevel@tonic-gate 	return (CFGA_OK);
17030Sstevel@tonic-gate }
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate static int
usb_confirm(struct cfga_confirm * confp,char * msg)17070Sstevel@tonic-gate usb_confirm(struct cfga_confirm *confp, char *msg)
17080Sstevel@tonic-gate {
17090Sstevel@tonic-gate 	int rval;
17100Sstevel@tonic-gate 
17110Sstevel@tonic-gate 	if (confp == NULL || confp->confirm == NULL) {
17120Sstevel@tonic-gate 		return (0);
17130Sstevel@tonic-gate 	}
17140Sstevel@tonic-gate 
17150Sstevel@tonic-gate 	rval = (*confp->confirm)(confp->appdata_ptr, msg);
17160Sstevel@tonic-gate 	DPRINTF("usb_confirm: %d\n", rval);
17170Sstevel@tonic-gate 
17180Sstevel@tonic-gate 	return (rval);
17190Sstevel@tonic-gate }
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 
17220Sstevel@tonic-gate static char *
usb_get_devicepath(const char * ap_id)17230Sstevel@tonic-gate usb_get_devicepath(const char *ap_id)
17240Sstevel@tonic-gate {
17250Sstevel@tonic-gate 	char		*devpath = NULL;
17260Sstevel@tonic-gate 	size_t		size;
17270Sstevel@tonic-gate 	cfga_usb_ret_t	rv;
17280Sstevel@tonic-gate 
17290Sstevel@tonic-gate 	rv = do_control_ioctl(ap_id, HUBD_GET_DEVICE_PATH, NULL,
17300Sstevel@tonic-gate 	    (void **)&devpath, &size);
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 	if (rv == CFGA_USB_OK) {
17330Sstevel@tonic-gate 		DPRINTF("usb_get_devicepath: get device path ioctl ok\n");
17340Sstevel@tonic-gate 		return (devpath);
17350Sstevel@tonic-gate 	} else {
17360Sstevel@tonic-gate 		DPRINTF("usb_get_devicepath: get device path ioctl failed\n");
17370Sstevel@tonic-gate 		return ((char *)NULL);
17380Sstevel@tonic-gate 	}
17390Sstevel@tonic-gate }
1740