├── .gitignore ├── COPYING ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── include └── ccan │ ├── build_assert │ ├── LICENSE │ ├── _info │ ├── build_assert.h │ └── test │ │ ├── compile_fail-expr.c │ │ ├── compile_fail.c │ │ ├── compile_ok.c │ │ └── run-BUILD_ASSERT_OR_ZERO.c │ ├── check_type │ ├── LICENSE │ ├── _info │ ├── check_type.h │ └── test │ │ ├── compile_fail-check_type.c │ │ ├── compile_fail-check_type_unsigned.c │ │ ├── compile_fail-check_types_match.c │ │ └── run.c │ ├── container_of │ ├── LICENSE │ ├── _info │ ├── container_of.h │ └── test │ │ ├── compile_fail-bad-type.c │ │ ├── compile_fail-types.c │ │ ├── compile_fail-var-types.c │ │ └── run.c │ ├── list │ ├── LICENSE │ ├── _info │ ├── list.c │ ├── list.h │ └── test │ │ ├── compile_ok-constant.c │ │ ├── helper.c │ │ ├── helper.h │ │ ├── run-CCAN_LIST_DEBUG.c │ │ ├── run-check-corrupt.c │ │ ├── run-check-nonconst.c │ │ ├── run-list_del_from-assert.c │ │ ├── run-list_prev-list_next.c │ │ ├── run-prepend_list.c │ │ ├── run-single-eval.c │ │ ├── run-with-debug.c │ │ └── run.c │ └── str │ ├── LICENSE │ ├── _info │ ├── debug.c │ ├── str.c │ ├── str.h │ ├── str_debug.h │ └── test │ ├── compile_fail-STR_MAX_CHARS.c │ ├── compile_fail-isalnum.c │ ├── compile_fail-isalpha.c │ ├── compile_fail-isascii.c │ ├── compile_fail-isblank.c │ ├── compile_fail-iscntrl.c │ ├── compile_fail-isdigit.c │ ├── compile_fail-islower.c │ ├── compile_fail-isprint.c │ ├── compile_fail-ispunct.c │ ├── compile_fail-isspace.c │ ├── compile_fail-isupper.c │ ├── compile_fail-isxdigit.c │ ├── compile_fail-strchr.c │ ├── compile_fail-strrchr.c │ ├── compile_fail-strstr.c │ ├── debug.c │ ├── run-STR_MAX_CHARS.c │ └── run.c ├── licenses ├── BSD-MIT └── CC0 ├── meson.build ├── python └── report.py ├── src ├── Makefile.am ├── hid-recorder.c ├── hid-recorder.txt ├── hid-replay.c └── hid-replay.txt └── tools ├── HID_editor.ui ├── capture_usbmon.py ├── data ├── 0000_undefined.hut ├── 0001_generic_desktop.hut ├── 0002_simulation_controls.hut ├── 0003_vr_controls.hut ├── 0004_sports_controls.hut ├── 0005_gaming_controls.hut ├── 0006_generic_device_controls.hut ├── 0007_keyboard.hut ├── 0008_leds.hut ├── 0009_button.hut ├── 000a_ordinals.hut ├── 000b_telephony_devices.hut ├── 000c_consumer_devices.hut ├── 000d_digitizers.hut ├── 000e_haptic_page.hut ├── 0010_unicode.hut ├── 0014_alphanumeric_display.hut ├── 0014_auxiliary_display.hut ├── 0020_sensor.hut ├── 0040_medical_instruments.hut ├── 0080_monitor.hut ├── 0081_monitor_enumerated_values.hut ├── 0082_vesa_virtual_controls.hut ├── 0083_vesa_command.hut ├── 0084_power_device.hut ├── 0085_battery_system.hut ├── 008c_bar_code_scanner.hut ├── 008d_scale.hut ├── 008e_magnetic_stripe_reading.hut ├── 0090_camera_control.hut ├── 0091_arcade_page_oaaf.hut ├── 0092_gaming_device.hut ├── f1d0_fast_identity_online_alliance.hut ├── ff00_vendor_defined_page_1.hut └── ff0d_wacom.hut ├── editor.py ├── hid.py ├── parse_hid.py ├── parse_hut.py ├── parse_rdesc.py ├── plot_evtest.py ├── plot_hid.py ├── plot_traces.py └── usbmon2hid-replay.py /.gitignore: -------------------------------------------------------------------------------- 1 | aclocal.m4 2 | autom4te.cache/ 3 | config-aux/ 4 | autoscan.log 5 | ChangeLog 6 | compile 7 | config.guess 8 | config.h 9 | config.h.in 10 | config.log 11 | config-ml.in 12 | config.py 13 | config.status 14 | config.status.lineno 15 | config.sub 16 | configure 17 | configure.scan 18 | depcomp 19 | .deps/ 20 | install-sh 21 | .libs/ 22 | libtool 23 | libtool.m4 24 | ltmain.sh 25 | lt~obsolete.m4 26 | ltoptions.m4 27 | ltsugar.m4 28 | ltversion.m4 29 | Makefile 30 | Makefile.in 31 | mdate-sh 32 | missing 33 | mkinstalldirs 34 | *.pc 35 | py-compile 36 | stamp-h? 37 | symlink-tree 38 | texinfo.tex 39 | ylwrap 40 | 41 | *~ 42 | *.[0-9] 43 | *.[0-9]x 44 | *.bak 45 | *.bin 46 | core 47 | *.dll 48 | *.exe 49 | *-ISO*.bdf 50 | *-JIS*.bdf 51 | *-KOI8*.bdf 52 | *.kld 53 | *.ko 54 | *.ko.cmd 55 | *.lai 56 | *.l[oa] 57 | *.[oa] 58 | *.obj 59 | *.patch 60 | *.so 61 | *.pcf.gz 62 | *.pdb 63 | *.pyc 64 | *.tar.bz2 65 | *.tar.gz 66 | 67 | src/hid-replay 68 | src/hid-recorder 69 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | 3 | pkgconfigdir = $(libdir)/pkgconfig 4 | 5 | INCLUDES = $(top_srcdir)/include/ 6 | 7 | .PHONY: ChangeLog INSTALL 8 | INSTALL: 9 | $(INSTALL_CMD) 10 | 11 | ChangeLog: 12 | @if test -d ".git"; then \ 13 | cmd=git; \ 14 | else \ 15 | cmd="echo could not generate change"; \ 16 | fi; \ 17 | $${cmd} log > ChangeLog; 18 | 19 | dist-hook: ChangeLog INSTALL 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **THIS REPOSITORY IS OBSOLETE** 2 | 3 | Use [hid-tools](https://gitlab.freedesktop.org/libevdev/hid-tools) instead. 4 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | srcdir=`dirname $0` 4 | test -z "$srcdir" && srcdir=. 5 | 6 | ORIGDIR=`pwd` 7 | cd $srcdir 8 | 9 | autoreconf -v --install || exit 1 10 | cd $ORIGDIR || exit $? 11 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Initialize Autoconf 2 | AC_PREREQ([2.60]) 3 | AC_INIT([HID device replay], 4 | [0.7.1], 5 | [], 6 | [hid-replay]) 7 | AC_CONFIG_SRCDIR([Makefile.am]) 8 | AC_CONFIG_HEADERS([config.h]) 9 | AC_CONFIG_AUX_DIR([config-aux]) 10 | 11 | # Initialize Automake 12 | AM_INIT_AUTOMAKE([foreign dist-bzip2]) 13 | AC_USE_SYSTEM_EXTENSIONS 14 | 15 | LIB_VERSION=1:0:0 16 | AC_SUBST([LIB_VERSION]) 17 | 18 | # Checks for programs. 19 | AC_PROG_CC 20 | AC_PROG_INSTALL 21 | 22 | AC_CHECK_HEADERS([linux/uhid.h], 23 | AM_CONDITIONAL(HAVE_UHID, true), 24 | [AM_CONDITIONAL(HAVE_UHID, false) 25 | AC_MSG_WARN([uhid.h not found - cannot create hid-replay])]) 26 | 27 | 28 | # man page generation 29 | AC_ARG_VAR([XMLTO], [Path to xmlto command]) 30 | AC_PATH_PROG([XMLTO], [xmlto]) 31 | AC_ARG_VAR([ASCIIDOC], [Path to asciidoc command]) 32 | AC_PATH_PROG([ASCIIDOC], [asciidoc]) 33 | AM_CONDITIONAL([HAVE_DOCTOOLS], [test "x$XMLTO" != "x" && test "x$ASCIIDOC" != "x"]) 34 | if test "x$XMLTO" = "x" || test "x$ASCIIDOC" = "x"; then 35 | AC_MSG_WARN([xmlto or asciidoc not found - cannot create man pages without it]) 36 | fi 37 | 38 | AC_WARNING([ 39 | ****************************************************** 40 | * This tool is obsolete. Please switch to hid-tools: * 41 | * https://gitlab.freedesktop.org/libevdev/hid-tools * 42 | ******************************************************]) 43 | 44 | AC_CONFIG_FILES([Makefile 45 | src/Makefile]) 46 | AC_OUTPUT 47 | -------------------------------------------------------------------------------- /include/ccan/build_assert/LICENSE: -------------------------------------------------------------------------------- 1 | ../../licenses/CC0 -------------------------------------------------------------------------------- /include/ccan/build_assert/_info: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "config.h" 4 | 5 | /** 6 | * build_assert - routines for build-time assertions 7 | * 8 | * This code provides routines which will cause compilation to fail should some 9 | * assertion be untrue: such failures are preferable to run-time assertions, 10 | * but much more limited since they can only depends on compile-time constants. 11 | * 12 | * These assertions are most useful when two parts of the code must be kept in 13 | * sync: it is better to avoid such cases if possible, but seconds best is to 14 | * detect invalid changes at build time. 15 | * 16 | * For example, a tricky piece of code might rely on a certain element being at 17 | * the start of the structure. To ensure that future changes don't break it, 18 | * you would catch such changes in your code like so: 19 | * 20 | * Example: 21 | * #include 22 | * #include 23 | * 24 | * struct foo { 25 | * char string[5]; 26 | * int x; 27 | * }; 28 | * 29 | * static char *foo_string(struct foo *foo) 30 | * { 31 | * // This trick requires that the string be first in the structure 32 | * BUILD_ASSERT(offsetof(struct foo, string) == 0); 33 | * return (char *)foo; 34 | * } 35 | * 36 | * License: CC0 (Public domain) 37 | * Author: Rusty Russell 38 | */ 39 | int main(int argc, char *argv[]) 40 | { 41 | if (argc != 2) 42 | return 1; 43 | 44 | if (strcmp(argv[1], "depends") == 0) 45 | /* Nothing. */ 46 | return 0; 47 | 48 | return 1; 49 | } 50 | -------------------------------------------------------------------------------- /include/ccan/build_assert/build_assert.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_BUILD_ASSERT_H 3 | #define CCAN_BUILD_ASSERT_H 4 | 5 | /** 6 | * BUILD_ASSERT - assert a build-time dependency. 7 | * @cond: the compile-time condition which must be true. 8 | * 9 | * Your compile will fail if the condition isn't true, or can't be evaluated 10 | * by the compiler. This can only be used within a function. 11 | * 12 | * Example: 13 | * #include 14 | * ... 15 | * static char *foo_to_char(struct foo *foo) 16 | * { 17 | * // This code needs string to be at start of foo. 18 | * BUILD_ASSERT(offsetof(struct foo, string) == 0); 19 | * return (char *)foo; 20 | * } 21 | */ 22 | #define BUILD_ASSERT(cond) \ 23 | do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) 24 | 25 | /** 26 | * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression. 27 | * @cond: the compile-time condition which must be true. 28 | * 29 | * Your compile will fail if the condition isn't true, or can't be evaluated 30 | * by the compiler. This can be used in an expression: its value is "0". 31 | * 32 | * Example: 33 | * #define foo_to_char(foo) \ 34 | * ((char *)(foo) \ 35 | * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0)) 36 | */ 37 | #define BUILD_ASSERT_OR_ZERO(cond) \ 38 | (sizeof(char [1 - 2*!(cond)]) - 1) 39 | 40 | #endif /* CCAN_BUILD_ASSERT_H */ 41 | -------------------------------------------------------------------------------- /include/ccan/build_assert/test/compile_fail-expr.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | #ifdef FAIL 6 | return BUILD_ASSERT_OR_ZERO(1 == 0); 7 | #else 8 | return 0; 9 | #endif 10 | } 11 | -------------------------------------------------------------------------------- /include/ccan/build_assert/test/compile_fail.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | #ifdef FAIL 6 | BUILD_ASSERT(1 == 0); 7 | #endif 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /include/ccan/build_assert/test/compile_ok.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | BUILD_ASSERT(1 == 1); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /include/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | plan_tests(1); 7 | ok1(BUILD_ASSERT_OR_ZERO(1 == 1) == 0); 8 | return exit_status(); 9 | } 10 | -------------------------------------------------------------------------------- /include/ccan/check_type/LICENSE: -------------------------------------------------------------------------------- 1 | ../../licenses/CC0 -------------------------------------------------------------------------------- /include/ccan/check_type/_info: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "config.h" 4 | 5 | /** 6 | * check_type - routines for compile time type checking 7 | * 8 | * C has fairly weak typing: ints get automatically converted to longs, signed 9 | * to unsigned, etc. There are some cases where this is best avoided, and 10 | * these macros provide methods for evoking warnings (or build errors) when 11 | * a precise type isn't used. 12 | * 13 | * On compilers which don't support typeof() these routines are less effective, 14 | * since they have to use sizeof() which can only distiguish between types of 15 | * different size. 16 | * 17 | * License: CC0 (Public domain) 18 | * Author: Rusty Russell 19 | */ 20 | int main(int argc, char *argv[]) 21 | { 22 | if (argc != 2) 23 | return 1; 24 | 25 | if (strcmp(argv[1], "depends") == 0) { 26 | #if !HAVE_TYPEOF 27 | printf("ccan/build_assert\n"); 28 | #endif 29 | return 0; 30 | } 31 | 32 | return 1; 33 | } 34 | -------------------------------------------------------------------------------- /include/ccan/check_type/check_type.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_CHECK_TYPE_H 3 | #define CCAN_CHECK_TYPE_H 4 | #include "config.h" 5 | 6 | /** 7 | * check_type - issue a warning or build failure if type is not correct. 8 | * @expr: the expression whose type we should check (not evaluated). 9 | * @type: the exact type we expect the expression to be. 10 | * 11 | * This macro is usually used within other macros to try to ensure that a macro 12 | * argument is of the expected type. No type promotion of the expression is 13 | * done: an unsigned int is not the same as an int! 14 | * 15 | * check_type() always evaluates to 0. 16 | * 17 | * If your compiler does not support typeof, then the best we can do is fail 18 | * to compile if the sizes of the types are unequal (a less complete check). 19 | * 20 | * Example: 21 | * // They should always pass a 64-bit value to _set_some_value! 22 | * #define set_some_value(expr) \ 23 | * _set_some_value((check_type((expr), uint64_t), (expr))) 24 | */ 25 | 26 | /** 27 | * check_types_match - issue a warning or build failure if types are not same. 28 | * @expr1: the first expression (not evaluated). 29 | * @expr2: the second expression (not evaluated). 30 | * 31 | * This macro is usually used within other macros to try to ensure that 32 | * arguments are of identical types. No type promotion of the expressions is 33 | * done: an unsigned int is not the same as an int! 34 | * 35 | * check_types_match() always evaluates to 0. 36 | * 37 | * If your compiler does not support typeof, then the best we can do is fail 38 | * to compile if the sizes of the types are unequal (a less complete check). 39 | * 40 | * Example: 41 | * // Do subtraction to get to enclosing type, but make sure that 42 | * // pointer is of correct type for that member. 43 | * #define container_of(mbr_ptr, encl_type, mbr) \ 44 | * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ 45 | * ((encl_type *) \ 46 | * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) 47 | */ 48 | #if HAVE_TYPEOF 49 | #define check_type(expr, type) \ 50 | ((typeof(expr) *)0 != (type *)0) 51 | 52 | #define check_types_match(expr1, expr2) \ 53 | ((typeof(expr1) *)0 != (typeof(expr2) *)0) 54 | #else 55 | #include 56 | /* Without typeof, we can only test the sizes. */ 57 | #define check_type(expr, type) \ 58 | BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type)) 59 | 60 | #define check_types_match(expr1, expr2) \ 61 | BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2)) 62 | #endif /* HAVE_TYPEOF */ 63 | 64 | #endif /* CCAN_CHECK_TYPE_H */ 65 | -------------------------------------------------------------------------------- /include/ccan/check_type/test/compile_fail-check_type.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | #ifdef FAIL 6 | check_type(argc, char); 7 | #endif 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /include/ccan/check_type/test/compile_fail-check_type_unsigned.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | #ifdef FAIL 6 | #if HAVE_TYPEOF 7 | check_type(argc, unsigned int); 8 | #else 9 | /* This doesn't work without typeof, so just fail */ 10 | #error "Fail without typeof" 11 | #endif 12 | #endif 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /include/ccan/check_type/test/compile_fail-check_types_match.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | unsigned char x = argc; 6 | #ifdef FAIL 7 | check_types_match(argc, x); 8 | #endif 9 | return x; 10 | } 11 | -------------------------------------------------------------------------------- /include/ccan/check_type/test/run.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | int x = 0, y = 0; 7 | 8 | plan_tests(9); 9 | 10 | ok1(check_type(argc, int) == 0); 11 | ok1(check_type(&argc, int *) == 0); 12 | ok1(check_types_match(argc, argc) == 0); 13 | ok1(check_types_match(argc, x) == 0); 14 | ok1(check_types_match(&argc, &x) == 0); 15 | 16 | ok1(check_type(x++, int) == 0); 17 | ok(x == 0, "check_type does not evaluate expression"); 18 | ok1(check_types_match(x++, y++) == 0); 19 | ok(x == 0 && y == 0, "check_types_match does not evaluate expressions"); 20 | 21 | return exit_status(); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/container_of/LICENSE: -------------------------------------------------------------------------------- 1 | ../../licenses/CC0 -------------------------------------------------------------------------------- /include/ccan/container_of/_info: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "config.h" 4 | 5 | /** 6 | * container_of - routine for upcasting 7 | * 8 | * It is often convenient to create code where the caller registers a pointer 9 | * to a generic structure and a callback. The callback might know that the 10 | * pointer points to within a larger structure, and container_of gives a 11 | * convenient and fairly type-safe way of returning to the enclosing structure. 12 | * 13 | * This idiom is an alternative to providing a void * pointer for every 14 | * callback. 15 | * 16 | * Example: 17 | * #include 18 | * #include 19 | * 20 | * struct timer { 21 | * void *members; 22 | * }; 23 | * 24 | * struct info { 25 | * int my_stuff; 26 | * struct timer timer; 27 | * }; 28 | * 29 | * static void register_timer(struct timer *timer) 30 | * { 31 | * //... 32 | * } 33 | * 34 | * static void my_timer_callback(struct timer *timer) 35 | * { 36 | * struct info *info = container_of(timer, struct info, timer); 37 | * printf("my_stuff is %u\n", info->my_stuff); 38 | * } 39 | * 40 | * int main(void) 41 | * { 42 | * struct info info = { .my_stuff = 1 }; 43 | * 44 | * register_timer(&info.timer); 45 | * // ... 46 | * return 0; 47 | * } 48 | * 49 | * License: CC0 (Public domain) 50 | * Author: Rusty Russell 51 | */ 52 | int main(int argc, char *argv[]) 53 | { 54 | if (argc != 2) 55 | return 1; 56 | 57 | if (strcmp(argv[1], "depends") == 0) { 58 | printf("ccan/check_type\n"); 59 | return 0; 60 | } 61 | 62 | return 1; 63 | } 64 | -------------------------------------------------------------------------------- /include/ccan/container_of/container_of.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_CONTAINER_OF_H 3 | #define CCAN_CONTAINER_OF_H 4 | #include 5 | 6 | #include "config.h" 7 | #include 8 | 9 | /** 10 | * container_of - get pointer to enclosing structure 11 | * @member_ptr: pointer to the structure member 12 | * @containing_type: the type this member is within 13 | * @member: the name of this member within the structure. 14 | * 15 | * Given a pointer to a member of a structure, this macro does pointer 16 | * subtraction to return the pointer to the enclosing type. 17 | * 18 | * Example: 19 | * struct foo { 20 | * int fielda, fieldb; 21 | * // ... 22 | * }; 23 | * struct info { 24 | * int some_other_field; 25 | * struct foo my_foo; 26 | * }; 27 | * 28 | * static struct info *foo_to_info(struct foo *foo) 29 | * { 30 | * return container_of(foo, struct info, my_foo); 31 | * } 32 | */ 33 | #define container_of(member_ptr, containing_type, member) \ 34 | ((containing_type *) \ 35 | ((char *)(member_ptr) \ 36 | - container_off(containing_type, member)) \ 37 | + check_types_match(*(member_ptr), ((containing_type *)0)->member)) 38 | 39 | /** 40 | * container_off - get offset to enclosing structure 41 | * @containing_type: the type this member is within 42 | * @member: the name of this member within the structure. 43 | * 44 | * Given a pointer to a member of a structure, this macro does 45 | * typechecking and figures out the offset to the enclosing type. 46 | * 47 | * Example: 48 | * struct foo { 49 | * int fielda, fieldb; 50 | * // ... 51 | * }; 52 | * struct info { 53 | * int some_other_field; 54 | * struct foo my_foo; 55 | * }; 56 | * 57 | * static struct info *foo_to_info(struct foo *foo) 58 | * { 59 | * size_t off = container_off(struct info, my_foo); 60 | * return (void *)((char *)foo - off); 61 | * } 62 | */ 63 | #define container_off(containing_type, member) \ 64 | offsetof(containing_type, member) 65 | 66 | /** 67 | * container_of_var - get pointer to enclosing structure using a variable 68 | * @member_ptr: pointer to the structure member 69 | * @container_var: a pointer of same type as this member's container 70 | * @member: the name of this member within the structure. 71 | * 72 | * Given a pointer to a member of a structure, this macro does pointer 73 | * subtraction to return the pointer to the enclosing type. 74 | * 75 | * Example: 76 | * static struct info *foo_to_i(struct foo *foo) 77 | * { 78 | * struct info *i = container_of_var(foo, i, my_foo); 79 | * return i; 80 | * } 81 | */ 82 | #if HAVE_TYPEOF 83 | #define container_of_var(member_ptr, container_var, member) \ 84 | container_of(member_ptr, typeof(*container_var), member) 85 | #else 86 | #define container_of_var(member_ptr, container_var, member) \ 87 | ((void *)((char *)(member_ptr) - \ 88 | container_off_var(container_var, member))) 89 | #endif 90 | 91 | /** 92 | * container_off_var - get offset of a field in enclosing structure 93 | * @container_var: a pointer to a container structure 94 | * @member: the name of a member within the structure. 95 | * 96 | * Given (any) pointer to a structure and a its member name, this 97 | * macro does pointer subtraction to return offset of member in a 98 | * structure memory layout. 99 | * 100 | */ 101 | #if HAVE_TYPEOF 102 | #define container_off_var(var, member) \ 103 | container_off(typeof(*var), member) 104 | #else 105 | #define container_off_var(var, member) \ 106 | ((const char *)&(var)->member - (const char *)(var)) 107 | #endif 108 | 109 | #endif /* CCAN_CONTAINER_OF_H */ 110 | -------------------------------------------------------------------------------- /include/ccan/container_of/test/compile_fail-bad-type.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct foo { 5 | int a; 6 | char b; 7 | }; 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | struct foo foo = { .a = 1, .b = 2 }; 12 | int *intp = &foo.a; 13 | char *p; 14 | 15 | #ifdef FAIL 16 | /* p is a char *, but this gives a struct foo * */ 17 | p = container_of(intp, struct foo, a); 18 | #else 19 | p = (char *)intp; 20 | #endif 21 | return p == NULL; 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/container_of/test/compile_fail-types.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct foo { 5 | int a; 6 | char b; 7 | }; 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | struct foo foo = { .a = 1, .b = 2 }, *foop; 12 | int *intp = &foo.a; 13 | 14 | #ifdef FAIL 15 | /* b is a char, but intp is an int * */ 16 | foop = container_of(intp, struct foo, b); 17 | #else 18 | foop = NULL; 19 | #endif 20 | (void) foop; /* Suppress unused-but-set-variable warning. */ 21 | return intp == NULL; 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/container_of/test/compile_fail-var-types.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct foo { 5 | int a; 6 | char b; 7 | }; 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | struct foo foo = { .a = 1, .b = 2 }, *foop; 12 | int *intp = &foo.a; 13 | 14 | #ifdef FAIL 15 | /* b is a char, but intp is an int * */ 16 | foop = container_of_var(intp, foop, b); 17 | #if !HAVE_TYPEOF 18 | #error "Unfortunately we don't fail if we don't have typeof." 19 | #endif 20 | #else 21 | foop = NULL; 22 | #endif 23 | (void) foop; /* Suppress unused-but-set-variable warning. */ 24 | return intp == NULL; 25 | } 26 | -------------------------------------------------------------------------------- /include/ccan/container_of/test/run.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct foo { 5 | int a; 6 | char b; 7 | }; 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | struct foo foo = { .a = 1, .b = 2 }; 12 | int *intp = &foo.a; 13 | char *charp = &foo.b; 14 | 15 | plan_tests(8); 16 | ok1(container_of(intp, struct foo, a) == &foo); 17 | ok1(container_of(charp, struct foo, b) == &foo); 18 | ok1(container_of_var(intp, &foo, a) == &foo); 19 | ok1(container_of_var(charp, &foo, b) == &foo); 20 | 21 | ok1(container_off(struct foo, a) == 0); 22 | ok1(container_off(struct foo, b) == offsetof(struct foo, b)); 23 | ok1(container_off_var(&foo, a) == 0); 24 | ok1(container_off_var(&foo, b) == offsetof(struct foo, b)); 25 | return exit_status(); 26 | } 27 | -------------------------------------------------------------------------------- /include/ccan/list/LICENSE: -------------------------------------------------------------------------------- 1 | ../../licenses/BSD-MIT -------------------------------------------------------------------------------- /include/ccan/list/_info: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "config.h" 4 | 5 | /** 6 | * list - double linked list routines 7 | * 8 | * The list header contains routines for manipulating double linked lists. 9 | * It defines two types: struct list_head used for anchoring lists, and 10 | * struct list_node which is usually embedded in the structure which is placed 11 | * in the list. 12 | * 13 | * Example: 14 | * #include 15 | * #include 16 | * #include 17 | * #include 18 | * 19 | * struct parent { 20 | * const char *name; 21 | * struct list_head children; 22 | * unsigned int num_children; 23 | * }; 24 | * 25 | * struct child { 26 | * const char *name; 27 | * struct list_node list; 28 | * }; 29 | * 30 | * int main(int argc, char *argv[]) 31 | * { 32 | * struct parent p; 33 | * struct child *c; 34 | * unsigned int i; 35 | * 36 | * if (argc < 2) 37 | * errx(1, "Usage: %s parent children...", argv[0]); 38 | * 39 | * p.name = argv[1]; 40 | * list_head_init(&p.children); 41 | * p.num_children = 0; 42 | * for (i = 2; i < argc; i++) { 43 | * c = malloc(sizeof(*c)); 44 | * c->name = argv[i]; 45 | * list_add(&p.children, &c->list); 46 | * p.num_children++; 47 | * } 48 | * 49 | * printf("%s has %u children:", p.name, p.num_children); 50 | * list_for_each(&p.children, c, list) 51 | * printf("%s ", c->name); 52 | * printf("\n"); 53 | * return 0; 54 | * } 55 | * 56 | * License: BSD-MIT 57 | * Author: Rusty Russell 58 | */ 59 | int main(int argc, char *argv[]) 60 | { 61 | if (argc != 2) 62 | return 1; 63 | 64 | if (strcmp(argv[1], "depends") == 0) { 65 | printf("ccan/str\n"); 66 | printf("ccan/container_of\n"); 67 | printf("ccan/check_type\n"); 68 | return 0; 69 | } 70 | 71 | return 1; 72 | } 73 | -------------------------------------------------------------------------------- /include/ccan/list/list.c: -------------------------------------------------------------------------------- 1 | /* Licensed under BSD-MIT - see LICENSE file for details */ 2 | #include 3 | #include 4 | #include "list.h" 5 | 6 | static void *corrupt(const char *abortstr, 7 | const struct list_node *head, 8 | const struct list_node *node, 9 | unsigned int count) 10 | { 11 | if (abortstr) { 12 | fprintf(stderr, 13 | "%s: prev corrupt in node %p (%u) of %p\n", 14 | abortstr, node, count, head); 15 | abort(); 16 | } 17 | return NULL; 18 | } 19 | 20 | struct list_node *list_check_node(const struct list_node *node, 21 | const char *abortstr) 22 | { 23 | const struct list_node *p, *n; 24 | int count = 0; 25 | 26 | for (p = node, n = node->next; n != node; p = n, n = n->next) { 27 | count++; 28 | if (n->prev != p) 29 | return corrupt(abortstr, node, n, count); 30 | } 31 | /* Check prev on head node. */ 32 | if (node->prev != p) 33 | return corrupt(abortstr, node, node, 0); 34 | 35 | return (struct list_node *)node; 36 | } 37 | 38 | struct list_head *list_check(const struct list_head *h, const char *abortstr) 39 | { 40 | if (!list_check_node(&h->n, abortstr)) 41 | return NULL; 42 | return (struct list_head *)h; 43 | } 44 | -------------------------------------------------------------------------------- /include/ccan/list/test/compile_ok-constant.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct child { 8 | const char *name; 9 | struct list_node list; 10 | }; 11 | 12 | static bool children(const struct list_head *list) 13 | { 14 | return !list_empty(list); 15 | } 16 | 17 | static const struct child *first_child(const struct list_head *list) 18 | { 19 | return list_top(list, struct child, list); 20 | } 21 | 22 | static const struct child *last_child(const struct list_head *list) 23 | { 24 | return list_tail(list, struct child, list); 25 | } 26 | 27 | static void check_children(const struct list_head *list) 28 | { 29 | list_check(list, "bad child list"); 30 | } 31 | 32 | static void print_children(const struct list_head *list) 33 | { 34 | const struct child *c; 35 | list_for_each(list, c, list) 36 | printf("%s\n", c->name); 37 | } 38 | 39 | int main(void) 40 | { 41 | LIST_HEAD(h); 42 | 43 | children(&h); 44 | first_child(&h); 45 | last_child(&h); 46 | check_children(&h); 47 | print_children(&h); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /include/ccan/list/test/helper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include "helper.h" 7 | 8 | #define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \ 9 | (42) 10 | 11 | struct opaque { 12 | struct list_node list; 13 | size_t secret_offset; 14 | char secret_drawer[42]; 15 | }; 16 | 17 | static bool not_randomized = true; 18 | 19 | struct opaque *create_opaque_blob(void) 20 | { 21 | struct opaque *blob = calloc(1, sizeof(struct opaque)); 22 | 23 | if (not_randomized) { 24 | srandom((int)time(NULL)); 25 | not_randomized = false; 26 | } 27 | 28 | blob->secret_offset = random() % (sizeof(blob->secret_drawer)); 29 | blob->secret_drawer[blob->secret_offset] = 30 | ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING; 31 | 32 | return blob; 33 | } 34 | 35 | bool if_blobs_know_the_secret(struct opaque *blob) 36 | { 37 | bool answer = true; 38 | int i; 39 | for (i = 0; i < sizeof(blob->secret_drawer) / 40 | sizeof(blob->secret_drawer[0]); i++) 41 | if (i != blob->secret_offset) 42 | answer = answer && (blob->secret_drawer[i] == 0); 43 | else 44 | answer = answer && 45 | (blob->secret_drawer[blob->secret_offset] == 46 | ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING); 47 | 48 | return answer; 49 | } 50 | 51 | void destroy_opaque_blob(struct opaque *blob) 52 | { 53 | free(blob); 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /include/ccan/list/test/helper.h: -------------------------------------------------------------------------------- 1 | /* These are in a separate C file so we can test undefined structures. */ 2 | struct opaque; 3 | typedef struct opaque opaque_t; 4 | 5 | opaque_t *create_opaque_blob(void); 6 | bool if_blobs_know_the_secret(opaque_t *blob); 7 | void destroy_opaque_blob(opaque_t *blob); 8 | 9 | 10 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-CCAN_LIST_DEBUG.c: -------------------------------------------------------------------------------- 1 | /* Check that CCAN_LIST_DEBUG works */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* We don't actually want it to exit... */ 10 | static jmp_buf aborted; 11 | #define abort() longjmp(aborted, 1) 12 | 13 | #define fprintf my_fprintf 14 | static char printf_buffer[1000]; 15 | 16 | static int my_fprintf(FILE *stream, const char *format, ...) 17 | { 18 | va_list ap; 19 | int ret; 20 | va_start(ap, format); 21 | ret = vsprintf(printf_buffer, format, ap); 22 | va_end(ap); 23 | return ret; 24 | } 25 | 26 | #define CCAN_LIST_DEBUG 1 27 | #include 28 | #include 29 | #include 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | struct list_head list; 34 | struct list_node n1; 35 | char expect[100]; 36 | 37 | plan_tests(2); 38 | /* Empty list. */ 39 | list.n.next = &list.n; 40 | list.n.prev = &list.n; 41 | ok1(list_check(&list, NULL) == &list); 42 | 43 | /* Bad back ptr */ 44 | list.n.prev = &n1; 45 | 46 | /* Aborting version. */ 47 | sprintf(expect, "run-CCAN_LIST_DEBUG.c:50: prev corrupt in node %p (0) of %p\n", 48 | &list, &list); 49 | if (setjmp(aborted) == 0) { 50 | assert(list_empty(&list)); 51 | fail("list_empty on empty with bad back ptr didn't fail!"); 52 | } else { 53 | /* __FILE__ might give full path. */ 54 | int prep = strlen(printf_buffer) - strlen(expect); 55 | ok1(prep >= 0 && strcmp(printf_buffer + prep, expect) == 0); 56 | } 57 | 58 | return exit_status(); 59 | } 60 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-check-corrupt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* We don't actually want it to exit... */ 9 | static jmp_buf aborted; 10 | #define abort() longjmp(aborted, 1) 11 | 12 | #define fprintf my_fprintf 13 | static char printf_buffer[1000]; 14 | 15 | static int my_fprintf(FILE *stream, const char *format, ...) 16 | { 17 | va_list ap; 18 | int ret; 19 | va_start(ap, format); 20 | ret = vsprintf(printf_buffer, format, ap); 21 | va_end(ap); 22 | return ret; 23 | } 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | struct list_head list; 32 | struct list_node n1; 33 | char expect[100]; 34 | 35 | plan_tests(9); 36 | /* Empty list. */ 37 | list.n.next = &list.n; 38 | list.n.prev = &list.n; 39 | ok1(list_check(&list, NULL) == &list); 40 | 41 | /* Bad back ptr */ 42 | list.n.prev = &n1; 43 | /* Non-aborting version. */ 44 | ok1(list_check(&list, NULL) == NULL); 45 | 46 | /* Aborting version. */ 47 | sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", 48 | &list, &list); 49 | if (setjmp(aborted) == 0) { 50 | list_check(&list, "test message"); 51 | fail("list_check on empty with bad back ptr didn't fail!"); 52 | } else { 53 | ok1(strcmp(printf_buffer, expect) == 0); 54 | } 55 | 56 | /* n1 in list. */ 57 | list.n.next = &n1; 58 | list.n.prev = &n1; 59 | n1.prev = &list.n; 60 | n1.next = &list.n; 61 | ok1(list_check(&list, NULL) == &list); 62 | ok1(list_check_node(&n1, NULL) == &n1); 63 | 64 | /* Bad back ptr */ 65 | n1.prev = &n1; 66 | ok1(list_check(&list, NULL) == NULL); 67 | ok1(list_check_node(&n1, NULL) == NULL); 68 | 69 | /* Aborting version. */ 70 | sprintf(expect, "test message: prev corrupt in node %p (1) of %p\n", 71 | &n1, &list); 72 | if (setjmp(aborted) == 0) { 73 | list_check(&list, "test message"); 74 | fail("list_check on n1 bad back ptr didn't fail!"); 75 | } else { 76 | ok1(strcmp(printf_buffer, expect) == 0); 77 | } 78 | 79 | sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", 80 | &n1, &n1); 81 | if (setjmp(aborted) == 0) { 82 | list_check_node(&n1, "test message"); 83 | fail("list_check_node on n1 bad back ptr didn't fail!"); 84 | } else { 85 | ok1(strcmp(printf_buffer, expect) == 0); 86 | } 87 | 88 | return exit_status(); 89 | } 90 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-check-nonconst.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "helper.h" 5 | 6 | struct child { 7 | const char *name; 8 | struct list_node list; 9 | }; 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | struct child c1, c2; 14 | struct list_head list = LIST_HEAD_INIT(list); 15 | 16 | plan_tests(1); 17 | 18 | list_add(&list, &c1.list); 19 | list_add_tail(list_check(&list, "Bad list!"), &c2.list); 20 | list_del_from(list_check(&list, "Bad list!"), 21 | list_check_node(&c2.list, "Bad node!")); 22 | list_del_from(list_check(&list, "Bad list!"), 23 | list_check_node(&c1.list, "Bad node!")); 24 | ok1(list_empty(list_check(&list, "Bad emptied list"))); 25 | 26 | return exit_status(); 27 | } 28 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-list_del_from-assert.c: -------------------------------------------------------------------------------- 1 | #define CCAN_LIST_DEBUG 1 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | struct list_head list1, list2; 13 | struct list_node n1, n2, n3; 14 | pid_t child; 15 | int status; 16 | 17 | plan_tests(1); 18 | list_head_init(&list1); 19 | list_head_init(&list2); 20 | list_add(&list1, &n1); 21 | list_add(&list2, &n2); 22 | list_add_tail(&list2, &n3); 23 | 24 | child = fork(); 25 | if (child) { 26 | wait(&status); 27 | } else { 28 | /* This should abort. */ 29 | list_del_from(&list1, &n3); 30 | exit(0); 31 | } 32 | 33 | ok1(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT); 34 | list_del_from(&list2, &n3); 35 | return exit_status(); 36 | } 37 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-list_prev-list_next.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "helper.h" 5 | 6 | struct parent { 7 | const char *name; 8 | unsigned int num_children; 9 | struct list_head children; 10 | }; 11 | 12 | struct child { 13 | const char *name; 14 | struct list_node list; 15 | }; 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | struct parent parent; 20 | struct child c1, c2, c3; 21 | const struct parent *p; 22 | const struct child *c; 23 | 24 | plan_tests(20); 25 | parent.num_children = 0; 26 | list_head_init(&parent.children); 27 | 28 | c1.name = "c1"; 29 | list_add(&parent.children, &c1.list); 30 | 31 | ok1(list_next(&parent.children, &c1, list) == NULL); 32 | ok1(list_prev(&parent.children, &c1, list) == NULL); 33 | 34 | c2.name = "c2"; 35 | list_add_tail(&parent.children, &c2.list); 36 | 37 | ok1(list_next(&parent.children, &c1, list) == &c2); 38 | ok1(list_prev(&parent.children, &c1, list) == NULL); 39 | ok1(list_next(&parent.children, &c2, list) == NULL); 40 | ok1(list_prev(&parent.children, &c2, list) == &c1); 41 | 42 | c3.name = "c3"; 43 | list_add_tail(&parent.children, &c3.list); 44 | 45 | ok1(list_next(&parent.children, &c1, list) == &c2); 46 | ok1(list_prev(&parent.children, &c1, list) == NULL); 47 | ok1(list_next(&parent.children, &c2, list) == &c3); 48 | ok1(list_prev(&parent.children, &c2, list) == &c1); 49 | ok1(list_next(&parent.children, &c3, list) == NULL); 50 | ok1(list_prev(&parent.children, &c3, list) == &c2); 51 | 52 | /* Const variants */ 53 | p = &parent; 54 | c = &c2; 55 | ok1(list_next(&p->children, &c1, list) == &c2); 56 | ok1(list_prev(&p->children, &c1, list) == NULL); 57 | ok1(list_next(&p->children, c, list) == &c3); 58 | ok1(list_prev(&p->children, c, list) == &c1); 59 | ok1(list_next(&parent.children, c, list) == &c3); 60 | ok1(list_prev(&parent.children, c, list) == &c1); 61 | ok1(list_next(&p->children, &c3, list) == NULL); 62 | ok1(list_prev(&p->children, &c3, list) == &c2); 63 | 64 | return exit_status(); 65 | } 66 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-prepend_list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static bool list_expect(struct list_head *h, ...) 7 | { 8 | va_list ap; 9 | struct list_node *n = &h->n, *expected; 10 | 11 | va_start(ap, h); 12 | while ((expected = va_arg(ap, struct list_node *)) != NULL) { 13 | n = n->next; 14 | if (n != expected) 15 | return false; 16 | } 17 | return (n->next == &h->n); 18 | } 19 | 20 | int main(int argc, char *argv[]) 21 | { 22 | struct list_head h1, h2; 23 | struct list_node n[4]; 24 | 25 | plan_tests(40); 26 | 27 | list_head_init(&h1); 28 | list_head_init(&h2); 29 | 30 | /* Append an empty list to an empty list. */ 31 | list_append_list(&h1, &h2); 32 | ok1(list_empty(&h1)); 33 | ok1(list_empty(&h2)); 34 | ok1(list_check(&h1, NULL)); 35 | ok1(list_check(&h2, NULL)); 36 | 37 | /* Prepend an empty list to an empty list. */ 38 | list_prepend_list(&h1, &h2); 39 | ok1(list_empty(&h1)); 40 | ok1(list_empty(&h2)); 41 | ok1(list_check(&h1, NULL)); 42 | ok1(list_check(&h2, NULL)); 43 | 44 | /* Append an empty list to a non-empty list */ 45 | list_add(&h1, &n[0]); 46 | list_append_list(&h1, &h2); 47 | ok1(list_empty(&h2)); 48 | ok1(list_check(&h1, NULL)); 49 | ok1(list_check(&h2, NULL)); 50 | ok1(list_expect(&h1, &n[0], NULL)); 51 | 52 | /* Prepend an empty list to a non-empty list */ 53 | list_prepend_list(&h1, &h2); 54 | ok1(list_empty(&h2)); 55 | ok1(list_check(&h1, NULL)); 56 | ok1(list_check(&h2, NULL)); 57 | ok1(list_expect(&h1, &n[0], NULL)); 58 | 59 | /* Append a non-empty list to an empty list. */ 60 | list_append_list(&h2, &h1); 61 | ok1(list_empty(&h1)); 62 | ok1(list_check(&h1, NULL)); 63 | ok1(list_check(&h2, NULL)); 64 | ok1(list_expect(&h2, &n[0], NULL)); 65 | 66 | /* Prepend a non-empty list to an empty list. */ 67 | list_prepend_list(&h1, &h2); 68 | ok1(list_empty(&h2)); 69 | ok1(list_check(&h1, NULL)); 70 | ok1(list_check(&h2, NULL)); 71 | ok1(list_expect(&h1, &n[0], NULL)); 72 | 73 | /* Prepend a non-empty list to non-empty list. */ 74 | list_add(&h2, &n[1]); 75 | list_prepend_list(&h1, &h2); 76 | ok1(list_empty(&h2)); 77 | ok1(list_check(&h1, NULL)); 78 | ok1(list_check(&h2, NULL)); 79 | ok1(list_expect(&h1, &n[1], &n[0], NULL)); 80 | 81 | /* Append a non-empty list to non-empty list. */ 82 | list_add(&h2, &n[2]); 83 | list_append_list(&h1, &h2); 84 | ok1(list_empty(&h2)); 85 | ok1(list_check(&h1, NULL)); 86 | ok1(list_check(&h2, NULL)); 87 | ok1(list_expect(&h1, &n[1], &n[0], &n[2], NULL)); 88 | 89 | /* Prepend a 2-entry list to a 2-entry list. */ 90 | list_del_from(&h1, &n[2]); 91 | list_add(&h2, &n[2]); 92 | list_add_tail(&h2, &n[3]); 93 | list_prepend_list(&h1, &h2); 94 | ok1(list_empty(&h2)); 95 | ok1(list_check(&h1, NULL)); 96 | ok1(list_check(&h2, NULL)); 97 | ok1(list_expect(&h1, &n[2], &n[3], &n[1], &n[0], NULL)); 98 | 99 | /* Append a 2-entry list to a 2-entry list. */ 100 | list_del_from(&h1, &n[2]); 101 | list_del_from(&h1, &n[3]); 102 | list_add(&h2, &n[2]); 103 | list_add_tail(&h2, &n[3]); 104 | list_append_list(&h1, &h2); 105 | ok1(list_empty(&h2)); 106 | ok1(list_check(&h1, NULL)); 107 | ok1(list_check(&h2, NULL)); 108 | ok1(list_expect(&h1, &n[1], &n[0], &n[2], &n[3], NULL)); 109 | 110 | return exit_status(); 111 | } 112 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-single-eval.c: -------------------------------------------------------------------------------- 1 | /* Make sure macros only evaluate their args once. */ 2 | #include 3 | #include 4 | #include 5 | 6 | struct parent { 7 | const char *name; 8 | struct list_head children; 9 | unsigned int num_children; 10 | int eval_count; 11 | }; 12 | 13 | struct child { 14 | const char *name; 15 | struct list_node list; 16 | }; 17 | 18 | static LIST_HEAD(static_list); 19 | 20 | #define ref(obj, counter) ((counter)++, (obj)) 21 | 22 | int main(int argc, char *argv[]) 23 | { 24 | struct parent parent; 25 | struct child c1, c2, c3, *c, *n; 26 | unsigned int i; 27 | unsigned int static_count = 0, parent_count = 0, list_count = 0, 28 | node_count = 0; 29 | struct list_head list = LIST_HEAD_INIT(list); 30 | 31 | plan_tests(74); 32 | /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ 33 | ok1(list_empty(ref(&static_list, static_count))); 34 | ok1(static_count == 1); 35 | ok1(list_check(ref(&static_list, static_count), NULL)); 36 | ok1(static_count == 2); 37 | ok1(list_empty(ref(&list, list_count))); 38 | ok1(list_count == 1); 39 | ok1(list_check(ref(&list, list_count), NULL)); 40 | ok1(list_count == 2); 41 | 42 | parent.num_children = 0; 43 | list_head_init(ref(&parent.children, parent_count)); 44 | ok1(parent_count == 1); 45 | /* Test list_head_init */ 46 | ok1(list_empty(ref(&parent.children, parent_count))); 47 | ok1(parent_count == 2); 48 | ok1(list_check(ref(&parent.children, parent_count), NULL)); 49 | ok1(parent_count == 3); 50 | 51 | c2.name = "c2"; 52 | list_add(ref(&parent.children, parent_count), &c2.list); 53 | ok1(parent_count == 4); 54 | /* Test list_add and !list_empty. */ 55 | ok1(!list_empty(ref(&parent.children, parent_count))); 56 | ok1(parent_count == 5); 57 | ok1(c2.list.next == &parent.children.n); 58 | ok1(c2.list.prev == &parent.children.n); 59 | ok1(parent.children.n.next == &c2.list); 60 | ok1(parent.children.n.prev == &c2.list); 61 | /* Test list_check */ 62 | ok1(list_check(ref(&parent.children, parent_count), NULL)); 63 | ok1(parent_count == 6); 64 | 65 | c1.name = "c1"; 66 | list_add(ref(&parent.children, parent_count), &c1.list); 67 | ok1(parent_count == 7); 68 | /* Test list_add and !list_empty. */ 69 | ok1(!list_empty(ref(&parent.children, parent_count))); 70 | ok1(parent_count == 8); 71 | ok1(c2.list.next == &parent.children.n); 72 | ok1(c2.list.prev == &c1.list); 73 | ok1(parent.children.n.next == &c1.list); 74 | ok1(parent.children.n.prev == &c2.list); 75 | ok1(c1.list.next == &c2.list); 76 | ok1(c1.list.prev == &parent.children.n); 77 | /* Test list_check */ 78 | ok1(list_check(ref(&parent.children, parent_count), NULL)); 79 | ok1(parent_count == 9); 80 | 81 | c3.name = "c3"; 82 | list_add_tail(ref(&parent.children, parent_count), &c3.list); 83 | ok1(parent_count == 10); 84 | /* Test list_add_tail and !list_empty. */ 85 | ok1(!list_empty(ref(&parent.children, parent_count))); 86 | ok1(parent_count == 11); 87 | ok1(parent.children.n.next == &c1.list); 88 | ok1(parent.children.n.prev == &c3.list); 89 | ok1(c1.list.next == &c2.list); 90 | ok1(c1.list.prev == &parent.children.n); 91 | ok1(c2.list.next == &c3.list); 92 | ok1(c2.list.prev == &c1.list); 93 | ok1(c3.list.next == &parent.children.n); 94 | ok1(c3.list.prev == &c2.list); 95 | /* Test list_check */ 96 | ok1(list_check(ref(&parent.children, parent_count), NULL)); 97 | ok1(parent_count == 12); 98 | 99 | /* Test list_check_node */ 100 | ok1(list_check_node(&c1.list, NULL)); 101 | ok1(list_check_node(&c2.list, NULL)); 102 | ok1(list_check_node(&c3.list, NULL)); 103 | 104 | /* Test list_top */ 105 | ok1(list_top(ref(&parent.children, parent_count), struct child, list) == &c1); 106 | ok1(parent_count == 13); 107 | 108 | /* Test list_tail */ 109 | ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == &c3); 110 | ok1(parent_count == 14); 111 | 112 | /* Test list_for_each. */ 113 | i = 0; 114 | list_for_each(&parent.children, c, list) { 115 | switch (i++) { 116 | case 0: 117 | ok1(c == &c1); 118 | break; 119 | case 1: 120 | ok1(c == &c2); 121 | break; 122 | case 2: 123 | ok1(c == &c3); 124 | break; 125 | } 126 | if (i > 2) 127 | break; 128 | } 129 | ok1(i == 3); 130 | 131 | /* Test list_for_each_safe, list_del and list_del_from. */ 132 | i = 0; 133 | list_for_each_safe(&parent.children, c, n, list) { 134 | switch (i++) { 135 | case 0: 136 | ok1(c == &c1); 137 | list_del(ref(&c->list, node_count)); 138 | ok1(node_count == 1); 139 | break; 140 | case 1: 141 | ok1(c == &c2); 142 | list_del_from(ref(&parent.children, parent_count), 143 | ref(&c->list, node_count)); 144 | ok1(node_count == 2); 145 | break; 146 | case 2: 147 | ok1(c == &c3); 148 | list_del_from(ref(&parent.children, parent_count), 149 | ref(&c->list, node_count)); 150 | ok1(node_count == 3); 151 | break; 152 | } 153 | ok1(list_check(ref(&parent.children, parent_count), NULL)); 154 | if (i > 2) 155 | break; 156 | } 157 | ok1(i == 3); 158 | ok1(parent_count == 19); 159 | ok1(list_empty(ref(&parent.children, parent_count))); 160 | ok1(parent_count == 20); 161 | 162 | /* Test list_top/list_tail on empty list. */ 163 | ok1(list_top(ref(&parent.children, parent_count), struct child, list) == NULL); 164 | ok1(parent_count == 21); 165 | ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == NULL); 166 | ok1(parent_count == 22); 167 | return exit_status(); 168 | } 169 | -------------------------------------------------------------------------------- /include/ccan/list/test/run-with-debug.c: -------------------------------------------------------------------------------- 1 | /* Just like run.c, but with all debug checks enabled. */ 2 | #define CCAN_LIST_DEBUG 1 3 | #include 4 | -------------------------------------------------------------------------------- /include/ccan/list/test/run.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "helper.h" 5 | 6 | struct parent { 7 | const char *name; 8 | struct list_head children; 9 | unsigned int num_children; 10 | }; 11 | 12 | struct child { 13 | const char *name; 14 | struct list_node list; 15 | }; 16 | 17 | static LIST_HEAD(static_list); 18 | 19 | int main(int argc, char *argv[]) 20 | { 21 | struct parent parent; 22 | struct child c1, c2, c3, *c, *n; 23 | unsigned int i; 24 | struct list_head list = LIST_HEAD_INIT(list); 25 | opaque_t *q, *nq; 26 | struct list_head opaque_list = LIST_HEAD_INIT(opaque_list); 27 | 28 | plan_tests(68); 29 | /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ 30 | ok1(list_empty(&static_list)); 31 | ok1(list_check(&static_list, NULL)); 32 | ok1(list_empty(&list)); 33 | ok1(list_check(&list, NULL)); 34 | 35 | parent.num_children = 0; 36 | list_head_init(&parent.children); 37 | /* Test list_head_init */ 38 | ok1(list_empty(&parent.children)); 39 | ok1(list_check(&parent.children, NULL)); 40 | 41 | c2.name = "c2"; 42 | list_add(&parent.children, &c2.list); 43 | /* Test list_add and !list_empty. */ 44 | ok1(!list_empty(&parent.children)); 45 | ok1(c2.list.next == &parent.children.n); 46 | ok1(c2.list.prev == &parent.children.n); 47 | ok1(parent.children.n.next == &c2.list); 48 | ok1(parent.children.n.prev == &c2.list); 49 | /* Test list_check */ 50 | ok1(list_check(&parent.children, NULL)); 51 | 52 | c1.name = "c1"; 53 | list_add(&parent.children, &c1.list); 54 | /* Test list_add and !list_empty. */ 55 | ok1(!list_empty(&parent.children)); 56 | ok1(c2.list.next == &parent.children.n); 57 | ok1(c2.list.prev == &c1.list); 58 | ok1(parent.children.n.next == &c1.list); 59 | ok1(parent.children.n.prev == &c2.list); 60 | ok1(c1.list.next == &c2.list); 61 | ok1(c1.list.prev == &parent.children.n); 62 | /* Test list_check */ 63 | ok1(list_check(&parent.children, NULL)); 64 | 65 | c3.name = "c3"; 66 | list_add_tail(&parent.children, &c3.list); 67 | /* Test list_add_tail and !list_empty. */ 68 | ok1(!list_empty(&parent.children)); 69 | ok1(parent.children.n.next == &c1.list); 70 | ok1(parent.children.n.prev == &c3.list); 71 | ok1(c1.list.next == &c2.list); 72 | ok1(c1.list.prev == &parent.children.n); 73 | ok1(c2.list.next == &c3.list); 74 | ok1(c2.list.prev == &c1.list); 75 | ok1(c3.list.next == &parent.children.n); 76 | ok1(c3.list.prev == &c2.list); 77 | /* Test list_check */ 78 | ok1(list_check(&parent.children, NULL)); 79 | 80 | /* Test list_check_node */ 81 | ok1(list_check_node(&c1.list, NULL)); 82 | ok1(list_check_node(&c2.list, NULL)); 83 | ok1(list_check_node(&c3.list, NULL)); 84 | 85 | /* Test list_top */ 86 | ok1(list_top(&parent.children, struct child, list) == &c1); 87 | 88 | /* Test list_pop */ 89 | ok1(list_pop(&parent.children, struct child, list) == &c1); 90 | ok1(list_top(&parent.children, struct child, list) == &c2); 91 | list_add(&parent.children, &c1.list); 92 | 93 | /* Test list_tail */ 94 | ok1(list_tail(&parent.children, struct child, list) == &c3); 95 | 96 | /* Test list_for_each. */ 97 | i = 0; 98 | list_for_each(&parent.children, c, list) { 99 | switch (i++) { 100 | case 0: 101 | ok1(c == &c1); 102 | break; 103 | case 1: 104 | ok1(c == &c2); 105 | break; 106 | case 2: 107 | ok1(c == &c3); 108 | break; 109 | } 110 | if (i > 2) 111 | break; 112 | } 113 | ok1(i == 3); 114 | 115 | /* Test list_for_each_rev. */ 116 | i = 0; 117 | list_for_each_rev(&parent.children, c, list) { 118 | switch (i++) { 119 | case 0: 120 | ok1(c == &c3); 121 | break; 122 | case 1: 123 | ok1(c == &c2); 124 | break; 125 | case 2: 126 | ok1(c == &c1); 127 | break; 128 | } 129 | if (i > 2) 130 | break; 131 | } 132 | ok1(i == 3); 133 | 134 | /* Test list_for_each_safe, list_del and list_del_from. */ 135 | i = 0; 136 | list_for_each_safe(&parent.children, c, n, list) { 137 | switch (i++) { 138 | case 0: 139 | ok1(c == &c1); 140 | list_del(&c->list); 141 | break; 142 | case 1: 143 | ok1(c == &c2); 144 | list_del_from(&parent.children, &c->list); 145 | break; 146 | case 2: 147 | ok1(c == &c3); 148 | list_del_from(&parent.children, &c->list); 149 | break; 150 | } 151 | ok1(list_check(&parent.children, NULL)); 152 | if (i > 2) 153 | break; 154 | } 155 | ok1(i == 3); 156 | ok1(list_empty(&parent.children)); 157 | 158 | /* Test list_for_each_off. */ 159 | list_add_tail(&opaque_list, 160 | (struct list_node *)create_opaque_blob()); 161 | list_add_tail(&opaque_list, 162 | (struct list_node *)create_opaque_blob()); 163 | list_add_tail(&opaque_list, 164 | (struct list_node *)create_opaque_blob()); 165 | 166 | i = 0; 167 | 168 | list_for_each_off(&opaque_list, q, 0) { 169 | i++; 170 | ok1(if_blobs_know_the_secret(q)); 171 | } 172 | ok1(i == 3); 173 | 174 | /* Test list_for_each_safe_off, list_del_off and list_del_from_off. */ 175 | i = 0; 176 | list_for_each_safe_off(&opaque_list, q, nq, 0) { 177 | switch (i++) { 178 | case 0: 179 | ok1(if_blobs_know_the_secret(q)); 180 | list_del_off(q, 0); 181 | destroy_opaque_blob(q); 182 | break; 183 | case 1: 184 | ok1(if_blobs_know_the_secret(q)); 185 | list_del_from_off(&opaque_list, q, 0); 186 | destroy_opaque_blob(q); 187 | break; 188 | case 2: 189 | ok1(c == &c3); 190 | list_del_from_off(&opaque_list, q, 0); 191 | destroy_opaque_blob(q); 192 | break; 193 | } 194 | ok1(list_check(&opaque_list, NULL)); 195 | if (i > 2) 196 | break; 197 | } 198 | ok1(i == 3); 199 | ok1(list_empty(&opaque_list)); 200 | 201 | /* Test list_top/list_tail/list_pop on empty list. */ 202 | ok1(list_top(&parent.children, struct child, list) == NULL); 203 | ok1(list_tail(&parent.children, struct child, list) == NULL); 204 | ok1(list_pop(&parent.children, struct child, list) == NULL); 205 | return exit_status(); 206 | } 207 | -------------------------------------------------------------------------------- /include/ccan/str/LICENSE: -------------------------------------------------------------------------------- 1 | ../../licenses/CC0 -------------------------------------------------------------------------------- /include/ccan/str/_info: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "config.h" 4 | 5 | /** 6 | * str - string helper routines 7 | * 8 | * This is a grab bag of functions for string operations, designed to enhance 9 | * the standard string.h. 10 | * 11 | * Note that if you define CCAN_STR_DEBUG, you will get extra compile 12 | * checks on common misuses of the following functions (they will now 13 | * be out-of-line, so there is a runtime penalty!). 14 | * 15 | * strstr, strchr, strrchr: 16 | * Return const char * if first argument is const (gcc only). 17 | * 18 | * isalnum, isalpha, isascii, isblank, iscntrl, isdigit, isgraph, 19 | * islower, isprint, ispunct, isspace, isupper, isxdigit: 20 | * Static and runtime check that input is EOF or an *unsigned* 21 | * char, as per C standard (really!). 22 | * 23 | * Example: 24 | * #include 25 | * #include 26 | * 27 | * int main(int argc, char *argv[]) 28 | * { 29 | * if (argv[1] && streq(argv[1], "--verbose")) 30 | * printf("verbose set\n"); 31 | * if (argv[1] && strstarts(argv[1], "--")) 32 | * printf("Some option set\n"); 33 | * if (argv[1] && strends(argv[1], "cow-powers")) 34 | * printf("Magic option set\n"); 35 | * return 0; 36 | * } 37 | * 38 | * License: CC0 (Public domain) 39 | * Author: Rusty Russell 40 | */ 41 | int main(int argc, char *argv[]) 42 | { 43 | if (argc != 2) 44 | return 1; 45 | 46 | if (strcmp(argv[1], "depends") == 0) { 47 | printf("ccan/build_assert\n"); 48 | return 0; 49 | } 50 | 51 | return 1; 52 | } 53 | -------------------------------------------------------------------------------- /include/ccan/str/debug.c: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #include "config.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef CCAN_STR_DEBUG 9 | /* Because we mug the real ones with macros, we need our own wrappers. */ 10 | int str_isalnum(int i) 11 | { 12 | assert(i >= -1 && i < 256); 13 | return isalnum(i); 14 | } 15 | 16 | int str_isalpha(int i) 17 | { 18 | assert(i >= -1 && i < 256); 19 | return isalpha(i); 20 | } 21 | 22 | int str_isascii(int i) 23 | { 24 | assert(i >= -1 && i < 256); 25 | return isascii(i); 26 | } 27 | 28 | #if HAVE_ISBLANK 29 | int str_isblank(int i) 30 | { 31 | assert(i >= -1 && i < 256); 32 | return isblank(i); 33 | } 34 | #endif 35 | 36 | int str_iscntrl(int i) 37 | { 38 | assert(i >= -1 && i < 256); 39 | return iscntrl(i); 40 | } 41 | 42 | int str_isdigit(int i) 43 | { 44 | assert(i >= -1 && i < 256); 45 | return isdigit(i); 46 | } 47 | 48 | int str_isgraph(int i) 49 | { 50 | assert(i >= -1 && i < 256); 51 | return isgraph(i); 52 | } 53 | 54 | int str_islower(int i) 55 | { 56 | assert(i >= -1 && i < 256); 57 | return islower(i); 58 | } 59 | 60 | int str_isprint(int i) 61 | { 62 | assert(i >= -1 && i < 256); 63 | return isprint(i); 64 | } 65 | 66 | int str_ispunct(int i) 67 | { 68 | assert(i >= -1 && i < 256); 69 | return ispunct(i); 70 | } 71 | 72 | int str_isspace(int i) 73 | { 74 | assert(i >= -1 && i < 256); 75 | return isspace(i); 76 | } 77 | 78 | int str_isupper(int i) 79 | { 80 | assert(i >= -1 && i < 256); 81 | return isupper(i); 82 | } 83 | 84 | int str_isxdigit(int i) 85 | { 86 | assert(i >= -1 && i < 256); 87 | return isxdigit(i); 88 | } 89 | 90 | #undef strstr 91 | #undef strchr 92 | #undef strrchr 93 | 94 | char *str_strstr(const char *haystack, const char *needle) 95 | { 96 | return strstr(haystack, needle); 97 | } 98 | 99 | char *str_strchr(const char *haystack, int c) 100 | { 101 | return strchr(haystack, c); 102 | } 103 | 104 | char *str_strrchr(const char *haystack, int c) 105 | { 106 | return strrchr(haystack, c); 107 | } 108 | #endif 109 | -------------------------------------------------------------------------------- /include/ccan/str/str.c: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #include 3 | 4 | size_t strcount(const char *haystack, const char *needle) 5 | { 6 | size_t i = 0, nlen = strlen(needle); 7 | 8 | while ((haystack = strstr(haystack, needle)) != NULL) { 9 | i++; 10 | haystack += nlen; 11 | } 12 | return i; 13 | } 14 | -------------------------------------------------------------------------------- /include/ccan/str/str.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_STR_H 3 | #define CCAN_STR_H 4 | #include "config.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /** 11 | * streq - Are two strings equal? 12 | * @a: first string 13 | * @b: first string 14 | * 15 | * This macro is arguably more readable than "!strcmp(a, b)". 16 | * 17 | * Example: 18 | * if (streq(somestring, "")) 19 | * printf("String is empty!\n"); 20 | */ 21 | #define streq(a,b) (strcmp((a),(b)) == 0) 22 | 23 | /** 24 | * strstarts - Does this string start with this prefix? 25 | * @str: string to test 26 | * @prefix: prefix to look for at start of str 27 | * 28 | * Example: 29 | * if (strstarts(somestring, "foo")) 30 | * printf("String %s begins with 'foo'!\n", somestring); 31 | */ 32 | #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) 33 | 34 | /** 35 | * strends - Does this string end with this postfix? 36 | * @str: string to test 37 | * @postfix: postfix to look for at end of str 38 | * 39 | * Example: 40 | * if (strends(somestring, "foo")) 41 | * printf("String %s end with 'foo'!\n", somestring); 42 | */ 43 | static inline bool strends(const char *str, const char *postfix) 44 | { 45 | if (strlen(str) < strlen(postfix)) 46 | return false; 47 | 48 | return streq(str + strlen(str) - strlen(postfix), postfix); 49 | } 50 | 51 | /** 52 | * stringify - Turn expression into a string literal 53 | * @expr: any C expression 54 | * 55 | * Example: 56 | * #define PRINT_COND_IF_FALSE(cond) \ 57 | * ((cond) || printf("%s is false!", stringify(cond))) 58 | */ 59 | #define stringify(expr) stringify_1(expr) 60 | /* Double-indirection required to stringify expansions */ 61 | #define stringify_1(expr) #expr 62 | 63 | /** 64 | * strcount - Count number of (non-overlapping) occurrences of a substring. 65 | * @haystack: a C string 66 | * @needle: a substring 67 | * 68 | * Example: 69 | * assert(strcount("aaa aaa", "a") == 6); 70 | * assert(strcount("aaa aaa", "ab") == 0); 71 | * assert(strcount("aaa aaa", "aa") == 2); 72 | */ 73 | size_t strcount(const char *haystack, const char *needle); 74 | 75 | /** 76 | * STR_MAX_CHARS - Maximum possible size of numeric string for this type. 77 | * @type_or_expr: a pointer or integer type or expression. 78 | * 79 | * This provides enough space for a nul-terminated string which represents the 80 | * largest possible value for the type or expression. 81 | * 82 | * Note: The implementation adds extra space so hex values or negative 83 | * values will fit (eg. sprintf(... "%p"). ) 84 | * 85 | * Example: 86 | * char str[STR_MAX_CHARS(int)]; 87 | * 88 | * sprintf(str, "%i", 7); 89 | */ 90 | #define STR_MAX_CHARS(type_or_expr) \ 91 | ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \ 92 | + STR_MAX_CHARS_TCHECK_(type_or_expr)) 93 | 94 | #if HAVE_TYPEOF 95 | /* Only a simple type can have 0 assigned, so test that. */ 96 | #define STR_MAX_CHARS_TCHECK_(type_or_expr) \ 97 | ({ typeof(type_or_expr) x = 0; (void)x; 0; }) 98 | #else 99 | #define STR_MAX_CHARS_TCHECK_(type_or_expr) 0 100 | #endif 101 | 102 | /** 103 | * cisalnum - isalnum() which takes a char (and doesn't accept EOF) 104 | * @c: a character 105 | * 106 | * Surprisingly, the standard ctype.h isalnum() takes an int, which 107 | * must have the value of EOF (-1) or an unsigned char. This variant 108 | * takes a real char, and doesn't accept EOF. 109 | */ 110 | static inline bool cisalnum(char c) 111 | { 112 | return isalnum((unsigned char)c); 113 | } 114 | static inline bool cisalpha(char c) 115 | { 116 | return isalpha((unsigned char)c); 117 | } 118 | static inline bool cisascii(char c) 119 | { 120 | return isascii((unsigned char)c); 121 | } 122 | #if HAVE_ISBLANK 123 | static inline bool cisblank(char c) 124 | { 125 | return isblank((unsigned char)c); 126 | } 127 | #endif 128 | static inline bool ciscntrl(char c) 129 | { 130 | return iscntrl((unsigned char)c); 131 | } 132 | static inline bool cisdigit(char c) 133 | { 134 | return isdigit((unsigned char)c); 135 | } 136 | static inline bool cisgraph(char c) 137 | { 138 | return isgraph((unsigned char)c); 139 | } 140 | static inline bool cislower(char c) 141 | { 142 | return islower((unsigned char)c); 143 | } 144 | static inline bool cisprint(char c) 145 | { 146 | return isprint((unsigned char)c); 147 | } 148 | static inline bool cispunct(char c) 149 | { 150 | return ispunct((unsigned char)c); 151 | } 152 | static inline bool cisspace(char c) 153 | { 154 | return isspace((unsigned char)c); 155 | } 156 | static inline bool cisupper(char c) 157 | { 158 | return isupper((unsigned char)c); 159 | } 160 | static inline bool cisxdigit(char c) 161 | { 162 | return isxdigit((unsigned char)c); 163 | } 164 | 165 | #include 166 | 167 | /* These checks force things out of line, hence they are under DEBUG. */ 168 | #ifdef CCAN_STR_DEBUG 169 | #include 170 | 171 | /* These are commonly misused: they take -1 or an *unsigned* char value. */ 172 | #undef isalnum 173 | #undef isalpha 174 | #undef isascii 175 | #undef isblank 176 | #undef iscntrl 177 | #undef isdigit 178 | #undef isgraph 179 | #undef islower 180 | #undef isprint 181 | #undef ispunct 182 | #undef isspace 183 | #undef isupper 184 | #undef isxdigit 185 | 186 | /* You can use a char if char is unsigned. */ 187 | #if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF 188 | #define str_check_arg_(i) \ 189 | ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \ 190 | char) \ 191 | || (char)255 > 0)) 192 | #else 193 | #define str_check_arg_(i) (i) 194 | #endif 195 | 196 | #define isalnum(i) str_isalnum(str_check_arg_(i)) 197 | #define isalpha(i) str_isalpha(str_check_arg_(i)) 198 | #define isascii(i) str_isascii(str_check_arg_(i)) 199 | #if HAVE_ISBLANK 200 | #define isblank(i) str_isblank(str_check_arg_(i)) 201 | #endif 202 | #define iscntrl(i) str_iscntrl(str_check_arg_(i)) 203 | #define isdigit(i) str_isdigit(str_check_arg_(i)) 204 | #define isgraph(i) str_isgraph(str_check_arg_(i)) 205 | #define islower(i) str_islower(str_check_arg_(i)) 206 | #define isprint(i) str_isprint(str_check_arg_(i)) 207 | #define ispunct(i) str_ispunct(str_check_arg_(i)) 208 | #define isspace(i) str_isspace(str_check_arg_(i)) 209 | #define isupper(i) str_isupper(str_check_arg_(i)) 210 | #define isxdigit(i) str_isxdigit(str_check_arg_(i)) 211 | 212 | #if HAVE_TYPEOF 213 | /* With GNU magic, we can make const-respecting standard string functions. */ 214 | #undef strstr 215 | #undef strchr 216 | #undef strrchr 217 | 218 | /* + 0 is needed to decay array into pointer. */ 219 | #define strstr(haystack, needle) \ 220 | ((typeof((haystack) + 0))str_strstr((haystack), (needle))) 221 | #define strchr(haystack, c) \ 222 | ((typeof((haystack) + 0))str_strchr((haystack), (c))) 223 | #define strrchr(haystack, c) \ 224 | ((typeof((haystack) + 0))str_strrchr((haystack), (c))) 225 | #endif 226 | #endif /* CCAN_STR_DEBUG */ 227 | 228 | #endif /* CCAN_STR_H */ 229 | -------------------------------------------------------------------------------- /include/ccan/str/str_debug.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_STR_DEBUG_H 3 | #define CCAN_STR_DEBUG_H 4 | 5 | /* #define CCAN_STR_DEBUG 1 */ 6 | 7 | #ifdef CCAN_STR_DEBUG 8 | /* Because we mug the real ones with macros, we need our own wrappers. */ 9 | int str_isalnum(int i); 10 | int str_isalpha(int i); 11 | int str_isascii(int i); 12 | #if HAVE_ISBLANK 13 | int str_isblank(int i); 14 | #endif 15 | int str_iscntrl(int i); 16 | int str_isdigit(int i); 17 | int str_isgraph(int i); 18 | int str_islower(int i); 19 | int str_isprint(int i); 20 | int str_ispunct(int i); 21 | int str_isspace(int i); 22 | int str_isupper(int i); 23 | int str_isxdigit(int i); 24 | 25 | char *str_strstr(const char *haystack, const char *needle); 26 | char *str_strchr(const char *s, int c); 27 | char *str_strrchr(const char *s, int c); 28 | #endif /* CCAN_STR_DEBUG */ 29 | 30 | #endif /* CCAN_STR_DEBUG_H */ 31 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-STR_MAX_CHARS.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct s { 4 | int val; 5 | }; 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | struct s 10 | #ifdef FAIL 11 | #if !HAVE_TYPEOF 12 | #error We need typeof to check STR_MAX_CHARS. 13 | #endif 14 | #else 15 | /* A pointer is OK. */ 16 | * 17 | #endif 18 | val; 19 | char str[STR_MAX_CHARS(val)]; 20 | 21 | str[0] = '\0'; 22 | return str[0] ? 0 : 1; 23 | } 24 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isalnum.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isalnum. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isalnum(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isalpha.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isalpha. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isalpha(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isascii.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isascii. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isascii(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isblank.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF || !HAVE_ISBLANK 8 | #error We need typeof to check isblank. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | #if HAVE_ISBLANK 22 | return isblank(c); 23 | #else 24 | return c; 25 | #endif 26 | } 27 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-iscntrl.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check iscntrl. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return iscntrl(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isdigit.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isdigit. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isdigit(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-islower.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check islower. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return islower(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isprint.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isprint. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isprint(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-ispunct.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check ispunct. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return ispunct(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isspace.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isspace. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isspace(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isupper.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isupper. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isupper(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-isxdigit.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_BUILTIN_TYPES_COMPATIBLE_P || !HAVE_TYPEOF 8 | #error We need typeof to check isxdigit. 9 | #endif 10 | char 11 | #else 12 | unsigned char 13 | #endif 14 | c = argv[0][0]; 15 | 16 | #ifdef FAIL 17 | /* Fake fail on unsigned char platforms. */ 18 | BUILD_ASSERT((char)255 < 0); 19 | #endif 20 | 21 | return isxdigit(c); 22 | } 23 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-strchr.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_TYPEOF 8 | #error We need typeof to check strstr. 9 | #endif 10 | #else 11 | const 12 | #endif 13 | char *ret; 14 | const char *str = "hello"; 15 | 16 | ret = strchr(str, 'l'); 17 | return ret ? 0 : 1; 18 | } 19 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-strrchr.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_TYPEOF 8 | #error We need typeof to check strstr. 9 | #endif 10 | #else 11 | const 12 | #endif 13 | char *ret; 14 | const char *str = "hello"; 15 | 16 | ret = strrchr(str, 'l'); 17 | return ret ? 0 : 1; 18 | } 19 | -------------------------------------------------------------------------------- /include/ccan/str/test/compile_fail-strstr.c: -------------------------------------------------------------------------------- 1 | #define CCAN_STR_DEBUG 1 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | #ifdef FAIL 7 | #if !HAVE_TYPEOF 8 | #error We need typeof to check strstr. 9 | #endif 10 | #else 11 | const 12 | #endif 13 | char *ret; 14 | const char *str = "hello"; 15 | 16 | ret = strstr(str, "hell"); 17 | return ret ? 0 : 1; 18 | } 19 | -------------------------------------------------------------------------------- /include/ccan/str/test/debug.c: -------------------------------------------------------------------------------- 1 | /* We can't use the normal "#include the .c file" trick, since this is 2 | contaminated by str.h's macro overrides. So we put it in all tests 3 | like this. */ 4 | #define CCAN_STR_DEBUG 1 5 | #include 6 | -------------------------------------------------------------------------------- /include/ccan/str/test/run-STR_MAX_CHARS.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char str[1000]; 10 | struct { 11 | uint8_t u1byte; 12 | int8_t s1byte; 13 | uint16_t u2byte; 14 | int16_t s2byte; 15 | uint32_t u4byte; 16 | int32_t s4byte; 17 | uint64_t u8byte; 18 | int64_t s8byte; 19 | void *ptr; 20 | } types; 21 | 22 | plan_tests(13); 23 | 24 | memset(&types, 0xFF, sizeof(types)); 25 | 26 | /* Hex versions */ 27 | sprintf(str, "0x%llx", (unsigned long long)types.u1byte); 28 | ok1(strlen(str) < STR_MAX_CHARS(types.u1byte)); 29 | sprintf(str, "0x%llx", (unsigned long long)types.u2byte); 30 | ok1(strlen(str) < STR_MAX_CHARS(types.u2byte)); 31 | sprintf(str, "0x%llx", (unsigned long long)types.u4byte); 32 | ok1(strlen(str) < STR_MAX_CHARS(types.u4byte)); 33 | sprintf(str, "0x%llx", (unsigned long long)types.u8byte); 34 | ok1(strlen(str) < STR_MAX_CHARS(types.u8byte)); 35 | 36 | /* Decimal versions */ 37 | sprintf(str, "%u", types.u1byte); 38 | ok1(strlen(str) < STR_MAX_CHARS(types.u1byte)); 39 | sprintf(str, "%d", types.s1byte); 40 | ok1(strlen(str) < STR_MAX_CHARS(types.s1byte)); 41 | sprintf(str, "%u", types.u2byte); 42 | ok1(strlen(str) < STR_MAX_CHARS(types.u2byte)); 43 | sprintf(str, "%d", types.s2byte); 44 | ok1(strlen(str) < STR_MAX_CHARS(types.s2byte)); 45 | sprintf(str, "%u", types.u4byte); 46 | ok1(strlen(str) < STR_MAX_CHARS(types.u4byte)); 47 | sprintf(str, "%d", types.s4byte); 48 | ok1(strlen(str) < STR_MAX_CHARS(types.s4byte)); 49 | sprintf(str, "%llu", (unsigned long long)types.u8byte); 50 | ok1(strlen(str) < STR_MAX_CHARS(types.u8byte)); 51 | sprintf(str, "%lld", (long long)types.s8byte); 52 | ok1(strlen(str) < STR_MAX_CHARS(types.s8byte)); 53 | 54 | /* Pointer version. */ 55 | sprintf(str, "%p", types.ptr); 56 | ok1(strlen(str) < STR_MAX_CHARS(types.ptr)); 57 | 58 | return exit_status(); 59 | } 60 | -------------------------------------------------------------------------------- /include/ccan/str/test/run.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 8 | 9 | static const char *substrings[] = { "far", "bar", "baz", "b", "ba", "z", "ar", 10 | NULL }; 11 | 12 | #define NUM_SUBSTRINGS (ARRAY_SIZE(substrings) - 1) 13 | 14 | static char *strdup_rev(const char *s) 15 | { 16 | char *ret = strdup(s); 17 | unsigned int i; 18 | 19 | for (i = 0; i < strlen(s); i++) 20 | ret[i] = s[strlen(s) - i - 1]; 21 | return ret; 22 | } 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | unsigned int i, j, n; 27 | char *strings[NUM_SUBSTRINGS * NUM_SUBSTRINGS]; 28 | 29 | n = 0; 30 | for (i = 0; i < NUM_SUBSTRINGS; i++) { 31 | for (j = 0; j < NUM_SUBSTRINGS; j++) { 32 | strings[n] = malloc(strlen(substrings[i]) 33 | + strlen(substrings[j]) + 1); 34 | sprintf(strings[n++], "%s%s", 35 | substrings[i], substrings[j]); 36 | } 37 | } 38 | 39 | plan_tests(n * n * 5 + 16); 40 | for (i = 0; i < n; i++) { 41 | for (j = 0; j < n; j++) { 42 | unsigned int k, identical = 0; 43 | char *reva, *revb; 44 | 45 | /* Find first difference. */ 46 | for (k = 0; strings[i][k]==strings[j][k]; k++) { 47 | if (k == strlen(strings[i])) { 48 | identical = 1; 49 | break; 50 | } 51 | } 52 | 53 | if (identical) 54 | ok1(streq(strings[i], strings[j])); 55 | else 56 | ok1(!streq(strings[i], strings[j])); 57 | 58 | /* Postfix test should be equivalent to prefix 59 | * test on reversed string. */ 60 | reva = strdup_rev(strings[i]); 61 | revb = strdup_rev(strings[j]); 62 | 63 | if (!strings[i][k]) { 64 | ok1(strstarts(strings[j], strings[i])); 65 | ok1(strends(revb, reva)); 66 | } else { 67 | ok1(!strstarts(strings[j], strings[i])); 68 | ok1(!strends(revb, reva)); 69 | } 70 | if (!strings[j][k]) { 71 | ok1(strstarts(strings[i], strings[j])); 72 | ok1(strends(reva, revb)); 73 | } else { 74 | ok1(!strstarts(strings[i], strings[j])); 75 | ok1(!strends(reva, revb)); 76 | } 77 | free(reva); 78 | free(revb); 79 | } 80 | } 81 | 82 | for (i = 0; i < n; i++) 83 | free(strings[i]); 84 | 85 | ok1(streq(stringify(NUM_SUBSTRINGS), 86 | "((sizeof(substrings) / sizeof(substrings[0])) - 1)")); 87 | ok1(streq(stringify(ARRAY_SIZE(substrings)), 88 | "(sizeof(substrings) / sizeof(substrings[0]))")); 89 | ok1(streq(stringify(i == 0), "i == 0")); 90 | 91 | ok1(strcount("aaaaaa", "b") == 0); 92 | ok1(strcount("aaaaaa", "a") == 6); 93 | ok1(strcount("aaaaaa", "aa") == 3); 94 | ok1(strcount("aaaaaa", "aaa") == 2); 95 | ok1(strcount("aaaaaa", "aaaa") == 1); 96 | ok1(strcount("aaaaaa", "aaaaa") == 1); 97 | ok1(strcount("aaaaaa", "aaaaaa") == 1); 98 | ok1(strcount("aaa aaa", "b") == 0); 99 | ok1(strcount("aaa aaa", "a") == 6); 100 | ok1(strcount("aaa aaa", "aa") == 2); 101 | ok1(strcount("aaa aaa", "aaa") == 2); 102 | ok1(strcount("aaa aaa", "aaaa") == 0); 103 | ok1(strcount("aaa aaa", "aaaaa") == 0); 104 | 105 | return exit_status(); 106 | } 107 | -------------------------------------------------------------------------------- /licenses/BSD-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /licenses/CC0: -------------------------------------------------------------------------------- 1 | Statement of Purpose 2 | 3 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). 4 | 5 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. 6 | 7 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 8 | 9 | 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: 10 | 11 | the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; 12 | moral rights retained by the original author(s) and/or performer(s); 13 | publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; 14 | rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; 15 | rights protecting the extraction, dissemination, use and reuse of data in a Work; 16 | database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and 17 | other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 18 | 19 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 20 | 21 | 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 22 | 23 | 4. Limitations and Disclaimers. 24 | 25 | No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. 26 | Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. 27 | Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. 28 | Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. 29 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('hid-replay', 'c', 2 | version: '0.7.1', 3 | license: 'GPL2+', 4 | default_options: ['c_std=gnu99', 'warning_level=2'], 5 | meson_version: '>= 0.47.0') 6 | 7 | dir_man1 = join_paths(get_option('prefix'), get_option('mandir'), '1') 8 | 9 | includes_include = include_directories('include') 10 | 11 | cc = meson.get_compiler('c') 12 | cflags = ['-Wno-unused-parameter', '-DHAVE_CONFIG_H'] 13 | add_project_arguments(cflags, language : 'c') 14 | 15 | # We need a config.h for the ccan headers 16 | config_h = configuration_data() 17 | config_h.set('_GNU_SOURCE', '1') 18 | configure_file(output: 'config.h', 19 | install: false, 20 | configuration: config_h) 21 | 22 | executable('hid-recorder', 23 | 'src/hid-recorder.c', 24 | install: true, 25 | include_directories: [includes_include]) 26 | 27 | if not cc.check_header('linux/uhid.h') 28 | warning('linux/uhid.h not found - cannot create hid-replay') 29 | else 30 | executable('hid-replay', 31 | 'src/hid-replay.c', 32 | install: true, 33 | include_directories: [includes_include]) 34 | endif 35 | 36 | xmlto = find_program('xmlto', required: false) 37 | asciidoc = find_program('asciidoc', required: false) 38 | if xmlto.found() and asciidoc.found() 39 | manpages = ['hid-replay.txt', 'hid-recorder.txt'] 40 | foreach f: manpages 41 | f_xml = custom_target('xml for @0@'.format(f), 42 | input: join_paths('src', f), 43 | output: '@BASENAME@.xml', 44 | command: [asciidoc.path(), '-b', 'docbook', '-d', 'manpage', '-o', '@BASENAME@.xml', '@INPUT@'], 45 | install: false) 46 | custom_target('man page for @0@'.format(f), 47 | input: f_xml, 48 | output: '@BASENAME@.1', 49 | command: [xmlto.path(), 'man', f_xml], 50 | install: true, 51 | install_dir: dir_man1) 52 | endforeach 53 | else 54 | warning('xmlto or asciidoc not found - cannot create man pages without it') 55 | endif 56 | 57 | warning(' 58 | ****************************************************** 59 | * This tool is obsolete. Please switch to hid-tools: * 60 | * https://gitlab.freedesktop.org/libevdev/hid-tools * 61 | ******************************************************') 62 | -------------------------------------------------------------------------------- /python/report.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # report.py, a multitouch diagnostic reporter 5 | # 6 | # The copyright owners for the contents of this file are: 7 | # Ecole Nationale de l'Aviation Civile, France (2010-2011) 8 | # Red Hat, Inc. (2012) 9 | # 10 | # Contributors: 11 | # Benjamin Tissoires 12 | # 13 | # 14 | # This program is provided to you as free software; 15 | # you can redistribute it and/or modify it under the terms of the 16 | # GNU General Public License as published by the Free Software 17 | # Foundation; either version 2 of the License, or (at your option) 18 | # any later version. 19 | 20 | import os 21 | import sys 22 | import re 23 | 24 | # first, check the user id 25 | if int(os.popen("id -u").read()) != 0: 26 | print "Must be run with root privileges" 27 | sys.exit(1) 28 | 29 | # then, disable stdout buffering 30 | sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) 31 | 32 | # get the kernel version 33 | cmd = "uname -a" 34 | print cmd 35 | print os.popen(cmd).read() 36 | 37 | # get the report descriptors through lsusb first. 38 | 39 | os.chdir("/sys/bus/usb/drivers/usbhid") 40 | 41 | USB_PATH = [] 42 | USB = [] 43 | 44 | for file in os.listdir("."): 45 | m = re.match(r".*\:.*", file) 46 | if not m: 47 | continue 48 | # we now have only the links to the devices 49 | 50 | uevent = "" 51 | name = "" 52 | usb = "" 53 | # find the name and path of the device 54 | for subfile in os.listdir(file): 55 | m = re.match(r"\d*\:.*", subfile) 56 | if not m: 57 | continue 58 | uevent = file + "/" + subfile + "/uevent" 59 | 60 | uevent_f = open(uevent,'r') 61 | for l in uevent_f: 62 | m = re.match(r"(HID_ID|HID_NAME)=(.*)",l) 63 | if m: 64 | var, value = m.groups() 65 | if var == "HID_NAME": 66 | name = value 67 | elif var == "HID_ID": 68 | usb = ":".join(value.split(":")[1:]) 69 | 70 | if usb not in USB: 71 | print "found device", name 72 | USB.append(usb) 73 | 74 | USB_PATH.append(file) 75 | cmd = "echo " + file + " > unbind" 76 | print cmd 77 | os.popen(cmd) 78 | 79 | for usb in USB: 80 | cmd = "lsusb -v -d " + usb 81 | print cmd 82 | print os.popen(cmd).read() 83 | 84 | for file in USB_PATH: 85 | cmd = "echo " + file + " > bind" 86 | print cmd 87 | os.popen(cmd) 88 | 89 | # now work with hidraw 90 | import fcntl 91 | import struct 92 | import array 93 | import os 94 | import glob 95 | import sys 96 | import select 97 | from datetime import datetime 98 | 99 | def ioctl(fd, EVIOC, code, return_type, buf = None): 100 | size = struct.calcsize(return_type) 101 | if buf == None: 102 | buf = size*'\x00' 103 | abs = fcntl.ioctl(fd, EVIOC(code, size), buf) 104 | return struct.unpack(return_type, abs) 105 | 106 | # extracted from 107 | _IOC_WRITE = 1 108 | _IOC_READ = 2 109 | 110 | _IOC_NRBITS = 8 111 | _IOC_TYPEBITS = 8 112 | _IOC_SIZEBITS = 14 113 | _IOC_DIRBITS = 2 114 | 115 | _IOC_NRSHIFT = 0 116 | _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS 117 | _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS 118 | _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS 119 | 120 | #define _IOC(dir,type,nr,size) \ 121 | # (((dir) << _IOC_DIRSHIFT) | \ 122 | # ((type) << _IOC_TYPESHIFT) | \ 123 | # ((nr) << _IOC_NRSHIFT) | \ 124 | # ((size) << _IOC_SIZESHIFT)) 125 | def _IOC(dir, type, nr, size): 126 | return ( (dir << _IOC_DIRSHIFT) | 127 | (ord(type) << _IOC_TYPESHIFT) | 128 | (nr << _IOC_NRSHIFT) | 129 | (size << _IOC_SIZESHIFT)) 130 | 131 | #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) 132 | def _IOR(type,nr,size): 133 | return _IOC(_IOC_READ, type, nr, size) 134 | 135 | #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) 136 | def _IOW(type,nr,size): 137 | return _IOC(_IOC_WRITE, type, nr, size) 138 | 139 | 140 | #define HIDIOCGRDESCSIZE _IOR('H', 0x01, int) 141 | def _HIDIOCGRDESCSIZE(none, len): 142 | return _IOR('H', 0x01, len) 143 | 144 | def HIDIOCGRDESCSIZE(fd): 145 | """ get report descriptors size """ 146 | type = 'i' 147 | return int(*ioctl(fd, _HIDIOCGRDESCSIZE, None, type)) 148 | 149 | #define HIDIOCGRDESC _IOR('H', 0x02, struct hidraw_report_descriptor) 150 | def _HIDIOCGRDESC(none, len): 151 | return _IOR('H', 0x02, len) 152 | 153 | def HIDIOCGRDESC(fd, size): 154 | """ get report descriptors """ 155 | format = "I4096c" 156 | value = '\0'*4096 157 | tmp = struct.pack("i", size) + value[:4096].ljust(4096, '\0') 158 | _buffer = array.array('B', tmp) 159 | fcntl.ioctl(fd, _HIDIOCGRDESC(None, struct.calcsize(format)), _buffer) 160 | size, = struct.unpack("i", _buffer[:4]) 161 | value = _buffer[4:size+4] 162 | return size, value 163 | 164 | #define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) 165 | def _HIDIOCGRAWINFO(none, len): 166 | return _IOR('H', 0x03, len) 167 | 168 | def HIDIOCGRAWINFO(fd): 169 | """ get hidraw device infos """ 170 | type = 'ihh' 171 | return ioctl(fd, _HIDIOCGRAWINFO, None, type) 172 | 173 | #define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len) 174 | def _HIDIOCGRAWNAME(none, len): 175 | return _IOC(_IOC_READ, 'H', 0x04, len) 176 | 177 | def HIDIOCGRAWNAME(fd): 178 | """ get device name """ 179 | type = 1024*'c' 180 | return "".join(ioctl(fd, _HIDIOCGRAWNAME, None, type)).rstrip('\x00') 181 | 182 | instructions = ( 183 | "1. Drag _one_ finger on the screen from one corner to the opposite, and release it.", 184 | """2. Land one finger, 185 | - land a second finger far enough from the first 186 | - move your two fingers 187 | - release the *first* finger 188 | - release the second finger""", 189 | "3. Finally, move your ten fingers on the screen and release them.", 190 | "Thank you.", 191 | ) 192 | 193 | def trace(device): 194 | file = os.open(device, os.O_RDONLY) 195 | size = HIDIOCGRDESCSIZE(file) 196 | rsize, rdesc = HIDIOCGRDESC(file, size) 197 | if size != rsize: 198 | print "error, got", rsize, "instead of", size 199 | return 200 | rdesc_str = ["%02x" % (data) for data in rdesc] 201 | bus, vid, pid = HIDIOCGRAWINFO(file) 202 | name = HIDIOCGRAWNAME(file) 203 | sys.stderr.write("Opening "+name+" ("+device+")\n") 204 | print "R:", size, " ".join(rdesc_str) 205 | print "N:", name 206 | print "I:", bus, "%04x %04x" % (vid,pid) 207 | starttime = None 208 | instr = 0 209 | sys.stderr.write("Please follow these "+str(len(instructions) - 1)+" steps:\n") 210 | sys.stderr.write(instructions[instr] + "\n") 211 | print "# " + instructions[instr] 212 | instr += 1 213 | capture = True 214 | events = False 215 | while capture: 216 | ready,_,_ = select.select([file],[],[], 2) 217 | if not ready: 218 | if events: 219 | sys.stderr.write(instructions[instr] + "\n") 220 | print "# " + instructions[instr] 221 | events = False 222 | instr += 1 223 | capture = instr < len(instructions) 224 | continue 225 | rdata = os.read(file, 4096) 226 | events = True 227 | now = datetime.now() 228 | if not starttime: 229 | starttime = now 230 | delta = now - starttime 231 | fmt = 'B'*len(rdata) 232 | data = struct.unpack(fmt, rdata) 233 | data = ["%02x" % (d) for d in data] 234 | print "E:", "%d.%06d" % (delta.seconds, delta.microseconds), len(rdata), " ".join(data) 235 | os.close(file) 236 | 237 | def find_device(path): 238 | filename = "" 239 | files = glob.glob(path + '*') 240 | if len(files) < 1: 241 | return None 242 | files.sort() 243 | sys.stderr.write("These are the available hidraw devices so far:\n") 244 | for hidfile in files: 245 | file = os.open(hidfile, os.O_RDONLY) 246 | name = HIDIOCGRAWNAME(file) 247 | os.close(file) 248 | sys.stderr.write(hidfile +": "+name+"\n") 249 | print hidfile, name 250 | n = None 251 | while n == None: 252 | sys.stderr.write("Select the device event number [0-%d]: " % (len(files) - 1)) 253 | l = sys.stdin.readline() 254 | try: 255 | n = int(l) 256 | except: 257 | pass 258 | print n 259 | return path+str(n) 260 | 261 | dev = find_device('/dev/hidraw-compat') 262 | if not dev: 263 | dev = find_device('/dev/hidraw') 264 | if not dev: 265 | sys.stderr.write("Unable to find any hidraw devices\n") 266 | sys.exit(1) 267 | trace(dev) 268 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_PROGRAMS = 2 | 3 | if HAVE_UHID 4 | bin_replay = hid-replay 5 | endif 6 | 7 | bin_PROGRAMS = hid-recorder $(bin_replay) 8 | 9 | INCLUDES=-I$(top_srcdir)/include/ 10 | 11 | # man page generation 12 | if HAVE_DOCTOOLS 13 | # actual man pages 14 | man_pages_sources = hid-replay.txt hid-recorder.txt 15 | # shadow man pages 16 | man_pages_shadows = 17 | 18 | man_pages = $(man_pages_sources:.txt=.1) $(man_pages_shadows) 19 | 20 | SUFFIXES = .1 .txt .xml 21 | 22 | .xml.1: 23 | @$(XMLTO) man $< 24 | 25 | .txt.xml: 26 | @$(ASCIIDOC) -b docbook -d manpage -o $@ $< 27 | 28 | dist_man_MANS = $(man_pages) 29 | CLEANFILES = $(dist_man_MANS) 30 | MAINTAINERCLEANFILES = $(man_pages) *.xml 31 | endif 32 | -------------------------------------------------------------------------------- /src/hid-recorder.txt: -------------------------------------------------------------------------------- 1 | HID-RECORDER(1) 2 | =============== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | 8 | hid-recorder - HID Input device recorder 9 | 10 | SYNOPSIS 11 | -------- 12 | *hid-recorder* 13 | 14 | *hid-recorder* [OPTIONS] [/dev/hidrawX] [[/dev/hidrawY] ... ] 15 | 16 | OPTIONS 17 | ------- 18 | 19 | *-d, --debugfs*:: 20 | use HID debugfs node. Some devices (like "Logitech USB Receiver" or 21 | "Apple Magic Mouse") grab the hidraw output, resulting in an empty output. 22 | In many cases relying on HID debugfs can help but if the device 23 | reports too many events at once (multitouch panels for example), 24 | the HID debugfs node will be missing a lot of events. 25 | 26 | DESCRIPTION 27 | ----------- 28 | *hid-recorder* is a tool that allows users to capture the hidraw description 29 | and events in order to replay them through the uhid kernel module. 30 | 31 | When invoked without arguments, *hid-recorder* lets the user choose the 32 | hidraw device interactively. 33 | 34 | Several hidraw nodes can be passed to *hid-recorder*. In this case, the 35 | recordings of each node are multiplexed using the tag *D:*, see below. 36 | 37 | *hid-recorder* needs to be able to read from the hidraw device; in most cases this 38 | means it must be run as root. 39 | 40 | *hid-recorder* and *hid-replay* are used to debug kernel issues with HID input 41 | devices. The output of *hid-recorder* has the following syntax: 42 | 43 | - *#* lines comments 44 | - *D:* the device we are currently working with 45 | - *R:* size dump_of_report_descriptor_in_hexadecimal 46 | - *N:* common_name_of_the_device 47 | - *P:* physical_path 48 | - *I:* bus vendor_id product_id 49 | - *E:* timestamp size report_in_hexadecimal 50 | 51 | 52 | EXIT CODE 53 | --------- 54 | *hid-recorder* returns EXIT_FAILURE on error. 55 | 56 | SEE ALSO 57 | -------- 58 | hid-replay(1) 59 | 60 | COPYRIGHT 61 | --------- 62 | Copyright 2012, Benjamin Tissoires. 63 | Copyright 2012-2018, Red Hat, Inc. 64 | 65 | AUTHOR 66 | ------ 67 | Benjamin Tissoires 68 | -------------------------------------------------------------------------------- /src/hid-replay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Hid replay 3 | * 4 | * Copyright (c) 2012 Benjamin Tissoires 5 | * Copyright (c) 2012 Red Hat, Inc. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #if HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | /* Linux */ 26 | #include 27 | #include 28 | #include 29 | 30 | /* Unix */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | /* C */ 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #ifdef __UCLIBC__ 49 | extern const char *program_invocation_name; 50 | extern const char *program_invocation_short_name; 51 | #else 52 | extern char *program_invocation_name; 53 | extern char *program_invocation_short_name; 54 | #endif 55 | 56 | #define UHID_NODE "/dev/uhid" 57 | __u8 rdesc_buf[4096]; 58 | 59 | #define HID_REPLAY_MASK_NAME 1 << 0 60 | #define HID_REPLAY_MASK_RDESC 1 << 1 61 | #define HID_REPLAY_MASK_INFO 1 << 2 62 | #define HID_REPLAY_MASK_COMPLETE (HID_REPLAY_MASK_NAME | \ 63 | HID_REPLAY_MASK_RDESC | \ 64 | HID_REPLAY_MASK_INFO) 65 | 66 | struct hid_replay_device { 67 | int fuhid; 68 | int idx; 69 | struct list_node list; 70 | }; 71 | 72 | struct hid_replay_devices_list { 73 | struct list_head devices; 74 | struct hid_replay_device *current; 75 | struct pollfd *fds; 76 | int count; 77 | }; 78 | 79 | /* global because used in the signal handler */ 80 | static struct hid_replay_devices_list *devices; 81 | static FILE *fp; 82 | 83 | /** 84 | * Print usage information. 85 | */ 86 | static int usage(void) 87 | { 88 | printf("USAGE:\n"); 89 | printf(" %s [OPTION] filename\n", program_invocation_short_name); 90 | 91 | printf("\n"); 92 | printf("where OPTION is either:\n"); 93 | printf(" -h or --help: print this message\n"); 94 | printf(" -i or --interactive: default mode: interactive mode (allow to control and to replay several times)\n"); 95 | printf(" -1 or --one: play once the events without waiting and then exit\n"); 96 | printf(" -s X or --sleep X: sleep X seconds once the device is created before next step\n"); 97 | 98 | return EXIT_FAILURE; 99 | } 100 | 101 | enum hid_replay_mode { 102 | MODE_AUTO, 103 | MODE_INTERACTIVE, 104 | }; 105 | 106 | static const struct option long_options[] = { 107 | { "help", no_argument, NULL, 'h' }, 108 | { "sleep", required_argument, NULL, 's' }, 109 | { "one", no_argument, NULL, '1' }, 110 | { "interactive", no_argument, NULL, 'i' }, 111 | { 0, }, 112 | }; 113 | 114 | static void hid_replay_rdesc(char *rdesc, ssize_t len, struct uhid_create_req *dev) 115 | { 116 | int rdesc_len, i; 117 | int n = sscanf(rdesc, "R: %d %[^\n]\n", &rdesc_len, rdesc); 118 | if (n != 2) 119 | return; 120 | /* TODO: check consistency of rdesc */ 121 | for (i = 0; i < rdesc_len; ++i) { 122 | n = sscanf(rdesc, "%hhx %[^\n]\n", &rdesc_buf[i], rdesc); 123 | if (n != 2) { 124 | if ((i == rdesc_len - 1) && n == 1) 125 | break; 126 | return; 127 | } 128 | } 129 | dev->rd_data = rdesc_buf; 130 | dev->rd_size = rdesc_len; 131 | } 132 | 133 | static int hid_replay_switch_dev(char *rdesc, ssize_t len) 134 | { 135 | int i; 136 | int n = sscanf(rdesc, "D: %d\n", &i); 137 | if (n != 1) 138 | return -1; 139 | return i; 140 | } 141 | 142 | static void hid_replay_incoming_event(int fd, struct uhid_event *r_event) 143 | { 144 | struct uhid_event w_event = { 0 }; 145 | 146 | switch (r_event->type) { 147 | case UHID_GET_REPORT: 148 | w_event.type = UHID_GET_REPORT_REPLY; 149 | w_event.u.get_report_reply.id = r_event->u.get_report.id; 150 | w_event.u.get_report_reply.err = -EIO; 151 | w_event.u.get_report_reply.size = 0; 152 | write(fd, &w_event, sizeof(w_event)); 153 | break; 154 | case UHID_SET_REPORT: 155 | w_event.type = UHID_SET_REPORT_REPLY; 156 | w_event.u.set_report_reply.id = r_event->u.get_report.id; 157 | w_event.u.set_report_reply.err = -EIO; 158 | write(fd, &w_event, sizeof(w_event)); 159 | break; 160 | case UHID_START: 161 | case UHID_STOP: 162 | case UHID_OPEN: 163 | case UHID_CLOSE: 164 | case UHID_OUTPUT: 165 | case __UHID_LEGACY_OUTPUT_EV: 166 | case __UHID_LEGACY_INPUT: 167 | case UHID_INPUT2: 168 | /* do nothing */ 169 | break; 170 | default: 171 | fprintf(stderr, 172 | "received unknown uhid event %d\n", 173 | r_event->type); 174 | } 175 | } 176 | 177 | static int hid_replay_get_one_event(struct hid_replay_devices_list *devices, 178 | struct uhid_event *event, 179 | int timeout) 180 | { 181 | int i, ret; 182 | 183 | do { 184 | ret = poll(devices->fds, devices->count, timeout); 185 | 186 | if (ret > 0 && devices->fds[0].revents & POLLIN) 187 | ret--; /* ignore stdin inputs */ 188 | 189 | if (ret == 0) 190 | return 0; /* timeout */ 191 | 192 | if (ret > 0) { 193 | for (i=1; i < devices->count; i++) { 194 | if (devices->fds[i].revents & POLLIN) { 195 | read(devices->fds[i].fd, event, sizeof(*event)); 196 | hid_replay_incoming_event(devices->fds[i].fd, 197 | event); 198 | return 1; 199 | } 200 | } 201 | } 202 | } while (ret > 0); 203 | 204 | return ret; 205 | } 206 | 207 | static void hid_replay_sleep(struct hid_replay_devices_list *devices, 208 | long timeout_usec) 209 | { 210 | struct uhid_event event; 211 | struct timeval current_time, end_time; 212 | long current_timeout; 213 | 214 | if (timeout_usec < 1000) { 215 | usleep(timeout_usec); 216 | return; 217 | } 218 | 219 | gettimeofday(&end_time, NULL); 220 | 221 | end_time.tv_usec += timeout_usec; 222 | end_time.tv_sec += timeout_usec / 1000000L; 223 | 224 | /* we need to flush the incoming events until timeout is done */ 225 | do { 226 | gettimeofday(¤t_time, NULL); 227 | current_timeout = (end_time.tv_sec - current_time.tv_sec) * 1000000L; 228 | current_timeout += end_time.tv_usec - current_time.tv_usec; 229 | if (current_timeout / 1000 > 0) { 230 | hid_replay_get_one_event(devices, &event, current_timeout / 1000); 231 | } else if (current_timeout > 0) { 232 | usleep(current_timeout); 233 | } 234 | } while (current_timeout > 0); 235 | } 236 | 237 | static void hid_replay_event(int fuhid, char *ubuf, ssize_t len, struct timeval *time) 238 | { 239 | struct uhid_event ev; 240 | struct uhid_input_req *input = &ev.u.input; 241 | int event_len, i; 242 | struct timeval ev_time; 243 | unsigned long sec; 244 | unsigned usec; 245 | char *buf = ubuf; 246 | int n = sscanf(buf, "E: %lu.%06u %d %[^\n]\n", &sec, &usec, &event_len, buf); 247 | if (n != 4) 248 | return; 249 | /* TODO: check consistency of buf */ 250 | 251 | memset(&ev, 0, sizeof(ev)); 252 | ev.type = UHID_INPUT; 253 | ev_time.tv_sec = sec; 254 | ev_time.tv_usec = usec; 255 | 256 | if (time->tv_sec == 0 && time->tv_usec == 0) 257 | *time = ev_time; 258 | 259 | usec = 1000000L * (ev_time.tv_sec - time->tv_sec); 260 | usec += ev_time.tv_usec - time->tv_usec; 261 | 262 | if (usec > 500) { 263 | if (usec > 3000000) 264 | usec = 3000000; 265 | hid_replay_sleep(devices, usec); 266 | 267 | *time = ev_time; 268 | } 269 | 270 | for (i = 0; i < event_len; ++i) { 271 | n = sscanf(buf, "%hhx %[^\n]\n", &input->data[i], buf); 272 | if (n != 2) { 273 | if ((i == event_len - 1) && n == 1) 274 | break; 275 | return; 276 | } 277 | } 278 | 279 | input->size = event_len; 280 | 281 | if (write(fuhid, &ev, sizeof(ev)) < 0) 282 | fprintf(stderr, "Failed to write uHID event: %s\n", strerror(errno)); 283 | } 284 | 285 | static void hid_replay_name(char *buf, ssize_t len, struct uhid_create_req *dev) 286 | { 287 | if (len - 3 >= (ssize_t)sizeof(dev->name)) 288 | return; 289 | sscanf(buf, "N: %[^\n]\n", dev->name); 290 | len = strlen((const char *)dev->name); 291 | if (dev->name[len - 1] == '\r') 292 | dev->name[len - 1] = '\0'; 293 | } 294 | 295 | static void hid_replay_phys(char *buf, ssize_t len, struct uhid_create_req *dev) 296 | { 297 | if (len - 3 >= (ssize_t)sizeof(dev->phys)) 298 | return; 299 | sscanf(buf, "P: %[^\n]\n", dev->phys); 300 | } 301 | 302 | static void hid_replay_info(char *buf, ssize_t len, struct uhid_create_req *dev) 303 | { 304 | int bus; 305 | int vid; 306 | int pid; 307 | int n; 308 | if (len - 3 >= (ssize_t)sizeof(dev->phys)) 309 | return; 310 | n = sscanf(buf, "I: %x %x %x\n", &bus, &vid, &pid); 311 | if (n != 3) 312 | return; 313 | 314 | dev->bus = bus; 315 | dev->vendor = vid; 316 | dev->product = pid; 317 | } 318 | 319 | static void hid_replay_destroy_device(struct hid_replay_device *device) 320 | { 321 | if (device->fuhid) 322 | close(device->fuhid); 323 | free(device); 324 | } 325 | 326 | static void hid_replay_destroy_devices(struct hid_replay_devices_list *devices) 327 | { 328 | struct hid_replay_device *device, *next; 329 | 330 | list_for_each_safe(&devices->devices, device, next, list) { 331 | list_del(&device->list); 332 | hid_replay_destroy_device(device); 333 | } 334 | free(devices); 335 | } 336 | 337 | static int hid_replay_parse_header(FILE *fp, struct uhid_create_req *dev) 338 | { 339 | unsigned int mask = 0; 340 | char *buf = 0; 341 | int stop = 0; 342 | ssize_t size; 343 | size_t n; 344 | int device_index = 0; 345 | 346 | do { 347 | size = getline(&buf, &n, fp); 348 | if (size == -1) 349 | continue; 350 | 351 | switch (buf[0]) { 352 | case '#': 353 | /* comments, just skip the line */ 354 | break; 355 | case 'D': 356 | if (mask == 0) { 357 | if (device_index == 0) 358 | device_index = hid_replay_switch_dev(buf, size); 359 | } else { 360 | fprintf(stderr, "Error while parsing hid-replay file, got a Device switch while the previous device was not fully configured.\n"); 361 | return -1; 362 | } 363 | break; 364 | case 'R': 365 | hid_replay_rdesc(buf, size, dev); 366 | mask |= HID_REPLAY_MASK_RDESC; 367 | break; 368 | case 'N': 369 | hid_replay_name(buf, size, dev); 370 | mask |= HID_REPLAY_MASK_NAME; 371 | break; 372 | case 'P': 373 | hid_replay_phys(buf, size, dev); 374 | break; 375 | case 'I': 376 | hid_replay_info(buf, size, dev); 377 | mask |= HID_REPLAY_MASK_INFO; 378 | break; 379 | } 380 | 381 | if (mask == HID_REPLAY_MASK_COMPLETE) { 382 | stop = 1; 383 | } 384 | 385 | } while (size > 0 && !stop); 386 | 387 | free(buf); 388 | 389 | if (mask != HID_REPLAY_MASK_COMPLETE) 390 | return -1; 391 | 392 | return device_index; 393 | } 394 | 395 | static struct hid_replay_device *__hid_replay_create_device(int idx, struct uhid_create_req *dev) 396 | { 397 | struct uhid_event event; 398 | struct hid_replay_device *device; 399 | 400 | device = calloc(1, sizeof(struct hid_replay_device)); 401 | if (device == NULL) { 402 | fprintf(stderr, "Failed to allocate uHID device: %s\n", strerror(errno)); 403 | return NULL; 404 | } 405 | 406 | device->idx = idx; 407 | device->fuhid = open(UHID_NODE, O_RDWR); 408 | if (device->fuhid < 0){ 409 | fprintf(stderr, "Failed to open uHID node: %s\n", strerror(errno)); 410 | free(device); 411 | return NULL; 412 | } 413 | 414 | memset(&event, 0, sizeof(event)); 415 | event.type = UHID_CREATE; 416 | event.u.create = *dev; 417 | 418 | if (write(device->fuhid, &event, sizeof(event)) < 0) { 419 | fprintf(stderr, "Failed to create uHID device: %s\n", strerror(errno)); 420 | hid_replay_destroy_device(device); 421 | } 422 | 423 | return device; 424 | } 425 | 426 | 427 | 428 | static struct hid_replay_devices_list *hid_replay_create_devices(FILE *fp) 429 | { 430 | struct uhid_create_req dev; 431 | struct hid_replay_devices_list *list = calloc(1, sizeof(struct hid_replay_devices_list)); 432 | int idx; 433 | 434 | if (!list) 435 | return NULL; 436 | 437 | list_head_init(&list->devices); 438 | 439 | do { 440 | memset(&dev, 0, sizeof(dev)); 441 | idx = hid_replay_parse_header(fp, &dev); 442 | if (idx >= 0) { 443 | struct hid_replay_device *new_dev = __hid_replay_create_device(idx, &dev); 444 | if (!new_dev) 445 | continue; 446 | 447 | list_add(&list->devices, &new_dev->list); 448 | list->current = new_dev; 449 | } 450 | } while (idx >= 0); 451 | 452 | return list; 453 | } 454 | 455 | static int hid_replay_setup_pollfd(struct hid_replay_devices_list *devices) 456 | { 457 | struct pollfd *fds; 458 | struct hid_replay_device *device; 459 | int count = 1; /* stdin */ 460 | 461 | list_for_each(&devices->devices, device, list) 462 | ++count; 463 | 464 | fds = calloc(count, sizeof(struct pollfd)); 465 | if (!fds) 466 | return -ENOMEM; 467 | 468 | fds[0].fd = STDIN_FILENO; 469 | fds[0].events = POLLIN; 470 | count = 1; 471 | 472 | list_for_each(&devices->devices, device, list) { 473 | fds[count].fd = device->fuhid; 474 | fds[count].events = POLLIN; 475 | ++count; 476 | } 477 | 478 | devices->fds = fds; 479 | devices->count = count; 480 | 481 | return 0; 482 | } 483 | 484 | static int hid_replay_wait_opened(struct hid_replay_devices_list *devices) 485 | { 486 | int ret; 487 | struct uhid_event event; 488 | 489 | do { 490 | ret = hid_replay_get_one_event(devices, &event, -1); 491 | if (ret == 1) { 492 | if (event.type == UHID_OPEN) { 493 | return 0; 494 | } 495 | } 496 | } while (ret == 1); 497 | 498 | return 0; 499 | } 500 | 501 | static int hid_replay_read_one(FILE *fp, struct hid_replay_devices_list *devices, struct timeval *time) 502 | { 503 | char *buf = 0; 504 | ssize_t size; 505 | size_t n; 506 | int new_id; 507 | struct hid_replay_device *device; 508 | 509 | do { 510 | size = getline(&buf, &n, fp); 511 | if (size < 1) 512 | break; 513 | switch (buf[0]) { 514 | case 'E': 515 | hid_replay_event(devices->current->fuhid, buf, size, time); 516 | free(buf); 517 | return 0; 518 | case 'D': 519 | new_id = hid_replay_switch_dev(buf, size); 520 | list_for_each(&devices->devices, device, list) 521 | if (device->idx == new_id) 522 | devices->current = device; 523 | } 524 | } while (1); 525 | 526 | free(buf); 527 | 528 | return 1; 529 | } 530 | 531 | static int try_open_uhid() 532 | { 533 | int fuhid = open(UHID_NODE, O_RDWR); 534 | if (fuhid < 0){ 535 | fprintf(stderr, "Failed to open uHID node: %s\n", strerror(errno)); 536 | return 1; 537 | } 538 | 539 | close(fuhid); 540 | 541 | return 0; 542 | } 543 | 544 | static void signal_callback_handler(int signum) 545 | { 546 | free(devices->fds); 547 | fclose(fp); 548 | hid_replay_destroy_devices(devices); 549 | 550 | /* Terminate program */ 551 | exit(signum); 552 | } 553 | 554 | int main(int argc, char **argv) 555 | { 556 | struct uhid_event event; 557 | struct uhid_create_req dev; 558 | struct timeval time; 559 | int stop = 0; 560 | char line[40]; 561 | char *hid_file; 562 | enum hid_replay_mode mode = MODE_INTERACTIVE; 563 | int sleep_time = 0; 564 | int error; 565 | 566 | memset(&event, 0, sizeof(event)); 567 | memset(&dev, 0, sizeof(dev)); 568 | 569 | if (try_open_uhid()) 570 | return EXIT_FAILURE; 571 | 572 | while (1) { 573 | int option_index = 0; 574 | int c = getopt_long(argc, argv, "hi1s:", long_options, &option_index); 575 | if (c == -1) 576 | break; 577 | switch (c) { 578 | case '1': 579 | mode = MODE_AUTO; 580 | break; 581 | case 'i': 582 | mode = MODE_INTERACTIVE; 583 | break; 584 | case 's': 585 | sleep_time = atoi(optarg); 586 | break; 587 | default: 588 | return usage(); 589 | } 590 | } 591 | 592 | if (optind < argc) { 593 | hid_file = argv[optind++]; 594 | fp = fopen(hid_file, "r"); 595 | } else 596 | fp = stdin; 597 | 598 | if (!fp) { 599 | fprintf(stderr, "Failed to open %s: %s\n", hid_file, strerror(errno)); 600 | return usage(); 601 | } 602 | 603 | devices = hid_replay_create_devices(fp); 604 | 605 | if (!devices) 606 | return EXIT_FAILURE; 607 | 608 | error = hid_replay_setup_pollfd(devices); 609 | if (error) 610 | return error; 611 | 612 | hid_replay_wait_opened(devices); 613 | 614 | if (sleep_time) 615 | hid_replay_sleep(devices, sleep_time * 1000000); 616 | 617 | signal(SIGINT, signal_callback_handler); 618 | 619 | stop = 0; 620 | while (!stop) { 621 | if (mode == MODE_INTERACTIVE) { 622 | printf("Hit enter (re)start replaying the events\n"); 623 | do { 624 | hid_replay_get_one_event(devices, &event, -1); 625 | } while (!devices->fds[0].revents & POLLIN); 626 | fgets (line, sizeof(line), stdin); 627 | } else 628 | stop = 1; 629 | 630 | memset(&time, 0, sizeof(time)); 631 | fseek(fp, 0, SEEK_SET); 632 | do { 633 | error = hid_replay_read_one(fp, devices, &time); 634 | } while (!error); 635 | } 636 | 637 | free(devices->fds); 638 | fclose(fp); 639 | hid_replay_destroy_devices(devices); 640 | return 0; 641 | } 642 | -------------------------------------------------------------------------------- /src/hid-replay.txt: -------------------------------------------------------------------------------- 1 | HID-REPLAY(1) 2 | ============= 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | 8 | hid-replay - HID Input device replay through uhid 9 | 10 | SYNOPSIS 11 | -------- 12 | *hid-replay* [OPTIONS] [FILENAME] 13 | 14 | OPTIONS 15 | ------- 16 | 17 | *-i, --interactive*:: 18 | _interactive_ mode: This is the default mode. The device is created and 19 | once the user hits _enter_ the event sequence in FILENAME is played 20 | in real time. Once completed, the program waits for the next _enter_ 21 | key event. 22 | 23 | Hit *Ctrl-C* to stop the replay. 24 | 25 | *-1, --one*:: 26 | _one shot_ mode: the device is created and the events are replayed 27 | once in real time. The process exists at the end of the replay. 28 | 29 | *-s, --sleep X*: 30 | _sleep_ X seconds once the device is created before next step. Useful 31 | in conjunction with *--one*. 32 | 33 | DESCRIPTION 34 | ----------- 35 | *hid-replay* requires the kernel uhid module to be loaded (available in 36 | kernels 3.6 and later). 37 | 38 | If the kernel module is loaded, *hid-replay* allows the user to create 39 | virtual HID devices as if they were physically plugged to the system. 40 | Replaying a previously recorded HID event sequence results in those events 41 | being handled by the right HID kernel module. 42 | 43 | *hid-replay* needs to be able to write to /dev/uhid; in most cases this means it 44 | must be run as root. 45 | 46 | *hid-recorder* and *hid-replay* are used to debug kernel issues with HID input 47 | devices. 48 | 49 | INPUT FILE 50 | ---------- 51 | The input file of *hid-replay* has the following syntax: 52 | 53 | - *#* comments 54 | - *D:* tell which device we are currently working with 55 | - *R:* size dump_of_report_descriptor_in_hexadecimal 56 | - *N:* common_name_of_the_device 57 | - *P:* physical_path 58 | - *I:* bus vendor_id product_id 59 | - *E:* timestamp size report_in_hexadecimal 60 | 61 | Any other starting characters are ignored. 62 | 63 | CAUTION 64 | ------- 65 | *hid-replay* is a low level events injector. To have the virtual device 66 | handled by the right HID kernel module, *hid-replay* fakes the device on 67 | its original bus (USB, I2C or Bluetooth). Thus, if the kernel module in use 68 | has to write _back_ to the device the kernel may oops if the module is 69 | trying to direclty talk to the physical layer. 70 | 71 | Be sure to use this program with friendly HID modules that rely only on the 72 | generic hid callbacks. 73 | 74 | EXIT CODE 75 | --------- 76 | *hid-replay* returns EXIT_FAILURE on error. 77 | 78 | SEE ALSO 79 | -------- 80 | hid-recorder(1) 81 | 82 | COPYRIGHT 83 | --------- 84 | Copyright 2012, Benjamin Tissoires. 85 | Copyright 2012-2018, Red Hat, Inc. 86 | 87 | AUTHOR 88 | ------ 89 | Benjamin Tissoires 90 | -------------------------------------------------------------------------------- /tools/HID_editor.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1279 10 | 876 11 | 12 | 13 | 14 | hid-replay HID report descriptor editor 15 | 16 | 17 | 18 | 19 | 0 20 | 0 21 | 22 | 23 | 24 | 25 | 26 | 27 | Qt::Horizontal 28 | 29 | 30 | 31 | 32 | 33 | 0 34 | 0 35 | 36 | 37 | 38 | Qt::ScrollBarAlwaysOff 39 | 40 | 41 | QTextEdit::NoWrap 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 0 52 | 0 53 | 1279 54 | 27 55 | 56 | 57 | 58 | 59 | File 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable 71 | 72 | 73 | Qt::BottomDockWidgetArea 74 | 75 | 76 | Parsing output 77 | 78 | 79 | 8 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Monospace 88 | 89 | 90 | 91 | QTextEdit::NoWrap 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | Open... 101 | 102 | 103 | Open... 104 | 105 | 106 | Open... 107 | 108 | 109 | Ctrl+O 110 | 111 | 112 | 113 | 114 | Save 115 | 116 | 117 | Ctrl+S 118 | 119 | 120 | 121 | 122 | Save As... 123 | 124 | 125 | Ctrl+Shift+S 126 | 127 | 128 | 129 | 130 | 131 | 132 | rawTextEdit 133 | textChanged() 134 | MainWindow 135 | updateRDescFromRaw() 136 | 137 | 138 | 122 139 | 169 140 | 141 | 142 | 1034 143 | 29 144 | 145 | 146 | 147 | 148 | actionOpen 149 | activated() 150 | MainWindow 151 | openFileAction() 152 | 153 | 154 | -1 155 | -1 156 | 157 | 158 | 639 159 | 437 160 | 161 | 162 | 163 | 164 | actionSave 165 | activated() 166 | MainWindow 167 | saveFileAction() 168 | 169 | 170 | -1 171 | -1 172 | 173 | 174 | 639 175 | 437 176 | 177 | 178 | 179 | 180 | actionSave_As 181 | activated() 182 | MainWindow 183 | saveAsFileAction() 184 | 185 | 186 | -1 187 | -1 188 | 189 | 190 | 639 191 | 437 192 | 193 | 194 | 195 | 196 | rawTextEdit 197 | cursorPositionChanged() 198 | MainWindow 199 | rCursorMoved() 200 | 201 | 202 | 55 203 | 115 204 | 205 | 206 | 826 207 | 30 208 | 209 | 210 | 211 | 212 | humanTextEdit 213 | cursorPositionChanged() 214 | MainWindow 215 | hCursorMoved() 216 | 217 | 218 | 729 219 | 265 220 | 221 | 222 | 556 223 | 30 224 | 225 | 226 | 227 | 228 | 229 | updateRDescFromHuman() 230 | updateRDescFromRaw() 231 | openFileAction() 232 | saveFileAction() 233 | saveAsFileAction() 234 | hCursorMoved() 235 | rCursorMoved() 236 | 237 | 238 | -------------------------------------------------------------------------------- /tools/capture_usbmon.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / capture_usbmon.py 5 | # 6 | # must be run as root. 7 | # 8 | # This program is useful to capture both the raw usb events from an input 9 | # device and its kernel generated events. 10 | # 11 | # Requires several tools to be installed: usbmon, evemu and pyudev 12 | # 13 | # Copyright (c) 2014 Benjamin Tissoires 14 | # Copyright (c) 2014 Red Hat, Inc. 15 | # 16 | # This program is free software: you can redistribute it and/or modify 17 | # it under the terms of the GNU General Public License as published by 18 | # the Free Software Foundation; either version 2 of the License, or 19 | # (at your option) any later version. 20 | # 21 | # This program is distributed in the hope that it will be useful, 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | # GNU General Public License for more details. 25 | # 26 | # You should have received a copy of the GNU General Public License 27 | # along with this program. If not, see . 28 | # 29 | 30 | import os 31 | import sys 32 | import threading 33 | import subprocess 34 | import shlex 35 | import time 36 | from optparse import OptionParser 37 | import pyudev 38 | import inspect 39 | 40 | 41 | module_path = os.path.abspath(inspect.getsourcefile(lambda _: None)) 42 | module_dirname = os.path.dirname(module_path) 43 | usbmon2hid_replay = module_dirname + "/usbmon2hid-replay.py" 44 | 45 | class UDevObject(object): 46 | """ Abstract class for an udev tree element""" 47 | def __init__(self, device, parent, children_class): 48 | "device being an udev device" 49 | self.device = device 50 | self.parent = parent 51 | self.children_class = children_class 52 | self.childrens = {} 53 | self.bind_file = None 54 | 55 | def is_child_type(self, other): 56 | "is the other UDevObject from the correct child type" 57 | # abstract method, has to be overwritten in the subclasses 58 | return False 59 | 60 | def is_parent(self, other): 61 | "true if the current UDevObject is a parent of the given UDevObject" 62 | return self.device.sys_path in other.sys_path 63 | 64 | def add_child(self, device): 65 | """add a child to the hierarchy: instanciate a new subclass of UDevObject 66 | stored in self.children_class. 67 | """ 68 | if not self.children_class: 69 | return 70 | child = self.children_class(device, self) 71 | self.childrens[device.sys_path] = child 72 | 73 | def removed(self): 74 | "called when the item is removed from the parent" 75 | # abstract method, has to be overwritten in the subclasses 76 | pass 77 | 78 | def clean(self): 79 | "remove all of the children of the UDevObject" 80 | for child in self.childrens.values(): 81 | child.removed() 82 | child.clean() 83 | self.childrens = {} 84 | 85 | def udev_event(self, action, device): 86 | "called when a udev event is processed" 87 | if self.is_child_type(device): 88 | # the device is our direct child, add/remove it to the hierarchy 89 | if action == "add": 90 | self.add_child(device) 91 | else: 92 | if device.sys_path in self.childrens.keys(): 93 | # be sure to notify the "removed" call before deleting it 94 | self.childrens[device.sys_path].removed() 95 | del(self.childrens[device.sys_path]) 96 | else: 97 | # maybe our children know how to handle it 98 | for child in self.childrens.values(): 99 | if child.is_parent(device): 100 | child.udev_event(action, device) 101 | 102 | def get_name(self): 103 | "return a more convenient name for the current object" 104 | return self.device.sys_path 105 | 106 | def print_tree(self, level = 0): 107 | "convenient function to print a tree of the current known devices" 108 | print self.get_name() 109 | for child in self.childrens.values(): 110 | print (" " * level) + u' └', 111 | child.print_tree(level + 1) 112 | 113 | def unbind(self): 114 | "unbind the device from its current driver" 115 | path = self.device.sys_path 116 | unbind_path = "{0}/driver/unbind".format(path) 117 | bind_path = "{0}/driver/bind".format(path) 118 | if not os.path.exists(unbind_path): 119 | return False 120 | self.unbind_file = open(unbind_path, "w") 121 | self.bind_file = open(bind_path, "w") 122 | 123 | self.unbind_file.write(self.device.sys_name) 124 | self.unbind_file.close() 125 | return True 126 | 127 | def rebind(self): 128 | "rebind the device to its driver (unbind has to be called first)" 129 | if not self.bind_file: 130 | raise Exception, "trying to rebind an unbind device" 131 | 132 | self.bind_file.write(self.device.sys_name) 133 | self.bind_file.close() 134 | self.bind_file = None 135 | 136 | class EventNode(UDevObject): 137 | def __init__(self, device, parent): 138 | # no children devices for this one 139 | UDevObject.__init__(self, device, parent, None) 140 | self.index = int(self.device.sys_name.replace("event", "")) 141 | self.start_evemu() 142 | 143 | def get_name(self): 144 | return "{0}_{1}.ev".format(self.parent.get_name(), self.index) 145 | 146 | def removed(self): 147 | # close the underlying evemu process when the device is removed 148 | self.p.terminate() 149 | self.p.wait() 150 | self.output.close() 151 | 152 | def start_evemu(self): 153 | # start an evemu-record of the event node 154 | print "dumping evdev events in", self.get_name() 155 | self.output = open(self.get_name(), 'w') 156 | evemu_command = "evemu-record /dev/input/{0}".format(self.device.sys_name) 157 | print evemu_command 158 | self.p = subprocess.Popen(shlex.split(evemu_command), stdout=self.output) 159 | 160 | 161 | class USBInterface(UDevObject): 162 | def __init__(self, device, parent): 163 | UDevObject.__init__(self, device, parent, EventNode) 164 | self.intf_number = device.sys_name.split(':')[-1] 165 | self.lsusb() 166 | 167 | def is_child_type(self, other): 168 | return other.subsystem == u'input' and u'event' in other.sys_name 169 | 170 | def get_name(self): 171 | return "{0}_{1}".format(self.parent.get_name(), self.intf_number) 172 | 173 | def write_hid_file(self): 174 | "convert the usbmon recording into a hid recording" 175 | intf = self.intf_number.split(".")[-1] 176 | usbmon = self.parent.get_usbmon_filename() 177 | usbmon_command = "python {0} {1} --intf {2}".format(usbmon2hid_replay, usbmon, intf) 178 | f = open(self.get_name() + ".hid", "w") 179 | p = subprocess.Popen(shlex.split(usbmon_command), stdout=f) 180 | p.wait() 181 | print "written", self.get_name() + ".hid" 182 | 183 | def removed(self): 184 | self.parent.remove_interface(self) 185 | 186 | def lsusb(self): 187 | """when the usb driver does not checks for the report descriptors, we have 188 | to ask them ourself: call `lsusb -v' when the driver is not bound.""" 189 | # unbind the device first 190 | if not self.unbind(): 191 | return 192 | 193 | # call lsusb -v 194 | lsusbcall = "lsusb -v -d {0}:{1}".format(self.parent.vid, self.parent.pid) 195 | subprocess.call(shlex.split(lsusbcall), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 196 | 197 | #rebind the device 198 | self.rebind() 199 | 200 | class USBDev(UDevObject): 201 | """ A USB device object: 202 | - will keep the interface hierarchy 203 | - at unplug, convert the usb recording into the various hid files (one 204 | per known interface) 205 | """ 206 | def __init__(self, device): 207 | UDevObject.__init__(self, device, None, USBInterface) 208 | self.vid = device.get("ID_VENDOR_ID") 209 | self.pid = device.get("ID_MODEL_ID") 210 | self.vendor = device.get("ID_VENDOR").replace(".", "") 211 | self.start_usbmon() 212 | self.removed_intf = [] 213 | 214 | def is_child_type(self, other): 215 | return other.device_type == u'usb_interface' 216 | 217 | def get_name(self): 218 | return "{0}_{1}_{2}".format(self.vendor, self.vid, self.pid) 219 | 220 | def get_usbmon_filename(self): 221 | "return the usbmon file name were the events are recorded" 222 | return self.get_name() + ".usbmon" 223 | 224 | def start_usbmon(self): 225 | "start the usbmon subprocess" 226 | number = self.device.device_node.split('/')[-1] 227 | bus = int(self.device.device_node.split('/')[-2]) 228 | 229 | # start usbmon 230 | print "dumping usb events in", self.get_usbmon_filename() 231 | self.usbmon_file = open(self.get_usbmon_filename(), 'w') 232 | USBMon.add_listener(bus, number, self.usbmon_file) 233 | 234 | def remove_interface(self, intf): 235 | "when an interface is removed, this method is called" 236 | self.removed_intf.append(intf) 237 | 238 | def terminate(self): 239 | """clean up and terminate the usb device: 240 | - stop the usbmon capture for this device 241 | - remove any zombi child 242 | - ask for each known interface to translate the usbmon capture into a hid one 243 | """ 244 | number = self.device.device_node.split('/')[-1] 245 | bus = int(self.device.device_node.split('/')[-2]) 246 | USBMon.remove_listener(bus, number) 247 | self.usbmon_file.close() 248 | self.clean() 249 | for intf in self.removed_intf: 250 | intf.write_hid_file() 251 | 252 | class USBMon(threading.Thread): 253 | """usbmon recorder class: 254 | - calling a new object USBMon(bus) starts recording usb events on this bus 255 | - each device gets buffered in its own queue of events 256 | - when someone add a listener for a specific device, the buffered events are 257 | dumped into the given file and each new event is dumped too 258 | """ 259 | busses = {} 260 | def __init__(self, bus): 261 | threading.Thread.__init__(self) 262 | self.bus = bus 263 | USBMon.busses[bus] = self 264 | self.devices = {} 265 | self.bufs = {} 266 | # launch the actual usbmon tool with a buffer big enough to store 267 | # the various hid report descriptors 268 | self.p = subprocess.Popen(shlex.split("usbmon -i {0} -fu -s 512".format(bus)), stdout=subprocess.PIPE) 269 | self.start() 270 | 271 | def pump_events(self, addr): 272 | """matches the given device with the current listeners and dumps 273 | the queue of events into the correct listener""" 274 | if not addr in self.devices.keys(): 275 | # no listener found, keep the events for later 276 | return 277 | while len(self.bufs[addr]) > 0: 278 | line = self.bufs[addr].pop(0) 279 | self.devices[addr].write(line) 280 | 281 | def run(self): 282 | while self.p: 283 | line = self.p.stdout.readline() 284 | if not line: 285 | # end of capture 286 | break 287 | 288 | # extract the device address 289 | tag, timestamp, event_type, address, status, usbmon_data = line.rstrip().split(" ", 5) 290 | URB_type, bus, dev_address, endpoint = address.split(":") 291 | key_addr = USBMon.create_key(bus, dev_address) 292 | 293 | if not self.bufs.has_key(key_addr): 294 | # new device, add a new queue to the list 295 | self.bufs[key_addr] = [] 296 | 297 | # add the event to the queue 298 | self.bufs[key_addr].append(line) 299 | 300 | # try flushing the event into the listeners 301 | self.pump_events(key_addr) 302 | 303 | def stop(self): 304 | p = self.p 305 | if not p: 306 | return 307 | self.p = None 308 | p.terminate() 309 | 310 | @classmethod 311 | def create_key(cls, bus, number): 312 | "create a uniq key for a given usb device (bus, number)" 313 | return "{0}:{1}:".format(bus, number) 314 | 315 | @classmethod 316 | def add_listener(cls, bus, number, file): 317 | "append a listener (an opened writable file) for a given usb device" 318 | if not cls.busses.has_key(bus): 319 | USBMon(bus) 320 | usbmon = cls.busses[bus] 321 | usbmon.devices[cls.create_key(bus, number)] = file 322 | 323 | @classmethod 324 | def remove_listener(cls, bus, number): 325 | "remove the listener from the given usb device" 326 | usbmon = cls.busses[bus] 327 | del(usbmon.devices[cls.create_key(bus, number)]) 328 | 329 | @classmethod 330 | def terminate_usbmon(cls): 331 | "terminate all instances of usbmon tools" 332 | for usbmon in cls.busses.values(): 333 | usbmon.stop() 334 | cls.busses = {} 335 | 336 | class Conductor(object): 337 | """ In charge of reading udev events and launching the various 338 | external program.""" 339 | def __init__(self): 340 | self.context = pyudev.Context() 341 | # create udev notification system 342 | self.monitor = pyudev.Monitor.from_netlink(pyudev.Context()) 343 | self.monitor.filter_by('input') 344 | self.monitor.filter_by('usb') 345 | self.cv = threading.Condition() 346 | self.done = False 347 | self.devices = {} 348 | self.start_usbmon() 349 | self.observer = pyudev.MonitorObserver(self.monitor, self.udev_event) 350 | 351 | def start_usbmon(self): 352 | "look for any root hub and start usbmon on every found one" 353 | for device in self.context.list_devices(subsystem='usb', DEVTYPE='usb_device'): 354 | id_model = device.get('ID_MODEL_FROM_DATABASE') 355 | if id_model and " root hub" in id_model: 356 | USBMon(int(device.get('BUSNUM'))) 357 | 358 | def start(self): 359 | "start monitoring udev events" 360 | self.observer.start() 361 | 362 | def stop(self): 363 | "stop monitoring udev events" 364 | self.observer.stop() 365 | self.cv.acquire() 366 | self.done = True 367 | self.cv.notify() 368 | self.cv.release() 369 | 370 | def wait(self): 371 | "wait for the user to hit ctrl-C to terminate the monitoring/recording" 372 | self.cv.acquire() 373 | try: 374 | while not self.done: 375 | self.cv.wait(timeout=1.0) 376 | except (KeyboardInterrupt, SystemExit): 377 | print "" 378 | self.stop() 379 | self.cv.release() 380 | 381 | def print_tree(self): 382 | "convenient function to print a tree of the current known devices" 383 | for usb in self.devices.values(): 384 | usb.print_tree() 385 | 386 | def udev_event(self, action, device): 387 | "called when an udev event is processed" 388 | # print action, device 389 | if device.device_type == u'usb_device': 390 | # the device is a new usb device (hub), add/remove it to 391 | # the tree 392 | if action == "add": 393 | usb = USBDev(device) 394 | self.devices[device.sys_path] = usb 395 | else: 396 | if device.sys_path in self.devices.keys(): 397 | usb = self.devices[device.sys_path] 398 | del(self.devices[device.sys_path]) 399 | # stopping all captures and flush the various files 400 | usb.terminate() 401 | else: 402 | # this device is unknown at this level, maybe the usb device 403 | # knows how to handle it 404 | for usb in self.devices.values(): 405 | if usb.is_parent(device): 406 | usb.udev_event(action, device) 407 | 408 | def flush(self): 409 | for usb in self.devices.values(): 410 | usb.terminate() 411 | USBMon.terminate_usbmon() 412 | 413 | def get_options(): 414 | description = \ 415 | """ 416 | %prog will capture several traces from the plugged USB devices: 417 | the raw usb events, the evemu events generated by the kernel, and if possible 418 | will convert the usb events into hid events for later replay. 419 | Be careful when recording events (from keyboards for instance), it _can_ (will) 420 | record any password you type, so use it carefully. 421 | """ 422 | parser = OptionParser(description=description) 423 | # parser.add_option("", "--intf", dest="intf", 424 | # help="capture only the given interface number, omit if you don't want to filter") 425 | return parser.parse_args() 426 | 427 | def main(): 428 | (options, args) = get_options() 429 | conductor = Conductor() 430 | conductor.start() 431 | print """ 432 | This program will capture the usb raw events, the kernel emitted input events 433 | and also will convert the usb capture into a HID recording. 434 | 435 | Please now plug any device you wish to capture, make some events with it, and 436 | unplug it when you have finished. You can plug/unplug several different devices 437 | at the same time, but only the latest recordings from each device will be kept. 438 | 439 | Hit Ctrl-C to terminate the program. 440 | """ 441 | conductor.wait() 442 | conductor.flush() 443 | 444 | if __name__ == "__main__": 445 | main() 446 | -------------------------------------------------------------------------------- /tools/data/0000_undefined.hut: -------------------------------------------------------------------------------- 1 | (00) Undefined 2 | 00 Undefined 3 | 01-FFFF Reserved 4 | -------------------------------------------------------------------------------- /tools/data/0001_generic_desktop.hut: -------------------------------------------------------------------------------- 1 | (01) Generic Desktop 2 | 00 Undefined 3 | 01 Pointer 4 | 02 Mouse 5 | 03 Reserved 6 | 04 Joystick 7 | 05 Game Pad 8 | 06 Keyboard 9 | 07 Keypad 10 | 08 Multi Axis 11 | 09 Reserved 12 | 0A Water Cooling Device / Assistive Control 13 | 0B Computer Chassis Device 14 | 0C Wireless Radio Controls 15 | 0D Portable Device Control 16 | 0E System Multi-Axis Controller 17 | 0F Spatial Controller 18 | 10-2F Reserved 19 | 30 X 20 | 31 Y 21 | 32 Z 22 | 33 Rx 23 | 34 Ry 24 | 35 Rz 25 | 36 Slider 26 | 37 Dial 27 | 38 Wheel 28 | 39 Hat switch 29 | 3A Counted Buffer 30 | 3B Byte Count 31 | 3C Motion 32 | 3D Start 33 | 3E Select 34 | 3F Reserved 35 | 40 Vx 36 | 41 Vy 37 | 42 Vz 38 | 43 Vbrx 39 | 44 Vbry 40 | 45 Vbrz 41 | 46 Vno 42 | 47 Feature 43 | 48 Resolution Multiplier 44 | 49 Qx 45 | 4A Qy 46 | 4B Qz 47 | 4C Qw 48 | 4D-7F Reserved 49 | 80 System Control 50 | 81 System Power Down 51 | 82 System Sleep 52 | 83 System Wake Up 53 | 84 System Context Menu 54 | 85 System Main Menu 55 | 86 System App Menu 56 | 87 System Help Menu 57 | 88 System Menu Exit 58 | 89 System Menu Select 59 | 8A System Menu Right 60 | 8B System Menu Left 61 | 8C System Menu Up 62 | 8D System Menu Down 63 | 8E System Cold Restart 64 | 8F System Warm Restart 65 | 90 D-Pad Up 66 | 91 D-Pad Down 67 | 92 D-Pad Right 68 | 93 D-Pad Left 69 | 94 Index Trigger 70 | 95 Palm Trigger 71 | 96 Thumbstick 72 | 97-9F Reserved 73 | A0 System Dock 74 | A1 System UnDock 75 | A2 System Setup 76 | A3 System Break 77 | A4 System Debugger Break 78 | A5 Application Break 79 | A6 Application Debugger Break 80 | A7 System Speaker Mute 81 | A8 System Hibernate 82 | A9-AF Reserved 83 | B0 System Display Invert 84 | B1 System Display Internal 85 | B2 System Display External 86 | B3 System Display Both 87 | B4 System Display Dual 88 | B5 System Display Toggle Internal External 89 | B6 System Display Swap Primary Secondary 90 | B7 System Display LCDAuto Scale 91 | B8-BF Reserved 92 | C0 Sensor Zone 93 | C1 RPM 94 | C2 Coolant Level 95 | C3 Coolant Critical Level 96 | C4 Coolant Pump 97 | C5 Chassis Enclosure 98 | C6 Wireless Radio Button 99 | C7 Wireless Radio LED 100 | C8 Wireless Radio Slider Switch 101 | C9 System Display Rotation Lock Button 102 | CA System Display Rotation Lock Slider Switch 103 | CB Control Enable 104 | CC-FFFF Reserved 105 | -------------------------------------------------------------------------------- /tools/data/0002_simulation_controls.hut: -------------------------------------------------------------------------------- 1 | (02) Simulation Controls 2 | 00 Undefined 3 | 01 Flight Simulation Device 4 | 02 Automobile Simulation Device 5 | 03 Tank Simulation Device 6 | 04 Spaceship Simulation Device 7 | 05 Submarine Simulation Device 8 | 06 Sailing Simulation Device 9 | 07 Motorcycle Simulation Device 10 | 08 Sports Simulation Device 11 | 09 Airplane Simulation Device 12 | 0A Helicopter Simulation Device 13 | 0B Magic Carpet Simulation Device 14 | 0C Bicycle 15 | 0D-1F reserved 16 | 20 Flight Control Stick 17 | 21 Flight Stick 18 | 22 Cyclic Control 19 | 23 Cyclic Trim 20 | 24 Flight Yoke 21 | 25 Track Control 22 | 26 Driving Control 23 | 27-CF reserved 24 | B0 Aileron 25 | B1 Aileron Trim 26 | B2 Anti-Torque Control 27 | B3 Auto-pilot enable 28 | B4 Chaff Release 29 | B5 Collective Control 30 | B6 Dive Brake 31 | B7 Electronic Counter Measures 32 | B8 Elevator 33 | B9 Elevator Trim 34 | BA Rudder 35 | BB Throttle 36 | BC Flight Communication 37 | BD Flare Release 38 | BE Landing Gear 39 | BF Toe Brake 40 | C0 Trigger 41 | C1 Weapons Arm 42 | C2 Weapons Select 43 | C3 Wing Flaps 44 | C4 Accelerator 45 | C5 Brake 46 | C6 Clutch 47 | C7 Shifter 48 | C8 Steering 49 | C9 Turret Direction 50 | CA Barrel Elevation 51 | CB Dive Plane 52 | CC Ballast 53 | CD Bicycle Crank 54 | CE Handle Bars 55 | CF Front Brake 56 | D0 Rear Brake 57 | D1-FFFF reserved 58 | -------------------------------------------------------------------------------- /tools/data/0003_vr_controls.hut: -------------------------------------------------------------------------------- 1 | (03) VR Controls 2 | 00 Unidentified 3 | 01 Belt 4 | 02 Body Suit 5 | 03 Flexor 6 | 04 Glove 7 | 05 Head Tracker 8 | 06 Head Mounted Display 9 | 07 Hand Tracker 10 | 08 Oculometer 11 | 09 Vest 12 | 0A Animatronic Device 13 | 0B-1F Reserved 14 | 20 Stereo Enable 15 | 21 Display Enable 16 | 22-FFFF Reserved 17 | -------------------------------------------------------------------------------- /tools/data/0004_sports_controls.hut: -------------------------------------------------------------------------------- 1 | (04) Sports Controls 2 | 00 Unidentified 3 | 01 Baseball Bat 4 | 02 Golf Club 5 | 03 Rowing Machine 6 | 04 Treadmill 7 | 05-2F Reserved 8 | 30 Oar 9 | 31 Slope 10 | 32 Rate 11 | 33 Stick Speed 12 | 34 Stick Face Angle 13 | 35 Stick Heel/Toe 14 | 36 Stick Follow Through 15 | 37 Stick Tempo 16 | 38 Stick Type 17 | 39 Stick Height 18 | 3A-4F Reserved 19 | 50 Putter 20 | 51 1 Iron 21 | 52 2 Iron 22 | 53 3 Iron 23 | 54 4 Iron 24 | 55 5 Iron 25 | 56 6 Iron 26 | 57 7 Iron 27 | 58 8 Iron 28 | 59 9 Iron 29 | 5A 10 Iron 30 | 5B 11 Iron 31 | 5C Sand Wedge 32 | 5D Loft Wedge 33 | 5E Power Wedge 34 | 5F 1 Wood 35 | 60 3 Wood 36 | 61 5 Wood 37 | 62 7 Wood 38 | 63 9 Wood 39 | 64-FFFF Reserved 40 | -------------------------------------------------------------------------------- /tools/data/0005_gaming_controls.hut: -------------------------------------------------------------------------------- 1 | (05) Gaming Controls 2 | 00 Undefined 3 | 01 3D Game Controller 4 | 02 Pinball Device 5 | 03 Gun Device 6 | 04-1F Reserved 7 | 20 Point of View 8 | 21 Turn Right/Left 9 | 22 Pitch Forward/Backward 10 | 23 Roll Right/Left 11 | 24 Move Right/Left 12 | 25 Move Forward/Backward 13 | 26 Move Up/Down 14 | 27 Lean Right/Left 15 | 28 Lean Forward/Backward 16 | 29 Height of POV 17 | 2A Flipper 18 | 2B Secondary Flipper 19 | 2C Bump 20 | 2D New Game 21 | 2E Shoot Ball 22 | 2F Player 23 | 30 Gun Bolt 24 | 31 Gun Clip 25 | 32 Gun Selector 26 | 33 Gun Single Shot 27 | 34 Gun Burst 28 | 35 Gun Automatic 29 | 36 Gun Safety 30 | 37 Gamepad Fire/Jump 31 | 38 Reserved 32 | 39 Gamepad Trigger 33 | 3A Form-fitting gamepad 34 | 3B-FFFF Reserved 35 | -------------------------------------------------------------------------------- /tools/data/0006_generic_device_controls.hut: -------------------------------------------------------------------------------- 1 | (06) Generic Device Controls 2 | 00 Unidentified 3 | 01 Background Controls 4 | 02-1F Reserved 5 | 20 Battery Strength 6 | 21 Wireless Channel 7 | 22 Wireless ID 8 | 23 Discover Wireless Control 9 | 24 Security Code Character Entered 10 | 25 Security Code Character Erased 11 | 26 Security Code Cleared 12 | 27 Sequence ID 13 | 28 Sequence ID Reset 14 | 29 RF Signal Strength 15 | 2A Software Version 16 | 2B Protocol Version 17 | 2C Hardware Version 18 | 2D Major 19 | 2E Minor 20 | 2F Revision 21 | 30 Handedness 22 | 31 Either Hand 23 | 32 Left Hand 24 | 33 Right Hand 25 | 34 Both Hands 26 | 35-3F Reserved 27 | 40 Grip Pose Offset 28 | 41 Pointer Pose Offset 29 | 42-FFFF Reserved 30 | -------------------------------------------------------------------------------- /tools/data/0007_keyboard.hut: -------------------------------------------------------------------------------- 1 | (07) Keyboard 2 | 00 Reserved (no event indicated) 3 | 01 ErrorRollOver 4 | 02 POSTFail 5 | 03 ErrorUndefine 6 | 04 a and A 7 | 05 b and B 8 | 06 c and C 9 | 07 d and D 10 | 08 e and E 11 | 09 f and F 12 | 0A g and G 13 | 0B h and H 14 | 0C i and I 15 | 0D j and J 16 | 0E k and K 17 | 0F l and L 18 | 10 m and M 19 | 11 n and N 20 | 12 o and O 21 | 13 p and P 22 | 14 q and Q 23 | 15 r and R 24 | 16 s and S 25 | 17 t and T 26 | 18 u and U 27 | 19 v and V 28 | 1A w and W 29 | 1B x and X 30 | 1C y and Y 31 | 1D z and Z 32 | 1E 1 and ! 33 | 1F 2 and @ 34 | 20 3 and # 35 | 21 4 and $ 36 | 22 5 and % 37 | 23 6 and ^ 38 | 24 7 and & 39 | 25 8 and * 40 | 26 9 and ( 41 | 27 0 and ) 42 | 28 Return (ENTER) 43 | 29 ESCAPE 44 | 2A DELETE (Backspace) 45 | 2B Tab 46 | 2C Spacebar 47 | 2D - and (underscore) 48 | 2E = and + 49 | 2F [ and { 50 | 30 ] and } 51 | 31 \ and | 52 | 32 Non-US # and ~ 53 | 33 ; and : 54 | 34 ' and " 55 | 35 Grave Accent and Tilde 56 | 36 Keyboard, and < 57 | 37 . and > 58 | 38 / and ? 59 | 39 Caps Lock 60 | 3A F1 61 | 3B F2 62 | 3C F3 63 | 3D F4 64 | 3E F5 65 | 3F F6 66 | 40 F7 67 | 41 F8 68 | 42 F9 69 | 43 F10 70 | 44 F11 71 | 45 F12 72 | 46 PrintScreen 73 | 47 Scroll Lock 74 | 48 Pause 75 | 49 Insert 76 | 4A Home 77 | 4B PageUp 78 | 4C Delete Forward 79 | 4D End 80 | 4E PageDown 81 | 4F RightArrow 82 | 50 LeftArrow 83 | 51 DownArrow 84 | 52 UpArrow 85 | 53 Keypad Num Lock and Clear 86 | 54 Keypad / 87 | 55 Keypad * 88 | 56 Keypad - 89 | 57 Keypad + 90 | 58 Keypad ENTER 91 | 59 Keypad 1 and End 92 | 5A Keypad 2 and Down Arrow 93 | 5B Keypad 3 and PageDn 94 | 5C Keypad 4 and Left Arrow 95 | 5D Keypad 5 96 | 5E Keypad 6 and Right Arrow 97 | 5F Keypad 7 and Home 98 | 60 Keypad 8 and Up Arrow 99 | 61 Keypad 9 and PageUp 100 | 62 Keypad 0 and Insert 101 | 63 Keypad . and Delete 102 | 64 Non-US \ and | 103 | 65 Application 104 | 66 Power 105 | 67 Keypad = 106 | 68 F13 107 | 69 F14 108 | 6A F15 109 | 6B F16 110 | 6C F17 111 | 6D F18 112 | 6E F19 113 | 6F F20 114 | 70 F21 115 | 71 F22 116 | 72 F23 117 | 73 F24 118 | 74 Execute 119 | 75 Help 120 | 76 Menu 121 | 77 Select 122 | 78 Stop 123 | 79 Again 124 | 7A Undo 125 | 7B Cut 126 | 7C Copy 127 | 7D Paste 128 | 7E Find 129 | 7F Mute 130 | 80 Volume Up 131 | 81 Volume Down 132 | 82 Locking Caps Lock 133 | 83 Locking Num Lock 134 | 84 Locking Scroll Lock 135 | 85 Keypad Comma 136 | 86 Keypad Equal Sign 137 | 87 Kanji1 138 | 88 Kanji2 139 | 89 Kanji3 140 | 8A Kanji4 141 | 8B Kanji5 142 | 8C Kanji6 143 | 8D Kanji7 144 | 8E Kanji8 145 | 8F Kanji9 146 | 90 LANG1 147 | 91 LANG2 148 | 92 LANG3 149 | 93 LANG4 150 | 94 LANG5 151 | 95 LANG6 152 | 96 LANG7 153 | 97 LANG8 154 | 98 LANG9 155 | 99 Alternate Erase 156 | 9A SysReq/Attention 157 | 9B Cancel 158 | 9C Clear 159 | 9D Prior 160 | 9E Return 161 | 9F Separator 162 | A0 Out 163 | A1 Oper 164 | A2 Clear/Again 165 | A3 CrSel/Props 166 | A4 ExSel 167 | A5-DF Reserved 168 | E0 LeftControl 169 | E1 LeftShift 170 | E2 LeftAlt 171 | E3 Left GUI 172 | E4 RightControl 173 | E5 RightShift 174 | E6 RightAlt 175 | E7 Right GUI 176 | E8-FFFF Reserved 177 | -------------------------------------------------------------------------------- /tools/data/0008_leds.hut: -------------------------------------------------------------------------------- 1 | (08) LEDs 2 | 00 Undefined 3 | 01 Num Lock 4 | 02 Caps Lock 5 | 03 Scroll Lock 6 | 04 Compose 7 | 05 Kana 8 | 06 Power 9 | 07 Shift 10 | 08 Do Not Disturb 11 | 09 Mute 12 | 0A Tone Enable 13 | 0B High Cut Filter 14 | 0C Low Cut Filter 15 | 0D Equalizer Enable 16 | 0E Sound Field On 17 | 0F Surround field On 18 | 10 Repeat 19 | 11 Stereo 20 | 12 Sampling Rate Detect 21 | 13 Spinning 22 | 14 CAV 23 | 15 CLV 24 | 16 Recording Format Detect 25 | 17 Off-Hook 26 | 18 Ring 27 | 19 Message Waiting 28 | 1A Data Mode 29 | 1B Battery Operation 30 | 1C Battery OK 31 | 1D Battery Low 32 | 1E Speaker 33 | 1F Head Set 34 | 20 Hold 35 | 21 Microphone 36 | 22 Coverage 37 | 23 Night Mode 38 | 24 Send Calls 39 | 25 Call Pickup 40 | 26 Conference 41 | 27 Stand-by 42 | 28 Camera On 43 | 29 Camera Off 44 | 2A On-Line 45 | 2B Off-Line 46 | 2C Busy 47 | 2D Ready 48 | 2E Paper-Out 49 | 2F Paper-Jam 50 | 30 Remote 51 | 31 Forward 52 | 32 Reverse 53 | 33 Stop 54 | 34 Rewind 55 | 35 Fast Forward 56 | 36 Play 57 | 37 Pause 58 | 38 Record 59 | 39 Error 60 | 3A Usage Selected Indicator 61 | 3B Usage In Use Indicator 62 | 3C Usage Multi Mode Indicator 63 | 3D Indicator On 64 | 3E Indicator Flash 65 | 3F Indicator Slow Blink 66 | 40 Indicator Fast Blink 67 | 41 Indicator Off 68 | 42 Flash On Time 69 | 43 Slow Blink On Time 70 | 44 Slow Blink Off Time 71 | 45 Fast Blink On Time 72 | 46 Fast Blink Off Time 73 | 47 Usage Indicator Color 74 | 48 Indicator Red 75 | 49 Indicator Green 76 | 4A Indicator Amber 77 | 4B Generic Indicator 78 | 4C System Suspend 79 | 4D External Power Connected 80 | 4E Indicator Blue 81 | 4F Indicator Orange 82 | 50 Good Status 83 | 51 Warning Status 84 | 52 RGBLED 85 | 53 Red LEDChannel 86 | 54 Greed LEDChannel 87 | 55 Blue LEDChannel 88 | 56 LEDIntensity 89 | 57-FFFF Reserved 90 | 57-5F Reserved 91 | 60 Player Indicator 92 | 61 Player 1 93 | 62 Player 2 94 | 63 Player 3 95 | 64 Player 4 96 | 65 Player 5 97 | 66 Player 6 98 | 67 Player 7 99 | 68 Player 8 100 | 69-FFFF Reserved 101 | -------------------------------------------------------------------------------- /tools/data/0009_button.hut: -------------------------------------------------------------------------------- 1 | (09) Button 2 | 00 No Buttons Pressed 3 | -------------------------------------------------------------------------------- /tools/data/000a_ordinals.hut: -------------------------------------------------------------------------------- 1 | (0a) Ordinals 2 | 00 Unused 3 | -------------------------------------------------------------------------------- /tools/data/000b_telephony_devices.hut: -------------------------------------------------------------------------------- 1 | (0b) Telephony Devices 2 | 00 Unassigned 3 | 01 Phone 4 | 02 Answering Machine 5 | 03 Message Controls 6 | 04 Handset 7 | 05 Headset 8 | 06 Telephony Key Pad 9 | 07 Programmable Button 10 | 08-1F Reserved 11 | 20 Hook Switch 12 | 21 Flash 13 | 22 Feature 14 | 23 Hold 15 | 24 Redial 16 | 25 Transfer 17 | 26 Drop 18 | 27 Park 19 | 28 Forward Calls 20 | 29 Alternate Function 21 | 2A Line OSC 22 | 2B Speaker Phone 23 | 2C Conference 24 | 2D Ring Enable 25 | 2E Ring Select 26 | 2F Phone Mute 27 | 30 Caller ID 28 | 31 Send 29 | 32-4F Reserved 30 | 50 Speed Dial 31 | 51 Store Number 32 | 52 Recall Number 33 | 53 Phone Directory 34 | 54-6F Reserved 35 | 70 Voice Mail 36 | 71 Screen Calls 37 | 72 Do Not Disturb 38 | 73 Message 39 | 74 Answer On/Off 40 | 75-8F Reserved 41 | 90 Inside Dial Tone 42 | 91 Outside Dial Tone 43 | 92 Inside Ring Tone 44 | 93 Outside Ring Tone 45 | 94 Priority Ring Tone 46 | 95 Inside Ringback 47 | 96 Priority Ringback 48 | 97 Line Busy Tone 49 | 98 Reorder Tone 50 | 99 Call Waiting Tone 51 | 9A Confirmation Tone 1 52 | 9B Confirmation Tone 2 53 | 9C Tones Off 54 | 9D Outside Ringback 55 | 9E Ringer 56 | 9F-AF Reserved 57 | B0 Phone Key 0 58 | B1 Phone Key 1 59 | B2 Phone Key 2 60 | B3 Phone Key 3 61 | B4 Phone Key 4 62 | B5 Phone Key 5 63 | B6 Phone Key 6 64 | B7 Phone Key 7 65 | B8 Phone Key 8 66 | B9 Phone Key 9 67 | BA Phone Key Star 68 | BB Phone Key Pound 69 | BC Phone Key A 70 | BD Phone Key B 71 | BE Phone Key C 72 | BF Phone Key D 73 | C0 Phone Call History Key 74 | C1 Phone Caller ID Key 75 | C2 Phone Settings Key 76 | C3-DF Reserved 77 | F0 Host Control 78 | F1 Host Available 79 | F2 Host Call Active 80 | F3 Activate Handset Audio 81 | F4 Ring Type 82 | F5 Re-dialable Phone Number 83 | F6-F7 Reserved 84 | F8 Stop Ring Tone 85 | F9 PSTN Ring Tone 86 | FA Host Ring Tone 87 | FB Alert Sound Error 88 | FC Alert Sound Confirm 89 | FD Alert Sound Notification 90 | FE Silent Ring 91 | FF-107 Reserved 92 | 108 Email Message Waiting 93 | 109 oicemail Message Waiting 94 | 10A ost Hold 95 | 109-10F Reserved 96 | 110 Incoming Call History Count 97 | 111 Outgoing Call History Count 98 | 112 Incoming Call History 99 | 113 Outgoing Call History 100 | 114 Phone Locale 101 | 115-13F Reserved 102 | 140 Phone Time Second 103 | 141 Phone Time Minute 104 | 142 Phone Time Hour 105 | 143 Phone Date Day 106 | 144 Phone Date Month 107 | 145 Phone Date Year 108 | 146 Handset Nickname 109 | 147 Address Book ID 110 | 14A Call Duration 111 | 14B Dual Mode Phone 112 | 14C-FFFF Reserved 113 | -------------------------------------------------------------------------------- /tools/data/000c_consumer_devices.hut: -------------------------------------------------------------------------------- 1 | (0c) Consumer Devices 2 | 01 Consumer Control 3 | 02 Numeric Key Pad 4 | 03 Programmable Buttons 5 | 04 Microphone 6 | 05 Headphone 7 | 06 Graphic Equalizer 8 | 07-1F Reserved 9 | 20 +10 10 | 21 +100 11 | 22 AM/PM 12 | 23-2F Reserved 13 | 30 Power 14 | 31 Reset 15 | 32 Sleep 16 | 33 Sleep After 17 | 34 Sleep Mode 18 | 35 Illumination 19 | 36 Function Buttons 20 | 37-3F Reserved 21 | 40 Menu 22 | 41 Menu Pick 23 | 42 Menu Up 24 | 43 Menu Down 25 | 44 Menu Left 26 | 45 Menu Right 27 | 46 Menu Escape 28 | 47 Menu Value Increase 29 | 48 Menu Value Decrease 30 | 49-5F Reserved 31 | 60 Data On Screen 32 | 61 Closed Caption 33 | 62 Closed Caption Select 34 | 63 VCR/TV 35 | 64 Broadcast Mode 36 | 65 Snapshot 37 | 66 Still 38 | 67 Picture-in-Picture Toggle 39 | 68 Picture-in-Picture Swap 40 | 69 Red Menu Button 41 | 6A Green Menu Button 42 | 6B Blue Menu Button 43 | 6C Yellow Menu Button 44 | 6D Aspect 45 | 6E 3D Mode Select 46 | 6F Display Brightness Increment 47 | 70 Display Brightness Decrement 48 | 71 Display Brightness 49 | 72 Display Backlight Toggle 50 | 73 Display Set Brightness to Minimum 51 | 74 Display Set Brightness to Maximum 52 | 75 Display Set Auto Brightness 53 | 76 Camera Access Enabled 54 | 77 Camera Access Disabled 55 | 78 Camera Access Toggle 56 | 79-7F Reserved 57 | 80 Selection 58 | 81 Assign Selection 59 | 82 Mode Step 60 | 83 Recall Last 61 | 84 Enter Channel 62 | 85 Order Movie 63 | 86 Channel 64 | 87 Media Selection 65 | 88 Media Select Computer 66 | 89 Media Select TV 67 | 8A Media Select WWW 68 | 8B Media Select DVD 69 | 8C Media Select Telephone 70 | 8D Media Select Program Guide 71 | 8E Media Select Video Phone 72 | 8F Media Select Games 73 | 90 Media Select Messages 74 | 91 Media Select CD 75 | 92 Media Select VCR 76 | 93 Media Select Tuner 77 | 94 Quit 78 | 95 Help 79 | 96 Media Select Tape 80 | 97 Media Select Cable 81 | 98 Media Select Satellite 82 | 99 Media Select Security 83 | 9A Media Select Home 84 | 9B Media Select Call 85 | 9C Channel Increment 86 | 9D Channel Decrement 87 | 9E Media Select SAP 88 | 9F-9F Reserved 89 | A0 VCR Plus 90 | A1 Once 91 | A2 Daily 92 | A3 Weekly 93 | A4 Monthly 94 | A5-AF Reserved 95 | B0 Play 96 | B1 Pause 97 | B2 Record 98 | B3 Fast Forward 99 | B4 Rewind 100 | B5 Scan Next Track 101 | B6 Scan Previous Track 102 | B7 Stop 103 | B8 Eject 104 | B9 Random Play 105 | BA Select Disc 106 | BB Enter Disc 107 | BC Repeat 108 | BD Tracking 109 | BE Track Normal 110 | BF Slow Tracking 111 | C0 Frame Forward 112 | C1 Frame Back 113 | C2 Mark 114 | C3 Clear Mark 115 | C4 Repeat From Mark 116 | C5 Return To Mark 117 | C6 Search Mark Forward 118 | C7 Search Mark Backwards 119 | C8 Counter Reset 120 | C9 Show Counter 121 | CA Tracking Increment 122 | CB Tracking Decrement 123 | CC Stop/Eject 124 | CD Play/Pause 125 | CE Play/Skip 126 | CF Voice Command 127 | D0 Invoke Capture Interface 128 | D1 Start or Stop Game Recording 129 | D2 Historical Game Capture 130 | D3 Capture Game Screenshot 131 | D4 Show or Hide Recording Indicator 132 | D5 Start or Stop Microphone Capture 133 | D6 Start or Stop Camera Capture 134 | D7 Start or Stop Game Broadcast 135 | D8-DF Reserved 136 | E0 Volume 137 | E1 Balance 138 | E2 Mute 139 | E3 Bass 140 | E4 Treble 141 | E5 Bass Boost 142 | E6 Surround Mode 143 | E7 Loudness 144 | E8 MPX 145 | E9 Volume Up 146 | EA Volume Down 147 | EB-EF Reserved 148 | F0 Speed Select 149 | F1 Playback Speed 150 | F2 Standard Play 151 | F3 Long Play 152 | F4 Extended Play 153 | F5 Slow 154 | F6-FF Reserved 155 | 100 Fan Enable 156 | 101 Fan Speed 157 | 102 Light Enable 158 | 103 Light Illumination Level 159 | 104 Climate Control Enable 160 | 105 Room Temperature 161 | 106 Security Enable 162 | 107 Fire Alarm 163 | 108 Police Alarm 164 | 109 Proximity 165 | 10A Motion 166 | 10B Duress Alarm 167 | 10C Holdup Alarm 168 | 10D Medical Alarm 169 | 10E-14F Reserved 170 | 150 Balance Right 171 | 151 Balance Left 172 | 152 Bass Increment 173 | 153 Bass Decrement 174 | 154 Treble Increment 175 | 155 Treble Decrement 176 | 156-15F Reserved 177 | 160 Speaker System 178 | 161 Channel Left 179 | 162 Channel Right 180 | 163 Channel Center 181 | 164 Channel Front 182 | 165 Channel Center Front 183 | 166 Channel Side 184 | 167 Channel Surround 185 | 168 Channel Low Freq Enhancement 186 | 169 Channel Top 187 | 16A Channel Unknown 188 | 16B-16F Reserved 189 | 170 Sub-channel 190 | 171 Sub-channel Increment 191 | 172 Sub-channel Decrement 192 | 173 Alternate Audio Increment 193 | 174 Alternate Audio Decrement 194 | 175-17F Reserved 195 | 180 Application Launch Buttons 196 | 181 AL Launch Button Config Tool 197 | 182 AL Programmable Button Config 198 | 183 AL Consumer Control Config 199 | 184 AL Word Processor 200 | 185 AL Text Editor 201 | 186 AL Spreadsheet 202 | 187 AL Graphics Editor 203 | 188 AL Presentation App 204 | 189 AL Database App 205 | 18A AL Email Reader 206 | 18B AL Newsreader 207 | 18C AL Voicemail 208 | 18D AL Contacts/Address Book 209 | 18E AL Calendar/Schedule 210 | 18F AL Task/Project Manager 211 | 190 AL Log/Journal/Timecard 212 | 191 AL Checkbook/Finance 213 | 192 AL Calculator 214 | 193 AL A/VCapture/Playback 215 | 194 AL Local Machine Browser 216 | 195 AL LAN/WANBrowser 217 | 196 AL Internet Browser 218 | 197 AL Remote Networking/ISPConnect 219 | 198 AL Network Conference 220 | 199 AL Network Chat 221 | 19A AL Telephony/Dialer 222 | 19B AL Logon 223 | 19C AL Logoff 224 | 19D AL Logon/Logoff 225 | 19E AL Terminal Lock/Screensaver 226 | 19F AL Control Panel 227 | 1A0 AL Command Line Processor/Run 228 | 1A1 AL Process/Task Manager 229 | 1A2 AL Select Task/Application 230 | 1A3 AL Next Task/Application 231 | 1A4 AL Previous Task/Application 232 | 1A5 AL Preempt Halt Task/Application 233 | 1A6 AL Integrated Help Center 234 | 1A7 AL Documents 235 | 1A8 AL Thesaurus 236 | 1A9 AL Dictionary 237 | 1AA AL Desktop 238 | 1AB AL Spell Check 239 | 1AC AL Grammar Check 240 | 1AD AL Wireless Status 241 | 1AE AL Keyboard Layout 242 | 1AF AL Virus Protection 243 | 1B0 AL Encryption 244 | 1B1 AL Screen Saver 245 | 1B2 AL Alarms 246 | 1B3 AL Clock 247 | 1B4 AL File Browser 248 | 1B5 AL Power Status 249 | 1B6 AL Image Browser 250 | 1B7 AL Audio Browser 251 | 1B8 AL Movie Browser 252 | 1B9 AL Digital Rights Manager 253 | 1BA AL Digital Wallet 254 | 1BB-1BB Reserved 255 | 1BC AL Instant Messaging 256 | 1BD AL OEMFeatures Tips Tuto Browser 257 | 1BE AL OEMHelp 258 | 1BF AL Online Community 259 | 1C0 AL Entertainment Content Browser 260 | 1C1 AL Online Shopping Browser 261 | 1C2 AL Smart Card Information/Help 262 | 1C3 AL Market Monitor Finance Browser 263 | 1C4 AL Customized Corp News Browser 264 | 1C5 AL Online Activity Browser 265 | 1C6 AL Research/Search Browser 266 | 1C7 AL Audio Player 267 | 1C8 AL Message Status 268 | 1C9 AL Contact Sync 269 | 1CA-1FF Reserved 270 | 200 Generic GUIApplication Controls 271 | 201 AC New 272 | 202 AC Open 273 | 203 AC Close 274 | 204 AC Exit 275 | 205 AC Maximize 276 | 206 AC Minimize 277 | 207 AC Save 278 | 208 AC Print 279 | 209 AC Properties 280 | 20A-219 Reserved 281 | 21A AC Undo 282 | 21B AC Copy 283 | 21C AC Cut 284 | 21D AC Paste 285 | 21E AC Select All 286 | 21F AC Find 287 | 220 AC Findand Replace 288 | 221 AC Search 289 | 222 AC Go To 290 | 223 AC Home 291 | 224 AC Back 292 | 225 AC Forward 293 | 226 AC Stop 294 | 227 AC Refresh 295 | 228 AC Previous Link 296 | 229 AC Next Link 297 | 22A AC Bookmarks 298 | 22B AC History 299 | 22C AC Subscriptions 300 | 22D AC Zoom In 301 | 22E AC Zoom Out 302 | 22F AC Zoom 303 | 230 AC Full Screen View 304 | 231 AC Normal View 305 | 232 AC View Toggle 306 | 233 AC Scroll Up 307 | 234 AC Scroll Down 308 | 235 AC Scroll 309 | 236 AC Pan Left 310 | 237 AC Pan Right 311 | 238 AC Pan 312 | 239 AC New Window 313 | 23A AC Tile Horizontally 314 | 23B AC Tile Vertically 315 | 23C AC Format 316 | 23D AC Edit 317 | 23E AC Bold 318 | 23F AC Italics 319 | 240 AC Underline 320 | 241 AC Strikethrough 321 | 242 AC Subscript 322 | 243 AC Superscript 323 | 244 AC All Caps 324 | 245 AC Rotate 325 | 246 AC Resize 326 | 247 AC Fliphorizontal 327 | 248 AC Flip Vertical 328 | 249 AC Mirror Horizontal 329 | 24A AC Mirror Vertical 330 | 24B AC Font Select 331 | 24C AC Font Color 332 | 24D AC Font Size 333 | 24E AC Justify Left 334 | 24F AC Justify Center H 335 | 250 AC Justify Right 336 | 251 AC Justify Block H 337 | 252 AC Justify Top 338 | 253 AC Justify Center V 339 | 254 AC Justify Bottom 340 | 255 AC Justify Block V 341 | 256 AC Indent Decrease 342 | 257 AC Indent Increase 343 | 258 AC Numbered List 344 | 259 AC Restart Numbering 345 | 25A AC Bulleted List 346 | 25B AC Promote 347 | 25C AC Demote 348 | 25D AC Yes 349 | 25E AC No 350 | 25F AC Cancel 351 | 260 AC Catalog 352 | 261 AC Buy/Checkout 353 | 262 AC Addto Cart 354 | 263 AC Expand 355 | 264 AC Expand All 356 | 265 AC Collapse 357 | 266 AC Collapse All 358 | 267 AC Print Preview 359 | 268 AC Paste Special 360 | 269 AC Insert Mode 361 | 26A AC Delete 362 | 26B AC Lock 363 | 26C AC Unlock 364 | 26D AC Protect 365 | 26E AC Unprotect 366 | 26F AC Attach Comment 367 | 270 AC Delete Comment 368 | 271 AC View Comment 369 | 272 AC Select Word 370 | 273 AC Select Sentence 371 | 274 AC Select Paragraph 372 | 275 AC Select Column 373 | 276 AC Select Row 374 | 277 AC Select Table 375 | 278 AC Select Object 376 | 279 AC Redo/Repeat 377 | 27A AC Sort 378 | 27B AC Sort Ascending 379 | 27C AC Sort Descending 380 | 27D AC Filter 381 | 27E AC Set Clock 382 | 27F AC View Clock 383 | 280 AC Select Time Zone 384 | 281 AC Edit Time Zones 385 | 282 AC Set Alarm 386 | 283 AC Clear Alarm 387 | 284 AC Snooze Alarm 388 | 285 AC Reset Alarm 389 | 286 AC Synchronize 390 | 287 AC Send/Receive 391 | 288 AC Send To 392 | 289 AC Reply 393 | 28A AC Reply All 394 | 28B AC Forward Msg 395 | 28C AC Send 396 | 28D AC Attach File 397 | 28E AC Upload 398 | 28F AC Download(Save Target As) 399 | 290 AC Set Borders 400 | 291 AC Insert Row 401 | 292 AC Insert Column 402 | 293 AC Insert File 403 | 294 AC Insert Picture 404 | 295 AC Insert Object 405 | 296 AC Insert Symbol 406 | 297 AC Saveand Close 407 | 298 AC Rename 408 | 299 AC Merge 409 | 29A AC Split 410 | 29B AC Disribute Horizontally 411 | 29C AC Distribute Vertically 412 | 29D AC Next Keyboard Layout Select 413 | 29E-29F Reserved 414 | 2A0 ACSoft Key Left 415 | 2A1 ACSoft Key Right 416 | 2A2-2AF Reserved 417 | 2B0 AC Idle Keep Alive 418 | 2B1-2BF Reserved 419 | 2C0 Extended Keyboard Attributes Collection 420 | 2C1 Keyboard Form Factor 421 | 2C2 Keyboard Key Type 422 | 2C3 Keyboard Physical Layout 423 | 2C4 Vendor-Specific Keyboard Physical Layout 424 | 2C5 Keyboard IETF Language Tag Index 425 | 2C6 Implemented Keyboard Input AssistControls 426 | 2C7 Keyboard Input Assist Previous 427 | 2C8 Keyboard Input Assist NextS 428 | 2C9 Keyboard Input Assist Previous Group 429 | 2CA Keyboard Input Assist NextGroup 430 | 2CB Keyboard Input Assist Accept 431 | 2CC Keyboard Input Assist Cancel 432 | 2CB-0x2DF Reserved 433 | 2E0-4FF Reserved 434 | 500 Contact Edited 435 | 501 Contact Added 436 | 502 Contact Record Active 437 | 503 Contact Index 438 | 504 Contact Nickname 439 | 505 Contact First Name 440 | 506 Contact Last Name 441 | 507 Contact Full Name 442 | 508 Contact Phone Number Personal 443 | 509 Contact Phone Number Business 444 | 50A Contact Phone Number Mobile 445 | 50B Contact Phone Number Pager 446 | 50C Contact Phone Number Fax 447 | 50D Contact Phone Number Other 448 | 50E Contact Email Personal 449 | 50F Contact Email Business 450 | 510 Contact Email Other 451 | 511 Contact Email Main 452 | 512 Contact Speed Dial Number 453 | 513 Contact Status Flag 454 | 514 Contact Misc. 455 | 515-FFFF Reserved 456 | -------------------------------------------------------------------------------- /tools/data/000d_digitizers.hut: -------------------------------------------------------------------------------- 1 | (0d) Digitizers 2 | 00 Undefined 3 | 01 Digitizer 4 | 02 Pen 5 | 03 Light Pen 6 | 04 Touch Screen 7 | 05 Touch Pad 8 | 06 White Board 9 | 07 Coordinate Measuring Machine 10 | 08 3-D Digitizer 11 | 09 Stereo Plotter 12 | 0A Articulated Arm 13 | 0B Armature 14 | 0C Multiple Point Digitizer 15 | 0D Free Space Wand 16 | 0E Device Configuration 17 | 0F-1F Reserved 18 | 20 Stylus 19 | 21 Puck 20 | 22 Finger 21 | 23 Device Settings 22 | 24-2F Reserved 23 | 30 Tip Pressure 24 | 31 Barrel Pressure 25 | 32 In Range 26 | 33 Touch 27 | 34 Untouch 28 | 35 Tap 29 | 36 Quality 30 | 37 Data Valid 31 | 38 Transducer Index 32 | 39 Tablet Function Keys 33 | 3A Program Change Keys 34 | 3B Battery Strength 35 | 3C Invert 36 | 3D X Tilt 37 | 3E Y Tilt 38 | 3F Azimuth 39 | 40 Altitude 40 | 41 Twist 41 | 42 Tip Switch 42 | 43 Secondary Tip Switch 43 | 44 Barrel Switch 44 | 45 Eraser 45 | 46 Tablet Pick 46 | 47 Confidence 47 | 48 Width 48 | 49 Height 49 | 4A-50 Reserved 50 | 51 Contact Id 51 | 52 Inputmode 52 | 53 Device Index 53 | 54 Contact Count 54 | 55 Contact Max 55 | 56 Scan Time 56 | 57 Surface Switch 57 | 58 Button Switch 58 | 59 Button Type 59 | 5A Secondary Barrel Switch 60 | 5B Transducer Serial Number 61 | 5C Preferred Inking Color 62 | 5D Preferred Color is Locked 63 | 5E Preferred Line Width 64 | 5F Preferred Line Width is Locked 65 | 70 Preferred Line Style 66 | 71 Preferred Line Style is Locked 67 | 72 Ink 68 | 73 Pencil 69 | 74 Highlighter 70 | 75 Chisel Marker 71 | 76 Brush 72 | 77 No preference 73 | 78-7F Reserved for future line styles 74 | 80 Digitizer Diagnostic 75 | 81 Digitizer Error 76 | 82 Err Normal Status 77 | 83 Err Transducers Exceeded 78 | 84 Err Full Trans Features Unavail 79 | 85 Err Charge Low 80 | 86-8F Reserved for future errors 81 | 90 Transducer Software Info. 82 | 91 Transducer Vendor ID 83 | 92 Transducer Product ID 84 | 93 Device Supported Protocols 85 | 94 Transducer Supported Protocols 86 | 95 No Protocol 87 | 96 Wacom AES Protocol 88 | 97 USI Protocol 89 | 98‐9F Reserved for future transducer protocols 90 | A0 Supported Report Rates 91 | A1 Report Rate 92 | A2 Transducer Connected 93 | A3 Switch Disabled 94 | A4 Switch Unimplemented 95 | A5 Transducer Switches 96 | A6‐FFFF Reserved 97 | -------------------------------------------------------------------------------- /tools/data/000e_haptic_page.hut: -------------------------------------------------------------------------------- 1 | (0e) Haptic 2 | 01 Simple Haptic Controller 3 | 02‐0F Reserved 4 | 10 Waveform 5 | 11 Duration 6 | 12‐1F Reserved 7 | 20 Auto Trigger 8 | 21 Manual Trigger 9 | 22 Auto Trigger Associated Control 10 | 23 Intensity 11 | 24 Repeat Count 12 | 25 Retrigger Period 13 | 26 Waveform Vendor Page 14 | 27 Waveform Vendor ID 15 | 28 Waveform Cutoff Time 16 | 29‐0x0FFF Reserved 17 | 1000 Reserved 18 | 1001 WAVEFORM_NONE 19 | 1002 WAVEFORM_STOP 20 | 1003 WAVEFORM_CLICK 21 | 1004 WAVEFORM_BUZZ_CONTINUOUS 22 | 1005 WAVEFORM_RUMBLE_CONTINUOUS 23 | 1006 WAVEFORM_PRESS 24 | 1007 WAVEFORM_RELEASE 25 | 1006‐1FFF Reserved for standard waveforms 26 | 2000 Reserved 27 | 2001-2FFF Reserved: Vendor Waveforms 28 | 3000-FFFF Reserved 29 | -------------------------------------------------------------------------------- /tools/data/0010_unicode.hut: -------------------------------------------------------------------------------- 1 | (10) Unicode 2 | -------------------------------------------------------------------------------- /tools/data/0014_alphanumeric_display.hut: -------------------------------------------------------------------------------- 1 | (14) Alphanumeric Display 2 | 00 Undefined 3 | 01 Alphanumeric Display 4 | 02 Auxiliary Display 5 | 03-1F Reserved 6 | 20 Display Attributes Report 7 | 21 ASCII Character Set 8 | 22 Data Read Back 9 | 23 Font Read Back 10 | 24 Display Control Report 11 | 25 Clear Display 12 | 26 Display Enable 13 | 27 Screen Saver Delay 14 | 28 Screen Saver Enable 15 | 29 Vertical Scroll 16 | 2A Horizontal Scroll 17 | 2B Character Report 18 | 2C Display Data 19 | 2D Display Status 20 | 2E Stat Not Ready 21 | 2F Stat Ready 22 | 30 Err Not a loadable character 23 | 31 Err Font data cannot be read 24 | 32 Cursor Position Report 25 | 33 Row 26 | 34 Column 27 | 35 Rows 28 | 36 Columns 29 | 37 Cursor Pixel Positioning 30 | 38 Cursor Mode 31 | 39 Cursor Enable 32 | 3A Cursor Blink 33 | 3B Font Report 34 | 3C Font Data 35 | 3D Character Width 36 | 3E Character Height 37 | 3F Character Spacing Horizontal 38 | 40 Character Spacing Vertical 39 | 41 Unicode Character Set 40 | 42 Font 7-Segment 41 | 43 7-Segment Direct Map 42 | 44 Font 14-Segment 43 | 45 14-Segment Direct Map 44 | 46 Display Brightness 45 | 47 Display Contrast 46 | 48 Character Attribute 47 | 49 Attribute Readback 48 | 4A Attribute Data 49 | 4B Char Attr Enhance 50 | 4C Char Attr Underline 51 | 4D Char Attr Blink 52 | 4E-7F Reserved 53 | 80 Bitmap Size X 54 | 81 Bitmap Size Y 55 | 82 Max Blit Size 56 | 83 Bit Depth Format 57 | 84 Display Orientation 58 | 85 Palette Report 59 | 86 Palette Data Size 60 | 87 Palette Data Offset 61 | 88 Palette Data 62 | 89 Reserved 63 | 8A Blit Report 64 | 8B Blit Rectangle X1 65 | 8C Blit Rectangle Y1 66 | 8D Blit Rectangle X2 67 | 8E Blit Rectangle Y2 68 | 8F Blit Data 69 | 90 Soft Button 70 | 91 Soft Button ID 71 | 92 Soft Button Side 72 | 93 Soft Button Offset1 73 | 94 Soft Button Offset2 74 | 95 Soft Button Report 75 | 96-C1 Reserved 76 | C2 Soft Keys 77 | C3-CB Reserved 78 | CC Display Data Extensions 79 | DD-CE Reserved 80 | CF Character Mapping 81 | D0-DC Reserved 82 | DD Unicode Equivalent 83 | DE Reserved 84 | DF Character Page Mapping 85 | F0-FE Reserved 86 | FF Request Report 87 | -------------------------------------------------------------------------------- /tools/data/0014_auxiliary_display.hut: -------------------------------------------------------------------------------- 1 | (14) Auxiliary Display 2 | 00 Undefined 3 | 01 Alphanumeric Display 4 | 02 Auxiliary Display 5 | 03-1F Reserved 6 | 20 Display Attributes Report 7 | 21 ASCII Character Set 8 | 22 Data Read Back 9 | 23 Font Read Back 10 | 24 Display Control Report 11 | 25 Clear Display 12 | 26 Display Enable 13 | 27 Screen Saver Delay 14 | 28 Screen Saver Enable 15 | 29 Vertical Scroll 16 | 2A Horizontal Scroll 17 | 2B Character Report 18 | 2C Display Data 19 | 2D Display Status 20 | 2E Stat Not Ready 21 | 2F Stat Ready 22 | 30 Err Not a loadable character 23 | 31 Err Font data cannot be read 24 | 32 Cursor Position Report 25 | 33 Row 26 | 34 Column 27 | 35 Rows 28 | 36 Columns 29 | 37 Cursor Pixel Positioning 30 | 38 Cursor Mode 31 | 39 Cursor Enable 32 | 3A Cursor Blink 33 | 3B Font Report 34 | 3C Font Data 35 | 3D Character Width 36 | 3E Character Height 37 | 3F Character Spacing Horizontal 38 | 40 Character Spacing Vertical 39 | 41 Unicode Character Set 40 | 42 Font 7-Segment 41 | 43 7-Segment Direct Map 42 | 44 Font 14-Segment 43 | 45 14-Segment Direct Map 44 | 46 Display Brightness 45 | 47 Display Contrast 46 | 48 Character Attribute 47 | 49 Attribute Readback 48 | 4A Attribute Data 49 | 4B Char Attr Enhance 50 | 4C Char Attr Underline 51 | 4D Char Attr Blink 52 | 4E-7F Reserved 53 | 80 Bitmap Size X 54 | 81 Bitmap Size Y 55 | 82 Max Blit Size 56 | 83 Bit Depth Format 57 | 84 Display Orientation 58 | 85 Palette Report 59 | 86 Palette Data Size 60 | 87 Palette Data Offset 61 | 88 Palette Data 62 | 89 Reserved 63 | 8A Blit Report 64 | 8B Blit Rectangle X1 65 | 8C Blit Rectangle Y1 66 | 8D Blit Rectangle X2 67 | 8E Blit Rectangle Y2 68 | 8F Blit Data 69 | 90 Soft Button 70 | 91 Soft Button ID 71 | 92 Soft Button Side 72 | 93 Soft Button Offset1 73 | 94 Soft Button Offset2 74 | 95 Soft Button Report 75 | 96-C1 Reserved 76 | C2 Soft Keys 77 | C3-CB Reserved 78 | CC Display Data Extensions 79 | DD-CE Reserved 80 | CF Character Mapping 81 | D0-DC Reserved 82 | DD Unicode Equivalent 83 | DE Reserved 84 | DF Character Page Mapping 85 | F0-FE Reserved 86 | FF Request Report 87 | -------------------------------------------------------------------------------- /tools/data/0040_medical_instruments.hut: -------------------------------------------------------------------------------- 1 | (40) Medical Instruments 2 | 00 Undefined 3 | 01 Medical Ultrasound 4 | 02-1F Reserved 5 | 20 VCR/Acquisition 6 | 21 Freeze/Thaw 7 | 22 Clip Store 8 | 23 Update 9 | 24 Next 10 | 25 Save 11 | 26 Print 12 | 27 Microphone Enable 13 | 28-3F Reserved 14 | 40 Cine 15 | 41 Transmit Power 16 | 42 Volume 17 | 43 Focus 18 | 44 Depth 19 | 45-5F Reserved 20 | 60 Soft Step-Primary 21 | 61 Soft Step-Secondary 22 | 62-6F Reserved 23 | 70 Depth Gain Compensation 24 | 71-7F Reserved 25 | 80 Zoom Select 26 | 81 Zoom Adjust 27 | 82 Spectral Doppler Mode Select 28 | 83 Spectral Doppler Adjust 29 | 84 Color Doppler Mode Select 30 | 85 Color Doppler Adjust 31 | 86 Motion Mode Select 32 | 87 Motion Mode Adjust 33 | 88 2-D Mode Select 34 | 89 2-D Mode Adjust 35 | 8A-9F Reserved 36 | A0 Soft Control Select 37 | A1 Soft Control Adjust 38 | A2-FFFF Reserved 39 | -------------------------------------------------------------------------------- /tools/data/0080_monitor.hut: -------------------------------------------------------------------------------- 1 | (80) Monitor 2 | 00 Undefined 3 | 01 Monitor Control 4 | 02 EDID Information 5 | 03 VDIF Information 6 | 04 VESA Version 7 | 05 On Screen Display 8 | 06 Auto Size Center 9 | 07 Polarity Horz Synch 10 | 08 Polarity Vert Synch 11 | 09 Sync Type 12 | 0A Screen Position 13 | 0B Horizontal Frequency 14 | 0C Vertical Frequency 15 | 0D-FFFF Reserved 16 | -------------------------------------------------------------------------------- /tools/data/0081_monitor_enumerated_values.hut: -------------------------------------------------------------------------------- 1 | (81) Monitor Enumerated Values 2 | 00 unassigned 3 | -------------------------------------------------------------------------------- /tools/data/0082_vesa_virtual_controls.hut: -------------------------------------------------------------------------------- 1 | (82) VESA Virtual Controls 2 | 00-0F Reserved 3 | 10 Brightness 4 | 12 Contrast 5 | 16 Video Gain Red 6 | 18 Video Gain Green 7 | 1A Video Gain Blue 8 | 1C Focus 9 | 20 Horizontal Position 10 | 22 Horizontal Size 11 | 24 Horizontal Pincushion 12 | 26 Horizontal Pincushion Balance 13 | 28 Horizontal Misconvergence 14 | 2A Horizontal Linearity 15 | 2C Horizontal Linearity Balance 16 | 30 Vertical Position 17 | 32 Vertical Size 18 | 34 Vertical Pincushion 19 | 36 Vertical Pincushion Balance 20 | 38 Vertical Misconvergence 21 | 3A Vertical Linearity 22 | 3C Vertical Linearity Balance 23 | 40 Parallelogram Distortion 24 | 42 Trapezoidal Distortion 25 | 44 Tilt 26 | 46 Top Corner Distortion Control 27 | 48 Top Corner Distortion Balance 28 | 4A Bottom Corner Distortion Control 29 | 4C Bottom Corner Distortion Balance 30 | 56 Moiré Horizontal 31 | 58 Moiré Vertical 32 | 5E Input Level Select 33 | 60 Input Source Select 34 | 62 Stereo Mode 35 | 6C Video Black Level Red 36 | 6E Video Black Level Green 37 | 70 Video Black Level Blue 38 | 71-FFFF Reserved 39 | -------------------------------------------------------------------------------- /tools/data/0083_vesa_command.hut: -------------------------------------------------------------------------------- 1 | (83) VESA Command 2 | 00 Undefined 3 | 01 Settings 4 | 02 Degauss 5 | 03-FFFF Reserved 6 | -------------------------------------------------------------------------------- /tools/data/0084_power_device.hut: -------------------------------------------------------------------------------- 1 | (84) Power Device 2 | -------------------------------------------------------------------------------- /tools/data/0085_battery_system.hut: -------------------------------------------------------------------------------- 1 | (85) Battery System 2 | -------------------------------------------------------------------------------- /tools/data/008c_bar_code_scanner.hut: -------------------------------------------------------------------------------- 1 | (8c) Bar Code Scanner 2 | 00 Undefined 3 | 01-FFFF Reserved 4 | -------------------------------------------------------------------------------- /tools/data/008d_scale.hut: -------------------------------------------------------------------------------- 1 | (8d) Scale 2 | 00 Undefined 3 | 01-FFFF Reserved 4 | -------------------------------------------------------------------------------- /tools/data/008e_magnetic_stripe_reading.hut: -------------------------------------------------------------------------------- 1 | (8e) Magnetic Stripe Reading 2 | 00 Undefined 3 | 01-FFFF Reserved 4 | -------------------------------------------------------------------------------- /tools/data/0090_camera_control.hut: -------------------------------------------------------------------------------- 1 | (90) Camera Control 2 | 00 Undefined 3 | 01-1F Reserved 4 | 20 Camera Auto-focus 5 | 21 Camera Shutter 6 | -------------------------------------------------------------------------------- /tools/data/0091_arcade_page_oaaf.hut: -------------------------------------------------------------------------------- 1 | (91) Arcade Page OAAF 2 | 00 Undefined 3 | 01-FFFF Reserved 4 | -------------------------------------------------------------------------------- /tools/data/0092_gaming_device.hut: -------------------------------------------------------------------------------- 1 | (92) Gaming Device 2 | -------------------------------------------------------------------------------- /tools/data/f1d0_fast_identity_online_alliance.hut: -------------------------------------------------------------------------------- 1 | (F1D0) FIDO Alliance 2 | 00 Undefined 3 | 01 U2F Authenticator Device 4 | 02‐1F Reserved 5 | 20 Input Report Data 6 | 21 Output Report Data 7 | 22‐FFFF Reserved 8 | -------------------------------------------------------------------------------- /tools/data/ff00_vendor_defined_page_1.hut: -------------------------------------------------------------------------------- 1 | (ff00) Vendor Defined Page 1 2 | 00 Undefined 3 | 01 Vendor Usage 1 4 | 02 Vendor Usage 2 5 | 03-FFFF Reserved 6 | -------------------------------------------------------------------------------- /tools/data/ff0d_wacom.hut: -------------------------------------------------------------------------------- 1 | (ff0d) Wacom 2 | 01 Wacom Digitizer 3 | 02 Wacom Pen 4 | 03 Light Pen 5 | 04 Touch Screen 6 | 05 Touch Pad 7 | 06 White Board 8 | 07 Coordinate Measuring Machine 9 | 08 3-D Digitizer 10 | 09 Stereo Plotter 11 | 0A Articulated Arm 12 | 0B Armature 13 | 0C Multiple Point Digitizer 14 | 0D Free Space Wand 15 | 0E Device Configuration 16 | 0F-1F Reserved 17 | 20 Stylus 18 | 21 Puck 19 | 22 Finger 20 | 23 Device Settings 21 | 24-2F Reserved 22 | 30 Tip Pressure 23 | 31 Barrel Pressure 24 | 32 In Range 25 | 33 Touch 26 | 34 Untouch 27 | 35 Tap 28 | 36 Wacom Sense 29 | 37 Data Valid 30 | 38 Transducer Index 31 | 39 Wacom DigitizerFnKeys 32 | 3A Program Change Keys 33 | 3B Battery Strength 34 | 3C Invert 35 | 3D X Tilt 36 | 3E Y Tilt 37 | 3F Azimuth 38 | 40 Altitude 39 | 41 Twist 40 | 42 Tip Switch 41 | 43 Secondary Tip Switch 42 | 44 Barrel Switch 43 | 45 Eraser 44 | 46 Tablet Pick 45 | 47 Confidence 46 | 48 Width 47 | 49 Height 48 | 4A-50 Reserved 49 | 51 Contact Id 50 | 52 Inputmode 51 | 53 Device Index 52 | 54 Contact Count 53 | 55 Contact Max 54 | 56 Scan Time 55 | 57 Surface Switch 56 | 58 Button Switch 57 | 59 Button Type 58 | 5A Secondary Barrel Switch 59 | 5B Transducer Serial Number 60 | 5C Wacom SerialHi 61 | 5D Preferred Color is Locked 62 | 5E Preferred Line Width 63 | 5F Preferred Line Width is Locked 64 | 70 Preferred Line Style 65 | 71 Preferred Line Style is Locked 66 | 72 Ink 67 | 73 Pencil 68 | 74 Highlighter 69 | 75 Chisel Marker 70 | 76 Brush 71 | 77 Wacom ToolType 72 | 78-7F Reserved for future line styles 73 | 80 Digitizer Diagnostic 74 | 81 Digitizer Error 75 | 82 Err Normal Status 76 | 83 Err Transducers Exceeded 77 | 84 Err Full Trans Features Unavail 78 | 85 Err Charge Low 79 | 86-8F Reserved for future errors 80 | 0132 Wacom Distance 81 | 0136 Wacom TouchStrip 82 | 0137 Wacom TouchStrip2 83 | 0138 Wacom TouchRing 84 | 0139 Wacom TouchRingStatus 85 | 0401 Wacom Accelerometer X 86 | 0402 Wacom Accelerometer Y 87 | 0403 Wacom Accelerometer Z 88 | 0404 Wacom Battery Charging 89 | 0454 Wacom TouchOnOff 90 | 043B Wacom Battery Level 91 | 0910 Wacom ExpressKey00 92 | 0950 Wacom ExpressKeyCap00 93 | 0980 Wacom Mode Change 94 | 0981 Wacom Button Desktop Center 95 | 0982 Wacom Button On Screen Keyboard 96 | 0983 Wacom Button Display Setting 97 | 0986 Wacom Button Touch On/Off 98 | 0990 Wacom Button Home 99 | 0991 Wacom Button Up 100 | 0992 Wacom Button Down 101 | 0993 Wacom Button Left 102 | 0994 Wacom Button Right 103 | 0995 Wacom Button Center 104 | 0D03 Wacom FingerWheel 105 | 0D30 Wacom Offset Left 106 | 0D31 Wacom Offset Top 107 | 0D32 Wacom Offset Right 108 | 0D33 Wacom Offset Bottom 109 | 1002 Wacom DataMode 110 | 1013 Wacom Digitizer Info 111 | 112 | 113 | -------------------------------------------------------------------------------- /tools/editor.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2014 Benjamin Tissoires 5 | # Copyright (c) 2014 Red Hat, Inc. 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import os,sys 22 | from PyQt4 import QtCore, QtGui, uic 23 | 24 | try: 25 | _fromUtf8 = QtCore.QString.fromUtf8 26 | except AttributeError: 27 | def _fromUtf8(s): 28 | return s 29 | 30 | import parse_rdesc 31 | import parse_hid 32 | 33 | 34 | class Main(QtGui.QMainWindow): 35 | def __init__(self, filename): 36 | QtGui.QMainWindow.__init__(self) 37 | self.ui = uic.loadUi('HID_editor.ui', self) 38 | self.prevPosition = 0 39 | self.auto_changes = False 40 | self.ui.humanTextEdit.verticalScrollBar().valueChanged.connect(self.ui.rawTextEdit.verticalScrollBar().setValue) 41 | self.ui.rawTextEdit.verticalScrollBar().valueChanged.connect(self.ui.humanTextEdit.verticalScrollBar().setValue) 42 | 43 | # self.block_events_propagation() 44 | if filename: 45 | self.openFile(filename) 46 | # self.release_events_propagation() 47 | 48 | def block_events_propagation(self): 49 | if self.auto_changes: 50 | return 51 | self.ui.humanTextEdit.verticalScrollBar().valueChanged.disconnect(self.ui.rawTextEdit.verticalScrollBar().setValue) 52 | self.auto_changes = True 53 | 54 | def release_events_propagation(self): 55 | if not self.auto_changes: 56 | return 57 | self.auto_changes = False 58 | self.ui.humanTextEdit.verticalScrollBar().valueChanged.connect(self.ui.rawTextEdit.verticalScrollBar().setValue) 59 | 60 | def events_blocked(self): 61 | return self.auto_changes 62 | 63 | def parseRawRdesc(self): 64 | human = "" 65 | input_str = str(self.ui.rawTextEdit.toPlainText()).strip() 66 | lines = input_str.split("\n") 67 | 68 | rdesc_object = parse_rdesc.ReportDescriptor() 69 | indent = 0 70 | 71 | if lines[-1].endswith("0x00"): 72 | # some device present a trailing 0, skipping it 73 | lines[-1] = lines[-1][:-4] 74 | 75 | for line in lines: 76 | l = line.replace(",", "").replace("0x", "").replace("-", "") 77 | 78 | rdesc = [int(r, 16) for r in l.split()] 79 | 80 | for v in rdesc: 81 | try: 82 | rdesc_item = rdesc_object.consume(v, 0) 83 | except: 84 | self.statusBar().showMessage("Error while parsing rdesc, see output"); 85 | raise 86 | if rdesc_item: 87 | descr, indent = parse_rdesc.get_human_descr(rdesc_item, indent) 88 | human += descr 89 | human += "\n" 90 | self.ui.humanTextEdit.setText(human) 91 | 92 | rdesc_object.close_rdesc() 93 | 94 | return rdesc_object 95 | 96 | 97 | def updateRDescFromRaw(self): 98 | self.block_events_propagation() 99 | rdesc = self.parseRawRdesc() 100 | self.statusBar().showMessage(""); 101 | self.update_rdesc(rdesc) 102 | cursor = self.ui.rawTextEdit.textCursor() 103 | err_format = QtGui.QTextCharFormat() 104 | err_format.setBackground(QtGui.QBrush(QtGui.QColor("red"))) 105 | self.redraw() 106 | self.highlight(self.prevPosition) 107 | self.ui.humanTextEdit.verticalScrollBar().setValue(self.ui.rawTextEdit.verticalScrollBar().value()) 108 | self.release_events_propagation() 109 | 110 | def setCurrentLine(self, cursor, n): 111 | cursor.movePosition(QtGui.QTextCursor.Start) 112 | cursor.movePosition(QtGui.QTextCursor.Down, QtGui.QTextCursor.MoveAnchor, n) 113 | 114 | def highlight(self, lineNumber): 115 | hi_selection = QtGui.QTextEdit.ExtraSelection() 116 | hi_selection.format.setBackground(self.palette().alternateBase()) 117 | hi_selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, QtCore.QVariant(True)) 118 | 119 | rCursor = self.ui.rawTextEdit.textCursor() 120 | self.setCurrentLine(rCursor, lineNumber) 121 | hi_selection.cursor = rCursor 122 | hi_selection.cursor.clearSelection() 123 | self.ui.rawTextEdit.setExtraSelections([hi_selection]) 124 | 125 | hCursor = self.ui.humanTextEdit.textCursor() 126 | self.setCurrentLine(hCursor, lineNumber) 127 | hi_selection.cursor = hCursor 128 | hi_selection.cursor.clearSelection() 129 | self.ui.humanTextEdit.setExtraSelections([hi_selection]) 130 | 131 | self.prevPosition = lineNumber 132 | 133 | def rCursorMoved(self): 134 | cursor = self.ui.rawTextEdit.textCursor() 135 | self.highlight(cursor.blockNumber()) 136 | 137 | def hCursorMoved(self): 138 | cursor = self.ui.humanTextEdit.textCursor() 139 | self.highlight(cursor.blockNumber()) 140 | 141 | def openFileAction(self): 142 | fname = QtGui.QFileDialog.getOpenFileName(self, 'Open file', '') 143 | print fname 144 | 145 | def saveFileAction(self): 146 | output = sys.stdout 147 | filename = self.filename 148 | output = open(filename, "w") 149 | output.write("O: %d " % self.original_rdesc.size()) 150 | self.original_rdesc.dump_raw(output) 151 | output.write("\n") 152 | output.write("R: %d " % self.rdesc.size()) 153 | self.rdesc.dump_raw(output) 154 | output.write("\n") 155 | for line in self.file_content: 156 | output.write(line + "\n") 157 | output.close() 158 | self.statusBar().showMessage(filename + " saved"); 159 | 160 | def saveAsFileAction(self): 161 | print "saveAsFileAction" 162 | 163 | def openFile(self, filename): 164 | if not os.path.exists(filename): 165 | raise IOError, filename 166 | f = open(filename) 167 | rdesc = None 168 | events = [] 169 | file_content = [] 170 | original_rdesc = None 171 | for line in f.readlines(): 172 | if line.startswith("R:"): 173 | rdesc = parse_rdesc.parse_rdesc(line.lstrip("R: "), None) 174 | if not rdesc: 175 | raise IOError, filename 176 | rdesc = self.update_rdesc(rdesc) 177 | else: 178 | file_content.append(line.strip()) 179 | if line.startswith("E:") or line.startswith("#"): 180 | events.append(line.strip()) 181 | elif line.startswith("O:"): 182 | original_rdesc = parse_rdesc.parse_rdesc(line.lstrip("O: "), None) 183 | f.close() 184 | # everything went fine, store the new configuration 185 | self.filename = filename 186 | if not original_rdesc: 187 | original_rdesc = self.rdesc 188 | self.original_rdesc = original_rdesc 189 | self.events = events 190 | self.file_content = file_content 191 | self.redraw() 192 | self.redrawRaw() 193 | 194 | def update_rdesc(self, rdesc): 195 | self.rdesc_dict = {} 196 | self.maybe_numbered = False 197 | for k in rdesc.reports.keys(): 198 | if len(rdesc.reports[k][0]): 199 | if k == -1: 200 | self.maybe_numbered = True 201 | key = parse_hid.build_rkey(k, rdesc.reports[k][1]) 202 | self.rdesc_dict[key] = rdesc.reports[k][0] 203 | self.rdesc = rdesc 204 | 205 | def redrawRaw(self): 206 | raws = "" 207 | for item in self.rdesc.rdesc_items: 208 | if item.item == "Report ID": 209 | raws += "----------------------\n" 210 | raws += parse_rdesc.get_raw_values(item) + "\n" 211 | if item.item == "Input": 212 | raws += "\n" 213 | self.ui.rawTextEdit.setText(raws) 214 | 215 | def redraw(self): 216 | scrollVPosition = self.ui.outputTextBrowser.verticalScrollBar().value() 217 | scrollHPosition = self.ui.outputTextBrowser.horizontalScrollBar().value() 218 | data = "" 219 | for e in self.events: 220 | if e.startswith("E:"): 221 | event = parse_hid.parse_event(e, self.rdesc, self.rdesc_dict, self.maybe_numbered) 222 | if event: 223 | data += event 224 | data += "\n" 225 | else: 226 | data += e 227 | data += "\n" 228 | self.ui.outputTextBrowser.setText(data) 229 | self.ui.outputTextBrowser.verticalScrollBar().setValue(scrollVPosition) 230 | self.ui.outputTextBrowser.horizontalScrollBar().setValue(scrollHPosition) 231 | 232 | def main(): 233 | app = QtGui.QApplication(sys.argv) 234 | filename = None 235 | if len(sys.argv) > 1: 236 | filename = sys.argv[1] 237 | try: 238 | window=Main(filename) 239 | except IOError: 240 | sys.exit(1) 241 | window.show() 242 | sys.exit(app.exec_()) 243 | 244 | if __name__ == "__main__": 245 | main() 246 | -------------------------------------------------------------------------------- /tools/hid.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / hid.py: table of hid usages and definitions 5 | # 6 | # Copyright (c) 2012-2017 Benjamin Tissoires 7 | # Copyright (c) 2012-2017 Red Hat, Inc. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | import parse_hut 24 | 25 | hid_items = { 26 | "Main": { 27 | "Input" : 0b10000000, 28 | "Output" : 0b10010000, 29 | "Feature" : 0b10110000, 30 | "Collection" : 0b10100000, 31 | "End Collection" : 0b11000000, 32 | }, 33 | 34 | "Global": { 35 | "Usage Page" : 0b00000100, 36 | "Logical Minimum" : 0b00010100, 37 | "Logical Maximum" : 0b00100100, 38 | "Physical Minimum" : 0b00110100, 39 | "Physical Maximum" : 0b01000100, 40 | "Unit Exponent" : 0b01010100, 41 | "Unit" : 0b01100100, 42 | "Report Size" : 0b01110100, 43 | "Report ID" : 0b10000100, 44 | "Report Count" : 0b10010100, 45 | "Push" : 0b10100100, 46 | "Pop" : 0b10110100, 47 | }, 48 | 49 | "Local": { 50 | "Usage" : 0b00001000, 51 | "Usage Minimum" : 0b00011000, 52 | "Usage Maximum" : 0b00101000, 53 | "Designator Index" : 0b00111000, 54 | "Designator Minimum" : 0b01001000, 55 | "Designator Maximum" : 0b01011000, 56 | "String Index" : 0b01111000, 57 | "String Minimum" : 0b10001000, 58 | "String Maximum" : 0b10011000, 59 | "Delimiter" : 0b10101000, 60 | }, 61 | } 62 | 63 | collections = { 64 | 'PHYSICAL' : 0, 65 | 'APPLICATION' : 1, 66 | 'LOGICAL' : 2, 67 | } 68 | 69 | sensor_mods = { 70 | 0x00: 'Mod None', 71 | 0x10: 'Mod Change Sensitivity Abs', 72 | 0x20: 'Mod Max', 73 | 0x30: 'Mod Min', 74 | 0x40: 'Mod Accuracy', 75 | 0x50: 'Mod Resolution', 76 | 0x60: 'Mod Threshold High', 77 | 0x70: 'Mod Threshold Low', 78 | 0x80: 'Mod Calibration Offset', 79 | 0x90: 'Mod Calibration Multiplier', 80 | 0xa0: 'Mod Report Interval', 81 | 0xb0: 'Mod Frequency Max', 82 | 0xc0: 'Mod Period Max', 83 | 0xd0: 'Mod Change Sensitivity Range Percent', 84 | 0xe0: 'Mod Change Sensitivity Rel Percent', 85 | 0xf0: 'Mod Vendor Reserved', 86 | } 87 | 88 | inv_hid = {} 89 | hid_type = {} 90 | for type, items in list(hid_items.items()): 91 | for k, v in list(items.items()): 92 | inv_hid[v] = k 93 | hid_type[k] = type 94 | 95 | usages = parse_hut.parse() 96 | 97 | usage_pages = {} 98 | inv_usage_pages = {} 99 | inv_usages = {} 100 | for usage, (name, filename, usage_list) in usages.items(): 101 | inv_usage_pages[usage] = name 102 | usage_pages[name] = usage 103 | for k, v in list(usage_list.items()): 104 | inv_usages[(usage << 16) | k] = v 105 | 106 | inv_collections = dict([(v, k) for k, v in list(collections.items())]) 107 | -------------------------------------------------------------------------------- /tools/parse_hid.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / parse_hid.py 5 | # 6 | # Copyright (c) 2012-2017 Benjamin Tissoires 7 | # Copyright (c) 2012-2017 Red Hat, Inc. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | import sys 24 | import parse_rdesc 25 | import hid 26 | 27 | 28 | def get_usage(usage): 29 | usage_page = usage >> 16 30 | if usage_page in hid.inv_usage_pages and \ 31 | hid.inv_usage_pages[usage_page] == "Button": 32 | usage = f'B{str(usage & 0xFF)}' 33 | elif usage in hid.inv_usages: 34 | usage = hid.inv_usages[usage] 35 | else: 36 | usage = f'0x{usage:04x}' 37 | return usage 38 | 39 | 40 | def get_value(report, start, size, twos_comp): 41 | value = 0 42 | start_bit = start 43 | end_bit = start_bit + size 44 | data = report[int(start_bit / 8): int(end_bit / 8 + 1)] 45 | if len(data) == 0: 46 | return "<.>", end_bit 47 | for d in range(len(data)): 48 | value |= data[d] << (8 * d) 49 | 50 | bit_offset = start % 8 51 | value = value >> bit_offset 52 | garbage = (value >> size) << size 53 | value = value - garbage 54 | if twos_comp and size > 1: 55 | value = parse_rdesc.twos_comp(value, size) 56 | return value, end_bit 57 | 58 | 59 | def get_report(time, report, rdesc, numbered): 60 | """ 61 | Translate the given report to a human readable format. 62 | """ 63 | total_bit_offset = 0 64 | 65 | output = f'{time:>10s} ' 66 | sep = '' 67 | report_descriptor = rdesc 68 | if numbered: 69 | output += f'ReportID: {report[0]} ' 70 | sep = '/' 71 | # first byte is report ID, actual data starts at 8 72 | total_bit_offset = 8 73 | prev = None 74 | usages_printed = {} 75 | indent_2nd_line = len(output) 76 | for report_item in report_descriptor: 77 | size = report_item["size"] 78 | array = not (report_item["type"] & (0x1 << 1)) # Variable 79 | const = report_item["type"] & (0x1 << 0) 80 | values = [] 81 | usage_page_name = '' 82 | usage_page = report_item["usage page"] >> 16 83 | if usage_page in hid.inv_usage_pages: 84 | usage_page_name = hid.inv_usage_pages[usage_page] 85 | 86 | # get the value and consumes bits 87 | for i in range(report_item["count"]): 88 | value, total_bit_offset = get_value( 89 | report, total_bit_offset, size, report_item["logical min"] < 0) 90 | values.append(value) 91 | 92 | if const: 93 | output += f'{sep} # ' 94 | elif not array: 95 | value_format = "{:d}" 96 | if size > 1: 97 | value_format = f'{{:{str(len(str(1 << size)) + 1)}d}}' 98 | if isinstance(values[0], str): 99 | value_format = "{}" 100 | usage = f' {get_usage(report_item["usage"])}:' 101 | 102 | # if we don't get a key error this is a duplicate in 103 | # this report descriptor and we need a linebreak 104 | try: 105 | usages_printed[usage] 106 | usages_printed = {} 107 | output += '\n' + indent_2nd_line * ' ' 108 | except KeyError: 109 | pass 110 | finally: 111 | usages_printed[usage] = True 112 | 113 | if (prev and 114 | prev["type"] == report_item["type"] and 115 | prev["usage"] == report_item["usage"]): 116 | sep = "," 117 | usage = "" 118 | output += f'{sep}{usage} {value_format.format(values[0])} ' 119 | else: 120 | if not usage_page_name: 121 | usage_page_name = "Array" 122 | usages = [] 123 | for v in values: 124 | if (v < report_item["logical min"] or 125 | v > report_item["logical max"]): 126 | usages.append('') 127 | else: 128 | usage = "" 129 | if isinstance(values[0], str): 130 | usage = v 131 | else: 132 | usage = f'{v:02x}' 133 | if ('vendor' not in usage_page_name.lower() and 134 | v > 0 and 135 | v < len(report_item["usages"])): 136 | usage = get_usage(report_item["usages"][v]) 137 | if "no event indicated" in usage.lower(): 138 | usage = '' 139 | usages.append(usage) 140 | output += f'{sep}{usage_page_name} [{", ".join(usages)}] ' 141 | sep = '|' 142 | prev = report_item 143 | return output 144 | 145 | 146 | def build_rkey(reportID, length): 147 | return f'{reportID}:{length}' 148 | 149 | 150 | def parse_event(line, rdesc, rdesc_dict, maybe_numbered): 151 | e, time, size, report = line.split(' ', 3) 152 | size = int(size) 153 | report = [int(item, 16) for item in report.split(' ')] 154 | numbered = True 155 | key = build_rkey(report[0], size) 156 | if key not in rdesc_dict and maybe_numbered: 157 | # the report is maybe not numbered 158 | numbered = False 159 | key = build_rkey(-1, size) 160 | if key not in rdesc_dict: 161 | # mabe the report is larger than it should 162 | key = None 163 | current_size = 0 164 | for k in list(rdesc_dict.keys()): 165 | id, id_size = k.split(":") 166 | id = int(id) 167 | id_size = int(id_size) 168 | if id == report[0] and id_size < size and current_size < size: 169 | current_size = id_size 170 | key = k 171 | if key in rdesc_dict: 172 | return get_report(time, report, rdesc_dict[key], numbered) 173 | return None 174 | 175 | 176 | def dump_report(line, rdesc, rdesc_dict, maybe_numbered, f_out): 177 | """ 178 | Translate the given report to a human readable format. 179 | """ 180 | event = parse_event(line, rdesc, rdesc_dict, maybe_numbered) 181 | if event: 182 | f_out.write(event) 183 | f_out.write("\n") 184 | 185 | 186 | def parse_hid(f_in, f_out): 187 | rdesc_dict = {} 188 | rdesc = None 189 | maybe_numbered = False 190 | while True: 191 | try: 192 | line = f_in.readline() 193 | except KeyboardInterrupt: 194 | break 195 | if line.startswith("R:"): 196 | rdesc_object = parse_rdesc.parse_rdesc(line.lstrip("R: "), f_out) 197 | rdesc = rdesc_object.reports 198 | win8 = rdesc_object.win8 199 | for k in list(rdesc.keys()): 200 | if len(rdesc[k][0]): 201 | if k == -1: 202 | maybe_numbered = True 203 | key = build_rkey(k, rdesc[k][1]) 204 | rdesc_dict[key] = rdesc[k][0] 205 | if win8: 206 | f_out.write("**** win 8 certified ****\n") 207 | elif line.startswith("E:"): 208 | dump_report(line, rdesc, rdesc_dict, maybe_numbered, f_out) 209 | elif line == '': 210 | # End of file 211 | break 212 | elif line.startswith("N:") or \ 213 | line.startswith("P:") or \ 214 | line.startswith("I:"): 215 | continue 216 | else: 217 | f_out.write(line) 218 | 219 | 220 | def main(): 221 | f = sys.stdin 222 | if len(sys.argv) > 1: 223 | f = open(sys.argv[1]) 224 | parse_hid(f, sys.stdout) 225 | f.close() 226 | 227 | 228 | if __name__ == "__main__": 229 | main() 230 | -------------------------------------------------------------------------------- /tools/parse_hut.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / parse_hid.py: generate a table of hid usages and definitions 5 | # 6 | # Copyright (c) 2012-2017 Benjamin Tissoires 7 | # Copyright (c) 2012-2017 Red Hat, Inc. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | import os 24 | 25 | DATA_DIRNAME = "data" 26 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 27 | DATA_DIR = os.path.join(SCRIPT_DIR, DATA_DIRNAME) 28 | 29 | 30 | def parse_usages(usage_list): 31 | usages = {} 32 | idx, page_name = None, None 33 | for line in usage_list: 34 | line = line.strip() 35 | if not line: 36 | continue 37 | if line.startswith('('): 38 | idx, page_name = line.split('\t') 39 | idx = int(idx.lstrip('(').rstrip(')'), 16) 40 | continue 41 | usage, name = line.split('\t') 42 | if 'reserved' in name.lower(): 43 | continue 44 | if '-' in usage: 45 | print(line) 46 | continue 47 | usages[int(usage, 16)] = name 48 | return idx, page_name, usages 49 | 50 | 51 | def parse(): 52 | usages = {} 53 | for filename in os.listdir(DATA_DIR): 54 | if filename.endswith('.hut'): 55 | with open(os.path.join(DATA_DIR, filename), 'r') as f: 56 | try: 57 | idx, name, usages_list = parse_usages(f.readlines()) 58 | usages[idx] = (name, filename, usages_list) 59 | except UnicodeDecodeError: 60 | print(filename) 61 | raise 62 | return usages 63 | -------------------------------------------------------------------------------- /tools/plot_evtest.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / plot_hid.py 5 | # 6 | # Copyright (c) 2012-2013 Benjamin Tissoires 7 | # Copyright (c) 2012-2013 Red Hat, Inc. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | import sys 24 | import matplotlib.pyplot as pyplot 25 | 26 | def retrieve_t_x_y_from_evtest(f): 27 | times = [] 28 | xs = [] 29 | ys = [] 30 | start_time = -1 31 | x = 0 32 | y = 0 33 | while True: 34 | try: 35 | line = f.readline() 36 | except KeyboardInterrupt: 37 | break 38 | if "Event:" in line: 39 | if "SYN_REPORT" in line: 40 | data = line.replace(",", "").split() 41 | time = float(data[2]) 42 | if start_time < 0: 43 | start_time = time 44 | time -= start_time 45 | times.append(time) 46 | xs.append(x) 47 | ys.append(y) 48 | elif "ABS_X" in line: 49 | data = line.split() 50 | x = int(data[-1]) 51 | elif "ABS_Y" in line: 52 | data = line.split() 53 | y = int(data[-1]) 54 | elif line == '': 55 | # End of file 56 | break 57 | return times, xs, ys 58 | 59 | def main(): 60 | f = sys.stdin 61 | if len(sys.argv) > 1: 62 | f = open(sys.argv[1]) 63 | times, xs, ys = retrieve_t_x_y_from_evtest(f) 64 | f.close() 65 | 66 | pyplot.plot(times, xs, label="X") 67 | pyplot.hold(True) 68 | pyplot.plot(times, ys, label="Y") 69 | pyplot.hold(False) 70 | pyplot.legend(loc='lower left') 71 | 72 | # Draw the plot to the screen 73 | pyplot.show() 74 | 75 | if __name__ == "__main__": 76 | main() 77 | -------------------------------------------------------------------------------- /tools/plot_hid.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / plot_hid.py 5 | # 6 | # Copyright (c) 2012-2013 Benjamin Tissoires 7 | # Copyright (c) 2012-2013 Red Hat, Inc. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | import sys 24 | import os 25 | import parse_hid 26 | import matplotlib.pyplot as pyplot 27 | import plot_evtest 28 | 29 | def retrieve_t_x_y_from_hid(f): 30 | times = [] 31 | xs = [] 32 | ys = [] 33 | start_time = -1 34 | tmp_out = os.tmpfile() 35 | parse_hid.parse_hid(f, tmp_out) 36 | tmp_out.seek(0) 37 | while True: 38 | try: 39 | line = tmp_out.readline() 40 | except KeyboardInterrupt: 41 | break 42 | if "X:" in line: 43 | line = line.strip() 44 | data = line.split() 45 | time = float(data[0]) 46 | if start_time < 0: 47 | start_time = time 48 | time -= start_time 49 | x = int(data[data.index("X:") + 1]) 50 | y = int(data[data.index("Y:") + 1]) 51 | times.append(time) 52 | xs.append(x) 53 | ys.append(y) 54 | elif line == '': 55 | # End of file 56 | break 57 | tmp_out.close() 58 | return times, xs, ys 59 | 60 | 61 | def main(): 62 | f = sys.stdin 63 | if len(sys.argv) > 1: 64 | f = open(sys.argv[1]) 65 | times, xs, ys = retrieve_t_x_y_from_hid(f) 66 | f.close() 67 | 68 | pyplot.plot(times, xs, label="X") 69 | pyplot.hold(True) 70 | pyplot.plot(times, ys, label="Y") 71 | pyplot.hold(False) 72 | pyplot.legend(loc='lower left') 73 | 74 | # Draw the plot to the screen 75 | pyplot.show() 76 | 77 | if __name__ == "__main__": 78 | main() 79 | -------------------------------------------------------------------------------- /tools/plot_traces.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / plot_hid.py 5 | # 6 | # Copyright (c) 2012-2013 Benjamin Tissoires 7 | # Copyright (c) 2012-2013 Red Hat, Inc. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | import sys 24 | import os 25 | import parse_hid 26 | import matplotlib.pyplot as pyplot 27 | import plot_evtest 28 | import plot_hid 29 | 30 | def main(): 31 | plots = [] 32 | files = [] 33 | if len(sys.argv) > 1: 34 | files = [open(f) for f in sys.argv] 35 | 36 | for i in xrange(len(files)): 37 | f = files[i] 38 | times, xs, ys = [], [], [] 39 | try: 40 | times, xs, ys = plot_hid.retrieve_t_x_y_from_hid(f) 41 | except: 42 | pass 43 | if len(times) > 0: 44 | pyplot.plot(times, xs, label="X_hid" + str(i)) 45 | pyplot.hold(True) 46 | pyplot.plot(times, ys, label="Y_hid" + str(i)) 47 | continue 48 | 49 | f.seek(0) 50 | try: 51 | times, xs, ys = plot_evtest.retrieve_t_x_y_from_evtest(f) 52 | except: 53 | pass 54 | if len(times) > 0: 55 | pyplot.plot(times, xs, label="X_evtest" + str(i)) 56 | pyplot.hold(True) 57 | pyplot.plot(times, ys, label="Y_evtest" + str(i)) 58 | continue 59 | 60 | pyplot.hold(False) 61 | pyplot.legend(loc='lower left') 62 | 63 | # Draw the plot to the screen 64 | pyplot.show() 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /tools/usbmon2hid-replay.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Hid replay / usbmon2hid-replay.py 5 | # 6 | # must be run with: sudo usbmon -i 3 -fu -s 256 | python usbmon2hid-replay.py 7 | # or: python usbmon2hid-replay.py file.txt 8 | # 9 | # Copyright (c) 2014 Benjamin Tissoires 10 | # Copyright (c) 2014 Red Hat, Inc. 11 | # 12 | # This program is free software: you can redistribute it and/or modify 13 | # it under the terms of the GNU General Public License as published by 14 | # the Free Software Foundation; either version 2 of the License, or 15 | # (at your option) any later version. 16 | # 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | # 25 | 26 | import sys 27 | from optparse import OptionParser 28 | 29 | current_device = None 30 | known_devices = [] 31 | hid_devices = {} 32 | 33 | class HID_Device(object): 34 | def __init__(self, bus, id): 35 | self.bus = bus 36 | self.id = id 37 | self.bcdUSB = None 38 | self.bdeviceClass = None 39 | self.bdeviceSubClass = None 40 | self.bdeviceProtocol = None 41 | self.bMaxPacketSize0 = None 42 | self.idVendor = None 43 | self.idProduct = None 44 | self.bcdDevice = None 45 | self.iManufacturer = None 46 | self.iProduct = None 47 | self.iSerialNumber = None 48 | self.bNumConfiguration = None 49 | self.wLANGID = None 50 | self.rdesc = {} 51 | self.incomming_data = {} 52 | self.init_timestamp = None 53 | self.endpointMapping = {} 54 | 55 | def extract_bytes(string): 56 | data = string.replace(" ", "") 57 | return [ data[i*2]+data[i*2+1] for i in xrange(len(data) / 2)] 58 | 59 | def prep_incoming_data(data): 60 | length, content = data.split(" = ") 61 | length = int(length) 62 | content = extract_bytes(content) 63 | return length, content 64 | 65 | def null_request(params, data, device, intf): 66 | return 67 | 68 | def print_request(params, data, device): 69 | print data 70 | 71 | def parse_desc_request(data): 72 | result = [] 73 | length = 0 74 | total_length, value = data.split(" = ") 75 | total_length = int(total_length) 76 | content = extract_bytes(value) 77 | while length < total_length: 78 | length_v = int(content[0], 16) 79 | if length_v + length > total_length: 80 | # print "MALFORMED USB DESC PACKET" 81 | return [[None, None, None]] 82 | type = content[1] 83 | result.append( (length_v, type, content[2:length_v]) ) 84 | content = content[length_v:] 85 | length += length_v 86 | return result 87 | 88 | def parse_desc_device_request(params, data, device, intf): 89 | length, type, content = parse_desc_request(data)[0] 90 | if not length: 91 | return 92 | 93 | device.bcdUSB = int(content[1] + content[0], 16) 94 | device.bdeviceClass = int(content[2], 16) 95 | device.bdeviceSubClass = int(content[3], 16) 96 | device.bdeviceProtocol = int(content[4], 16) 97 | device.bMaxPacketSize0 = int(content[5], 16) 98 | device.idVendor = (content[7] + content[6]).upper() 99 | device.idProduct = (content[9] + content[8]).upper() 100 | device.bcdDevice = int(content[11] + content[10], 16) 101 | device.iManufacturer = int(content[12], 16) 102 | device.iProduct = int(content[13], 16) 103 | device.iSerialNumber = int(content[14], 16) 104 | device.bNumConfiguration = int(content[15], 16) 105 | 106 | #print device.bcdUSB, device.bdeviceClass, device.bdeviceSubClass, device.bdeviceProtocol, device.bMaxPacketSize0, "0x{0}:0x{1}".format(device.idVendor, device.idProduct), device.bcdDevice, device.iManufacturer, device.iProduct, device.iSerialNumber, device.bNumConfiguration 107 | 108 | def parse_desc_configuration_request(params, data, device, intf): 109 | confs = parse_desc_request(data) 110 | if not confs[0][0]: 111 | return 112 | 113 | if len(confs) == 1: 114 | # first configuration contains the generic usb with size of confs 115 | return 116 | 117 | current_intf_number = -1 118 | hid_class = False 119 | 120 | for length, type, content in confs[1:]: 121 | if type == '04': # INTERFACE 122 | current_intf_number = int(content[0], 16) 123 | intfClass = content[3] 124 | hid_class = (intfClass == '03') # HID 125 | elif type == '05': # ENDPOINT 126 | endpointAddress = int(content[0], 16) 127 | if hid_class and endpointAddress & 0x80: 128 | device.endpointMapping[endpointAddress & 0x0f] = current_intf_number 129 | 130 | def utf16s_to_utf8s(length, string): 131 | # TODO: do something much better 132 | result = "" 133 | for i in xrange(len(string) / 2): 134 | result += chr(int(string[i*2], 16)) 135 | missings = (length / 2) - len(result) 136 | result += "."*missings 137 | return result 138 | 139 | def read_le8(array): 140 | return int(array[0], 16) 141 | 142 | def read_le16(array): 143 | return int(array[1], 16) | (int(array[0], 16) << 8) 144 | 145 | class Ctrl(object):pass 146 | 147 | def parse_ctrl_parts(ctrl): 148 | out = Ctrl() 149 | out.bmRequestType = read_le8(ctrl[0:]) 150 | out.bRequest = read_le8(ctrl[1:]) 151 | out.wValue = read_le16(ctrl[2:]) 152 | out.wIndex = read_le16(ctrl[4:]) 153 | out.wLength = read_le16(ctrl[6:]) 154 | return out 155 | 156 | def parse_desc_string_request(ctrl, data, device, intf): 157 | length, type, content = parse_desc_request(data)[0] 158 | if not length: 159 | return 160 | 161 | if ctrl.wIndex == 0: 162 | device.wLANGID = "".join(content) 163 | return 164 | 165 | length -= 2 # remove 2 bytes of prefix (length + type) 166 | 167 | index = ctrl.wValue & 0xff 168 | 169 | if index == device.iManufacturer: 170 | device.iManufacturer = utf16s_to_utf8s(length, content) 171 | elif index == device.iProduct: 172 | device.iProduct = utf16s_to_utf8s(length, content) 173 | elif index == device.iSerialNumber: 174 | device.iSerialNumber = utf16s_to_utf8s(length, content) 175 | # else: 176 | # print params, length, type, utf16s_to_utf8s(length, content) 177 | 178 | def parse_desc_rdesc_request(ctrl, data, device, intf): 179 | if data == "0": 180 | return 181 | if ctrl.wIndex in known_devices: 182 | return 183 | if intf != None and str(ctrl.wIndex) != intf: 184 | return 185 | length, content = prep_incoming_data(data) 186 | device.rdesc[ctrl.wIndex] = length, content 187 | # device.incomming_data.append((timestamp, length, " ".join(content))) 188 | desc = get_description(device, ctrl.wIndex) 189 | if desc: 190 | print desc 191 | print get_rdesc(device, ctrl.wIndex) 192 | name = get_name(device) 193 | if name: 194 | print name 195 | print get_devinfo(device) 196 | known_devices.append(ctrl.wIndex) 197 | 198 | def parse_set_report_request(ctrl, data, device, intf): 199 | type_dict = { 200 | 0x01: "Input", 201 | 0x02: "Output", 202 | 0x03: "Feature", 203 | } 204 | if data == "0": 205 | return 206 | content = extract_bytes(data) 207 | reportID = ctrl.wValue & 0xff 208 | type = (ctrl.wValue >> 8) & 0xff 209 | type = type_dict[type] 210 | # we do not store them for later like we do for the others 211 | if not ctrl.wIndex in known_devices: 212 | return 213 | desc = get_description(device, ctrl.wIndex) 214 | if desc: 215 | print desc 216 | print "# SET_REPORT (%s) ID: %02x -> %s (length %d)"% (type, reportID, " ".join(content), ctrl.wLength) 217 | 218 | def interrupt(timestamp, address, data, device, intf): 219 | if data == "0": 220 | return 221 | length, content = prep_incoming_data(data) 222 | endpoint = address.split(":")[-1] 223 | endpoint = int(endpoint) 224 | pipe = endpoint 225 | if device.endpointMapping.has_key(endpoint): 226 | pipe = device.endpointMapping[endpoint] 227 | if not device.incomming_data.has_key(pipe): 228 | device.incomming_data[pipe] = [] 229 | if not pipe in known_devices: 230 | return 231 | device.incomming_data[pipe].append((timestamp, length, " ".join(content))) 232 | if intf == None or int(intf) == pipe: 233 | desc = get_description(device, pipe) 234 | if desc: 235 | print desc 236 | print get_event(device, pipe, -1) 237 | 238 | class HidCommand(object): 239 | def __init__(self, prefix, name, request_host, request_device, debug = False): 240 | self.prefix = prefix 241 | self.name = name 242 | self.request_host = request_host 243 | self.request_device = request_device 244 | self.debug = debug 245 | 246 | HID_COMMANDS = ( 247 | HidCommand("80 06 01", 248 | name = "GET DESCRIPTOR Request DEVICE", 249 | request_host = null_request, 250 | request_device = parse_desc_device_request), 251 | HidCommand("80 06 02", 252 | name = "GET DESCRIPTOR Request CONFIGURATION", 253 | request_host = null_request, 254 | request_device = parse_desc_configuration_request), 255 | HidCommand("80 06 03", 256 | name = "GET DESCRIPTOR Request STRING", 257 | request_host = null_request, 258 | request_device = parse_desc_string_request), 259 | HidCommand("80 06 06", 260 | name = "GET DESCRIPTOR Request DEVICE", 261 | request_host = null_request, 262 | request_device = null_request), 263 | HidCommand("81 06 22", 264 | name = "GET DESCRIPTOR Request Reports Descriptor", 265 | request_host = null_request, 266 | request_device = parse_desc_rdesc_request), 267 | HidCommand("a1 01", 268 | name = "GET REPORT Request", 269 | request_host = null_request, 270 | request_device = null_request), 271 | HidCommand("21 09", 272 | name = "SET REPORT Request", 273 | request_host = parse_set_report_request, 274 | request_device = null_request), 275 | ) 276 | 277 | def usbmon2hid_replay(f_in, intf): 278 | current_request = null_request 279 | current_params = None 280 | while True: 281 | try: 282 | line = f_in.readline() 283 | except KeyboardInterrupt: 284 | break 285 | if line == "": 286 | break 287 | tag, timestamp, event_type, address, status, usbmon_data = line.rstrip().split(" ", 5) 288 | URB_type, bus, dev_address, endpoint = address.split(":") 289 | if not hid_devices.has_key(dev_address): 290 | hid_devices[dev_address] = HID_Device(bus, dev_address) 291 | 292 | if URB_type in ('Ci', 'Co'): # synchronous control 293 | if event_type == 'C': # answer 294 | if not current_params: 295 | continue 296 | ctrl, debug = current_params 297 | if debug: 298 | print "<---", line, 299 | current_request(ctrl, usbmon_data, hid_devices[dev_address], intf) 300 | current_params = None 301 | else: 302 | for command in HID_COMMANDS: 303 | if usbmon_data.startswith(command.prefix): 304 | req_name = command.name 305 | current_request = command.request_device 306 | host_request = command.request_host 307 | debug = command.debug 308 | 309 | # the ctrl prefix is 8 bytes 310 | params = usbmon_data.rstrip(" <").replace(" ", "")[:16] 311 | params = extract_bytes(params) 312 | ctrl = parse_ctrl_parts(params) 313 | data = "" 314 | if "=" in usbmon_data: 315 | data = usbmon_data.split("=")[1] 316 | current_params = ctrl, debug 317 | if debug: 318 | print "--->", line, 319 | print " ", req_name, dev_address, current_params 320 | host_request(ctrl, data, hid_devices[dev_address], intf) 321 | break 322 | else: 323 | current_request = null_request 324 | elif URB_type == 'Ii': # Interrupt 325 | if event_type == 'C': # data from device 326 | interrupt(timestamp, address, usbmon_data, hid_devices[dev_address], intf) 327 | 328 | return hid_devices 329 | 330 | def get_rdesc(device, index): 331 | length, rdesc = device.rdesc[index] 332 | missing_chars = length - len(rdesc) 333 | rdesc.extend( ("**",) * missing_chars) 334 | return "R: " + str(length) + " " + " ".join(rdesc) 335 | 336 | def get_description(device, index): 337 | global current_device 338 | if current_device == index: 339 | return None 340 | 341 | current_device = index 342 | desc = "# " + device.id + ":" + str(index) + " -> " 343 | if device.idVendor and device.idProduct: 344 | desc += device.idVendor + ":" + device.idProduct 345 | if isinstance(device.iManufacturer, str): 346 | desc += " / " + device.iManufacturer 347 | if isinstance(device.iProduct, str): 348 | desc += " | " + device.iProduct 349 | 350 | desc += '\nD:' + str(index) 351 | 352 | return desc 353 | 354 | def get_name(device): 355 | desc = "N:" 356 | if isinstance(device.iManufacturer, str): 357 | desc += " " + device.iManufacturer 358 | if isinstance(device.iProduct, str): 359 | desc += " " + device.iProduct 360 | if desc == "N:": 361 | return "" 362 | return desc 363 | 364 | def get_devinfo(device): 365 | if device.idVendor and device.idProduct: 366 | return "I: {0} {1} {2}".format(device.bus, device.idVendor, device.idProduct) 367 | return None 368 | 369 | def get_event(device, index, num): 370 | ts, length, data = device.incomming_data[index][num] 371 | if not device.init_timestamp: 372 | for d in hid_devices.values(): 373 | d.init_timestamp = long(ts) 374 | ts = long(ts) - device.init_timestamp 375 | return "E: {0:.06f} {1} {2}".format(ts / 1000000.0, length, data) 376 | 377 | def get_options(): 378 | parser = OptionParser() 379 | parser.add_option("", "--intf", dest="intf", 380 | help="capture only the given interface number, omit if you don't want to filter") 381 | return parser.parse_args() 382 | 383 | def main(): 384 | f = sys.stdin 385 | (options, args) = get_options() 386 | if len(args) > 0: 387 | f = open(args[0]) 388 | devs = usbmon2hid_replay(f, options.intf) 389 | f.close() 390 | 391 | if __name__ == "__main__": 392 | main() 393 | --------------------------------------------------------------------------------