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