1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
25 */
26
27
28 #include "cfga_usb.h"
29
30
31 /* function prototypes */
32 cfga_err_t usb_err_msg(char **, cfga_usb_ret_t, const char *, int);
33 extern cfga_usb_ret_t usb_rcm_offline(const char *, char **, char *,
34 cfga_flags_t);
35 extern cfga_usb_ret_t usb_rcm_online(const char *, char **, char *,
36 cfga_flags_t);
37 extern cfga_usb_ret_t usb_rcm_remove(const char *, char **, char *,
38 cfga_flags_t);
39 static int usb_confirm(struct cfga_confirm *, char *);
40 static char *usb_get_devicepath(const char *);
41
42 /*
43 * This file contains the entry points to the plugin as defined in the
44 * config_admin(3X) man page.
45 */
46
47 /*
48 * Set the version number for the cfgadm library's use.
49 */
50 int cfga_version = CFGA_HSL_V2;
51
52 #define HELP_HEADER 1
53 #define HELP_CONFIG 2
54 #define HELP_RESET_SLOT 3
55 #define HELP_CONFIG_SLOT 4
56 #define HELP_UNKNOWN 5
57
58 /* Help messages */
59 static char *
60 usb_help[] = {
61 NULL,
62 "USB specific commands:\n",
63 " cfgadm -c [configure|unconfigure|disconnect] ap_id [ap_id...]\n",
64 " cfgadm -x usb_reset ap_id [ap_id...]\n",
65 " cfgadm -x usb_config -o config=<index of desired configuration> ap_id\n",
66 "\tunknown command or option: ",
67 NULL
68 }; /* End help messages */
69
70 /* Error messages */
71 static msgcvt_t
72 usb_error_msgs[] = {
73 /* CFGA_USB_OK */
74 { CVT, CFGA_OK, "ok" },
75
76 /* CFGA_USB_UNKNOWN */
77 { CVT, CFGA_LIB_ERROR, "Unknown message; internal error" },
78
79 /* CFGA_USB_INTERNAL_ERROR */
80 { CVT, CFGA_LIB_ERROR, "Internal error" },
81
82 /* CFGA_USB_OPTIONS */
83 { CVT, CFGA_ERROR, "Hardware specific options not supported" },
84
85 /* CFGA_USB_DYNAMIC_AP */
86 { CVT, CFGA_INVAL, "Dynamic attachment points not supported" },
87
88 /* CFGA_USB_AP */
89 { CVT, CFGA_APID_NOEXIST, "" },
90
91 /* CFGA_USB_PORT */
92 { CVT, CFGA_LIB_ERROR, "Cannot determine hub port number for " },
93
94 /* CFGA_USB_DEVCTL */
95 { CVT, CFGA_ERROR, "Cannot issue devctl to " },
96
97 /* CFGA_USB_NOT_CONNECTED */
98 { CVT, CFGA_INVAL, "No device connected to " },
99
100 /* CFGA_USB_NOT_CONFIGURED */
101 { CVT, CFGA_INVAL, "No device configured to " },
102
103 /* CFGA_USB_ALREADY_CONNECTED */
104 { CVT, CFGA_INSUFFICENT_CONDITION,
105 "Device already connected; cannot connect again " },
106
107 /* CFGA_USB_ALREADY_CONFIGURED */
108 { CVT, CFGA_INVAL, "device already configured for " },
109
110 /* CFGA_USB_OPEN */
111 { CVT, CFGA_LIB_ERROR, "Cannot open " },
112
113 /* CFGA_USB_IOCTL */
114 { CVT, CFGA_ERROR, "Driver ioctl failed " },
115
116 /* CFGA_USB_BUSY */
117 { CVT, CFGA_SYSTEM_BUSY, "" },
118
119 /* CFGA_USB_ALLOC_FAIL */
120 { CVT, CFGA_LIB_ERROR, "Memory allocation failure" },
121
122 /* CFGA_USB_OPNOTSUPP */
123 { CVT, CFGA_OPNOTSUPP, "Operation not supported" },
124
125 /* CFGA_USB_DEVLINK */
126 { CVT, CFGA_LIB_ERROR, "Could not find /dev/cfg link for " },
127
128 /* CFGA_USB_STATE */
129 { CVT, CFGA_LIB_ERROR, "Internal error: Unrecognized ap state" },
130
131 /* CFGA_USB_CONFIG_INVAL */
132 { CVT, CFGA_ERROR,
133 "Specified configuration index unrecognized or exceeds "
134 "maximum available" },
135
136 /* CFGA_USB_PRIV */
137 { CVT, CFGA_PRIV, "" },
138
139 /* CFGA_USB_NVLIST */
140 { CVT, CFGA_ERROR, "Internal error (nvlist)" },
141
142 /* CFGA_USB_ZEROLEN */
143 { CVT, CFGA_ERROR, "Internal error (zerolength string)" },
144
145 /* CFGA_USB_CONFIG_FILE */
146 { CVT, CFGA_ERROR,
147 "Cannot open/fstat/read USB system configuration file" },
148
149 /* CFGA_USB_LOCK_FILE */
150 { CVT, CFGA_ERROR, "Cannot lock USB system configuration file" },
151
152 /* CFGA_USB_UNLOCK_FILE */
153 { CVT, CFGA_ERROR, "Cannot unlock USB system configuration file" },
154
155 /* CFGA_USB_ONE_CONFIG */
156 { CVT, CFGA_ERROR,
157 "Operation not supported for devices with one configuration" },
158
159 /* CFGA_USB_RCM_HANDLE Errors */
160 { CVT, CFGA_ERROR, "cannot get RCM handle"},
161
162 /* CFGA_USB_RCM_ONLINE */
163 { CVT, CFGA_SYSTEM_BUSY, "failed to online: "},
164
165 /* CFGA_USB_RCM_OFFLINE */
166 { CVT, CFGA_SYSTEM_BUSY, "failed to offline: "},
167
168 /* CFGA_USB_RCM_INFO */
169 { CVT, CFGA_ERROR, "failed to query: "}
170
171 }; /* End error messages */
172
173
174 /* ========================================================================= */
175 /*
176 * The next two funcs imported verbatim from cfgadm_scsi.
177 * physpath_to_devlink is the only func directly used by cfgadm_usb.
178 * get_link supports it.
179 */
180
181 /*
182 * Routine to search the /dev directory or a subtree of /dev.
183 */
184 static int
get_link(di_devlink_t devlink,void * arg)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
physpath_to_devlink(const char * basedir,const char * node_path,char ** logpp,int * l_errnop,int match_minor)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 *
get_msg(uint_t msg_index,msgcvt_t * msg_tbl,uint_t tbl_size)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
set_msg(char ** ret_str,...)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
usb_err_msg(char ** errstring,cfga_usb_ret_t rv,const char * ap_id,int l_errno)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
verify_valid_apid(const char * ap_id)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
verify_params(const char * ap_id,const char * options,char ** errstring)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
get_port_num(const char * ap_id,uint_t * port)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
cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl,nvlist_t * user_nvlist)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
setup_for_devctl_cmd(const char * ap_id,devctl_hdl_t * devctl_hdl,nvlist_t ** user_nvlistp,uint_t oflag)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
device_configured(devctl_hdl_t hdl,nvlist_t * nvl,ap_rstate_t * rstate)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
device_connected(devctl_hdl_t hdl,nvlist_t * list,ap_ostate_t * ostate)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
do_control_ioctl(const char * ap_id,uint_t subcommand,uint_t arg,void ** descrp,size_t * sizep)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
set_configuration(const char * ap_id,uint_t config,char * driver,usb_dev_descr_t * descrp,char ** errstring)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
get_config(const char * ap_id,uint_t * config)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
reset_device(devctl_hdl_t devctl_hdl,nvlist_t * nvl)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
fill_in_ap_info(const char * ap_id,char * info_buf,size_t info_size)888 fill_in_ap_info(const char *ap_id, char *info_buf, size_t info_size)
889 {
890 char *mfg_str = NULL; /* iManufacturer */
891 char *prod_str = NULL; /* iProduct */
892 char *cfg_descr = NULL; /* iConfiguration */
893 uint_t config; /* curr cfg index */
894 size_t size; /* tmp stuff */
895 boolean_t flag; /* wether to print ":" or not */
896 boolean_t free_mfg_str = B_FALSE;
897 boolean_t free_prod_str = B_FALSE;
898 boolean_t free_cfg_str = B_FALSE;
899 cfga_usb_ret_t rv = CFGA_USB_OK;
900 usb_dev_descr_t *dev_descrp = NULL; /* device descriptor */
901
902 DPRINTF("fill_in_ap_info:\n");
903
904 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, 0,
905 (void **)&dev_descrp, &size)) != CFGA_USB_OK) {
906 DPRINTF("fill_in_ap_info: get dev descr failed\n");
907 return (rv);
908 }
909
910 /* iManufacturer */
911 mfg_str = USB_UNDEF_STR;
912 if (dev_descrp->iManufacturer != 0) {
913 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING,
914 HUBD_MFG_STR, (void **)&mfg_str, &size)) != CFGA_USB_OK) {
915 if (rv == CFGA_USB_ZEROLEN) {
916 rv = CFGA_USB_OK;
917 } else {
918 DPRINTF("get iManufacturer failed\n");
919 goto bailout;
920 }
921 }
922 free_mfg_str = B_TRUE;
923 }
924
925 /* iProduct */
926 prod_str = USB_UNDEF_STR;
927 if (dev_descrp->iProduct != 0) {
928 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING,
929 HUBD_PRODUCT_STR, (void **)&prod_str,
930 &size)) != CFGA_USB_OK) {
931 if (rv == CFGA_USB_ZEROLEN) {
932 rv = CFGA_USB_OK;
933 } else {
934 DPRINTF("getting iProduct failed\n");
935 goto bailout;
936 }
937 }
938 free_prod_str = B_TRUE;
939 }
940
941 /* Current conifguration */
942 if ((rv = get_config(ap_id, &config)) != CFGA_USB_OK) {
943 DPRINTF("get_config failed\n");
944 goto bailout;
945 }
946
947 /* Configuration string descriptor */
948 cfg_descr = USB_NO_CFG_STR;
949 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_STRING,
950 HUBD_CFG_DESCR_STR, (void **)&cfg_descr, &size)) != CFGA_USB_OK) {
951 if (rv == CFGA_USB_ZEROLEN) {
952 rv = CFGA_USB_OK;
953 flag = B_TRUE;
954 } else {
955 DPRINTF("HUBD_CFG_DESCR_STR failed\n");
956 goto bailout;
957 }
958 }
959
960 /* add ": " to output coz PSARC case says so */
961 if ((cfg_descr != (char *)NULL) && rv != CFGA_USB_ZEROLEN) {
962 flag = B_TRUE;
963 free_cfg_str = B_TRUE;
964 } else {
965 flag = B_FALSE;
966 cfg_descr = USB_NO_CFG_STR;
967 }
968
969 /* Dump local buf into passed-in buf. */
970 (void) snprintf(info_buf, info_size,
971 "Mfg: %s Product: %s NConfigs: %d Config: %d %s%s", mfg_str,
972 prod_str, dev_descrp->bNumConfigurations, config,
973 (flag == B_TRUE) ? ": " : "", cfg_descr);
974
975 bailout:
976 if (dev_descrp) {
977 free(dev_descrp);
978 }
979
980 if ((free_mfg_str == B_TRUE) && mfg_str) {
981 free(mfg_str);
982 }
983
984 if ((free_prod_str == B_TRUE) && prod_str) {
985 free(prod_str);
986 }
987
988 if ((free_cfg_str == B_TRUE) && cfg_descr) {
989 free(cfg_descr);
990 }
991
992 return (rv);
993 }
994
995
996 /* ========================================================================== */
997 /* Entry points */
998
999
1000 /*ARGSUSED*/
1001 cfga_err_t
cfga_change_state(cfga_cmd_t state_change_cmd,const char * ap_id,const char * options,struct cfga_confirm * confp,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)1002 cfga_change_state(
1003 cfga_cmd_t state_change_cmd,
1004 const char *ap_id,
1005 const char *options,
1006 struct cfga_confirm *confp,
1007 struct cfga_msg *msgp,
1008 char **errstring,
1009 cfga_flags_t flags)
1010 {
1011 int ret;
1012 int len;
1013 char *msg;
1014 char *devpath;
1015 nvlist_t *nvl = NULL;
1016 ap_rstate_t rstate;
1017 ap_ostate_t ostate;
1018 devctl_hdl_t hdl = NULL;
1019 cfga_usb_ret_t rv = CFGA_USB_OK;
1020
1021 DPRINTF("cfga_change_state:\n");
1022
1023 if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) {
1024 (void) cfga_help(msgp, options, flags);
1025 goto bailout;
1026 }
1027
1028 /*
1029 * All subcommands which can change state of device require
1030 * root privileges.
1031 */
1032 if (geteuid() != 0) {
1033 rv = CFGA_USB_PRIV;
1034 goto bailout;
1035 }
1036
1037 if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &nvl, 0)) !=
1038 CFGA_USB_OK) {
1039 goto bailout;
1040 }
1041
1042 switch (state_change_cmd) {
1043 case CFGA_CMD_CONFIGURE:
1044 if ((rv = device_configured(hdl, nvl, &rstate)) !=
1045 CFGA_USB_NOT_CONFIGURED) {
1046 goto bailout;
1047 }
1048
1049 if (rstate == AP_RSTATE_EMPTY) {
1050 goto bailout;
1051 }
1052 rv = CFGA_USB_OK; /* Other statuses don't matter */
1053
1054 if (devctl_ap_configure(hdl, nvl) != 0) {
1055 DPRINTF("cfga_change_state: devctl_ap_configure "
1056 "failed. errno: %d\n", errno);
1057 rv = CFGA_USB_DEVCTL;
1058 }
1059
1060 devpath = usb_get_devicepath(ap_id);
1061 if (devpath == NULL) {
1062 int i;
1063 /*
1064 * try for some time as USB hotplug thread
1065 * takes a while to create the path
1066 * and then eventually give up
1067 */
1068 for (i = 0; i < 12 && (devpath == NULL); i++) {
1069 (void) sleep(6);
1070 devpath = usb_get_devicepath(ap_id);
1071 }
1072
1073 if (devpath == NULL) {
1074 DPRINTF("cfga_change_state: get device "
1075 "path failed i = %d\n", i);
1076 rv = CFGA_USB_DEVCTL;
1077 break;
1078 }
1079 }
1080 S_FREE(devpath);
1081 break;
1082 case CFGA_CMD_UNCONFIGURE:
1083 if ((rv = device_connected(hdl, nvl, &ostate)) !=
1084 CFGA_USB_ALREADY_CONNECTED) {
1085 goto bailout;
1086 }
1087
1088 /* check if it is already unconfigured */
1089 if ((rv = device_configured(hdl, nvl, &rstate)) ==
1090 CFGA_USB_NOT_CONFIGURED) {
1091 goto bailout;
1092 }
1093 rv = CFGA_USB_OK; /* Other statuses don't matter */
1094
1095 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1096 strlen("Unconfigure") + strlen(ap_id);
1097 if ((msg = (char *)calloc(len + 3, 1)) != NULL) {
1098 (void) snprintf(msg, len + 3, "Unconfigure %s%s\n%s",
1099 USB_CONFIRM_0, ap_id, USB_CONFIRM_1);
1100 }
1101 if (!usb_confirm(confp, msg)) {
1102 free(msg);
1103 cleanup_after_devctl_cmd(hdl, nvl);
1104 return (CFGA_NACK);
1105 }
1106 free(msg);
1107
1108 devpath = usb_get_devicepath(ap_id);
1109 if (devpath == NULL) {
1110 DPRINTF("cfga_change_state: get device path failed\n");
1111 rv = CFGA_USB_DEVCTL;
1112 break;
1113 }
1114
1115 if ((rv = usb_rcm_offline(ap_id, errstring, devpath, flags)) !=
1116 CFGA_USB_OK) {
1117 break;
1118 }
1119
1120 ret = devctl_ap_unconfigure(hdl, nvl);
1121 if (ret != 0) {
1122 DPRINTF("cfga_change_state: devctl_ap_unconfigure "
1123 "failed with errno: %d\n", errno);
1124 rv = CFGA_USB_DEVCTL;
1125 if (errno == EBUSY) {
1126 rv = CFGA_USB_BUSY;
1127 }
1128 (void) usb_rcm_online(ap_id, errstring, devpath, flags);
1129 } else {
1130 (void) usb_rcm_remove(ap_id, errstring, devpath, flags);
1131 }
1132 S_FREE(devpath);
1133 break;
1134 case CFGA_CMD_DISCONNECT:
1135 if ((rv = device_connected(hdl, nvl, &ostate)) !=
1136 CFGA_USB_ALREADY_CONNECTED) {
1137 /*
1138 * special case handling for
1139 * SLM based cfgadm disconnects
1140 */
1141 if (ostate == AP_OSTATE_UNCONFIGURED)
1142 goto bailout;
1143 }
1144 rv = CFGA_USB_OK; /* Other statuses don't matter */
1145
1146 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1147 strlen("Disconnect") + strlen(ap_id);
1148 if ((msg = (char *)calloc(len + 3, 1)) != NULL) {
1149 (void) snprintf(msg, len + 3, "Disconnect %s%s\n%s",
1150 USB_CONFIRM_0, ap_id, USB_CONFIRM_1);
1151 }
1152 if (!usb_confirm(confp, msg)) {
1153 free(msg);
1154 cleanup_after_devctl_cmd(hdl, nvl);
1155 return (CFGA_NACK);
1156 }
1157 free(msg);
1158
1159 devpath = usb_get_devicepath(ap_id);
1160 if (devpath == NULL) {
1161 DPRINTF("cfga_change_state: get device path failed\n");
1162 rv = CFGA_USB_DEVCTL;
1163 break;
1164 }
1165
1166 /* only call rcm_offline iff the state was CONFIGURED */
1167 if (ostate == AP_OSTATE_CONFIGURED) {
1168 if ((rv = usb_rcm_offline(ap_id, errstring,
1169 devpath, flags)) != CFGA_USB_OK) {
1170 break;
1171 }
1172 }
1173
1174 ret = devctl_ap_disconnect(hdl, nvl);
1175 if (ret != 0) {
1176 DPRINTF("cfga_change_state: devctl_ap_disconnect "
1177 "failed with errno: %d\n", errno);
1178 rv = CFGA_USB_DEVCTL;
1179 if (errno == EBUSY) {
1180 rv = CFGA_USB_BUSY;
1181 }
1182 if (ostate == AP_OSTATE_CONFIGURED) {
1183 (void) usb_rcm_online(ap_id, errstring,
1184 devpath, flags);
1185 }
1186 } else {
1187 if (ostate == AP_OSTATE_CONFIGURED) {
1188 (void) usb_rcm_remove(ap_id, errstring,
1189 devpath, flags);
1190 }
1191 }
1192 S_FREE(devpath);
1193 break;
1194 case CFGA_CMD_CONNECT:
1195 case CFGA_CMD_LOAD:
1196 case CFGA_CMD_UNLOAD:
1197 (void) cfga_help(msgp, options, flags);
1198 rv = CFGA_USB_OPNOTSUPP;
1199 break;
1200 case CFGA_CMD_NONE:
1201 default:
1202 (void) cfga_help(msgp, options, flags);
1203 rv = CFGA_USB_INTERNAL_ERROR;
1204 }
1205
1206 bailout:
1207 cleanup_after_devctl_cmd(hdl, nvl);
1208
1209 return (usb_err_msg(errstring, rv, ap_id, errno));
1210 }
1211
1212
1213 /*ARGSUSED*/
1214 cfga_err_t
cfga_private_func(const char * func,const char * ap_id,const char * options,struct cfga_confirm * confp,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)1215 cfga_private_func(
1216 const char *func,
1217 const char *ap_id,
1218 const char *options,
1219 struct cfga_confirm *confp,
1220 struct cfga_msg *msgp,
1221 char **errstring,
1222 cfga_flags_t flags)
1223 {
1224 int len;
1225 char *msg;
1226 nvlist_t *list = NULL;
1227 ap_ostate_t ostate;
1228 devctl_hdl_t hdl = NULL;
1229 cfga_usb_ret_t rv;
1230 usb_dev_descr_t *dev_descrp = NULL;
1231 char *driver = NULL;
1232
1233 DPRINTF("cfga_private_func:\n");
1234
1235 if ((rv = verify_params(ap_id, NULL, errstring)) != CFGA_USB_OK) {
1236 (void) cfga_help(msgp, options, flags);
1237 return (usb_err_msg(errstring, rv, ap_id, errno));
1238 }
1239
1240 /*
1241 * All subcommands which can change state of device require
1242 * root privileges.
1243 */
1244 if (geteuid() != 0) {
1245 rv = CFGA_USB_PRIV;
1246 goto bailout;
1247 }
1248
1249 if (func == NULL) {
1250 rv = CFGA_USB_INTERNAL_ERROR;
1251 goto bailout;
1252 }
1253
1254 if ((rv = setup_for_devctl_cmd(ap_id, &hdl, &list, 0)) !=
1255 CFGA_USB_OK) {
1256 goto bailout;
1257 }
1258
1259 if ((rv = device_connected(hdl, list, &ostate)) !=
1260 CFGA_USB_ALREADY_CONNECTED) {
1261 goto bailout;
1262 }
1263 rv = CFGA_USB_OK;
1264
1265 if (strcmp(func, RESET_DEVICE) == 0) { /* usb_reset? */
1266 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1267 strlen("Reset") + strlen(ap_id);
1268 if ((msg = (char *)calloc(len + 3, 1)) != NULL) {
1269 (void) snprintf(msg, len + 3, "Reset %s%s\n%s",
1270 USB_CONFIRM_0, ap_id, USB_CONFIRM_1);
1271 } else {
1272 cleanup_after_devctl_cmd(hdl, list);
1273 return (CFGA_NACK);
1274 }
1275
1276 if (!usb_confirm(confp, msg)) {
1277 cleanup_after_devctl_cmd(hdl, list);
1278 return (CFGA_NACK);
1279 }
1280
1281 if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) {
1282 goto bailout;
1283 }
1284 } else if (strncmp(func, USB_CONFIG, sizeof (USB_CONFIG)) == 0) {
1285 uint_t config = 0;
1286 uint_t actual_config;
1287 size_t size;
1288 char *subopts, *value;
1289 uint_t cfg_opt_flag = B_FALSE;
1290
1291 /* these are the only valid options */
1292 char *cfg_opts[] = {
1293 "config", /* 0 */
1294 "drv", /* 1 */
1295 NULL
1296 };
1297
1298 /* return error if no options are specified */
1299 subopts = (char *)options;
1300 if (subopts == (char *)NULL) {
1301 DPRINTF("cfga_private_func: no options\n");
1302 rv = CFGA_USB_OPNOTSUPP;
1303 (void) cfga_help(msgp, options, flags);
1304 goto bailout;
1305 }
1306
1307 /* parse options specified */
1308 while (*subopts != '\0') {
1309 switch (getsubopt(&subopts, cfg_opts, &value)) {
1310 case 0: /* config */
1311 if (value == NULL) {
1312 rv = CFGA_USB_OPNOTSUPP;
1313 (void) cfga_help(msgp,
1314 options, flags);
1315 goto bailout;
1316 } else {
1317 errno = 0;
1318 config = strtol(value,
1319 (char **)NULL, 10);
1320 if (errno) {
1321 DPRINTF(
1322 "config conversion"
1323 "failed\n");
1324 rv =
1325 CFGA_USB_CONFIG_INVAL;
1326 goto bailout;
1327 }
1328 }
1329 cfg_opt_flag = B_TRUE;
1330 break;
1331
1332 case 1: /* drv */
1333 if (value == NULL) {
1334 rv = CFGA_USB_OPNOTSUPP;
1335 (void) cfga_help(msgp,
1336 options, flags);
1337 goto bailout;
1338 } else {
1339 S_FREE(driver);
1340 driver = strdup(value);
1341 if (driver == NULL) {
1342 rv =
1343 CFGA_USB_INTERNAL_ERROR;
1344 goto bailout;
1345 }
1346 }
1347 break;
1348
1349 default:
1350 rv = CFGA_USB_OPNOTSUPP;
1351 (void) cfga_help(msgp, options, flags);
1352 goto bailout;
1353 }
1354 }
1355
1356 /* config is mandatory */
1357 if (cfg_opt_flag != B_TRUE) {
1358 rv = CFGA_USB_OPNOTSUPP;
1359 (void) cfga_help(msgp, options, flags);
1360 goto bailout;
1361 }
1362 DPRINTF("config = %x\n", config);
1363
1364 len = strlen(USB_CONFIRM_0) + strlen(USB_CONFIRM_1) +
1365 strlen("Setting") + strlen(ap_id) +
1366 strlen("to USB configuration");
1367 /* len + 8 to account for config, \n and white space */
1368 if ((msg = (char *)calloc(len + 8, 1)) != NULL) {
1369 (void) snprintf(msg, len + 8,
1370 "Setting %s%s\nto USB configuration %d\n%s",
1371 USB_CONFIRM_0, ap_id, config, USB_CONFIRM_1);
1372 } else {
1373 rv = CFGA_USB_INTERNAL_ERROR;
1374 goto bailout;
1375 }
1376
1377 if (!usb_confirm(confp, msg)) {
1378 S_FREE(driver);
1379 cleanup_after_devctl_cmd(hdl, list);
1380 return (CFGA_NACK);
1381 }
1382
1383 /*
1384 * Check that the option setting selected is in range.
1385 */
1386 if ((rv = do_control_ioctl(ap_id, USB_DESCR_TYPE_DEV, 0,
1387 (void **)&dev_descrp, &size)) != CFGA_USB_OK) {
1388 DPRINTF("cfga_private_func: get dev descr failed\n");
1389 goto bailout;
1390 }
1391
1392 if (config > dev_descrp->bNumConfigurations - 1) {
1393 DPRINTF("cfga_private_func: config index requested "
1394 "(%d) exceeds bNumConfigurations - 1 (%d)\n",
1395 config, dev_descrp->bNumConfigurations - 1);
1396 rv = CFGA_USB_CONFIG_INVAL;
1397 goto bailout;
1398 }
1399
1400 /* Pass current setting to set_configuration */
1401 if ((rv = get_config(ap_id, &actual_config)) != CFGA_USB_OK) {
1402 goto bailout;
1403 }
1404
1405 /* check if they match - yes, then nothing to do */
1406 if (actual_config == config) {
1407 DPRINTF("cfga_private_func: config index requested "
1408 "(%d) matches the actual config value %d\n",
1409 config, actual_config);
1410 rv = CFGA_USB_OK;
1411 goto bailout;
1412 }
1413
1414 /* Save the configuration settings */
1415 if ((rv = set_configuration(ap_id, config, driver,
1416 dev_descrp, errstring)) != CFGA_USB_OK) {
1417 goto bailout;
1418 }
1419
1420 /* Reset device to force new config to take effect */
1421 if ((rv = reset_device(hdl, list)) != CFGA_USB_OK) {
1422 goto bailout;
1423 }
1424
1425 } else {
1426 DPRINTF("cfga_private_func: unrecognized command.\n");
1427 (void) cfga_help(msgp, options, flags);
1428 errno = EINVAL;
1429
1430 return (CFGA_INVAL);
1431 }
1432
1433 bailout:
1434 S_FREE(dev_descrp);
1435 S_FREE(driver);
1436 cleanup_after_devctl_cmd(hdl, list);
1437
1438 return (usb_err_msg(errstring, rv, ap_id, errno));
1439 }
1440
1441
1442 /*ARGSUSED*/
1443 cfga_err_t
cfga_test(const char * ap_id,const char * options,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)1444 cfga_test(
1445 const char *ap_id,
1446 const char *options,
1447 struct cfga_msg *msgp,
1448 char **errstring,
1449 cfga_flags_t flags)
1450 {
1451 (void) cfga_help(msgp, options, flags);
1452 return (CFGA_OPNOTSUPP);
1453 }
1454
1455
1456 /*ARGSUSED*/
1457 cfga_err_t
cfga_list_ext(const char * ap_id,cfga_list_data_t ** ap_id_list,int * nlistp,const char * options,const char * listopts,char ** errstring,cfga_flags_t flags)1458 cfga_list_ext(
1459 const char *ap_id,
1460 cfga_list_data_t **ap_id_list,
1461 int *nlistp,
1462 const char *options,
1463 const char *listopts,
1464 char **errstring,
1465 cfga_flags_t flags)
1466 {
1467 int l_errno;
1468 char *ap_id_log = NULL;
1469 size_t size;
1470 nvlist_t *user_nvlist = NULL;
1471 devctl_hdl_t devctl_hdl = NULL;
1472 cfga_usb_ret_t rv = CFGA_USB_OK;
1473 devctl_ap_state_t devctl_ap_state;
1474
1475 DPRINTF("cfga_list_ext:\n");
1476
1477 if ((rv = verify_params(ap_id, options, errstring)) != CFGA_USB_OK) {
1478 goto bailout;
1479 }
1480
1481 if (ap_id_list == NULL || nlistp == NULL) {
1482 DPRINTF("cfga_list_ext: list = NULL or nlistp = NULL\n");
1483 rv = CFGA_USB_INTERNAL_ERROR;
1484 goto bailout;
1485 }
1486
1487 /* Get ap status */
1488 if ((rv = setup_for_devctl_cmd(ap_id, &devctl_hdl, &user_nvlist,
1489 DC_RDONLY)) != CFGA_USB_OK) {
1490 goto bailout;
1491 }
1492
1493 if (devctl_ap_getstate(devctl_hdl, user_nvlist, &devctl_ap_state) ==
1494 -1) {
1495 DPRINTF("cfga_list_ext: devctl_ap_getstate failed. errno: %d\n",
1496 errno);
1497 cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
1498 rv = CFGA_USB_DEVCTL;
1499 goto bailout;
1500 }
1501 cleanup_after_devctl_cmd(devctl_hdl, user_nvlist);
1502
1503 /*
1504 * Create cfga_list_data_t struct.
1505 */
1506 if ((*ap_id_list =
1507 (cfga_list_data_t *)malloc(sizeof (**ap_id_list))) == NULL) {
1508 DPRINTF("cfga_list_ext: malloc for cfga_list_data_t failed. "
1509 "errno: %d\n", errno);
1510 rv = CFGA_USB_ALLOC_FAIL;
1511 goto bailout;
1512 }
1513 *nlistp = 1;
1514
1515
1516 /*
1517 * Rest of the code fills in the cfga_list_data_t struct.
1518 */
1519
1520 /* Get /dev/cfg path to corresponding to the physical ap_id */
1521 /* Remember ap_id_log must be freed */
1522 rv = (cfga_usb_ret_t)physpath_to_devlink(CFGA_DEV_DIR, (char *)ap_id,
1523 &ap_id_log, &l_errno, MATCH_MINOR_NAME);
1524 if (rv != 0) {
1525 rv = CFGA_USB_DEVLINK;
1526 goto bailout;
1527 }
1528 assert(ap_id_log != NULL);
1529
1530 /* Get logical ap-id corresponding to the physical */
1531 if (strstr(ap_id_log, CFGA_DEV_DIR) == NULL) {
1532 DPRINTF("cfga_list_ext: devlink doesn't contain /dev/cfg\n");
1533 rv = CFGA_USB_DEVLINK;
1534 goto bailout;
1535 }
1536 (void) strlcpy((*ap_id_list)->ap_log_id,
1537 /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR)+ 1,
1538 sizeof ((*ap_id_list)->ap_log_id));
1539 free(ap_id_log);
1540 ap_id_log = NULL;
1541
1542 (void) strlcpy((*ap_id_list)->ap_phys_id, ap_id,
1543 sizeof ((*ap_id_list)->ap_phys_id));
1544
1545 switch (devctl_ap_state.ap_rstate) {
1546 case AP_RSTATE_EMPTY:
1547 (*ap_id_list)->ap_r_state = CFGA_STAT_EMPTY;
1548 break;
1549 case AP_RSTATE_DISCONNECTED:
1550 (*ap_id_list)->ap_r_state = CFGA_STAT_DISCONNECTED;
1551 break;
1552 case AP_RSTATE_CONNECTED:
1553 (*ap_id_list)->ap_r_state = CFGA_STAT_CONNECTED;
1554 break;
1555 default:
1556 rv = CFGA_USB_STATE;
1557 goto bailout;
1558 }
1559
1560 switch (devctl_ap_state.ap_ostate) {
1561 case AP_OSTATE_CONFIGURED:
1562 (*ap_id_list)->ap_o_state = CFGA_STAT_CONFIGURED;
1563 break;
1564 case AP_OSTATE_UNCONFIGURED:
1565 (*ap_id_list)->ap_o_state = CFGA_STAT_UNCONFIGURED;
1566 break;
1567 default:
1568 rv = CFGA_USB_STATE;
1569 goto bailout;
1570 }
1571
1572 switch (devctl_ap_state.ap_condition) {
1573 case AP_COND_OK:
1574 (*ap_id_list)->ap_cond = CFGA_COND_OK;
1575 break;
1576 case AP_COND_FAILING:
1577 (*ap_id_list)->ap_cond = CFGA_COND_FAILING;
1578 break;
1579 case AP_COND_FAILED:
1580 (*ap_id_list)->ap_cond = CFGA_COND_FAILED;
1581 break;
1582 case AP_COND_UNUSABLE:
1583 (*ap_id_list)->ap_cond = CFGA_COND_UNUSABLE;
1584 break;
1585 case AP_COND_UNKNOWN:
1586 (*ap_id_list)->ap_cond = CFGA_COND_UNKNOWN;
1587 break;
1588 default:
1589 rv = CFGA_USB_STATE;
1590 goto bailout;
1591 }
1592
1593 (*ap_id_list)->ap_class[0] = '\0'; /* Filled by libcfgadm */
1594 (*ap_id_list)->ap_busy = devctl_ap_state.ap_in_transition;
1595 (*ap_id_list)->ap_status_time = devctl_ap_state.ap_last_change;
1596 (*ap_id_list)->ap_info[0] = '\0';
1597
1598 if ((*ap_id_list)->ap_r_state == CFGA_STAT_CONNECTED) {
1599 char *str_p;
1600 size_t str_len;
1601
1602 /* Fill in the info for the -v option display. */
1603 if ((rv = fill_in_ap_info(ap_id, (*ap_id_list)->ap_info,
1604 sizeof ((*ap_id_list)->ap_info))) != CFGA_USB_OK) {
1605 DPRINTF("cfga_list_ext: fill_in_ap_info failed\n");
1606 goto bailout;
1607 }
1608
1609 /* Fill in ap_type */
1610 if ((rv = do_control_ioctl(ap_id, HUBD_GET_CFGADM_NAME, 0,
1611 (void **)&str_p, &size)) != CFGA_USB_OK) {
1612 DPRINTF("cfga_list_ext: do_control_ioctl failed\n");
1613 goto bailout;
1614 }
1615
1616 (void) strcpy((*ap_id_list)->ap_type, "usb-");
1617 str_len = strlen((*ap_id_list)->ap_type);
1618
1619 /*
1620 * NOTE: In the cfgadm display the "Type" column is only 12
1621 * chars long. Most USB devices can be displayed here with a
1622 * "usb-" prefix. Only USB keyboard cannot be displayed in
1623 * its entirety as "usb-keybaord" is 13 chars in length.
1624 * It will show up as "usb-kbd".
1625 */
1626 if (strncasecmp(str_p, "keyboard", 8) != 0) {
1627 (void) strlcpy((*ap_id_list)->ap_type + str_len, str_p,
1628 sizeof ((*ap_id_list)->ap_type) - str_len);
1629 } else {
1630 (void) strlcpy((*ap_id_list)->ap_type + str_len, "kbd",
1631 sizeof ((*ap_id_list)->ap_type) - str_len);
1632 }
1633
1634 free(str_p);
1635 } else {
1636 (void) strcpy((*ap_id_list)->ap_type,
1637 USB_CFGADM_DEFAULT_AP_TYPE);
1638 }
1639
1640 return (usb_err_msg(errstring, rv, ap_id, errno));
1641 bailout:
1642 if (*ap_id_list != NULL) {
1643 free(*ap_id_list);
1644 }
1645 if (ap_id_log != NULL) {
1646 free(ap_id_log);
1647 }
1648
1649 return (usb_err_msg(errstring, rv, ap_id, errno));
1650 }
1651
1652
1653 /*
1654 * This routine accepts a variable number of message IDs and constructs
1655 * a corresponding error string which is printed via the message print routine
1656 * argument.
1657 */
1658 static void
cfga_msg(struct cfga_msg * msgp,const char * str)1659 cfga_msg(struct cfga_msg *msgp, const char *str)
1660 {
1661 int len;
1662 char *q;
1663
1664 if (msgp == NULL || msgp->message_routine == NULL) {
1665 DPRINTF("cfga_msg: msg\n");
1666 return;
1667 }
1668
1669 if ((len = strlen(str)) == 0) {
1670 DPRINTF("cfga_msg: null str\n");
1671 return;
1672 }
1673
1674 if ((q = (char *)calloc(len + 1, 1)) == NULL) {
1675 DPRINTF("cfga_msg: null q\n");
1676 return;
1677 }
1678
1679 (void) strcpy(q, str);
1680 (*msgp->message_routine)(msgp->appdata_ptr, q);
1681
1682 free(q);
1683 }
1684
1685
1686 /* ARGSUSED */
1687 cfga_err_t
cfga_help(struct cfga_msg * msgp,const char * options,cfga_flags_t flags)1688 cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
1689 {
1690 DPRINTF("cfga_help:\n");
1691 if (options) {
1692 cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_UNKNOWN]));
1693 cfga_msg(msgp, options);
1694 }
1695
1696 cfga_msg(msgp, dgettext(TEXT_DOMAIN, usb_help[HELP_HEADER]));
1697 cfga_msg(msgp, usb_help[HELP_CONFIG]);
1698 cfga_msg(msgp, usb_help[HELP_RESET_SLOT]);
1699 cfga_msg(msgp, usb_help[HELP_CONFIG_SLOT]);
1700
1701 return (CFGA_OK);
1702 }
1703
1704
1705 static int
usb_confirm(struct cfga_confirm * confp,char * msg)1706 usb_confirm(struct cfga_confirm *confp, char *msg)
1707 {
1708 int rval;
1709
1710 if (confp == NULL || confp->confirm == NULL) {
1711 return (0);
1712 }
1713
1714 rval = (*confp->confirm)(confp->appdata_ptr, msg);
1715 DPRINTF("usb_confirm: %d\n", rval);
1716
1717 return (rval);
1718 }
1719
1720
1721 static char *
usb_get_devicepath(const char * ap_id)1722 usb_get_devicepath(const char *ap_id)
1723 {
1724 char *devpath = NULL;
1725 size_t size;
1726 cfga_usb_ret_t rv;
1727
1728 rv = do_control_ioctl(ap_id, HUBD_GET_DEVICE_PATH, 0,
1729 (void **)&devpath, &size);
1730
1731 if (rv == CFGA_USB_OK) {
1732 DPRINTF("usb_get_devicepath: get device path ioctl ok\n");
1733 return (devpath);
1734 } else {
1735 DPRINTF("usb_get_devicepath: get device path ioctl failed\n");
1736 return (NULL);
1737 }
1738 }
1739