summaryrefslogtreecommitdiffstats
path: root/usbhid-dump/src
diff options
context:
space:
mode:
Diffstat (limited to 'usbhid-dump/src')
-rw-r--r--usbhid-dump/src/Makefile.am19
-rw-r--r--usbhid-dump/src/Makefile.in609
-rw-r--r--usbhid-dump/src/dev.c72
-rw-r--r--usbhid-dump/src/dev.h57
-rw-r--r--usbhid-dump/src/dev_list.c125
-rw-r--r--usbhid-dump/src/dev_list.h69
-rw-r--r--usbhid-dump/src/iface.c227
-rw-r--r--usbhid-dump/src/iface.h158
-rw-r--r--usbhid-dump/src/iface_list.c220
-rw-r--r--usbhid-dump/src/iface_list.h92
-rw-r--r--usbhid-dump/src/misc.h70
-rw-r--r--usbhid-dump/src/usbhid-dump.c1067
12 files changed, 2785 insertions, 0 deletions
diff --git a/usbhid-dump/src/Makefile.am b/usbhid-dump/src/Makefile.am
new file mode 100644
index 0000000..262bb74
--- /dev/null
+++ b/usbhid-dump/src/Makefile.am
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2010 Nikolai Kondrashov
+
+noinst_HEADERS = \
+ dev.h \
+ dev_list.h \
+ iface.h \
+ iface_list.h \
+ misc.h
+
+bin_PROGRAMS = usbhid-dump
+
+usbhid_dump_SOURCES = \
+ dev.c \
+ dev_list.c \
+ iface.c \
+ iface_list.c \
+ usbhid-dump.c
diff --git a/usbhid-dump/src/Makefile.in b/usbhid-dump/src/Makefile.in
new file mode 100644
index 0000000..5fadc80
--- /dev/null
+++ b/usbhid-dump/src/Makefile.in
@@ -0,0 +1,609 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2010 Nikolai Kondrashov
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+bin_PROGRAMS = usbhid-dump$(EXEEXT)
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_usbhid_dump_OBJECTS = dev.$(OBJEXT) dev_list.$(OBJEXT) \
+ iface.$(OBJEXT) iface_list.$(OBJEXT) usbhid-dump.$(OBJEXT)
+usbhid_dump_OBJECTS = $(am_usbhid_dump_OBJECTS)
+usbhid_dump_LDADD = $(LDADD)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/dev.Po ./$(DEPDIR)/dev_list.Po \
+ ./$(DEPDIR)/iface.Po ./$(DEPDIR)/iface_list.Po \
+ ./$(DEPDIR)/usbhid-dump.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(usbhid_dump_SOURCES)
+DIST_SOURCES = $(usbhid_dump_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBUSB_CFLAGS = @LIBUSB_CFLAGS@
+LIBUSB_LIBS = @LIBUSB_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_HEADERS = \
+ dev.h \
+ dev_list.h \
+ iface.h \
+ iface_list.h \
+ misc.h
+
+usbhid_dump_SOURCES = \
+ dev.c \
+ dev_list.c \
+ iface.c \
+ iface_list.c \
+ usbhid-dump.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+usbhid-dump$(EXEEXT): $(usbhid_dump_OBJECTS) $(usbhid_dump_DEPENDENCIES) $(EXTRA_usbhid_dump_DEPENDENCIES)
+ @rm -f usbhid-dump$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(usbhid_dump_OBJECTS) $(usbhid_dump_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev_list.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iface.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iface_list.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usbhid-dump.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/dev.Po
+ -rm -f ./$(DEPDIR)/dev_list.Po
+ -rm -f ./$(DEPDIR)/iface.Po
+ -rm -f ./$(DEPDIR)/iface_list.Po
+ -rm -f ./$(DEPDIR)/usbhid-dump.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/dev.Po
+ -rm -f ./$(DEPDIR)/dev_list.Po
+ -rm -f ./$(DEPDIR)/iface.Po
+ -rm -f ./$(DEPDIR)/iface_list.Po
+ -rm -f ./$(DEPDIR)/usbhid-dump.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-binPROGRAMS install-data install-data-am \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-html install-html-am install-info install-info-am \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/usbhid-dump/src/dev.c b/usbhid-dump/src/dev.c
new file mode 100644
index 0000000..1978496
--- /dev/null
+++ b/usbhid-dump/src/dev.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - device
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#include "config.h"
+
+#include "dev.h"
+#include <assert.h>
+#include <stdlib.h>
+
+bool
+uhd_dev_valid(const uhd_dev *dev)
+{
+ return dev != NULL &&
+ dev->handle != NULL;
+}
+
+
+enum libusb_error
+uhd_dev_open(libusb_device *lusb_dev,
+ uhd_dev **pdev)
+{
+ enum libusb_error err;
+ uhd_dev *dev;
+
+ assert(lusb_dev != NULL);
+
+ dev = malloc(sizeof(*dev));
+ if (dev == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ dev->next = NULL;
+
+ err = libusb_open(lusb_dev, &dev->handle);
+ if (err != LIBUSB_SUCCESS)
+ {
+ free(dev);
+ return err;
+ }
+
+ assert(uhd_dev_valid(dev));
+
+ if (pdev != NULL)
+ *pdev = dev;
+ else
+ {
+ libusb_close(dev->handle);
+ free(dev);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+
+void
+uhd_dev_close(uhd_dev *dev)
+{
+ if (dev == NULL)
+ return;
+
+ assert(uhd_dev_valid(dev));
+
+ libusb_close(dev->handle);
+ dev->handle = NULL;
+
+ free(dev);
+}
+
+
diff --git a/usbhid-dump/src/dev.h b/usbhid-dump/src/dev.h
new file mode 100644
index 0000000..3f647a5
--- /dev/null
+++ b/usbhid-dump/src/dev.h
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - device
+ *
+ * Copyright (C) 2010-2011 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#ifndef __UHD_DEV_H__
+#define __UHD_DEV_H__
+
+#include <stdbool.h>
+#include <libusb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** usbhid-dump device */
+typedef struct uhd_dev uhd_dev;
+
+struct uhd_dev {
+ uhd_dev *next; /**< Next device in the list */
+ libusb_device_handle *handle; /**< Handle */
+};
+
+/**
+ * Check if a device is valid.
+ *
+ * @param dev Device to check.
+ *
+ * @return True if the device is valid, false otherwise.
+ */
+extern bool uhd_dev_valid(const uhd_dev *dev);
+
+/**
+ * Open a device.
+ *
+ * @param lusb_dev Libusb device.
+ * @param pdev Location for the opened device pointer.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_dev_open(libusb_device *lusb_dev,
+ uhd_dev **pdev);
+
+/**
+ * Close a device.
+ *
+ * @param dev The device to close.
+ */
+extern void uhd_dev_close(uhd_dev *dev);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __UHD_DEV_H__ */
diff --git a/usbhid-dump/src/dev_list.c b/usbhid-dump/src/dev_list.c
new file mode 100644
index 0000000..9848902
--- /dev/null
+++ b/usbhid-dump/src/dev_list.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - device list
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#include "config.h"
+
+#include "misc.h"
+#include "dev_list.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+bool
+uhd_dev_list_valid(const uhd_dev *list)
+{
+ UHD_DEV_LIST_FOR_EACH(list, list)
+ if (!uhd_dev_valid(list))
+ return false;
+
+ return true;
+}
+
+
+void
+uhd_dev_list_close(uhd_dev *list)
+{
+ uhd_dev *next;
+
+ for (; list != NULL; list = next)
+ {
+ next = list->next;
+ uhd_dev_close(list);
+ }
+}
+
+
+enum libusb_error
+uhd_dev_list_open(libusb_context *ctx,
+ uint8_t bus_num, uint8_t dev_addr,
+ uint16_t vid, uint16_t pid,
+ uhd_dev **plist)
+{
+ enum libusb_error err = LIBUSB_ERROR_OTHER;
+ libusb_device **lusb_list = NULL;
+ ssize_t num;
+ ssize_t idx;
+ libusb_device *lusb_dev;
+ struct libusb_device_descriptor desc;
+ uhd_dev *list = NULL;
+ uhd_dev *dev;
+
+ assert(ctx != NULL);
+
+ /* Retrieve libusb device list */
+ num = libusb_get_device_list(ctx, &lusb_list);
+ if (num == LIBUSB_ERROR_NO_MEM)
+ {
+ err = num;
+ goto cleanup;
+ }
+
+ /* Find and open the devices */
+ for (idx = 0; idx < num; idx++)
+ {
+ lusb_dev = lusb_list[idx];
+
+ /* Skip devices not matching bus_num/dev_addr mask */
+ if ((bus_num != UHD_BUS_NUM_ANY &&
+ libusb_get_bus_number(lusb_dev) != bus_num) ||
+ (dev_addr != UHD_DEV_ADDR_ANY &&
+ libusb_get_device_address(lusb_dev) != dev_addr))
+ continue;
+
+ /* Skip devices not matching vendor/product mask */
+ if (vid != UHD_VID_ANY || pid != UHD_PID_ANY)
+ {
+ err = libusb_get_device_descriptor(lusb_dev, &desc);
+ if (err != LIBUSB_SUCCESS)
+ goto cleanup;
+
+ if ((vid != UHD_VID_ANY && vid != desc.idVendor) ||
+ (pid != UHD_PID_ANY && pid != desc.idProduct))
+ continue;
+ }
+
+ /* Open and append the device to the list */
+ err = uhd_dev_open(lusb_dev, &dev);
+ if (err != LIBUSB_SUCCESS)
+ goto cleanup;
+
+ dev->next = list;
+ list = dev;
+ }
+
+ /* Free the libusb device list freeing unused devices */
+ libusb_free_device_list(lusb_list, true);
+ lusb_list = NULL;
+
+ /* Output device list, if requested */
+ assert(uhd_dev_list_valid(list));
+ if (plist != NULL)
+ {
+ *plist = list;
+ list = NULL;
+ }
+
+ /* Done! */
+ err = LIBUSB_SUCCESS;
+
+cleanup:
+
+ /* Close the device list if not output */
+ uhd_dev_list_close(list);
+
+ /* Free the libusb device list along with devices */
+ if (lusb_list != NULL)
+ libusb_free_device_list(lusb_list, true);
+
+ return err;
+}
+
+
diff --git a/usbhid-dump/src/dev_list.h b/usbhid-dump/src/dev_list.h
new file mode 100644
index 0000000..5c70ad1
--- /dev/null
+++ b/usbhid-dump/src/dev_list.h
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - device list
+ *
+ * Copyright (C) 2010-2011 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#ifndef __UHD_DEV_LIST_H__
+#define __UHD_DEV_LIST_H__
+
+#include <stddef.h>
+#include <stdint.h>
+#include "dev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Check if a device list is valid.
+ *
+ * @param list Device list to check.
+ *
+ * @return True if the device list is valid, false otherwise.
+ */
+extern bool uhd_dev_list_valid(const uhd_dev *list);
+
+/**
+ * Close every device in a device list.
+ *
+ * @param list The device list to close.
+ */
+extern void uhd_dev_list_close(uhd_dev *list);
+
+/**
+ * Iterate over a device list.
+ *
+ * @param _dev Loop device variable.
+ * @param _list Device list to iterate over.
+ */
+#define UHD_DEV_LIST_FOR_EACH(_dev, _list) \
+ for (_dev = _list; _dev != NULL; _dev = _dev->next)
+
+/**
+ * Open a list of devices optionally matching bus number/device address and
+ * vendor/product IDs.
+ *
+ * @param ctx Libusb context.
+ * @param bus_num Bus number, or 0 for any bus.
+ * @param dev_addr Device address, or 0 for any address.
+ * @param vid Vendor ID, or 0 for any vendor.
+ * @param pid Product ID, or 0 for any product.
+ * @param plist Location for the resulting device list head; could be
+ * NULL.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_dev_list_open(libusb_context *ctx,
+ uint8_t bus_num,
+ uint8_t dev_addr,
+ uint16_t vid,
+ uint16_t pid,
+ uhd_dev **plist);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __UHD_DEV_LIST_H__ */
diff --git a/usbhid-dump/src/iface.c b/usbhid-dump/src/iface.c
new file mode 100644
index 0000000..7bcf28b
--- /dev/null
+++ b/usbhid-dump/src/iface.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - interface
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#include "config.h"
+
+#include "iface.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+bool
+uhd_iface_valid(const uhd_iface *iface)
+{
+ return iface != NULL &&
+ uhd_dev_valid(iface->dev) &&
+ iface->number < UINT8_MAX &&
+ strlen(iface->addr_str) == (sizeof(iface->addr_str) - 1);
+}
+
+uhd_iface *
+uhd_iface_new(const uhd_dev *dev,
+ uint8_t number,
+ uint8_t int_in_ep_addr,
+ uint16_t int_in_ep_maxp,
+ uint16_t rd_len)
+{
+ uhd_iface *iface;
+ libusb_device *lusb_dev;
+ int rc;
+
+ iface = malloc(sizeof(*iface));
+ if (iface == NULL)
+ return NULL;
+
+ iface->next = NULL;
+ iface->dev = dev;
+ iface->number = number;
+ iface->int_in_ep_addr = int_in_ep_addr;
+ iface->int_in_ep_maxp = int_in_ep_maxp;
+ iface->rd_len = rd_len;
+ iface->detached = false;
+ iface->claimed = false;
+ iface->submitted = false;
+
+ /* Format address string */
+ lusb_dev = libusb_get_device(dev->handle);
+ rc = snprintf(iface->addr_str, sizeof(iface->addr_str),
+ "%.3hhu:%.3hhu:%.3hhu",
+ libusb_get_bus_number(lusb_dev),
+ libusb_get_device_address(lusb_dev),
+ number);
+ (void)rc;
+ assert(rc == (sizeof(iface->addr_str) - 1));
+
+ assert(uhd_iface_valid(iface));
+
+ return iface;
+}
+
+
+void
+uhd_iface_free(uhd_iface *iface)
+{
+ if (iface == NULL)
+ return;
+
+ assert(uhd_iface_valid(iface));
+
+ free(iface);
+}
+
+
+enum libusb_error
+uhd_iface_detach(uhd_iface *iface)
+{
+ enum libusb_error err;
+
+ assert(uhd_iface_valid(iface));
+
+ err = libusb_detach_kernel_driver(iface->dev->handle, iface->number);
+ if (err == LIBUSB_SUCCESS)
+ iface->detached = true;
+ else if (err != LIBUSB_ERROR_NOT_FOUND)
+ return err;
+
+ return LIBUSB_SUCCESS;
+}
+
+
+enum libusb_error
+uhd_iface_attach(uhd_iface *iface)
+{
+ enum libusb_error err;
+
+ assert(uhd_iface_valid(iface));
+
+ if (iface->detached)
+ {
+ err = libusb_attach_kernel_driver(iface->dev->handle,
+ iface->number);
+ if (err != LIBUSB_SUCCESS)
+ return err;
+ iface->detached = false;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+
+enum libusb_error
+uhd_iface_claim(uhd_iface *iface)
+{
+ enum libusb_error err;
+
+ assert(uhd_iface_valid(iface));
+
+ err = libusb_claim_interface(iface->dev->handle, iface->number);
+ if (err != LIBUSB_SUCCESS)
+ return err;
+
+ iface->claimed = true;
+
+ return LIBUSB_SUCCESS;
+}
+
+
+enum libusb_error
+uhd_iface_release(uhd_iface *iface)
+{
+ enum libusb_error err;
+
+ assert(uhd_iface_valid(iface));
+
+ err = libusb_release_interface(iface->dev->handle, iface->number);
+ if (err != LIBUSB_SUCCESS)
+ return err;
+
+ iface->claimed = false;
+
+ return LIBUSB_SUCCESS;
+}
+
+
+enum libusb_error
+uhd_iface_clear_halt(uhd_iface *iface)
+{
+ enum libusb_error err;
+
+ assert(uhd_iface_valid(iface));
+
+ err = libusb_clear_halt(iface->dev->handle, iface->int_in_ep_addr);
+ if (err != LIBUSB_SUCCESS)
+ return err;
+
+ return LIBUSB_SUCCESS;
+}
+
+
+enum libusb_error
+uhd_iface_set_idle(const uhd_iface *iface,
+ uint8_t duration,
+ unsigned int timeout)
+{
+ int rc;
+
+ assert(uhd_iface_valid(iface));
+
+ rc = libusb_control_transfer(iface->dev->handle,
+ /* host->device, class, interface */
+ 0x21,
+ /* Set_Idle */
+ 0x0A,
+ /* duration for all report IDs */
+ duration << 8,
+ /* interface */
+ iface->number,
+ NULL, 0,
+ timeout);
+ /*
+ * Ignoring EPIPE, which means a STALL handshake, which is OK on
+ * control pipes and indicates request is not supported.
+ * See USB 2.0 spec, 8.4.5 Handshake Packets
+ */
+ if (rc < 0 && rc != LIBUSB_ERROR_PIPE)
+ return rc;
+
+ return LIBUSB_SUCCESS;
+}
+
+
+enum libusb_error
+uhd_iface_set_protocol(const uhd_iface *iface,
+ bool report,
+ unsigned int timeout)
+{
+ int rc;
+
+ assert(uhd_iface_valid(iface));
+
+ rc = libusb_control_transfer(iface->dev->handle,
+ /* host->device, class, interface */
+ 0x21,
+ /* Set_Protocol */
+ 0x0B,
+ /* 0 - boot, 1 - report */
+ report ? 1 : 0,
+ /* interface */
+ iface->number,
+ NULL, 0,
+ timeout);
+ /*
+ * Ignoring EPIPE, which means a STALL handshake, which is OK on
+ * control pipes and indicates request is not supported.
+ * See USB 2.0 spec, 8.4.5 Handshake Packets
+ */
+ if (rc < 0 && rc != LIBUSB_ERROR_PIPE)
+ return rc;
+
+ return LIBUSB_SUCCESS;
+}
+
+
diff --git a/usbhid-dump/src/iface.h b/usbhid-dump/src/iface.h
new file mode 100644
index 0000000..0e1da6b
--- /dev/null
+++ b/usbhid-dump/src/iface.h
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - interface
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#ifndef __UHD_IFACE_H__
+#define __UHD_IFACE_H__
+
+#include <stdint.h>
+#include "dev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** usbhid-dump interface */
+typedef struct uhd_iface uhd_iface;
+
+struct uhd_iface {
+ uhd_iface *next;
+ const uhd_dev *dev; /**< Device */
+ uint8_t number; /**< Interface number */
+ char addr_str[12]; /**< Address string */
+ uint8_t int_in_ep_addr; /**< Interrupt IN EP address */
+ uint16_t int_in_ep_maxp; /**< Interrupt IN EP maximum
+ packet size */
+ uint16_t rd_len; /**< Report descriptor length */
+ bool detached; /**< True if the interface was
+ detached from the kernel
+ driver, false otherwise */
+ bool claimed; /**< True if the interface was
+ claimed */
+ /*
+ * This is somewhat hackish and doesn't belong here, since theoretically
+ * there could be more than one transfer submitted for an interface.
+ * However, we don't do it yet. This flag is used to track transfer
+ * cancellation during stream dumping.
+ */
+ bool submitted; /**< True if an asynchronous
+ transfer has been submitted
+ for the interface */
+};
+
+/**
+ * Check if an interface is valid.
+ *
+ * @param iface Interface.
+ *
+ * @return True if the interface is valid, false otherwise.
+ */
+extern bool uhd_iface_valid(const uhd_iface *iface);
+
+/**
+ * Create a new interface.
+ *
+ * @param handle Device handle.
+ * @param number Interface number.
+ * @param int_in_ep_addr Interrupt in endpoint address.
+ * @param int_in_ep_maxp Interrupt in endpoint maximum packet size.
+ * @param rd_len Report descriptor length.
+ *
+ * @return New interface or NULL, if failed to allocate.
+ */
+extern uhd_iface *uhd_iface_new(const uhd_dev *dev,
+ uint8_t number,
+ uint8_t int_in_ep_addr,
+ uint16_t int_in_ep_maxp,
+ uint16_t rd_len);
+
+/**
+ * Free an interface.
+ *
+ * @param iface The interface to free, could be NULL.
+ */
+extern void uhd_iface_free(uhd_iface *iface);
+
+/**
+ * Detach an interface from its kernel driver (if any).
+ *
+ * @param iface The interface to detach.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_detach(uhd_iface *iface);
+
+/**
+ * Attach an interface to its kernel driver (if detached before).
+ *
+ * @param iface The interface to attach.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_attach(uhd_iface *iface);
+
+/**
+ * Claim an interface.
+ *
+ * @param iface The interface to claim.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_claim(uhd_iface *iface);
+
+/**
+ * Set idle duration on an interface; ignore errors indicating missing
+ * support.
+ *
+ * @param iface The interface to set idle duration on.
+ * @param duration The duration in 4 ms steps starting from 4 ms.
+ * @param timeout The request timeout, ms.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_set_idle(
+ const uhd_iface *iface,
+ uint8_t duration,
+ unsigned int timeout);
+
+/**
+ * Set HID protocol on an interface; ignore errors indicating missing
+ * support.
+ *
+ * @param iface The interface to set idle duration on.
+ * @param report True for "report" protocol, false for "boot" protocol.
+ * @param timeout The request timeout, ms.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_set_protocol(
+ const uhd_iface *iface,
+ bool report,
+ unsigned int timeout);
+
+/**
+ * Clear halt condition on the input interrupt endpoint of an interface.
+ *
+ * @param iface The interface to clear halt condition on.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_clear_halt(uhd_iface *iface);
+
+/**
+ * Release an interface (if claimed before).
+ *
+ * @param iface The interface to release.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_release(uhd_iface *iface);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __UHD_IFACE_H__ */
diff --git a/usbhid-dump/src/iface_list.c b/usbhid-dump/src/iface_list.c
new file mode 100644
index 0000000..3c82504
--- /dev/null
+++ b/usbhid-dump/src/iface_list.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - interface list
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#include "config.h"
+
+#include "iface_list.h"
+#include "misc.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+bool
+uhd_iface_list_valid(const uhd_iface *list)
+{
+ UHD_IFACE_LIST_FOR_EACH(list, list)
+ if (!uhd_iface_valid(list))
+ return false;
+
+ return true;
+}
+
+
+size_t
+uhd_iface_list_len(const uhd_iface *list)
+{
+ size_t len = 0;
+
+ UHD_IFACE_LIST_FOR_EACH(list, list)
+ len++;
+
+ return len;
+}
+
+
+void
+uhd_iface_list_free(uhd_iface *list)
+{
+ uhd_iface *next;
+
+ for (; list != NULL; list = next)
+ {
+ next = list->next;
+ uhd_iface_free(list);
+ }
+}
+
+
+enum libusb_error
+uhd_iface_list_new(uhd_dev *dev_list,
+ uhd_iface **plist)
+{
+ enum libusb_error err = LIBUSB_ERROR_OTHER;
+
+ uhd_dev *dev;
+ struct libusb_config_descriptor *config = NULL;
+ const struct libusb_interface *lusb_iface;
+ const struct libusb_interface_descriptor *iface_desc;
+ const uhd_hid_descriptor *hid_desc;
+ const uhd_hid_descriptor_extra *hid_desc_extra;
+ uint16_t rd_len;
+ const struct libusb_endpoint_descriptor *ep_list;
+ uint8_t ep_num;
+ const struct libusb_endpoint_descriptor *ep;
+ uhd_iface *list = NULL;
+ uhd_iface *iface;
+
+ assert(uhd_dev_list_valid(dev_list));
+
+ UHD_DEV_LIST_FOR_EACH(dev, dev_list)
+ {
+ /* Retrieve active configuration descriptor */
+ err = libusb_get_active_config_descriptor(libusb_get_device(dev->handle),
+ &config);
+ if (err != LIBUSB_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Build the matching interface list
+ */
+
+ /* For each interface */
+ for (lusb_iface = config->interface;
+ lusb_iface - config->interface < config->bNumInterfaces;
+ lusb_iface++)
+ {
+ /* Skip interfaces with altsettings */
+ if (lusb_iface->num_altsetting != 1)
+ continue;
+
+ iface_desc = lusb_iface->altsetting;
+
+ /* Skip non-HID interfaces */
+ if (iface_desc->bInterfaceClass != LIBUSB_CLASS_HID)
+ continue;
+
+ /*
+ * Try to retrieve report descriptor length
+ */
+ rd_len = UHD_MAX_DESCRIPTOR_SIZE;
+ /* If interface descriptor has space for a HID descriptor */
+ if (iface_desc->extra_length >= (int)sizeof(uhd_hid_descriptor))
+ {
+ hid_desc = (const uhd_hid_descriptor *)iface_desc->extra;
+ /* If this is truly a HID class descriptor */
+ if (hid_desc->bDescriptorType == LIBUSB_DT_HID)
+ {
+ /* For each extra HID descriptor entry */
+ for (hid_desc_extra = hid_desc->extra;
+ hid_desc_extra <
+ hid_desc->extra + hid_desc->bNumDescriptors &&
+ (uint8_t *)hid_desc_extra <
+ (uint8_t *)hid_desc + hid_desc->bLength &&
+ (unsigned char *)hid_desc_extra <
+ iface_desc->extra +
+ iface_desc->extra_length;
+ hid_desc_extra++) {
+ /* If this is a report descriptor entry */
+ if (hid_desc_extra->bDescriptorType ==
+ LIBUSB_DT_REPORT)
+ {
+ rd_len = hid_desc_extra->wDescriptorLength;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Retrieve endpoint list */
+ ep_list = iface_desc->endpoint;
+ ep_num = iface_desc->bNumEndpoints;
+
+ /* For each endpoint */
+ for (ep = ep_list; (ep - ep_list) < ep_num; ep++)
+ {
+ /* Skip non-interrupt and non-in endpoints */
+ if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) !=
+ LIBUSB_TRANSFER_TYPE_INTERRUPT ||
+ (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) !=
+ LIBUSB_ENDPOINT_IN)
+ continue;
+
+ /* Create the interface */
+ iface = uhd_iface_new(
+ dev,
+ iface_desc->bInterfaceNumber,
+ ep->bEndpointAddress, ep->wMaxPacketSize,
+ rd_len);
+ if (iface == NULL)
+ {
+ err = LIBUSB_ERROR_NO_MEM;
+ goto cleanup;
+ }
+
+ /* Add the interface */
+ iface->next = list;
+ list = iface;
+
+ break;
+ }
+ }
+
+ /* Free the config descriptor */
+ libusb_free_config_descriptor(config);
+ config = NULL;
+ }
+
+ /* Output the resulting list, if requested */
+ assert(uhd_iface_list_valid(list));
+ if (plist != NULL)
+ {
+ *plist = list;
+ list = NULL;
+ }
+
+ /* Done! */
+ err = LIBUSB_SUCCESS;
+
+cleanup:
+
+ libusb_free_config_descriptor(config);
+ uhd_iface_list_free(list);
+
+ return err;
+}
+
+
+uhd_iface *
+uhd_iface_list_fltr_by_num(uhd_iface *list,
+ uint8_t number)
+{
+ uhd_iface *prev;
+ uhd_iface *iface;
+ uhd_iface *next;
+
+ assert(uhd_iface_list_valid(list));
+ assert(number < UINT8_MAX);
+
+ for (prev = NULL, iface = list; iface != NULL; iface = next)
+ {
+ next = iface->next;
+ if (iface->number == number)
+ prev = iface;
+ else
+ {
+ if (prev == NULL)
+ list = next;
+ else
+ prev->next = next;
+ uhd_iface_free(iface);
+ }
+ }
+
+ return list;
+}
+
+
diff --git a/usbhid-dump/src/iface_list.h b/usbhid-dump/src/iface_list.h
new file mode 100644
index 0000000..d99f761
--- /dev/null
+++ b/usbhid-dump/src/iface_list.h
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - interface list
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#ifndef __UHD_IFACE_LIST_H__
+#define __UHD_IFACE_LIST_H__
+
+#include <stdint.h>
+#include "dev_list.h"
+#include "iface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Check if an interface list is valid.
+ *
+ * @param list Interface list to check.
+ *
+ * @return True if the interface list is valid, false otherwise.
+ */
+extern bool uhd_iface_list_valid(const uhd_iface *list);
+
+/**
+ * Check if an interface list is empty.
+ *
+ * @param list Interface list to check.
+ *
+ * @return True if the interface list is empty, false otherwise.
+ */
+static inline bool
+uhd_iface_list_empty(const uhd_iface *list)
+{
+ return list == NULL;
+}
+
+/**
+ * Calculate length of an interface list.
+ *
+ * @param list The list to calculate length of.
+ *
+ * @return The list length.
+ */
+extern size_t uhd_iface_list_len(const uhd_iface *list);
+
+/**
+ * Free an interface list.
+ *
+ * @param list The interface list to free.
+ */
+extern void uhd_iface_list_free(uhd_iface *list);
+
+/**
+ * Iterate over an interface list.
+ *
+ * @param _iface Loop interface variable.
+ * @param _list Interface list to iterate over.
+ */
+#define UHD_IFACE_LIST_FOR_EACH(_iface, _list) \
+ for (_iface = _list; _iface != NULL; _iface = _iface->next)
+
+/**
+ * Fetch a list of HID interfaces from a device list.
+ *
+ * @param dev_list The device list to fetch interface list from.
+ * @param plist Location for the resulting list head; could be NULL.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_iface_list_new(uhd_dev *dev_list,
+ uhd_iface **plist);
+
+/**
+ * Filter an interface list by an interface number.
+ *
+ * @param plist The original list head.
+ * @param number The interface number to match against.
+ *
+ * @return The resulting list head.
+ */
+extern uhd_iface *uhd_iface_list_fltr_by_num(uhd_iface *list,
+ uint8_t number);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __UHD_IFACE_LIST_H__ */
diff --git a/usbhid-dump/src/misc.h b/usbhid-dump/src/misc.h
new file mode 100644
index 0000000..17b7e6a
--- /dev/null
+++ b/usbhid-dump/src/misc.h
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - miscellaneous declarations
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#ifndef __UHD_MISC_H__
+#define __UHD_MISC_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma pack(1)
+
+/** HID extra descriptor record */
+typedef struct uhd_hid_descriptor_extra uhd_hid_descriptor_extra;
+
+struct uhd_hid_descriptor_extra {
+ uint8_t bDescriptorType;
+ uint16_t wDescriptorLength;
+};
+
+/** HID class-specific descriptor */
+typedef struct uhd_hid_descriptor uhd_hid_descriptor;
+
+struct uhd_hid_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdHID;
+ uint8_t bCountryCode;
+ uint8_t bNumDescriptors;
+ uhd_hid_descriptor_extra extra[1];
+};
+
+#pragma pack()
+
+/**
+ * Maximum descriptor size.
+ *
+ * @note 4096 here is maximum control buffer length.
+ */
+#define UHD_MAX_DESCRIPTOR_SIZE 4096
+
+/** Generic USB I/O timeout, ms */
+#define UHD_IO_TIMEOUT 1000
+
+/** Wildcard bus number */
+#define UHD_BUS_NUM_ANY 0
+
+/** Wildcard device address */
+#define UHD_DEV_ADDR_ANY 0
+
+/** Wildcard vendor ID */
+#define UHD_VID_ANY 0
+
+/** Wildcard product ID */
+#define UHD_PID_ANY 0
+
+/** Wildcard interface number */
+#define UHD_IFACE_NUM_ANY UINT8_MAX
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __UHD_MISC_H__ */
diff --git a/usbhid-dump/src/usbhid-dump.c b/usbhid-dump/src/usbhid-dump.c
new file mode 100644
index 0000000..bd669e2
--- /dev/null
+++ b/usbhid-dump/src/usbhid-dump.c
@@ -0,0 +1,1067 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * usbhid-dump - entry point *
+ *
+ * Copyright (C) 2010-2011 Nikolai Kondrashov <spbnick@gmail.com>
+ */
+
+#include "config.h"
+
+#include "iface_list.h"
+#include "misc.h"
+#include <libusb.h>
+
+#include <assert.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+
+/* Define LIBUSB_CALL for libusb <= 1.0.8 */
+#ifndef LIBUSB_CALL
+#define LIBUSB_CALL
+#endif
+
+#define GENERIC_ERROR(_fmt, _args...) \
+ fprintf(stderr, _fmt "\n", ##_args)
+
+#define IFACE_ERROR(_iface, _fmt, _args...) \
+ GENERIC_ERROR("%s:" _fmt, _iface->addr_str, ##_args)
+
+#define GENERIC_FAILURE(_fmt, _args...) \
+ GENERIC_ERROR("Failed to " _fmt, ##_args)
+
+#define IFACE_FAILURE(_iface, _fmt, _args...) \
+ IFACE_ERROR(_iface, "Failed to " _fmt, ##_args)
+
+#define LIBUSB_FAILURE(_fmt, _args...) \
+ GENERIC_FAILURE(_fmt ": %s", ##_args, libusb_strerror(err))
+
+#define LIBUSB_IFACE_FAILURE(_iface, _fmt, _args...) \
+ IFACE_FAILURE(_iface, _fmt ": %s", ##_args, libusb_strerror(err))
+
+#define ERROR_CLEANUP(_fmt, _args...) \
+ do { \
+ GENERIC_ERROR(_fmt, ##_args); \
+ goto cleanup; \
+ } while (0)
+
+#define FAILURE_CLEANUP(_fmt, _args...) \
+ do { \
+ GENERIC_FAILURE(_fmt, ##_args); \
+ goto cleanup; \
+ } while (0)
+
+#define LIBUSB_FAILURE_CLEANUP(_fmt, _args...) \
+ do { \
+ LIBUSB_FAILURE(_fmt, ##_args); \
+ goto cleanup; \
+ } while (0)
+
+#define LIBUSB_IFACE_FAILURE_CLEANUP(_iface, _fmt, _args...) \
+ do { \
+ LIBUSB_IFACE_FAILURE(_iface, _fmt, ##_args); \
+ goto cleanup; \
+ } while (0)
+
+#define LIBUSB_GUARD(_expr, _fmt, _args...) \
+ do { \
+ err = _expr; \
+ if (err != LIBUSB_SUCCESS) \
+ LIBUSB_FAILURE_CLEANUP(_fmt, ##_args); \
+ } while (0)
+
+#define LIBUSB_IFACE_GUARD(_expr, _iface, _fmt, _args...) \
+ do { \
+ err = _expr; \
+ if (err != LIBUSB_SUCCESS) \
+ LIBUSB_IFACE_FAILURE_CLEANUP(_iface, _fmt, ##_args); \
+ } while (0)
+
+/**< Number of the signal causing the exit */
+static volatile sig_atomic_t exit_signum = 0;
+
+static void
+exit_sighandler(int signum)
+{
+ if (exit_signum == 0)
+ exit_signum = signum;
+}
+
+/**< "Stream paused" flag - non-zero if paused */
+static volatile sig_atomic_t stream_paused = 0;
+
+static void
+stream_pause_sighandler(int signum)
+{
+ (void)signum;
+ stream_paused = 1;
+}
+
+static void
+stream_resume_sighandler(int signum)
+{
+ (void)signum;
+ stream_paused = 0;
+}
+
+/**< "Stream feedback" flag - non-zero if feedback is enabled */
+static volatile sig_atomic_t stream_feedback = 0;
+
+static void
+dump(const uhd_iface *iface,
+ const char *entity,
+ const uint8_t *ptr,
+ size_t len)
+{
+ static const char xd[] = "0123456789ABCDEF";
+ static char buf[] = " XX\n";
+ size_t pos;
+ uint8_t b;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ fprintf(stdout, "%s:%-16s %12llu.%.6u\n",
+ iface->addr_str, entity,
+ (unsigned long long int)tv.tv_sec,
+ (unsigned int)tv.tv_usec);
+
+ for (pos = 1; len > 0; len--, ptr++, pos++)
+ {
+ b = *ptr;
+ buf[1] = xd[b >> 4];
+ buf[2] = xd[b & 0xF];
+
+ (void)fwrite(buf, ((pos % 16 == 0) ? 4 : 3), 1, stdout);
+ }
+
+ if (pos % 16 != 1)
+ fputc('\n', stdout);
+ fputc('\n', stdout);
+
+ fflush(stdout);
+}
+
+
+static bool
+dump_iface_list_descriptor(const uhd_iface *list)
+{
+ const uhd_iface *iface;
+ uint8_t buf[UHD_MAX_DESCRIPTOR_SIZE];
+ int rc;
+ enum libusb_error err;
+
+ UHD_IFACE_LIST_FOR_EACH(iface, list)
+ {
+ if (iface->rd_len > sizeof(buf))
+ {
+ err = LIBUSB_ERROR_NO_MEM;
+ LIBUSB_IFACE_FAILURE(iface, "report descriptor too long: %hu",
+ iface->rd_len);
+ return false;
+ }
+
+ rc = libusb_control_transfer(iface->dev->handle,
+ /* See HID spec, 7.1.1 */
+ 0x81,
+ LIBUSB_REQUEST_GET_DESCRIPTOR,
+ (LIBUSB_DT_REPORT << 8), iface->number,
+ buf, iface->rd_len, UHD_IO_TIMEOUT);
+ if (rc < 0)
+ {
+ err = rc;
+ LIBUSB_IFACE_FAILURE(iface, "retrieve report descriptor");
+ return false;
+ }
+ dump(iface, "DESCRIPTOR", buf, rc);
+ }
+
+ return true;
+}
+
+
+static void LIBUSB_CALL
+dump_iface_list_stream_cb(struct libusb_transfer *transfer)
+{
+ enum libusb_error err;
+ uhd_iface *iface;
+
+ assert(transfer != NULL);
+
+ iface = (uhd_iface *)transfer->user_data;
+ assert(uhd_iface_valid(iface));
+
+ /* Clear interface "has transfer submitted" flag */
+ iface->submitted = false;
+
+ switch (transfer->status)
+ {
+ case LIBUSB_TRANSFER_COMPLETED:
+ /* Dump the result */
+ if (!stream_paused)
+ {
+ dump(iface, "STREAM",
+ transfer->buffer, transfer->actual_length);
+ if (stream_feedback)
+ fputc('.', stderr);
+ }
+ /* Resubmit the transfer */
+ err = libusb_submit_transfer(transfer);
+ if (err != LIBUSB_SUCCESS)
+ LIBUSB_IFACE_FAILURE(iface, "resubmit a transfer");
+ else
+ {
+ /* Set interface "has transfer submitted" flag */
+ iface->submitted = true;
+ }
+ break;
+
+#define MAP(_name, _desc) \
+ case LIBUSB_TRANSFER_##_name: \
+ IFACE_ERROR(iface, _desc); \
+ break
+
+ MAP(ERROR, "Interrupt transfer failed");
+ MAP(TIMED_OUT, "Interrupt transfer timed out");
+ MAP(STALL, "Interrupt transfer halted (endpoint stalled)");
+ MAP(NO_DEVICE, "Device was disconnected");
+ MAP(OVERFLOW, "Interrupt transfer overflowed "
+ "(device sent more data than requested)");
+#undef MAP
+
+ case LIBUSB_TRANSFER_CANCELLED:
+ break;
+ }
+}
+
+
+static const char *
+format_time_interval(unsigned int i)
+{
+ static char buf[128];
+ char *p = buf;
+ unsigned int h = i / (60 * 60 * 1000);
+ unsigned int m = (i % (60 * 60 * 1000)) / (60 * 1000);
+ unsigned int s = (i % (60 * 1000)) / 1000;
+ unsigned int ms = i % 1000;
+
+#define FRACTION(_prev_sum, _name, _val) \
+ do { \
+ if ((_val) > 0) \
+ p += snprintf(p, sizeof(buf) - (p - buf), \
+ "%s%u " _name "%s", \
+ ((_prev_sum) > 0 ? " " : ""), \
+ _val, \
+ (((_val) == 1) ? "" : "s")); \
+ if (p >= (buf + sizeof(buf))) \
+ return buf; \
+ } while (0)
+
+ FRACTION(0, "hour", h);
+ FRACTION(h, "minute", m);
+ FRACTION(h + m, "second", s);
+ FRACTION(h + m + s, "millisecond", ms);
+
+#undef FRACTION
+
+ return buf;
+}
+
+
+static const char *
+format_timeout(unsigned int i)
+{
+ return (i == 0) ? "infinite" : format_time_interval(i);
+}
+
+
+static bool
+dump_iface_list_stream(libusb_context *ctx,
+ uhd_iface *list,
+ unsigned int timeout)
+{
+ bool result = false;
+ enum libusb_error err;
+ size_t transfer_num = 0;
+ struct libusb_transfer **transfer_list = NULL;
+ struct libusb_transfer **ptransfer;
+ uhd_iface *iface;
+ bool submitted = false;
+
+ fprintf(stderr,
+ "Starting dumping interrupt transfer stream\n"
+ "with %s timeout.\n\n",
+ format_timeout(timeout));
+
+ UHD_IFACE_LIST_FOR_EACH(iface, list)
+ {
+ /* Set report protocol */
+ LIBUSB_IFACE_GUARD(uhd_iface_set_protocol(iface, true,
+ UHD_IO_TIMEOUT),
+ iface, "set report protocol");
+ /* Set infinite idle duration */
+ LIBUSB_IFACE_GUARD(uhd_iface_set_idle(iface, 0, UHD_IO_TIMEOUT),
+ iface, "set infinite idle duration");
+ }
+
+ /* Calculate number of interfaces and thus transfers */
+ transfer_num = uhd_iface_list_len(list);
+
+ /* Allocate transfer list */
+ transfer_list = malloc(sizeof(*transfer_list) * transfer_num);
+ if (transfer_list == NULL)
+ FAILURE_CLEANUP("allocate transfer list");
+
+ /* Zero transfer list */
+ for (ptransfer = transfer_list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++)
+ *ptransfer = NULL;
+
+ /* Allocate transfers */
+ for (ptransfer = transfer_list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++)
+ {
+ *ptransfer = libusb_alloc_transfer(0);
+ if (*ptransfer == NULL)
+ FAILURE_CLEANUP("allocate a transfer");
+ /*
+ * Set user_data to NULL explicitly, since libusb_alloc_transfer
+ * does memset to zero only and zero is not NULL, strictly speaking.
+ */
+ (*ptransfer)->user_data = NULL;
+ }
+
+ /* Initialize the transfers as interrupt transfers */
+ for (ptransfer = transfer_list, iface = list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++, iface = iface->next)
+ {
+ void *buf;
+ const size_t len = iface->int_in_ep_maxp;
+
+ /* Allocate the transfer buffer */
+ buf = malloc(len);
+ if (len > 0 && buf == NULL)
+ FAILURE_CLEANUP("allocate a transfer buffer");
+
+ /* Initialize the transfer */
+ libusb_fill_interrupt_transfer(*ptransfer,
+ iface->dev->handle, iface->int_in_ep_addr,
+ buf, len,
+ dump_iface_list_stream_cb,
+ (void *)iface,
+ timeout);
+
+ /* Ask to free the buffer when the transfer is freed */
+ (*ptransfer)->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
+ }
+
+ /* Submit first transfer requests */
+ for (ptransfer = transfer_list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++)
+ {
+ LIBUSB_GUARD(libusb_submit_transfer(*ptransfer),
+ "submit a transfer");
+ /* Set interface "has transfer submitted" flag */
+ ((uhd_iface *)(*ptransfer)->user_data)->submitted = true;
+ /* Set "have any submitted transfers" flag */
+ submitted = true;
+ }
+
+ /* Run the event machine */
+ while (submitted && exit_signum == 0)
+ {
+ /* Handle the transfer events */
+ err = libusb_handle_events(ctx);
+ if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_INTERRUPTED)
+ LIBUSB_FAILURE_CLEANUP("handle transfer events");
+
+ /* Check if there are any submitted transfers left */
+ submitted = false;
+ for (ptransfer = transfer_list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++)
+ {
+ iface = (uhd_iface *)(*ptransfer)->user_data;
+
+ if (iface != NULL && iface->submitted)
+ submitted = true;
+ }
+ }
+
+ /* If all the transfers were terminated unexpectedly */
+ if (transfer_num > 0 && !submitted)
+ ERROR_CLEANUP("No more interfaces to dump");
+
+ result = true;
+
+cleanup:
+
+ /* Cancel the transfers */
+ if (submitted)
+ {
+ submitted = false;
+ for (ptransfer = transfer_list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++)
+ {
+ iface = (uhd_iface *)(*ptransfer)->user_data;
+
+ if (iface != NULL && iface->submitted)
+ {
+ err = libusb_cancel_transfer(*ptransfer);
+ if (err == LIBUSB_SUCCESS)
+ submitted = true;
+ else
+ {
+ LIBUSB_FAILURE("cancel a transfer, ignoring");
+ /*
+ * XXX are we really sure
+ * the transfer won't be finished?
+ */
+ iface->submitted = false;
+ }
+ }
+ }
+ }
+
+ /* Wait for transfer cancellation */
+ while (submitted)
+ {
+ /* Handle cancellation events */
+ err = libusb_handle_events(ctx);
+ if (err != LIBUSB_SUCCESS && err != LIBUSB_ERROR_INTERRUPTED)
+ {
+ LIBUSB_FAILURE("handle transfer cancellation events, "
+ "aborting transfer cancellation");
+ break;
+ }
+
+ /* Check if there are any submitted transfers left */
+ submitted = false;
+ for (ptransfer = transfer_list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++)
+ {
+ iface = (uhd_iface *)(*ptransfer)->user_data;
+
+ if (iface != NULL && iface->submitted)
+ submitted = true;
+ }
+ }
+
+ /*
+ * Free transfer list along with non-submitted transfers and their
+ * buffers.
+ */
+ if (transfer_list != NULL)
+ {
+ for (ptransfer = transfer_list;
+ (size_t)(ptransfer - transfer_list) < transfer_num;
+ ptransfer++)
+ {
+ iface = (uhd_iface *)(*ptransfer)->user_data;
+
+ /*
+ * Only free a transfer if it is not submitted. Better leak some
+ * memory than have some important memory overwritten.
+ */
+ if (iface == NULL || !iface->submitted)
+ libusb_free_transfer(*ptransfer);
+ }
+
+ free(transfer_list);
+ }
+
+ return result;
+}
+
+
+static int
+run(bool dump_descriptor,
+ bool dump_stream,
+ unsigned int stream_timeout,
+ uint8_t bus_num,
+ uint8_t dev_addr,
+ uint16_t vid,
+ uint16_t pid,
+ int iface_num)
+{
+ int result = 1;
+ enum libusb_error err;
+ libusb_context *ctx = NULL;
+ uhd_dev *dev_list = NULL;
+ uhd_iface *iface_list = NULL;
+ uhd_iface *iface;
+
+ /* Create libusb context */
+ LIBUSB_GUARD(libusb_init(&ctx), "create libusb context");
+
+ /* Set libusb debug level to informational only */
+#if HAVE_LIBUSB_SET_OPTION
+ libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
+#else
+ libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_INFO);
+#endif
+
+ /* Open device list */
+ LIBUSB_GUARD(uhd_dev_list_open(ctx, bus_num, dev_addr,
+ vid, pid, &dev_list),
+ "find and open the devices");
+
+ /* Retrieve the list of HID interfaces from the device list */
+ LIBUSB_GUARD(uhd_iface_list_new(dev_list, &iface_list),
+ "find HID interfaces");
+
+ /* Filter the interface list by specified interface number */
+ if (iface_num != UHD_IFACE_NUM_ANY)
+ iface_list = uhd_iface_list_fltr_by_num(iface_list, iface_num);
+
+ /* Check if there are any interfaces left */
+ if (uhd_iface_list_empty(iface_list))
+ ERROR_CLEANUP("No matching HID interfaces");
+
+ /* Detach and claim the interfaces */
+ UHD_IFACE_LIST_FOR_EACH(iface, iface_list)
+ {
+ LIBUSB_IFACE_GUARD(uhd_iface_detach(iface),
+ iface, "detach from the kernel driver");
+ LIBUSB_IFACE_GUARD(uhd_iface_claim(iface),
+ iface, "claim");
+ }
+
+ /* Run with the prepared interface list */
+ result = (!dump_descriptor || dump_iface_list_descriptor(iface_list)) &&
+ (!dump_stream || dump_iface_list_stream(ctx, iface_list,
+ stream_timeout))
+ ? 0
+ : 1;
+
+cleanup:
+
+ /* Release and attach the interfaces back */
+ UHD_IFACE_LIST_FOR_EACH(iface, iface_list)
+ {
+ err = uhd_iface_release(iface);
+ if (err != LIBUSB_SUCCESS)
+ LIBUSB_IFACE_FAILURE(iface, "release");
+
+ err = uhd_iface_attach(iface);
+ if (err != LIBUSB_SUCCESS)
+ LIBUSB_IFACE_FAILURE(iface, "attach to the kernel driver");
+ }
+
+ /* Free the interface list */
+ uhd_iface_list_free(iface_list);
+
+ /* Close the device list */
+ uhd_dev_list_close(dev_list);
+
+ /* Destroy the libusb context */
+ if (ctx != NULL)
+ libusb_exit(ctx);
+
+ return result;
+}
+
+
+static bool
+parse_number_pair(const char *str,
+ int base,
+ long *pn1,
+ long *pn2)
+{
+ const char *p;
+ char *end;
+ long n1;
+ long n2;
+
+ assert(str != NULL);
+
+ p = str;
+
+ /* Skip space (prevent strtol doing so) */
+ while (isspace((int)*p))
+ p++;
+
+ /* Extract the first number */
+ errno = 0;
+ n1 = strtol(p, &end, base);
+ if (errno != 0)
+ return false;
+
+ /* If nothing was read */
+ if (end == p)
+ return false;
+
+ /* Move on */
+ p = end;
+
+ /* Skip space */
+ while (isspace((int)*p))
+ p++;
+
+ /* If it is the end of string */
+ if (*p == '\0')
+ n2 = 0;
+ else
+ {
+ /* If it is not the number separator */
+ if (*p != ':')
+ return false;
+
+ /* Skip the number separator */
+ p++;
+
+ /* Skip space (prevent strtol doing so) */
+ while (isspace((int)*p))
+ p++;
+
+ /* Extract the second number */
+ errno = 0;
+ n2 = strtol(p, &end, base);
+ if (errno != 0)
+ return false;
+ /* If nothing was read */
+ if (end == p)
+ return false;
+
+ /* Move on */
+ p = end;
+
+ /* Skip space */
+ while (isspace((int)*p))
+ p++;
+
+ /* If it is not the end of string */
+ if (*p != '\0')
+ return false;
+ }
+
+ /* Output the numbers */
+ if (pn1 != NULL)
+ *pn1 = n1;
+ if (pn2 != NULL)
+ *pn2 = n2;
+
+ return true;
+}
+
+
+static bool
+parse_address(const char *str,
+ uint8_t *pbus_num,
+ uint8_t *pdev_addr)
+{
+ long bus_num;
+ long dev_addr;
+
+ assert(str != NULL);
+
+ if (!parse_number_pair(str, 10, &bus_num, &dev_addr))
+ return false;
+
+ if (bus_num < 0 || bus_num > UINT8_MAX ||
+ dev_addr < 0 || dev_addr > UINT8_MAX)
+ return false;
+
+ if (pbus_num != NULL)
+ *pbus_num = bus_num;
+ if (pdev_addr != NULL)
+ *pdev_addr = dev_addr;
+
+ return true;
+}
+
+
+static bool
+parse_model(const char *str,
+ uint16_t *pvid,
+ uint16_t *ppid)
+{
+ long vid;
+ long pid;
+
+ assert(str != NULL);
+
+ if (!parse_number_pair(str, 16, &vid, &pid))
+ return false;
+
+ if (vid < 0 || vid > UINT16_MAX ||
+ pid < 0 || pid > UINT16_MAX)
+ return false;
+
+ if (pvid != NULL)
+ *pvid = vid;
+ if (ppid != NULL)
+ *ppid = pid;
+
+ return true;
+}
+
+
+static bool
+parse_iface_num(const char *str,
+ uint8_t *piface_num)
+{
+ long iface_num;
+ const char *p;
+ char *end;
+
+ assert(str != NULL);
+
+ p = str;
+
+ /* Skip space (prevent strtol doing so) */
+ while (isspace((int)*p))
+ p++;
+
+ /* Extract interface number */
+ errno = 0;
+ iface_num = strtol(p, &end, 10);
+ if (errno != 0 || end == p || iface_num < 0 || iface_num > UINT8_MAX)
+ return false;
+
+ /* Output interface number */
+ if (piface_num != NULL)
+ *piface_num = iface_num;
+
+ return true;
+}
+
+
+static bool
+parse_timeout(const char *str,
+ unsigned int *ptimeout)
+{
+ long long timeout;
+ const char *p;
+ char *end;
+
+ assert(str != NULL);
+
+ p = str;
+
+ /* Skip space (prevent strtoll doing so) */
+ while (isspace((int)*p))
+ p++;
+
+ /* Extract timeout */
+ errno = 0;
+ timeout = strtoll(p, &end, 10);
+ if (errno != 0 || end == p || timeout < 0 || timeout > UINT_MAX)
+ return false;
+
+ /* Output timeout */
+ if (ptimeout != NULL)
+ *ptimeout = timeout;
+
+ return true;
+}
+
+
+static bool
+version(FILE *stream)
+{
+ return
+ fprintf(
+ stream,
+PACKAGE_STRING "\n"
+"Copyright (C) 2010 Nikolai Kondrashov\n"
+"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
+"\n"
+"This is free software: you are free to change and redistribute it.\n"
+"There is NO WARRANTY, to the extent permitted by law.\n") >= 0;
+}
+
+
+static bool
+usage(FILE *stream, const char *name)
+{
+ return
+ fprintf(
+ stream,
+"Usage: %s [OPTION]...\n"
+"Dump USB device HID report descriptor(s) and/or stream(s).\n"
+"\n"
+"Options:\n"
+" -h, --help output this help message and exit\n"
+" -v, --version output version information and exit\n"
+"\n"
+" -s, -a, --address=bus[:dev] limit interfaces by bus number\n"
+" (1-255) and device address (1-255),\n"
+" decimal; zeroes match any\n"
+" -d, -m, --model=vid[:pid] limit interfaces by vendor and\n"
+" product IDs (0001-ffff), hexadecimal;\n"
+" zeroes match any\n"
+" -i, --interface=NUMBER limit interfaces by number (0-254),\n"
+" decimal; 255 matches any\n"
+"\n"
+" -e, --entity=STRING what to dump: either \"descriptor\",\n"
+" \"stream\" or \"all\"; value can be\n"
+" abbreviated\n"
+"\n"
+" -t, --stream-timeout=NUMBER stream interrupt transfer timeout, ms;\n"
+" zero means infinity\n"
+" -p, --stream-paused start with the stream dump output\n"
+" paused\n"
+" -f, --stream-feedback enable stream dumping feedback: for\n"
+" every transfer dumped a dot is\n"
+" printed to stderr\n"
+"\n"
+"Default options: --stream-timeout=60000 --entity=descriptor\n"
+"\n"
+"Signals:\n"
+" USR1/USR2 pause/resume the stream dump output\n"
+"\n",
+ name) >= 0;
+}
+
+
+typedef enum opt_val {
+ OPT_VAL_HELP = 'h',
+ OPT_VAL_VERSION = 'v',
+ OPT_VAL_ADDRESS = 'a',
+ OPT_VAL_ADDRESS_COMP = 's',
+ OPT_VAL_MODEL = 'm',
+ OPT_VAL_MODEL_COMP = 'd',
+ OPT_VAL_INTERFACE = 'i',
+ OPT_VAL_ENTITY = 'e',
+ OPT_VAL_STREAM_TIMEOUT = 't',
+ OPT_VAL_STREAM_PAUSED = 'p',
+ OPT_VAL_STREAM_FEEDBACK = 'f',
+} opt_val;
+
+
+static const struct option long_opt_list[] = {
+ {.val = OPT_VAL_HELP,
+ .name = "help",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_VERSION,
+ .name = "version",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_ADDRESS,
+ .name = "address",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_MODEL,
+ .name = "model",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_INTERFACE,
+ .name = "interface",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_ENTITY,
+ .name = "entity",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_STREAM_TIMEOUT,
+ .name = "stream-timeout",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_STREAM_PAUSED,
+ .name = "stream-paused",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_STREAM_FEEDBACK,
+ .name = "stream-feedback",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = 0,
+ .name = NULL,
+ .has_arg = 0,
+ .flag = NULL}
+};
+
+
+static const char *short_opt_list = "hvs:a:d:m:i:e:t:pf";
+
+
+int
+main(int argc, char **argv)
+{
+ int result;
+
+ const char *name;
+
+ int c;
+
+ uint8_t bus_num = UHD_BUS_NUM_ANY;
+ uint8_t dev_addr = UHD_DEV_ADDR_ANY;
+
+ uint16_t vid = UHD_VID_ANY;
+ uint16_t pid = UHD_PID_ANY;
+
+ uint8_t iface_num = UHD_IFACE_NUM_ANY;
+
+ bool dump_descriptor = true;
+ bool dump_stream = false;
+ unsigned int stream_timeout = 60000;
+
+ struct sigaction sa;
+
+ /*
+ * Extract program invocation name
+ */
+ name = strrchr(argv[0], '/');
+ if (name == NULL)
+ name = argv[0];
+ else
+ name++;
+
+#define USAGE_ERROR(_fmt, _args...) \
+ do { \
+ fprintf(stderr, _fmt "\n", ##_args); \
+ usage(stderr, name); \
+ return 1; \
+ } while (0)
+
+ /*
+ * Parse command line arguments
+ */
+ while ((c = getopt_long(argc, argv,
+ short_opt_list, long_opt_list, NULL)) >= 0)
+ {
+ switch (c)
+ {
+ case OPT_VAL_HELP:
+ usage(stdout, name);
+ return 0;
+ break;
+ case OPT_VAL_VERSION:
+ version(stdout);
+ return 0;
+ break;
+ case OPT_VAL_ADDRESS:
+ case OPT_VAL_ADDRESS_COMP:
+ if (!parse_address(optarg, &bus_num, &dev_addr))
+ USAGE_ERROR("Invalid device address \"%s\"", optarg);
+ break;
+ case OPT_VAL_MODEL:
+ case OPT_VAL_MODEL_COMP:
+ if (!parse_model(optarg, &vid, &pid))
+ USAGE_ERROR("Invalid model \"%s\"", optarg);
+ break;
+ case OPT_VAL_INTERFACE:
+ if (!parse_iface_num(optarg, &iface_num))
+ USAGE_ERROR("Invalid interface number \"%s\"", optarg);
+ break;
+ case OPT_VAL_ENTITY:
+ if (strncmp(optarg, "descriptor", strlen(optarg)) == 0)
+ {
+ dump_descriptor = true;
+ dump_stream = false;
+ }
+ else if (strncmp(optarg, "stream", strlen(optarg)) == 0)
+ {
+ dump_descriptor = false;
+ dump_stream = true;
+ }
+ else if (strncmp(optarg, "all", strlen(optarg)) == 0)
+ {
+ dump_descriptor = true;
+ dump_stream = true;
+ }
+ else
+ USAGE_ERROR("Unknown entity \"%s\"", optarg);
+
+ break;
+ case OPT_VAL_STREAM_TIMEOUT:
+ if (!parse_timeout(optarg, &stream_timeout))
+ USAGE_ERROR("Invalid stream timeout \"%s\"", optarg);
+ break;
+ case OPT_VAL_STREAM_PAUSED:
+ stream_paused = 1;
+ break;
+ case OPT_VAL_STREAM_FEEDBACK:
+ stream_feedback = 1;
+ break;
+ case '?':
+ usage(stderr, name);
+ return 1;
+ break;
+ }
+ }
+
+ /*
+ * Verify positional arguments
+ */
+ if (optind < argc)
+ USAGE_ERROR("Positional arguments are not accepted");
+
+ /*
+ * Setup signal handlers
+ */
+ /* Setup SIGINT to terminate gracefully */
+ sigaction(SIGINT, NULL, &sa);
+ if (sa.sa_handler != SIG_IGN)
+ {
+ sa.sa_handler = exit_sighandler;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGTERM);
+ sa.sa_flags = 0; /* NOTE: no SA_RESTART on purpose */
+ sigaction(SIGINT, &sa, NULL);
+ }
+
+ /* Setup SIGTERM to terminate gracefully */
+ sigaction(SIGTERM, NULL, &sa);
+ if (sa.sa_handler != SIG_IGN)
+ {
+ sa.sa_handler = exit_sighandler;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGINT);
+ sa.sa_flags = 0; /* NOTE: no SA_RESTART on purpose */
+ sigaction(SIGTERM, &sa, NULL);
+ }
+
+ /* Setup SIGUSR1/SIGUSR2 to pause/resume the stream output */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = stream_pause_sighandler;
+ sigaction(SIGUSR1, &sa, NULL);
+ sa.sa_handler = stream_resume_sighandler;
+ sigaction(SIGUSR2, &sa, NULL);
+
+ /* Make stdout buffered - we will flush it explicitly */
+ setbuf(stdout, NULL);
+
+ /* Run! */
+ result = run(dump_descriptor, dump_stream, stream_timeout,
+ bus_num, dev_addr, vid, pid, iface_num);
+
+ /*
+ * Restore signal handlers
+ */
+ sigaction(SIGINT, NULL, &sa);
+ if (sa.sa_handler != SIG_IGN)
+ signal(SIGINT, SIG_DFL);
+
+ sigaction(SIGTERM, NULL, &sa);
+ if (sa.sa_handler != SIG_IGN)
+ signal(SIGTERM, SIG_DFL);
+
+ /*
+ * Reproduce the signal used to stop the program to get proper exit
+ * status.
+ */
+ if (exit_signum != 0)
+ raise(exit_signum);
+
+ return result;
+}
+
+