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