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