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