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
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 *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
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)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
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)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
cfga_test(const char * ap_id,const char * options,struct cfga_msg * msgp,char ** errstring,cfga_flags_t flags)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
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)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
cfga_msg(struct cfga_msg * msgp,const char * str)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
cfga_help(struct cfga_msg * msgp,const char * options,cfga_flags_t flags)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
usb_confirm(struct cfga_confirm * confp,char * msg)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 *
usb_get_devicepath(const char * ap_id)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