usbconfig.c revision 356408
1139823Simp/* $FreeBSD: stable/10/usr.sbin/usbconfig/usbconfig.c 356408 2020-01-06 09:40:59Z hselasky $ */
2157067Srwatson/*-
3157067Srwatson * Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved.
4192753Srwatson *
5157067Srwatson * Redistribution and use in source and binary forms, with or without
611819Sjulian * modification, are permitted provided that the following conditions
711819Sjulian * are met:
811819Sjulian * 1. Redistributions of source code must retain the above copyright
911819Sjulian *    notice, this list of conditions and the following disclaimer.
1011819Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1111819Sjulian *    notice, this list of conditions and the following disclaimer in the
1211819Sjulian *    documentation and/or other materials provided with the distribution.
1311819Sjulian *
1411819Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15165899Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16165899Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17165899Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18165899Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19165899Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20165899Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21165899Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22165899Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23165899Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24165899Srwatson * SUCH DAMAGE.
25165899Srwatson */
26165899Srwatson
27165899Srwatson#include <stdio.h>
28165899Srwatson#include <stdlib.h>
29165899Srwatson#include <stdint.h>
30165899Srwatson#include <err.h>
31165899Srwatson#include <string.h>
32165899Srwatson#include <pwd.h>
33165899Srwatson#include <grp.h>
34165899Srwatson#include <errno.h>
35165899Srwatson#include <ctype.h>
36165899Srwatson#include <sys/types.h>
37165899Srwatson
38165899Srwatson#include <libusb20_desc.h>
39165899Srwatson#include <libusb20.h>
40165899Srwatson
41165899Srwatson#include "dump.h"
4211819Sjulian
4311819Sjulianstruct options {
4411819Sjulian	const char *quirkname;
4511819Sjulian	void   *buffer;
4611819Sjulian	int template;
4711819Sjulian	gid_t	gid;
4811819Sjulian	uid_t	uid;
4911819Sjulian	mode_t	mode;
5011819Sjulian	uint32_t got_any;
5111819Sjulian	struct LIBUSB20_CONTROL_SETUP_DECODED setup;
5211819Sjulian	uint16_t bus;
5311819Sjulian	uint16_t addr;
5411819Sjulian	uint16_t iface;
5511819Sjulian	uint16_t vid;
5611819Sjulian	uint16_t pid;
5711819Sjulian	uint16_t lo_rev;		/* inclusive */
5811819Sjulian	uint16_t hi_rev;		/* inclusive */
5911819Sjulian	uint8_t	string_index;
6011819Sjulian	uint8_t	config_index;
6111819Sjulian	uint8_t	alt_index;
6212057Sjulian	uint8_t	got_list:1;
6311819Sjulian	uint8_t	got_bus:1;
6411819Sjulian	uint8_t	got_addr:1;
65116189Sobrien	uint8_t	got_iface:1;
66116189Sobrien	uint8_t	got_set_config:1;
67116189Sobrien	uint8_t	got_set_alt:1;
6811819Sjulian	uint8_t	got_set_template:1;
6976166Smarkm	uint8_t	got_get_template:1;
7029024Sbde	uint8_t	got_suspend:1;
7111819Sjulian	uint8_t	got_resume:1;
7276166Smarkm	uint8_t	got_reset:1;
7325345Sjhay	uint8_t	got_power_off:1;
7411819Sjulian	uint8_t	got_power_save:1;
7595759Stanimura	uint8_t	got_power_on:1;
7611819Sjulian	uint8_t	got_dump_device_quirks:1;
7711819Sjulian	uint8_t	got_dump_quirk_names:1;
7895759Stanimura	uint8_t	got_dump_all_desc:1;
7995759Stanimura	uint8_t	got_dump_device_desc:1;
8011819Sjulian	uint8_t	got_dump_curr_config:1;
8111819Sjulian	uint8_t	got_dump_all_config:1;
8211819Sjulian	uint8_t	got_dump_info:1;
8311819Sjulian  	uint8_t	got_dump_stats:1;
8411819Sjulian	uint8_t	got_show_iface_driver:1;
8511819Sjulian	uint8_t	got_remove_device_quirk:1;
8611819Sjulian	uint8_t	got_add_device_quirk:1;
8711819Sjulian	uint8_t	got_remove_quirk:1;
8895759Stanimura	uint8_t	got_add_quirk:1;
8911819Sjulian	uint8_t	got_dump_string:1;
9011819Sjulian	uint8_t	got_do_request:1;
9111819Sjulian	uint8_t	got_detach_kernel_driver:1;
92194545Srwatson};
93194545Srwatson
9411819Sjulianstruct token {
9511819Sjulian	const char *name;
9611819Sjulian	uint8_t	value;
97157069Srwatson	uint8_t	narg;
9833181Seivind};
99192746Srwatson
10033181Seivindenum {
10133181Seivind	T_UNIT,
102192746Srwatson	T_ADDR,
10311819Sjulian	T_UGEN,
104157069Srwatson	T_IFACE,
105157069Srwatson	T_SET_CONFIG,
106157069Srwatson	T_SET_ALT,
107157069Srwatson	T_SET_TEMPLATE,
108132045Srwatson	T_GET_TEMPLATE,
10925652Sjhay	T_ADD_DEVICE_QUIRK,
11011819Sjulian	T_REMOVE_DEVICE_QUIRK,
111139931Srwatson	T_ADD_QUIRK,
112139931Srwatson	T_REMOVE_QUIRK,
113139931Srwatson	T_SHOW_IFACE_DRIVER,
11425652Sjhay	T_DETACH_KERNEL_DRIVER,
11525652Sjhay	T_DUMP_QUIRK_NAMES,
116157128Srwatson	T_DUMP_DEVICE_QUIRKS,
117139931Srwatson	T_DUMP_ALL_DESC,
11825652Sjhay	T_DUMP_DEVICE_DESC,
119157366Srwatson	T_DUMP_CURR_CONFIG_DESC,
12028270Swollman	T_DUMP_ALL_CONFIG_DESC,
12183366Sjulian	T_DUMP_STRING,
12283366Sjulian	T_DUMP_INFO,
123160549Srwatson	T_DUMP_STATS,
12428270Swollman	T_SUSPEND,
12583366Sjulian	T_RESUME,
126157370Srwatson	T_POWER_OFF,
127157128Srwatson	T_POWER_SAVE,
12824659Sjhay	T_POWER_ON,
129151888Srwatson	T_RESET,
13024659Sjhay	T_LIST,
13124659Sjhay	T_DO_REQUEST,
13224659Sjhay};
133139584Srwatson
13483366Sjulianstatic struct options options;
13524659Sjhay
13683366Sjulianstatic const struct token token[] = {
13724659Sjhay	{"-u", T_UNIT, 1},
13824659Sjhay	{"-a", T_ADDR, 1},
139137386Sphk	{"-d", T_UGEN, 1},
140137386Sphk	{"-i", T_IFACE, 1},
141137386Sphk	{"set_config", T_SET_CONFIG, 1},
142137386Sphk	{"set_alt", T_SET_ALT, 1},
143137386Sphk	{"set_template", T_SET_TEMPLATE, 1},
144137386Sphk	{"get_template", T_GET_TEMPLATE, 0},
145137386Sphk	{"add_dev_quirk_vplh", T_ADD_DEVICE_QUIRK, 5},
146137386Sphk	{"remove_dev_quirk_vplh", T_REMOVE_DEVICE_QUIRK, 5},
147137386Sphk	{"add_quirk", T_ADD_QUIRK, 1},
148137386Sphk	{"remove_quirk", T_REMOVE_QUIRK, 1},
149137386Sphk	{"detach_kernel_driver", T_DETACH_KERNEL_DRIVER, 0},
150137386Sphk	{"dump_quirk_names", T_DUMP_QUIRK_NAMES, 0},
151137386Sphk	{"dump_device_quirks", T_DUMP_DEVICE_QUIRKS, 0},
152137386Sphk	{"dump_all_desc", T_DUMP_ALL_DESC, 0},
153137386Sphk	{"dump_device_desc", T_DUMP_DEVICE_DESC, 0},
154160549Srwatson	{"dump_curr_config_desc", T_DUMP_CURR_CONFIG_DESC, 0},
15524659Sjhay	{"dump_all_config_desc", T_DUMP_ALL_CONFIG_DESC, 0},
15624659Sjhay	{"dump_string", T_DUMP_STRING, 1},
15724659Sjhay	{"dump_info", T_DUMP_INFO, 0},
158137386Sphk	{"dump_stats", T_DUMP_STATS, 0},
159137386Sphk	{"show_ifdrv", T_SHOW_IFACE_DRIVER, 0},
160137386Sphk	{"suspend", T_SUSPEND, 0},
161137386Sphk	{"resume", T_RESUME, 0},
162137386Sphk	{"power_off", T_POWER_OFF, 0},
163137386Sphk	{"power_save", T_POWER_SAVE, 0},
164137386Sphk	{"power_on", T_POWER_ON, 0},
165137386Sphk	{"reset", T_RESET, 0},
166137386Sphk	{"list", T_LIST, 0},
167137386Sphk	{"do_request", T_DO_REQUEST, 5},
168137386Sphk};
169137386Sphk
170137386Sphkstatic void
171137386Sphkbe_dev_remove_quirk(struct libusb20_backend *pbe,
172137386Sphk    uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
173160549Srwatson    const char *str)
17424659Sjhay{
17524659Sjhay	struct libusb20_quirk q;
17611819Sjulian	int error;
177157067Srwatson
17811819Sjulian	memset(&q, 0, sizeof(q));
17911819Sjulian
180157069Srwatson	q.vid = vid;
18111819Sjulian	q.pid = pid;
18211819Sjulian	q.bcdDeviceLow = lorev;
18311819Sjulian	q.bcdDeviceHigh = hirev;
18411819Sjulian	strlcpy(q.quirkname, str, sizeof(q.quirkname));
185157067Srwatson
18611819Sjulian	error = libusb20_be_remove_dev_quirk(pbe, &q);
187157067Srwatson	if (error) {
188157067Srwatson		fprintf(stderr, "Removing quirk '%s' failed, continuing.\n", str);
189157067Srwatson	}
190157051Srwatson	return;
19111819Sjulian}
19211819Sjulian
19311819Sjulianstatic void
19411819Sjulianbe_dev_add_quirk(struct libusb20_backend *pbe,
195157094Srwatson    uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
19611819Sjulian    const char *str)
197139932Srwatson{
198139932Srwatson	struct libusb20_quirk q;
199139932Srwatson	int error;
200139932Srwatson
201139932Srwatson	memset(&q, 0, sizeof(q));
202139932Srwatson
203139932Srwatson	q.vid = vid;
204139932Srwatson	q.pid = pid;
205139932Srwatson	q.bcdDeviceLow = lorev;
20611819Sjulian	q.bcdDeviceHigh = hirev;
207157128Srwatson	strlcpy(q.quirkname, str, sizeof(q.quirkname));
20811819Sjulian
209157128Srwatson	error = libusb20_be_add_dev_quirk(pbe, &q);
210157128Srwatson	if (error) {
211157128Srwatson		fprintf(stderr, "Adding quirk '%s' failed, continuing.\n", str);
21211819Sjulian	}
21325652Sjhay	return;
214139932Srwatson}
215139932Srwatson
21611819Sjulianstatic uint8_t
21711819Sjulianget_token(const char *str, uint8_t narg)
21811819Sjulian{
21911819Sjulian	uint8_t n;
22011819Sjulian
22111819Sjulian	for (n = 0; n != (sizeof(token) / sizeof(token[0])); n++) {
22211819Sjulian		if (strcasecmp(str, token[n].name) == 0) {
22311819Sjulian			if (token[n].narg > narg) {
22411819Sjulian				/* too few arguments */
22511819Sjulian				break;
226157094Srwatson			}
22711819Sjulian			return (token[n].value);
228194561Srwatson		}
229194561Srwatson	}
230194561Srwatson	return (0 - 1);
231194561Srwatson}
232194561Srwatson
23311819Sjulianstatic uid_t
23411819Sjuliannum_id(const char *name, const char *type)
23511819Sjulian{
23611819Sjulian	uid_t val;
23711819Sjulian	char *ep;
23811819Sjulian
23911819Sjulian	errno = 0;
24011819Sjulian	val = strtoul(name, &ep, 0);
241157094Srwatson	if (errno) {
24211819Sjulian		err(1, "%s", name);
243157094Srwatson	}
24411819Sjulian	if (*ep != '\0') {
24511819Sjulian		errx(1, "%s: illegal %s name", name, type);
24611819Sjulian	}
247157094Srwatson	return (val);
248157094Srwatson}
249157094Srwatson
250157094Srwatsonstatic int
251157094Srwatsonget_int(const char *s)
252157094Srwatson{
253157128Srwatson	int val;
254157128Srwatson	char *ep;
255157128Srwatson
256157128Srwatson	errno = 0;
257157128Srwatson	val = strtoul(s, &ep, 0);
258157128Srwatson	if (errno) {
259157128Srwatson		err(1, "%s", s);
260157128Srwatson	}
26111819Sjulian	if (*ep != '\0') {
26211819Sjulian		errx(1, "illegal number: %s", s);
263139932Srwatson	}
26411819Sjulian	return val;
265139932Srwatson}
26611819Sjulian
26711819Sjulianstatic void
26811819Sjulianduplicate_option(const char *ptr)
26911819Sjulian{
27011819Sjulian	fprintf(stderr, "Syntax error: "
27111819Sjulian	    "Duplicate option: '%s'\n", ptr);
27297658Stanimura	exit(1);
273157094Srwatson}
27411819Sjulian
27511819Sjulianstatic void
276157094Srwatsonusage(void)
277157094Srwatson{
27811819Sjulian	fprintf(stderr, ""
27911819Sjulian	    "usbconfig - configure the USB subsystem" "\n"
28011819Sjulian	    "usage: usbconfig -u <busnum> -a <devaddr> -i <ifaceindex> [cmds...]" "\n"
28111819Sjulian	    "usage: usbconfig -d [ugen]<busnum>.<devaddr> -i <ifaceindex> [cmds...]" "\n"
28211819Sjulian	    "commands:" "\n"
28311819Sjulian	    "  set_config <cfg_index>" "\n"
28428270Swollman	    "  set_alt <alt_index>" "\n"
28511819Sjulian	    "  set_template <template>" "\n"
28611819Sjulian	    "  get_template" "\n"
28711819Sjulian	    "  add_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
288157094Srwatson	    "  remove_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
289157094Srwatson	    "  add_quirk <quirk>" "\n"
290157094Srwatson	    "  remove_quirk <quirk>" "\n"
29111819Sjulian	    "  detach_kernel_driver" "\n"
29211819Sjulian	    "  dump_quirk_names" "\n"
29311819Sjulian	    "  dump_device_quirks" "\n"
29411819Sjulian	    "  dump_all_desc" "\n"
29511819Sjulian	    "  dump_device_desc" "\n"
29628270Swollman	    "  dump_curr_config_desc" "\n"
29728270Swollman	    "  dump_all_config_desc" "\n"
29811819Sjulian	    "  dump_string <index>" "\n"
29911819Sjulian	    "  dump_info" "\n"
30011819Sjulian	    "  dump_stats" "\n"
30111819Sjulian	    "  show_ifdrv" "\n"
30211819Sjulian	    "  suspend" "\n"
30311819Sjulian	    "  resume" "\n"
30490361Sjulian	    "  power_off" "\n"
30511819Sjulian	    "  power_save" "\n"
30611819Sjulian	    "  power_on" "\n"
30711819Sjulian	    "  reset" "\n"
30811819Sjulian	    "  list" "\n"
30911819Sjulian	    "  do_request <bmReqTyp> <bReq> <wVal> <wIdx> <wLen> <data...>" "\n"
31011819Sjulian	);
31111819Sjulian	exit(1);
31211819Sjulian}
31311819Sjulian
31411819Sjulianstatic void
31511819Sjulianreset_options(struct options *opt)
31611819Sjulian{
31711819Sjulian	if (opt->buffer)
31811819Sjulian		free(opt->buffer);
31911819Sjulian	memset(opt, 0, sizeof(*opt));
32011819Sjulian	return;
32111819Sjulian}
322157094Srwatson
32311819Sjulianstatic void
324157094Srwatsonflush_command(struct libusb20_backend *pbe, struct options *opt)
325157094Srwatson{
326157094Srwatson	struct libusb20_device *pdev = NULL;
327157094Srwatson	uint32_t matches = 0;
328157094Srwatson	uint8_t dump_any;
32925652Sjhay
33011819Sjulian	/* check for invalid option combinations */
33111819Sjulian	if ((opt->got_suspend +
33211819Sjulian	    opt->got_resume +
33311819Sjulian	    opt->got_reset +
33411819Sjulian	    opt->got_set_config +
33511819Sjulian	    opt->got_set_alt +
33611819Sjulian	    opt->got_power_save +
33711819Sjulian	    opt->got_power_on +
33811819Sjulian	    opt->got_power_off) > 1) {
33911819Sjulian		err(1, "can only specify one of 'set_config', "
34011819Sjulian		    "'set_alt', 'reset', 'suspend', 'resume', "
34111819Sjulian		    "'power_save', 'power_on' and 'power_off' "
34211819Sjulian		    "at the same time!");
34311819Sjulian	}
344157094Srwatson	if (opt->got_dump_quirk_names) {
345157094Srwatson		opt->got_any--;
346157094Srwatson		dump_be_quirk_names(pbe);
347157094Srwatson	}
348157094Srwatson	if (opt->got_dump_device_quirks) {
349157094Srwatson		opt->got_any--;
350157094Srwatson		dump_be_dev_quirks(pbe);
351157094Srwatson	}
35225652Sjhay	if (opt->got_remove_device_quirk) {
35311819Sjulian		opt->got_any--;
35411819Sjulian		be_dev_remove_quirk(pbe,
35511819Sjulian		    opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
35611819Sjulian	}
35711819Sjulian	if (opt->got_add_device_quirk) {
35811819Sjulian		opt->got_any--;
35911819Sjulian		be_dev_add_quirk(pbe,
36011819Sjulian		    opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
36111819Sjulian	}
36211819Sjulian	if (opt->got_set_template) {
36311819Sjulian		opt->got_any--;
36411819Sjulian		if (libusb20_be_set_template(pbe, opt->template)) {
365179408Srwatson			fprintf(stderr, "Setting USB template %u failed, "
366157094Srwatson			    "continuing.\n", opt->template);
367157094Srwatson		}
368157094Srwatson	}
36911819Sjulian	if (opt->got_get_template) {
37011819Sjulian		opt->got_any--;
37111819Sjulian		if (libusb20_be_get_template(pbe, &opt->template))
37211819Sjulian			printf("USB template: <unknown>\n");
37311819Sjulian		else
37411819Sjulian			printf("USB template: %u\n", opt->template);
37511819Sjulian	}
37611819Sjulian	if (opt->got_any == 0) {
37711819Sjulian		/*
378157094Srwatson		 * do not scan through all the devices if there are no valid
37997658Stanimura		 * options
38011819Sjulian		 */
38111819Sjulian		goto done;
38225652Sjhay	}
38325652Sjhay	while ((pdev = libusb20_be_device_foreach(pbe, pdev))) {
38425652Sjhay
38511819Sjulian		if (opt->got_bus &&
386194547Srwatson		    (libusb20_dev_get_bus_number(pdev) != opt->bus)) {
38725652Sjhay			continue;
38811819Sjulian		}
389139579Srwatson		if (opt->got_addr &&
39011819Sjulian		    (libusb20_dev_get_address(pdev) != opt->addr)) {
391139932Srwatson			continue;
392139932Srwatson		}
39311819Sjulian		matches++;
39411819Sjulian
39511819Sjulian		if (opt->got_remove_quirk) {
396157094Srwatson			struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
397157164Srwatson
398157128Srwatson			ddesc = libusb20_dev_get_device_desc(pdev);
399157128Srwatson
400139932Srwatson			be_dev_remove_quirk(pbe,
401130822Srwatson			    ddesc->idVendor, ddesc->idProduct,
402130822Srwatson			    ddesc->bcdDevice, ddesc->bcdDevice,
403130822Srwatson			    opt->quirkname);
404130822Srwatson		}
405130822Srwatson
406130822Srwatson		if (opt->got_add_quirk) {
407130822Srwatson			struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
408130822Srwatson
409130822Srwatson			ddesc = libusb20_dev_get_device_desc(pdev);
410130822Srwatson
411130822Srwatson			be_dev_add_quirk(pbe,
41225652Sjhay			    ddesc->idVendor, ddesc->idProduct,
413130822Srwatson			    ddesc->bcdDevice, ddesc->bcdDevice,
414139932Srwatson			    opt->quirkname);
415179332Srwatson		}
41611819Sjulian
41711819Sjulian		if (libusb20_dev_open(pdev, 0)) {
41811819Sjulian			err(1, "could not open device");
419157094Srwatson		}
420157128Srwatson		if (opt->got_dump_string) {
42111819Sjulian			dump_string_by_index(pdev, opt->string_index);
422139932Srwatson		}
423139932Srwatson		if (opt->got_do_request) {
42411819Sjulian			uint16_t actlen;
42511819Sjulian			uint16_t t;
42611819Sjulian
427192754Srwatson			if (libusb20_dev_request_sync(pdev, &opt->setup,
428192754Srwatson			    opt->buffer, &actlen, 5000 /* 5 seconds */ , 0)) {
429192754Srwatson				printf("REQUEST = <ERROR>\n");
430192754Srwatson			} else if (!(opt->setup.bmRequestType &
431192754Srwatson			    LIBUSB20_ENDPOINT_IN)) {
432192754Srwatson				printf("REQUEST = <OK>\n");
433192754Srwatson			} else {
434192746Srwatson				t = actlen;
435157067Srwatson				printf("REQUEST = <");
43611819Sjulian				for (t = 0; t != actlen; t++) {
43711819Sjulian					printf("0x%02x%s",
438192756Srwatson					    ((uint8_t *)opt->buffer)[t],
439157067Srwatson					    (t == (actlen - 1)) ? "" : " ");
440157067Srwatson				}
44111819Sjulian				printf("><");
44211819Sjulian				for (t = 0; t != actlen; t++) {
44311819Sjulian					char c;
44411819Sjulian
44511819Sjulian					c = ((uint8_t *)opt->buffer)[t];
44611819Sjulian					if ((c != '<') &&
44711819Sjulian					    (c != '>') && isprint(c)) {
44811819Sjulian						putchar(c);
44911819Sjulian					}
450139932Srwatson				}
451139932Srwatson				printf(">\n");
45225652Sjhay			}
45311819Sjulian		}
45411819Sjulian		if (opt->got_set_config) {
455157094Srwatson			if (libusb20_dev_set_config_index(pdev,
45611819Sjulian			    opt->config_index)) {
45711819Sjulian				err(1, "could not set config index");
45811819Sjulian			}
45925652Sjhay		}
46011819Sjulian		if (opt->got_set_alt) {
46111819Sjulian			if (libusb20_dev_set_alt_index(pdev, opt->iface,
46211819Sjulian			    opt->alt_index)) {
46311819Sjulian				err(1, "could not set alternate setting");
46411819Sjulian			}
46511819Sjulian		}
46625652Sjhay		if (opt->got_reset) {
46711819Sjulian			if (libusb20_dev_reset(pdev)) {
46811819Sjulian				err(1, "could not reset device");
46911819Sjulian			}
47011819Sjulian		}
47111819Sjulian		if (opt->got_suspend) {
47211819Sjulian			if (libusb20_dev_set_power_mode(pdev,
47311819Sjulian			    LIBUSB20_POWER_SUSPEND)) {
47411819Sjulian				err(1, "could not set suspend");
47511819Sjulian			}
476243882Sglebius		}
477157167Srwatson		if (opt->got_resume) {
478157167Srwatson			if (libusb20_dev_set_power_mode(pdev,
479157167Srwatson			    LIBUSB20_POWER_RESUME)) {
480157167Srwatson				err(1, "could not set resume");
481157167Srwatson			}
48211819Sjulian		}
48311819Sjulian		if (opt->got_power_off) {
48411819Sjulian			if (libusb20_dev_set_power_mode(pdev,
48525652Sjhay			    LIBUSB20_POWER_OFF)) {
48611819Sjulian				err(1, "could not set power OFF");
48711819Sjulian			}
48811819Sjulian		}
48911819Sjulian		if (opt->got_power_save) {
49011819Sjulian			if (libusb20_dev_set_power_mode(pdev,
49111819Sjulian			    LIBUSB20_POWER_SAVE)) {
49211819Sjulian				err(1, "could not set power SAVE");
49311819Sjulian			}
49425652Sjhay		}
49511819Sjulian		if (opt->got_power_on) {
49611819Sjulian			if (libusb20_dev_set_power_mode(pdev,
49711819Sjulian			    LIBUSB20_POWER_ON)) {
49811819Sjulian				err(1, "could not set power ON");
49911819Sjulian			}
50011819Sjulian		}
50111819Sjulian		if (opt->got_detach_kernel_driver) {
502157094Srwatson			if (libusb20_dev_detach_kernel_driver(pdev, opt->iface)) {
50311819Sjulian				err(1, "could not detach kernel driver");
50411819Sjulian			}
50511819Sjulian		}
50611819Sjulian		dump_any =
50711819Sjulian		    (opt->got_dump_all_desc ||
50811819Sjulian		    opt->got_dump_device_desc ||
50911819Sjulian		    opt->got_dump_curr_config ||
51011819Sjulian		    opt->got_dump_all_config ||
51111819Sjulian		    opt->got_dump_info ||
512243882Sglebius		    opt->got_dump_stats);
51311819Sjulian
51425652Sjhay		if (opt->got_list || dump_any) {
51511819Sjulian			dump_device_info(pdev,
51611819Sjulian			    opt->got_show_iface_driver);
51711819Sjulian		}
51811819Sjulian		if (opt->got_dump_device_desc) {
51911819Sjulian			printf("\n");
52011819Sjulian			dump_device_desc(pdev);
52111819Sjulian		}
52211819Sjulian		if (opt->got_dump_all_config) {
523243882Sglebius			printf("\n");
52425652Sjhay			dump_config(pdev, 1);
52511819Sjulian		} else if (opt->got_dump_curr_config) {
52611819Sjulian			printf("\n");
52711819Sjulian			dump_config(pdev, 0);
528157094Srwatson		} else if (opt->got_dump_all_desc) {
52911819Sjulian			printf("\n");
530157094Srwatson			dump_device_desc(pdev);
531157094Srwatson			dump_config(pdev, 1);
53211819Sjulian		}
53325652Sjhay		if (opt->got_dump_stats) {
53425652Sjhay			printf("\n");
53511819Sjulian			dump_device_stats(pdev);
53611819Sjulian		}
537192754Srwatson		if (dump_any) {
53811819Sjulian			printf("\n");
53911819Sjulian		}
540157067Srwatson		if (libusb20_dev_close(pdev)) {
54125652Sjhay			err(1, "could not close device");
54211819Sjulian		}
54325652Sjhay	}
54411819Sjulian
54511819Sjulian	if (matches == 0) {
54611819Sjulian		printf("No device match or lack of permissions.\n");
54711819Sjulian	}
54811819Sjuliandone:
54911819Sjulian	reset_options(opt);
55011819Sjulian
55111819Sjulian	return;
55225652Sjhay}
55325652Sjhay
55425652Sjhayint
55511819Sjulianmain(int argc, char **argv)
55611819Sjulian{
55711819Sjulian	struct libusb20_backend *pbe;
55825652Sjhay	struct options *opt = &options;
55911819Sjulian	const char *ptr;
56011819Sjulian	int unit;
56111819Sjulian	int addr;
56211819Sjulian	int n;
563157094Srwatson	int t;
564157094Srwatson
565157094Srwatson	if (argc < 1) {
566157094Srwatson		usage();
567157094Srwatson	}
56811819Sjulian	pbe = libusb20_be_alloc_default();
56911819Sjulian	if (pbe == NULL)
57011819Sjulian		err(1, "could not access USB backend\n");
57111819Sjulian
57211819Sjulian	for (n = 1; n != argc; n++) {
57311819Sjulian
57411819Sjulian		/* get number of additional options */
57511819Sjulian		t = (argc - n - 1);
576157094Srwatson		if (t > 255)
57711819Sjulian			t = 255;
578157094Srwatson		switch (get_token(argv[n], t)) {
57911819Sjulian		case T_ADD_QUIRK:
58011819Sjulian			if (opt->got_add_quirk) {
58111819Sjulian				flush_command(pbe, opt);
58211819Sjulian			}
58311819Sjulian			opt->quirkname = argv[n + 1];
58411819Sjulian			n++;
58511819Sjulian
58611819Sjulian			opt->got_add_quirk = 1;
58711819Sjulian			opt->got_any++;
58811819Sjulian			break;
58925652Sjhay
59011819Sjulian		case T_REMOVE_QUIRK:
59111819Sjulian			if (opt->got_remove_quirk) {
592157094Srwatson				flush_command(pbe, opt);
593179408Srwatson			}
594179408Srwatson			opt->quirkname = argv[n + 1];
59511819Sjulian			n++;
59611819Sjulian
59711819Sjulian			opt->got_remove_quirk = 1;
59811819Sjulian			opt->got_any++;
59911819Sjulian			break;
60011819Sjulian
60111819Sjulian		case T_ADD_DEVICE_QUIRK:
60211819Sjulian			if (opt->got_add_device_quirk) {
60311819Sjulian				flush_command(pbe, opt);
60411819Sjulian			}
60511819Sjulian			opt->vid = num_id(argv[n + 1], "Vendor ID");
60611819Sjulian			opt->pid = num_id(argv[n + 2], "Product ID");
607157094Srwatson			opt->lo_rev = num_id(argv[n + 3], "Low Revision");
608157094Srwatson			opt->hi_rev = num_id(argv[n + 4], "High Revision");
609157094Srwatson			opt->quirkname = argv[n + 5];
610157094Srwatson			n += 5;
61111819Sjulian
61211819Sjulian			opt->got_add_device_quirk = 1;
61311819Sjulian			opt->got_any++;
61411819Sjulian			break;
61511819Sjulian
61611819Sjulian		case T_REMOVE_DEVICE_QUIRK:
61711819Sjulian			if (opt->got_remove_device_quirk) {
61811819Sjulian				flush_command(pbe, opt);
61911819Sjulian			}
62011819Sjulian			opt->vid = num_id(argv[n + 1], "Vendor ID");
62111819Sjulian			opt->pid = num_id(argv[n + 2], "Product ID");
62211819Sjulian			opt->lo_rev = num_id(argv[n + 3], "Low Revision");
62311819Sjulian			opt->hi_rev = num_id(argv[n + 4], "High Revision");
62411819Sjulian			opt->quirkname = argv[n + 5];
62511819Sjulian			n += 5;
62611819Sjulian			opt->got_remove_device_quirk = 1;
627157094Srwatson			opt->got_any++;
62811819Sjulian			break;
62911819Sjulian
63011819Sjulian		case T_DETACH_KERNEL_DRIVER:
63111819Sjulian			if (opt->got_detach_kernel_driver)
63211819Sjulian				duplicate_option(argv[n]);
63311819Sjulian			opt->got_detach_kernel_driver = 1;
63411819Sjulian			opt->got_any++;
63511819Sjulian			break;
63611819Sjulian
63711819Sjulian		case T_DUMP_QUIRK_NAMES:
638157094Srwatson			if (opt->got_dump_quirk_names)
63911819Sjulian				duplicate_option(argv[n]);
640157094Srwatson			opt->got_dump_quirk_names = 1;
64111819Sjulian			opt->got_any++;
64211819Sjulian			break;
64311819Sjulian
644157094Srwatson		case T_DUMP_DEVICE_QUIRKS:
64511819Sjulian			if (opt->got_dump_device_quirks)
646157094Srwatson				duplicate_option(argv[n]);
647157094Srwatson			opt->got_dump_device_quirks = 1;
648157094Srwatson			opt->got_any++;
649157094Srwatson			break;
65011819Sjulian
65111819Sjulian		case T_SHOW_IFACE_DRIVER:
65211819Sjulian			opt->got_show_iface_driver = 1;
65311819Sjulian			break;
654139584Srwatson
65511819Sjulian		case T_UGEN:
65611819Sjulian			if (opt->got_any) {
65711819Sjulian				/* allow multiple commands on the same line */
65811819Sjulian				flush_command(pbe, opt);
65911819Sjulian			}
66011819Sjulian			ptr = argv[n + 1];
66111819Sjulian
66211819Sjulian			if ((ptr[0] == 'u') &&
663157094Srwatson			    (ptr[1] == 'g') &&
66411819Sjulian			    (ptr[2] == 'e') &&
665157094Srwatson			    (ptr[3] == 'n'))
66611819Sjulian				ptr += 4;
667157094Srwatson
668157094Srwatson			if ((sscanf(ptr, "%d.%d",
669179408Srwatson			    &unit, &addr) != 2) ||
67011819Sjulian			    (unit < 0) || (unit > 65535) ||
67111819Sjulian			    (addr < 0) || (addr > 65535)) {
672157128Srwatson				errx(1, "cannot "
673157128Srwatson				    "parse '%s'", argv[n + 1]);
674157128Srwatson			}
67511819Sjulian			opt->bus = unit;
676157094Srwatson			opt->addr = addr;
67711819Sjulian			opt->got_bus = 1;
67811819Sjulian			opt->got_addr = 1;
67911819Sjulian			n++;
68011819Sjulian			break;
68111819Sjulian
68211819Sjulian		case T_UNIT:
68311819Sjulian			if (opt->got_any) {
68411819Sjulian				/* allow multiple commands on the same line */
68511819Sjulian				flush_command(pbe, opt);
68611819Sjulian			}
687192755Srwatson			opt->bus = num_id(argv[n + 1], "busnum");
688192756Srwatson			opt->got_bus = 1;
68911819Sjulian			n++;
69011819Sjulian			break;
691192748Srwatson		case T_ADDR:
69211819Sjulian			opt->addr = num_id(argv[n + 1], "addr");
69311819Sjulian			opt->got_addr = 1;
69411819Sjulian			n++;
69511819Sjulian			break;
69611819Sjulian		case T_IFACE:
69725652Sjhay			opt->iface = num_id(argv[n + 1], "iface");
698192756Srwatson			opt->got_iface = 1;
699192756Srwatson			n++;
700192756Srwatson			break;
701192756Srwatson		case T_SET_CONFIG:
702192756Srwatson			if (opt->got_set_config)
703192756Srwatson				duplicate_option(argv[n]);
70411819Sjulian			opt->config_index = num_id(argv[n + 1], "cfg_index");
70511819Sjulian			opt->got_set_config = 1;
706157094Srwatson			opt->got_any++;
70711819Sjulian			n++;
708157094Srwatson			break;
70911819Sjulian		case T_SET_ALT:
71011819Sjulian			if (opt->got_set_alt)
71111819Sjulian				duplicate_option(argv[n]);
71211819Sjulian			opt->alt_index = num_id(argv[n + 1], "cfg_index");
713139584Srwatson			opt->got_set_alt = 1;
71411819Sjulian			opt->got_any++;
71511819Sjulian			n++;
716192756Srwatson			break;
71711819Sjulian		case T_SET_TEMPLATE:
718157094Srwatson			if (opt->got_set_template)
719157094Srwatson				duplicate_option(argv[n]);
72011819Sjulian			opt->template = get_int(argv[n + 1]);
721192756Srwatson			opt->got_set_template = 1;
722157094Srwatson			opt->got_any++;
72311819Sjulian			n++;
72411819Sjulian			break;
72511819Sjulian		case T_GET_TEMPLATE:
72611819Sjulian			if (opt->got_get_template)
72711819Sjulian				duplicate_option(argv[n]);
72811819Sjulian			opt->got_get_template = 1;
72911819Sjulian			opt->got_any++;
73011819Sjulian			break;
731157094Srwatson		case T_DUMP_ALL_DESC:
73211819Sjulian			if (opt->got_dump_all_desc)
73311819Sjulian				duplicate_option(argv[n]);
73411819Sjulian			opt->got_dump_all_desc = 1;
73511819Sjulian			opt->got_any++;
73611819Sjulian			break;
737243882Sglebius		case T_DUMP_DEVICE_DESC:
73825652Sjhay			if (opt->got_dump_device_desc)
73911819Sjulian				duplicate_option(argv[n]);
740157094Srwatson			opt->got_dump_device_desc = 1;
74111819Sjulian			opt->got_any++;
742157094Srwatson			break;
743157094Srwatson		case T_DUMP_CURR_CONFIG_DESC:
74411819Sjulian			if (opt->got_dump_curr_config)
74525652Sjhay				duplicate_option(argv[n]);
74625652Sjhay			opt->got_dump_curr_config = 1;
74725652Sjhay			opt->got_any++;
74811819Sjulian			break;
749192754Srwatson		case T_DUMP_ALL_CONFIG_DESC:
75011819Sjulian			if (opt->got_dump_all_config)
75111819Sjulian				duplicate_option(argv[n]);
75225652Sjhay			opt->got_dump_all_config = 1;
75311819Sjulian			opt->got_any++;
75411819Sjulian			break;
75511819Sjulian		case T_DUMP_INFO:
75697658Stanimura			if (opt->got_dump_info)
75711819Sjulian				duplicate_option(argv[n]);
75811819Sjulian			opt->got_dump_info = 1;
75911819Sjulian			opt->got_any++;
760179408Srwatson			break;
76111819Sjulian		case T_DUMP_STATS:
76211819Sjulian			if (opt->got_dump_stats)
76311819Sjulian				duplicate_option(argv[n]);
76411819Sjulian			opt->got_dump_stats = 1;
76511819Sjulian			opt->got_any++;
76611819Sjulian			break;
76711819Sjulian		case T_DUMP_STRING:
768139584Srwatson			if (opt->got_dump_string)
76911819Sjulian				duplicate_option(argv[n]);
77011819Sjulian			opt->string_index = num_id(argv[n + 1], "str_index");
77111819Sjulian			opt->got_dump_string = 1;
77211819Sjulian			opt->got_any++;
77311819Sjulian			n++;
77411819Sjulian			break;
77511819Sjulian		case T_SUSPEND:
77611819Sjulian			if (opt->got_suspend)
77711819Sjulian				duplicate_option(argv[n]);
77811819Sjulian			opt->got_suspend = 1;
779157094Srwatson			opt->got_any++;
78011819Sjulian			break;
781157094Srwatson		case T_RESUME:
782157094Srwatson			if (opt->got_resume)
783157094Srwatson				duplicate_option(argv[n]);
784157094Srwatson			opt->got_resume = 1;
785157094Srwatson			opt->got_any++;
78611819Sjulian			break;
78711819Sjulian		case T_POWER_OFF:
78811819Sjulian			if (opt->got_power_off)
78911819Sjulian				duplicate_option(argv[n]);
79011819Sjulian			opt->got_power_off = 1;
79111819Sjulian			opt->got_any++;
79211819Sjulian			break;
79311819Sjulian		case T_POWER_SAVE:
79411819Sjulian			if (opt->got_power_save)
795157094Srwatson				duplicate_option(argv[n]);
79611819Sjulian			opt->got_power_save = 1;
79711819Sjulian			opt->got_any++;
79811819Sjulian			break;
79911819Sjulian		case T_POWER_ON:
80011819Sjulian			if (opt->got_power_on)
80111819Sjulian				duplicate_option(argv[n]);
80211819Sjulian			opt->got_power_on = 1;
80311819Sjulian			opt->got_any++;
804157094Srwatson			break;
805157094Srwatson		case T_RESET:
806157094Srwatson			if (opt->got_reset)
807157094Srwatson				duplicate_option(argv[n]);
808157094Srwatson			opt->got_reset = 1;
809157094Srwatson			opt->got_any++;
810157094Srwatson			break;
811157094Srwatson		case T_LIST:
812157094Srwatson			if (opt->got_list)
81311819Sjulian				duplicate_option(argv[n]);
814157094Srwatson			opt->got_list = 1;
815157094Srwatson			opt->got_any++;
816157094Srwatson			break;
817157094Srwatson		case T_DO_REQUEST:
81811819Sjulian			if (opt->got_do_request)
819157094Srwatson				duplicate_option(argv[n]);
820157094Srwatson			LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &opt->setup);
821157094Srwatson			opt->setup.bmRequestType = num_id(argv[n + 1], "bmReqTyp");
822157094Srwatson			opt->setup.bRequest = num_id(argv[n + 2], "bReq");
823194545Srwatson			opt->setup.wValue = num_id(argv[n + 3], "wVal");
824194545Srwatson			opt->setup.wIndex = num_id(argv[n + 4], "wIndex");
825194545Srwatson			opt->setup.wLength = num_id(argv[n + 5], "wLen");
826194545Srwatson			if (opt->setup.wLength != 0) {
827157094Srwatson				opt->buffer = malloc(opt->setup.wLength);
828157094Srwatson			} else {
829157094Srwatson				opt->buffer = NULL;
830157094Srwatson			}
831157094Srwatson
83211819Sjulian			n += 5;
83311819Sjulian
834157094Srwatson			if (!(opt->setup.bmRequestType &
83511819Sjulian			    LIBUSB20_ENDPOINT_IN)) {
836157094Srwatson				/* copy in data */
837157094Srwatson				t = (argc - n - 1);
838157094Srwatson				if (t < opt->setup.wLength) {
83911819Sjulian					err(1, "request data missing");
84011819Sjulian				}
84111819Sjulian				t = opt->setup.wLength;
84211819Sjulian				while (t--) {
84311819Sjulian					((uint8_t *)opt->buffer)[t] =
84411819Sjulian					    num_id(argv[n + t + 1], "req_data");
84511819Sjulian				}
84611819Sjulian				n += opt->setup.wLength;
84711819Sjulian			}
84811819Sjulian			opt->got_do_request = 1;
84911819Sjulian			opt->got_any++;
85033181Seivind			break;
85111819Sjulian		default:
85225652Sjhay			if (n == 1) {
853157067Srwatson				ptr = argv[n];
85411819Sjulian
855157067Srwatson				if ((ptr[0] == 'u') &&
85611819Sjulian				    (ptr[1] == 'g') &&
857139932Srwatson				    (ptr[2] == 'e') &&
858139932Srwatson				    (ptr[3] == 'n'))
85911819Sjulian					ptr += 4;
86011819Sjulian
861157094Srwatson				if ((sscanf(ptr, "%d.%d",
86211819Sjulian				    &unit, &addr) != 2) ||
86311819Sjulian				    (unit < 0) || (unit > 65535) ||
86411819Sjulian				    (addr < 0) || (addr > 65535)) {
86511819Sjulian					usage();
86611819Sjulian					break;
86711819Sjulian				}
86811819Sjulian
86911819Sjulian				opt->bus = unit;
87011819Sjulian				opt->addr = addr;
87125652Sjhay				opt->got_bus = 1;
87211819Sjulian				opt->got_addr = 1;
873157067Srwatson				break;
87411819Sjulian			}
875157125Srwatson			usage();
876157125Srwatson			break;
877157067Srwatson		}
87838482Swollman	}
87938482Swollman	if (opt->got_any) {
88038482Swollman		/* flush out last command */
88138482Swollman		flush_command(pbe, opt);
88211819Sjulian	} else {
883157128Srwatson		/* list all the devices */
884157128Srwatson		opt->got_list = 1;
885157128Srwatson		opt->got_any++;
886157094Srwatson		flush_command(pbe, opt);
887157094Srwatson	}
888157094Srwatson	/* release data */
889157094Srwatson	libusb20_be_free(pbe);
890157094Srwatson
89138482Swollman	return (0);
892157125Srwatson}
893157128Srwatson