xref: /freebsd/usr.sbin/usbconfig/usbconfig.c (revision b17b639832e707aab0e9514cf94727498e2d67bd)
1 /* $FreeBSD$ */
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause
4  *
5  * Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <err.h>
33 #include <sysexits.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <pwd.h>
37 #include <grp.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <sys/types.h>
41 
42 #include <libusb20_desc.h>
43 #include <libusb20.h>
44 
45 #include "dump.h"
46 
47 struct options {
48 	const char *quirkname;
49 	void   *buffer;
50 	int template;
51 	gid_t	gid;
52 	uid_t	uid;
53 	mode_t	mode;
54 	uint32_t got_any;
55 	struct LIBUSB20_CONTROL_SETUP_DECODED setup;
56 	uint16_t bus;
57 	uint16_t addr;
58 	uint16_t iface;
59 	uint16_t vid;
60 	uint16_t pid;
61 	uint16_t lo_rev;		/* inclusive */
62 	uint16_t hi_rev;		/* inclusive */
63 	uint8_t	string_index;
64 	uint8_t	config_index;
65 	uint8_t	alt_index;
66 	uint8_t	got_list:1;
67 	uint8_t	got_bus:1;
68 	uint8_t	got_addr:1;
69 	uint8_t	got_set_config:1;
70 	uint8_t	got_set_alt:1;
71 	uint8_t	got_set_template:1;
72 	uint8_t	got_get_template:1;
73 	uint8_t	got_suspend:1;
74 	uint8_t	got_resume:1;
75 	uint8_t	got_reset:1;
76 	uint8_t	got_power_off:1;
77 	uint8_t	got_power_save:1;
78 	uint8_t	got_power_on:1;
79 	uint8_t	got_dump_device_quirks:1;
80 	uint8_t	got_dump_quirk_names:1;
81 	uint8_t	got_dump_all_desc:1;
82 	uint8_t	got_dump_device_desc:1;
83 	uint8_t	got_dump_curr_config:1;
84 	uint8_t	got_dump_all_config:1;
85 	uint8_t	got_dump_info:1;
86   	uint8_t	got_dump_stats:1;
87 	uint8_t	got_show_iface_driver:1;
88 	uint8_t	got_remove_device_quirk:1;
89 	uint8_t	got_add_device_quirk:1;
90 	uint8_t	got_remove_quirk:1;
91 	uint8_t	got_add_quirk:1;
92 	uint8_t	got_dump_string:1;
93 	uint8_t	got_do_request:1;
94 	uint8_t	got_detach_kernel_driver:1;
95 };
96 
97 struct token {
98 	const char *name;
99 	uint8_t	value;
100 	uint8_t	narg;
101 };
102 
103 enum {
104 	T_SET_CONFIG,
105 	T_SET_ALT,
106 	T_SET_TEMPLATE,
107 	T_GET_TEMPLATE,
108 	T_ADD_DEVICE_QUIRK,
109 	T_REMOVE_DEVICE_QUIRK,
110 	T_ADD_QUIRK,
111 	T_REMOVE_QUIRK,
112 	T_SHOW_IFACE_DRIVER,
113 	T_DETACH_KERNEL_DRIVER,
114 	T_DUMP_QUIRK_NAMES,
115 	T_DUMP_DEVICE_QUIRKS,
116 	T_DUMP_ALL_DESC,
117 	T_DUMP_DEVICE_DESC,
118 	T_DUMP_CURR_CONFIG_DESC,
119 	T_DUMP_ALL_CONFIG_DESC,
120 	T_DUMP_STRING,
121 	T_DUMP_INFO,
122 	T_DUMP_STATS,
123 	T_SUSPEND,
124 	T_RESUME,
125 	T_POWER_OFF,
126 	T_POWER_SAVE,
127 	T_POWER_ON,
128 	T_RESET,
129 	T_LIST,
130 	T_DO_REQUEST,
131 };
132 
133 static struct options options;
134 
135 static const struct token token[] = {
136 	{"set_config", T_SET_CONFIG, 1},
137 	{"set_alt", T_SET_ALT, 1},
138 	{"set_template", T_SET_TEMPLATE, 1},
139 	{"get_template", T_GET_TEMPLATE, 0},
140 	{"add_dev_quirk_vplh", T_ADD_DEVICE_QUIRK, 5},
141 	{"remove_dev_quirk_vplh", T_REMOVE_DEVICE_QUIRK, 5},
142 	{"add_quirk", T_ADD_QUIRK, 1},
143 	{"remove_quirk", T_REMOVE_QUIRK, 1},
144 	{"detach_kernel_driver", T_DETACH_KERNEL_DRIVER, 0},
145 	{"dump_quirk_names", T_DUMP_QUIRK_NAMES, 0},
146 	{"dump_device_quirks", T_DUMP_DEVICE_QUIRKS, 0},
147 	{"dump_all_desc", T_DUMP_ALL_DESC, 0},
148 	{"dump_device_desc", T_DUMP_DEVICE_DESC, 0},
149 	{"dump_curr_config_desc", T_DUMP_CURR_CONFIG_DESC, 0},
150 	{"dump_all_config_desc", T_DUMP_ALL_CONFIG_DESC, 0},
151 	{"dump_string", T_DUMP_STRING, 1},
152 	{"dump_info", T_DUMP_INFO, 0},
153 	{"dump_stats", T_DUMP_STATS, 0},
154 	{"show_ifdrv", T_SHOW_IFACE_DRIVER, 0},
155 	{"suspend", T_SUSPEND, 0},
156 	{"resume", T_RESUME, 0},
157 	{"power_off", T_POWER_OFF, 0},
158 	{"power_save", T_POWER_SAVE, 0},
159 	{"power_on", T_POWER_ON, 0},
160 	{"reset", T_RESET, 0},
161 	{"list", T_LIST, 0},
162 	{"do_request", T_DO_REQUEST, 5},
163 };
164 
165 static void
166 be_dev_remove_quirk(struct libusb20_backend *pbe,
167     uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
168     const char *str)
169 {
170 	struct libusb20_quirk q;
171 	int error;
172 
173 	memset(&q, 0, sizeof(q));
174 
175 	q.vid = vid;
176 	q.pid = pid;
177 	q.bcdDeviceLow = lorev;
178 	q.bcdDeviceHigh = hirev;
179 	strlcpy(q.quirkname, str, sizeof(q.quirkname));
180 
181 	error = libusb20_be_remove_dev_quirk(pbe, &q);
182 	if (error) {
183 		fprintf(stderr, "Removing quirk '%s' failed, continuing.\n", str);
184 	}
185 }
186 
187 static void
188 be_dev_add_quirk(struct libusb20_backend *pbe,
189     uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
190     const char *str)
191 {
192 	struct libusb20_quirk q;
193 	int error;
194 
195 	memset(&q, 0, sizeof(q));
196 
197 	q.vid = vid;
198 	q.pid = pid;
199 	q.bcdDeviceLow = lorev;
200 	q.bcdDeviceHigh = hirev;
201 	strlcpy(q.quirkname, str, sizeof(q.quirkname));
202 
203 	error = libusb20_be_add_dev_quirk(pbe, &q);
204 	if (error) {
205 		fprintf(stderr, "Adding quirk '%s' failed, continuing.\n", str);
206 	}
207 }
208 
209 static uint8_t
210 get_token(const char *str, uint8_t narg)
211 {
212 	uint8_t n;
213 
214 	for (n = 0; n != (sizeof(token) / sizeof(token[0])); n++) {
215 		if (strcasecmp(str, token[n].name) == 0) {
216 			if (token[n].narg > narg) {
217 				/* too few arguments */
218 				break;
219 			}
220 			return (token[n].value);
221 		}
222 	}
223 	return (0 - 1);
224 }
225 
226 static uid_t
227 num_id(const char *name, const char *type)
228 {
229 	uid_t val;
230 	char *ep;
231 
232 	errno = 0;
233 	val = strtoul(name, &ep, 0);
234 	if (errno) {
235 		err(1, "%s", name);
236 	}
237 	if (*ep != '\0') {
238 		errx(1, "%s: illegal %s name", name, type);
239 	}
240 	return (val);
241 }
242 
243 static int
244 get_int(const char *s)
245 {
246 	int val;
247 	char *ep;
248 
249 	errno = 0;
250 	val = strtoul(s, &ep, 0);
251 	if (errno) {
252 		err(1, "%s", s);
253 	}
254 	if (*ep != '\0') {
255 		errx(1, "illegal number: %s", s);
256 	}
257 	return val;
258 }
259 
260 static void
261 duplicate_option(const char *ptr)
262 {
263 	fprintf(stderr, "Syntax error: "
264 	    "Duplicate option: '%s'\n", ptr);
265 	exit(1);
266 }
267 
268 static void
269 usage(int exitcode)
270 {
271 	fprintf(stderr, ""
272 	    "usbconfig - configure the USB subsystem" "\n"
273 	    "usage: usbconfig [-u <busnum>] [-a <devaddr>] [-i <ifaceindex>] [-v] [cmds...]" "\n"
274 	    "usage: usbconfig -d [ugen]<busnum>.<devaddr> [-i <ifaceindex>] [-v] [cmds...]" "\n"
275 	    "commands:" "\n"
276 	    "  set_config <cfg_index>" "\n"
277 	    "  set_alt <alt_index>" "\n"
278 	    "  set_template <template>" "\n"
279 	    "  get_template" "\n"
280 	    "  add_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
281 	    "  remove_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
282 	    "  add_quirk <quirk>" "\n"
283 	    "  remove_quirk <quirk>" "\n"
284 	    "  detach_kernel_driver" "\n"
285 	    "  dump_quirk_names" "\n"
286 	    "  dump_device_quirks" "\n"
287 	    "  dump_all_desc" "\n"
288 	    "  dump_device_desc" "\n"
289 	    "  dump_curr_config_desc" "\n"
290 	    "  dump_all_config_desc" "\n"
291 	    "  dump_string <index>" "\n"
292 	    "  dump_info" "\n"
293 	    "  dump_stats" "\n"
294 	    "  show_ifdrv" "\n"
295 	    "  suspend" "\n"
296 	    "  resume" "\n"
297 	    "  power_off" "\n"
298 	    "  power_save" "\n"
299 	    "  power_on" "\n"
300 	    "  reset" "\n"
301 	    "  list" "\n"
302 	    "  do_request <bmReqTyp> <bReq> <wVal> <wIdx> <wLen> <data...>" "\n"
303 	);
304 	exit(exitcode);
305 }
306 
307 static void
308 reset_options(struct options *opt)
309 {
310 	if (opt->buffer)
311 		free(opt->buffer);
312 	memset(opt, 0, sizeof(*opt));
313 }
314 
315 static void
316 flush_command(struct libusb20_backend *pbe, struct options *opt)
317 {
318 	struct libusb20_device *pdev = NULL;
319 	uint32_t matches = 0;
320 	uint8_t dump_any;
321 
322 	/* check for invalid option combinations */
323 	if ((opt->got_suspend +
324 	    opt->got_resume +
325 	    opt->got_reset +
326 	    opt->got_set_config +
327 	    opt->got_set_alt +
328 	    opt->got_power_save +
329 	    opt->got_power_on +
330 	    opt->got_power_off) > 1) {
331 		err(1, "can only specify one of 'set_config', "
332 		    "'set_alt', 'reset', 'suspend', 'resume', "
333 		    "'power_save', 'power_on' and 'power_off' "
334 		    "at the same time!");
335 	}
336 	if (opt->got_dump_quirk_names) {
337 		opt->got_any--;
338 		dump_be_quirk_names(pbe);
339 	}
340 	if (opt->got_dump_device_quirks) {
341 		opt->got_any--;
342 		dump_be_dev_quirks(pbe);
343 	}
344 	if (opt->got_remove_device_quirk) {
345 		opt->got_any--;
346 		be_dev_remove_quirk(pbe,
347 		    opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
348 	}
349 	if (opt->got_add_device_quirk) {
350 		opt->got_any--;
351 		be_dev_add_quirk(pbe,
352 		    opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
353 	}
354 	if (opt->got_set_template) {
355 		opt->got_any--;
356 		if (libusb20_be_set_template(pbe, opt->template)) {
357 			fprintf(stderr, "Setting USB template %u failed, "
358 			    "continuing.\n", opt->template);
359 		}
360 	}
361 	if (opt->got_get_template) {
362 		opt->got_any--;
363 		if (libusb20_be_get_template(pbe, &opt->template))
364 			printf("USB template: <unknown>\n");
365 		else
366 			printf("USB template: %u\n", opt->template);
367 	}
368 	if (opt->got_any == 0) {
369 		/*
370 		 * do not scan through all the devices if there are no valid
371 		 * options
372 		 */
373 		goto done;
374 	}
375 	while ((pdev = libusb20_be_device_foreach(pbe, pdev))) {
376 
377 		if (opt->got_bus &&
378 		    (libusb20_dev_get_bus_number(pdev) != opt->bus)) {
379 			continue;
380 		}
381 		if (opt->got_addr &&
382 		    (libusb20_dev_get_address(pdev) != opt->addr)) {
383 			continue;
384 		}
385 		matches++;
386 
387 		if (opt->got_remove_quirk) {
388 			struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
389 
390 			ddesc = libusb20_dev_get_device_desc(pdev);
391 
392 			be_dev_remove_quirk(pbe,
393 			    ddesc->idVendor, ddesc->idProduct,
394 			    ddesc->bcdDevice, ddesc->bcdDevice,
395 			    opt->quirkname);
396 		}
397 
398 		if (opt->got_add_quirk) {
399 			struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
400 
401 			ddesc = libusb20_dev_get_device_desc(pdev);
402 
403 			be_dev_add_quirk(pbe,
404 			    ddesc->idVendor, ddesc->idProduct,
405 			    ddesc->bcdDevice, ddesc->bcdDevice,
406 			    opt->quirkname);
407 		}
408 
409 		if (libusb20_dev_open(pdev, 0)) {
410 			err(1, "could not open device");
411 		}
412 		if (opt->got_dump_string) {
413 			dump_string_by_index(pdev, opt->string_index);
414 		}
415 		if (opt->got_do_request) {
416 			uint16_t actlen;
417 			uint16_t t;
418 
419 			if (libusb20_dev_request_sync(pdev, &opt->setup,
420 			    opt->buffer, &actlen, 5000 /* 5 seconds */ , 0)) {
421 				printf("REQUEST = <ERROR>\n");
422 			} else if (!(opt->setup.bmRequestType &
423 			    LIBUSB20_ENDPOINT_IN)) {
424 				printf("REQUEST = <OK>\n");
425 			} else {
426 				t = actlen;
427 				printf("REQUEST = <");
428 				for (t = 0; t != actlen; t++) {
429 					printf("0x%02x%s",
430 					    ((uint8_t *)opt->buffer)[t],
431 					    (t == (actlen - 1)) ? "" : " ");
432 				}
433 				printf("><");
434 				for (t = 0; t != actlen; t++) {
435 					char c;
436 
437 					c = ((uint8_t *)opt->buffer)[t];
438 					if ((c != '<') &&
439 					    (c != '>') && isprint(c)) {
440 						putchar(c);
441 					}
442 				}
443 				printf(">\n");
444 			}
445 		}
446 		if (opt->got_set_config) {
447 			if (libusb20_dev_set_config_index(pdev,
448 			    opt->config_index)) {
449 				err(1, "could not set config index");
450 			}
451 		}
452 		if (opt->got_set_alt) {
453 			if (libusb20_dev_set_alt_index(pdev, opt->iface,
454 			    opt->alt_index)) {
455 				err(1, "could not set alternate setting");
456 			}
457 		}
458 		if (opt->got_reset) {
459 			if (libusb20_dev_reset(pdev)) {
460 				err(1, "could not reset device");
461 			}
462 		}
463 		if (opt->got_suspend) {
464 			if (libusb20_dev_set_power_mode(pdev,
465 			    LIBUSB20_POWER_SUSPEND)) {
466 				err(1, "could not set suspend");
467 			}
468 		}
469 		if (opt->got_resume) {
470 			if (libusb20_dev_set_power_mode(pdev,
471 			    LIBUSB20_POWER_RESUME)) {
472 				err(1, "could not set resume");
473 			}
474 		}
475 		if (opt->got_power_off) {
476 			if (libusb20_dev_set_power_mode(pdev,
477 			    LIBUSB20_POWER_OFF)) {
478 				err(1, "could not set power OFF");
479 			}
480 		}
481 		if (opt->got_power_save) {
482 			if (libusb20_dev_set_power_mode(pdev,
483 			    LIBUSB20_POWER_SAVE)) {
484 				err(1, "could not set power SAVE");
485 			}
486 		}
487 		if (opt->got_power_on) {
488 			if (libusb20_dev_set_power_mode(pdev,
489 			    LIBUSB20_POWER_ON)) {
490 				err(1, "could not set power ON");
491 			}
492 		}
493 		if (opt->got_detach_kernel_driver) {
494 			if (libusb20_dev_detach_kernel_driver(pdev, opt->iface)) {
495 				err(1, "could not detach kernel driver");
496 			}
497 		}
498 		dump_any =
499 		    (opt->got_dump_all_desc ||
500 		    opt->got_dump_device_desc ||
501 		    opt->got_dump_curr_config ||
502 		    opt->got_dump_all_config ||
503 		    opt->got_dump_info ||
504 		    opt->got_dump_stats);
505 
506 		if (opt->got_list || dump_any) {
507 			dump_device_info(pdev,
508 			    opt->got_show_iface_driver);
509 		}
510 		if (opt->got_dump_device_desc) {
511 			printf("\n");
512 			dump_device_desc(pdev);
513 		}
514 		if (opt->got_dump_all_config) {
515 			printf("\n");
516 			dump_config(pdev, 1);
517 		} else if (opt->got_dump_curr_config) {
518 			printf("\n");
519 			dump_config(pdev, 0);
520 		} else if (opt->got_dump_all_desc) {
521 			printf("\n");
522 			dump_device_desc(pdev);
523 			dump_config(pdev, 1);
524 		}
525 		if (opt->got_dump_stats) {
526 			printf("\n");
527 			dump_device_stats(pdev);
528 		}
529 		if (dump_any) {
530 			printf("\n");
531 		}
532 		if (libusb20_dev_close(pdev)) {
533 			err(1, "could not close device");
534 		}
535 	}
536 
537 	if (matches == 0) {
538 		printf("No device match or lack of permissions.\n");
539 	}
540 done:
541 	reset_options(opt);
542 }
543 
544 int
545 main(int argc, char **argv)
546 {
547 	struct libusb20_backend *pbe;
548 	struct options *opt = &options;
549 	const char *ptr;
550 	int unit;
551 	int addr;
552 	int n;
553 	int t;
554 	int ch;
555 
556 	if (argc < 1) {
557 		usage(EX_USAGE);
558 	}
559 	pbe = libusb20_be_alloc_default();
560 	if (pbe == NULL)
561 		err(1, "could not access USB backend\n");
562 
563 	while ((ch = getopt(argc, argv, "a:d:hi:u:v")) != -1) {
564 		switch (ch) {
565 		case 'a':
566 			opt->addr = num_id(optarg, "addr");
567 			opt->got_addr = 1;
568 			break;
569 
570 		case 'd':
571 			if (strncmp(optarg, "ugen", strlen("ugen")) == 0) {
572 				ptr = optarg + strlen("ugen");
573 			} else if (strncmp(optarg, "/dev/ugen",
574 			   strlen("/dev/ugen")) == 0) {
575 				ptr = optarg + strlen("/dev/ugen");
576 			} else {
577 				ptr = optarg;
578 			}
579 			if ((sscanf(ptr, "%d.%d",
580 			    &unit, &addr) != 2) ||
581 			    (unit < 0) || (unit > 65535) ||
582 			    (addr < 0) || (addr > 65535)) {
583 				errx(1, "cannot "
584 				    "parse '%s'", optarg);
585 			}
586 			opt->bus = unit;
587 			opt->addr = addr;
588 			opt->got_bus = 1;
589 			opt->got_addr = 1;
590 			break;
591 
592 		case 'h':
593 			usage(EX_OK);
594 			break;
595 
596 		case 'i':
597 			opt->iface = num_id(optarg, "iface");
598 			break;
599 
600 		case 'u':
601 			opt->bus = num_id(optarg, "busnum");
602 			opt->got_bus = 1;
603 			break;
604 
605 		case 'v':
606 			opt->got_dump_device_desc = 1;
607 			opt->got_dump_curr_config = 1;
608 			opt->got_show_iface_driver = 1;
609 			opt->got_any += 2; /* only the dump options count */
610 			break;
611 
612 		default:
613 			usage(EX_USAGE);
614 		}
615 	}
616 	argc -= optind;
617 	argv += optind;
618 
619 	for (n = 0; n != argc; n++) {
620 
621 		/* get number of additional options */
622 		t = (argc - n - 1);
623 		if (t > 255)
624 			t = 255;
625 		switch (get_token(argv[n], t)) {
626 		case T_ADD_QUIRK:
627 			if (opt->got_add_quirk) {
628 				flush_command(pbe, opt);
629 			}
630 			opt->quirkname = argv[n + 1];
631 			n++;
632 
633 			opt->got_add_quirk = 1;
634 			opt->got_any++;
635 			break;
636 
637 		case T_REMOVE_QUIRK:
638 			if (opt->got_remove_quirk) {
639 				flush_command(pbe, opt);
640 			}
641 			opt->quirkname = argv[n + 1];
642 			n++;
643 
644 			opt->got_remove_quirk = 1;
645 			opt->got_any++;
646 			break;
647 
648 		case T_ADD_DEVICE_QUIRK:
649 			if (opt->got_add_device_quirk) {
650 				flush_command(pbe, opt);
651 			}
652 			opt->vid = num_id(argv[n + 1], "Vendor ID");
653 			opt->pid = num_id(argv[n + 2], "Product ID");
654 			opt->lo_rev = num_id(argv[n + 3], "Low Revision");
655 			opt->hi_rev = num_id(argv[n + 4], "High Revision");
656 			opt->quirkname = argv[n + 5];
657 			n += 5;
658 
659 			opt->got_add_device_quirk = 1;
660 			opt->got_any++;
661 			break;
662 
663 		case T_REMOVE_DEVICE_QUIRK:
664 			if (opt->got_remove_device_quirk) {
665 				flush_command(pbe, opt);
666 			}
667 			opt->vid = num_id(argv[n + 1], "Vendor ID");
668 			opt->pid = num_id(argv[n + 2], "Product ID");
669 			opt->lo_rev = num_id(argv[n + 3], "Low Revision");
670 			opt->hi_rev = num_id(argv[n + 4], "High Revision");
671 			opt->quirkname = argv[n + 5];
672 			n += 5;
673 			opt->got_remove_device_quirk = 1;
674 			opt->got_any++;
675 			break;
676 
677 		case T_DETACH_KERNEL_DRIVER:
678 			if (opt->got_detach_kernel_driver)
679 				duplicate_option(argv[n]);
680 			opt->got_detach_kernel_driver = 1;
681 			opt->got_any++;
682 			break;
683 
684 		case T_DUMP_QUIRK_NAMES:
685 			if (opt->got_dump_quirk_names)
686 				duplicate_option(argv[n]);
687 			opt->got_dump_quirk_names = 1;
688 			opt->got_any++;
689 			break;
690 
691 		case T_DUMP_DEVICE_QUIRKS:
692 			if (opt->got_dump_device_quirks)
693 				duplicate_option(argv[n]);
694 			opt->got_dump_device_quirks = 1;
695 			opt->got_any++;
696 			break;
697 
698 		case T_SHOW_IFACE_DRIVER:
699 			opt->got_show_iface_driver = 1;
700 			break;
701 
702 		case T_SET_CONFIG:
703 			if (opt->got_set_config)
704 				duplicate_option(argv[n]);
705 			opt->config_index = num_id(argv[n + 1], "cfg_index");
706 			opt->got_set_config = 1;
707 			opt->got_any++;
708 			n++;
709 			break;
710 		case T_SET_ALT:
711 			if (opt->got_set_alt)
712 				duplicate_option(argv[n]);
713 			opt->alt_index = num_id(argv[n + 1], "cfg_index");
714 			opt->got_set_alt = 1;
715 			opt->got_any++;
716 			n++;
717 			break;
718 		case T_SET_TEMPLATE:
719 			if (opt->got_set_template)
720 				duplicate_option(argv[n]);
721 			opt->template = get_int(argv[n + 1]);
722 			opt->got_set_template = 1;
723 			opt->got_any++;
724 			n++;
725 			break;
726 		case T_GET_TEMPLATE:
727 			if (opt->got_get_template)
728 				duplicate_option(argv[n]);
729 			opt->got_get_template = 1;
730 			opt->got_any++;
731 			break;
732 		case T_DUMP_ALL_DESC:
733 			if (opt->got_dump_all_desc)
734 				duplicate_option(argv[n]);
735 			opt->got_dump_all_desc = 1;
736 			opt->got_any++;
737 			break;
738 		case T_DUMP_DEVICE_DESC:
739 			if (opt->got_dump_device_desc)
740 				duplicate_option(argv[n]);
741 			opt->got_dump_device_desc = 1;
742 			opt->got_any++;
743 			break;
744 		case T_DUMP_CURR_CONFIG_DESC:
745 			if (opt->got_dump_curr_config)
746 				duplicate_option(argv[n]);
747 			opt->got_dump_curr_config = 1;
748 			opt->got_any++;
749 			break;
750 		case T_DUMP_ALL_CONFIG_DESC:
751 			if (opt->got_dump_all_config)
752 				duplicate_option(argv[n]);
753 			opt->got_dump_all_config = 1;
754 			opt->got_any++;
755 			break;
756 		case T_DUMP_INFO:
757 			if (opt->got_dump_info)
758 				duplicate_option(argv[n]);
759 			opt->got_dump_info = 1;
760 			opt->got_any++;
761 			break;
762 		case T_DUMP_STATS:
763 			if (opt->got_dump_stats)
764 				duplicate_option(argv[n]);
765 			opt->got_dump_stats = 1;
766 			opt->got_any++;
767 			break;
768 		case T_DUMP_STRING:
769 			if (opt->got_dump_string)
770 				duplicate_option(argv[n]);
771 			opt->string_index = num_id(argv[n + 1], "str_index");
772 			opt->got_dump_string = 1;
773 			opt->got_any++;
774 			n++;
775 			break;
776 		case T_SUSPEND:
777 			if (opt->got_suspend)
778 				duplicate_option(argv[n]);
779 			opt->got_suspend = 1;
780 			opt->got_any++;
781 			break;
782 		case T_RESUME:
783 			if (opt->got_resume)
784 				duplicate_option(argv[n]);
785 			opt->got_resume = 1;
786 			opt->got_any++;
787 			break;
788 		case T_POWER_OFF:
789 			if (opt->got_power_off)
790 				duplicate_option(argv[n]);
791 			opt->got_power_off = 1;
792 			opt->got_any++;
793 			break;
794 		case T_POWER_SAVE:
795 			if (opt->got_power_save)
796 				duplicate_option(argv[n]);
797 			opt->got_power_save = 1;
798 			opt->got_any++;
799 			break;
800 		case T_POWER_ON:
801 			if (opt->got_power_on)
802 				duplicate_option(argv[n]);
803 			opt->got_power_on = 1;
804 			opt->got_any++;
805 			break;
806 		case T_RESET:
807 			if (opt->got_reset)
808 				duplicate_option(argv[n]);
809 			opt->got_reset = 1;
810 			opt->got_any++;
811 			break;
812 		case T_LIST:
813 			if (opt->got_list)
814 				duplicate_option(argv[n]);
815 			opt->got_list = 1;
816 			opt->got_any++;
817 			break;
818 		case T_DO_REQUEST:
819 			if (opt->got_do_request)
820 				duplicate_option(argv[n]);
821 			LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &opt->setup);
822 			opt->setup.bmRequestType = num_id(argv[n + 1], "bmReqTyp");
823 			opt->setup.bRequest = num_id(argv[n + 2], "bReq");
824 			opt->setup.wValue = num_id(argv[n + 3], "wVal");
825 			opt->setup.wIndex = num_id(argv[n + 4], "wIndex");
826 			opt->setup.wLength = num_id(argv[n + 5], "wLen");
827 			if (opt->setup.wLength != 0) {
828 				opt->buffer = malloc(opt->setup.wLength);
829 			} else {
830 				opt->buffer = NULL;
831 			}
832 
833 			n += 5;
834 
835 			if (!(opt->setup.bmRequestType &
836 			    LIBUSB20_ENDPOINT_IN)) {
837 				/* copy in data */
838 				t = (argc - n - 1);
839 				if (t < opt->setup.wLength) {
840 					err(1, "request data missing");
841 				}
842 				t = opt->setup.wLength;
843 				while (t--) {
844 					((uint8_t *)opt->buffer)[t] =
845 					    num_id(argv[n + t + 1], "req_data");
846 				}
847 				n += opt->setup.wLength;
848 			}
849 			opt->got_do_request = 1;
850 			opt->got_any++;
851 			break;
852 		default:
853 			if (n == 1) {
854 				ptr = argv[n];
855 
856 				if ((ptr[0] == 'u') &&
857 				    (ptr[1] == 'g') &&
858 				    (ptr[2] == 'e') &&
859 				    (ptr[3] == 'n'))
860 					ptr += 4;
861 
862 				if ((sscanf(ptr, "%d.%d",
863 				    &unit, &addr) != 2) ||
864 				    (unit < 0) || (unit > 65535) ||
865 				    (addr < 0) || (addr > 65535)) {
866 					usage(EX_USAGE);
867 					break;
868 				}
869 
870 				opt->bus = unit;
871 				opt->addr = addr;
872 				opt->got_bus = 1;
873 				opt->got_addr = 1;
874 				break;
875 			}
876 			usage(EX_USAGE);
877 			break;
878 		}
879 	}
880 	if (opt->got_any) {
881 		/* flush out last command */
882 		flush_command(pbe, opt);
883 	} else {
884 		/* list all the devices */
885 		opt->got_list = 1;
886 		opt->got_any++;
887 		flush_command(pbe, opt);
888 	}
889 	/* release data */
890 	libusb20_be_free(pbe);
891 
892 	return (0);
893 }
894