Path: blob/main/share/examples/libusb20/control.c
102294 views
/*-1* SPDX-License-Identifier: Beerware2*3* ----------------------------------------------------------------------------4* "THE BEER-WARE LICENSE" (Revision 42) (by Poul-Henning Kamp):5* <[email protected]> wrote this file. As long as you retain this notice you6* can do whatever you want with this stuff. If we meet some day, and you think7* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch8* ----------------------------------------------------------------------------9*/1011/*12* Simple demo program to illustrate the handling of FreeBSD's13* libusb20.14*/1516/*17* Examples:18* Just list all VID:PID pairs19* ./control20*21* Standard device request GET_STATUS, report two bytes of status22* (bit 0 in the first byte returned is the "self powered" bit)23* ./control -v 0x3eb -p 0x2103 in std dev get_status 0 0 224*25* Request input reports through the interrupt pipe from a mouse26* device (move the mouse around after issuing the command):27* ./control -v 0x093a -p 0x2516 -i 0x8128*29*/303132#include <limits.h>33#include <stdbool.h>34#include <stdio.h>35#include <stdint.h>36#include <stdlib.h>37#include <sysexits.h>38#include <unistd.h>39#include <string.h>4041#include <libusb20.h>42#include <libusb20_desc.h>4344#include <sys/queue.h>4546#include "util.h"4748/*49* If you want to see the details of the internal datastructures50* in the debugger, unifdef the following.51*/52#ifdef DEBUG53# include "/usr/src/lib/libusb/libusb20_int.h"54#endif5556#define BUFLEN 645758#define TIMEOUT 5000 /* 5 s */5960int intr_ep; /* endpoints */61struct LIBUSB20_CONTROL_SETUP_DECODED setup;6263uint8_t out_buf[BUFLEN];64uint16_t out_len;6566bool do_request;6768static void69doit(struct libusb20_device *dev)70{71int rv;7273if (do_request)74printf("doit(): bmRequestType 0x%02x, bRequest 0x%02x, wValue 0x%04x, wIndex 0x%04x, wLength 0x%04x\n",75setup.bmRequestType,76setup.bRequest,77setup.wValue,78setup.wIndex,79setup.wLength);8081/*82* Open the device, allocating memory for two possible (bulk or83* interrupt) transfers.84*85* If only control transfers are intended (via86* libusb20_dev_request_sync()), transfer_max can be given as 0.87*/88if ((rv = libusb20_dev_open(dev, 1)) != 0)89{90fprintf(stderr, "libusb20_dev_open: %s\n", libusb20_strerror(rv));91return;92}9394/*95* If the device has more than one configuration, select the desired96* one here.97*/98if ((rv = libusb20_dev_set_config_index(dev, 0)) != 0)99{100fprintf(stderr, "libusb20_dev_set_config_index: %s\n", libusb20_strerror(rv));101return;102}103104uint8_t *data = 0;105uint16_t actlen;106107if ((setup.bmRequestType & 0x80) != 0)108{109/* this is an IN request, allocate a buffer */110data = malloc(setup.wLength);111if (data == 0)112{113fprintf(stderr,114"Out of memory allocating %u bytes of reply buffer\n",115setup.wLength);116return;117}118}119else120data = out_buf;121122if (do_request)123{124if ((rv = libusb20_dev_request_sync(dev, &setup, data,125&actlen,126TIMEOUT,1270 /* flags */)) != 0)128{129fprintf(stderr,130"libusb20_dev_request_sync: %s\n", libusb20_strerror(rv));131}132printf("sent %d bytes\n", actlen);133if ((setup.bmRequestType & 0x80) != 0)134{135print_formatted(data, (uint32_t)setup.wLength);136free(data);137}138}139140if (intr_ep != 0)141{142/*143* One transfer has been requested in libusb20_dev_open() above;144* obtain the corresponding transfer struct pointer.145*/146struct libusb20_transfer *xfr_intr = libusb20_tr_get_pointer(dev, 0);147148if (xfr_intr == NULL)149{150fprintf(stderr, "libusb20_tr_get_pointer: %s\n", libusb20_strerror(rv));151return;152}153154/*155* Open the interrupt transfer.156*/157if ((rv = libusb20_tr_open(xfr_intr, 0, 1, intr_ep)) != 0)158{159fprintf(stderr, "libusb20_tr_open: %s\n", libusb20_strerror(rv));160return;161}162163uint8_t in_buf[BUFLEN];164uint32_t rlen;165166if ((rv = libusb20_tr_bulk_intr_sync(xfr_intr, in_buf, BUFLEN, &rlen, TIMEOUT))167!= 0)168{169fprintf(stderr, "libusb20_tr_bulk_intr_sync: %s\n", libusb20_strerror(rv));170}171printf("received %d bytes\n", rlen);172if (rlen > 0)173print_formatted(in_buf, rlen);174175libusb20_tr_close(xfr_intr);176}177178libusb20_dev_close(dev);179}180181static void182usage(void)183{184fprintf(stderr,185"Usage ./usb [-i <INTR_EP>] -v <VID> -p <PID> [dir type rcpt req wValue wIndex wLength [<outdata> ...]]\n");186exit(EX_USAGE);187}188189static const char *reqnames[] =190{191"get_status",192"clear_feature",193"res1",194"set_feature",195"res2",196"set_address",197"get_descriptor",198"set_descriptor",199"get_configuration",200"set_configuration",201"get_interface",202"set_interface",203"synch_frame",204};205206static int207get_req(const char *reqname)208{209size_t i;210size_t l = strlen(reqname);211212for (i = 0;213i < sizeof reqnames / sizeof reqnames[0];214i++)215if (strncasecmp(reqname, reqnames[i], l) == 0)216return i;217218return strtoul(reqname, 0, 0);219}220221222static int223parse_req(int argc, char **argv)224{225int idx;226uint8_t rt = 0;227228for (idx = 0; argc != 0 && idx <= 6; argc--, idx++)229switch (idx)230{231case 0:232/* dir[ection]: i[n] | o[ut] */233if (*argv[idx] == 'i')234rt |= 0x80;235else if (*argv[idx] == 'o')236/* nop */;237else238{239fprintf(stderr, "request direction must be \"in\" or \"out\" (got %s)\n",240argv[idx]);241return -1;242}243break;244245case 1:246/* type: s[tandard] | c[lass] | v[endor] */247if (*argv[idx] == 's')248/* nop */;249else if (*argv[idx] == 'c')250rt |= 0x20;251else if (*argv[idx] == 'v')252rt |= 0x40;253else254{255fprintf(stderr,256"request type must be one of \"standard\", \"class\", or \"vendor\" (got %s)\n",257argv[idx]);258return -1;259}260break;261262case 2:263/* rcpt: d[evice], i[nterface], e[ndpoint], o[ther] */264if (*argv[idx] == 'd')265/* nop */;266else if (*argv[idx] == 'i')267rt |= 1;268else if (*argv[idx] == 'e')269rt |= 2;270else if (*argv[idx] == 'o')271rt |= 3;272else273{274fprintf(stderr,275"recipient must be one of \"device\", \"interface\", \"endpoint\", or \"other\" (got %s)\n",276argv[idx]);277return -1;278}279setup.bmRequestType = rt;280break;281282case 3:283setup.bRequest = get_req(argv[idx]);284break;285286case 4:287setup.wValue = strtoul(argv[idx], 0, 0);288break;289290case 5:291setup.wIndex = strtoul(argv[idx], 0, 0);292break;293294case 6:295setup.wLength = strtoul(argv[idx], 0, 0);296break;297}298299return argc;300}301302303int304main(int argc, char **argv)305{306unsigned int vid = UINT_MAX, pid = UINT_MAX; /* impossible VID:PID */307int c;308309/*310* Initialize setup struct. This step is required, and initializes311* internal fields in the struct.312*313* All the "public" fields are named exactly the way as the USB314* standard describes them, namely:315*316* setup.bmRequestType: bitmask, bit 7 is direction317* bits 6/5 is request type318* (standard, class, vendor)319* bits 4..0 is recipient320* (device, interface, endpoint,321* other)322* setup.bRequest: the request itself (see get_req() for standard323* requests, or specific value)324* setup.wValue: a 16-bit value325* setup.wIndex: another 16-bit value326* setup.wLength: length of associated data transfer, direction327* depends on bit 7 of bmRequestType328*/329LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup);330331while ((c = getopt(argc, argv, "i:p:v:")) != -1)332switch (c)333{334case 'i':335intr_ep = strtol(optarg, NULL, 0);336break;337338case 'p':339pid = strtol(optarg, NULL, 0);340break;341342case 'v':343vid = strtol(optarg, NULL, 0);344break;345346default:347usage();348break;349}350argc -= optind;351argv += optind;352353if (vid != UINT_MAX || pid != UINT_MAX)354{355if (intr_ep != 0 && (intr_ep & 0x80) == 0)356{357fprintf(stderr, "Interrupt endpoint must be of type IN\n");358usage();359}360361if (argc > 0)362{363do_request = true;364365int rv = parse_req(argc, argv);366if (rv < 0)367return EX_USAGE;368argc = rv;369370if (argc > 0)371{372for (out_len = 0; argc > 0 && out_len < BUFLEN; out_len++, argc--)373{374unsigned n = strtoul(argv[out_len], 0, 0);375if (n > 255)376fprintf(stderr,377"Warning: data #%d 0x%0x > 0xff, truncating\n",378out_len, n);379out_buf[out_len] = (uint8_t)n;380}381out_len++;382if (argc > 0)383fprintf(stderr,384"Data count exceeds maximum of %d, ignoring %d elements\n",385BUFLEN, optind);386}387}388}389390struct libusb20_backend *be;391struct libusb20_device *dev;392393if ((be = libusb20_be_alloc_default()) == NULL)394{395perror("libusb20_be_alloc()");396return 1;397}398399dev = NULL;400while ((dev = libusb20_be_device_foreach(be, dev)) != NULL)401{402struct LIBUSB20_DEVICE_DESC_DECODED *ddp =403libusb20_dev_get_device_desc(dev);404405printf("Found device %s (VID:PID = 0x%04x:0x%04x)\n",406libusb20_dev_get_desc(dev),407ddp->idVendor, ddp->idProduct);408409if (ddp->idVendor == vid && ddp->idProduct == pid)410doit(dev);411}412413libusb20_be_free(be);414return 0;415}416417418