1 /* $FreeBSD: head/usr.sbin/usbconfig/usbconfig.c 248236 2013-03-13 12:23:14Z hselasky $ */
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
);