xref: /freebsd/share/examples/libusb20/control.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /* ----------------------------------------------------------------------------
2  * "THE BEER-WARE LICENSE" (Revision 42) (by Poul-Henning Kamp):
3  * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
4  * can do whatever you want with this stuff. If we meet some day, and you think
5  * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
6  * ----------------------------------------------------------------------------
7  *
8  * $FreeBSD$
9  */
10 
11 /*
12  * Simple demo program to illustrate the handling of FreeBSD's
13  * libusb20.
14  */
15 
16 /*
17  * Examples:
18  * Just list all VID:PID pairs
19  * ./control
20  *
21  * Standard device request GET_STATUS, report two bytes of status
22  * (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 2
24  *
25  * Request input reports through the interrupt pipe from a mouse
26  * device (move the mouse around after issuing the command):
27  * ./control -v 0x093a -p 0x2516 -i 0x81
28  *
29  */
30 
31 
32 #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>
40 
41 #include <libusb20.h>
42 #include <libusb20_desc.h>
43 
44 #include <sys/queue.h>
45 
46 #include "util.h"
47 
48 /*
49  * If you want to see the details of the internal datastructures
50  * in the debugger, unifdef the following.
51  */
52 #ifdef DEBUG
53 #  include "/usr/src/lib/libusb/libusb20_int.h"
54 #endif
55 
56 #define BUFLEN 64
57 
58 #define TIMEOUT 5000 		/* 5 s */
59 
60 int intr_ep;		/* endpoints */
61 struct LIBUSB20_CONTROL_SETUP_DECODED setup;
62 
63 uint8_t out_buf[BUFLEN];
64 uint16_t out_len;
65 
66 bool do_request;
67 
68 static void
69 doit(struct libusb20_device *dev)
70 {
71   int rv;
72 
73   if (do_request)
74     printf("doit(): bmRequestType 0x%02x, bRequest 0x%02x, wValue 0x%04x, wIndex 0x%04x, wLength 0x%04x\n",
75 	   setup.bmRequestType,
76 	   setup.bRequest,
77 	   setup.wValue,
78 	   setup.wIndex,
79 	   setup.wLength);
80 
81   /*
82    * Open the device, allocating memory for two possible (bulk or
83    * interrupt) transfers.
84    *
85    * If only control transfers are intended (via
86    * libusb20_dev_request_sync()), transfer_max can be given as 0.
87    */
88   if ((rv = libusb20_dev_open(dev, 1)) != 0)
89     {
90       fprintf(stderr, "libusb20_dev_open: %s\n", libusb20_strerror(rv));
91       return;
92     }
93 
94   /*
95    * If the device has more than one configuration, select the desired
96    * one here.
97    */
98   if ((rv = libusb20_dev_set_config_index(dev, 0)) != 0)
99     {
100       fprintf(stderr, "libusb20_dev_set_config_index: %s\n", libusb20_strerror(rv));
101       return;
102     }
103 
104   uint8_t *data = 0;
105   uint16_t actlen;
106 
107   if ((setup.bmRequestType & 0x80) != 0)
108     {
109       /* this is an IN request, allocate a buffer */
110       data = malloc(setup.wLength);
111       if (data == 0)
112 	{
113 	  fprintf(stderr,
114 		  "Out of memory allocating %u bytes of reply buffer\n",
115 		  setup.wLength);
116 	  return;
117 	}
118     }
119   else
120     data = out_buf;
121 
122   if (do_request)
123     {
124       if ((rv = libusb20_dev_request_sync(dev, &setup, data,
125 					  &actlen,
126 					  TIMEOUT,
127 					  0 /* flags */)) != 0)
128 	{
129 	  fprintf(stderr,
130 		  "libusb20_dev_request_sync: %s\n", libusb20_strerror(rv));
131 	}
132       printf("sent %d bytes\n", actlen);
133       if ((setup.bmRequestType & 0x80) != 0)
134 	{
135 	  print_formatted(data, (uint32_t)setup.wLength);
136 	  free(data);
137 	}
138     }
139 
140   if (intr_ep != 0)
141     {
142       /*
143        * One transfer has been requested in libusb20_dev_open() above;
144        * obtain the corresponding transfer struct pointer.
145        */
146       struct libusb20_transfer *xfr_intr = libusb20_tr_get_pointer(dev, 0);
147 
148       if (xfr_intr == NULL)
149 	{
150 	  fprintf(stderr, "libusb20_tr_get_pointer: %s\n", libusb20_strerror(rv));
151 	  return;
152 	}
153 
154       /*
155        * Open the interrupt transfer.
156        */
157       if ((rv = libusb20_tr_open(xfr_intr, 0, 1, intr_ep)) != 0)
158 	{
159 	  fprintf(stderr, "libusb20_tr_open: %s\n", libusb20_strerror(rv));
160 	  return;
161 	}
162 
163       uint8_t in_buf[BUFLEN];
164       uint32_t rlen;
165 
166       if ((rv = libusb20_tr_bulk_intr_sync(xfr_intr, in_buf, BUFLEN, &rlen, TIMEOUT))
167 	  != 0)
168 	{
169 	  fprintf(stderr, "libusb20_tr_bulk_intr_sync: %s\n", libusb20_strerror(rv));
170 	}
171       printf("received %d bytes\n", rlen);
172       if (rlen > 0)
173 	print_formatted(in_buf, rlen);
174 
175       libusb20_tr_close(xfr_intr);
176     }
177 
178   libusb20_dev_close(dev);
179 }
180 
181 static void
182 usage(void)
183 {
184   fprintf(stderr,
185 	  "Usage ./usb [-i <INTR_EP>] -v <VID> -p <PID> [dir type rcpt req wValue wIndex wLength [<outdata> ...]]\n");
186   exit(EX_USAGE);
187 }
188 
189 static 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 };
205 
206 static int
207 get_req(const char *reqname)
208 {
209   size_t i;
210   size_t l = strlen(reqname);
211 
212   for (i = 0;
213        i < sizeof reqnames / sizeof reqnames[0];
214        i++)
215     if (strncasecmp(reqname, reqnames[i], l) == 0)
216       return i;
217 
218   return strtoul(reqname, 0, 0);
219 }
220 
221 
222 static int
223 parse_req(int argc, char **argv)
224 {
225   int idx;
226   uint8_t rt = 0;
227 
228   for (idx = 0; argc != 0 && idx <= 6; argc--, idx++)
229     switch (idx)
230       {
231       case 0:
232 	/* dir[ection]: i[n] | o[ut] */
233 	if (*argv[idx] == 'i')
234 	  rt |= 0x80;
235 	else if (*argv[idx] == 'o')
236 	  /* nop */;
237 	else
238 	  {
239 	    fprintf(stderr, "request direction must be \"in\" or \"out\" (got %s)\n",
240 		    argv[idx]);
241 	    return -1;
242 	  }
243 	break;
244 
245       case 1:
246 	/* type: s[tandard] | c[lass] | v[endor] */
247 	if (*argv[idx] == 's')
248 	  /* nop */;
249 	else if (*argv[idx] == 'c')
250 	  rt |= 0x20;
251 	else if (*argv[idx] == 'v')
252 	  rt |= 0x40;
253 	else
254 	  {
255 	    fprintf(stderr,
256 		    "request type must be one of \"standard\", \"class\", or \"vendor\" (got %s)\n",
257 		    argv[idx]);
258 	    return -1;
259 	  }
260 	break;
261 
262       case 2:
263 	/* rcpt: d[evice], i[nterface], e[ndpoint], o[ther] */
264 	if (*argv[idx] == 'd')
265 	  /* nop */;
266 	else if (*argv[idx] == 'i')
267 	  rt |= 1;
268 	else if (*argv[idx] == 'e')
269 	  rt |= 2;
270 	else if (*argv[idx] == 'o')
271 	  rt |= 3;
272 	else
273 	  {
274 	    fprintf(stderr,
275 		    "recipient must be one of \"device\", \"interface\", \"endpoint\", or \"other\" (got %s)\n",
276 		    argv[idx]);
277 	    return -1;
278 	  }
279 	setup.bmRequestType = rt;
280 	break;
281 
282       case 3:
283 	setup.bRequest = get_req(argv[idx]);
284 	break;
285 
286       case 4:
287 	setup.wValue = strtoul(argv[idx], 0, 0);
288 	break;
289 
290       case 5:
291 	setup.wIndex = strtoul(argv[idx], 0, 0);
292 	break;
293 
294       case 6:
295 	setup.wLength = strtoul(argv[idx], 0, 0);
296 	break;
297       }
298 
299   return argc;
300 }
301 
302 
303 int
304 main(int argc, char **argv)
305 {
306   unsigned int vid = UINT_MAX, pid = UINT_MAX; /* impossible VID:PID */
307   int c;
308 
309   /*
310    * Initialize setup struct.  This step is required, and initializes
311    * internal fields in the struct.
312    *
313    * All the "public" fields are named exactly the way as the USB
314    * standard describes them, namely:
315    *
316    *	setup.bmRequestType: bitmask, bit 7 is direction
317    *	                              bits 6/5 is request type
318    *	                                       (standard, class, vendor)
319    *	                              bits 4..0 is recipient
320    *	                                       (device, interface, endpoint,
321    *	                                        other)
322    *	setup.bRequest:      the request itself (see get_req() for standard
323    *	                                         requests, or specific value)
324    *	setup.wValue:        a 16-bit value
325    *	setup.wIndex:        another 16-bit value
326    *	setup.wLength:       length of associated data transfer, direction
327    *	                     depends on bit 7 of bmRequestType
328    */
329   LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup);
330 
331   while ((c = getopt(argc, argv, "i:p:v:")) != -1)
332     switch (c)
333       {
334       case 'i':
335 	intr_ep = strtol(optarg, NULL, 0);
336 	break;
337 
338       case 'p':
339 	pid = strtol(optarg, NULL, 0);
340 	break;
341 
342       case 'v':
343 	vid = strtol(optarg, NULL, 0);
344 	break;
345 
346       default:
347 	usage();
348 	break;
349       }
350   argc -= optind;
351   argv += optind;
352 
353   if (vid != UINT_MAX || pid != UINT_MAX)
354     {
355       if (intr_ep != 0 && (intr_ep & 0x80) == 0)
356 	{
357 	  fprintf(stderr, "Interrupt endpoint must be of type IN\n");
358 	  usage();
359 	}
360 
361       if (argc > 0)
362 	{
363 	  do_request = true;
364 
365 	  int rv = parse_req(argc, argv);
366 	  if (rv < 0)
367 	    return EX_USAGE;
368 	  argc = rv;
369 
370 	  if (argc > 0)
371 	    {
372 	      for (out_len = 0; argc > 0 && out_len < BUFLEN; out_len++, argc--)
373 		{
374 		  unsigned n = strtoul(argv[out_len], 0, 0);
375 		  if (n > 255)
376 		    fprintf(stderr,
377 			    "Warning: data #%d 0x%0x > 0xff, truncating\n",
378 			    out_len, n);
379 		  out_buf[out_len] = (uint8_t)n;
380 		}
381 	      out_len++;
382 	      if (argc > 0)
383 		fprintf(stderr,
384 			"Data count exceeds maximum of %d, ignoring %d elements\n",
385 			BUFLEN, optind);
386 	    }
387 	}
388     }
389 
390   struct libusb20_backend *be;
391   struct libusb20_device *dev;
392 
393   if ((be = libusb20_be_alloc_default()) == NULL)
394     {
395       perror("libusb20_be_alloc()");
396       return 1;
397     }
398 
399   dev = NULL;
400   while ((dev = libusb20_be_device_foreach(be, dev)) != NULL)
401     {
402       struct LIBUSB20_DEVICE_DESC_DECODED *ddp =
403       libusb20_dev_get_device_desc(dev);
404 
405       printf("Found device %s (VID:PID = 0x%04x:0x%04x)\n",
406 	     libusb20_dev_get_desc(dev),
407 	     ddp->idVendor, ddp->idProduct);
408 
409       if (ddp->idVendor == vid && ddp->idProduct == pid)
410 	doit(dev);
411     }
412 
413   libusb20_be_free(be);
414   return 0;
415 }
416