~ubuntu-branches/ubuntu/precise/usbutils/precise

« back to all changes in this revision

Viewing changes to usbhid-dump/src/usbhid-dump.c

  • Committer: Bazaar Package Importer
  • Author(s): Aurelien Jarno
  • Date: 2011-02-06 13:32:44 UTC
  • mfrom: (2.1.14 sid)
  • Revision ID: james.westby@ubuntu.com-20110206133244-9111gklgz7vuygd7
Tags: 1:001-1
* New upstream version, with new version numbering system.
* Switch build-dependencies from libusb-dev to libusb-1.0-0-dev.
* Update usb.ids.
* Fix 13fe:3100 entry, 4GB versions of the device also exists.  
  Closes: bug#599817.
* Bumped Standards-Version to 3.9.1 (no changes).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 * @brief usbhid-dump - entry point
 
3
 *
 
4
 * Copyright (C) 2010 Nikolai Kondrashov
 
5
 *
 
6
 * This file is part of usbhid-dump.
 
7
 *
 
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.
 
12
 *
 
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.
 
17
 *
 
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
 
21
 *
 
22
 * @author Nikolai Kondrashov <spbnick@gmail.com>
 
23
 *
 
24
 * @(#) $Id$
 
25
 */
 
26
 
 
27
#include "usbhid_dump/iface.h"
 
28
#include "usbhid_dump/str.h"
 
29
#include "usbhid_dump/libusb.h"
 
30
 
 
31
#include <assert.h>
 
32
#include <stdbool.h>
 
33
#include <ctype.h>
 
34
#include <string.h>
 
35
#include <errno.h>
 
36
#include <signal.h>
 
37
#include <stdlib.h>
 
38
#include <unistd.h>
 
39
#include <getopt.h>
 
40
#include <stdio.h>
 
41
#include <libusb.h>
 
42
 
 
43
/**
 
44
 * Maximum descriptor size.
 
45
 *
 
46
 * 4096 here is maximum control buffer length.
 
47
 */
 
48
#define MAX_DESCRIPTOR_SIZE 4096
 
49
 
 
50
/**
 
51
 * USB I/O timeout
 
52
 */
 
53
#define TIMEOUT 1000
 
54
 
 
55
#define ERROR(_fmt, _args...) \
 
56
    fprintf(stderr, _fmt "\n", ##_args)
 
57
 
 
58
#define FAILURE(_fmt, _args...) \
 
59
    fprintf(stderr, "Failed to " _fmt "\n", ##_args)
 
60
 
 
61
#define LIBUSB_FAILURE(_fmt, _args...) \
 
62
    FAILURE(_fmt ": %s", ##_args, libusb_strerror(err))
 
63
 
 
64
#define ERROR_CLEANUP(_fmt, _args...) \
 
65
    do {                                \
 
66
        ERROR(_fmt, ##_args);           \
 
67
        goto cleanup;                   \
 
68
    } while (0)
 
69
 
 
70
#define LIBUSB_ERROR_CLEANUP(_fmt, _args...) \
 
71
    ERROR_CLEANUP(_fmt ": %s", ##_args, libusb_strerror(err))
 
72
 
 
73
#define FAILURE_CLEANUP(_fmt, _args...) \
 
74
    ERROR_CLEANUP("Failed to " _fmt, ##_args)
 
75
 
 
76
#define LIBUSB_FAILURE_CLEANUP(_fmt, _args...) \
 
77
    LIBUSB_ERROR_CLEANUP("Failed to " _fmt, ##_args)
 
78
 
 
79
/**< Number of the signal causing the exit */
 
80
static volatile sig_atomic_t exit_signum  = 0;
 
81
 
 
82
static void
 
83
exit_sighandler(int signum)
 
84
{
 
85
    if (exit_signum == 0)
 
86
        exit_signum = signum;
 
87
}
 
88
 
 
89
/**< "Stream paused" flag - non-zero if paused */
 
90
static volatile sig_atomic_t stream_paused = 0;
 
91
 
 
92
static void
 
93
stream_pause_sighandler(int signum)
 
94
{
 
95
    (void)signum;
 
96
    stream_paused = 1;
 
97
}
 
98
 
 
99
static void
 
100
stream_resume_sighandler(int signum)
 
101
{
 
102
    (void)signum;
 
103
    stream_paused = 0;
 
104
}
 
105
 
 
106
/**< "Stream feedback" flag - non-zero if feedback is enabled */
 
107
static volatile sig_atomic_t stream_feedback = 0;
 
108
 
 
109
static bool
 
110
usage(FILE *stream, const char *progname)
 
111
{
 
112
    return
 
113
        fprintf(
 
114
            stream,
 
115
"Usage: %s [OPTION]... <bus> <dev> [if]\n"
 
116
"Dump a USB device HID report descriptor(s) and/or stream(s)."
 
117
"\n"
 
118
"Arguments:\n"
 
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"
 
123
"                           are dumped\n"
 
124
"\n"
 
125
"Options:\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"
 
129
"                           abbreviated\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"
 
133
"\n"
 
134
"Default options: --entity=descriptor\n"
 
135
"\n"
 
136
"Signals:\n"
 
137
"  USR1/USR2                pause/resume the stream dump output\n"
 
138
"\n",
 
139
            progname) >= 0;
 
140
}
 
141
 
 
142
 
 
143
static void
 
144
dump(uint8_t        iface_num,
 
145
     const char    *entity,
 
146
     const uint8_t *ptr,
 
147
     size_t         len)
 
148
{
 
149
    static const char   xd[]    = "0123456789ABCDEF";
 
150
    static char         buf[]   = " XX\n";
 
151
    size_t              pos;
 
152
    uint8_t             b;
 
153
    struct timeval      tv;
 
154
 
 
155
    gettimeofday(&tv, NULL);
 
156
 
 
157
    fprintf(stdout, "%.3hhu:%-16s %20llu.%.6u\n",
 
158
            iface_num, entity,
 
159
            (unsigned long long int)tv.tv_sec,
 
160
            (unsigned int)tv.tv_usec);
 
161
 
 
162
    for (pos = 1; len > 0; len--, ptr++, pos++)
 
163
    {
 
164
        b = *ptr;
 
165
        buf[1] = xd[b >> 4];
 
166
        buf[2] = xd[b & 0xF];
 
167
 
 
168
        fwrite(buf, ((pos % 16 == 0) ? 4 : 3), 1, stdout);
 
169
    }
 
170
 
 
171
    if (pos % 16 != 1)
 
172
        fputc('\n', stdout);
 
173
    fputc('\n', stdout);
 
174
 
 
175
    fflush(stdout);
 
176
}
 
177
 
 
178
 
 
179
static bool
 
180
dump_iface_list_descriptor(const usbhid_dump_iface *list)
 
181
{
 
182
    const usbhid_dump_iface    *iface;
 
183
    uint8_t                     buf[MAX_DESCRIPTOR_SIZE];
 
184
    int                         rc;
 
185
    enum libusb_error           err;
 
186
 
 
187
    for (iface = list; iface != NULL; iface = iface->next)
 
188
    {
 
189
        rc = libusb_control_transfer(iface->handle,
 
190
                                     /* See HID spec, 7.1.1 */
 
191
                                     0x81,
 
192
                                     LIBUSB_REQUEST_GET_DESCRIPTOR,
 
193
                                     (LIBUSB_DT_REPORT << 8), iface->number,
 
194
                                     buf, sizeof(buf), TIMEOUT);
 
195
        if (rc < 0)
 
196
        {
 
197
            err = rc;
 
198
            LIBUSB_FAILURE("retrieve interface #%hhu "
 
199
                           "report descriptor", iface->number);
 
200
            return false;
 
201
        }
 
202
        dump(iface->number, "DESCRIPTOR", buf, rc);
 
203
    }
 
204
 
 
205
    return true;
 
206
}
 
207
 
 
208
 
 
209
static void
 
210
dump_iface_list_stream_cb(struct libusb_transfer *transfer)
 
211
{
 
212
    enum libusb_error   err;
 
213
    usbhid_dump_iface  *iface;
 
214
 
 
215
    assert(transfer != NULL);
 
216
 
 
217
    iface = (usbhid_dump_iface *)transfer->user_data;
 
218
    assert(usbhid_dump_iface_valid(iface));
 
219
 
 
220
    /* Clear interface "has transfer submitted" flag */
 
221
    iface->submitted = false;
 
222
 
 
223
    switch (transfer->status)
 
224
    {
 
225
        case LIBUSB_TRANSFER_COMPLETED:
 
226
            /* Dump the result */
 
227
            if (!stream_paused)
 
228
            {
 
229
                dump(iface->number, "STREAM",
 
230
                     transfer->buffer, transfer->actual_length);
 
231
                if (stream_feedback)
 
232
                    fputc('.', stderr);
 
233
            }
 
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;
 
240
            break;
 
241
#define MAP(_name) \
 
242
    case LIBUSB_TRANSFER_##_name:                               \
 
243
        fprintf(stderr, "%.3hhu:%s\n", iface->number, #_name);  \
 
244
        break
 
245
 
 
246
        MAP(ERROR);
 
247
        MAP(TIMED_OUT);
 
248
        MAP(STALL);
 
249
        MAP(NO_DEVICE);
 
250
        MAP(OVERFLOW);
 
251
 
 
252
#undef MAP
 
253
        default:
 
254
            break;
 
255
    }
 
256
}
 
257
 
 
258
 
 
259
static bool
 
260
dump_iface_list_stream(libusb_context *ctx, usbhid_dump_iface *list)
 
261
{
 
262
    bool                        result              = false;
 
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;
 
269
 
 
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");
 
274
 
 
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");
 
279
 
 
280
    /* Calculate number of interfaces and thus transfers */
 
281
    transfer_num = usbhid_dump_iface_list_len(list);
 
282
 
 
283
    /* Allocate transfer list */
 
284
    transfer_list = malloc(sizeof(*transfer_list) * transfer_num);
 
285
    if (transfer_list == NULL)
 
286
        FAILURE_CLEANUP("allocate transfer list");
 
287
 
 
288
    /* Zero transfer list */
 
289
    for (ptransfer = transfer_list;
 
290
         (size_t)(ptransfer - transfer_list) < transfer_num;
 
291
         ptransfer++)
 
292
        *ptransfer = NULL;
 
293
 
 
294
    /* Allocate transfers */
 
295
    for (ptransfer = transfer_list;
 
296
         (size_t)(ptransfer - transfer_list) < transfer_num;
 
297
         ptransfer++)
 
298
    {
 
299
        *ptransfer = libusb_alloc_transfer(0);
 
300
        if (*ptransfer == NULL)
 
301
            FAILURE_CLEANUP("allocate a transfer");
 
302
        /*
 
303
         * Set user_data to NULL explicitly, since libusb_alloc_transfer
 
304
         * does memset to zero only and zero is not NULL, strictly speaking.
 
305
         */
 
306
        (*ptransfer)->user_data = NULL;
 
307
    }
 
308
 
 
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)
 
313
    {
 
314
        void           *buf;
 
315
        const size_t    len = iface->int_in_ep_maxp;
 
316
 
 
317
        /* Allocate the transfer buffer */
 
318
        buf = malloc(len);   
 
319
        if (len > 0 && buf == NULL)
 
320
            FAILURE_CLEANUP("allocate a transfer buffer");
 
321
 
 
322
        /* Initialize the transfer */
 
323
        libusb_fill_interrupt_transfer(*ptransfer,
 
324
                                       iface->handle, iface->int_in_ep_addr,
 
325
                                       buf, len,
 
326
                                       dump_iface_list_stream_cb,
 
327
                                       (void *)iface,
 
328
                                       /* Infinite timeout */
 
329
                                       0);
 
330
 
 
331
        /* Ask to free the buffer when the transfer is freed */
 
332
        (*ptransfer)->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
 
333
    }
 
334
 
 
335
    /* Submit first transfer requests */
 
336
    for (ptransfer = transfer_list;
 
337
         (size_t)(ptransfer - transfer_list) < transfer_num;
 
338
         ptransfer++)
 
339
    {
 
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 */
 
346
        submitted = true;
 
347
    }
 
348
 
 
349
    /* Run the event machine */
 
350
    while (submitted && exit_signum == 0)
 
351
    {
 
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");
 
356
 
 
357
        /* Check if there are any submitted transfers left */
 
358
        submitted = false;
 
359
        for (ptransfer = transfer_list;
 
360
             (size_t)(ptransfer - transfer_list) < transfer_num;
 
361
             ptransfer++)
 
362
        {
 
363
            iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
 
364
 
 
365
            if (iface != NULL && iface->submitted)
 
366
                submitted = true;
 
367
        }
 
368
    }
 
369
 
 
370
    /* If all the transfers were terminated unexpectedly */
 
371
    if (transfer_num > 0 && !submitted)
 
372
        ERROR_CLEANUP("No more interfaces to dump");
 
373
 
 
374
    result = true;
 
375
 
 
376
cleanup:
 
377
 
 
378
    /* Cancel the transfers */
 
379
    if (submitted)
 
380
    {
 
381
        submitted = false;
 
382
        for (ptransfer = transfer_list;
 
383
             (size_t)(ptransfer - transfer_list) < transfer_num;
 
384
             ptransfer++)
 
385
        {
 
386
            iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
 
387
 
 
388
            if (iface != NULL && iface->submitted)
 
389
            {
 
390
                err = libusb_cancel_transfer(*ptransfer);
 
391
                if (err == LIBUSB_SUCCESS)
 
392
                    submitted = true;
 
393
                else
 
394
                {
 
395
                    LIBUSB_FAILURE("cancel a transfer, ignoring");
 
396
                    /*
 
397
                     * XXX are we really sure
 
398
                     * the transfer won't be finished?
 
399
                     */
 
400
                    iface->submitted = false;
 
401
                }
 
402
            }
 
403
        }
 
404
    }
 
405
 
 
406
    /* Wait for transfer cancellation */
 
407
    while (submitted)
 
408
    {
 
409
        /* Handle cancellation events */
 
410
        err = libusb_handle_events(ctx);
 
411
        if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_INTERRUPTED)
 
412
        {
 
413
            LIBUSB_FAILURE("handle transfer cancellation events, "
 
414
                           "aborting transfer cancellation");
 
415
            break;
 
416
        }
 
417
 
 
418
        /* Check if there are any submitted transfers left */
 
419
        submitted = false;
 
420
        for (ptransfer = transfer_list;
 
421
             (size_t)(ptransfer - transfer_list) < transfer_num;
 
422
             ptransfer++)
 
423
        {
 
424
            iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
 
425
 
 
426
            if (iface != NULL && iface->submitted)
 
427
                submitted = true;
 
428
        }
 
429
    }
 
430
 
 
431
    /*
 
432
     * Free transfer list along with non-submitted transfers and their
 
433
     * buffers.
 
434
     */
 
435
    if (transfer_list != NULL)
 
436
    {
 
437
        for (ptransfer = transfer_list;
 
438
             (size_t)(ptransfer - transfer_list) < transfer_num;
 
439
             ptransfer++)
 
440
        {
 
441
            iface = (usbhid_dump_iface *)(*ptransfer)->user_data;
 
442
 
 
443
            /*
 
444
             * Only free a transfer if it is not submitted. Better leak some
 
445
             * memory than have some important memory overwritten.
 
446
             */
 
447
            if (iface == NULL || !iface->submitted)
 
448
                libusb_free_transfer(*ptransfer);
 
449
        }
 
450
 
 
451
        free(transfer_list);
 
452
    }
 
453
 
 
454
    return result;
 
455
}
 
456
 
 
457
 
 
458
static int
 
459
run(bool    dump_descriptor,
 
460
    bool    dump_stream,
 
461
    uint8_t bus_num,
 
462
    uint8_t dev_addr,
 
463
    int     iface_num)
 
464
{
 
465
    int                     result      = 1;
 
466
    enum libusb_error       err;
 
467
    libusb_context         *ctx         = NULL;
 
468
    libusb_device_handle   *handle      = NULL;
 
469
    usbhid_dump_iface      *iface_list  = NULL;
 
470
 
 
471
    /* Initialize libusb context */
 
472
    err = libusb_init(&ctx);
 
473
    if (err != LIBUSB_SUCCESS)
 
474
        LIBUSB_FAILURE_CLEANUP("create libusb context");
 
475
 
 
476
    /* Set libusb debug level */
 
477
    libusb_set_debug(ctx, 3);
 
478
 
 
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");
 
483
 
 
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");
 
488
 
 
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");
 
493
 
 
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");
 
499
 
 
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)");
 
504
 
 
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))
 
508
               ? 0
 
509
               : 1;
 
510
 
 
511
cleanup:
 
512
 
 
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)");
 
517
 
 
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");
 
522
 
 
523
    /* Free the interface list */
 
524
    usbhid_dump_iface_list_free(iface_list);
 
525
 
 
526
    /* Free the device */
 
527
    if (handle != NULL)
 
528
        libusb_close(handle);
 
529
 
 
530
    /* Cleanup libusb context, if necessary */
 
531
    if (ctx != NULL)
 
532
        libusb_exit(ctx);
 
533
 
 
534
    return result;
 
535
}
 
536
 
 
537
 
 
538
typedef enum opt_val {
 
539
    OPT_VAL_HELP            = 'h',
 
540
    OPT_VAL_ENTITY          = 'e',
 
541
    OPT_VAL_STREAM_PAUSED   = 'p',
 
542
    OPT_VAL_STREAM_FEEDBACK = 'f',
 
543
} opt_val;
 
544
 
 
545
 
 
546
int
 
547
main(int argc, char **argv)
 
548
{
 
549
    static const struct option long_opt_list[] = {
 
550
        {.val       = OPT_VAL_HELP,
 
551
         .name      = "help",
 
552
         .has_arg   = no_argument,
 
553
         .flag      = NULL},
 
554
        {.val       = OPT_VAL_ENTITY,
 
555
         .name      = "entity",
 
556
         .has_arg   = required_argument,
 
557
         .flag      = NULL},
 
558
        {.val       = OPT_VAL_STREAM_PAUSED,
 
559
         .name      = "stream-paused",
 
560
         .has_arg   = no_argument,
 
561
         .flag      = NULL},
 
562
        {.val       = OPT_VAL_STREAM_FEEDBACK,
 
563
         .name      = "stream-feedback",
 
564
         .has_arg   = no_argument,
 
565
         .flag      = NULL},
 
566
        {.val       = 0,
 
567
         .name      = NULL,
 
568
         .has_arg   = 0,
 
569
         .flag      = NULL}
 
570
    };
 
571
 
 
572
    static const char  *short_opt_list = "he:pf";
 
573
 
 
574
    int             result;
 
575
 
 
576
    char            c;
 
577
    const char     *bus_str;
 
578
    const char     *dev_str;
 
579
    const char     *if_str      = "";
 
580
 
 
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;
 
584
    size_t          i;
 
585
    char           *end;
 
586
 
 
587
    bool            dump_descriptor = true;
 
588
    bool            dump_stream     = false;
 
589
    long            bus_num;
 
590
    long            dev_num;
 
591
    long            if_num          = -1;
 
592
 
 
593
    struct sigaction    sa;
 
594
 
 
595
#define USAGE_ERROR(_fmt, _args...) \
 
596
    do {                                                \
 
597
        fprintf(stderr, _fmt "\n", ##_args);            \
 
598
        usage(stderr, program_invocation_short_name);   \
 
599
        return 1;                                       \
 
600
    } while (0)
 
601
 
 
602
    /*
 
603
     * Parse command line arguments
 
604
     */
 
605
    while ((c = getopt_long(argc, argv,
 
606
                            short_opt_list, long_opt_list, NULL)) >= 0)
 
607
    {
 
608
        switch (c)
 
609
        {
 
610
            case OPT_VAL_HELP:
 
611
                usage(stdout, program_invocation_short_name);
 
612
                return 0;
 
613
                break;
 
614
            case OPT_VAL_ENTITY:
 
615
                if (strncmp(optarg, "descriptor", strlen(optarg)) == 0)
 
616
                {
 
617
                    dump_descriptor = true;
 
618
                    dump_stream = false;
 
619
                }
 
620
                else if (strncmp(optarg, "stream", strlen(optarg)) == 0)
 
621
                {
 
622
                    dump_descriptor = false;
 
623
                    dump_stream = true;
 
624
                }
 
625
                else if (strncmp(optarg, "both", strlen(optarg)) == 0)
 
626
                {
 
627
                    dump_descriptor = true;
 
628
                    dump_stream = true;
 
629
                }
 
630
                else
 
631
                    USAGE_ERROR("Unknown entity \"%s\"", optarg);
 
632
 
 
633
                break;
 
634
            case OPT_VAL_STREAM_PAUSED:
 
635
                stream_paused = 1;
 
636
                break;
 
637
            case OPT_VAL_STREAM_FEEDBACK:
 
638
                stream_feedback = 1;
 
639
                break;
 
640
            case '?':
 
641
                usage(stderr, program_invocation_short_name);
 
642
                return 1;
 
643
                break;
 
644
        }
 
645
    }
 
646
 
 
647
    /*
 
648
     * Assign positional parameters
 
649
     */
 
650
    for (i = 0; i < max_arg_num && optind < argc; i++, optind++)
 
651
        *arg_list[i] = argv[optind];
 
652
 
 
653
    if (i < req_arg_num)
 
654
        USAGE_ERROR("Not enough arguments");
 
655
 
 
656
    /*
 
657
     * Parse and verify positional parameters
 
658
     */
 
659
    errno = 0;
 
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);
 
664
 
 
665
    errno = 0;
 
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);
 
670
 
 
671
    if (!usbhid_dump_strisblank(if_str))
 
672
    {
 
673
        errno = 0;
 
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);
 
678
    }
 
679
 
 
680
    /*
 
681
     * Setup signal handlers
 
682
     */
 
683
    /* Setup SIGINT to terminate gracefully */
 
684
    sigaction(SIGINT, NULL, &sa);
 
685
    if (sa.sa_handler != SIG_IGN)
 
686
    {
 
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);
 
692
    }
 
693
 
 
694
    /* Setup SIGTERM to terminate gracefully */
 
695
    sigaction(SIGTERM, NULL, &sa);
 
696
    if (sa.sa_handler != SIG_IGN)
 
697
    {
 
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);
 
703
    }
 
704
 
 
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);
 
712
 
 
713
    /* Make stdout buffered - we will flush it explicitly */
 
714
    setbuf(stdout, NULL);
 
715
 
 
716
    result = run(dump_descriptor, dump_stream, bus_num, dev_num, if_num);
 
717
 
 
718
    /*
 
719
     * Restore signal handlers
 
720
     */
 
721
    sigaction(SIGINT, NULL, &sa);
 
722
    if (sa.sa_handler != SIG_IGN)
 
723
        signal(SIGINT, SIG_DFL);
 
724
 
 
725
    sigaction(SIGTERM, NULL, &sa);
 
726
    if (sa.sa_handler != SIG_IGN)
 
727
        signal(SIGTERM, SIG_DFL);
 
728
 
 
729
    /*
 
730
     * Reproduce the signal used to stop the program to get proper exit
 
731
     * status.
 
732
     */
 
733
    if (exit_signum != 0)
 
734
        raise(exit_signum);
 
735
 
 
736
    return result;
 
737
}
 
738
 
 
739