"Fossies" - the Fresh Open Source Software Archive

Member "usbutils-014/usbhid-dump/src/usbhid-dump.c" (22 Feb 2021, 30324 Bytes) of package /linux/misc/usbutils-014.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "usbhid-dump.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 013_vs_014.

    1 // SPDX-License-Identifier: GPL-2.0-or-later
    2 /*
    3  * usbhid-dump - entry point *
    4  *
    5  * Copyright (C) 2010-2011 Nikolai Kondrashov <spbnick@gmail.com>
    6  */
    7 
    8 #include "config.h"
    9 
   10 #include "iface_list.h"
   11 #include "misc.h"
   12 #include <libusb.h>
   13 
   14 #include <assert.h>
   15 #include <stdbool.h>
   16 #include <ctype.h>
   17 #include <limits.h>
   18 #include <string.h>
   19 #include <errno.h>
   20 #include <signal.h>
   21 #include <stdlib.h>
   22 #include <unistd.h>
   23 #include <getopt.h>
   24 #include <stdio.h>
   25 #include <stdint.h>
   26 
   27 /* Define LIBUSB_CALL for libusb <= 1.0.8 */
   28 #ifndef LIBUSB_CALL
   29 #define LIBUSB_CALL
   30 #endif
   31 
   32 #define GENERIC_ERROR(_fmt, _args...) \
   33     fprintf(stderr, _fmt "\n", ##_args)
   34 
   35 #define IFACE_ERROR(_iface, _fmt, _args...) \
   36     GENERIC_ERROR("%s:" _fmt, _iface->addr_str, ##_args)
   37 
   38 #define GENERIC_FAILURE(_fmt, _args...) \
   39     GENERIC_ERROR("Failed to " _fmt, ##_args)
   40 
   41 #define IFACE_FAILURE(_iface, _fmt, _args...) \
   42     IFACE_ERROR(_iface, "Failed to " _fmt, ##_args)
   43 
   44 #define LIBUSB_FAILURE(_fmt, _args...) \
   45     GENERIC_FAILURE(_fmt ": %s", ##_args, libusb_strerror(err))
   46 
   47 #define LIBUSB_IFACE_FAILURE(_iface, _fmt, _args...) \
   48     IFACE_FAILURE(_iface, _fmt ": %s", ##_args, libusb_strerror(err))
   49 
   50 #define ERROR_CLEANUP(_fmt, _args...) \
   51     do {                                \
   52         GENERIC_ERROR(_fmt, ##_args);   \
   53         goto cleanup;                   \
   54     } while (0)
   55 
   56 #define FAILURE_CLEANUP(_fmt, _args...) \
   57     do {                                \
   58         GENERIC_FAILURE(_fmt, ##_args); \
   59         goto cleanup;                   \
   60     } while (0)
   61 
   62 #define LIBUSB_FAILURE_CLEANUP(_fmt, _args...) \
   63     do {                                        \
   64         LIBUSB_FAILURE(_fmt, ##_args);          \
   65         goto cleanup;                           \
   66     } while (0)
   67 
   68 #define LIBUSB_IFACE_FAILURE_CLEANUP(_iface, _fmt, _args...) \
   69     do {                                                        \
   70         LIBUSB_IFACE_FAILURE(_iface, _fmt, ##_args);            \
   71         goto cleanup;                                           \
   72     } while (0)
   73 
   74 #define LIBUSB_GUARD(_expr, _fmt, _args...) \
   75     do {                                            \
   76         err = _expr;                                \
   77         if (err != LIBUSB_SUCCESS)                  \
   78             LIBUSB_FAILURE_CLEANUP(_fmt, ##_args);  \
   79     } while (0)
   80 
   81 #define LIBUSB_IFACE_GUARD(_expr, _iface, _fmt, _args...) \
   82     do {                                                            \
   83         err = _expr;                                                \
   84         if (err != LIBUSB_SUCCESS)                                  \
   85             LIBUSB_IFACE_FAILURE_CLEANUP(_iface, _fmt, ##_args);    \
   86     } while (0)
   87 
   88 /**< Number of the signal causing the exit */
   89 static volatile sig_atomic_t exit_signum  = 0;
   90 
   91 static void
   92 exit_sighandler(int signum)
   93 {
   94     if (exit_signum == 0)
   95         exit_signum = signum;
   96 }
   97 
   98 /**< "Stream paused" flag - non-zero if paused */
   99 static volatile sig_atomic_t stream_paused = 0;
  100 
  101 static void
  102 stream_pause_sighandler(int signum)
  103 {
  104     (void)signum;
  105     stream_paused = 1;
  106 }
  107 
  108 static void
  109 stream_resume_sighandler(int signum)
  110 {
  111     (void)signum;
  112     stream_paused = 0;
  113 }
  114 
  115 /**< "Stream feedback" flag - non-zero if feedback is enabled */
  116 static volatile sig_atomic_t stream_feedback = 0;
  117 
  118 static void
  119 dump(const uhd_iface   *iface,
  120      const char        *entity,
  121      const uint8_t     *ptr,
  122      size_t             len)
  123 {
  124     static const char   xd[]    = "0123456789ABCDEF";
  125     static char         buf[]   = " XX\n";
  126     size_t              pos;
  127     uint8_t             b;
  128     struct timeval      tv;
  129 
  130     gettimeofday(&tv, NULL);
  131 
  132     fprintf(stdout, "%s:%-16s %12llu.%.6u\n",
  133             iface->addr_str, entity,
  134             (unsigned long long int)tv.tv_sec,
  135             (unsigned int)tv.tv_usec);
  136 
  137     for (pos = 1; len > 0; len--, ptr++, pos++)
  138     {
  139         b = *ptr;
  140         buf[1] = xd[b >> 4];
  141         buf[2] = xd[b & 0xF];
  142 
  143         (void)fwrite(buf, ((pos % 16 == 0) ? 4 : 3), 1, stdout);
  144     }
  145 
  146     if (pos % 16 != 1)
  147         fputc('\n', stdout);
  148     fputc('\n', stdout);
  149 
  150     fflush(stdout);
  151 }
  152 
  153 
  154 static bool
  155 dump_iface_list_descriptor(const uhd_iface *list)
  156 {
  157     const uhd_iface    *iface;
  158     uint8_t             buf[UHD_MAX_DESCRIPTOR_SIZE];
  159     int                 rc;
  160     enum libusb_error   err;
  161 
  162     UHD_IFACE_LIST_FOR_EACH(iface, list)
  163     {
  164         if (iface->rd_len > sizeof(buf))
  165         {
  166             err = LIBUSB_ERROR_NO_MEM;
  167             LIBUSB_IFACE_FAILURE(iface, "report descriptor too long: %hu",
  168                                  iface->rd_len);
  169             return false;
  170         }
  171 
  172         rc = libusb_control_transfer(iface->dev->handle,
  173                                      /* See HID spec, 7.1.1 */
  174                                      0x81,
  175                                      LIBUSB_REQUEST_GET_DESCRIPTOR,
  176                                      (LIBUSB_DT_REPORT << 8), iface->number,
  177                                      buf, iface->rd_len, UHD_IO_TIMEOUT);
  178         if (rc < 0)
  179         {
  180             err = rc;
  181             LIBUSB_IFACE_FAILURE(iface, "retrieve report descriptor");
  182             return false;
  183         }
  184         dump(iface, "DESCRIPTOR", buf, rc);
  185     }
  186 
  187     return true;
  188 }
  189 
  190 
  191 static void LIBUSB_CALL
  192 dump_iface_list_stream_cb(struct libusb_transfer *transfer)
  193 {
  194     enum libusb_error   err;
  195     uhd_iface          *iface;
  196 
  197     assert(transfer != NULL);
  198 
  199     iface = (uhd_iface *)transfer->user_data;
  200     assert(uhd_iface_valid(iface));
  201 
  202     /* Clear interface "has transfer submitted" flag */
  203     iface->submitted = false;
  204 
  205     switch (transfer->status)
  206     {
  207         case LIBUSB_TRANSFER_COMPLETED:
  208             /* Dump the result */
  209             if (!stream_paused)
  210             {
  211                 dump(iface, "STREAM",
  212                      transfer->buffer, transfer->actual_length);
  213                 if (stream_feedback)
  214                     fputc('.', stderr);
  215             }
  216             /* Resubmit the transfer */
  217             err = libusb_submit_transfer(transfer);
  218             if (err != LIBUSB_SUCCESS)
  219                 LIBUSB_IFACE_FAILURE(iface, "resubmit a transfer");
  220             else
  221             {
  222                 /* Set interface "has transfer submitted" flag */
  223                 iface->submitted = true;
  224             }
  225             break;
  226 
  227 #define MAP(_name, _desc) \
  228     case LIBUSB_TRANSFER_##_name: \
  229         IFACE_ERROR(iface, _desc);  \
  230         break
  231 
  232         MAP(ERROR,      "Interrupt transfer failed");
  233         MAP(TIMED_OUT,  "Interrupt transfer timed out");
  234         MAP(STALL,      "Interrupt transfer halted (endpoint stalled)");
  235         MAP(NO_DEVICE,  "Device was disconnected");
  236         MAP(OVERFLOW,   "Interrupt transfer overflowed "
  237                         "(device sent more data than requested)");
  238 #undef MAP
  239 
  240         case LIBUSB_TRANSFER_CANCELLED:
  241             break;
  242     }
  243 }
  244 
  245 
  246 static const char *
  247 format_time_interval(unsigned int i)
  248 {
  249     static char     buf[128];
  250     char           *p           = buf;
  251     unsigned int    h           = i / (60 * 60 * 1000);
  252     unsigned int    m           = (i % (60 * 60 * 1000)) / (60 * 1000);
  253     unsigned int    s           = (i % (60 * 1000)) / 1000;
  254     unsigned int    ms          = i % 1000;
  255 
  256 #define FRACTION(_prev_sum, _name, _val) \
  257     do {                                                \
  258         if ((_val) > 0)                                 \
  259             p += snprintf(p, sizeof(buf) - (p - buf),   \
  260                           "%s%u " _name "%s",           \
  261                           ((_prev_sum) > 0 ? " " : ""), \
  262                           _val,                         \
  263                           (((_val) == 1) ? "" : "s"));  \
  264         if (p >= (buf + sizeof(buf)))                   \
  265             return buf;                                 \
  266     } while (0)
  267 
  268     FRACTION(0, "hour", h);
  269     FRACTION(h, "minute", m);
  270     FRACTION(h + m, "second", s);
  271     FRACTION(h + m + s, "millisecond", ms);
  272 
  273 #undef FRACTION
  274 
  275     return buf;
  276 }
  277 
  278 
  279 static const char *
  280 format_timeout(unsigned int i)
  281 {
  282     return (i == 0) ? "infinite" : format_time_interval(i);
  283 }
  284 
  285 
  286 static bool
  287 dump_iface_list_stream(libusb_context  *ctx,
  288                        uhd_iface       *list,
  289                        unsigned int     timeout)
  290 {
  291     bool                        result              = false;
  292     enum libusb_error           err;
  293     size_t                      transfer_num        = 0;
  294     struct libusb_transfer    **transfer_list       = NULL;
  295     struct libusb_transfer    **ptransfer;
  296     uhd_iface                  *iface;
  297     bool                        submitted           = false;
  298 
  299     fprintf(stderr,
  300             "Starting dumping interrupt transfer stream\n"
  301             "with %s timeout.\n\n",
  302             format_timeout(timeout));
  303 
  304     UHD_IFACE_LIST_FOR_EACH(iface, list)
  305     {
  306         /* Set report protocol */
  307         LIBUSB_IFACE_GUARD(uhd_iface_set_protocol(iface, true,
  308                                                   UHD_IO_TIMEOUT),
  309                            iface, "set report protocol");
  310         /* Set infinite idle duration */
  311         LIBUSB_IFACE_GUARD(uhd_iface_set_idle(iface, 0, UHD_IO_TIMEOUT),
  312                            iface, "set infinite idle duration");
  313     }
  314 
  315     /* Calculate number of interfaces and thus transfers */
  316     transfer_num = uhd_iface_list_len(list);
  317 
  318     /* Allocate transfer list */
  319     transfer_list = malloc(sizeof(*transfer_list) * transfer_num);
  320     if (transfer_list == NULL)
  321         FAILURE_CLEANUP("allocate transfer list");
  322 
  323     /* Zero transfer list */
  324     for (ptransfer = transfer_list;
  325          (size_t)(ptransfer - transfer_list) < transfer_num;
  326          ptransfer++)
  327         *ptransfer = NULL;
  328 
  329     /* Allocate transfers */
  330     for (ptransfer = transfer_list;
  331          (size_t)(ptransfer - transfer_list) < transfer_num;
  332          ptransfer++)
  333     {
  334         *ptransfer = libusb_alloc_transfer(0);
  335         if (*ptransfer == NULL)
  336             FAILURE_CLEANUP("allocate a transfer");
  337         /*
  338          * Set user_data to NULL explicitly, since libusb_alloc_transfer
  339          * does memset to zero only and zero is not NULL, strictly speaking.
  340          */
  341         (*ptransfer)->user_data = NULL;
  342     }
  343 
  344     /* Initialize the transfers as interrupt transfers */
  345     for (ptransfer = transfer_list, iface = list;
  346          (size_t)(ptransfer - transfer_list) < transfer_num;
  347          ptransfer++, iface = iface->next)
  348     {
  349         void           *buf;
  350         const size_t    len = iface->int_in_ep_maxp;
  351 
  352         /* Allocate the transfer buffer */
  353         buf = malloc(len);
  354         if (len > 0 && buf == NULL)
  355             FAILURE_CLEANUP("allocate a transfer buffer");
  356 
  357         /* Initialize the transfer */
  358         libusb_fill_interrupt_transfer(*ptransfer,
  359                                        iface->dev->handle, iface->int_in_ep_addr,
  360                                        buf, len,
  361                                        dump_iface_list_stream_cb,
  362                                        (void *)iface,
  363                                        timeout);
  364 
  365         /* Ask to free the buffer when the transfer is freed */
  366         (*ptransfer)->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
  367     }
  368 
  369     /* Submit first transfer requests */
  370     for (ptransfer = transfer_list;
  371          (size_t)(ptransfer - transfer_list) < transfer_num;
  372          ptransfer++)
  373     {
  374         LIBUSB_GUARD(libusb_submit_transfer(*ptransfer),
  375                      "submit a transfer");
  376         /* Set interface "has transfer submitted" flag */
  377         ((uhd_iface *)(*ptransfer)->user_data)->submitted = true;
  378         /* Set "have any submitted transfers" flag */
  379         submitted = true;
  380     }
  381 
  382     /* Run the event machine */
  383     while (submitted && exit_signum == 0)
  384     {
  385         /* Handle the transfer events */
  386         err = libusb_handle_events(ctx);
  387         if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_INTERRUPTED)
  388             LIBUSB_FAILURE_CLEANUP("handle transfer events");
  389 
  390         /* Check if there are any submitted transfers left */
  391         submitted = false;
  392         for (ptransfer = transfer_list;
  393              (size_t)(ptransfer - transfer_list) < transfer_num;
  394              ptransfer++)
  395         {
  396             iface = (uhd_iface *)(*ptransfer)->user_data;
  397 
  398             if (iface != NULL && iface->submitted)
  399                 submitted = true;
  400         }
  401     }
  402 
  403     /* If all the transfers were terminated unexpectedly */
  404     if (transfer_num > 0 && !submitted)
  405         ERROR_CLEANUP("No more interfaces to dump");
  406 
  407     result = true;
  408 
  409 cleanup:
  410 
  411     /* Cancel the transfers */
  412     if (submitted)
  413     {
  414         submitted = false;
  415         for (ptransfer = transfer_list;
  416              (size_t)(ptransfer - transfer_list) < transfer_num;
  417              ptransfer++)
  418         {
  419             iface = (uhd_iface *)(*ptransfer)->user_data;
  420 
  421             if (iface != NULL && iface->submitted)
  422             {
  423                 err = libusb_cancel_transfer(*ptransfer);
  424                 if (err == LIBUSB_SUCCESS)
  425                     submitted = true;
  426                 else
  427                 {
  428                     LIBUSB_FAILURE("cancel a transfer, ignoring");
  429                     /*
  430                      * XXX are we really sure
  431                      * the transfer won't be finished?
  432                      */
  433                     iface->submitted = false;
  434                 }
  435             }
  436         }
  437     }
  438 
  439     /* Wait for transfer cancellation */
  440     while (submitted)
  441     {
  442         /* Handle cancellation events */
  443         err = libusb_handle_events(ctx);
  444         if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_INTERRUPTED)
  445         {
  446             LIBUSB_FAILURE("handle transfer cancellation events, "
  447                            "aborting transfer cancellation");
  448             break;
  449         }
  450 
  451         /* Check if there are any submitted transfers left */
  452         submitted = false;
  453         for (ptransfer = transfer_list;
  454              (size_t)(ptransfer - transfer_list) < transfer_num;
  455              ptransfer++)
  456         {
  457             iface = (uhd_iface *)(*ptransfer)->user_data;
  458 
  459             if (iface != NULL && iface->submitted)
  460                 submitted = true;
  461         }
  462     }
  463 
  464     /*
  465      * Free transfer list along with non-submitted transfers and their
  466      * buffers.
  467      */
  468     if (transfer_list != NULL)
  469     {
  470         for (ptransfer = transfer_list;
  471              (size_t)(ptransfer - transfer_list) < transfer_num;
  472              ptransfer++)
  473         {
  474             iface = (uhd_iface *)(*ptransfer)->user_data;
  475 
  476             /*
  477              * Only free a transfer if it is not submitted. Better leak some
  478              * memory than have some important memory overwritten.
  479              */
  480             if (iface == NULL || !iface->submitted)
  481                 libusb_free_transfer(*ptransfer);
  482         }
  483 
  484         free(transfer_list);
  485     }
  486 
  487     return result;
  488 }
  489 
  490 
  491 static int
  492 run(bool            dump_descriptor,
  493     bool            dump_stream,
  494     unsigned int    stream_timeout,
  495     uint8_t         bus_num,
  496     uint8_t         dev_addr,
  497     uint16_t        vid,
  498     uint16_t        pid,
  499     int             iface_num)
  500 {
  501     int                 result      = 1;
  502     enum libusb_error   err;
  503     libusb_context     *ctx         = NULL;
  504     uhd_dev            *dev_list    = NULL;
  505     uhd_iface          *iface_list  = NULL;
  506     uhd_iface          *iface;
  507 
  508     /* Create libusb context */
  509     LIBUSB_GUARD(libusb_init(&ctx), "create libusb context");
  510 
  511     /* Set libusb debug level to informational only */
  512 #if HAVE_LIBUSB_SET_OPTION
  513     libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
  514 #else
  515     libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_INFO);
  516 #endif
  517 
  518     /* Open device list */
  519     LIBUSB_GUARD(uhd_dev_list_open(ctx, bus_num, dev_addr,
  520                                    vid, pid, &dev_list),
  521                  "find and open the devices");
  522 
  523     /* Retrieve the list of HID interfaces from the device list */
  524     LIBUSB_GUARD(uhd_iface_list_new(dev_list, &iface_list),
  525                  "find HID interfaces");
  526 
  527     /* Filter the interface list by specified interface number */
  528     if (iface_num != UHD_IFACE_NUM_ANY)
  529         iface_list = uhd_iface_list_fltr_by_num(iface_list, iface_num);
  530 
  531     /* Check if there are any interfaces left */
  532     if (uhd_iface_list_empty(iface_list))
  533         ERROR_CLEANUP("No matching HID interfaces");
  534 
  535     /* Detach and claim the interfaces */
  536     UHD_IFACE_LIST_FOR_EACH(iface, iface_list)
  537     {
  538         LIBUSB_IFACE_GUARD(uhd_iface_detach(iface),
  539                            iface, "detach from the kernel driver");
  540         LIBUSB_IFACE_GUARD(uhd_iface_claim(iface),
  541                            iface, "claim");
  542     }
  543 
  544     /* Run with the prepared interface list */
  545     result = (!dump_descriptor || dump_iface_list_descriptor(iface_list)) &&
  546              (!dump_stream || dump_iface_list_stream(ctx, iface_list,
  547                                                      stream_timeout))
  548                ? 0
  549                : 1;
  550 
  551 cleanup:
  552 
  553     /* Release and attach the interfaces back */
  554     UHD_IFACE_LIST_FOR_EACH(iface, iface_list)
  555     {
  556         err = uhd_iface_release(iface);
  557         if (err != LIBUSB_SUCCESS)
  558             LIBUSB_IFACE_FAILURE(iface, "release");
  559 
  560         err = uhd_iface_attach(iface);
  561         if (err != LIBUSB_SUCCESS)
  562             LIBUSB_IFACE_FAILURE(iface, "attach to the kernel driver");
  563     }
  564 
  565     /* Free the interface list */
  566     uhd_iface_list_free(iface_list);
  567 
  568     /* Close the device list */
  569     uhd_dev_list_close(dev_list);
  570 
  571     /* Destroy the libusb context */
  572     if (ctx != NULL)
  573         libusb_exit(ctx);
  574 
  575     return result;
  576 }
  577 
  578 
  579 static bool
  580 parse_number_pair(const char   *str,
  581                   int           base,
  582                   long         *pn1,
  583                   long         *pn2)
  584 {
  585     const char *p;
  586     char       *end;
  587     long        n1;
  588     long        n2;
  589 
  590     assert(str != NULL);
  591 
  592     p = str;
  593 
  594     /* Skip space (prevent strtol doing so) */
  595     while (isspace((int)*p))
  596         p++;
  597 
  598     /* Extract the first number */
  599     errno = 0;
  600     n1 = strtol(p, &end, base);
  601     if (errno != 0)
  602         return false;
  603 
  604     /* If nothing was read */
  605     if (end == p)
  606         return false;
  607 
  608     /* Move on */
  609     p = end;
  610 
  611     /* Skip space */
  612     while (isspace((int)*p))
  613         p++;
  614 
  615     /* If it is the end of string */
  616     if (*p == '\0')
  617         n2 = 0;
  618     else
  619     {
  620         /* If it is not the number separator */
  621         if (*p != ':')
  622             return false;
  623 
  624         /* Skip the number separator */
  625         p++;
  626 
  627         /* Skip space (prevent strtol doing so) */
  628         while (isspace((int)*p))
  629             p++;
  630 
  631         /* Extract the second number */
  632         errno = 0;
  633         n2 = strtol(p, &end, base);
  634         if (errno != 0)
  635             return false;
  636         /* If nothing was read */
  637         if (end == p)
  638             return false;
  639 
  640         /* Move on */
  641         p = end;
  642 
  643         /* Skip space */
  644         while (isspace((int)*p))
  645             p++;
  646 
  647         /* If it is not the end of string */
  648         if (*p != '\0')
  649             return false;
  650     }
  651 
  652     /* Output the numbers */
  653     if (pn1 != NULL)
  654         *pn1 = n1;
  655     if (pn2 != NULL)
  656         *pn2 = n2;
  657 
  658     return true;
  659 }
  660 
  661 
  662 static bool
  663 parse_address(const char   *str,
  664               uint8_t      *pbus_num,
  665               uint8_t      *pdev_addr)
  666 {
  667     long    bus_num;
  668     long    dev_addr;
  669 
  670     assert(str != NULL);
  671 
  672     if (!parse_number_pair(str, 10, &bus_num, &dev_addr))
  673         return false;
  674 
  675     if (bus_num < 0 || bus_num > UINT8_MAX ||
  676         dev_addr < 0 || dev_addr > UINT8_MAX)
  677         return false;
  678 
  679     if (pbus_num != NULL)
  680         *pbus_num = bus_num;
  681     if (pdev_addr != NULL)
  682         *pdev_addr = dev_addr;
  683 
  684     return true;
  685 }
  686 
  687 
  688 static bool
  689 parse_model(const char   *str,
  690             uint16_t     *pvid,
  691             uint16_t     *ppid)
  692 {
  693     long    vid;
  694     long    pid;
  695 
  696     assert(str != NULL);
  697 
  698     if (!parse_number_pair(str, 16, &vid, &pid))
  699         return false;
  700 
  701     if (vid < 0 || vid > UINT16_MAX ||
  702         pid < 0 || pid > UINT16_MAX)
  703         return false;
  704 
  705     if (pvid != NULL)
  706         *pvid = vid;
  707     if (ppid != NULL)
  708         *ppid = pid;
  709 
  710     return true;
  711 }
  712 
  713 
  714 static bool
  715 parse_iface_num(const char *str,
  716                 uint8_t    *piface_num)
  717 {
  718     long        iface_num;
  719     const char *p;
  720     char       *end;
  721 
  722     assert(str != NULL);
  723 
  724     p = str;
  725 
  726     /* Skip space (prevent strtol doing so) */
  727     while (isspace((int)*p))
  728         p++;
  729 
  730     /* Extract interface number */
  731     errno = 0;
  732     iface_num = strtol(p, &end, 10);
  733     if (errno != 0 || end == p || iface_num < 0 || iface_num > UINT8_MAX)
  734         return false;
  735 
  736     /* Output interface number */
  737     if (piface_num != NULL)
  738         *piface_num = iface_num;
  739 
  740     return true;
  741 }
  742 
  743 
  744 static bool
  745 parse_timeout(const char   *str,
  746               unsigned int *ptimeout)
  747 {
  748     long long   timeout;
  749     const char *p;
  750     char       *end;
  751 
  752     assert(str != NULL);
  753 
  754     p = str;
  755 
  756     /* Skip space (prevent strtoll doing so) */
  757     while (isspace((int)*p))
  758         p++;
  759 
  760     /* Extract timeout */
  761     errno = 0;
  762     timeout = strtoll(p, &end, 10);
  763     if (errno != 0 || end == p || timeout < 0 || timeout > UINT_MAX)
  764         return false;
  765 
  766     /* Output timeout */
  767     if (ptimeout != NULL)
  768         *ptimeout = timeout;
  769 
  770     return true;
  771 }
  772 
  773 
  774 static bool
  775 version(FILE *stream)
  776 {
  777     return
  778         fprintf(
  779             stream,
  780 PACKAGE_STRING "\n"
  781 "Copyright (C) 2010 Nikolai Kondrashov\n"
  782 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
  783 "\n"
  784 "This is free software: you are free to change and redistribute it.\n"
  785 "There is NO WARRANTY, to the extent permitted by law.\n") >= 0;
  786 }
  787 
  788 
  789 static bool
  790 usage(FILE *stream, const char *name)
  791 {
  792     return
  793         fprintf(
  794             stream,
  795 "Usage: %s [OPTION]...\n"
  796 "Dump USB device HID report descriptor(s) and/or stream(s).\n"
  797 "\n"
  798 "Options:\n"
  799 "  -h, --help                       output this help message and exit\n"
  800 "  -v, --version                    output version information and exit\n"
  801 "\n"
  802 "  -s, -a, --address=bus[:dev]      limit interfaces by bus number\n"
  803 "                                   (1-255) and device address (1-255),\n"
  804 "                                   decimal; zeroes match any\n"
  805 "  -d, -m, --model=vid[:pid]        limit interfaces by vendor and\n"
  806 "                                   product IDs (0001-ffff), hexadecimal;\n"
  807 "                                   zeroes match any\n"
  808 "  -i, --interface=NUMBER           limit interfaces by number (0-254),\n"
  809 "                                   decimal; 255 matches any\n"
  810 "\n"
  811 "  -e, --entity=STRING              what to dump: either \"descriptor\",\n"
  812 "                                   \"stream\" or \"all\"; value can be\n"
  813 "                                   abbreviated\n"
  814 "\n"
  815 "  -t, --stream-timeout=NUMBER      stream interrupt transfer timeout, ms;\n"
  816 "                                   zero means infinity\n"
  817 "  -p, --stream-paused              start with the stream dump output\n"
  818 "                                   paused\n"
  819 "  -f, --stream-feedback            enable stream dumping feedback: for\n"
  820 "                                   every transfer dumped a dot is\n"
  821 "                                   printed to stderr\n"
  822 "\n"
  823 "Default options: --stream-timeout=60000 --entity=descriptor\n"
  824 "\n"
  825 "Signals:\n"
  826 "  USR1/USR2                        pause/resume the stream dump output\n"
  827 "\n",
  828             name) >= 0;
  829 }
  830 
  831 
  832 typedef enum opt_val {
  833     OPT_VAL_HELP            = 'h',
  834     OPT_VAL_VERSION         = 'v',
  835     OPT_VAL_ADDRESS         = 'a',
  836     OPT_VAL_ADDRESS_COMP    = 's',
  837     OPT_VAL_MODEL           = 'm',
  838     OPT_VAL_MODEL_COMP      = 'd',
  839     OPT_VAL_INTERFACE       = 'i',
  840     OPT_VAL_ENTITY          = 'e',
  841     OPT_VAL_STREAM_TIMEOUT  = 't',
  842     OPT_VAL_STREAM_PAUSED   = 'p',
  843     OPT_VAL_STREAM_FEEDBACK = 'f',
  844 } opt_val;
  845 
  846 
  847 static const struct option long_opt_list[] = {
  848     {.val       = OPT_VAL_HELP,
  849      .name      = "help",
  850      .has_arg   = no_argument,
  851      .flag      = NULL},
  852     {.val       = OPT_VAL_VERSION,
  853      .name      = "version",
  854      .has_arg   = no_argument,
  855      .flag      = NULL},
  856     {.val       = OPT_VAL_ADDRESS,
  857      .name      = "address",
  858      .has_arg   = required_argument,
  859      .flag      = NULL},
  860     {.val       = OPT_VAL_MODEL,
  861      .name      = "model",
  862      .has_arg   = required_argument,
  863      .flag      = NULL},
  864     {.val       = OPT_VAL_INTERFACE,
  865      .name      = "interface",
  866      .has_arg   = required_argument,
  867      .flag      = NULL},
  868     {.val       = OPT_VAL_ENTITY,
  869      .name      = "entity",
  870      .has_arg   = required_argument,
  871      .flag      = NULL},
  872     {.val       = OPT_VAL_STREAM_TIMEOUT,
  873      .name      = "stream-timeout",
  874      .has_arg   = required_argument,
  875      .flag      = NULL},
  876     {.val       = OPT_VAL_STREAM_PAUSED,
  877      .name      = "stream-paused",
  878      .has_arg   = no_argument,
  879      .flag      = NULL},
  880     {.val       = OPT_VAL_STREAM_FEEDBACK,
  881      .name      = "stream-feedback",
  882      .has_arg   = no_argument,
  883      .flag      = NULL},
  884     {.val       = 0,
  885      .name      = NULL,
  886      .has_arg   = 0,
  887      .flag      = NULL}
  888 };
  889 
  890 
  891 static const char  *short_opt_list = "hvs:a:d:m:i:e:t:pf";
  892 
  893 
  894 int
  895 main(int argc, char **argv)
  896 {
  897     int                 result;
  898 
  899     const char         *name;
  900 
  901     int                 c;
  902 
  903     uint8_t             bus_num         = UHD_BUS_NUM_ANY;
  904     uint8_t             dev_addr        = UHD_DEV_ADDR_ANY;
  905 
  906     uint16_t            vid             = UHD_VID_ANY;
  907     uint16_t            pid             = UHD_PID_ANY;
  908 
  909     uint8_t             iface_num       = UHD_IFACE_NUM_ANY;
  910 
  911     bool                dump_descriptor = true;
  912     bool                dump_stream     = false;
  913     unsigned int        stream_timeout  = 60000;
  914 
  915     struct sigaction    sa;
  916 
  917     /*
  918      * Extract program invocation name
  919      */
  920     name = strrchr(argv[0], '/');
  921     if (name == NULL)
  922         name = argv[0];
  923     else
  924         name++;
  925 
  926 #define USAGE_ERROR(_fmt, _args...) \
  927     do {                                        \
  928         fprintf(stderr, _fmt "\n", ##_args);    \
  929         usage(stderr, name);                    \
  930         return 1;                               \
  931     } while (0)
  932 
  933     /*
  934      * Parse command line arguments
  935      */
  936     while ((c = getopt_long(argc, argv,
  937                             short_opt_list, long_opt_list, NULL)) >= 0)
  938     {
  939         switch (c)
  940         {
  941             case OPT_VAL_HELP:
  942                 usage(stdout, name);
  943                 return 0;
  944                 break;
  945             case OPT_VAL_VERSION:
  946                 version(stdout);
  947                 return 0;
  948                 break;
  949             case OPT_VAL_ADDRESS:
  950             case OPT_VAL_ADDRESS_COMP:
  951                 if (!parse_address(optarg, &bus_num, &dev_addr))
  952                     USAGE_ERROR("Invalid device address \"%s\"", optarg);
  953                 break;
  954             case OPT_VAL_MODEL:
  955             case OPT_VAL_MODEL_COMP:
  956                 if (!parse_model(optarg, &vid, &pid))
  957                     USAGE_ERROR("Invalid model \"%s\"", optarg);
  958                 break;
  959             case OPT_VAL_INTERFACE:
  960                 if (!parse_iface_num(optarg, &iface_num))
  961                     USAGE_ERROR("Invalid interface number \"%s\"", optarg);
  962                 break;
  963             case OPT_VAL_ENTITY:
  964                 if (strncmp(optarg, "descriptor", strlen(optarg)) == 0)
  965                 {
  966                     dump_descriptor = true;
  967                     dump_stream = false;
  968                 }
  969                 else if (strncmp(optarg, "stream", strlen(optarg)) == 0)
  970                 {
  971                     dump_descriptor = false;
  972                     dump_stream = true;
  973                 }
  974                 else if (strncmp(optarg, "all", strlen(optarg)) == 0)
  975                 {
  976                     dump_descriptor = true;
  977                     dump_stream = true;
  978                 }
  979                 else
  980                     USAGE_ERROR("Unknown entity \"%s\"", optarg);
  981 
  982                 break;
  983             case OPT_VAL_STREAM_TIMEOUT:
  984                 if (!parse_timeout(optarg, &stream_timeout))
  985                     USAGE_ERROR("Invalid stream timeout \"%s\"", optarg);
  986                 break;
  987             case OPT_VAL_STREAM_PAUSED:
  988                 stream_paused = 1;
  989                 break;
  990             case OPT_VAL_STREAM_FEEDBACK:
  991                 stream_feedback = 1;
  992                 break;
  993             case '?':
  994                 usage(stderr, name);
  995                 return 1;
  996                 break;
  997         }
  998     }
  999 
 1000     /*
 1001      * Verify positional arguments
 1002      */
 1003     if (optind < argc)
 1004         USAGE_ERROR("Positional arguments are not accepted");
 1005 
 1006     /*
 1007      * Setup signal handlers
 1008      */
 1009     /* Setup SIGINT to terminate gracefully */
 1010     sigaction(SIGINT, NULL, &sa);
 1011     if (sa.sa_handler != SIG_IGN)
 1012     {
 1013         sa.sa_handler = exit_sighandler;
 1014         sigemptyset(&sa.sa_mask);
 1015         sigaddset(&sa.sa_mask, SIGTERM);
 1016         sa.sa_flags = 0;    /* NOTE: no SA_RESTART on purpose */
 1017         sigaction(SIGINT, &sa, NULL);
 1018     }
 1019 
 1020     /* Setup SIGTERM to terminate gracefully */
 1021     sigaction(SIGTERM, NULL, &sa);
 1022     if (sa.sa_handler != SIG_IGN)
 1023     {
 1024         sa.sa_handler = exit_sighandler;
 1025         sigemptyset(&sa.sa_mask);
 1026         sigaddset(&sa.sa_mask, SIGINT);
 1027         sa.sa_flags = 0;    /* NOTE: no SA_RESTART on purpose */
 1028         sigaction(SIGTERM, &sa, NULL);
 1029     }
 1030 
 1031     /* Setup SIGUSR1/SIGUSR2 to pause/resume the stream output */
 1032     sigemptyset(&sa.sa_mask);
 1033     sa.sa_flags = SA_RESTART;
 1034     sa.sa_handler = stream_pause_sighandler;
 1035     sigaction(SIGUSR1, &sa, NULL);
 1036     sa.sa_handler = stream_resume_sighandler;
 1037     sigaction(SIGUSR2, &sa, NULL);
 1038 
 1039     /* Make stdout buffered - we will flush it explicitly */
 1040     setbuf(stdout, NULL);
 1041 
 1042     /* Run! */
 1043     result = run(dump_descriptor, dump_stream, stream_timeout,
 1044                  bus_num, dev_addr, vid, pid, iface_num);
 1045 
 1046     /*
 1047      * Restore signal handlers
 1048      */
 1049     sigaction(SIGINT, NULL, &sa);
 1050     if (sa.sa_handler != SIG_IGN)
 1051         signal(SIGINT, SIG_DFL);
 1052 
 1053     sigaction(SIGTERM, NULL, &sa);
 1054     if (sa.sa_handler != SIG_IGN)
 1055         signal(SIGTERM, SIG_DFL);
 1056 
 1057     /*
 1058      * Reproduce the signal used to stop the program to get proper exit
 1059      * status.
 1060      */
 1061     if (exit_signum != 0)
 1062         raise(exit_signum);
 1063 
 1064     return result;
 1065 }
 1066 
 1067