]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/usbconfig/usbconfig.c
ssh: Update to OpenSSH 9.3p1
[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 <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 }