3 * Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
36 #include <sys/types.h>
38 #include <libusb20_desc.h>
44 const char *quirkname;
51 struct LIBUSB20_CONTROL_SETUP_DECODED setup;
57 uint16_t lo_rev; /* inclusive */
58 uint16_t hi_rev; /* inclusive */
66 uint8_t got_set_config:1;
67 uint8_t got_set_alt:1;
68 uint8_t got_set_template:1;
69 uint8_t got_get_template:1;
70 uint8_t got_suspend:1;
73 uint8_t got_power_off:1;
74 uint8_t got_power_save:1;
75 uint8_t got_power_on:1;
76 uint8_t got_dump_device_quirks:1;
77 uint8_t got_dump_quirk_names:1;
78 uint8_t got_dump_device_desc:1;
79 uint8_t got_dump_curr_config:1;
80 uint8_t got_dump_all_config:1;
81 uint8_t got_dump_info:1;
82 uint8_t got_show_iface_driver:1;
83 uint8_t got_remove_device_quirk:1;
84 uint8_t got_add_device_quirk:1;
85 uint8_t got_remove_quirk:1;
86 uint8_t got_add_quirk:1;
87 uint8_t got_dump_string:1;
88 uint8_t got_do_request:1;
107 T_REMOVE_DEVICE_QUIRK,
112 T_DUMP_DEVICE_QUIRKS,
114 T_DUMP_CURR_CONFIG_DESC,
115 T_DUMP_ALL_CONFIG_DESC,
128 static struct options options;
130 static const struct token token[] = {
135 {"set_config", T_SET_CONFIG, 1},
136 {"set_alt", T_SET_ALT, 1},
137 {"set_template", T_SET_TEMPLATE, 1},
138 {"get_template", T_GET_TEMPLATE, 0},
139 {"add_dev_quirk_vplh", T_ADD_DEVICE_QUIRK, 5},
140 {"remove_dev_quirk_vplh", T_REMOVE_DEVICE_QUIRK, 5},
141 {"add_quirk", T_ADD_QUIRK, 1},
142 {"remove_quirk", T_REMOVE_QUIRK, 1},
143 {"dump_quirk_names", T_DUMP_QUIRK_NAMES, 0},
144 {"dump_device_quirks", T_DUMP_DEVICE_QUIRKS, 0},
145 {"dump_device_desc", T_DUMP_DEVICE_DESC, 0},
146 {"dump_curr_config_desc", T_DUMP_CURR_CONFIG_DESC, 0},
147 {"dump_all_config_desc", T_DUMP_ALL_CONFIG_DESC, 0},
148 {"dump_string", T_DUMP_STRING, 1},
149 {"dump_info", T_DUMP_INFO, 0},
150 {"show_ifdrv", T_SHOW_IFACE_DRIVER, 0},
151 {"suspend", T_SUSPEND, 0},
152 {"resume", T_RESUME, 0},
153 {"power_off", T_POWER_OFF, 0},
154 {"power_save", T_POWER_SAVE, 0},
155 {"power_on", T_POWER_ON, 0},
156 {"reset", T_RESET, 0},
158 {"do_request", T_DO_REQUEST, 5},
162 be_dev_remove_quirk(struct libusb20_backend *pbe,
163 uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
166 struct libusb20_quirk q;
169 memset(&q, 0, sizeof(q));
173 q.bcdDeviceLow = lorev;
174 q.bcdDeviceHigh = hirev;
175 strlcpy(q.quirkname, str, sizeof(q.quirkname));
177 error = libusb20_be_remove_dev_quirk(pbe, &q);
179 fprintf(stderr, "Removing quirk '%s' failed, continuing.\n", str);
185 be_dev_add_quirk(struct libusb20_backend *pbe,
186 uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
189 struct libusb20_quirk q;
192 memset(&q, 0, sizeof(q));
196 q.bcdDeviceLow = lorev;
197 q.bcdDeviceHigh = hirev;
198 strlcpy(q.quirkname, str, sizeof(q.quirkname));
200 error = libusb20_be_add_dev_quirk(pbe, &q);
202 fprintf(stderr, "Adding quirk '%s' failed, continuing.\n", str);
208 get_token(const char *str, uint8_t narg)
212 for (n = 0; n != (sizeof(token) / sizeof(token[0])); n++) {
213 if (strcasecmp(str, token[n].name) == 0) {
214 if (token[n].narg > narg) {
215 /* too few arguments */
218 return (token[n].value);
225 num_id(const char *name, const char *type)
231 val = strtoul(name, &ep, 0);
236 errx(1, "%s: illegal %s name", name, type);
242 get_int(const char *s)
248 val = strtoul(s, &ep, 0);
253 errx(1, "illegal number: %s", s);
259 duplicate_option(const char *ptr)
261 fprintf(stderr, "Syntax error: "
262 "Duplicate option: '%s'\n", ptr);
270 "usbconfig - configure the USB subsystem" "\n"
271 "usage: usbconfig -u <busnum> -a <devaddr> -i <ifaceindex> [cmds...]" "\n"
272 "usage: usbconfig -d [ugen]<busnum>.<devaddr> -i <ifaceindex> [cmds...]" "\n"
274 " set_config <cfg_index>" "\n"
275 " set_alt <alt_index>" "\n"
276 " set_template <template>" "\n"
278 " add_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
279 " remove_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
280 " add_quirk <quirk>" "\n"
281 " remove_quirk <quirk>" "\n"
282 " dump_quirk_names" "\n"
283 " dump_device_quirks" "\n"
284 " dump_device_desc" "\n"
285 " dump_curr_config_desc" "\n"
286 " dump_all_config_desc" "\n"
287 " dump_string <index>" "\n"
297 " do_request <bmReqTyp> <bReq> <wVal> <wIdx> <wLen> <data...>" "\n"
303 reset_options(struct options *opt)
307 memset(opt, 0, sizeof(*opt));
312 flush_command(struct libusb20_backend *pbe, struct options *opt)
314 struct libusb20_device *pdev = NULL;
315 uint32_t matches = 0;
318 /* check for invalid option combinations */
319 if ((opt->got_suspend +
322 opt->got_set_config +
324 opt->got_power_save +
326 opt->got_power_off) > 1) {
327 err(1, "can only specify one of 'set_config', "
328 "'set_alt', 'reset', 'suspend', 'resume', "
329 "'power_save', 'power_on' and 'power_off' "
330 "at the same time!");
332 if (opt->got_dump_quirk_names) {
334 dump_be_quirk_names(pbe);
336 if (opt->got_dump_device_quirks) {
338 dump_be_dev_quirks(pbe);
340 if (opt->got_remove_device_quirk) {
342 be_dev_remove_quirk(pbe,
343 opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
345 if (opt->got_add_device_quirk) {
347 be_dev_add_quirk(pbe,
348 opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
350 if (opt->got_set_template) {
352 if (libusb20_be_set_template(pbe, opt->template)) {
353 fprintf(stderr, "Setting USB template %u failed, "
354 "continuing.\n", opt->template);
357 if (opt->got_get_template) {
359 if (libusb20_be_get_template(pbe, &opt->template))
360 printf("USB template: <unknown>\n");
362 printf("USB template: %u\n", opt->template);
364 if (opt->got_any == 0) {
366 * do not scan through all the devices if there are no valid
371 while ((pdev = libusb20_be_device_foreach(pbe, pdev))) {
374 (libusb20_dev_get_bus_number(pdev) != opt->bus)) {
378 (libusb20_dev_get_address(pdev) != opt->addr)) {
383 if (opt->got_remove_quirk) {
384 struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
386 ddesc = libusb20_dev_get_device_desc(pdev);
388 be_dev_remove_quirk(pbe,
389 ddesc->idVendor, ddesc->idProduct,
390 ddesc->bcdDevice, ddesc->bcdDevice,
394 if (opt->got_add_quirk) {
395 struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
397 ddesc = libusb20_dev_get_device_desc(pdev);
399 be_dev_add_quirk(pbe,
400 ddesc->idVendor, ddesc->idProduct,
401 ddesc->bcdDevice, ddesc->bcdDevice,
405 if (libusb20_dev_open(pdev, 0)) {
406 err(1, "could not open device");
408 if (opt->got_dump_string) {
409 dump_string_by_index(pdev, opt->string_index);
411 if (opt->got_do_request) {
415 if (libusb20_dev_request_sync(pdev, &opt->setup,
416 opt->buffer, &actlen, 5000 /* 5 seconds */ , 0)) {
417 printf("REQUEST = <ERROR>\n");
418 } else if (!(opt->setup.bmRequestType &
419 LIBUSB20_ENDPOINT_IN)) {
420 printf("REQUEST = <OK>\n");
423 printf("REQUEST = <");
424 for (t = 0; t != actlen; t++) {
426 ((uint8_t *)opt->buffer)[t],
427 (t == (actlen - 1)) ? "" : " ");
430 for (t = 0; t != actlen; t++) {
433 c = ((uint8_t *)opt->buffer)[t];
435 (c != '>') && isprint(c)) {
442 if (opt->got_set_config) {
443 if (libusb20_dev_set_config_index(pdev,
444 opt->config_index)) {
445 err(1, "could not set config index");
448 if (opt->got_set_alt) {
449 if (libusb20_dev_set_alt_index(pdev, opt->iface,
451 err(1, "could not set alternate setting");
454 if (opt->got_reset) {
455 if (libusb20_dev_reset(pdev)) {
456 err(1, "could not reset device");
459 if (opt->got_suspend) {
460 if (libusb20_dev_set_power_mode(pdev,
461 LIBUSB20_POWER_SUSPEND)) {
462 err(1, "could not set suspend");
465 if (opt->got_resume) {
466 if (libusb20_dev_set_power_mode(pdev,
467 LIBUSB20_POWER_RESUME)) {
468 err(1, "could not set resume");
471 if (opt->got_power_off) {
472 if (libusb20_dev_set_power_mode(pdev,
473 LIBUSB20_POWER_OFF)) {
474 err(1, "could not set power OFF");
477 if (opt->got_power_save) {
478 if (libusb20_dev_set_power_mode(pdev,
479 LIBUSB20_POWER_SAVE)) {
480 err(1, "could not set power SAVE");
483 if (opt->got_power_on) {
484 if (libusb20_dev_set_power_mode(pdev,
485 LIBUSB20_POWER_ON)) {
486 err(1, "could not set power ON");
490 (opt->got_dump_device_desc ||
491 opt->got_dump_curr_config ||
492 opt->got_dump_all_config ||
495 if (opt->got_list || dump_any) {
496 dump_device_info(pdev,
497 opt->got_show_iface_driver);
499 if (opt->got_dump_device_desc) {
501 dump_device_desc(pdev);
503 if (opt->got_dump_all_config) {
505 dump_config(pdev, 1);
506 } else if (opt->got_dump_curr_config) {
508 dump_config(pdev, 0);
513 if (libusb20_dev_close(pdev)) {
514 err(1, "could not close device");
519 printf("No device match or lack of permissions.\n");
528 main(int argc, char **argv)
530 struct libusb20_backend *pbe;
531 struct options *opt = &options;
541 pbe = libusb20_be_alloc_default();
543 err(1, "could not access USB backend\n");
545 for (n = 1; n != argc; n++) {
547 /* get number of additional options */
551 switch (get_token(argv[n], t)) {
553 if (opt->got_add_quirk) {
554 flush_command(pbe, opt);
556 opt->quirkname = argv[n + 1];
559 opt->got_add_quirk = 1;
564 if (opt->got_remove_quirk) {
565 flush_command(pbe, opt);
567 opt->quirkname = argv[n + 1];
570 opt->got_remove_quirk = 1;
574 case T_ADD_DEVICE_QUIRK:
575 if (opt->got_add_device_quirk) {
576 flush_command(pbe, opt);
578 opt->vid = num_id(argv[n + 1], "Vendor ID");
579 opt->pid = num_id(argv[n + 2], "Product ID");
580 opt->lo_rev = num_id(argv[n + 3], "Low Revision");
581 opt->hi_rev = num_id(argv[n + 4], "High Revision");
582 opt->quirkname = argv[n + 5];
585 opt->got_add_device_quirk = 1;
589 case T_REMOVE_DEVICE_QUIRK:
590 if (opt->got_remove_device_quirk) {
591 flush_command(pbe, opt);
593 opt->vid = num_id(argv[n + 1], "Vendor ID");
594 opt->pid = num_id(argv[n + 2], "Product ID");
595 opt->lo_rev = num_id(argv[n + 3], "Low Revision");
596 opt->hi_rev = num_id(argv[n + 4], "High Revision");
597 opt->quirkname = argv[n + 5];
599 opt->got_remove_device_quirk = 1;
603 case T_DUMP_QUIRK_NAMES:
604 if (opt->got_dump_quirk_names)
605 duplicate_option(argv[n]);
606 opt->got_dump_quirk_names = 1;
610 case T_DUMP_DEVICE_QUIRKS:
611 if (opt->got_dump_device_quirks)
612 duplicate_option(argv[n]);
613 opt->got_dump_device_quirks = 1;
617 case T_SHOW_IFACE_DRIVER:
618 opt->got_show_iface_driver = 1;
623 /* allow multiple commands on the same line */
624 flush_command(pbe, opt);
628 if ((ptr[0] == 'u') &&
634 if ((sscanf(ptr, "%d.%d",
635 &unit, &addr) != 2) ||
636 (unit < 0) || (unit > 65535) ||
637 (addr < 0) || (addr > 65535)) {
639 "parse '%s'", argv[n + 1]);
650 /* allow multiple commands on the same line */
651 flush_command(pbe, opt);
653 opt->bus = num_id(argv[n + 1], "busnum");
658 opt->addr = num_id(argv[n + 1], "addr");
663 opt->iface = num_id(argv[n + 1], "iface");
668 if (opt->got_set_config)
669 duplicate_option(argv[n]);
670 opt->config_index = num_id(argv[n + 1], "cfg_index");
671 opt->got_set_config = 1;
676 if (opt->got_set_alt)
677 duplicate_option(argv[n]);
678 opt->alt_index = num_id(argv[n + 1], "cfg_index");
679 opt->got_set_alt = 1;
684 if (opt->got_set_template)
685 duplicate_option(argv[n]);
686 opt->template = get_int(argv[n + 1]);
687 opt->got_set_template = 1;
692 if (opt->got_get_template)
693 duplicate_option(argv[n]);
694 opt->got_get_template = 1;
697 case T_DUMP_DEVICE_DESC:
698 if (opt->got_dump_device_desc)
699 duplicate_option(argv[n]);
700 opt->got_dump_device_desc = 1;
703 case T_DUMP_CURR_CONFIG_DESC:
704 if (opt->got_dump_curr_config)
705 duplicate_option(argv[n]);
706 opt->got_dump_curr_config = 1;
709 case T_DUMP_ALL_CONFIG_DESC:
710 if (opt->got_dump_all_config)
711 duplicate_option(argv[n]);
712 opt->got_dump_all_config = 1;
716 if (opt->got_dump_info)
717 duplicate_option(argv[n]);
718 opt->got_dump_info = 1;
722 if (opt->got_dump_string)
723 duplicate_option(argv[n]);
724 opt->string_index = num_id(argv[n + 1], "str_index");
725 opt->got_dump_string = 1;
730 if (opt->got_suspend)
731 duplicate_option(argv[n]);
732 opt->got_suspend = 1;
737 duplicate_option(argv[n]);
742 if (opt->got_power_off)
743 duplicate_option(argv[n]);
744 opt->got_power_off = 1;
748 if (opt->got_power_save)
749 duplicate_option(argv[n]);
750 opt->got_power_save = 1;
754 if (opt->got_power_on)
755 duplicate_option(argv[n]);
756 opt->got_power_on = 1;
761 duplicate_option(argv[n]);
767 duplicate_option(argv[n]);
772 if (opt->got_do_request)
773 duplicate_option(argv[n]);
774 LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &opt->setup);
775 opt->setup.bmRequestType = num_id(argv[n + 1], "bmReqTyp");
776 opt->setup.bRequest = num_id(argv[n + 2], "bReq");
777 opt->setup.wValue = num_id(argv[n + 3], "wVal");
778 opt->setup.wIndex = num_id(argv[n + 4], "wIndex");
779 opt->setup.wLength = num_id(argv[n + 5], "wLen");
780 if (opt->setup.wLength != 0) {
781 opt->buffer = malloc(opt->setup.wLength);
788 if (!(opt->setup.bmRequestType &
789 LIBUSB20_ENDPOINT_IN)) {
792 if (t < opt->setup.wLength) {
793 err(1, "request data missing");
795 t = opt->setup.wLength;
797 ((uint8_t *)opt->buffer)[t] =
798 num_id(argv[n + t + 1], "req_data");
800 n += opt->setup.wLength;
802 opt->got_do_request = 1;
811 /* flush out last command */
812 flush_command(pbe, opt);
814 /* list all the devices */
817 flush_command(pbe, opt);
820 libusb20_be_free(pbe);