2
* @brief usbhid-dump - entry point
4
* Copyright (C) 2010 Nikolai Kondrashov
6
* This file is part of usbhid-dump.
8
* Usbhid-dump is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* Usbhid-dump is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with usbhid-dump; if not, write to the Free Software
20
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
* @author Nikolai Kondrashov <spbnick@gmail.com>
27
#include "usbhid_dump/iface.h"
28
#include "usbhid_dump/str.h"
29
#include "usbhid_dump/libusb.h"
44
* Maximum descriptor size.
46
* 4096 here is maximum control buffer length.
48
#define MAX_DESCRIPTOR_SIZE 4096
55
#define ERROR(_fmt, _args...) \
56
fprintf(stderr, _fmt "\n", ##_args)
58
#define FAILURE(_fmt, _args...) \
59
fprintf(stderr, "Failed to " _fmt "\n", ##_args)
61
#define LIBUSB_FAILURE(_fmt, _args...) \
62
FAILURE(_fmt ": %s", ##_args, libusb_strerror(err))
64
#define ERROR_CLEANUP(_fmt, _args...) \
66
ERROR(_fmt, ##_args); \
70
#define LIBUSB_ERROR_CLEANUP(_fmt, _args...) \
71
ERROR_CLEANUP(_fmt ": %s", ##_args, libusb_strerror(err))
73
#define FAILURE_CLEANUP(_fmt, _args...) \
74
ERROR_CLEANUP("Failed to " _fmt, ##_args)
76
#define LIBUSB_FAILURE_CLEANUP(_fmt, _args...) \
77
LIBUSB_ERROR_CLEANUP("Failed to " _fmt, ##_args)
79
/**< Number of the signal causing the exit */
80
static volatile sig_atomic_t exit_signum = 0;
83
exit_sighandler(int signum)
89
/**< "Stream paused" flag - non-zero if paused */
90
static volatile sig_atomic_t stream_paused = 0;
93
stream_pause_sighandler(int signum)
100
stream_resume_sighandler(int signum)
106
/**< "Stream feedback" flag - non-zero if feedback is enabled */
107
static volatile sig_atomic_t stream_feedback = 0;
110
usage(FILE *stream, const char *progname)
115
"Usage: %s [OPTION]... <bus> <dev> [if]\n"
116
"Dump a USB device HID report descriptor(s) and/or stream(s)."
119
" bus bus number (1-255)\n"
120
" dev device address (1-255)\n"
121
" if interface number (0-254);\n"
122
" if omitted, all device HID interfaces\n"
126
" -h, --help this help message\n"
127
" -e, --entity=STRING what to dump: either \"descriptor\",\n"
128
" \"stream\" or \"both\"; value can be\n"
130
" -p, --stream-paused start with the stream dump output paused\n"
131
" -f, --stream-feedback enable stream dumping feedback: for every\n"
132
" transfer dumped a dot is printed to stderr\n"
134
"Default options: --entity=descriptor\n"
137
" USR1/USR2 pause/resume the stream dump output\n"
144
dump(uint8_t iface_num,
149
static const char xd[] = "0123456789ABCDEF";
150
static char buf[] = " XX\n";
155
gettimeofday(&tv, NULL);
157
fprintf(stdout, "%.3hhu:%-16s %20llu.%.6u\n",
159
(unsigned long long int)tv.tv_sec,
160
(unsigned int)tv.tv_usec);
162
for (pos = 1; len > 0; len--, ptr++, pos++)
166
buf[2] = xd[b & 0xF];
168
fwrite(buf, ((pos % 16 == 0) ? 4 : 3), 1, stdout);
180
dump_iface_list_descriptor(const usbhid_dump_iface *list)
182
const usbhid_dump_iface *iface;
183
uint8_t buf[MAX_DESCRIPTOR_SIZE];
185
enum libusb_error err;
187
for (iface = list; iface != NULL; iface = iface->next)
189
rc = libusb_control_transfer(iface->handle,
190
/* See HID spec, 7.1.1 */
192
LIBUSB_REQUEST_GET_DESCRIPTOR,
193
(LIBUSB_DT_REPORT << 8), iface->number,
194
buf, sizeof(buf), TIMEOUT);
198
LIBUSB_FAILURE("retrieve interface #%hhu "
199
"report descriptor", iface->number);
202
dump(iface->number, "DESCRIPTOR", buf, rc);
210
dump_iface_list_stream_cb(struct libusb_transfer *transfer)
212
enum libusb_error err;
213
usbhid_dump_iface *iface;
215
assert(transfer != NULL);
217
iface = (usbhid_dump_iface *)transfer->user_data;
218
assert(usbhid_dump_iface_valid(iface));
220
/* Clear interface "has transfer submitted" flag */
221
iface->submitted = false;
223
switch (transfer->status)
225
case LIBUSB_TRANSFER_COMPLETED:
226
/* Dump the result */
229
dump(iface->number, "STREAM",
230
transfer->buffer, transfer->actual_length);
234
/* Resubmit the transfer */
235
err = libusb_submit_transfer(transfer);
236
if (err != LIBUSB_SUCCESS)
237
LIBUSB_FAILURE("resubmit a transfer");
238
/* Set interface "has transfer submitted" flag */
239
iface->submitted = true;
242
case LIBUSB_TRANSFER_##_name: \
243
fprintf(stderr, "%.3hhu:%s\n", iface->number, #_name); \
260
dump_iface_list_stream(libusb_context *ctx, usbhid_dump_iface *list)
263
enum libusb_error err;
264
size_t transfer_num = 0;
265
struct libusb_transfer **transfer_list = NULL;
266
struct libusb_transfer **ptransfer;
267
usbhid_dump_iface *iface;
268
bool submitted = false;
270
/* Set report protocol on all interfaces */
271
err = usbhid_dump_iface_list_set_protocol(list, true, TIMEOUT);
272
if (err != LIBUSB_SUCCESS)
273
LIBUSB_ERROR_CLEANUP("set report protocol");
275
/* Set infinite idle duration on all interfaces */
276
err = usbhid_dump_iface_list_set_idle(list, 0, TIMEOUT);
277
if (err != LIBUSB_SUCCESS)
278
LIBUSB_ERROR_CLEANUP("set infinite idle duration");
280
/* Calculate number of interfaces and thus transfers */
281
transfer_num = usbhid_dump_iface_list_len(list);
283
/* Allocate transfer list */
284
transfer_list = malloc(sizeof(*transfer_list) * transfer_num);
285
if (transfer_list == NULL)
286
FAILURE_CLEANUP("allocate transfer list");
288
/* Zero transfer list */
289
for (ptransfer = transfer_list;
290
(size_t)(ptransfer - transfer_list) < transfer_num;
294
/* Allocate transfers */
295
for (ptransfer = transfer_list;
296
(size_t)(ptransfer - transfer_list) < transfer_num;
299
*ptransfer = libusb_alloc_transfer(0);
300
if (*ptransfer == NULL)
301
FAILURE_CLEANUP("allocate a transfer");
303
* Set user_data to NULL explicitly, since libusb_alloc_transfer
304
* does memset to zero only and zero is not NULL, strictly speaking.
306
(*ptransfer)->user_data = NULL;
309
/* Initialize the transfers as interrupt transfers */
310
for (ptransfer = transfer_list, iface = list;
311
(size_t)(ptransfer - transfer_list) < transfer_num;
312
ptransfer++, iface = iface->next)
315
const size_t len = iface->int_in_ep_maxp;
317
/* Allocate the transfer buffer */
319
if (len > 0 && buf == NULL)
320
FAILURE_CLEANUP("allocate a transfer buffer");
322
/* Initialize the transfer */
323
libusb_fill_interrupt_transfer(*ptransfer,
324
iface->handle, iface->int_in_ep_addr,
326
dump_iface_list_stream_cb,
328
/* Infinite timeout */
331
/* Ask to free the buffer when the transfer is freed */
332
(*ptransfer)->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
335
/* Submit first transfer requests */
336
for (ptransfer = transfer_list;
337
(size_t)(ptransfer - transfer_list) < transfer_num;
340
err = libusb_submit_transfer(*ptransfer);
341
if (err != LIBUSB_SUCCESS)
342
LIBUSB_ERROR_CLEANUP("submit a transfer");
343
/* Set interface "has transfer submitted" flag */
344
((usbhid_dump_iface *)(*ptransfer)->user_data)->submitted = true;
345
/* Set "have any submitted transfers" flag */
349
/* Run the event machine */
350
while (submitted && exit_signum == 0)
352
/* Handle the transfer events */
353
err = libusb_handle_events(ctx);
354
if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_INTERRUPTED)
355
LIBUSB_FAILURE_CLEANUP("handle transfer events");
357
/* Check if there are any submitted transfers left */
359
for (ptransfer = transfer_list;
360
(size_t)(ptransfer - transfer_list) < transfer_num;
363
iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
365
if (iface != NULL && iface->submitted)
370
/* If all the transfers were terminated unexpectedly */
371
if (transfer_num > 0 && !submitted)
372
ERROR_CLEANUP("No more interfaces to dump");
378
/* Cancel the transfers */
382
for (ptransfer = transfer_list;
383
(size_t)(ptransfer - transfer_list) < transfer_num;
386
iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
388
if (iface != NULL && iface->submitted)
390
err = libusb_cancel_transfer(*ptransfer);
391
if (err == LIBUSB_SUCCESS)
395
LIBUSB_FAILURE("cancel a transfer, ignoring");
397
* XXX are we really sure
398
* the transfer won't be finished?
400
iface->submitted = false;
406
/* Wait for transfer cancellation */
409
/* Handle cancellation events */
410
err = libusb_handle_events(ctx);
411
if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_INTERRUPTED)
413
LIBUSB_FAILURE("handle transfer cancellation events, "
414
"aborting transfer cancellation");
418
/* Check if there are any submitted transfers left */
420
for (ptransfer = transfer_list;
421
(size_t)(ptransfer - transfer_list) < transfer_num;
424
iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
426
if (iface != NULL && iface->submitted)
432
* Free transfer list along with non-submitted transfers and their
435
if (transfer_list != NULL)
437
for (ptransfer = transfer_list;
438
(size_t)(ptransfer - transfer_list) < transfer_num;
441
iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
444
* Only free a transfer if it is not submitted. Better leak some
445
* memory than have some important memory overwritten.
447
if (iface == NULL || !iface->submitted)
448
libusb_free_transfer(*ptransfer);
459
run(bool dump_descriptor,
466
enum libusb_error err;
467
libusb_context *ctx = NULL;
468
libusb_device_handle *handle = NULL;
469
usbhid_dump_iface *iface_list = NULL;
471
/* Initialize libusb context */
472
err = libusb_init(&ctx);
473
if (err != LIBUSB_SUCCESS)
474
LIBUSB_FAILURE_CLEANUP("create libusb context");
476
/* Set libusb debug level */
477
libusb_set_debug(ctx, 3);
479
/* Find and open the device */
480
err = libusb_open_device_with_bus_dev(ctx, bus_num, dev_addr, &handle);
481
if (err != LIBUSB_SUCCESS)
482
LIBUSB_FAILURE_CLEANUP("find and open the device");
484
/* Retrieve the list of HID interfaces from a device */
485
err = usbhid_dump_iface_list_new_from_dev(handle, &iface_list);
486
if (err != LIBUSB_SUCCESS)
487
LIBUSB_FAILURE_CLEANUP("find a HID interface");
489
/* Filter the interface list by specified interface number */
490
iface_list = usbhid_dump_iface_list_fltr_by_num(iface_list, iface_num);
491
if (usbhid_dump_iface_list_empty(iface_list))
492
ERROR_CLEANUP("No matching HID interfaces");
494
/* Detach interfaces */
495
err = usbhid_dump_iface_list_detach(iface_list);
496
if (err != LIBUSB_SUCCESS)
497
LIBUSB_FAILURE_CLEANUP("detach the interface(s) from "
498
"the kernel drivers");
500
/* Claim interfaces */
501
err = usbhid_dump_iface_list_claim(iface_list);
502
if (err != LIBUSB_SUCCESS)
503
LIBUSB_FAILURE_CLEANUP("claim the interface(s)");
505
/* Run with the prepared interface list */
506
result = (!dump_descriptor || dump_iface_list_descriptor(iface_list)) &&
507
(!dump_stream || dump_iface_list_stream(ctx, iface_list))
513
/* Release the interfaces back */
514
err = usbhid_dump_iface_list_release(iface_list);
515
if (err != LIBUSB_SUCCESS)
516
LIBUSB_FAILURE("release the interface(s)");
518
/* Attach interfaces back */
519
err = usbhid_dump_iface_list_attach(iface_list);
520
if (err != LIBUSB_SUCCESS)
521
LIBUSB_FAILURE("attach the interface(s) to the kernel drivers");
523
/* Free the interface list */
524
usbhid_dump_iface_list_free(iface_list);
526
/* Free the device */
528
libusb_close(handle);
530
/* Cleanup libusb context, if necessary */
538
typedef enum opt_val {
540
OPT_VAL_ENTITY = 'e',
541
OPT_VAL_STREAM_PAUSED = 'p',
542
OPT_VAL_STREAM_FEEDBACK = 'f',
547
main(int argc, char **argv)
549
static const struct option long_opt_list[] = {
550
{.val = OPT_VAL_HELP,
552
.has_arg = no_argument,
554
{.val = OPT_VAL_ENTITY,
556
.has_arg = required_argument,
558
{.val = OPT_VAL_STREAM_PAUSED,
559
.name = "stream-paused",
560
.has_arg = no_argument,
562
{.val = OPT_VAL_STREAM_FEEDBACK,
563
.name = "stream-feedback",
564
.has_arg = no_argument,
572
static const char *short_opt_list = "he:pf";
579
const char *if_str = "";
581
const char **arg_list[] = {&bus_str, &dev_str, &if_str};
582
const size_t req_arg_num = 2;
583
const size_t max_arg_num = 3;
587
bool dump_descriptor = true;
588
bool dump_stream = false;
595
#define USAGE_ERROR(_fmt, _args...) \
597
fprintf(stderr, _fmt "\n", ##_args); \
598
usage(stderr, program_invocation_short_name); \
603
* Parse command line arguments
605
while ((c = getopt_long(argc, argv,
606
short_opt_list, long_opt_list, NULL)) >= 0)
611
usage(stdout, program_invocation_short_name);
615
if (strncmp(optarg, "descriptor", strlen(optarg)) == 0)
617
dump_descriptor = true;
620
else if (strncmp(optarg, "stream", strlen(optarg)) == 0)
622
dump_descriptor = false;
625
else if (strncmp(optarg, "both", strlen(optarg)) == 0)
627
dump_descriptor = true;
631
USAGE_ERROR("Unknown entity \"%s\"", optarg);
634
case OPT_VAL_STREAM_PAUSED:
637
case OPT_VAL_STREAM_FEEDBACK:
641
usage(stderr, program_invocation_short_name);
648
* Assign positional parameters
650
for (i = 0; i < max_arg_num && optind < argc; i++, optind++)
651
*arg_list[i] = argv[optind];
654
USAGE_ERROR("Not enough arguments");
657
* Parse and verify positional parameters
660
bus_num = strtol(bus_str, &end, 0);
661
if (errno != 0 || !usbhid_dump_strisblank(end) ||
662
bus_num <= 0 || bus_num > 255)
663
USAGE_ERROR("Invalid bus number \"%s\"", bus_str);
666
dev_num = strtol(dev_str, &end, 0);
667
if (errno != 0 || !usbhid_dump_strisblank(end) ||
668
dev_num <= 0 || dev_num > 255)
669
USAGE_ERROR("Invalid device address \"%s\"", dev_str);
671
if (!usbhid_dump_strisblank(if_str))
674
if_num = strtol(if_str, &end, 0);
675
if (errno != 0 || !usbhid_dump_strisblank(end) ||
676
if_num < 0 || if_num >= 255)
677
USAGE_ERROR("Invalid interface number \"%s\"", if_str);
681
* Setup signal handlers
683
/* Setup SIGINT to terminate gracefully */
684
sigaction(SIGINT, NULL, &sa);
685
if (sa.sa_handler != SIG_IGN)
687
sa.sa_handler = exit_sighandler;
688
sigemptyset(&sa.sa_mask);
689
sigaddset(&sa.sa_mask, SIGTERM);
690
sa.sa_flags = 0; /* NOTE: no SA_RESTART on purpose */
691
sigaction(SIGINT, &sa, NULL);
694
/* Setup SIGTERM to terminate gracefully */
695
sigaction(SIGTERM, NULL, &sa);
696
if (sa.sa_handler != SIG_IGN)
698
sa.sa_handler = exit_sighandler;
699
sigemptyset(&sa.sa_mask);
700
sigaddset(&sa.sa_mask, SIGINT);
701
sa.sa_flags = 0; /* NOTE: no SA_RESTART on purpose */
702
sigaction(SIGTERM, &sa, NULL);
705
/* Setup SIGUSR1/SIGUSR2 to pause/resume the stream output */
706
sigemptyset(&sa.sa_mask);
707
sa.sa_flags = SA_RESTART;
708
sa.sa_handler = stream_pause_sighandler;
709
sigaction(SIGUSR1, &sa, NULL);
710
sa.sa_handler = stream_resume_sighandler;
711
sigaction(SIGUSR2, &sa, NULL);
713
/* Make stdout buffered - we will flush it explicitly */
714
setbuf(stdout, NULL);
716
result = run(dump_descriptor, dump_stream, bus_num, dev_num, if_num);
719
* Restore signal handlers
721
sigaction(SIGINT, NULL, &sa);
722
if (sa.sa_handler != SIG_IGN)
723
signal(SIGINT, SIG_DFL);
725
sigaction(SIGTERM, NULL, &sa);
726
if (sa.sa_handler != SIG_IGN)
727
signal(SIGTERM, SIG_DFL);
730
* Reproduce the signal used to stop the program to get proper exit
733
if (exit_signum != 0)