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