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